diff --git a/gulpfile.mjs b/gulpfile.mjs index 3cb5f0c4..12992de4 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -55,7 +55,7 @@ function buildDB() { const src = join(PACK_SRC, folder.name); const dest = join(PACK_DEST, folder.name); console.info(`Compiling pack ${folder.name}`); - await compilePack(src, dest, {recursive: true, log: true, nedb: false}); + await compilePack(src, dest, {recursive: true, nedb: false}); } resolve() diff --git a/src/assets/deities/Angrosch.png b/src/assets/deities/Angrosch.png new file mode 100644 index 00000000..d878e2b3 Binary files /dev/null and b/src/assets/deities/Angrosch.png differ diff --git a/src/assets/deities/Aves.png b/src/assets/deities/Aves.png new file mode 100644 index 00000000..412161f4 Binary files /dev/null and b/src/assets/deities/Aves.png differ diff --git a/src/assets/deities/Boron.png b/src/assets/deities/Boron.png new file mode 100644 index 00000000..6ee4c127 Binary files /dev/null and b/src/assets/deities/Boron.png differ diff --git a/src/assets/deities/Efferd.png b/src/assets/deities/Efferd.png new file mode 100644 index 00000000..3786c773 Binary files /dev/null and b/src/assets/deities/Efferd.png differ diff --git a/src/assets/deities/Firun.png b/src/assets/deities/Firun.png new file mode 100644 index 00000000..b9492234 Binary files /dev/null and b/src/assets/deities/Firun.png differ diff --git a/src/assets/deities/Hesinde.png b/src/assets/deities/Hesinde.png new file mode 100644 index 00000000..28cd8c72 Binary files /dev/null and b/src/assets/deities/Hesinde.png differ diff --git a/src/assets/deities/Ifirn.png b/src/assets/deities/Ifirn.png new file mode 100644 index 00000000..b52a0fc6 Binary files /dev/null and b/src/assets/deities/Ifirn.png differ diff --git a/src/assets/deities/Ingerimm.png b/src/assets/deities/Ingerimm.png new file mode 100644 index 00000000..d878e2b3 Binary files /dev/null and b/src/assets/deities/Ingerimm.png differ diff --git a/src/assets/deities/Kor.png b/src/assets/deities/Kor.png new file mode 100644 index 00000000..d62b4970 Binary files /dev/null and b/src/assets/deities/Kor.png differ diff --git a/src/assets/deities/Marbo.png b/src/assets/deities/Marbo.png new file mode 100644 index 00000000..41455e61 Binary files /dev/null and b/src/assets/deities/Marbo.png differ diff --git a/src/assets/deities/Namenlos.png b/src/assets/deities/Namenlos.png new file mode 100644 index 00000000..06d320b0 Binary files /dev/null and b/src/assets/deities/Namenlos.png differ diff --git a/src/assets/deities/Nandus.png b/src/assets/deities/Nandus.png new file mode 100644 index 00000000..9d12a1e8 Binary files /dev/null and b/src/assets/deities/Nandus.png differ diff --git a/src/assets/deities/Peraine.png b/src/assets/deities/Peraine.png new file mode 100644 index 00000000..4c5acb76 Binary files /dev/null and b/src/assets/deities/Peraine.png differ diff --git a/src/assets/deities/Phex.png b/src/assets/deities/Phex.png new file mode 100644 index 00000000..337ba553 Binary files /dev/null and b/src/assets/deities/Phex.png differ diff --git a/src/assets/deities/Praios.png b/src/assets/deities/Praios.png new file mode 100644 index 00000000..72152130 Binary files /dev/null and b/src/assets/deities/Praios.png differ diff --git a/src/assets/deities/Rahja.png b/src/assets/deities/Rahja.png new file mode 100644 index 00000000..da0732e3 Binary files /dev/null and b/src/assets/deities/Rahja.png differ diff --git a/src/assets/deities/Rondra.png b/src/assets/deities/Rondra.png new file mode 100644 index 00000000..b4f6bc95 Binary files /dev/null and b/src/assets/deities/Rondra.png differ diff --git a/src/assets/deities/Satuaria.png b/src/assets/deities/Satuaria.png new file mode 100644 index 00000000..ed5fd7c7 Binary files /dev/null and b/src/assets/deities/Satuaria.png differ diff --git a/src/assets/deities/Simia.png b/src/assets/deities/Simia.png new file mode 100644 index 00000000..d777fc1f Binary files /dev/null and b/src/assets/deities/Simia.png differ diff --git a/src/assets/deities/Tairach.png b/src/assets/deities/Tairach.png new file mode 100644 index 00000000..fe6d724f Binary files /dev/null and b/src/assets/deities/Tairach.png differ diff --git a/src/assets/deities/Travia.png b/src/assets/deities/Travia.png new file mode 100644 index 00000000..3c7ae5d4 Binary files /dev/null and b/src/assets/deities/Travia.png differ diff --git a/src/assets/deities/Tsa.png b/src/assets/deities/Tsa.png new file mode 100644 index 00000000..a1e83e49 Binary files /dev/null and b/src/assets/deities/Tsa.png differ diff --git a/src/assets/deities/Ucuri.png b/src/assets/deities/Ucuri.png new file mode 100644 index 00000000..fdbb8704 Binary files /dev/null and b/src/assets/deities/Ucuri.png differ diff --git a/src/main.mjs b/src/main.mjs index ef4579b2..a4274232 100644 --- a/src/main.mjs +++ b/src/main.mjs @@ -13,6 +13,9 @@ import {EquipmentDataModel} from "./module/data/equipment.mjs"; import {AusruestungSheet} from "./module/sheets/equipmentSheet.mjs"; import {CreatureDataModel} from "./module/data/creature.mjs"; import {CreatureSheet} from "./module/sheets/creatureSheet.mjs"; +import {LiturgySheet} from "./module/sheets/liturgySheet.mjs"; +import {LiturgyDataModel} from "./module/data/liturgy.mjs"; +import {BlessingDataModel} from "./module/data/blessing.mjs"; async function preloadHandlebarsTemplates() { return loadTemplates([ @@ -26,7 +29,8 @@ async function preloadHandlebarsTemplates() { 'systems/DSA_4-1/templates/ui/partial-action-button.hbs', 'systems/DSA_4-1/templates/ui/partial-equipment-button.hbs', 'systems/DSA_4-1/templates/ui/partial-equipment-group-button.hbs', - 'systems/DSA_4-1/templates/ui/partial-array-editor.hbs' + 'systems/DSA_4-1/templates/ui/partial-array-editor.hbs', + 'systems/DSA_4-1/templates/dialog/modify-liturgy.hbs' ]); } @@ -51,6 +55,8 @@ Hooks.once("init", () => { Spell: SpellDataModel, Advantage: VornachteileDataModel, Equipment: EquipmentDataModel, + Liturgy: LiturgyDataModel, + Blessing: BlessingDataModel } CONFIG.Combat.initiative = { @@ -97,6 +103,11 @@ Hooks.once("init", () => { makeDefault: true, label: 'DSA41.AusruestungLabels.Item' }) + Items.registerSheet('dsa41.liturgy', LiturgySheet, { + types: ["Liturgy"], + makeDefault: true, + label: 'DSA41.LiturgyLabels.Item' + }) return preloadHandlebarsTemplates(); }) diff --git a/src/module/data/blessing.mjs b/src/module/data/blessing.mjs new file mode 100644 index 00000000..aeff57d3 --- /dev/null +++ b/src/module/data/blessing.mjs @@ -0,0 +1,13 @@ +const { + SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField +} = foundry.data.fields; + +export class BlessingDataModel extends foundry.abstract.TypeDataModel { + + static defineSchema() { + return { + gottheit: new StringField(), + wert: new NumberField({min: 0, integer: true}), + } + } +} diff --git a/src/module/data/character.mjs b/src/module/data/character.mjs index 00238a65..dfa814e1 100644 --- a/src/module/data/character.mjs +++ b/src/module/data/character.mjs @@ -107,23 +107,7 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel { }), gilde: new StringField(), }), - vornachteile: new ArrayField(new SchemaField({ - vornachteil: new DocumentIdField(Item), - wert: new NumberField({required: false, integer: true}), - })), - sonderfertigkeiten: new ArrayField(new SchemaField({ - name: new StringField(), - auswahlen: new ArrayField(new StringField()), - })), - talente: new ArrayField(new DocumentIdField(Item)), - zauber: new ArrayField(new SchemaField({ - talent: new DocumentIdField(), - zfw: new NumberField({integer: true, required: true}), - })), - liturgien: new ArrayField(new SchemaField({ - name: new StringField(), - })), kampfwerte: new ArrayField(new SchemaField({ name: new StringField(), at: new NumberField({required: true, integer: true}), diff --git a/src/module/data/liturgy.mjs b/src/module/data/liturgy.mjs new file mode 100644 index 00000000..87095a4c --- /dev/null +++ b/src/module/data/liturgy.mjs @@ -0,0 +1,49 @@ +import BaseItem from "./base-item.mjs"; + +const {BooleanField, NumberField, SchemaField, ArrayField, StringField, HTMLField} = foundry.data.fields; + +export class LiturgyDataModel extends BaseItem { + + static defineSchema() { + return { + herkunft: new ArrayField(new SchemaField({ + name: new StringField(), + grad: new NumberField(), + })), + grad: new NumberField({min: 1, max: 5}), + reichweite: new StringField(), + ziel: new StringField(), + wirkungsdauer: new StringField(), + auswirkung: new SchemaField({ + I: new StringField(), + II: new StringField(), + III: new StringField(), + IV: new StringField(), + V: new StringField(), + VI: new StringField(), + VII: new StringField(), + VIII: new StringField(), + }) + + } + } + + prepareData() { + } + + /** + * Prepare a data object which is passed to any Roll formulas which are created related to this Item + * @private + */ + getRollData() { + } + + /** + * Handle clickable rolls. + * @param {Event} event The originating click event + * @private + */ + async roll() { + } + +} diff --git a/src/module/data/miracle/liturgydata.mjs b/src/module/data/miracle/liturgydata.mjs new file mode 100644 index 00000000..31732332 --- /dev/null +++ b/src/module/data/miracle/liturgydata.mjs @@ -0,0 +1,115 @@ +export class LiturgyData { + + static ranks = ["I", "II", "III", "IV", "V", "VI", "VII", "VIII"] + + static #ranks = [ + {index: 0, name: "O", lkp: 3, mod: 2, costKaP: 2, costKaPPermant: 0, duration: "{*} KR", strength: "{*}/2"}, + {index: 1, name: "I", lkp: 3, mod: 0, costKaP: 5, costKaPPermant: 0, duration: "{*} KR", strength: "{*}/2"}, + { + index: 2, + name: "II", + lkp: 6, + mod: -2, + costKaP: 10, + costKaPPermant: 0, + duration: "{*}*10 KR", + strength: "{*}/2+5" + }, + {index: 3, name: "III", lkp: 9, mod: -4, costKaP: 15, costKaPPermant: 0, duration: "{*} SR", strength: "{*}+5"}, + { + index: 4, + name: "IV", + lkp: 12, + mod: -6, + costKaP: 20, + costKaPPermant: 0, + duration: "{*} Stunden", + strength: "{*}+10" + }, + { + index: 5, + name: "V", + lkp: 15, + mod: -8, + costKaP: 25, + costKaPPermant: 1, + duration: "{*} Tage", + strength: "{*}+15" + }, + { + index: 6, + name: "VI", + lkp: 18, + mod: -10, + costKaP: 30, + costKaPPermant: 3, + duration: "{*} Wochen", + strength: "{*}+20" + }, + { + index: 7, + name: "VII", + lkp: 21, + mod: -12, + costKaP: 35, + costKaPPermant: 5, + duration: "{*} Monate", + strength: "{*}+25" + }, + { + index: 8, + name: "VIII", + lkp: 24, + mod: -14, + costKaP: 40, + costKaPPermant: 7, + duration: "{*} Jahre oder permanent", + casttime: "", + strength: "{*}+30" + }, + ]; + + static alverans = [ + "Praios", + "Rondra", + "Efferd", + "Travia", + "Boron", + "Hesinde", + "Firun", + "Tsa", + "Phex", + "Peraine", + "Ingerimm", + "Rahja" + ] + + static #aliases = [ + { + "originalName": "Handwerkssegen", + "aliases": ["Cereborns Handreichung", "Hauch der Leidenschaft"] + }, + { + "originalName": "Heiliger Befehl", + "aliases": ["Wort der Wahrheit"], + }, + { + "originalName": "Eidsegen", + "aliases": ["Lehnseid"], + } + ] + + static getRankOfLiturgy(liturgy, deity) { + const lookupData = liturgy.herkunft.find(p => p.name === deity) + const rank = lookupData?.grad; + return LiturgyData.#ranks[rank]; + } + + static lookupAlias(alias) { + return LiturgyData.#aliases.find((entry) => { + console.log(alias, entry.aliases.indexOf(alias) !== -1) + return entry.aliases.indexOf(alias) !== -1 + })?.originalName ?? alias; // cant determine thus simply return the original query name + } + +} diff --git a/src/module/dialog/modify-liturgy.mjs b/src/module/dialog/modify-liturgy.mjs new file mode 100644 index 00000000..7228e3a8 --- /dev/null +++ b/src/module/dialog/modify-liturgy.mjs @@ -0,0 +1,89 @@ +import {LiturgyData} from "../data/miracle/liturgydata.mjs"; + +export class ModifyLiturgy { + + static data = {} + static naming = { + "range": "Reichweite", + "strength": "Wirkung", + "target": "Ziele", + "castduration": "Wirkzeit", + "duration": "Wirkdauer" + } + + constructor(data) { + ModifyLiturgy.data = data; + ModifyLiturgy.data.maxmods = Math.round(data.lkp / 3); + ModifyLiturgy.data.variation = null; + console.log("ModifyLiturgy constructed", data) + } + + static renderMods(html) { + + let result = ''; + + ModifyLiturgy.data.mods.forEach(((mod, index) => { + + result += `${LiturgyData.ranks[mod.rank]}${ModifyLiturgy.naming[mod.mod]}` + })) + + return result; + + } + + handleRender(html) { + + + html.off('click', 'input[name="data.variation"]') + html.on('click', 'input[name="data.variation"]', (evt) => { + if (evt.currentTarget.checked) { + ModifyLiturgy.data.variation = evt.currentTarget.dataset['rank']; + ModifyLiturgy.data.mods = []; + } + this.render(html) + }) + html.off('click', 'button[class="remove-mod"]') + html.on('click', 'button[class="remove-mod"]', (evt) => { + const {index} = evt.currentTarget.dataset; + ModifyLiturgy.data.mods.splice(index, 1); + this.render(html) + }) + html.off('change', 'select[name="mod"]') + html.on('change', 'select[name="mod"]', (evt) => { + const value = evt.currentTarget.value; + if (value === '') return; + const currentRank = ModifyLiturgy.data.mods.length + Number(ModifyLiturgy.data.rank); + ModifyLiturgy.data.mods.push({ + rank: currentRank, + mod: value, + }); + evt.currentTarget.value = ""; + this.render(html) + }) + + // render state + $('#mods', html).html(ModifyLiturgy.renderMods(html)) + + + // state handling + + if (ModifyLiturgy.data.mods.length === ModifyLiturgy.data.maxmods) { + $(".editor, .editor *", html).attr('disabled', 'disabled'); + $(".editor select", html).hide(); + $('span#info', html).text('LkW lässt keine weitere Modifikationen zu') + $("#mod_rank", html).text(LiturgyData.ranks[ModifyLiturgy.data.mods.length + Number(ModifyLiturgy.data.rank)]); + } else if (ModifyLiturgy.data.variation == null) { + $(".editor select *", html).attr('disabled', 'disabled'); + $(".editor select", html).hide(); + $('span#info', html).text('Keine Variante ausgewählt') + $("#mod_rank", html).text(''); + } else { + $(".editor, .editor *", html).removeAttr('disabled'); + $(".editor select", html).show(); + $('span#info', html).text('') + $("#mod_rank", html).text(''); + } + + } + +} diff --git a/src/module/documents/blessing.mjs b/src/module/documents/blessing.mjs new file mode 100644 index 00000000..e4e5a298 --- /dev/null +++ b/src/module/documents/blessing.mjs @@ -0,0 +1,9 @@ +export class Blessing extends Item { + /** + * Augment the basic Item data model with additional dynamic data. + */ + prepareData() { + super.prepareData(); + } + +} diff --git a/src/module/documents/character.mjs b/src/module/documents/character.mjs index 0a29ef58..67d20d9d 100644 --- a/src/module/documents/character.mjs +++ b/src/module/documents/character.mjs @@ -1,4 +1,5 @@ import {importCharacter} from "../xml-import/xml-import.mjs"; +import {LiturgyData} from "../data/miracle/liturgydata.mjs"; export class Character extends Actor { @@ -42,12 +43,29 @@ export class Character extends Actor { systemData.aup.max = Math.round((mu + ko + ge) / 2) + systemData.aup.mod; systemData.asp.max = Math.round((mu + _in + ch) / 2) + systemData.asp.mod; + systemData.at = Math.round((mu + ge + kk) / 5); systemData.pa = Math.round((_in + ge + kk) / 5); systemData.fk = Math.round((_in + ff + kk) / 5); systemData.ini.aktuell = Math.round((mu + mu + _in + ge) / 5) + systemData.ini.mod; systemData.mr.aktuell = Math.round((mu + kl + ko) / 5) + systemData.mr.mod; + // evaluate deities for KaP + + systemData.rs = 0; + + + systemData.kap.max = 0; + const deities = systemData.parent.items.filter(p => p.type === "Blessing") + + deities?.forEach((deity) => { + if (LiturgyData.alverans.includes(deity.system.gottheit)) { + systemData.kap.max = 24; + } else if (systemData.kap.max === 0) { + systemData.kap.max += 12; + } + }, 0) + } diff --git a/src/module/documents/liturgy.mjs b/src/module/documents/liturgy.mjs new file mode 100644 index 00000000..ff491d35 --- /dev/null +++ b/src/module/documents/liturgy.mjs @@ -0,0 +1,9 @@ +export class Liturgy extends Item { + /** + * Augment the basic Item data model with additional dynamic data. + */ + prepareData() { + super.prepareData(); + } + +} diff --git a/src/module/sheets/actions/action-manager.mjs b/src/module/sheets/actions/action-manager.mjs index 7c91c6b5..2aa31643 100644 --- a/src/module/sheets/actions/action-manager.mjs +++ b/src/module/sheets/actions/action-manager.mjs @@ -202,11 +202,11 @@ export class ActionManager { ] #hatSonderfertigkeitBeginnendMit(name) { - return this.actor.system.sonderfertigkeiten.find(p => p.name.startsWith(name)) != null + return this.actor.system.sonderfertigkeiten?.find(p => p.name.startsWith(name)) != null } #hatSonderfertigkeit(name) { - return this.actor.system.sonderfertigkeiten.find(p => p.name === name) != null + return this.actor.system.sonderfertigkeiten?.find(p => p.name === name) != null } evaluate() { diff --git a/src/module/sheets/characterSheet.mjs b/src/module/sheets/characterSheet.mjs index f8335ae8..4dd53336 100644 --- a/src/module/sheets/characterSheet.mjs +++ b/src/module/sheets/characterSheet.mjs @@ -1,5 +1,7 @@ import {PlayerCharacterDataModel} from "../data/character.mjs"; import {ActionManager} from "./actions/action-manager.mjs"; +import {LiturgyData} from "../data/miracle/liturgydata.mjs"; +import {ModifyLiturgy} from "../dialog/modify-liturgy.mjs"; export class CharacterSheet extends ActorSheet { /**@override */ @@ -23,26 +25,28 @@ export class CharacterSheet extends ActorSheet { return `systems/DSA_4-1/templates/actor/actor-character-sheet.hbs`; } - /** @override */ - async getData() { - const context = super.getData(); + static onDroppedData(actor, characterSheet, data) { + const uuid = foundry.utils.parseUuid(data.uuid); + const collection = uuid.collection.index ?? uuid.collection; + const document = CharacterSheet.getElementByName(collection, uuid.id); + const { + name, + type + } = document + console.log(name, type) + switch (type) { + case "Skill": + return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation + case "Advantage": + return characterSheet.#handleDroppedAdvantage(actor, document); + case "Equipment": + return characterSheet.#handleDroppedEquipment(actor, document); + case "Liturgy": + return characterSheet.#handleDroppedLiturgy(actor, document); + default: + return false; + } - - // Use a safe clone of the actor data for further operations. - const actorData = context.data; - - // Add the actor's data to context.data for easier access, as well as flags. - context.system = actorData.system; - context.flags = actorData.flags; - - this.#addSkillsToContext(context) - this.#addAdvantagesToContext(context) - this.#addAttributesToContext(context) - this.#addEquipmentsToContext(context) - await this.#addCombatStatistics(context) - this.#addActionsToContext(context) - this.#addSpellsToContext(context) - return context; } static getElementByName(collection, id) { @@ -101,6 +105,30 @@ export class CharacterSheet extends ActorSheet { return merkmale.split(",").map((merkmal) => merkmal.trim()) } + /** @override */ + async getData() { + const context = super.getData(); + + + // Use a safe clone of the actor data for further operations. + const actorData = context.data; + + // Add the actor's data to context.data for easier access, as well as flags. + context.system = actorData.system; + context.flags = actorData.flags; + context.derived = context.document.system; + + this.#addSkillsToContext(context) + this.#addAdvantagesToContext(context) + this.#addAttributesToContext(context) + this.#addEquipmentsToContext(context) + await this.#addCombatStatistics(context) + this.#addActionsToContext(context) + this.#addSpellsToContext(context) + this.#addLiturgiesToContext(context) + return context; + } + #addSpellsToContext(context) { const actorData = context.data; context.spells = []; @@ -127,6 +155,7 @@ export class CharacterSheet extends ActorSheet { }) } }) + context.hasSpells = context.spells.length > 0; } #addAttributesToContext(context) { @@ -566,6 +595,116 @@ export class CharacterSheet extends ActorSheet { } + #addLiturgiesToContext(context) { + const actorData = context.data; + context.liturgies = []; + context.blessings = []; + + Object.values(actorData.items).forEach((item, index) => { + if (item.type === "Blessing") { + context.blessings.push({ + deity: item.system.gottheit, + value: item.system.wert + }) + } + }) + Object.values(actorData.items).forEach((item, index) => { + if (item.type === "Liturgy") { + + context.blessings.forEach(({deity, value}) => { + let insertObject = context.liturgies.find(p => p.deity === deity); + if (!insertObject) { + insertObject = { + deity: deity, + lkp: value, + O: [], + I: [], + II: [], + III: [], + IV: [], + V: [], + VI: [], + VII: [], + VIII: [], + "NA": [], + countO: 1, + countI: 1, + countII: 1, + countIII: 1, + countIV: 1, + countV: 1, + countVI: 1, + countVII: 1, + countVIII: 1, + countNA: 0, + total: 3, + + } + context.liturgies.push(insertObject); + } + + // sort by rank + const rankData = LiturgyData.getRankOfLiturgy(item.system, deity) + if (rankData) { + let {index, name, lkp, mod, costKaP} = rankData; + + insertObject["count" + name] = insertObject["count" + name] + 1; + + insertObject[name].push({ + id: item._id, + name: item.name, + lkpReq: lkp, + lkpMod: mod, + costKaP, + rank: index, // get effective liturgy rank based on deity + liturgiekenntnis: deity, + }) + insertObject.total = insertObject.total + 1; + + } + }) + } + }) + + + // clean up counter + Object.values(context.liturgies).forEach((litObject) => { + + if (litObject.O.length === 0) litObject.countO = false; + if (litObject.I.length === 0) litObject.countI = false; + if (litObject.II.length === 0) litObject.countII = false; + if (litObject.III.length === 0) litObject.countIII = false; + if (litObject.IV.length === 0) litObject.countIV = false; + if (litObject.V.length === 0) litObject.countV = false; + if (litObject.VI.length === 0) litObject.countVI = false; + if (litObject.VII.length === 0) litObject.countVII = false; + if (litObject.VIII.length === 0) litObject.countVIII = false; + if (litObject.NA.length === 0) litObject.countNA = false; + + + }) + + context.hasLiturgies = context.blessings.length > 0; + } + + #handleDroppedSkill(actor, skill) { + const array = Array.from(actor.items); + for (let i = 0; i < array.length; i++) { + if (array[i].name === skill.name) { + return false; + } + } + } + + #handleDroppedAdvantage(actor, advantage) { + const array = Array.from(actor.items); + for (let i = 0; i < array.length; i++) { + if (array[i].name === advantage.name) { // TODO: adjust for uniqueness + return false; + } + } + } + activateListeners(html) { super.activateListeners(html); @@ -698,57 +837,77 @@ export class CharacterSheet extends ActorSheet { } ]); - } + html.on('click', '.liturgy.rollable', async (evt) => { - #handleDroppedSkill(actor, skill) { - const array = Array.from(actor.items); - for (let i = 0; i < array.length; i++) { - if (array[i].name === skill.name) { - return false; - } - } - } + evt.stopPropagation(); - #handleDroppedAdvantage(actor, advantage) { - const array = Array.from(actor.items); - for (let i = 0; i < array.length; i++) { - if (array[i].name === advantage.name) { // TODO: adjust for uniqueness - return false; + const {id, rank, lkp, deity} = evt.currentTarget.dataset; + const document = await this.object.items.get(id) + + const data = {}; + + data.rank = rank; + data.lkp = lkp; + data.deity = deity; + data.variations = []; + const ranks = LiturgyData.ranks + ranks.forEach(rank => { + if (document.system.auswirkung[rank]) { + data.variations.push({ + rank, + effect: document.system.auswirkung[rank] + }) + } + }) + data.mods = []; + + const htmlContent = await renderTemplate('systems/DSA_4-1/templates/dialog/modify-liturgy.hbs', data); + + const dialogData = { + title: document.name, + content: htmlContent, + data: {}, + buttons: { + submit: { + label: "Wirken", + icon: '', + callback: (html) => { + }, + }, + }, } - } + dialogData.render = new ModifyLiturgy(data).handleRender + + const dialog = new Dialog(dialogData, { + classes: ['dsa41', 'dialog', 'liturgy'], + height: 480 + }) + + dialog.render(true); + + return false; + }) + + + } #handleDroppedEquipment(actor, equipment) { const array = Array.from(actor.items); for (let i = 0; i < array.length; i++) { if (array[i].name === equipment.name) { // TODO: adjust item quantity if item is the same - console.log(equipment); return false; } } } - static onDroppedData(actor, characterSheet, data) { - const uuid = foundry.utils.parseUuid(data.uuid); - const collection = uuid.collection.index ?? uuid.collection; - const document = CharacterSheet.getElementByName(collection, uuid.id); - const { - name, - type - } = document - console.log(name, type) - switch (type) { - case "Skill": - return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation - case "Advantage": - return characterSheet.#handleDroppedAdvantage(actor, document); - case "Equipment": - return characterSheet.#handleDroppedEquipment(actor, document); - - default: + #handleDroppedLiturgy(actor, liturgy) { + const array = Array.from(actor.items); + for (let i = 0; i < array.length; i++) { + if (array[i].name === liturgy.name) { // TODO: allow multiple miracles with the same name return false; + } } - } } diff --git a/src/module/sheets/liturgySheet.mjs b/src/module/sheets/liturgySheet.mjs new file mode 100644 index 00000000..ec850750 --- /dev/null +++ b/src/module/sheets/liturgySheet.mjs @@ -0,0 +1,48 @@ +export class LiturgySheet extends ItemSheet { + /**@override */ + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ['dsa41', 'sheet', 'item', 'liturgy'], + width: 520, + height: 480, + tabs: [ + { + navSelector: '.sheet-tabs', + contentSelector: '.sheet-body', + initial: 'description', + }, + ], + }); + } + + /** @override */ + get template() { + return `systems/DSA_4-1/templates/item/item-liturgy-sheet.hbs`; + } + + /** @override */ + getData() { + // Retrieve the data structure from the base sheet. You can inspect or log + // the context variable to see the structure, but some key properties for + // sheets are the actor object, the data object, whether or not it's + // editable, the items array, and the effects array. + const context = super.getData(); + + // Use a safe clone of the actor data for further operations. + const liturgyData = context.data; + + // Add the actor's data to context.data for easier access, as well as flags. + context.system = liturgyData.system; + context.flags = liturgyData.flags; + context.json = JSON.stringify(liturgyData); + return context; + } + + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.isEditable) + } + +} diff --git a/src/module/xml-import/xml-import.mjs b/src/module/xml-import/xml-import.mjs index 0f2b15a6..66eb09d2 100644 --- a/src/module/xml-import/xml-import.mjs +++ b/src/module/xml-import/xml-import.mjs @@ -1,3 +1,7 @@ +import {LiturgyData} from "../data/miracle/liturgydata.mjs"; +import {BlessingDataModel} from "../data/blessing.mjs"; +import {Blessing} from "../documents/blessing.mjs"; + let months = [ "Praios", "Rondra", @@ -71,7 +75,7 @@ function getJsonFromXML(dom) { } async function addSkillFromCompendiumByNameToActor(talentName, taw, actor) { - const compendiumOfSkills = game.packs.get('DSA_4-1.talente-brw'); + const compendiumOfSkills = game.packs.get('DSA_4-1.talente'); const talentId = compendiumOfSkills.index.find(skill => skill.name === talentName) if (talentId) { @@ -119,6 +123,23 @@ async function addSpellsFromCompendiumByNameToActor(spellName, zfw, representati } } +async function addLiturgiesFromCompendiumByNameToActor(liturgyName, actor) { + const compendiumOfLiturgies = game.packs.get('DSA_4-1.liturgien'); + const liturgyId = compendiumOfLiturgies.index.find(liturgy => { + return liturgy.name === LiturgyData.lookupAlias(liturgyName.split(" (")[0]) + }) + if (liturgyId) { + + const liturgy = await compendiumOfLiturgies.getDocument(liturgyId._id); + + try { + await actor.createEmbeddedDocuments('Item', [liturgy]) + } catch (error) { + console.error(`${liturgy} not found in items`, error) + } + } +} + /** * gets the text content of a file * @param file the file with the desired content @@ -153,7 +174,25 @@ function calculateBirthdate(json) { function mapSkills(actor, held) { for (let talent in held.talentliste.talent) { talent = held.talentliste.talent[talent] - addSkillFromCompendiumByNameToActor(talent.name, talent.value, actor) + + // hook liturgy + if (talent.name.startsWith("Liturgiekenntnis")) { + + actor.createEmbeddedDocuments('Item', [ + new Blessing({ + name: talent.name, + type: "Blessing", + system: { + gottheit: new RegExp("\\((.+)\\)").exec(talent.name)[1], + wert: talent.value + } + }) + ]) + + } else { + // proceed + addSkillFromCompendiumByNameToActor(talent.name, talent.value, actor) + } } } @@ -171,6 +210,13 @@ function mapSpells(actor, held) { } } +function mapMiracles(actor, liturgies) { + for (let liturgy in liturgies) { + liturgy = liturgies[liturgy] + addLiturgiesFromCompendiumByNameToActor(liturgy.name, actor) + } +} + /** * parses a json into a fitting character-json * @param rawJson the json parsed from the Helden-Software XML @@ -291,6 +337,8 @@ function mapRawJson(actor, rawJson) { mapSkills(actor, held) mapSpells(actor, held) + mapMiracles(actor, liturgies) + let combatValues = [] for (let combatValue in held.kampf.kampfwerte) { combatValue = held.kampf.kampfwerte[combatValue] diff --git a/src/style/_character-sheet.scss b/src/style/_character-sheet.scss index 3417b8bd..44a35047 100644 --- a/src/style/_character-sheet.scss +++ b/src/style/_character-sheet.scss @@ -104,46 +104,37 @@ .tab.combat { - .initiaitve { - width: 100%; - height: 48px; - position: relative; - label { - width: 80px; - line-height: 48px; - vertical-align: middle; - } - - input { - display: inline-block; - width: 40px; - height: 48px; - } - - span.inline { - line-height: 48px; - vertical-align: middle; - width: 40px; - text-align: center; - } - - } } .tab.spells { + tr { + height: 24px; + margin: 0; + padding: 0; + } + + td { + margin: 0; + padding: 0; + height: 24px; + } + + $color: #05f; + .spell.rollable svg { width: 24px; height: 24px; + top: 1px; + z-index: 1; + position: relative; .border { fill: #0000; } - $color: #05f; - .center { fill: $color; stroke: colour.$rollable-die-border-color; @@ -170,6 +161,46 @@ } } + .die-column { + width: 24px; + } + + .clickable { + span { + position: relative; + z-index: 1; + } + } + + tbody { + + tr { + + position: relative; + + &::after { + content: ''; + background-image: linear-gradient(to right, rgba(color.scale($color, $lightness: numbers.$zebra_light), numbers.$start_gradient), rgba(color.scale($color, $lightness: numbers.$zebra_light), numbers.$end_2_gradient)); + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + position: absolute; + top: 2px; + left: 12px; + bottom: 2px; + right: 33%; + z-index: 0; + pointer-events: none; + } + + &:nth-child(odd) { + &::after { + background-image: linear-gradient(to right, rgba(color.scale($color, $lightness: numbers.$zebra_dark), numbers.$start_gradient), rgba(color.scale($color, $lightness: numbers.$zebra_dark), numbers.$end_2_gradient)); + } + } + } + + } + .merkmal-list { list-style: none; margin: 0; @@ -184,6 +215,70 @@ } + .tab.liturgies { + + table { + border-top: unset; + border-bottom: unset; + position: relative; + } + + .liturgy-header { + background: unset; + border: unset; + + tr { + height: 90px; + + th { + vertical-align: middle; + color: black; + text-shadow: 2px 2px 1px rgba(0, 0, 0, 0.2); + } + } + } + + td, th { + padding-left: 8px; + } + } + + .tab-resources { + display: flex; + justify-content: center; + gap: 0 16px; + padding-bottom: 8px; + + & > div { + + + height: 48px; + position: relative; + + label { + width: 80px; + line-height: 48px; + vertical-align: middle; + } + + input { + display: inline-block; + width: 40px; + height: 48px; + } + + span.inline { + line-height: 48px; + vertical-align: middle; + width: 40px; + text-align: center; + } + + + } + + } + } } diff --git a/src/style/_liturgy-banner.scss b/src/style/_liturgy-banner.scss new file mode 100644 index 00000000..a6d30efb --- /dev/null +++ b/src/style/_liturgy-banner.scss @@ -0,0 +1,247 @@ +@use "sass:map"; +@use "sass:color"; +@use "colours" as colour; +@use "numbers"; + +$deity_colours_border: ( + "Praios": orange, + "Rondra": red, + "Efferd": #74d0ec, + "Travia": #c6491a, + "Boron": #515151, + "Hesinde": #089e08, + "Firun": #8fdeff, + "Tsa": #6a24d8, + "Phex": #1569da, + "Peraine": #56a116, + "Ingerimm": #a53d19, + "Rahja": #ed70c2, +); +$deity_colours_tint: ( + "Praios": rgba(orange, 0.5), + "Rondra": rgba(red, 0.5), + "Efferd": rgba(#74d0ec, 0.5), + "Travia": rgba(#c6491a, 0.5), + "Boron": rgba(#515151, 0.5), + "Hesinde": rgba(#089e08, 0.5), + "Firun": rgba(#8fdeff, 0.5), + "Tsa": linear-gradient( + -45deg, + rgba(255, 0, 0, 0.5) 0%, + rgba(255, 154, 0, 0.5) 10%, + rgba(208, 222, 33, 0.5) 20%, + rgba(79, 220, 74, 0.5) 30%, + rgba(63, 218, 216, 0.5) 40%, + rgba(47, 201, 226, 0.5) 50%, + rgba(28, 127, 238, 0.5) 60%, + rgba(95, 21, 242, 0.5) 70%, + rgba(186, 12, 248, 0.5) 80%, + rgba(251, 7, 217, 0.5) 90%, + rgba(255, 0, 0, 0.5) 100% + ), + "Phex": rgba(#1569da, 0.5), + "Peraine": rgba(#56a116, 0.5), + "Ingerimm": rgba(#a53d19, 0.5), + "Rahja": rgba(#ed70c2, 0.5), +); + +@mixin coloring($name) { + $color: map.get($deity_colours_border, $name); + $color-tint: map.get($deity_colours_tint, $name); + &.#{$name} { + + th.background { + &::after { + background: $color-tint; + } + } + + .banner-bot { + border-color: $color; + } + + .banner-mid { + border-color: $color; + } + + .banner-top { + &::before, &::after { + border-color: $color; + } + } + } +} + +.tab.liturgies { + table { + + @include coloring('Praios'); + @include coloring('Rondra'); + @include coloring('Efferd'); + @include coloring('Travia'); + @include coloring('Boron'); + @include coloring('Hesinde'); + @include coloring('Firun'); + @include coloring('Tsa'); + @include coloring('Phex'); + @include coloring('Peraine'); + @include coloring('Ingerimm'); + @include coloring('Rahja'); + + tr { + th.background { + + &::before { + position: absolute; + content: ''; + background-image: url("../assets/velvet_strip.png"); + background-repeat: repeat-y; + background-size: cover; + width: 86px; + height: 100%; + + top: 45px; + left: 12px; + } + + &::after { /* for tinting the texture */ + content: ""; + position: absolute; + width: 86px; + height: 100%; + + top: 45px; + left: 12px; + } + } + } + + .banner-top { + position: relative; + + width: 90px; + + img { + position: absolute; + top: 2px; + left: 1px; + width: 90px; + height: 90px; + border: 0; + z-index: 1; + } + + &::after { + z-index: 0; + border-width: 0 4px 0 4px; + //background-color: #64b; + border-style: solid; + position: absolute; + content: ""; + left: -2px; + top: 45px; + bottom: 0; + width: 94px; + } + + &::before { + position: absolute; + border-radius: 45px; + height: 94px; + width: 94px; + content: ''; + left: -2px; + right: -2px; + top: 0; + border-width: 4px; + border-style: solid; + z-index: 2; + } + } + + .banner-mid { + position: relative; + border-width: 0 4px 0 4px; + //background-color: #64b; + border-style: solid; + width: 90px; + + div { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + display: flex; + justify-content: center; + align-items: center; + + div.rank-label { + position: absolute; + left: 2px; + right: 2px; + top: 0; + bottom: 0; + border-bottom: 2px solid black; + color: gold; + text-shadow: 2px 2px 1px black; + } + } + } + + .banner-bot { + position: relative; + border-width: 4px; + border-style: solid; + width: 90px; + height: 12px; + } + } + + .liturgy.rollable { + width: 24px; + + svg { + $color: #e4de61; + width: 24px; + height: 24px; + top: 1px; + z-index: 1; + position: relative; + + .border { + fill: #0000; + } + + .center { + fill: $color; + stroke: colour.$rollable-die-border-color; + } + + .topleft { + fill: color.adjust($color, $lightness: numbers.$lighter_factor); + stroke: colour.$rollable-die-border-color; + } + + .bottomleft { + fill: color.adjust($color, $lightness: numbers.$lightest_factor); + stroke: colour.$rollable-die-border-color; + } + + .topright { + fill: color.adjust($color, $lightness: numbers.$darken_factor); + stroke: colour.$rollable-die-border-color; + } + + .bottomright, .bottom { + fill: color.adjust($color, $lightness: numbers.$darkest_factor); + stroke: colour.$rollable-die-border-color; + } + } + } + + .clickable:hover { + text-shadow: 0 0 10px rgb(255 0 0); + } + +} diff --git a/src/style/_modify-liturgy.scss b/src/style/_modify-liturgy.scss new file mode 100644 index 00000000..86414daa --- /dev/null +++ b/src/style/_modify-liturgy.scss @@ -0,0 +1,45 @@ +.dsa41.dialog.liturgy { + + table { + tr { + th:first-child { + width: 48px; + } + } + } + + table#mods { + + tr { + th:first-child { + width: 48px; + } + } + + .remove-mod { + width: 32px; + height: 32px; + } + + } + + .editor { + + display: grid; + grid-template-columns: 48px 1fr; + + #mod_rank { + display: inline-block; + width: 48px; + } + + select { + + } + + } + + .dialog-buttons { + flex: 0; + } +} diff --git a/src/style/_numbers.scss b/src/style/_numbers.scss index 6781ec4b..e79d87d1 100644 --- a/src/style/_numbers.scss +++ b/src/style/_numbers.scss @@ -4,6 +4,7 @@ $darken_factor: -15%; $darkest_factor: -40%; $start_gradient: 0.8; $end_gradient: 0.2; +$end_2_gradient: 0; $direction_gradient: 90deg; $tab-border-width: 1px; @@ -17,3 +18,6 @@ $dice-box-border-width: 1px; $pill-box-inset: 2px; $pill-box-blur-radius: 4px; + +$zebra-dark: 0%; +$zebra-light: 20%; diff --git a/src/style/styles.scss b/src/style/styles.scss index 7f4dbfbe..e11377af 100644 --- a/src/style/styles.scss +++ b/src/style/styles.scss @@ -9,3 +9,5 @@ @use "_paperdoll"; @use "_creature-sheet"; @use "_player-action"; +@use "_modify-liturgy"; +@use "_liturgy-banner"; diff --git a/src/system.json b/src/system.json index 5129e5ca..ffd4083f 100644 --- a/src/system.json +++ b/src/system.json @@ -26,11 +26,11 @@ ], "packs": [ { - "name": "talente-brw", - "label": "Talente (BRW)", + "name": "talente", + "label": "Talente", "system": "DSA_4-1", "type": "Item", - "path": "packs/talente-brw", + "path": "packs/talente", "private": false }, { @@ -183,6 +183,8 @@ "voraussetzung" ] }, + "Blessing": {}, + "Liturgy": {}, "Spell": { "stringFields": [ "name", diff --git a/src/templates/actor/actor-character-sheet.hbs b/src/templates/actor/actor-character-sheet.hbs index 87404223..ac7bc9db 100644 --- a/src/templates/actor/actor-character-sheet.hbs +++ b/src/templates/actor/actor-character-sheet.hbs @@ -56,8 +56,8 @@ Kampf Talente Inventar - Zauber - Liturgien + {{#if this.hasSpells}}Zauber{{/if}} + {{#if this.hasLiturgies}}Liturgien{{/if}} Begleiter @@ -88,7 +88,9 @@
-
+
+ +
w6 @@ -106,6 +108,11 @@ von
+
+ + {{derived.rs}} +
+
{{#each this.actions}} @@ -253,12 +260,27 @@
+ {{#if this.hasSpells}}
+
+ +
+ + + von + {{derived.asp.max}} +
+
+ + {{derived.mr.aktuell}} +
+
+ - + @@ -271,7 +293,8 @@ - + @@ -286,9 +309,188 @@
Zaubername Probe ZfW {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} {{this.name}} + {{this.name}} {{this.eigenschaft1}} {{this.eigenschaft2}} {{this.eigenschaft3}}
-
+ {{/if}} + {{#if this.hasLiturgies}} +
+ +
+ +
+ + + von + {{derived.kap.max}} +
+
+ + {{#each this.liturgies}} + + + + + + + + + + {{#if this.countI}} + + + + + {{#each this.I}} + + + + + + {{/each}} + {{/if}} + {{#if this.countII}} + + + + {{#each this.II}} + + + + + {{/each}} + {{/if}} + {{#if this.countIII}} + + + + {{#each this.III}} + + + + + {{/each}} + {{/if}} + {{#if this.countIV}} + + + + {{#each this.IV}} + + + + + {{/each}}{{/if}} + {{#if this.countV}} + + + + {{#each this.V}} + + + + + {{/each}} + {{/if}} + {{#if this.countVI}} + + + + {{#each this.VI}} + + + + + {{/each}} + {{/if}}{{#if this.countVII}} + + + + {{#each this.VII}} + + + + + {{/each}} + {{/if}}{{#if this.countVIII}} + + + + {{#each this.VIII}} + + + + + {{/each}} + {{/if}} + +
Liturgiekenntnis: {{this.lkp}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + + {{this.name}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + {{this.name}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + {{this.name}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + {{this.name}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + {{this.name}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + {{this.name}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + {{this.name}}
+ {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + {{this.name}}
+ {{/each}}
+ {{/if}}
diff --git a/src/templates/dialog/modify-liturgy.hbs b/src/templates/dialog/modify-liturgy.hbs new file mode 100644 index 00000000..ebf2fbdd --- /dev/null +++ b/src/templates/dialog/modify-liturgy.hbs @@ -0,0 +1,36 @@ + + + + + + + {{#each variations}} + + + + + {{/each}} + + +
GradWirkung
{{this.effect}}
+ +

Modifizieren

+ +
+ +
+
+
+ + +
+
+ +
diff --git a/src/templates/item/item-liturgy-sheet.hbs b/src/templates/item/item-liturgy-sheet.hbs new file mode 100644 index 00000000..d50d00ce --- /dev/null +++ b/src/templates/item/item-liturgy-sheet.hbs @@ -0,0 +1,14 @@ +
+ + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
+
+
{{json}}
+
+
+
diff --git a/transformSources.mjs b/transformSources.mjs index 2c3319e7..28944b31 100644 --- a/transformSources.mjs +++ b/transformSources.mjs @@ -1,6 +1,6 @@ let crypto; -import {readdirSync, readFileSync, writeFileSync} from "fs"; +import {readdirSync, readFileSync, writeFileSync, rmdirSync, rmSync, mkdirSync} from "fs"; import {join} from "path"; try { @@ -46,18 +46,20 @@ try { } delete targetSource.system.image; let target = JSON.stringify(targetSource, null, 2); - let newFileName = "./" + join(DEST, targetSource.name.toLowerCase().replace(/[ /]/g, "-").replace(/\--{2,}/g, "-").replace(/[.,!]/g, "").trim() + ".json"); + let newFileName = "./" + join(DEST, id + ".json"); console.log(newFileName); writeFileSync(newFileName, target, {encoding: "utf8"}); }); } + convert("./src/packs/_source/talente", "./src/packs/__source/talente", "Skill"); convert("./src/packs/_source/zauber", "./src/packs/__source/zauber", "Spell"); convert("./src/packs/_source/vorteile", "./src/packs/__source/vorteile", "Advantage"); convert("./src/packs/_source/waffen", "./src/packs/__source/waffen", "Equipment"); convert("./src/packs/_source/munition", "./src/packs/__source/munition", "Equipment"); convert("./src/packs/_source/ruestzeug", "./src/packs/__source/ruestzeug", "Equipment"); + convert("./src/packs/_source/liturgien-und-segnungen", "./src/packs/__source/liturgien", "Liturgy"); } catch (err) { console.error(err);