diff --git a/daggerheart.mjs b/daggerheart.mjs index ae1502b0..ae93253c 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -10,7 +10,7 @@ import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module. import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'; import { BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll } from './module/dice/_module.mjs'; import { enrichedDualityRoll } from './module/enrichers/DualityRollEnricher.mjs'; -import { enrichedFateRoll, getFateType } from './module/enrichers/FateRollEnricher.mjs'; +import { enrichedFateRoll, getFateTypeData } from './module/enrichers/FateRollEnricher.mjs'; import { handlebarsRegistration, runMigrations, @@ -322,29 +322,26 @@ Hooks.on('chatMessage', (_, message) => { if (message.startsWith('/fr')) { const result = message.trim().toLowerCase() === '/fr' ? { result: {} } : rollCommandToJSON(message.replace(/\/fr\s?/, '')); + if (!result) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateParsing')); return false; } const { result: rollCommand, flavor } = result; + const fateTypeData = getFateTypeData(rollCommand?.type); - const fateTypeFromRollCommand = getFateType(rollCommand?.type); - - if (fateTypeFromRollCommand == 'BAD') { - ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing')); - return false; - } - - const fateType = fateTypeFromRollCommand; + if (!fateTypeData) + return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing')); + const { value: fateType, label: fateTypeLabel } = fateTypeData; const target = getCommandTarget({ allowNull: true }); - const title = fateType + ' Fate Roll'; + const title = flavor ?? game.i18n.localize('DAGGERHEART.GENERAL.fateRoll'); enrichedFateRoll({ target, title, - label: 'test', + label: fateTypeLabel, fateType }); return false; diff --git a/lang/en.json b/lang/en.json index 82129bac..69965b9e 100755 --- a/lang/en.json +++ b/lang/en.json @@ -237,10 +237,13 @@ "confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)" }, "viewLevelups": "View Levelups", + "resetCharacter": "Reset Character", "viewParty": "View Party", "InvalidOldCharacterImportTitle": "Old Character Import", "InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?", - "cancelBeastform": "Cancel Beastform" + "cancelBeastform": "Cancel Beastform", + "resetCharacterConfirmationTitle": "Reset Character", + "resetCharacterConfirmationContent": "You are reseting all character data except name and portrait. Are you sure?" }, "Companion": { "FIELDS": { @@ -314,6 +317,8 @@ "selectPrimaryWeapon": "Select Primary Weapon", "selectSecondaryWeapon": "Select Secondary Weapon", "selectSubclass": "Select Subclass", + "setupSkipTitle": "Skipping Character Setup", + "setupSkipContent": "You are skipping the Character Setup by adding this manually. The character setup is the blinking arrows in the top-right. Are you sure you want to continue?", "startingItems": "Starting Items", "story": "Story", "storyExplanation": "Select which background and connection prompts you want to copy into your character's background.", @@ -615,9 +620,11 @@ "rerollDice": "Reroll Dice" }, "RiskItAllDialog": { - "title": "Character: {name} - Risk It All - Clear Stress and/or Hit Points", - "subtitle": "Clear {hope} Stress and/or Hit Points", - "submit": "Submit" + "title": "{name} - Risk It All", + "subtitle": "Clear Stress and Hit Points", + "remainingTitle": "Remaining Points", + "clearResource": "Clear {resource}", + "finalTitle": "Final Character Resources" }, "TagTeamSelect": { "title": "Tag Team Roll", @@ -2461,10 +2468,6 @@ "label": "Show Resource Change Scrolltexts", "hint": "When a character is damaged, uses armor etc, a scrolling text will briefly appear by the token to signify this." }, - "playerCanEditSheet": { - "label": "Players Can Manually Edit Character Settings", - "hint": "Players are allowed to access the manual Character Settings and change their statistics beyond the rules." - }, "roll": { "roll": { "label": "Roll", @@ -2675,9 +2678,9 @@ "riskItAllCritical": "Critical Rolled, clearing all marked Stress and Hit Points.", "riskItAllFailure": "The fear die rolled higher. You have crossed through the veil of death.", "blazeOfGlory": "Blaze of Glory Effect Added!", - "riskItAllClearStressAndHitPoints": "Clear {hope} Stress Or Hit Points.", - "riskItAllSuccessWithEnoughHope": "Hope roll value is more than the marked Stress and Hit Points, clearing both.", - "riskItAllSuccess": "The hope die rolled higher, clear up to {hope} Stress Or Hit Points." + "riskItAllDialogButton": "Clear Stress And Hit Points.", + "riskItAllSuccessWithEnoughHope": "The Hope value is more than the marked Stress and Hit Points. Both are cleared fully.", + "riskItAllSuccess": "The hope die rolled higher, clear up to {hope} Stress And Hit Points." }, "dicePool": { "title": "Dice Pool" diff --git a/module/applications/dialogs/_module.mjs b/module/applications/dialogs/_module.mjs index 92038c41..d43045e6 100644 --- a/module/applications/dialogs/_module.mjs +++ b/module/applications/dialogs/_module.mjs @@ -14,3 +14,4 @@ export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs'; export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs'; export { default as GroupRollDialog } from './group-roll-dialog.mjs'; export { default as TagTeamDialog } from './tagTeamDialog.mjs'; +export { default as RiskItAllDialog } from './riskItAllDialog.mjs'; diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 441842dc..6f320152 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -123,7 +123,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.formula = this.roll.constructFormula(this.config); if (this.actor?.system?.traits) context.abilities = this.getTraitModifiers(); - context.showReaction = !this.config.roll?.type || context.rollType === 'DualityRoll'; + context.showReaction = !this.config.skips?.reaction && context.rollType === 'DualityRoll'; context.reactionOverride = this.reactionOverride; } diff --git a/module/applications/dialogs/deathMove.mjs b/module/applications/dialogs/deathMove.mjs index ea7a18eb..18ebf104 100644 --- a/module/applications/dialogs/deathMove.mjs +++ b/module/applications/dialogs/deathMove.mjs @@ -9,7 +9,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV this.actor = actor; this.selectedMove = null; this.showRiskItAllButton = false; - this.riskItAllButtonLabel = ""; + this.riskItAllButtonLabel = ''; this.riskItAllHope = 0; } @@ -84,7 +84,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV label: game.i18n.localize('DAGGERHEART.GENERAL.dualityDice'), actionType: null, advantage: null, - customConfig: { skips: { resources: true } } + customConfig: { skips: { resources: true, reaction: true } } }); if (!config.roll.result) return; @@ -108,10 +108,12 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV config.resourceUpdates.addResources(clearAllStressAndHitpointsUpdates); chatMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccessWithEnoughHope'); } else { - chatMessage = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccess', { hope: config.roll.hope.value }) + chatMessage = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccess', { + hope: config.roll.hope.value + }); this.showRiskItAllButton = true; this.riskItAllHope = config.roll.hope.value; - this.riskItAllButtonLabel = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllClearStressAndHitPoints', { hope: config.roll.hope.value }) + this.riskItAllButtonLabel = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllDialogButton'); } } diff --git a/module/applications/dialogs/riskItAllDialog.mjs b/module/applications/dialogs/riskItAllDialog.mjs index fef76e66..9bdd5dc2 100644 --- a/module/applications/dialogs/riskItAllDialog.mjs +++ b/module/applications/dialogs/riskItAllDialog.mjs @@ -1,11 +1,14 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class RiskItAllDialog extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(actor, config) { + constructor(actor, resourceValue) { super({}); this.actor = actor; - this.validChoices = null; - this.config = config; + this.resourceValue = resourceValue; + this.choices = { + hitPoints: 0, + stress: 0 + }; } get title() { @@ -14,10 +17,10 @@ export default class RiskItAllDialog extends HandlebarsApplicationMixin(Applicat static DEFAULT_OPTIONS = { classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'risk-it-all'], - position: { width: 'auto', height: 'auto' }, - window: { icon: 'fa-solid fa-skull' }, + position: { width: 280, height: 'auto' }, + window: { icon: 'fa-solid fa-dice fa-xl' }, actions: { - submit: this.submit + finish: RiskItAllDialog.#finish } }; @@ -28,42 +31,62 @@ export default class RiskItAllDialog extends HandlebarsApplicationMixin(Applicat } }; + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + for (const input of htmlElement.querySelectorAll('.resource-container input')) + input.addEventListener('change', this.updateChoice.bind(this)); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.RiskItAllDialog = this.RiskItAllDialog; - context.title = game.i18n.format('DAGGERHEART.APPLICATIONS.RiskItAllDialog.subtitle', { hope: this.config.hope }); - context.currentHitPointsLabel = "Current Marked Hit Points: " + this.actor.system.resources.hitPoints.value; - context.currentStressLabel = "Current Marked Stress: " + this.actor.system.resources.stress.value; + context.resourceValue = this.resourceValue; + context.remainingResource = this.resourceValue - this.choices.hitPoints - this.choices.stress; + context.unfinished = context.remainingResource !== 0; - context.newHitPoints = this.actor.system.resources.hitPoints.value; - context.newStress = this.actor.system.resources.stress.value; + context.choices = this.choices; + context.final = { + hitPoints: { + value: this.actor.system.resources.hitPoints.value - this.choices.hitPoints, + max: this.actor.system.resources.hitPoints.max + }, + stress: { + value: this.actor.system.resources.stress.value - this.choices.stress, + max: this.actor.system.resources.stress.max + } + }; + + context; return context; } - static checkForValidChoice() { - /* - TODO: + updateChoice(event) { + let value = Number.parseInt(event.target.value); + const choiceKey = event.target.dataset.choice; + const actorValue = this.actor.system.resources[choiceKey].value; + const remaining = this.resourceValue - this.choices.hitPoints - this.choices.stress; + const changeAmount = value - this.choices[choiceKey]; - return (this.config.hope == (this.actor.system.resources.hitPoints.value - newHitPointValue) + (this.actor.system.resources.stress.value - newStressValue)); - */ - return true; + /* If trying to increase beyond remaining resource points, just increase to max available */ + if (remaining - changeAmount < 0) value = this.choices[choiceKey] + remaining; + else if (actorValue - value < 0) value = actorValue; + + this.choices[choiceKey] = value; + this.render(); } - static async submit() { - this.close(); - // TODO: Update actor with changes. + static async #finish() { + const resourceUpdate = Object.keys(this.choices).reduce((acc, resourceKey) => { + const value = this.actor.system.resources[resourceKey].value - this.choices[resourceKey]; + acc[resourceKey] = { value }; + return acc; + }, {}); + await this.actor.update({ - system: { - resources: { - hitPoints: { - value: 0 // TODO put editted value here - }, - stress: { - value: 0 // TODO put editted value here - } - } - } + 'system.resources': resourceUpdate }); + + this.close(); } } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 5086757d..5c6bac3a 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -27,6 +27,7 @@ export default class CharacterSheet extends DHBaseActorSheet { makeDeathMove: CharacterSheet.#makeDeathMove, levelManagement: CharacterSheet.#levelManagement, viewLevelups: CharacterSheet.#viewLevelups, + resetCharacter: CharacterSheet.#resetCharacter, toggleEquipItem: CharacterSheet.#toggleEquipItem, toggleResourceDice: CharacterSheet.#toggleResourceDice, handleResourceDice: CharacterSheet.#handleResourceDice, @@ -42,6 +43,11 @@ export default class CharacterSheet extends DHBaseActorSheet { icon: 'fa-solid fa-angles-up', label: 'DAGGERHEART.ACTORS.Character.viewLevelups', action: 'viewLevelups' + }, + { + icon: 'fa-solid fa-arrow-rotate-left', + label: 'DAGGERHEART.ACTORS.Character.resetCharacter', + action: 'resetCharacter' } ] }, @@ -220,13 +226,6 @@ export default class CharacterSheet extends DHBaseActorSheet { async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options); switch (partId) { - case 'header': - const { playerCanEditSheet, levelupAuto } = game.settings.get( - CONFIG.DH.id, - CONFIG.DH.SETTINGS.gameSettings.Automation - ); - context.showSettings = game.user.isGM || !levelupAuto || (levelupAuto && playerCanEditSheet); - break; case 'loadout': await this._prepareLoadoutContext(context, options); break; @@ -666,6 +665,32 @@ export default class CharacterSheet extends DHBaseActorSheet { new LevelupViewMode(this.document).render({ force: true }); } + /** + * Resets the character data and removes all embedded documents. + */ + static async #resetCharacter() { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.ACTORS.Character.resetCharacterConfirmationTitle') + }, + content: game.i18n.localize('DAGGERHEART.ACTORS.Character.resetCharacterConfirmationContent') + }); + + if (!confirmed) return; + + await this.document.update({ + '==system': {} + }); + await this.document.deleteEmbeddedDocuments( + 'Item', + this.document.items.map(x => x.id) + ); + await this.document.deleteEmbeddedDocuments( + 'ActiveEffect', + this.document.effects.map(x => x.id) + ); + } + /** * Opens the Death Move interface for the character. * @type {ApplicationClickAction} @@ -955,6 +980,18 @@ export default class CharacterSheet extends DHBaseActorSheet { } async _onDropItem(event, item) { + const setupCriticalItemTypes = ['class', 'subclass', 'ancestry', 'community']; + if (this.document.system.needsCharacterSetup && setupCriticalItemTypes.includes(item.type)) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.APPLICATIONS.CharacterCreation.setupSkipTitle') + }, + content: game.i18n.localize('DAGGERHEART.APPLICATIONS.CharacterCreation.setupSkipContent') + }); + + if (!confirmed) return; + } + if (this.document.uuid === item.parent?.uuid) { return super._onDropItem(event, item); } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index b4aa246a..f0c7288c 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -1,6 +1,5 @@ import { abilities } from '../../config/actorConfig.mjs'; import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; -import RiskItAllDialog from '../dialogs/riskItAllDialog.mjs'; export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor(options) { @@ -98,15 +97,17 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo /** Ensure the chat theme inherits the interface theme */ _replaceHTML(result, content, options) { - const themedElement = result.log?.querySelector(".chat-log"); - themedElement?.classList.remove("themed", "theme-light", "theme-dark"); + const themedElement = result.log?.querySelector('.chat-log'); + themedElement?.classList.remove('themed', 'theme-light', 'theme-dark'); super._replaceHTML(result, content, options); } /** Remove chat log theme from notifications area */ async _onFirstRender(result, content) { await super._onFirstRender(result, content); - document.querySelector("#chat-notifications .chat-log")?.classList.remove("themed", "theme-light", "theme-dark") + document + .querySelector('#chat-notifications .chat-log') + ?.classList.remove('themed', 'theme-light', 'theme-dark'); } async onRollSimple(event, message) { @@ -388,14 +389,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo event.target.closest('.group-roll-section').querySelector('.group-roll-content').classList.toggle('closed'); } - async riskItAllClearStressAndHitPoints(event, data) { - const hopeValue = event.target.dataset.hope; - const config = { - hope: hopeValue - } - await new RiskItAllDialog(data.actor, config).render({ force: true }); + const resourceValue = event.target.dataset.resourceValue; + new game.system.api.applications.dialogs.RiskItAllDialog(data.actor, resourceValue).render({ force: true }); } - - } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index 3376b153..bff0bae9 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -55,11 +55,6 @@ export default class DhAutomation extends foundry.abstract.DataModel { initial: true, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.resourceScrollTexts.label' }), - playerCanEditSheet: new fields.BooleanField({ - required: true, - initial: false, - label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.playerCanEditSheet.label' - }), defeated: new fields.SchemaField({ enabled: new fields.BooleanField({ required: true, diff --git a/module/enrichers/FateRollEnricher.mjs b/module/enrichers/FateRollEnricher.mjs index c1b73bae..c50a563b 100644 --- a/module/enrichers/FateRollEnricher.mjs +++ b/module/enrichers/FateRollEnricher.mjs @@ -4,56 +4,35 @@ export default function DhFateRollEnricher(match, _options) { const roll = rollCommandToJSON(match[1], match[0]); if (!roll) return match[0]; - const fateTypeFromRoll = getFateType(roll?.type); - - if (fateTypeFromRoll == 'BAD') { - ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing')); - return; - } - return getFateMessage(roll.result, roll?.flavor); } -export function getFateType(fateTypeValue) { - const fateTypeFromValue = fateTypeValue - ? fateTypeValue.toLowerCase() == 'fear' - ? 'Fear' - : fateTypeValue.toLowerCase() == 'hope' - ? 'Hope' - : 'BAD' - : 'Hope'; - - return fateTypeFromValue; +export function getFateTypeData(fateTypeValue) { + const value = fateTypeValue ? fateTypeValue.capitalize() : 'Hope'; + const lowercased = fateTypeValue?.toLowerCase?.() ?? 'hope'; + switch (lowercased) { + case 'hope': + case 'fear': + return { value, label: game.i18n.localize(`DAGGERHEART.GENERAL.${lowercased}`) }; + default: + return null; + } } function getFateMessage(roll, flavor) { - const fateType = getFateType(roll?.type); + const fateTypeData = getFateTypeData(roll?.type); - if (fateType == 'BAD') { - ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing')); - return ''; - } + if (!fateTypeData) + return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.fateTypeParsing')); - const fateTypeLocalized = - fateType === 'Hope' - ? game.i18n.localize('DAGGERHEART.GENERAL.hope') - : game.i18n.localize('DAGGERHEART.GENERAL.fear'); - - const title = - flavor ?? - fateTypeLocalized + - ' ' + - game.i18n.localize('DAGGERHEART.GENERAL.fate') + - ' ' + - game.i18n.localize('DAGGERHEART.GENERAL.roll'); - - const dataLabel = game.i18n.localize('DAGGERHEART.GENERAL.fate'); + const { value: fateType, label: fateTypeLabel } = fateTypeData; + const title = flavor ?? game.i18n.localize('DAGGERHEART.GENERAL.fateRoll'); const fateElement = document.createElement('span'); fateElement.innerHTML = ` + - \ No newline at end of file diff --git a/templates/settings/automation-settings/general.hbs b/templates/settings/automation-settings/general.hbs index d49ef9b8..bd91b2b1 100644 --- a/templates/settings/automation-settings/general.hbs +++ b/templates/settings/automation-settings/general.hbs @@ -18,7 +18,6 @@ {{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}} {{formGroup settingFields.schema.fields.effects.fields.rangeDependent value=settingFields._source.effects.rangeDependent localize=true}} {{formGroup settingFields.schema.fields.levelupAuto value=settingFields._source.levelupAuto localize=true}} - {{formGroup settingFields.schema.fields.playerCanEditSheet value=settingFields._source.playerCanEditSheet localize=true}} {{formGroup settingFields.schema.fields.damageReductionRulesDefault value=settingFields._source.damageReductionRulesDefault localize=true}} {{formGroup settingFields.schema.fields.resourceScrollTexts value=settingFields._source.resourceScrollTexts localize=true}} diff --git a/templates/sheets-settings/action-settings/trigger.hbs b/templates/sheets-settings/action-settings/trigger.hbs index b048461e..9ef97733 100644 --- a/templates/sheets-settings/action-settings/trigger.hbs +++ b/templates/sheets-settings/action-settings/trigger.hbs @@ -30,7 +30,7 @@
- {{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") aria=(object label=(localize "Test")) }} + {{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") }}
{{/each}} diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 38c3a770..d2c01f3c 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -128,9 +128,7 @@ {{/each}} - {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' showSettings=showSettings }} - {{#if ../showSettings}} - - {{/if}} + {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }} + {{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} \ No newline at end of file diff --git a/templates/ui/chat/deathMove.hbs b/templates/ui/chat/deathMove.hbs index b36ee590..02691c7c 100644 --- a/templates/ui/chat/deathMove.hbs +++ b/templates/ui/chat/deathMove.hbs @@ -19,7 +19,7 @@ {{#if this.showRiskItAllButton}}
-