diff --git a/daggerheart.mjs b/daggerheart.mjs index f1d8c67a..2f3e861b 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -62,6 +62,7 @@ CONFIG.Token.rulerClass = placeables.DhTokenRuler; CONFIG.Token.hudClass = applications.hud.DHTokenHUD; CONFIG.ui.combat = applications.ui.DhCombatTracker; +CONFIG.ui.nav = applications.ui.DhSceneNavigation; CONFIG.ui.chat = applications.ui.DhChatLog; CONFIG.ui.effectsDisplay = applications.ui.DhEffectsDisplay; CONFIG.ui.hotbar = applications.ui.DhHotbar; diff --git a/lang/en.json b/lang/en.json index 3f8c4321..b2f4413e 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1016,6 +1016,14 @@ "spell": "Spell", "grimoire": "Grimoire" }, + "EnvironmentIcons": { + "city": "City", + "dungeon": "Dungeon", + "mountain": "Mountain", + "social": "Social", + "tree": "Tree", + "water": "Water" + }, "EnvironmentType": { "exploration": { "label": "Exploration", @@ -2567,7 +2575,9 @@ } }, "disabledText": "Daggerheart Measurements are disabled in System Settings - Variant Rules", - "rangeMeasurement": "Range Measurement" + "rangeMeasurement": "Range Measurement", + "sceneEnvironments": "Scene Environments", + "noEnvironmentLinked": "Drag in an environment" } }, "UI": { diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs index be8f7b71..5db6a502 100644 --- a/module/applications/scene/sceneConfigSettings.mjs +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -1,11 +1,12 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig { - // static DEFAULT_OPTIONS = { - // ...super.DEFAULT_OPTIONS, - // form: { - // handler: this.updateData, - // closeOnSubmit: true - // } - // }; + static DEFAULT_OPTIONS = { + ...super.DEFAULT_OPTIONS, + actions: { + ...super.DEFAULT_OPTIONS.actions, + addSceneEnvironment: DhSceneConfigSettings.#addSceneEnvironment, + removeSceneEnvironment: DhSceneConfigSettings.#removeSceneEnvironment + } + }; static buildParts() { const { footer, tabs, ...parts } = super.PARTS; @@ -30,6 +31,7 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); + switch (partId) { case 'dh': htmlElement.querySelector('#rangeMeasurementSetting')?.addEventListener('change', async event => { @@ -39,10 +41,37 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S this.document.flags.daggerheart = flagData; this.render(); }); + + htmlElement.querySelectorAll('.scene-environment').forEach(element => { + element.querySelector('select')?.addEventListener('change', async event => { + const id = event.target.dataset.key; + const flagData = foundry.utils.mergeObject(this.document.flags.daggerheart, { + sceneEnvironments: { [id]: { icon: event.target.value } } + }); + this.document.flags.daggerheart = flagData; + this.render(); + }); + + element.ondrop = this._onDrop.bind(this); + }); + break; } } + async _onDrop(event) { + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); + const item = await foundry.utils.fromUuid(data.uuid); + if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') { + const element = event.target.closest('.scene-environment'); + const flagData = foundry.utils.mergeObject(this.document.flags.daggerheart, { + sceneEnvironments: { [element.dataset.key]: { environment: data.uuid } } + }); + this.document.flags.daggerheart = flagData; + this.render(); + } + } + /** @inheritDoc */ async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options); @@ -50,14 +79,41 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S case 'dh': context.data = new game.system.api.data.scenes.DHScene(canvas.scene.flags.daggerheart); context.variantRules = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules); + context.environmentIcons = CONFIG.DH.GENERAL.environmentIcons; break; } return context; } - // static async updateData(event, _, formData) { - // const data = foundry.utils.expandObject(formData.object); - // this.close(data); - // } + static async #addSceneEnvironment() { + const flagData = foundry.utils.mergeObject(this.document.flags.daggerheart, { + sceneEnvironments: { [foundry.utils.randomID()]: { icon: CONFIG.DH.GENERAL.environmentIcons.tree.icon } } + }); + this.document.flags.daggerheart = flagData; + + this.render(); + } + + static async #removeSceneEnvironment(_event, button) { + this.document.flags.daggerheart.sceneEnvironments = Object.keys( + this.document.flags.daggerheart.sceneEnvironments + ).reduce((acc, key) => { + if (key !== button.dataset.key) acc[key] = this.document.flags.daggerheart.sceneEnvironments[key]; + return acc; + }, {}); + this.render(); + } + + /** @override */ + async _processSubmitData(event, form, submitData, options) { + submitData.flags.daggerheart.sceneEnvironments = this.document.flags.daggerheart.sceneEnvironments; + for (const key of Object.keys(this.document._source.flags.daggerheart.sceneEnvironments)) { + if (!submitData.flags.daggerheart.sceneEnvironments[key]) { + submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null; + } + } + + super._processSubmitData(event, form, submitData, options); + } } diff --git a/module/applications/ui/_module.mjs b/module/applications/ui/_module.mjs index d5f31906..8c5c020e 100644 --- a/module/applications/ui/_module.mjs +++ b/module/applications/ui/_module.mjs @@ -5,4 +5,5 @@ export { default as DhCombatTracker } from './combatTracker.mjs'; export { default as DhEffectsDisplay } from './effectsDisplay.mjs'; export { default as DhFearTracker } from './fearTracker.mjs'; export { default as DhHotbar } from './hotbar.mjs'; +export { default as DhSceneNavigation } from './sceneNavigation.mjs'; export { ItemBrowser } from './itemBrowser.mjs'; diff --git a/module/applications/ui/sceneNavigation.mjs b/module/applications/ui/sceneNavigation.mjs new file mode 100644 index 00000000..fbad056b --- /dev/null +++ b/module/applications/ui/sceneNavigation.mjs @@ -0,0 +1,54 @@ +export default class DhSceneNavigation extends foundry.applications.ui.SceneNavigation { + /** @inheritdoc */ + static DEFAULT_OPTIONS = { + ...super.DEFAULT_OPTIONS, + classes: ['faded-ui', 'flexcol', 'scene-navigation'], + actions: { + openSceneEnvironment: DhSceneNavigation.#openSceneEnvironment + } + }; + + /** @inheritdoc */ + static PARTS = { + scenes: { + root: true, + template: 'systems/daggerheart/templates/ui/sceneNavigation/scene-navigation.hbs' + } + }; + + /** @inheritdoc */ + async _prepareContext(options) { + const context = await super._prepareContext(options); + + const extendScenes = scenes => + scenes.map(x => { + const scene = game.scenes.get(x.id); + if (!scene.flags.daggerheart) return x; + + const daggerheartInfo = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart); + const environmentKeys = Object.keys(daggerheartInfo.sceneEnvironments); + const hasEnvironments = environmentKeys.length; + return { + ...x, + hasEnvironments, + environmentImage: hasEnvironments + ? daggerheartInfo.sceneEnvironments[environmentKeys[0]].environment.img + : null, + environments: daggerheartInfo.sceneEnvironments + }; + }); + context.scenes.active = extendScenes(context.scenes.active); + context.scenes.inactive = extendScenes(context.scenes.inactive); + + return context; + } + + static async #openSceneEnvironment(_event, button) { + const scene = game.scenes.get(button.dataset.sceneId); + const sceneEnvironments = Object.keys(scene.flags.daggerheart.sceneEnvironments); + const environment = await foundry.utils.fromUuid( + scene.flags.daggerheart.sceneEnvironments[sceneEnvironments[0]].environment + ); + environment.sheet.render(true); + } +} diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 3f49f7aa..33a6370e 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -731,3 +731,36 @@ export const sceneRangeMeasurementSetting = { label: 'Custom' } }; + +export const environmentIcons = { + tree: { + name: 'DAGGERHEART.CONFIG.EnvironmentIcons.tree', + icon: 'fa-solid fa-tree', + unicode: '' + }, + mountain: { + name: 'DAGGERHEART.CONFIG.EnvironmentIcons.mountain', + icon: 'fa-solid fa-mountain', + unicode: '' + }, + city: { + name: 'DAGGERHEART.CONFIG.EnvironmentIcons.city', + icon: 'fa-solid fa-house', + unicode: '' + }, + dungeon: { + name: 'DAGGERHEART.CONFIG.EnvironmentIcons.dungeon', + icon: 'fa-solid fa-dungeon', + unicode: '' + }, + water: { + name: 'DAGGERHEART.CONFIG.EnvironmentIcons.water', + icon: 'fa-solid fa-water', + unicode: '' + }, + social: { + name: 'DAGGERHEART.CONFIG.EnvironmentIcons.social', + icon: 'fa-solid fa-masks-theater', + unicode: '' + } +}; diff --git a/module/data/scene/scene.mjs b/module/data/scene/scene.mjs index 7cf74ade..e3796936 100644 --- a/module/data/scene/scene.mjs +++ b/module/data/scene/scene.mjs @@ -1,3 +1,5 @@ +import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; + export default class DHScene extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; @@ -13,7 +15,16 @@ export default class DHScene extends foundry.abstract.DataModel { veryClose: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.veryClose.name' }), close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }), far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' }) - }) + }), + sceneEnvironments: new fields.TypedObjectField( + new fields.SchemaField({ + environment: new ForeignDocumentUUIDField({ type: 'Actor' }), + icon: new fields.StringField({ + required: true, + initial: CONFIG.DH.GENERAL.environmentIcons.tree.icon + }) + }) + ) }; } } diff --git a/styles/less/ui/index.less b/styles/less/ui/index.less index 7f9ada25..25f51d0f 100644 --- a/styles/less/ui/index.less +++ b/styles/less/ui/index.less @@ -33,3 +33,5 @@ @import './scene-config/scene-config.less'; @import './effects-display/sheet.less'; + +@import './scene-navigation/scene-navigation.less'; diff --git a/styles/less/ui/scene-config/scene-config.less b/styles/less/ui/scene-config/scene-config.less index fb36dd33..664e7526 100644 --- a/styles/less/ui/scene-config/scene-config.less +++ b/styles/less/ui/scene-config/scene-config.less @@ -37,4 +37,63 @@ .helper-text { font-style: italic; } + + .scene-environments { + display: flex; + flex-direction: column; + gap: 8px; + + .scene-environment { + display: flex; + align-items: center; + gap: 8px; + + .scene-environment-inner { + display: flex; + align-items: center; + gap: 16px; + flex: 1; + + img { + height: 36px; + } + + h5 { + margin: 0; + } + + .tags { + display: flex; + gap: 4px; + padding-bottom: 0; + + .tag { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 3px 5px; + font-size: var(--font-size-12); + font: @font-body; + + background: light-dark(@dark-15, @beige-15); + border: 1px solid light-dark(@dark, @beige); + border-radius: 3px; + } + + .label { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + font-size: var(--font-size-12); + } + } + } + + .remove-icon { + font-size: 16px; + } + } + } } diff --git a/styles/less/ui/scene-navigation/scene-navigation.less b/styles/less/ui/scene-navigation/scene-navigation.less new file mode 100644 index 00000000..63c302c6 --- /dev/null +++ b/styles/less/ui/scene-navigation/scene-navigation.less @@ -0,0 +1,32 @@ +#ui-left #ui-left-column-2 { + flex: 0 0 230px; + + .scene-navigation { + .scene-wrapper { + display: flex; + gap: 2px; + height: var(--control-size); + width: 100%; + + .scene-environment { + padding: 0; + } + } + + .scene { + justify-content: center; + align-content: center; + background: var(--control-bg-color); + border: 1px solid var(--control-border-color); + border-radius: 4px; + color: var(--control-icon-color); + pointer-events: all; + transition: + border 0.25s, + color 0.25s; + text-shadow: none; + width: 200px; + max-width: 200px; + } + } +} diff --git a/templates/scene/dh-config.hbs b/templates/scene/dh-config.hbs index 1f7dcd81..c6446eb8 100644 --- a/templates/scene/dh-config.hbs +++ b/templates/scene/dh-config.hbs @@ -21,4 +21,41 @@ {{localize "DAGGERHEART.SETTINGS.Scene.disabledText"}} {{/if}} + +
\ No newline at end of file diff --git a/templates/ui/sceneNavigation/scene-navigation.hbs b/templates/ui/sceneNavigation/scene-navigation.hbs new file mode 100644 index 00000000..0666eabf --- /dev/null +++ b/templates/ui/sceneNavigation/scene-navigation.hbs @@ -0,0 +1,36 @@ +