From ece00c4fcb86952b79591fe3469d838e6d55380f Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 8 Jun 2025 18:49:42 +0200 Subject: [PATCH] Fixed up data model and a basic placeholder template --- lang/en.json | 56 +++++- module/applications/sheets/environment.mjs | 165 +++++++----------- module/config/actorConfig.mjs | 19 ++ module/config/generalConfig.mjs | 24 ++- module/data/adversary.mjs | 5 +- module/data/environment.mjs | 36 ++-- module/helpers/handlebarsHelper.mjs | 5 + styles/daggerheart.css | 25 +++ styles/less/global/elements.less | 4 + styles/sheets/environment.less | 27 +++ styles/sheets/sheets.less | 1 + system.json | 16 +- .../sheets/actors/environment/header.hbs | 9 + .../sheets/actors/environment/information.hbs | 16 ++ templates/sheets/actors/environment/main.hbs | 37 ++++ 15 files changed, 300 insertions(+), 145 deletions(-) create mode 100644 styles/sheets/environment.less create mode 100644 templates/sheets/actors/environment/header.hbs create mode 100644 templates/sheets/actors/environment/information.hbs create mode 100644 templates/sheets/actors/environment/main.hbs diff --git a/lang/en.json b/lang/en.json index eb5ecd62..26cedccb 100755 --- a/lang/en.json +++ b/lang/en.json @@ -261,6 +261,12 @@ "Description": "When an effect makes a creature Restrained, it means they cannot move until this condition is cleared.\nThey can still take actions from their current position." } }, + "Tiers": { + "tier1": "Tier 1", + "tier2": "Tier 2", + "tier3": "Tier 3", + "tier4": "Tier 4" + }, "Adversary": { "Bruiser": { "Name": "Bruiser", @@ -320,6 +326,26 @@ } } }, + "Environment": { + "Type": { + "Exploration": { + "label": "Exploration", + "description": "" + }, + "Social": { + "label": "Social", + "description": "" + }, + "Traversal": { + "label": "Traversal", + "description": "" + }, + "Event": { + "label": "Event", + "description": "" + } + } + }, "Domains": { "Arcana": { "Description": "This is the domain of the innate or instinctual use of magic. Those who walk this path tap into the raw, enigmatic forces of the realms to manipulate both the elements and their own energy. Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled." @@ -1030,9 +1056,33 @@ "NewFeature": "New Feature" }, "Environment": { - "ToneAndFeel": "Tone And feel", - "PotentialAdversaries": "Potential Adversaries", - "NewFeature": "New Feature" + "FIELDS": { + "tier": { + "label": "Tier" + }, + "type": { + "label": "Type" + }, + "difficulty": { + "label": "Difficulty" + } + }, + "Tabs": { + "Main": "Data", + "Information": "Information" + }, + "general": "General", + "newAdversary": "New Adversary", + "newFeature": "New feature", + "description": "Description", + "impulses": "Impulses", + "potentialAdversaries": { + "label": "Potential Adversaries", + "placeholder": "Optionally drag and drop adversaries here" + }, + "features": { + "label": "Features" + } }, "Armor": { "baseScore": "Base Score", diff --git a/module/applications/sheets/environment.mjs b/module/applications/sheets/environment.mjs index 8799d41a..7c59fd55 100644 --- a/module/applications/sheets/environment.mjs +++ b/module/applications/sheets/environment.mjs @@ -1,78 +1,60 @@ import DaggerheartSheet from './daggerheart-sheet.mjs'; -const { DocumentSheetV2 } = foundry.applications.api; -export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) { - constructor(options) { - super(options); - - this.editMode = false; - } - +const { ActorSheetV2 } = foundry.applications.sheets; +export default class DhpEnvironment extends DaggerheartSheet(ActorSheetV2) { static DEFAULT_OPTIONS = { tag: 'form', - classes: ['daggerheart', 'sheet', 'adversary', 'environment'], + classes: ['daggerheart', 'sheet', 'actor', 'dh-style', 'environment'], position: { - width: 600, - height: 'auto' + width: 450, + height: 1000 }, actions: { - toggleSlider: this.toggleSlider, - viewFeature: this.viewFeature, + addAdversary: this.addAdversary, addFeature: this.addFeature, - removeFeature: this.removeFeature, - addTone: this.addTone, - removeTone: this.removeTone, - useFeature: this.useFeature + deleteProperty: this.deleteProperty, + viewAdversary: this.viewAdversary }, form: { handler: this._updateForm, - closeOnSubmit: false, - submitOnChange: true - } + submitOnChange: true, + closeOnSubmit: false + }, + dragDrop: [{ dragSelector: null, dropSelector: '.adversary-container' }] }; - /** @override */ static PARTS = { - form: { - id: 'form', - template: 'systems/daggerheart/templates/sheets/environment.hbs' - } + header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' }, + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + main: { template: 'systems/daggerheart/templates/sheets/actors/environment/main.hbs' }, + information: { template: 'systems/daggerheart/templates/sheets/actors/environment/information.hbs' } }; - /* -------------------------------------------- */ - - /** @inheritDoc */ - get title() { - return `${game.i18n.localize('Environment')} - ${this.document.name}`; - } + static TABS = { + main: { + active: true, + cssClass: '', + group: 'primary', + id: 'main', + icon: null, + label: 'DAGGERHEART.Sheets.Environment.Tabs.Main' + }, + information: { + active: false, + cssClass: '', + group: 'primary', + id: 'information', + icon: null, + label: 'DAGGERHEART.Sheets.Environment.Tabs.Information' + } + }; async _prepareContext(_options) { - return { - title: `${this.document.name} - ${game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name)}`, - user: this.document, - source: this.document.toObject(), - fields: this.document.schema.fields, - data: { - type: game.i18n.localize(SYSTEM.ACTOR.adversaryTypes[this.document.system.type].name), - features: this.document.items.reduce((acc, x) => { - if (x.type === 'feature') { - const feature = x.toObject(); - acc.push({ - ...feature, - system: { - ...feature.system, - actionType: game.i18n.localize(SYSTEM.ITEM.actionTypes[feature.system.actionType].name) - }, - uuid: x.uuid - }); - } + const context = await super._prepareContext(_options); + context.document = this.document; + context.tabs = super._getTabs(this.constructor.TABS); - return acc; - }, []) - }, - editMode: this.editMode, - config: SYSTEM - }; + return context; } static async _updateForm(event, _, formData) { @@ -80,60 +62,41 @@ export default class DhpEnvironment extends DaggerheartSheet(DocumentSheetV2) { this.render(); } - static toggleSlider() { - this.editMode = !this.editMode; + static async addAdversary() { + await this.document.update({ + [`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize( + 'DAGGERHEART.Sheets.Environment.newAdversary' + ) + }); this.render(); } - static async viewFeature(_, button) { - const move = await fromUuid(button.dataset.feature); - move.sheet.render(true); - } - static async addFeature() { - const result = await this.document.createEmbeddedDocuments('Item', [ - { - name: game.i18n.localize('DAGGERHEART.Sheets.Environment.NewFeature'), - type: 'feature' - } - ]); - - await result[0].sheet.render(true); + ui.notifications.error('Not Implemented yet. Awaiting datamodel rework'); } - static async removeFeature(_, button) { - await this.document.items.find(x => x.uuid === button.dataset.feature).delete(); + static async deleteProperty(_, target) { + await this.document.update({ [`${target.dataset.path}.-=${target.id}`]: null }); + this.render(); } - static async addTone() { - await this.document.update({ 'system.toneAndFeel': [...this.document.system.toneAndFeel, ''] }); + static async viewAdversary(_, button) { + const adversary = foundry.utils.getProperty( + this.document.system.potentialAdversaries, + `${button.dataset.potentialAdversary}.adversaries.${button.dataset.adversary}` + ); + adversary.sheet.render(true); } - static async removeTone(button) { - await this.document.update({ - 'system.toneAndFeel': this.document.system.toneAndFeel.filter( - (_, index) => index !== Number.parseInt(button.dataset.tone) - ) - }); - } - - static async useFeature(_, button) { - const item = this.document.items.find(x => x.uuid === button.dataset.feature); - - const cls = getDocumentClass('ChatMessage'); - const msg = new cls({ - user: game.user.id, - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/chat/ability-use.hbs', - { - title: game.i18n.format('DAGGERHEART.Chat.EnvironmentTitle', { - actionType: button.dataset.actionType - }), - card: { name: item.name, img: item.img, description: item.system.description } - } - ) - }); - - cls.create(msg.toObject()); + async _onDrop(event) { + const data = TextEditor.getDragEventData(event); + const item = await fromUuid(data.uuid); + if (item.type === 'adversary') { + const target = event.target.closest('.adversary-container'); + const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries.${item.id}`; + await this.document.update({ + [path]: item.uuid + }); + } } } diff --git a/module/config/actorConfig.mjs b/module/config/actorConfig.mjs index 4db5ca9c..bcb6ee8b 100644 --- a/module/config/actorConfig.mjs +++ b/module/config/actorConfig.mjs @@ -123,6 +123,25 @@ export const adversaryTypes = { } }; +export const environmentTypes = { + exploration: { + label: 'DAGGERHEART.Environment.Type.Exploration.label', + description: 'DAGGERHEART.Environment.Type.Exploration.description' + }, + social: { + label: 'DAGGERHEART.Environment.Type.Social.label', + description: 'DAGGERHEART.Environment.Type.Social.description' + }, + traversal: { + label: 'DAGGERHEART.Environment.Type.Traversal.label', + description: 'DAGGERHEART.Environment.Type.Traversal.description' + }, + event: { + label: 'DAGGERHEART.Environment.Type.Event.label', + description: 'DAGGERHEART.Environment.Type.Event.description' + } +}; + export const adversaryTraits = { relentless: { name: 'DAGGERHEART.Adversary.Trait..Name', diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index fb596347..47736284 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -175,25 +175,21 @@ export const deathMoves = { }; export const tiers = { - 0: { - key: 0, - id: 'tier0', - name: 'DAGGERHEART.General.Tier.0' - }, - 1: { - key: 1, + tier1: { id: 'tier1', - name: 'DAGGERHEART.General.Tier.1' + label: 'DAGGERHEART.Tiers.tier1' }, - 2: { - key: 2, + tier2: { id: 'tier2', - name: 'DAGGERHEART.General.Tier.2' + label: 'DAGGERHEART.Tiers.tier2' }, - 3: { - key: 3, + tier3: { id: 'tier3', - name: 'DAGGERHEART.General.Tier.3' + label: 'DAGGERHEART.Tiers.tier3' + }, + tier4: { + id: 'tier4', + label: 'DAGGERHEART.Tiers.tier4' } }; diff --git a/module/data/adversary.mjs b/module/data/adversary.mjs index 3e8cdaf6..0f25b62d 100644 --- a/module/data/adversary.mjs +++ b/module/data/adversary.mjs @@ -14,7 +14,10 @@ export default class DhpAdversary extends foundry.abstract.TypeDataModel { max: new fields.NumberField({ initial: 0, integer: true }) }) }), - tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }), + tier: new fields.StringField({ + choices: Object.keys(SYSTEM.GENERAL.tiers), + initial: SYSTEM.GENERAL.tiers.tier1.id + }), type: new fields.StringField({ choices: Object.keys(SYSTEM.ACTOR.adversaryTypes), integer: false, diff --git a/module/data/environment.mjs b/module/data/environment.mjs index 23e9cd25..a2cd7529 100644 --- a/module/data/environment.mjs +++ b/module/data/environment.mjs @@ -1,22 +1,28 @@ -export default class DhpEnvironment extends foundry.abstract.TypeDataModel { +import { environmentTypes } from '../config/actorConfig.mjs'; +import ForeignDocumentUUIDField from './fields/foreignDocumentUUIDField.mjs'; + +export default class DhEnvironment extends foundry.abstract.TypeDataModel { + static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Environment']; + static defineSchema() { const fields = foundry.data.fields; return { - resources: new fields.SchemaField({}), - tier: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.tiers), integer: false }), - type: new fields.StringField({ - choices: Object.keys(SYSTEM.ACTOR.adversaryTypes), - integer: false, - initial: Object.keys(SYSTEM.ACTOR.adversaryTypes).find(x => x === 'standard') + tier: new fields.StringField({ + required: true, + choices: SYSTEM.GENERAL.tiers, + initial: SYSTEM.GENERAL.tiers.tier1.id }), - description: new fields.StringField({}), - toneAndFeel: new fields.StringField({}), - difficulty: new fields.NumberField({ initial: 1, integer: true }), - potentialAdversaries: new fields.StringField({}) + type: new fields.StringField({ choices: environmentTypes }), + description: new fields.HTMLField(), + impulses: new fields.HTMLField(), + difficulty: new fields.NumberField({ required: true, initial: 11, integer: true }), + potentialAdversaries: new fields.TypedObjectField( + new fields.SchemaField({ + label: new fields.StringField(), + adversaries: new fields.TypedObjectField(new ForeignDocumentUUIDField({ type: 'Actor' })) + }) + ) + /* Features pending datamodel rework */ }; } - - get features() { - return this.parent.items.filter(x => x.type === 'feature'); - } } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 87d1fb7f..25dd0e5e 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -11,6 +11,7 @@ export default class RegisterHandlebarsHelpers { includes: this.includes, debug: this.debug, signedNumber: this.signedNumber, + length: this.length, switch: this.switch, case: this.case }); @@ -82,6 +83,10 @@ export default class RegisterHandlebarsHelpers { return number >= 0 ? `+${number}` : number; } + static length(obj) { + return Object.keys(obj).length; + } + static switch(value, options) { this.switch_value = value; this.switch_break = false; diff --git a/styles/daggerheart.css b/styles/daggerheart.css index 78af5ecc..cb4ede19 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -2593,6 +2593,28 @@ div.daggerheart.views.multiclass { width: 40px; background: white; } +.daggerheart.sheet.actor.environment .potential-adversary-container { + width: 100%; + height: 50px; +} +.daggerheart.sheet.actor.environment .potential-adversary-container .adversary-placeholder { + font-style: italic; + text-align: center; + opacity: 0.6; +} +.daggerheart.sheet.actor.environment .potential-adversary-container .adversaries-container { + display: flex; + gap: 8px; +} +.daggerheart.sheet.actor.environment .potential-adversary-container .adversaries-container .adversary-container { + border: 1px solid var(--color-dark-5); + border-radius: 6px; + padding: 0 2px; + font-weight: bold; + cursor: pointer; + background-image: url(../assets/parchments/dh-parchment-dark.png); + color: var(--color-light-3); +} .daggerheart.sheet .title-container { display: flex; gap: 8px; @@ -3354,6 +3376,9 @@ div.daggerheart.views.multiclass { grid-template-columns: 1fr 2fr; gap: 10px; } +.application.sheet.dh-style fieldset.two-columns.even { + grid-template-columns: 1fr 1fr; +} .application.sheet.dh-style fieldset legend { font-family: 'Montserrat', sans-serif; font-weight: bold; diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 077d2226..14345ca6 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -103,6 +103,10 @@ display: grid; grid-template-columns: 1fr 2fr; gap: 10px; + + &.even { + grid-template-columns: 1fr 1fr; + } } legend { diff --git a/styles/sheets/environment.less b/styles/sheets/environment.less new file mode 100644 index 00000000..d534de38 --- /dev/null +++ b/styles/sheets/environment.less @@ -0,0 +1,27 @@ +.daggerheart.sheet.actor.environment { + .potential-adversary-container { + width: 100%; + height: 50px; + + .adversary-placeholder { + font-style: italic; + text-align: center; + opacity: 0.6; + } + + .adversaries-container { + display: flex; + gap: 8px; + + .adversary-container { + border: 1px solid var(--color-dark-5); + border-radius: 6px; + padding: 0 2px; + font-weight: bold; + cursor: pointer; + background-image: url(../assets/parchments/dh-parchment-dark.png); + color: var(--color-light-3); + } + } + } +} diff --git a/styles/sheets/sheets.less b/styles/sheets/sheets.less index 19c76980..3ce23be8 100644 --- a/styles/sheets/sheets.less +++ b/styles/sheets/sheets.less @@ -1,6 +1,7 @@ @import './heritage.less'; @import './class.less'; @import './adversary.less'; +@import './environment.less'; .daggerheart.sheet { .title-container { diff --git a/system.json b/system.json index f90f53c8..1717e6e6 100644 --- a/system.json +++ b/system.json @@ -163,10 +163,7 @@ "name": "Daggerheart", "sorting": "m", "color": "#08718c", - "packs": [ - "adversaries", - "environments" - ], + "packs": ["adversaries", "environments"], "folders": [ { "name": "Character Options", @@ -186,12 +183,7 @@ "name": "Items", "sorting": "m", "color": "#000000", - "packs": [ - "weapons", - "armors", - "consumables", - "general-items" - ] + "packs": ["weapons", "armors", "consumables", "general-items"] } ] } @@ -213,7 +205,9 @@ "Actor": { "pc": {}, "adversary": {}, - "environment": {} + "environment": { + "htmlFields": ["description", "impulses"] + } }, "Item": { "ancestry": { diff --git a/templates/sheets/actors/environment/header.hbs b/templates/sheets/actors/environment/header.hbs new file mode 100644 index 00000000..a21f5f64 --- /dev/null +++ b/templates/sheets/actors/environment/header.hbs @@ -0,0 +1,9 @@ +
+ +
+

+
+

{{localize 'TYPES.Actor.environment'}}

+
+
+
\ No newline at end of file diff --git a/templates/sheets/actors/environment/information.hbs b/templates/sheets/actors/environment/information.hbs new file mode 100644 index 00000000..3b49a933 --- /dev/null +++ b/templates/sheets/actors/environment/information.hbs @@ -0,0 +1,16 @@ +
+
+ {{localize "DAGGERHEART.Sheets.Environment.description"}} + + {{formInput systemFields.description value=source.system.description }} +
+
+ {{localize "DAGGERHEART.Sheets.Environment.impulses"}} + + {{formInput systemFields.impulses value=source.system.impulses }} +
+
\ No newline at end of file diff --git a/templates/sheets/actors/environment/main.hbs b/templates/sheets/actors/environment/main.hbs new file mode 100644 index 00000000..a973cdc9 --- /dev/null +++ b/templates/sheets/actors/environment/main.hbs @@ -0,0 +1,37 @@ +
+
+ {{localize "DAGGERHEART.Sheets.Environment.general"}} + + {{formGroup systemFields.tier value=source.system.tier localize=true }} + {{formGroup systemFields.type value=source.system.type localize=true }} + {{formGroup systemFields.difficulty value=source.system.difficulty localize=true }} +
+ +
+ {{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.label"}} + + {{#each source.system.potentialAdversaries}} +
+ + {{#if (eq (length this.adversaries) 0)}} +
{{localize "DAGGERHEART.Sheets.Environment.potentialAdversaries.placeholder"}}
+ {{else}} +
+ {{#each this.adversaries as |adversary id|}} +
{{adversary.name}}
+ {{/each}} +
+ {{/if}} +
+ {{/each}} +
+ +
+ {{localize "DAGGERHEART.Sheets.Environment.features.label"}} + +
+
\ No newline at end of file