diff --git a/lang/en.json b/lang/en.json index c971e084..f1785185 100755 --- a/lang/en.json +++ b/lang/en.json @@ -821,6 +821,10 @@ "name": "Dead", "description": "The character is dead" }, + "defeated": { + "name": "Defeated", + "description": "This adversary is defeated." + }, "hidden": { "name": "Hidden", "description": "While Hidden, attacks cannot be made directly targeting them nd any rolls against them are at disadvantage.\nWhen a Hidden creature moves or attacks, they are no longer Hidden. However, if a creature is Hidden when they begin making an attack, the roll has advantage; the Hidden condition isn’t cleared until after the attack is resolved." @@ -1870,7 +1874,8 @@ "tier3": "Tier 3", "tier4": "tier 4", "domains": "Domains", - "downtime": "Downtime" + "downtime": "Downtime", + "rules": "Rules" }, "Tiers": { "singular": "Tier", @@ -2110,6 +2115,13 @@ "label": "Damage Reduction Rules Default", "hint": "Wether using armor and reductions has rules on by default" }, + "defeated": { + "enabled": { "label": "Enabled" }, + "overlay": { "label": "Overlay Effect" }, + "characterDefault": { "label": "Character Default Defeated Status" }, + "adversaryDefault": { "label": "Adversary Default Defeated Status" }, + "companionDefault": { "label": "Companion Default Defeated Status" } + }, "hopeFear": { "label": "Hope & Fear", "gm": { "label": "GM" }, @@ -2141,6 +2153,9 @@ "label": "Players Can Manually Edit Character Settings", "hint": "Players are allowed to access the manual Character Settings and change their statistics beyond the rules." } + }, + "defeated": { + "title": "Defeated Handling" } }, "Homebrew": { diff --git a/module/applications/settings/automationSettings.mjs b/module/applications/settings/automationSettings.mjs index 489bae02..0157e016 100644 --- a/module/applications/settings/automationSettings.mjs +++ b/module/applications/settings/automationSettings.mjs @@ -31,8 +31,19 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App }; static PARTS = { + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' }, + general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' }, + rules: { template: 'systems/daggerheart/templates/settings/automation-settings/rules.hbs' }, + footer: { template: 'systems/daggerheart/templates/settings/automation-settings/footer.hbs' } + }; + + /** @inheritdoc */ + static TABS = { main: { - template: 'systems/daggerheart/templates/settings/automation-settings.hbs' + tabs: [{ id: 'general' }, { id: 'rules' }], + initial: 'general', + labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index ee0b6671..5abf2868 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -164,6 +164,27 @@ export const healingTypes = { } }; +export const defeatedConditions = { + defeated: { + id: 'defeated', + name: 'DAGGERHEART.CONFIG.Condition.defeated.name', + icon: 'icons/magic/control/fear-fright-mask-orange.webp', + description: 'DAGGERHEART.CONFIG.Condition.defeated.description' + }, + unconscious: { + id: 'unconscious', + name: 'DAGGERHEART.CONFIG.Condition.unconscious.name', + icon: 'icons/magic/control/sleep-bubble-purple.webp', + description: 'DAGGERHEART.CONFIG.Condition.unconscious.description' + }, + dead: { + id: 'dead', + name: 'DAGGERHEART.CONFIG.Condition.dead.name', + icon: 'icons/magic/death/grave-tombstone-glow-teal.webp', + description: 'DAGGERHEART.CONFIG.Condition.dead.description' + } +}; + export const conditions = { vulnerable: { id: 'vulnerable', @@ -183,18 +204,7 @@ export const conditions = { icon: 'icons/magic/control/debuff-chains-shackle-movement-red.webp', description: 'DAGGERHEART.CONFIG.Condition.restrained.description' }, - unconscious: { - id: 'unconscious', - name: 'DAGGERHEART.CONFIG.Condition.unconscious.name', - icon: 'icons/magic/control/sleep-bubble-purple.webp', - description: 'DAGGERHEART.CONFIG.Condition.unconscious.description' - }, - dead: { - id: 'dead', - name: 'DAGGERHEART.CONFIG.Condition.dead.name', - icon: 'icons/magic/death/grave-tombstone-glow-teal.webp', - description: 'DAGGERHEART.CONFIG.Condition.dead.description' - } + ...defeatedConditions }; export const defaultRestOptions = { diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index a32ac9dd..5b225228 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -106,6 +106,28 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { }, []); options.scrollingTextData = textData; } + + if (changes.system?.resources) { + const defeatedSettings = game.settings.get( + CONFIG.DH.id, + CONFIG.DH.SETTINGS.gameSettings.Automation + ).defeated; + const typeForDefeated = ['character', 'adversary', 'companion'].find(x => x === this.parent.type); + if (defeatedSettings.enabled && typeForDefeated) { + const resource = typeForDefeated === 'companion' ? 'stress' : 'hitPoints'; + if (changes.system.resources[resource]) { + const becameMax = changes.system.resources[resource].value === this.resources[resource].max; + const wasMax = + this.resources[resource].value === this.resources[resource].max && + this.resources[resource].value !== changes.system.resources[resource].value; + if (becameMax) { + this.parent.toggleDefeated(true); + } else if (wasMax) { + this.parent.toggleDefeated(false); + } + } + } + } } _onUpdate(changes, options, userId) { diff --git a/module/data/combatant.mjs b/module/data/combatant.mjs index cae5d08f..bb54c798 100644 --- a/module/data/combatant.mjs +++ b/module/data/combatant.mjs @@ -8,4 +8,10 @@ export default class DhCombatant extends foundry.abstract.TypeDataModel { actionTokens: new fields.NumberField({ required: true, integer: true, initial: 3 }) }; } + + get isDefeated() { + const { unconscious, defeated, dead } = CONFIG.DH.GENERAL.conditions; + const defeatedConditions = new Set([unconscious.id, defeated.id, dead.id]); + return this.defeated || this.actor?.statuses.intersection(defeatedConditions)?.size; + } } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index facaec17..e1d63669 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -50,6 +50,36 @@ export default class DhAutomation extends foundry.abstract.DataModel { required: true, initial: false, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.playerCanEditSheet.label' + }), + defeated: new fields.SchemaField({ + enabled: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.enabled.label' + }), + overlay: new fields.BooleanField({ + required: true, + initial: true, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.overlay.label' + }), + characterDefault: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.defeatedConditions, + initial: CONFIG.DH.GENERAL.defeatedConditions.unconscious.id, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.characterDefault.label' + }), + adversaryDefault: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.defeatedConditions, + initial: CONFIG.DH.GENERAL.defeatedConditions.defeated.id, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.adversaryDefault.label' + }), + companionDefault: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.defeatedConditions, + initial: CONFIG.DH.GENERAL.defeatedConditions.defeated.id, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.companionDefault.label' + }) }) }; } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 695c94a4..88d40c32 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -225,7 +225,8 @@ export const registerRollDiceHooks = () => { const actor = await fromUuid(config.source.actor); let updates = []; if (!actor) return; - if (config.roll.isCritical || config.roll.result.duality === 1) updates.push({ key: 'hope', value: 1, total: -1, enabled: true }); + if (config.roll.isCritical || config.roll.result.duality === 1) + updates.push({ key: 'hope', value: 1, total: -1, enabled: true }); if (config.roll.isCritical) updates.push({ key: 'stress', value: -1, total: 1, enabled: true }); if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, total: -1, enabled: true }); @@ -233,16 +234,15 @@ export const registerRollDiceHooks = () => { if (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1) updates.push({ key: 'hope', value: -1, total: 1, enabled: true }); if (config.rerolledRoll.isCritical) updates.push({ key: 'stress', value: 1, total: -1, enabled: true }); - if (config.rerolledRoll.result.duality === -1) updates.push({ key: 'fear', value: -1, total: 1, enabled: true }); + if (config.rerolledRoll.result.duality === -1) + updates.push({ key: 'fear', value: -1, total: 1, enabled: true }); } if (updates.length) { const target = actor.system.partner ?? actor; - if (!['dead', 'unconscious'].some(x => actor.statuses.has(x))) { - if(config.rerolledRoll) - target.modifyResource(updates); - else - config.costs = [...(config.costs ?? []), ...updates]; + if (!['dead', 'defeated', 'unconscious'].some(x => actor.statuses.has(x))) { + if (config.rerolledRoll) target.modifyResource(updates); + else config.costs = [...(config.costs ?? []), ...updates]; } } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 68c12b6f..6b01c058 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -718,4 +718,21 @@ export default class DhpActor extends Actor { value: 1 }); } + + async toggleDefeated(defeatedState) { + const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated; + const { unconscious, defeated, dead } = CONFIG.DH.GENERAL.conditions; + const defeatedConditions = new Set([unconscious.id, defeated.id, dead.id]); + if (!defeatedState) { + for (let defeatedId of defeatedConditions) { + await this.toggleStatusEffect(defeatedId, { overlay: settings.overlay, active: defeatedState }); + } + } else { + const noDefeatedConditions = this.statuses.intersection(defeatedConditions).size === 0; + if (noDefeatedConditions) { + const condition = settings[`${this.type}Default`]; + await this.toggleStatusEffect(condition, { overlay: settings.overlay, active: defeatedState }); + } + } + } } diff --git a/templates/settings/automation-settings/footer.hbs b/templates/settings/automation-settings/footer.hbs new file mode 100644 index 00000000..54939c17 --- /dev/null +++ b/templates/settings/automation-settings/footer.hbs @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/templates/settings/automation-settings.hbs b/templates/settings/automation-settings/general.hbs similarity index 71% rename from templates/settings/automation-settings.hbs rename to templates/settings/automation-settings/general.hbs index 9e9da6bb..04d08a9f 100644 --- a/templates/settings/automation-settings.hbs +++ b/templates/settings/automation-settings/general.hbs @@ -1,7 +1,8 @@ -