From faa3bc13280d377dae16d5dca64c61c8d3a1f126 Mon Sep 17 00:00:00 2001 From: macniel Date: Thu, 9 Oct 2025 21:16:51 +0200 Subject: [PATCH] feature/meta (#54) implements styling of meta and various other segments e.g. sheets --- src/assets/gradient.png | Bin 0 -> 500 bytes src/assets/velvet_button.png | Bin 0 -> 1275 bytes src/main.mjs | 22 +- src/module/data/activeeffect.mjs | 49 ++++ src/module/data/character.mjs | 29 ++- src/module/data/specialAbility.mjs | 12 + src/module/documents/character.mjs | 40 ++- src/module/documents/specialAbility.mjs | 9 + src/module/sheets/ActiveEffectSheet.mjs | 44 ++++ src/module/sheets/characterSheet.mjs | 174 +++++++++---- src/module/sheets/creatureSheet.mjs | 8 +- src/module/sheets/liturgySheet.mjs | 4 +- src/module/sheets/specialAbilitySheet.mjs | 51 ++++ src/module/sheets/vornachteilSheet.mjs | 12 +- src/module/xml-import/xml-import.mjs | 6 +- src/style/_active-effect-sheet.scss | 38 +++ src/style/_advantage-sheet.scss | 48 ++++ src/style/_character-sheet.scss | 182 ++++++++++++++ src/style/_equipment-sheet.scss | 1 - src/style/_sidebar-elements.scss | 31 ++- src/style/_talent-sheet.scss | 51 ++++ src/style/styles.scss | 3 + src/system.json | 11 +- src/templates/actor/actor-character-sheet.hbs | 232 ++++++++++++++++-- .../item/item-activeeffect-sheet.hbs | 20 ++ src/templates/item/item-advantage-sheet.hbs | 27 +- src/templates/item/item-skill-sheet.hbs | 65 ++--- src/templates/ui/partial-equipment-button.hbs | 2 +- src/templates/ui/partial-sf-button.hbs | 3 + 29 files changed, 1046 insertions(+), 128 deletions(-) create mode 100644 src/assets/gradient.png create mode 100644 src/assets/velvet_button.png create mode 100644 src/module/data/activeeffect.mjs create mode 100644 src/module/data/specialAbility.mjs create mode 100644 src/module/documents/specialAbility.mjs create mode 100644 src/module/sheets/ActiveEffectSheet.mjs create mode 100644 src/module/sheets/specialAbilitySheet.mjs create mode 100644 src/style/_active-effect-sheet.scss create mode 100644 src/style/_advantage-sheet.scss create mode 100644 src/style/_talent-sheet.scss create mode 100644 src/templates/item/item-activeeffect-sheet.hbs create mode 100644 src/templates/ui/partial-sf-button.hbs diff --git a/src/assets/gradient.png b/src/assets/gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef0f8613da07a637e35112c78b6a3a53d143922 GIT binary patch literal 500 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=E8oo-U3d6?5L++{oMPAmHkF z+lnb@-G4X51F1P5(zJFwc$qDD|Fg|~{>be2wtv~!I2HUD3Yj{D9p*4RViDNB?)9uX z=4Krb?^>bE<@|&T`M(&?M7@6l?tX u-tpDu{1<77E$@EM0|pb+rKmypo!whsp(X3zt|!14WAJqKb6Mw<&;$Sn4x0Dw{owAT3k{)Tf7tu-j6kaLE$7W=-# z7=v|PQEQd=*XtD@A0JRkp|ys2p7Htl31bX?e`>9;)?)C__8$9bvs;+~nY&%eSQz7-OK6 z`t#CSXst0#lPt!j8Y0h2;yFxb7HV_PR3DnjA=1O?E8*V3Sx}1(ZN5LZ?)ElF&^ASDFv-H zq?8V^g)WM!T-Oz)6r_}pQo=M%2jji>*tSg)8oD;EwWxod=fOXbZ|N3NN@%U2wRT8M zxELq(D%Dy;YmIH&BvGO9{NFhzZih;xdkP`odc6+*iG2I@^(6@iVN?g7t+k>aV~n__ z-|p&nX+t+uN|8A+M#*-KF&O-F`Id1`@UnI=f7f7Uz*Txx*)<@Ysfjv7(VBmG5F{5&C}D<8yf`H zT2Y}=3YW_T_xt@&Y7nfomc(V6CUGnL_TEdZ>Ejs75^|`{!9Rzut+kR__D1z}q_q~0 zkB{J3=iDLV)LKh|LDhy3M0M6$EXx7_n5IeG6Ppr)e;!}6JlOXgIp;%N$`&I4yuZJT z!X6$T4zVPJAj)Lc>72vmauHVsK-!kfMElOw;Ge|bwoR00t;P5EH>PPqYYmsn1tA29 zBOF6@dhaC$g%D&O;|$lLd*g=Lh)_yl@XzIIYptk^r37OQW682CSl3m4XLEsFCeH24 zlh&HRdzZ#6bNniM<;Sgh#?6j{fIk`W(?jclN|IAF4haR<7*({qJgMS`hl0dqZzE|9< zjEMyeY^@>2D79s;Qs#yA)y>nZ!9S00US3|_Qc98)w$>zLq)Xz5u_UF07^56wutoVh8DS#B z(t~S|Q)|Wbdd1+M$G3DRY(%iH%-Xb18v1gD#`UL9bX}}h`z}}C&*s?Poelmue096s z8naHi5N4A6BZQ|^>$=LlG8)|XrrFcxb*29@wBPLDpTpN$Yk2SBy%)Eml)^mEvPgeS z$@^zgNB*2i^Y%%9Gleb5ecz=)!Fg2V;GfR_qZF1T(= { Advantage: VornachteileDataModel, Equipment: EquipmentDataModel, Liturgy: LiturgyDataModel, - Blessing: BlessingDataModel + Blessing: BlessingDataModel, + SpecialAbility: SpecialAbilityDataModel, + ActiveEffect: ActiveEffectDataModel, } CONFIG.Combat.initiative = { @@ -100,15 +107,26 @@ Hooks.once("init", () => { }) Items.registerSheet('dsa41.equipment', AusruestungSheet, { types: ["Equipment"], - makeDefault: true, + makeDefault: false, label: 'DSA41.AusruestungLabels.Item' }) Items.registerSheet('dsa41.liturgy', LiturgySheet, { + types: ["SpecialAbility"], + makeDefault: true, + label: 'DSA41.SpecialAbilityLabels.Item' + }) + Items.registerSheet('dsa41.specialAbility', SpecialAbilitySheet, { types: ["Liturgy"], makeDefault: true, label: 'DSA41.LiturgyLabels.Item' }) + Items.registerSheet('dsa41.activeEffect', ActiveEffectSheet, { + types: ['ActiveEffect'], + makeDefault: true, + label: 'DSA41.ActiveEffectLabels.ActiveFfect' + }) + return preloadHandlebarsTemplates(); }) diff --git a/src/module/data/activeeffect.mjs b/src/module/data/activeeffect.mjs new file mode 100644 index 00000000..929cb489 --- /dev/null +++ b/src/module/data/activeeffect.mjs @@ -0,0 +1,49 @@ +import BaseItem from "./base-item.mjs"; + +const {ArrayField, NumberField, StringField, HTMLField} = foundry.data.fields; + +export class ActiveEffectDataModel extends BaseItem { + + static defineSchema() { + return { + name: new StringField({required: true}), + notes: new HTMLField(), + } + /* + + name: String, // Name of Vornachteil will be used for rendering and referencing by other Items + description: HTMLString, // only used for rendering + variant: [String]?, // variant name of Vornachteil e.g. "Mut" in the case of "Herausragende Eigenschaft" + levels: [Number]?, // available levels e.g. 1, 2 in the case of "Flink" + mods: [ + { + level: Number?, // in reference to level of the Vornachteil, is null when it does not have any levels or is the only modification + field: String, // Reference to Actor Data e.g. "FF" maps to "FF.mod" + value: Number, // value of the Modification e.g. "+2" maps to 2 + requirement: { + field: String // Reference to Actor Data e.g. "BE" maps "be.aktuell" + operation: String // Supported: "<=", ">" + value: Number // Target Value the referenced field has to compare against + }? // optional when the mod does not have an active requirement + } + ] + + */ + } + + _onCreate(data, options, userId) { + super._onCreate(data, options, userId); + if (this.parent.getEmbeddedCollection("ActiveEffect").contents.length === 0) { + + + this.parent.createEmbeddedDocuments("ActiveEffect", [{ + name: data.name, + changes: [], + duration: {}, + icon: this.img, + }]); + console.log("added default activeffect"); + } + } + +} diff --git a/src/module/data/character.mjs b/src/module/data/character.mjs index dfa814e1..1c257cab 100644 --- a/src/module/data/character.mjs +++ b/src/module/data/character.mjs @@ -1,5 +1,12 @@ const { - SchemaField, NumberField, StringField, EmbeddedDocumentField, DocumentIdField, ArrayField, ForeignDocumentField + SchemaField, + NumberField, + StringField, + HTMLField, + EmbeddedDocumentField, + DocumentIdField, + ArrayField, + ForeignDocumentField } = foundry.data.fields; export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel { @@ -13,16 +20,17 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel { professions: new ArrayField(new StringField()), geschlecht: new StringField(), haarfarbe: new StringField(), - groesse: new NumberField({required: true, integer: false}), + groesse: new StringField(), augenfarbe: new StringField(), geburtstag: new StringField(), - alter: new NumberField({required: true, integer: true}), - gewicht: new NumberField({required: true, integer: true}), - aussehen: new ArrayField(new StringField()), - familie: new ArrayField(new StringField()), + alter: new StringField(), + gewicht: new StringField(), + aussehen: new HTMLField(), + familie: new HTMLField(), titel: new StringField(), stand: new StringField(), }), + setEquipped: new NumberField({required: true, initial: 0, max: 3, integer: true}), ini: new SchemaField({ aktuell: new NumberField({required: true, integer: true, initial: 0}), mod: new NumberField({required: true, integer: true, initial: 0}), @@ -144,27 +152,22 @@ export class PlayerCharacterDataModel extends foundry.abstract.TypeDataModel { _initializeSource(data, options) { if (data.heldenausruestung.length === 0) { let sets = []; - for (let i = 0; i < 3; i++) { - const preppedSet = {} - PlayerCharacterDataModel.getSlots().forEach(slot => { preppedSet[slot] = null; }) - sets.push(preppedSet); - } data.heldenausruestung = sets - } - return super._initializeSource(data, options); } async _onCreate(data, options, userId) { + console.log(data, 'onCreate'); + } static getSlots() { diff --git a/src/module/data/specialAbility.mjs b/src/module/data/specialAbility.mjs new file mode 100644 index 00000000..cd143ca6 --- /dev/null +++ b/src/module/data/specialAbility.mjs @@ -0,0 +1,12 @@ +import BaseItem from "./base-item.mjs"; + +const {BooleanField, NumberField, SchemaField, ArrayField, StringField, HTMLField} = foundry.data.fields; + +export class SpecialAbilityDataModel extends BaseItem { + + static defineSchema() { + return { + name: new StringField() + } + } +} diff --git a/src/module/documents/character.mjs b/src/module/documents/character.mjs index 08d1c6bc..4393b264 100644 --- a/src/module/documents/character.mjs +++ b/src/module/documents/character.mjs @@ -50,14 +50,50 @@ export class Character extends Actor { 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.be = 0; + + // map current set to RS and BE + + const ausruestung = systemData.heldenausruestung[systemData.setEquipped]; + if (ausruestung) { + if (ausruestung.brust) { + systemData.rs += systemData.parent.items.get(ausruestung.brust).system.armorValue ?? 0 + systemData.be += systemData.parent.items.get(ausruestung.brust).system.armorHandicap ?? 0 + } + if (ausruestung.bauch) { + systemData.rs += systemData.parent.items.get(ausruestung.bauch).system.armorValue ?? 0 + systemData.be += systemData.parent.items.get(ausruestung.bauch).system.armorHandicap ?? 0 + } + if (ausruestung.ruecken) { + systemData.rs += systemData.parent.items.get(ausruestung.ruecken).system.armorValue ?? 0 + systemData.be += systemData.parent.items.get(ausruestung.ruecken).system.armorHandicap ?? 0 + } + if (ausruestung.armlinks) { + systemData.rs += systemData.parent.items.get(ausruestung.armlinks).system.armorValue ?? 0 + systemData.be += systemData.parent.items.get(ausruestung.armlinks).system.armorHandicap ?? 0 + } + if (ausruestung.armrechts) { + systemData.rs += systemData.parent.items.get(ausruestung.armrechts).system.armorValue ?? 0 + systemData.be += systemData.parent.items.get(ausruestung.armrechts).system.armorHandicap ?? 0 + } + if (ausruestung.beinlinks) { + systemData.rs += systemData.parent.items.get(ausruestung.beinlinks).system.armorValue ?? 0 + systemData.be += systemData.parent.items.get(ausruestung.beinlinks).system.armorHandicap ?? 0 + } + if (ausruestung.beinrechts) { + systemData.rs += systemData.parent.items.get(ausruestung.beinrechts).system.armorValue ?? 0 + systemData.be += systemData.parent.items.get(ausruestung.beinrechts).system.armorHandicap ?? 0 + } + } systemData.kap.max = 0; - const deities = systemData.parent.items.filter(p => p.type === "Blessing") + // evaluate deities for KaP + + const deities = systemData.parent.items.filter(p => p.type === "Blessing") deities?.forEach((deity) => { if (LiturgyData.alverans.includes(deity.system.gottheit)) { systemData.kap.max = 24; diff --git a/src/module/documents/specialAbility.mjs b/src/module/documents/specialAbility.mjs new file mode 100644 index 00000000..f3d30692 --- /dev/null +++ b/src/module/documents/specialAbility.mjs @@ -0,0 +1,9 @@ +export class SpecialAbility extends Item { + /** + * Augment the basic Item data model with additional dynamic data. + */ + prepareData() { + super.prepareData(); + } + +} diff --git a/src/module/sheets/ActiveEffectSheet.mjs b/src/module/sheets/ActiveEffectSheet.mjs new file mode 100644 index 00000000..99211ed2 --- /dev/null +++ b/src/module/sheets/ActiveEffectSheet.mjs @@ -0,0 +1,44 @@ +export class ActiveEffectSheet extends ItemSheet { + /**@override */ + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ['dsa41', 'sheet', 'activeeffect'], + width: 520, + height: 480 + }); + } + + /** @override */ + get template() { + return `systems/DSA_4-1/templates/item/item-activeeffect-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(); + const effects = context.document.getEmbeddedCollection("ActiveEffect").contents; + if (effects.length > 0) { + context.effectId = effects[0]._id; + } + + return context; + } + + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.isEditable) return; + + html.on('click', '.editEffects', (evt) => { + const {id} = evt.currentTarget.dataset; + const effect = this.object.effects.get(id); + effect.sheet.render(true); + }) + } + +} diff --git a/src/module/sheets/characterSheet.mjs b/src/module/sheets/characterSheet.mjs index 4dd53336..6c546520 100644 --- a/src/module/sheets/characterSheet.mjs +++ b/src/module/sheets/characterSheet.mjs @@ -39,10 +39,14 @@ export class CharacterSheet extends ActorSheet { return characterSheet.#handleDroppedSkill(actor, document); // on false cancel this whole operation case "Advantage": return characterSheet.#handleDroppedAdvantage(actor, document); + case "ActiveEffect": + return characterSheet.#handleDroppedActiveEffect(actor, document); case "Equipment": return characterSheet.#handleDroppedEquipment(actor, document); case "Liturgy": return characterSheet.#handleDroppedLiturgy(actor, document); + case "SpecialAbility": + return characterSheet.#handleDroppedSpecialAbility(actor, document); default: return false; } @@ -117,10 +121,14 @@ export class CharacterSheet extends ActorSheet { context.system = actorData.system; context.flags = actorData.flags; context.derived = context.document.system; + context.originalName = actorData.name; + context.name = context.derived.name ?? actorData.name; + context.effects = actorData.effects ?? []; this.#addSkillsToContext(context) this.#addAdvantagesToContext(context) - this.#addAttributesToContext(context) + this.#addSpecialAbilitiesToContext(context) + await this.#addAttributesToContext(context) this.#addEquipmentsToContext(context) await this.#addCombatStatistics(context) this.#addActionsToContext(context) @@ -158,56 +166,79 @@ export class CharacterSheet extends ActorSheet { context.hasSpells = context.spells.length > 0; } - #addAttributesToContext(context) { - const actorData = context.data; + async #getModsOfAttribute(keyPath) { + let returnValue = []; + Array.from(this.object.appliedEffects).forEach( + (e) => + e.changes.filter(c => c.key === keyPath).forEach(change => { + returnValue.push({ + name: e.name, + value: change.value > 0 ? "+" + change.value : "" + change.value, + icon: e.icon, + }) + })) + return returnValue; + } + + async #addAttributesToContext(context) { + context.mods = { + "mu": await this.#getModsOfAttribute('system.attribute.mu.mod'), + "kl": await this.#getModsOfAttribute('system.attribute.kl.mod'), + "in": await this.#getModsOfAttribute('system.attribute.in.mod'), + "ch": await this.#getModsOfAttribute('system.attribute.ch.mod'), + "ff": await this.#getModsOfAttribute('system.attribute.ff.mod'), + "ge": await this.#getModsOfAttribute('system.attribute.ge.mod'), + "ko": await this.#getModsOfAttribute('system.attribute.ko.mod'), + "kk": await this.#getModsOfAttribute('system.attribute.kk.mod') + } context.attributes = [ { eigenschaft: "mu", name: "MU", tooltip: "Mut", - wert: actorData.system.attribute.mu.aktuell ?? 0, + wert: (context.derived.attribute.mu.aktuell + context.derived.attribute.mu.mod) ?? 0, }, { eigenschaft: "kl", name: "KL", tooltip: "Klugheit", - wert: actorData.system.attribute.kl.aktuell ?? 0, + wert: (context.derived.attribute.kl.aktuell + context.derived.attribute.kl.mod) ?? 0, }, { eigenschaft: "in", name: "IN", tooltip: "Intuition", - wert: actorData.system.attribute.in.aktuell ?? 0, + wert: (context.derived.attribute.in.aktuell + context.derived.attribute.in.mod) ?? 0, }, { eigenschaft: "ch", name: "CH", tooltip: "Charisma", - wert: actorData.system.attribute.ch.aktuell ?? 0, + wert: (context.derived.attribute.ch.aktuell + context.derived.attribute.ch.mod) ?? 0, }, { eigenschaft: "ff", name: "FF", tooltip: "Fingerfertigkeit", - wert: actorData.system.attribute.ff.aktuell ?? 0, + wert: (context.derived.attribute.ff.aktuell + context.derived.attribute.ff.mod) ?? 0, }, { eigenschaft: "ge", name: "GE", tooltip: "Geschicklichkeit", - wert: actorData.system.attribute.ge.aktuell ?? 0, + wert: (context.derived.attribute.ge.aktuell + context.derived.attribute.ge.mod) ?? 0, }, { eigenschaft: "ko", name: "KO", tooltip: "Konstitution", - wert: actorData.system.attribute.ko.aktuell ?? 0, + wert: (context.derived.attribute.ko.aktuell + context.derived.attribute.ko.mod) ?? 0, }, { eigenschaft: "kk", name: "KK", tooltip: "Körperkraft", - wert: actorData.system.attribute.kk.aktuell ?? 0, + wert: (context.derived.attribute.kk.aktuell + context.derived.attribute.kk.mod) ?? 0, }, ]; @@ -231,12 +262,22 @@ export class CharacterSheet extends ActorSheet { ); } - #findEquipmentOnSlot(slot, setNumber) { - return this.object.items.get(this.object.system.heldenausruestung[setNumber][slot]) + #addSpecialAbilitiesToContext(context) { + context.specialAbilities = []; + const actorData = context.data; + Object.values(actorData.items).forEach((item) => { + if (item.type === "SpecialAbility") { + context.specialAbilities.push({ + id: item._id, + name: item.name, + }); + } + } + ); } - #findTalentsOfEquipment(equipment) { - + #findEquipmentOnSlot(slot, setNumber) { + return this.object.items.get(this.object.system.heldenausruestung[setNumber]?.[slot]) } #addActionsToContext(context) { @@ -244,14 +285,16 @@ export class CharacterSheet extends ActorSheet { context.actions = am.evaluate() } - #isWorn(itemId, setId) { + #isWorn(itemId) { const slots = PlayerCharacterDataModel.getSlots() - const set = this.object.system.heldenausruestung[setId] - for (const slot of slots) { - const equipmentSlotId = set[slot] - if (equipmentSlotId === itemId) { - return slot + const set = this.object.system.heldenausruestung[this.object.system.setEquipped] + if (set) { + for (const slot of slots) { + const equipmentSlotId = set[slot] + if (equipmentSlotId === itemId) { + return slot + } } } @@ -267,13 +310,15 @@ export class CharacterSheet extends ActorSheet { context.aupper = Math.min((context.actor.system.aup.aktuell / context.actor.system.aup.max) * 100, 100); context.lepper = Math.min((context.actor.system.lep.aktuell / context.actor.system.lep.max) * 100, 100); + context.keper = Math.min((context.actor.system.kap.aktuell / context.actor.system.kap.max) * 100, 100); + context.aspper = Math.min((context.actor.system.asp.aktuell / context.actor.system.asp.max) * 100, 100); context.lepcurrent = context.actor.system.lep.aktuell ?? 0 context.aupcurrent = context.actor.system.aup.aktuell ?? 0 - const fernkampf = this.#findEquipmentOnSlot("fernkampf", 0) - const links = this.#findEquipmentOnSlot("links", 0) - const rechts = this.#findEquipmentOnSlot("rechts", 0) + const fernkampf = this.#findEquipmentOnSlot("fernkampf", context.actor.system.setEquipped) + const links = this.#findEquipmentOnSlot("links", context.actor.system.setEquipped) + const rechts = this.#findEquipmentOnSlot("rechts", context.actor.system.setEquipped) context.attacks = []; if (fernkampf) { @@ -342,16 +387,24 @@ export class CharacterSheet extends ActorSheet { context.carryingweight = 0; Object.values(actorData.items).forEach((item, index) => { if (item.type === "Equipment") { + + // worn items are halved weight + + let effectiveWeight = item.system.weight ?? 0 + if (this.#isWorn(item._id)) { + effectiveWeight = item.system.weight ? item.system.weight / 2 : 0 + } + context.equipments.push({ index: index, id: item._id, quantity: item.system.quantity, name: item.name, icon: item.img ?? "", - weight: item.system.weight ?? 0, - worn: this.#isWorn(item._id, 0) + weight: item.system.weight, + worn: this.#isWorn(item._id) }) - context.carryingweight += item.system.quantity * item.system.weight; + context.carryingweight += item.system.quantity * effectiveWeight; } }) context.maxcarryingcapacity = actorData.system.attribute.kk.aktuell @@ -544,22 +597,23 @@ export class CharacterSheet extends ActorSheet { } } - #getEquipmentset(setId) { - const equipmentSet = this.object.system.heldenausruestung[setId] + #mapAllSets() { const updateObject = {} - // TODO: there's got to be a better angle! - updateObject[`system.heldenausruestung.${setId}.links`] = equipmentSet.links; - updateObject[`system.heldenausruestung.${setId}.rechts`] = equipmentSet.rechts; - updateObject[`system.heldenausruestung.${setId}.brust`] = equipmentSet.brust; - updateObject[`system.heldenausruestung.${setId}.bauch`] = equipmentSet.bauch; - updateObject[`system.heldenausruestung.${setId}.ruecken`] = equipmentSet.ruecken; - updateObject[`system.heldenausruestung.${setId}.kopf`] = equipmentSet.kopf; - updateObject[`system.heldenausruestung.${setId}.fernkampf`] = equipmentSet.fernkampf; - updateObject[`system.heldenausruestung.${setId}.munition`] = equipmentSet.munition; - updateObject[`system.heldenausruestung.${setId}.armlinks`] = equipmentSet.armlinks; - updateObject[`system.heldenausruestung.${setId}.armrechts`] = equipmentSet.armrechts; - updateObject[`system.heldenausruestung.${setId}.beinlinks`] = equipmentSet.beinlinks; - updateObject[`system.heldenausruestung.${setId}.beinrechts`] = equipmentSet.beinrechts; + Array.from(this.object.system.heldenausruestung).forEach((equipmentSet, index) => { + updateObject[`system.heldenausruestung.${index}.links`] = equipmentSet.links; + updateObject[`system.heldenausruestung.${index}.rechts`] = equipmentSet.rechts; + updateObject[`system.heldenausruestung.${index}.brust`] = equipmentSet.brust; + updateObject[`system.heldenausruestung.${index}.bauch`] = equipmentSet.bauch; + updateObject[`system.heldenausruestung.${index}.ruecken`] = equipmentSet.ruecken; + updateObject[`system.heldenausruestung.${index}.kopf`] = equipmentSet.kopf; + updateObject[`system.heldenausruestung.${index}.fernkampf`] = equipmentSet.fernkampf; + updateObject[`system.heldenausruestung.${index}.munition`] = equipmentSet.munition; + updateObject[`system.heldenausruestung.${index}.armlinks`] = equipmentSet.armlinks; + updateObject[`system.heldenausruestung.${index}.armrechts`] = equipmentSet.armrechts; + updateObject[`system.heldenausruestung.${index}.beinlinks`] = equipmentSet.beinlinks; + updateObject[`system.heldenausruestung.${index}.beinrechts`] = equipmentSet.beinrechts; + + }) return updateObject; } @@ -696,6 +750,20 @@ export class CharacterSheet extends ActorSheet { } } + async #handleDroppedActiveEffect(actor, activeEffect) { + const array = Array.from(actor.items); + for (let i = 0; i < array.length; i++) { + if (array[i].name === activeEffect.name) { + // replace active effect + actor.deleteEmbeddedDocuments('Item', [array[i].id]).then(() => { + console.log("await") + }) + + return true; + } + } + } + #handleDroppedAdvantage(actor, advantage) { const array = Array.from(actor.items); for (let i = 0; i < array.length; i++) { @@ -711,7 +779,7 @@ export class CharacterSheet extends ActorSheet { const tabs = new Tabs({ navSelector: ".paperdoll-tabs.tabs", contentSelector: ".sheet-body.paperdoll-sets", - initial: "set1" + initial: "set" + (this.object.system.setEquipped + 1) }); tabs.bind(html[0]); @@ -719,6 +787,12 @@ export class CharacterSheet extends ActorSheet { this._onAttributeRoll(evt); }); + html.on('click', '[data-operation="switchSet"]', (evt) => { + const {id} = evt.currentTarget.dataset; + console.log(id); + this.object.update({"system.setEquipped": id}) + }) + html.on('click', '.talent.rollable', (evt) => { this._onTalentRoll(evt); }); @@ -767,7 +841,8 @@ export class CharacterSheet extends ActorSheet { if (actor === this.object._id && documentId) { // managing equipped items //const slot = this.#isWorn(documentId, setId) - const updateObject = this.#getEquipmentset(setId) + //const updateObject = await this.#getEquipmentset(Number(setId)) + const updateObject = this.#mapAllSets() updateObject[`system.heldenausruestung.${setId}.${target}`] = documentId; console.log(updateObject); @@ -828,7 +903,7 @@ export class CharacterSheet extends ActorSheet { callback: (event) => { const {setId, target, actor} = event[0].dataset - const updateObject = this.#getEquipmentset(setId) + const updateObject = this.#mapAllSets() updateObject[`system.heldenausruestung.${setId}.${target}`] = null; this.object.update(updateObject); @@ -910,4 +985,13 @@ export class CharacterSheet extends ActorSheet { } } + #handleDroppedSpecialAbility(actor, specialAbility) { + const array = Array.from(actor.items); + for (let i = 0; i < array.length; i++) { + if (array[i].name === specialAbility.name) { // TODO: allow multiple miracles with the same name + return false; + } + } + } + } diff --git a/src/module/sheets/creatureSheet.mjs b/src/module/sheets/creatureSheet.mjs index a712b187..2c28134d 100644 --- a/src/module/sheets/creatureSheet.mjs +++ b/src/module/sheets/creatureSheet.mjs @@ -72,10 +72,10 @@ export class CreatureSheet extends foundry.appv1.sheets.ActorSheet { }) html.on('click', '.editor .add-attack', async (evt) => { - const name = evt.target.parentElement.querySelector('#attack_name').value - const at = evt.target.parentElement.querySelector('#attack_at').value - const pa = evt.target.parentElement.querySelector('#attack_pa').value - const tp = evt.target.parentElement.querySelector('#attack_tp').value + const name = html[0].querySelector('#attack_name').value + const at = html[0].querySelector('#attack_at').value + const pa = html[0].querySelector('#attack_pa').value + const tp = html[0].querySelector('#attack_tp').value const newAttack = { name, diff --git a/src/module/sheets/liturgySheet.mjs b/src/module/sheets/liturgySheet.mjs index ec850750..0a531f9a 100644 --- a/src/module/sheets/liturgySheet.mjs +++ b/src/module/sheets/liturgySheet.mjs @@ -42,7 +42,9 @@ export class LiturgySheet extends ItemSheet { super.activateListeners(html); // Everything below here is only needed if the sheet is editable - if (!this.isEditable) + if (this.isEditable) { + + } } } diff --git a/src/module/sheets/specialAbilitySheet.mjs b/src/module/sheets/specialAbilitySheet.mjs new file mode 100644 index 00000000..a1c6bb08 --- /dev/null +++ b/src/module/sheets/specialAbilitySheet.mjs @@ -0,0 +1,51 @@ +export class SpecialAbilitySheet extends ItemSheet { + /**@override */ + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ['dsa41', 'sheet', 'item', 'specialability'], + width: 520, + height: 480, + tabs: [ + { + navSelector: '.sheet-tabs', + contentSelector: '.sheet-body', + initial: 'description', + }, + ], + }); + } + + /** @override */ + get template() { + return `systems/DSA_4-1/templates/item/item-special-ability-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 advantageData = context.data; + + // Add the actor's data to context.data for easier access, as well as flags. + context.system = advantageData.system; + context.flags = advantageData.flags; + context.json = JSON.stringify(advantageData.system, null, 4); + + 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/sheets/vornachteilSheet.mjs b/src/module/sheets/vornachteilSheet.mjs index 842bd24f..aec033b9 100644 --- a/src/module/sheets/vornachteilSheet.mjs +++ b/src/module/sheets/vornachteilSheet.mjs @@ -34,7 +34,13 @@ export class VornachteilSheet extends ItemSheet { // Add the actor's data to context.data for easier access, as well as flags. context.system = advantageData.system; context.flags = advantageData.flags; - context.json = JSON.stringify(advantageData.system, null, 4); + + context.choices = {} + context.system.auswahl.forEach(a => { + context.choices[a] = a + }) + context.hasChoices = context.system.auswahl.length > 0; + context.hasModality = context.system.value == null return context; } @@ -43,7 +49,7 @@ export class VornachteilSheet extends ItemSheet { super.activateListeners(html); // Everything below here is only needed if the sheet is editable - if (!this.isEditable) return; + if (!this.isEditable) } -} \ No newline at end of file +} diff --git a/src/module/xml-import/xml-import.mjs b/src/module/xml-import/xml-import.mjs index 66eb09d2..6efb67d7 100644 --- a/src/module/xml-import/xml-import.mjs +++ b/src/module/xml-import/xml-import.mjs @@ -251,16 +251,14 @@ function mapRawJson(actor, rawJson) { held.basis.rasse.aussehen.aussehentext0, held.basis.rasse.aussehen.aussehentext1, held.basis.rasse.aussehen.aussehentext2, - held.basis.rasse.aussehen.aussehentext3, - ] + held.basis.rasse.aussehen.aussehentext3].join('\n') json.meta.familie = [ held.basis.rasse.aussehen.familietext0, held.basis.rasse.aussehen.familietext1, held.basis.rasse.aussehen.familietext2, held.basis.rasse.aussehen.familietext3, held.basis.rasse.aussehen.familietext4, - held.basis.rasse.aussehen.familietext5, - ] + held.basis.rasse.aussehen.familietext5].join('\n') json.meta.titel = held.basis.rasse.aussehen.titel json.meta.stand = held.basis.rasse.aussehen.stand let attributes = held.eigenschaften.eigenschaft diff --git a/src/style/_active-effect-sheet.scss b/src/style/_active-effect-sheet.scss new file mode 100644 index 00000000..dd1dddb0 --- /dev/null +++ b/src/style/_active-effect-sheet.scss @@ -0,0 +1,38 @@ +.active-effect { + + display: flex; + flex-direction: column; + + .header { + flex: 0; + display: grid; + grid-template-columns: 48px 1fr 48px; + grid-template-rows: 48px; + gap: 8px; + + input { + height: 48px; + line-height: 48px; + } + } + + .meta { + flex: 1; + display: flex; + flex-direction: column; + margin-top: 8px; + + label { + flex: 0; + } + + div.editor { + flex: 1; + border: 1px inset #ccc; + background-color: rgba(0, 0, 0, 0.2) + } + + + } + +} diff --git a/src/style/_advantage-sheet.scss b/src/style/_advantage-sheet.scss new file mode 100644 index 00000000..105bce7f --- /dev/null +++ b/src/style/_advantage-sheet.scss @@ -0,0 +1,48 @@ +.dsa41.sheet.item.advantage { + + .sheet-body { + padding: 8px; + } + + .tab.advantage.active { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: 48px 1fr; + gap: 8px; + height: 100%; + + div { + + label { + width: 100%; + display: inline-block; + } + + input { + width: 100%; + display: inline-block; + } + } + + .body { + grid-column: 1/3; + display: flex; + flex-direction: column; + height: 100%; + + label { + flex: 0; + } + + .editor { + flex: 1; + + border: 1px inset #ccc; + background-color: rgba(0, 0, 0, 0.2); + + } + } + + } + +} diff --git a/src/style/_character-sheet.scss b/src/style/_character-sheet.scss index 44a35047..6a671515 100644 --- a/src/style/_character-sheet.scss +++ b/src/style/_character-sheet.scss @@ -57,6 +57,188 @@ overflow: auto; } + .tab.overview.active { + + display: flex; + flex-direction: column; + height: 100%; + + .meta-data { + flex: 0; + + columns: 2; + gap: 0 16px; + + + div { + break-inside: avoid; + padding: 4px 0; + } + + .double { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: 'label label' 'left right'; + gap: 0 8px; + + label { + grid-area: label; + } + } + + .editor { + background-color: rgba(0, 0, 0, 0.2) + } + + & + .meta-data { + flex: 1; + } + } + + .meta-data.html { + + & > div { + display: flex; + flex-direction: column; + height: 100%; + + label { + flex: 0; + } + + .editor { + flex: 1; + } + } + + } + + } + + .tab.attributes.active { + height: 100%; + + .attribute { + padding: 8px 0; + display: flex; + gap: 0 8px; + + label { + width: 120px; + text-align: right; + vertical-align: middle; + line-height: 24px; + } + + input { + max-width: 80px; + text-align: right; + } + + .mod { + color: grey; + text-shadow: 0 -1px 0 #ccc; + box-Shadow: 1px 1px 1px rgba(0, 0, 0, 0.5); + border-radius: 4px; + height: 24px; + width: 24px; + background: linear-gradient(0deg, rgba(24, 24, 24, 1) 0%, rgba(80, 80, 80, 1) 100%);; + display: inline-block; + text-align: center; + vertical-align: middle; + line-height: 24px; + margin-left: 4px; + } + + } + + .attributes-overview { + + columns: 2; + gap: 0 16px; + + } + + .resource-overview { + + .attribute { + + } + + } + + .advantages, .special-abilities { + margin-bottom: 16px; + + ul { + list-style-type: none; + padding: 0; + margin: 0; + text-indent: 0; + + li { + display: inline-block; + } + + .advantage, .special-ability { + position: relative; + border: 1px solid gold; + box-shadow: 2px 2px 4px #000; + border-radius: 8px; + height: 24px; + color: gold; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2); + display: inline-block; + padding: 0 8px; + margin-left: 0; + margin-bottom: 4px; + background-image: url("../assets/velvet_button.png"); + background-repeat: repeat-y; + background-size: cover; + + span { + position: relative; + z-index: 2; + line-height: 24px; + vertical-align: middle; + } + + &.special-ability { + &::after { + background: rgba(128, 0, 96, 0.5); + } + } + + &::after { + content: ""; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + border-radius: 8px; + background: rgba(0, 128, 0, 0.5); + } + + & + .advantage, & + .special-ability { + margin-left: 8px; + } + + &.nachteil { + font-style: italic; + + &::after { + background: rgba(128, 0, 0, 0.5); + } + } + } + } + + } + + + } + .backpack.active { padding: 8px; display: grid; diff --git a/src/style/_equipment-sheet.scss b/src/style/_equipment-sheet.scss index 18b0ee70..80f041f0 100644 --- a/src/style/_equipment-sheet.scss +++ b/src/style/_equipment-sheet.scss @@ -6,7 +6,6 @@ .sheet-body { position: relative; - top: 5px; .tab.active { padding: 4px; diff --git a/src/style/_sidebar-elements.scss b/src/style/_sidebar-elements.scss index e48c6bcb..f774031b 100644 --- a/src/style/_sidebar-elements.scss +++ b/src/style/_sidebar-elements.scss @@ -51,6 +51,8 @@ line-height: 24px; vertical-align: middle; text-indent: 8px; + color: white; + text-shadow: 2px 2px 1px rgba(0, 0, 0, 0.3); } span.resource-fill { @@ -58,9 +60,36 @@ left: 0; top: 0; bottom: 0; - background: linear-gradient(to bottom, #0bad29 0%, #11f128 50%, #0cde24 51%, #6ff77b 100%); + background: url('../assets/gradient.png'); + background-size: 32px 100%; + + &::after { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + &.lep::after { + background-color: rgba(0, 192, 96, 0.5); + } + + &.aup::after { + background-color: rgba(192, 166, 0, 0.5); + } + + &.asp::after { + background-color: rgba(0, 26, 192, 0.5); + } + + &.kap::after { + background-color: rgba(128, 0, 192, 0.5); + } } + } } diff --git a/src/style/_talent-sheet.scss b/src/style/_talent-sheet.scss new file mode 100644 index 00000000..6a083006 --- /dev/null +++ b/src/style/_talent-sheet.scss @@ -0,0 +1,51 @@ +.sheet.item.skill { + + .tab.meta.active { + + display: grid; + grid-template-areas: + "category category category" + "taw statistics ebe" + "language language language" + "attack attack attack"; + + gap: 8px; + margin: 8px; + + .category { + grid-area: category; + } + + .taw { + grid-area: taw; + } + + .statistics { + grid-area: statistics; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: auto 1fr; + + .statistics > label { + grid-column: 1/4; + text-align: center; + } + } + + .ebe { + grid-area: ebe; + } + + .language-statistics { + grid-area: language; + } + + .attack-statistics { + grid-area: attack; + display: flex; + flex-direction: row; + } + + } + +} diff --git a/src/style/styles.scss b/src/style/styles.scss index e11377af..e30542c1 100644 --- a/src/style/styles.scss +++ b/src/style/styles.scss @@ -11,3 +11,6 @@ @use "_player-action"; @use "_modify-liturgy"; @use "_liturgy-banner"; +@use "_talent-sheet"; +@use "_active-effect-sheet"; +@use "_advantage-sheet"; diff --git a/src/system.json b/src/system.json index ffd4083f..bf0f5b45 100644 --- a/src/system.json +++ b/src/system.json @@ -113,7 +113,8 @@ ], "arrayFields": [ "talente", - "zauber" + "zauber", + "heldenausruestung" ] } }, @@ -163,6 +164,14 @@ "maxLEP", "maxMR" ] + }, + "SpecialAbility": { + "stringFields": [ + "name" + ] + }, + "ActiveEffect": { + }, "Skill": { "stringFields": [ diff --git a/src/templates/actor/actor-character-sheet.hbs b/src/templates/actor/actor-character-sheet.hbs index ac7bc9db..108caddf 100644 --- a/src/templates/actor/actor-character-sheet.hbs +++ b/src/templates/actor/actor-character-sheet.hbs @@ -13,14 +13,30 @@
-

- +

+ + +
+ + + + + {{#if this.hasLiturgies}} + + {{/if}} + + {{#if this.hasSpells}} + + {{/if}} {{#each attacks}}
@@ -64,26 +80,176 @@ {{!-- Sheet Body --}}
-
+ + -
-
-
-
-
+ +
+
+
+ + +
+ {{#each this.mods.mu}} + {{this.value}} + {{/each}} +
+
+
+ + +
+ {{#each this.mods.kl}} + {{this.value}} + {{/each}} +
+
+
+ + +
+ {{#each this.mods.in}} + {{this.value}} + {{/each}} +
+ +
+
+ + +
+ {{#each this.mods.ch}} + {{this.value}} + {{/each}} +
+ +
+
+ + +
+ {{#each this.mods.ff}} + {{this.value}} + {{/each}} +
+ +
+
+ + +
+ {{#each this.mods.ge}} + {{this.value}} + {{/each}} +
+ +
+
+ + +
+ {{#each this.mods.ko}} + {{this.value}} + {{/each}} +
+ +
+
+ + +
+ {{#each this.mods.kk}} + {{this.value}} + {{/each}} +
+ +
+
+ + +
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+

Vor- und Nachteile

    {{#each this.advantages}}
  • {{> "systems/DSA_4-1/templates/ui/partial-advantage-button.hbs" this}}
  • {{/each}}
+
+
+

Sonderfertigkeiten

+
    + {{#each this.specialAbilities}} +
  • {{> "systems/DSA_4-1/templates/ui/partial-sf-button.hbs" this}}
  • + {{/each}} +
+
@@ -112,6 +278,12 @@ {{derived.rs}}
+
+ + {{derived.be}} +
+ +
@@ -250,7 +422,11 @@ data-target="{{this.target}}" data-actor="{{../../actor.id}}">
{{/each}} - + {{#if (eq ../actor.system.setEquipped @index)}} + + {{else}} + + {{/if}}
@@ -334,9 +510,29 @@ - {{#if this.countI}} + {{#if this.countO}} + +
+
0
+
+ + + {{#each this.O}} + + + {{> 'systems/DSA_4-1/templates/ui/partial-die.hbs' }} + + + {{this.name}} + + + {{/each}} + {{/if}} + {{#if this.countI}} +
I
@@ -491,8 +687,10 @@
{{/if}} + {{#if this.hasPets}}
+ {{/if}} diff --git a/src/templates/item/item-activeeffect-sheet.hbs b/src/templates/item/item-activeeffect-sheet.hbs new file mode 100644 index 00000000..e7c02a70 --- /dev/null +++ b/src/templates/item/item-activeeffect-sheet.hbs @@ -0,0 +1,20 @@ +
+
+ +
+ + + + +
+ +
+ + {{editor item.system.notes target="system.notes" button=true owner=owner editable=editable}} +
+ + +
+
diff --git a/src/templates/item/item-advantage-sheet.hbs b/src/templates/item/item-advantage-sheet.hbs index d50d00ce..71a66c55 100644 --- a/src/templates/item/item-advantage-sheet.hbs +++ b/src/templates/item/item-advantage-sheet.hbs @@ -2,13 +2,34 @@ {{!-- Sheet Tab Navigation --}} {{!-- Sheet Body --}}
-
-
{{json}}
+
+
+ + + +
+ {{#if hasModality}} +
+ + {{#if hasChoices}} + + {{else}} + + {{/if}} +
+ {{/if}} +
+ + {{editor system.description target="system.description" button=true owner=owner editable=editable}} +
+
diff --git a/src/templates/item/item-skill-sheet.hbs b/src/templates/item/item-skill-sheet.hbs index d6444eb6..1c0d8010 100644 --- a/src/templates/item/item-skill-sheet.hbs +++ b/src/templates/item/item-skill-sheet.hbs @@ -10,57 +10,62 @@
-
+
-
+
-
- +
+ + +
+ +
+
+
+ +
-
-
- -
-
-