diff --git a/daggerheart.mjs b/daggerheart.mjs index b3af687c..5ee8bcfb 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -304,6 +304,7 @@ const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/views/actionTypes/roll.hbs', 'systems/daggerheart/templates/views/actionTypes/cost.hbs', 'systems/daggerheart/templates/views/actionTypes/range-target.hbs', - 'systems/daggerheart/templates/views/actionTypes/effect.hbs' + 'systems/daggerheart/templates/views/actionTypes/effect.hbs', + 'systems/daggerheart/templates/settings/components/settings-item-line.hbs' ]); }; diff --git a/lang/en.json b/lang/en.json index bed614d9..f1e60559 100755 --- a/lang/en.json +++ b/lang/en.json @@ -87,6 +87,11 @@ } }, "Homebrew": { + "NewDowntimeMove": "Downtime Move", + "DowntimeMoves": "Downtime Moves", + "NrChoices": "# Moves Per Rest", + "ResetMovesTitle": "Reset {type} Downtime Moves", + "ResetMovesText": "Are you sure you want to reset?", "FIELDS": { "maxFear": { "label": "Max Fear" }, "traitArray": { "label": "Initial Trait Modifiers" } @@ -499,29 +504,51 @@ } }, "Downtime": { - "TendToWounds": { - "Name": "Tend to Wounds", - "Description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead." + "DowntimeHeader": "Downtime Moves ({current}/{max})", + "ShortRest": { + "title": "Short Rest", + "TendToWounds": { + "Name": "Tend to Wounds", + "Description": "Describe how you hastily patch yourself up, then clear a number of Hit Points equal to 1d4 + your tier. You can do this to an ally instead." + }, + "ClearStress": { + "Name": "Clear Stress", + "Description": "Describe how you blow off steam or pull yourself together, then clear a number of Stress equal to 1d4 + your tier." + }, + "RepairArmor": { + "Name": "Repair Armor", + "Description": "Describe how you quickly repair your armor, then clear a number of Armor Slots equal to 1d4 + your tier. You can do this to an ally's armor instead." + }, + "Prepare": { + "Name": "Prepare", + "Description": "Describe how you prepare yourself for the path ahead, then gain a Hope. If you choose to Prepare with one or more members of your party, you each gain 2 Hope." + } }, - "ClearStress": { - "Name": "Clear Stress", - "Description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress." + "LongRest": { + "title": "Long Rest", + "TendToWounds": { + "Name": "Tend to Wounds", + "Description": "Describe how you patch yourself up and remove all marked Hit Points. You may also do this on an ally instead." + }, + "ClearStress": { + "Name": "Clear Stress", + "Description": "Describe how you blow off steam or pull yourself together, and clear all marked Stress." + }, + "RepairArmor": { + "Name": "Repair Armor", + "Description": "Describe how you spend time repairing your armor and clear all of its Armor Slots. You may also do this to an ally's armor instead." + }, + "Prepare": { + "Name": "Prepare", + "Description": "Describe how you are preparing for the next day's adventure, then gain a Hope. If you choose to Prepare with one or more members of your party, you may each take two Hope." + }, + "WorkOnAProject": { + "Name": "Work on a Project", + "Description": "Establish or continue work on a project." + } }, - "RepairArmor": { - "Name": "Repair Armor", - "Description": "Describe how you spend time repairing your armor and clear all of its Armor Slots. You may also do this to an ally’s armor instead." - }, - "Prepare": { - "Name": "Prepare", - "Description": "Describe how you are preparing for the next day’s adventure, then gain a Hope. If you choose to Prepare with one or more members of your party, you may each take two Hope." - }, - "WorkOnAProject": { - "Name": "Work on a Project", - "Description": "Establish or continue work on a project. The GM might ask for a roll to determine how much to tick down on the completion track." - }, - "Custom": { - "NamePlaceholder": "Custom Activity...", - "Placeholder": "A custom downtime activity description..." + "Notifications": { + "NoMoreMoves": "You cannot select any more downtime moves" } }, "DeathMoves": { diff --git a/module/applications/downtime.mjs b/module/applications/downtime.mjs index 49d8b1ab..d0fc34b6 100644 --- a/module/applications/downtime.mjs +++ b/module/applications/downtime.mjs @@ -1,3 +1,5 @@ +import { actionsTypes } from '../data/_module.mjs'; + const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV2) { @@ -5,25 +7,25 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV super({}); this.actor = actor; - this.selectedActivity = null; this.shortrest = shortrest; - this.customActivity = SYSTEM.GENERAL.downtime.custom; + const options = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).restMoves; + this.moveData = shortrest ? options.shortRest : options.longRest; } get title() { - return `${this.actor.name} - ${this.shortrest ? 'Short Rest' : 'Long Rest'}`; + return ''; } static DEFAULT_OPTIONS = { tag: 'form', classes: ['daggerheart', 'views', 'downtime'], - position: { width: 800, height: 'auto' }, + position: { width: 680, height: 'auto' }, actions: { - selectActivity: this.selectActivity, + selectMove: this.selectMove, takeDowntime: this.takeDowntime }, - form: { handler: this.updateData, submitOnChange: true } + form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } }; static PARTS = { @@ -33,51 +35,63 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV } }; + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + htmlElement + .querySelectorAll('.activity-image') + .forEach(element => element.addEventListener('contextmenu', this.deselectMove.bind(this))); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); context.selectedActivity = this.selectedActivity; - context.options = this.shortrest ? SYSTEM.GENERAL.downtime.shortRest : SYSTEM.GENERAL.downtime.longRest; - context.customActivity = this.customActivity; - - context.disabledDowntime = - !this.selectedActivity || - (this.selectedActivity.id === this.customActivity.id && - (!this.customActivity.name || !this.customActivity.description)); + context.moveData = this.moveData; + context.nrCurrentChoices = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); + context.disabledDowntime = context.nrCurrentChoices < context.moveData.nrChoices; return context; } - static selectActivity(_, button) { - const activity = button.dataset.activity; - this.selectedActivity = - activity === this.customActivity.id - ? this.customActivity - : this.shortrest - ? SYSTEM.GENERAL.downtime.shortRest[activity] - : SYSTEM.GENERAL.downtime.longRest[activity]; + static selectMove(_, button) { + const nrSelected = Object.values(this.moveData.moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); + if (nrSelected === this.moveData.nrChoices) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.Downtime.Notifications.NoMoreMoves')); + return; + } + + const move = button.dataset.move; + this.moveData.moves[move].selected = this.moveData.moves[move].selected + ? this.moveData.moves[move].selected + 1 + : 1; + + this.render(); + } + + deselectMove(event) { + const move = event.currentTarget.dataset.move; + this.moveData.moves[move].selected = this.moveData.moves[move].selected + ? this.moveData.moves[move].selected - 1 + : 0; this.render(); } static async takeDowntime() { - const refreshedFeatures = this.shortrest - ? this.actor.system.refreshableFeatures.shortRest - : [...this.actor.system.refreshableFeatures.shortRest, ...this.actor.system.refreshableFeatures.longRest]; - for (var feature of refreshedFeatures) { - await feature.system.refresh(); - } + const moves = Object.values(this.moveData.moves).filter(x => x.selected); const cls = getDocumentClass('ChatMessage'); const msg = new cls({ user: game.user.id, + system: { + moves: moves, + actor: this.actor.uuid + }, content: await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/chat/downtime.hbs', { - player: this.actor.name, - title: game.i18n.localize(this.selectedActivity.name), - img: this.selectedActivity.img, - description: game.i18n.localize(this.selectedActivity.description), - refreshedFeatures: refreshedFeatures + title: `${this.actor.name} - ${game.i18n.localize(`DAGGERHEART.Downtime.${this.shortRest ? 'ShortRest' : 'LongRest'}.title`)}`, + moves: moves } ) }); diff --git a/module/applications/settings/components/settingsActionsView.mjs b/module/applications/settings/components/settingsActionsView.mjs new file mode 100644 index 00000000..9b223ec5 --- /dev/null +++ b/module/applications/settings/components/settingsActionsView.mjs @@ -0,0 +1,144 @@ +import { actionsTypes } from '../../../data/_module.mjs'; +import DHActionConfig from '../../config/Action.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(resolve, reject, title, name, img, description, actions) { + super({}); + + this.resolve = resolve; + this.reject = reject; + this.viewTitle = title; + this.name = name; + this.img = img; + this.description = description; + this.actions = actions; + } + + get title() { + return this.viewTitle; + } + + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'setting', 'dh-style'], + position: { width: '400', height: 'auto' }, + actions: { + editImage: this.onEditImage, + addItem: this.addItem, + editItem: this.editItem, + removeItem: this.removeItem, + resetMoves: this.resetMoves, + saveForm: this.saveForm + }, + form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/settings/components/action-view-header.hbs' }, + main: { + template: 'systems/daggerheart/templates/settings/components/action-view.hbs' + }, + footer: { template: 'systems/daggerheart/templates/settings/components/action-view-footer.hbs' } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.name = this.name; + context.img = this.img; + context.description = this.description; + context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.description); + context.actions = this.actions; + + return context; + } + + static async updateData(event, element, formData) { + const { name, img, description } = foundry.utils.expandObject(formData.object); + this.name = name; + this.description = description; + + this.render(); + } + + static async saveForm(event) { + this.resolve({ + name: this.name, + img: this.img, + description: this.description, + actions: this.actions + }); + this.close(true); + } + + static close(fromSave) { + if (!fromSave) { + this.reject(); + } + + super.close(); + } + + static onEditImage() { + const fp = new FilePicker({ + current: this.img, + type: 'image', + callback: async path => { + this.img = path; + this.render(); + }, + top: this.position.top + 40, + left: this.position.left + 10 + }); + return fp.browse(); + } + + async selectActionType() { + const content = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/views/actionType.hbs', + { types: SYSTEM.ACTIONS.actionTypes } + ), + title = 'Select Action Type', + type = 'form', + data = {}; + return Dialog.prompt({ + title, + label: title, + content, + type, + callback: html => { + const form = html[0].querySelector('form'), + fd = new foundry.applications.ux.FormDataExtended(form); + foundry.utils.mergeObject(data, fd.object, { inplace: true }); + return data; + }, + rejectClose: false + }); + } + + static async addItem() { + const actionType = await this.selectActionType(); + const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, + action = new cls({ + _id: foundry.utils.randomID(), + type: actionType.type, + name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), + ...cls.getSourceConfig(this.document) + }); + + this.actions.push(action); + this.render(); + } + + static async editItem(_, button) { + await new DHActionConfig(this.actions[button.dataset.id]).render(true); + } + + static removeItem(event, button) { + this.actions = this.actions.filter((_, index) => index !== Number.parseInt(button.dataset.id)); + this.render(); + } + + static resetMoves() {} +} diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index e1b60726..d59bc35c 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -1,8 +1,9 @@ import { DhHomebrew } from '../../data/settings/_module.mjs'; +import DhSettingsActionView from './components/settingsActionsView.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; -export default class DhAutomationSettings extends HandlebarsApplicationMixin(ApplicationV2) { +export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { constructor() { super({}); @@ -19,7 +20,10 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App classes: ['daggerheart', 'setting', 'dh-style'], position: { width: '600', height: 'auto' }, actions: { - reset: this.reset, + addItem: this.addItem, + editItem: this.editItem, + removeItem: this.removeItem, + resetMoves: this.resetMoves, save: this.save }, form: { handler: this.updateData, submitOnChange: true } @@ -48,8 +52,101 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App this.render(); } - static async reset() { - this.settings = new DhHomebrew(); + static async addItem(_, target) { + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}.moves.${foundry.utils.randomID()}`]: { + name: game.i18n.localize('DAGGERHEART.Settings.Homebrew.NewDowntimeMove'), + img: 'icons/magic/life/cross-worn-green.webp', + description: '', + actions: [] + } + }); + this.render(); + } + + static async editItem(_, target) { + const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id]; + new Promise((resolve, reject) => { + new DhSettingsActionView( + resolve, + reject, + game.i18n.localize('DAGGERHEART.Settings.Homebrew.DowntimeMoves'), + move.name, + move.img, + move.description, + move.actions + ).render(true); + }).then(data => this.updateAction.bind(this)(data, target.dataset.type, target.dataset.id)); + } + + async updateAction(data, type, id) { + await this.settings.updateSource({ + [`restMoves.${type}.moves.${id}`]: { + name: data.name, + img: data.img, + description: data.description + } + }); + this.render(); + } + + static async removeItem(_, target) { + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}.moves.-=${target.dataset.id}`]: null + }); + this.render(); + } + + static async resetMoves(_, target) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.Settings.Homebrew.ResetMovesTitle', { + type: game.i18n.localize( + `DAGGERHEART.Downtime.${target.dataset.type === 'shortRest' ? 'ShortRest' : 'LongRest'}.title` + ) + }) + }, + content: game.i18n.localize('DAGGERHEART.Settings.Homebrew.ResetMovesText') + }); + + if (!confirmed) return; + + const fields = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).schema.fields; + + const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => { + acc[`-=${key}`] = null; + + return acc; + }, {}); + + const updateBase = + target.dataset.type === 'shortRest' + ? fields.restMoves.fields.shortRest.fields + : fields.restMoves.fields.longRest.fields; + const update = { + nrChoices: updateBase.nrChoices.initial, + moves: Object.keys(updateBase.moves.initial).reduce((acc, key) => { + const move = updateBase.moves.initial[key]; + acc[key] = { + ...move, + name: game.i18n.localize(move.name), + description: game.i18n.localize(move.description) + }; + + return acc; + }, {}) + }; + + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}`]: { + ...update, + moves: { + ...removeUpdate, + ...update.moves + } + } + }); + this.render(); } diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index a4a9ede4..4e0bbc39 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -101,65 +101,99 @@ export const conditions = { } }; -export const downtime = { - shortRest: { +export const defaultRestOptions = { + shortRest: () => ({ tendToWounds: { id: 'tendToWounds', - name: 'DAGGERHEART.Downtime.TendToWounds.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Name'), img: 'icons/magic/life/cross-worn-green.webp', - description: 'DAGGERHEART.Downtime.TendToWounds.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Description'), + actions: [ + { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.TendToWounds.Name'), + img: 'icons/magic/life/cross-worn-green.webp', + actionType: 'action', + healing: { + type: 'health', + value: { + custom: { + enabled: true, + formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param? + } + } + } + } + ] }, clearStress: { id: 'clearStress', - name: 'DAGGERHEART.Downtime.ClearStress.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Name'), img: 'icons/magic/perception/eye-ringed-green.webp', - description: 'DAGGERHEART.Downtime.ClearStress.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Description'), + actions: [ + { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.ClearStress.Name'), + img: 'icons/magic/perception/eye-ringed-green.webp', + actionType: 'action', + healing: { + type: 'stress', + value: { + custom: { + enabled: true, + formula: '1d4 + 1' // should be 1d4 + {tier}. How to use the roll param? + } + } + } + } + ] }, repairArmor: { id: 'repairArmor', - name: 'DAGGERHEART.Downtime.RepairArmor.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', - description: 'DAGGERHEART.Downtime.RepairArmor.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Description') }, prepare: { id: 'prepare', - name: 'DAGGERHEART.Downtime.Prepare.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Name'), img: 'icons/skills/trades/academics-merchant-scribe.webp', - description: 'DAGGERHEART.Downtime.Prepare.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Description') } - }, - longRest: { + }), + longRest: () => ({ tendToWounds: { id: 'tendToWounds', - name: 'DAGGERHEART.Downtime.TendToWounds.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Name'), img: 'icons/magic/life/cross-worn-green.webp', - description: 'DAGGERHEART.Downtime.TendToWounds.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Description') }, clearStress: { id: 'clearStress', - name: 'DAGGERHEART.Downtime.ClearStress.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Name'), img: 'icons/magic/perception/eye-ringed-green.webp', - description: 'DAGGERHEART.Downtime.ClearStress.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Description') }, repairArmor: { id: 'repairArmor', - name: 'DAGGERHEART.Downtime.RepairArmor.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', - description: 'DAGGERHEART.Downtime.RepairArmor.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Description') }, prepare: { id: 'prepare', - name: 'DAGGERHEART.Downtime.Prepare.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Name'), img: 'icons/skills/trades/academics-merchant-scribe.webp', - description: 'DAGGERHEART.Downtime.Prepare.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Description') }, workOnAProject: { id: 'workOnAProject', - name: 'DAGGERHEART.Downtime.WorkOnAProject.Name', + name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Name'), img: 'icons/skills/social/thumbsup-approval-like.webp', - description: 'DAGGERHEART.Downtime.WorkOnAProject.Description' + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Description') } - }, + }), custom: { id: 'customActivity', name: '', diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 8219f89e..273b7a72 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -87,6 +87,14 @@ export default class DhCharacter extends BaseDataActor { }; } + get tier() { + return this.levelData.level.current === 1 + ? 1 + : Object.values(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers).tiers).find( + tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end + ).tier; + } + get ancestry() { return this.parent.items.find(x => x.type === 'ancestry') ?? null; } @@ -134,19 +142,6 @@ export default class DhCharacter extends BaseDataActor { : null; } - get refreshableFeatures() { - return this.parent.items.reduce( - (acc, x) => { - if (x.type === 'feature' && x.system.refreshData?.type === 'feature' && x.system.refreshData?.type) { - acc[x.system.refreshData.type].push(x); - } - - return acc; - }, - { shortRest: [], longRest: [] } - ); - } - static async unequipBeforeEquip(itemToEquip) { const primary = this.primaryWeapon, secondary = this.secondaryWeapon; @@ -242,6 +237,14 @@ export default class DhCharacter extends BaseDataActor { this.evasion.total = (this.class?.evasion ?? 0) + this.evasion.bonus; this.proficiency.total = this.proficiency.value + this.proficiency.bonus; } + + getRollData() { + const data = super.getRollData(); + return { + ...data, + tier: this.tier + }; + } } class DhPCLevelData extends foundry.abstract.DataModel { diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 83498a92..3aef56d6 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -31,8 +31,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel { actions: new fields.ArrayField(new fields.ObjectField()) }), { initial: defaultRestOptions.longRest() } - ), - consequences: new fields.SchemaField({}) + ) }), shortRest: new fields.SchemaField({ nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }), diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index 1b575aa8..75ad4bf7 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -1,3 +1,5 @@ +import { actionsTypes } from '../data/_module.mjs'; + export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor() { super(); @@ -38,6 +40,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.ability-use-button').forEach(element => element.addEventListener('click', event => this.abilityUseButton.bind(this)(event, data.message)) ); + html.querySelectorAll('.action-use-button').forEach(element => + element.addEventListener('click', event => this.actionUseButton.bind(this)(event, data.message)) + ); }; setupHooks() { @@ -137,4 +142,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const actor = game.actors.get(message.system.origin); await actor.useAction(action); }; + + actionUseButton = async (_, message) => { + const parent = await foundry.utils.fromUuid(message.system.actor); + const testAction = Object.values(message.system.moves)[0].actions[0]; + const cls = actionsTypes[testAction.type]; + const action = new cls( + { ...testAction, _id: foundry.utils.randomID(), name: game.i18n.localize(testAction.name) }, + { parent: parent } + ); + + action.use(); + }; } diff --git a/styles/application.less b/styles/application.less index 5319a35e..8cdbf1ac 100644 --- a/styles/application.less +++ b/styles/application.less @@ -134,6 +134,12 @@ div.daggerheart.views.multiclass { } .downtime-container { + .downtime-header { + margin: 0; + color: light-dark(@dark-blue, @golden); + text-align: center; + } + .activity-container { display: flex; align-items: center; @@ -150,12 +156,32 @@ div.daggerheart.views.multiclass { } .activity-image { - width: 120px; + width: 80px; + position: relative; + display: flex; + justify-content: center; + margin-right: 8px; border: 2px solid black; border-radius: 50%; - margin-right: 8px; cursor: pointer; + .activity-select-label { + position: absolute; + top: -9px; + font-size: 14px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + background-image: url(../assets/parchments/dh-parchment-light.png); + padding: 0 8px; + line-height: 1; + font-weight: bold; + } + + img { + border-radius: 50%; + } + &:hover, &.selected { filter: drop-shadow(0 0 6px gold); diff --git a/styles/daggerheart.css b/styles/daggerheart.css index a7a0cdc3..e87b899a 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -1887,6 +1887,11 @@ div.daggerheart.views.multiclass { .daggerheart.views.levelup .levelup-section .levelup-container .levelup-inner-container .levelup-posttext { padding: 8px 0; } +.daggerheart.views .downtime-container .downtime-header { + margin: 0; + color: light-dark(#18162e, #f3c267); + text-align: center; +} .daggerheart.views .downtime-container .activity-container { display: flex; align-items: center; @@ -1902,12 +1907,30 @@ div.daggerheart.views.multiclass { font-weight: bold; } .daggerheart.views .downtime-container .activity-container .activity-title .activity-image { - width: 120px; + width: 80px; + position: relative; + display: flex; + justify-content: center; + margin-right: 8px; border: 2px solid black; border-radius: 50%; - margin-right: 8px; cursor: pointer; } +.daggerheart.views .downtime-container .activity-container .activity-title .activity-image .activity-select-label { + position: absolute; + top: -9px; + font-size: 14px; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + color: light-dark(#efe6d8, #222); + background-image: url(../assets/parchments/dh-parchment-light.png); + padding: 0 8px; + line-height: 1; + font-weight: bold; +} +.daggerheart.views .downtime-container .activity-container .activity-title .activity-image img { + border-radius: 50%; +} .daggerheart.views .downtime-container .activity-container .activity-title .activity-image:hover, .daggerheart.views .downtime-container .activity-container .activity-title .activity-image.selected { filter: drop-shadow(0 0 6px gold); @@ -3155,6 +3178,83 @@ div.daggerheart.views.multiclass { #resources:has(.fear-bar) { min-width: 200px; } +.daggerheart.dh-style.setting fieldset { + display: flex; + flex-direction: column; + gap: 4px; +} +.daggerheart.dh-style.setting fieldset.two-columns { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 10px; +} +.daggerheart.dh-style.setting fieldset.two-columns.even { + grid-template-columns: 1fr 1fr; +} +.daggerheart.dh-style.setting .setting-group-field { + white-space: nowrap; + display: flex; + align-items: center; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items { + display: flex; + flex-direction: column; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid; + border-radius: 8px; + padding: 0 8px 0 0; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item { + display: flex; + align-items: center; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item img { + width: 60px; + border-radius: 8px 0 0 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item i { + font-size: 18px; +} +.daggerheart.dh-style.setting .settings-item-header { + display: flex; + align-items: center; +} +.daggerheart.dh-style.setting .settings-item-header .profile { + height: 100px; + width: 100px; + object-fit: cover; + box-sizing: border-box; + cursor: pointer; +} +.daggerheart.dh-style.setting .settings-item-header .item-info { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + text-align: center; + width: 80%; +} +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + width: 90%; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; +} +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:hover[type='text'], +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(#18162e, #f3c267); +} .daggerheart.dh-style.setting .settings-col { display: flex; flex-direction: column; diff --git a/styles/settings.less b/styles/settings.less index e37f9891..d3e63a2d 100644 --- a/styles/settings.less +++ b/styles/settings.less @@ -1,4 +1,95 @@ .daggerheart.dh-style.setting { + fieldset { + display: flex; + flex-direction: column; + gap: 4px; + + &.two-columns { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 10px; + + &.even { + grid-template-columns: 1fr 1fr; + } + } + } + + .setting-group-field { + white-space: nowrap; + display: flex; + align-items: center; + gap: 8px; + } + + .settings-items { + display: flex; + flex-direction: column; + gap: 8px; + + .settings-item { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid; + border-radius: 8px; + padding: 0 8px 0 0; + + .settings-sub-item { + display: flex; + align-items: center; + gap: 8px; + + img { + width: 60px; + border-radius: 8px 0 0 8px; + } + + i { + font-size: 18px; + } + } + } + } + + .settings-item-header { + display: flex; + align-items: center; + + .profile { + height: 100px; + width: 100px; + object-fit: cover; + box-sizing: border-box; + cursor: pointer; + } + + .item-info { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + text-align: center; + width: 80%; + + .item-name input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + width: 90%; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; + + &:hover[type='text'], + &:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(@dark-blue, @golden); + } + } + } + } + .settings-col { display: flex; flex-direction: column; diff --git a/templates/chat/downtime.hbs b/templates/chat/downtime.hbs index 2c7c200e..98a7a227 100644 --- a/templates/chat/downtime.hbs +++ b/templates/chat/downtime.hbs @@ -1,16 +1,11 @@

-
{{this.player}} {{localize "DAGGERHEART.Chat.Downtime.Title"}}
-
{{this.title}}
+
{{title}}

- -
{{{this.description}}}
-
-
Refreshed Features
- {{#each this.refreshedFeatures}} -
-
{{this.name}}
-
- {{/each}} -
+ {{#each moves}} + {{this.name}} + +
{{{this.description}}}
+ {{#if (gt this.actions.length 0)}}{{/if}} + {{/each}}
\ No newline at end of file diff --git a/templates/settings/components/action-view-footer.hbs b/templates/settings/components/action-view-footer.hbs new file mode 100644 index 00000000..309f383f --- /dev/null +++ b/templates/settings/components/action-view-footer.hbs @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/settings/components/action-view-header.hbs b/templates/settings/components/action-view-header.hbs new file mode 100644 index 00000000..a5269faf --- /dev/null +++ b/templates/settings/components/action-view-header.hbs @@ -0,0 +1,6 @@ +
+ +
+

+
+
\ No newline at end of file diff --git a/templates/settings/components/action-view.hbs b/templates/settings/components/action-view.hbs new file mode 100644 index 00000000..6c01b072 --- /dev/null +++ b/templates/settings/components/action-view.hbs @@ -0,0 +1,18 @@ +
+
+ {{localize "Description"}} + + + {{{ enrichedDescription }}} + +
+ {{!--
+ {{localize "Actions"}} + +
+ {{#each this.actions as |action index|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" action type="actions" id=index }} + {{/each}} +
+
--}} +
\ No newline at end of file diff --git a/templates/settings/components/settings-item-line.hbs b/templates/settings/components/settings-item-line.hbs new file mode 100644 index 00000000..039301ec --- /dev/null +++ b/templates/settings/components/settings-item-line.hbs @@ -0,0 +1,22 @@ +
+
+ +
{{this.name}}
+
+
+ + + + + + +
+
\ No newline at end of file diff --git a/templates/settings/homebrew-settings.hbs b/templates/settings/homebrew-settings.hbs index aa866378..2e6ddb10 100644 --- a/templates/settings/homebrew-settings.hbs +++ b/templates/settings/homebrew-settings.hbs @@ -10,6 +10,52 @@ {{/each}} + +
+ {{localize "DAGGERHEART.Settings.Homebrew.DowntimeMoves"}} + +
+ + {{localize "DAGGERHEART.Downtime.LongRest.title"}} + + + + +
+ +
+ +
+
+ +
+ {{#each settingFields._source.restMoves.longRest.moves as |move id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="longRest" id=id }} + {{/each}} +
+
+ +
+ + {{localize "DAGGERHEART.Downtime.ShortRest.title"}} + + + + +
+ +
+ +
+
+ +
+ {{#each settingFields._source.restMoves.shortRest.moves as |move id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="shortRest" id=id }} + {{/each}} +
+
+
\ No newline at end of file