From a768b1dfdba58d3c87f69c35a939608e5c78bc3a Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 14 Jul 2025 01:58:16 +0200 Subject: [PATCH] Character Setup Rework (#315) * Updated to make use of setup tabs. Ancestry now has primary/secondary features * Changed so ancestry uses a single Features field * Revert "Changed so ancestry uses a single Features field" This reverts commit 0bda6b5dbeaf719c84c0733fef16583407784118. * Reapply "Changed so ancestry uses a single Features field" This reverts commit 1febafd4415c87c47e279e0575f603effa8f267a. * Made it work again the bad way \._./ * Changed so that Feature(Item) has a primary field * Feature(Item) now has subtype instead of primary as a field * Fixed experience/evasion * Moved light styling to appTheme mixing * Added svg and style rules --- lang/en.json | 20 +- .../characterCreation/characterCreation.mjs | 222 ++++++++++++++++-- module/applications/sheets/api/base-item.mjs | 4 +- .../sheets/api/heritage-sheet.mjs | 4 - module/applications/sheets/items/ancestry.mjs | 101 +++++++- module/applications/sheets/items/class.mjs | 2 +- .../applications/sheets/items/community.mjs | 6 +- module/applications/sheets/items/subclass.mjs | 4 +- module/config/itemConfig.mjs | 5 + module/data/item/ancestry.mjs | 16 ++ module/data/item/feature.mjs | 1 + module/documents/actor.mjs | 5 +- .../selections-container.less | 167 ++++++++++++- templates/characterCreation/footer.hbs | 6 +- .../characterCreation/setupTabs/ancestry.hbs | 63 +++++ .../characterCreation/setupTabs/class.hbs | 24 ++ .../characterCreation/setupTabs/community.hbs | 18 ++ .../setupTabs/domainCards.hbs | 22 ++ .../setupTabs/experience.hbs | 19 ++ .../characterCreation/setupTabs/traits.hbs | 32 +++ templates/characterCreation/tabs/setup.hbs | 102 +------- templates/components/card-preview.hbs | 8 +- templates/sheets/actors/character/header.hbs | 2 +- templates/sheets/items/ancestry/features.hbs | 47 ++++ 24 files changed, 756 insertions(+), 144 deletions(-) create mode 100644 templates/characterCreation/setupTabs/ancestry.hbs create mode 100644 templates/characterCreation/setupTabs/class.hbs create mode 100644 templates/characterCreation/setupTabs/community.hbs create mode 100644 templates/characterCreation/setupTabs/domainCards.hbs create mode 100644 templates/characterCreation/setupTabs/experience.hbs create mode 100644 templates/characterCreation/setupTabs/traits.hbs create mode 100644 templates/sheets/items/ancestry/features.hbs diff --git a/lang/en.json b/lang/en.json index c0dc1c3c..96f5a9dd 100755 --- a/lang/en.json +++ b/lang/en.json @@ -162,12 +162,25 @@ }, "APPLICATIONS": { "CharacterCreation": { + "setupTabs": { + "ancestry": "Ancestry", + "community": "Community", + "class": "Class", + "experience": "Experience", + "traits": "Traits", + "domainCards": "Domain Cards" + }, + "ancestryNamePlaceholder": "Your ancestry's name", + "buttonTitle": "Character Setup", "choice": "Choice", "finishCreation": "Finish Character Setup", "heritage": "Heritage", "initialExperiences": "Initial Experiences", + "mixedAncestry": "Mixed Ancestry", "newExperience": "New Experience..", "selectAncestry": "Select Ancestry", + "selectPrimaryAncestry": "Select Primary Ancestry", + "selectSecondaryAncestry": "Select Secondary Ancestry", "selectArmor": "Select Armor", "selectClass": "Select Class", "selectCommunity": "Select Community", @@ -1177,6 +1190,10 @@ "value": { "label": "Value" } } }, + "Ancestry": { + "primaryFeature": "Primary Feature", + "secondaryFeature": "Secondary Feature" + }, "Armor": { "baseScore": "Base Score", "baseThresholds": { @@ -1432,7 +1449,8 @@ "damageAlreadyNone": "The damage has already been reduced to none", "noAvailableArmorMarks": "You have no more available armor marks", "notEnoughStress": "You don't have enough stress", - "damageIgnore": "{character} did not take damage" + "damageIgnore": "{character} did not take damage", + "featureIsMissing": "Feature is missing" }, "Tooltip": { "openItemWorld": "Open Item World", diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index 71849f56..ed0ee5a7 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -11,13 +11,16 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl this.setup = { traits: this.character.system.traits, - ancestry: this.character.system.ancestry ?? {}, + ancestryName: '', + mixedAncestry: false, + primaryAncestry: this.character.system.ancestry ?? {}, + secondaryAncestry: {}, community: this.character.system.community ?? {}, class: this.character.system.class?.value ?? {}, subclass: this.character.system.class?.subclass ?? {}, experiences: { - [foundry.utils.randomID()]: { description: '', value: 2 }, - [foundry.utils.randomID()]: { description: '', value: 2 } + [foundry.utils.randomID()]: { name: '', value: 2 }, + [foundry.utils.randomID()]: { name: '', value: 2 } }, domainCards: { [foundry.utils.randomID()]: {}, @@ -47,12 +50,13 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl static DEFAULT_OPTIONS = { tag: 'form', classes: ['daggerheart', 'dialog', 'dh-style', 'character-creation'], - position: { width: 800, height: 'auto' }, + position: { width: 700, height: 'auto' }, actions: { viewCompendium: this.viewCompendium, viewItem: this.viewItem, useSuggestedTraits: this.useSuggestedTraits, equipmentChoice: this.equipmentChoice, + setupGoNext: this.setupGoNext, finish: this.finish }, form: { @@ -76,6 +80,12 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl static PARTS = { tabs: { template: 'systems/daggerheart/templates/characterCreation/tabs.hbs' }, setup: { template: 'systems/daggerheart/templates/characterCreation/tabs/setup.hbs' }, + ancestry: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/ancestry.hbs' }, + community: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/community.hbs' }, + class: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/class.hbs' }, + traits: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/traits.hbs' }, + experience: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/experience.hbs' }, + domainCards: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/domainCards.hbs' }, equipment: { template: 'systems/daggerheart/templates/characterCreation/tabs/equipment.hbs' }, // story: { template: 'systems/daggerheart/templates/characterCreation/tabs/story.hbs' }, footer: { template: 'systems/daggerheart/templates/characterCreation/footer.hbs' } @@ -107,6 +117,51 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl // } }; + static SETUPTABS = { + ancestry: { + active: true, + cssClass: '', + group: 'setup', + id: 'ancestry', + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.ancestry' + }, + community: { + active: false, + cssClass: '', + group: 'setup', + id: 'community', + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.community' + }, + class: { + active: false, + cssClass: '', + group: 'setup', + id: 'class', + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.class' + }, + traits: { + active: false, + cssClass: '', + group: 'setup', + id: 'traits', + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.traits' + }, + experience: { + active: false, + cssClass: '', + group: 'setup', + id: 'experience', + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.experience' + }, + domainCards: { + active: false, + cssClass: '', + group: 'setup', + id: 'domainCards', + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.domainCards' + } + }; + _getTabs(tabs) { for (const v of Object.values(tabs)) { v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; @@ -114,14 +169,16 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl switch (v.id) { case 'setup': + const ancestryFinished = this.setup.primaryAncestry.uuid; + const communityFinished = this.setup.community.uuid; const classFinished = this.setup.class.uuid && this.setup.subclass.uuid; - const heritageFinished = this.setup.ancestry.uuid && this.setup.community.uuid; const traitsFinished = Object.values(this.setup.traits).every(x => x.value !== null); - const experiencesFinished = Object.values(this.setup.experiences).every(x => x.description); + const experiencesFinished = Object.values(this.setup.experiences).every(x => x.name); const domainCardsFinished = Object.values(this.setup.domainCards).every(x => x.uuid); v.finished = + ancestryFinished && + communityFinished && classFinished && - heritageFinished && traitsFinished && experiencesFinished && domainCardsFinished; @@ -146,15 +203,44 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl return tabs; } + _getSetupTabs(tabs) { + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; + v.cssClass = v.active ? 'active' : ''; + + switch (v.id) { + case 'community': + v.disabled = this.setup.visibility < 2; + break; + case 'class': + v.disabled = this.setup.visibility < 3; + break; + case 'traits': + v.disabled = this.setup.visibility < 4; + break; + case 'experience': + v.disabled = this.setup.visibility < 5; + break; + case 'domainCards': + v.disabled = this.setup.visibility < 6; + break; + } + } + + return tabs; + } + changeTab(tab, group, options) { super.changeTab(tab, group, options); - for (var listTab of Object.keys(this.constructor.TABS)) { - const marker = options.navElement.querySelector(`a[data-action="tab"].${listTab} .finish-marker`); - if (listTab === tab) { - marker.classList.add('active'); - } else { - marker.classList.remove('active'); + if (group === 'primary') { + for (var listTab of Object.keys(this.constructor.TABS)) { + const marker = options.navElement.querySelector(`a[data-action="tab"].${listTab} .finish-marker`); + if (listTab === tab) { + marker.classList.add('active'); + } else { + marker.classList.remove('active'); + } } } } @@ -163,6 +249,11 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl super._attachPartListeners(partId, htmlElement, options); this._dragDrop.forEach(d => d.bind(htmlElement)); + + htmlElement.querySelectorAll('.mixed-ancestry-slider').forEach(element => { + element.addEventListener('input', this.mixedAncestryToggle.bind(this)); + element.addEventListener('click', this.mixedAncestryToggle.bind(this)); + }); } async _prepareContext(_options) { @@ -174,7 +265,30 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl async _preparePartContext(partId, context) { switch (partId) { + case 'footer': + context.isLastTab = this.tabGroups.setup === 'domainCards'; + switch (this.tabGroups.setup) { + case null: + case 'ancestry': + context.nextDisabled = this.setup.visibility === 1; + break; + case 'community': + context.nextDisabled = this.setup.visibility === 2; + break; + case 'class': + context.nextDisabled = this.setup.visibility === 3; + break; + case 'traits': + context.nextDisabled = this.setup.visibility === 4; + break; + case 'experience': + context.nextDisabled = this.setup.visibility === 5; + break; + } + + break; case 'setup': + context.setupTabs = this._getSetupTabs(this.constructor.SETUPTABS); const availableTraitModifiers = game.settings .get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew) .traitArray.map(trait => ({ key: trait, name: trait })); @@ -215,13 +329,13 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl context.experience = { values: this.setup.experiences, nrTotal: Object.keys(this.setup.experiences).length, - nrSelected: Object.values(this.setup.experiences).reduce( - (acc, exp) => acc + (exp.description ? 1 : 0), - 0 - ) + nrSelected: Object.values(this.setup.experiences).reduce((acc, exp) => acc + (exp.name ? 1 : 0), 0) }; - context.ancestry = { ...this.setup.ancestry, compendium: 'ancestries' }; + context.mixedAncestry = Number(this.setup.mixedAncestry); + context.ancestryName = this.setup.ancestryName; + context.primaryAncestry = { ...this.setup.primaryAncestry, compendium: 'ancestries' }; + context.secondaryAncestry = { ...this.setup.secondaryAncestry, compendium: 'ancestries' }; context.community = { ...this.setup.community, compendium: 'communities' }; context.class = { ...this.setup.class, compendium: 'classes' }; context.subclass = { ...this.setup.subclass, compendium: 'subclasses' }; @@ -278,18 +392,29 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl this.render(); } + mixedAncestryToggle(event) { + event.preventDefault(); + event.stopPropagation(); + this.setup.mixedAncestry = !this.setup.mixedAncestry; + if (!this.setup.mixedAncestry) this.setup.secondaryAncestry = {}; + + this.render(); + } + getUpdateVisibility() { switch (this.setup.visibility) { + case 6: + return 6; case 5: - return 5; + return Object.values(this.setup.experiences).every(x => x.name) ? 6 : 5; case 4: - return Object.values(this.setup.experiences).every(x => x.description) ? 5 : 4; + return Object.values(this.setup.traits).every(x => x.value !== null) ? 5 : 4; case 3: - return Object.values(this.setup.traits).every(x => x.value !== null) ? 4 : 3; + return this.setup.class.uuid && this.setup.subclass.uuid ? 4 : 3; case 2: - return this.setup.ancestry.uuid && this.setup.community.uuid ? 3 : 2; + return this.setup.community.uuid ? 3 : 2; case 1: - return this.setup.class.uuid && this.setup.subclass.uuid ? 2 : 1; + return this.setup.primaryAncestry.uuid ? 2 : 1; } } @@ -348,8 +473,46 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl this.render(); } + static setupGoNext() { + switch (this.setup.visibility) { + case 2: + this.tabGroups.setup = 'community'; + break; + case 3: + this.tabGroups.setup = 'class'; + break; + case 4: + this.tabGroups.setup = 'traits'; + break; + case 5: + this.tabGroups.setup = 'experience'; + break; + case 6: + this.tabGroups.setup = 'domainCards'; + break; + } + + this.render(); + } + static async finish() { - await this.character.createEmbeddedDocuments('Item', [this.setup.ancestry]); + const primaryAncestryFeature = this.setup.primaryAncestry.system.primaryFeature; + const secondaryAncestryFeature = this.setup.secondaryAncestry?.uuid + ? this.setup.secondaryAncestry.system.secondaryFeature + : this.setup.primaryAncestry.system.secondaryFeature; + + const ancestry = { + ...this.setup.primaryAncestry, + name: this.setup.ancestryName ?? this.setup.primaryAncestry.name, + system: { + ...this.setup.primaryAncestry.system, + features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid], + primaryFeature: primaryAncestryFeature.uuid, + secondaryFeature: secondaryAncestryFeature.uuid + } + }; + + await this.character.createEmbeddedDocuments('Item', [ancestry]); await this.character.createEmbeddedDocuments('Item', [this.setup.community]); await this.character.createEmbeddedDocuments('Item', [this.setup.class]); await this.character.createEmbeddedDocuments('Item', [this.setup.subclass]); @@ -396,8 +559,15 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl async _onDrop(event) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const item = await foundry.utils.fromUuid(data.uuid); - if (item.type === 'ancestry' && event.target.closest('.ancestry-card')) { - this.setup.ancestry = { + if (item.type === 'ancestry' && event.target.closest('.primary-ancestry-card')) { + this.setup.ancestryName = item.name; + this.setup.primaryAncestry = { + ...item, + effects: Array.from(item.effects).map(x => x.toObject()), + uuid: item.uuid + }; + } else if (item.type === 'ancestry' && event.target.closest('.secondary-ancestry-card')) { + this.setup.secondaryAncestry = { ...item, effects: Array.from(item.effects).map(x => x.toObject()), uuid: item.uuid diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 2847bfc8..0c25a44d 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -181,7 +181,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { const target = button.closest('.feature-item'); const feature = this.document.system.features.find(x => x?.id === target.id); if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsMissing')); + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); return; } @@ -251,7 +251,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { if (featureItem) { const feature = this.document.system.features.find(x => x?.id === featureItem.id); if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsMissing')); + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); return; } diff --git a/module/applications/sheets/api/heritage-sheet.mjs b/module/applications/sheets/api/heritage-sheet.mjs index 349da194..8ac8d7d6 100644 --- a/module/applications/sheets/api/heritage-sheet.mjs +++ b/module/applications/sheets/api/heritage-sheet.mjs @@ -10,10 +10,6 @@ export default class DHHeritageSheet extends DHBaseItemSheet { static PARTS = { tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' }, - feature: { - template: 'systems/daggerheart/templates/sheets/global/tabs/tab-features.hbs', - scrollable: ['.feature'] - }, effects: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', scrollable: ['.effects'] diff --git a/module/applications/sheets/items/ancestry.mjs b/module/applications/sheets/items/ancestry.mjs index 3636ea62..bd9e3792 100644 --- a/module/applications/sheets/items/ancestry.mjs +++ b/module/applications/sheets/items/ancestry.mjs @@ -3,12 +3,109 @@ import DHHeritageSheet from '../api/heritage-sheet.mjs'; export default class AncestrySheet extends DHHeritageSheet { /**@inheritdoc */ static DEFAULT_OPTIONS = { - classes: ['ancestry'] + classes: ['ancestry'], + actions: { + addFeature: AncestrySheet.#addFeature, + editFeature: AncestrySheet.#editFeature, + removeFeature: AncestrySheet.#removeFeature + }, + dragDrop: [{ dragSelector: null, dropSelector: '.tab.features .drop-section' }] }; /**@inheritdoc */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/ancestry/header.hbs' }, - ...super.PARTS + ...super.PARTS, + features: { template: 'systems/daggerheart/templates/sheets/items/ancestry/features.hbs' } }; + + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + + /** + * Add a new feature to the item, prompting the user for its type. + * @type {ApplicationClickAction} + */ + static async #addFeature(_event, button) { + const feature = await game.items.documentClass.create({ + type: 'feature', + name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }), + system: { + subType: button.dataset.type + } + }); + await this.document.update({ + 'system.features': [...this.document.system.features.map(x => x.uuid), feature.uuid] + }); + } + + /** + * Edit an existing feature on the item + * @type {ApplicationClickAction} + */ + static async #editFeature(_event, button) { + const target = button.closest('.feature-item'); + const feature = this.document.system[`${target.dataset.type}Feature`]; + if (!feature || Object.keys(feature).length === 0) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); + return; + } + + feature.sheet.render(true); + } + + /** + * Remove a feature from the item. + * @type {ApplicationClickAction} + */ + static async #removeFeature(event, button) { + event.stopPropagation(); + const target = button.closest('.feature-item'); + const feature = this.document.system[`${target.dataset.type}Feature`]; + const featureExists = feature && Object.keys(feature).length > 0; + + if (featureExists) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`TYPES.Item.feature`), + name: feature.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: feature.name }) + }); + if (!confirmed) return; + } + + if (featureExists && target.dataset.type === 'primary') await feature.update({ 'system.primary': null }); + await this.document.update({ + 'system.features': this.document.system.features.filter(x => x && x.uuid !== feature.uuid).map(x => x.uuid) + }); + } + + /* -------------------------------------------- */ + /* Application Drag/Drop */ + /* -------------------------------------------- */ + + /** + * On drop on the item. + * @param {DragEvent} event - The drag event + */ + async _onDrop(event) { + event.stopPropagation(); + event.preventDefault(); + + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); + + const item = await fromUuid(data.uuid); + if (item?.type === 'feature') { + const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary'; + await item.update({ 'system.subType': subType }); + + await this.document.update({ + 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] + }); + } + } } diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 55295455..b49105be 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -200,7 +200,7 @@ export default class ClassSheet extends DHBaseItemSheet { const actionPath = this.getActionPath(button.dataset.type); const feature = this.document.system[actionPath].find(x => x?.id === target.dataset.featureId); if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsMissing')); + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); return; } diff --git a/module/applications/sheets/items/community.mjs b/module/applications/sheets/items/community.mjs index 92c3731e..387556d9 100644 --- a/module/applications/sheets/items/community.mjs +++ b/module/applications/sheets/items/community.mjs @@ -9,6 +9,10 @@ export default class CommunitySheet extends DHHeritageSheet { /**@inheritdoc */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/community/header.hbs' }, - ...super.PARTS + ...super.PARTS, + feature: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-features.hbs', + scrollable: ['.feature'] + } }; } diff --git a/module/applications/sheets/items/subclass.mjs b/module/applications/sheets/items/subclass.mjs index 7761dcbc..31eca43a 100644 --- a/module/applications/sheets/items/subclass.mjs +++ b/module/applications/sheets/items/subclass.mjs @@ -50,7 +50,7 @@ export default class SubclassSheet extends DHBaseItemSheet { static async editFeature(_, button) { const feature = this.document.system[button.dataset.type]; if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsMissing')); + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); return; } @@ -71,7 +71,7 @@ export default class SubclassSheet extends DHBaseItemSheet { if (featureItem) { const feature = this.document.system[featureItem.dataset.type]; if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.notifications.featureIsMissing')); + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); return; } diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index c63a0008..ede5ef08 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -1320,6 +1320,11 @@ export const featureTypes = { } }; +export const featureSubTypes = { + primary: 'primary', + secondary: 'secondary' +}; + export const actionTypes = { passive: { id: 'passive', diff --git a/module/data/item/ancestry.mjs b/module/data/item/ancestry.mjs index 463fe6c1..71b7683d 100644 --- a/module/data/item/ancestry.mjs +++ b/module/data/item/ancestry.mjs @@ -18,4 +18,20 @@ export default class DHAncestry extends BaseDataItem { features: new ForeignDocumentUUIDArrayField({ type: 'Item' }) }; } + + get primaryFeature() { + return ( + this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.primary) ?? + (this.features.filter(x => !x).length > 0 ? {} : null) + ); + } + + get secondaryFeature() { + return ( + this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.secondary) ?? + (this.features.filter(x => !x || x.system.subType === CONFIG.DH.ITEM.featureSubTypes.primary).length > 1 + ? {} + : null) + ); + } } diff --git a/module/data/item/feature.mjs b/module/data/item/feature.mjs index 061772c8..62b955e9 100644 --- a/module/data/item/feature.mjs +++ b/module/data/item/feature.mjs @@ -22,6 +22,7 @@ export default class DHFeature extends BaseDataItem { nullable: true, initial: null }), + subType: new fields.StringField({ choices: CONFIG.DH.ITEM.featureSubTypes, nullable: true, initial: null }), originId: new fields.StringField({ nullable: true, initial: null }), identifier: new fields.StringField(), actions: new fields.ArrayField(new ActionField()) diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index bfeb103e..c789064b 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,9 +1,8 @@ -import DamageSelectionDialog from '../applications/dialogs/damageSelectionDialog.mjs'; -import { emitAsGM, emitAsOwner, GMUpdateEvent, socketEvent } from '../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; -import { damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs'; +import { damageKeyToNumber } from '../helpers/utils.mjs'; export default class DhpActor extends Actor { /** diff --git a/styles/less/dialog/character-creation/selections-container.less b/styles/less/dialog/character-creation/selections-container.less index 643a5c29..0c13fae9 100644 --- a/styles/less/dialog/character-creation/selections-container.less +++ b/styles/less/dialog/character-creation/selections-container.less @@ -1,11 +1,111 @@ @import '../../utils/colors.less'; +.appTheme({}, { + &.daggerheart.dh-style.dialog.character-creation { + .setup-tabs button { + background-image: url(../assets/parchments/dh-parchment-dark.png); + } + + nav a .descriptor { + background-image: url(../assets/parchments/dh-parchment-dark.png); + } + + .main-selections-container { + .ancestry-mixed-controller label { + background-image: url(../assets/parchments/dh-parchment-dark.png); + } + + .selections-container + .ancestry-preview-info-container + .ancestry-preview-features + .ancestry-preview-feature { + background-image: url(../assets/parchments/dh-parchment-light.png); + } + + .traits-container { + .suggested-traits-container .suggested-trait-container { + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + + .traits-inner-container .trait-container { + background: url('../assets/svg/trait-shield-light.svg') no-repeat; + + div { + filter: none; + text-shadow: none; + } + } + } + } + } +}); + .daggerheart.dh-style.dialog.character-creation { + .setup-tabs { + display: flex; + justify-content: center; + + button { + background-image: url(../assets/parchments/dh-parchment-light.png); + border-radius: 6px; + border-color: light-dark(@dark-blue, @golden); + color: light-dark(@beige, @dark); + } + } + .main-selections-container { display: flex; flex-direction: column; gap: 4px; + .ancestry-mixed-controller { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + gap: 4px; + margin-bottom: 8px; + + &.active { + label { + opacity: 1; + } + } + + label { + position: absolute; + font-size: 18px; + font-weight: bold; + padding: 0 2px; + background-image: url(../assets/parchments/dh-parchment-light.png); + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + opacity: 0.4; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; + } + + input { + width: 50%; + } + } + + .ancestry-name { + display: flex; + justify-content: center; + width: 100%; + margin-bottom: 8px; + + input { + width: 50%; + text-align: center; + } + } + .selections-container { width: 140px; display: flex; @@ -15,6 +115,43 @@ .card-preview-container { border-color: light-dark(@dark-blue, @golden); } + + .ancestry-preview-info-container { + display: flex; + flex-direction: column; + gap: 4px; + width: 100%; + + .ancestry-preview-label { + text-align: center; + font-weight: bold; + } + + .ancestry-preview-features { + display: flex; + flex-direction: column; + justify-content: end; + gap: 2px; + width: 100%; + padding: 0 2px 2px 2px; + + .ancestry-preview-feature { + flex: 1; + font-size: 14px; + white-space: wrap; + padding: 0 2px; + border: 1px solid light-dark(@golden, @dark-blue); + border-radius: 6px; + background-image: url(../assets/parchments/dh-parchment-dark.png); + color: light-dark(@dark, @beige); + height: min-content; + + &.inactive { + opacity: 0.2; + } + } + } + } } .selections-outer-container { @@ -27,6 +164,10 @@ border-radius: 8px; border-color: light-dark(@dark-blue, @golden); + &.inactive { + opacity: 0.2; + } + legend { margin-left: auto; margin-right: auto; @@ -44,6 +185,7 @@ legend { font-size: 20px; + white-space: nowrap; } .action-button { @@ -87,21 +229,38 @@ } .traits-inner-container { + width: 100%; display: flex; + align-items: center; justify-content: space-evenly; gap: 8px; .trait-container { - border: 1px solid light-dark(@dark-blue, @golden); - padding: 0 4px; + width: 60px; + height: 60px; + background: url(../assets/svg/trait-shield.svg) no-repeat; + + div { + filter: drop-shadow(0 0 3px black); + text-shadow: 0 0 3px black; + } + + select { + text-align: center; + width: 32px; + height: 24px; + position: relative; + top: 2px; + padding: 0; + } } } } .experiences-inner-container { display: flex; - justify-content: space-evenly; - text-align: center; + flex-direction: column; + gap: 8px; .experience-container { position: relative; diff --git a/templates/characterCreation/footer.hbs b/templates/characterCreation/footer.hbs index fb9b6a29..47321c7a 100644 --- a/templates/characterCreation/footer.hbs +++ b/templates/characterCreation/footer.hbs @@ -1,4 +1,8 @@ \ No newline at end of file diff --git a/templates/characterCreation/setupTabs/ancestry.hbs b/templates/characterCreation/setupTabs/ancestry.hbs new file mode 100644 index 00000000..1c87ca47 --- /dev/null +++ b/templates/characterCreation/setupTabs/ancestry.hbs @@ -0,0 +1,63 @@ +
+
+
+ {{localize "TYPES.Item.ancestry"}} + +
+ +
+ +
+ + +
+ +
+
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" primaryAncestry altPartialBlock=true secondaryDisabled=secondaryAncestry.uuid mixedAncestry=mixedAncestry }} + {{#if uuid}} +
+
{{name}}
+
+
{{system.primaryFeature.name}}
+
{{system.secondaryFeature.name}}
+
+
+ {{else}} + {{#if mixedAncestry}} + {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectPrimaryAncestry"}} + {{else}} + {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectAncestry"}} + {{/if}} + {{/if}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ + {{#if mixedAncestry}} +
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" secondaryAncestry altPartialBlock=true }} + {{#if uuid}} +
+
{{name}}
+
+
{{system.primaryFeature.name}}
+
{{system.secondaryFeature.name}}
+
+
+ {{else}} + {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectSecondaryAncestry"}} + {{/if}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ {{/if}} +
+
+
+
\ No newline at end of file diff --git a/templates/characterCreation/setupTabs/class.hbs b/templates/characterCreation/setupTabs/class.hbs new file mode 100644 index 00000000..d2215b80 --- /dev/null +++ b/templates/characterCreation/setupTabs/class.hbs @@ -0,0 +1,24 @@ +
+
+
+ {{localize "TYPES.Item.class"}} +
+
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" class }} + {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectClass"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ +
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" subclass disabled=(not class.img) }} + {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectSubclass"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+
+
+
+
\ No newline at end of file diff --git a/templates/characterCreation/setupTabs/community.hbs b/templates/characterCreation/setupTabs/community.hbs new file mode 100644 index 00000000..32a0844c --- /dev/null +++ b/templates/characterCreation/setupTabs/community.hbs @@ -0,0 +1,18 @@ +
+
+
+ {{localize "TYPES.Item.community"}} +
+
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" community }} + {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectCommunity"}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+
+
+
+
\ No newline at end of file diff --git a/templates/characterCreation/setupTabs/domainCards.hbs b/templates/characterCreation/setupTabs/domainCards.hbs new file mode 100644 index 00000000..298d8859 --- /dev/null +++ b/templates/characterCreation/setupTabs/domainCards.hbs @@ -0,0 +1,22 @@ +
+
+
+ {{localize "TYPES.Item.domainCard"}} +
+ {{#each domainCards as |domainCard id|}} +
+ {{#> "systems/daggerheart/templates/components/card-preview.hbs" domainCard }} + {{#each @root.class.system.domains }} +
{{localize (concat "DAGGERHEART.GENERAL.Domain." this ".label")}}
+ {{/each}} + {{/"systems/daggerheart/templates/components/card-preview.hbs"}} +
+ {{/each}} +
+
+
+
\ No newline at end of file diff --git a/templates/characterCreation/setupTabs/experience.hbs b/templates/characterCreation/setupTabs/experience.hbs new file mode 100644 index 00000000..46c5b754 --- /dev/null +++ b/templates/characterCreation/setupTabs/experience.hbs @@ -0,0 +1,19 @@ +
+
+
+ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.initialExperiences"}} {{experience.nrSelected}}/{{experience.nrTotal}} +
+ {{#each experience.values as |experience id|}} +
+ +
{{numberFormat this.value sign=true}}
+
+ {{/each}} +
+
+
+
\ No newline at end of file diff --git a/templates/characterCreation/setupTabs/traits.hbs b/templates/characterCreation/setupTabs/traits.hbs new file mode 100644 index 00000000..94f66452 --- /dev/null +++ b/templates/characterCreation/setupTabs/traits.hbs @@ -0,0 +1,32 @@ +
+
+
+ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.traitIncreases"}} {{traits.nrSelected}}/{{traits.nrTotal}} +
+
+ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.suggestedTraits"}} +
+ {{#each suggestedTraits}} +
{{this}}
+ {{/each}} +
+ +
+
+ {{#each traits.values}} +
+
{{this.name}}
+ +
+ {{/each}} +
+
+
+
+
\ No newline at end of file diff --git a/templates/characterCreation/tabs/setup.hbs b/templates/characterCreation/tabs/setup.hbs index 3f21fbb4..1504898d 100644 --- a/templates/characterCreation/tabs/setup.hbs +++ b/templates/characterCreation/tabs/setup.hbs @@ -3,99 +3,11 @@ data-tab='{{tabs.setup.id}}' data-group='{{tabs.setup.group}}' > -
-
- {{localize "TYPES.Item.class"}} -
-
- {{#> "systems/daggerheart/templates/components/card-preview.hbs" class }} - {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectClass"}} - {{/"systems/daggerheart/templates/components/card-preview.hbs"}} -
- -
- {{#> "systems/daggerheart/templates/components/card-preview.hbs" subclass disabled=(not class.img) }} - {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectSubclass"}} - {{/"systems/daggerheart/templates/components/card-preview.hbs"}} -
-
-
- - {{#if (gte visibility 2)}} -
- {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.heritage"}} -
-
- {{#> "systems/daggerheart/templates/components/card-preview.hbs" ancestry }} - {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectAncestry"}} - {{/"systems/daggerheart/templates/components/card-preview.hbs"}} -
- -
- {{#> "systems/daggerheart/templates/components/card-preview.hbs" community }} - {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectCommunity"}} - {{/"systems/daggerheart/templates/components/card-preview.hbs"}} -
-
-
- {{/if}} - - {{#if (gte visibility 3)}} -
- {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.traitIncreases"}} {{traits.nrSelected}}/{{traits.nrTotal}} -
-
- {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.suggestedTraits"}} -
- {{#each suggestedTraits}} -
{{this}}
- {{/each}} -
- -
-
- {{#each traits.values}} -
-
{{this.name}}
- -
- {{/each}} -
-
-
- {{/if}} - - {{#if (gte visibility 4)}} -
- {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.initialExperiences"}} {{experience.nrSelected}}/{{experience.nrTotal}} -
- {{#each experience.values as |experience id|}} -
- -
{{numberFormat this.value sign=true}}
-
- {{/each}} -
-
- {{/if}} - - {{#if (gte visibility 5)}} -
- {{localize "TYPES.Item.domainCard"}} -
- {{#each domainCards as |domainCard id|}} -
- {{#> "systems/daggerheart/templates/components/card-preview.hbs" domainCard }} - {{#each @root.class.system.domains }} -
{{localize (concat "DAGGERHEART.GENERAL.Domain." this ".label")}}
- {{/each}} - {{/"systems/daggerheart/templates/components/card-preview.hbs"}} -
- {{/each}} -
-
- {{/if}} -
+ \ No newline at end of file diff --git a/templates/components/card-preview.hbs b/templates/components/card-preview.hbs index da2abb77..08d4ca01 100644 --- a/templates/components/card-preview.hbs +++ b/templates/components/card-preview.hbs @@ -4,7 +4,13 @@ > {{#if this.img}} -
{{this.name}}
+
+ {{#if altPartialBlock}} + {{> @partial-block }} + {{else}} + {{this.name}} + {{/if}} +
{{else}}
diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 9c72526e..a09063d4 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -15,7 +15,7 @@ {{#if (or document.system.needsCharacterSetup document.system.levelData.canLevelUp)}}