diff --git a/daggerheart.mjs b/daggerheart.mjs index 5c333e27..4c2c3675 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -386,11 +386,11 @@ class RegisteredTriggers extends Map { super(); } - async registerTriggers(trigger, actor, uuid, commands) { + async registerTriggers(trigger, actor, triggeringActorType, uuid, commands) { const existingTrigger = this.get(trigger); if (!existingTrigger) this.set(trigger, new Map()); - this.get(trigger).set(uuid, { actor, commands }); + this.get(trigger).set(uuid, { actor, triggeringActorType, commands }); } async runTrigger(trigger, currentActor, ...args) { @@ -400,15 +400,19 @@ class RegisteredTriggers extends Map { const dualityTrigger = this.get(trigger); if (dualityTrigger) { - for (let { actor, commands } of dualityTrigger.values()) { - if (currentActor?.uuid !== actor) continue; + for (let { actor, triggeringActorType, commands } of dualityTrigger.values()) { + const triggerData = CONFIG.DH.TRIGGER.triggers[trigger]; + if (triggerData.usesActor && triggeringActorType !== 'any') { + if (triggeringActorType === 'self' && currentActor?.uuid !== actor) continue; + else if (triggeringActorType === 'other' && currentActor?.uuid === actor) continue; + } for (let command of commands) { try { const result = await command(...args); if (result?.updates?.length) updates.push(...result.updates); } catch (_) { - const triggerName = game.i18n.localize(CONFIG.DH.TRIGGER.triggers[trigger].label); + const triggerName = game.i18n.localize(triggerData.label); ui.notifications.error( game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerError', { trigger: triggerName, diff --git a/lang/en.json b/lang/en.json index 1a8797e2..9ad96675 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1145,7 +1145,8 @@ "any": "Any", "friendly": "Friendly", "hostile": "Hostile", - "self": "Self" + "self": "Self", + "other": "Other" }, "TemplateTypes": { "circle": "Circle", @@ -1220,17 +1221,25 @@ } }, "Triggers": { + "postDamageReduction": { + "label": "After Damage Reduction" + }, + "preDamageReduction": { + "label": "Before Damage Reduction" + }, "dualityRoll": { - "label": "Duality Roll", - "hint": "this: Action, roll: DhRoll, actor: DhActor" + "label": "Duality Roll" }, "fearRoll": { - "label": "Fear Roll", - "hint": "this: Action, roll: DhRoll, actor: DhActor" + "label": "Fear Roll" }, "triggerTexts": { - "strangePatternsContent": "Increase Hope or clear stress?" + "strangePatternsContent": "Increase Hope or clear stress?", + "ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?", + "ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you." }, + "triggerType": "Trigger Type", + "triggeringActorType": "Triggering Actor Type", "triggerError": "{trigger} trigger failed for {actor}. It's probably configured wrong." }, "WeaponFeature": { diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index 67398c6b..16ebcab5 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -127,12 +127,16 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty; context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus; context.hasRoll = this.action.hasRoll; - context.triggerOptions = CONFIG.DH.TRIGGER.triggers; - context.triggers = context.source.triggers.map((trigger, index) => ({ - ...trigger, - hint: CONFIG.DH.TRIGGER.triggers[trigger.trigger].hint, - revealed: this.openTrigger === index - })); + context.triggers = context.source.triggers.map((trigger, index) => { + const { hint, returns, usesActor } = CONFIG.DH.TRIGGER.triggers[trigger.trigger]; + return { + ...trigger, + hint, + returns, + usesActor, + revealed: this.openTrigger === index + }; + }); const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers; context.tierOptions = [ @@ -248,7 +252,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) static addTrigger() { const data = this.action.toObject(); - data.triggers.push({ trigger: CONFIG.DH.TRIGGER.triggers.dualityRoll.id }); + data.triggers.push({ + trigger: CONFIG.DH.TRIGGER.triggers.dualityRoll.id, + triggeringActor: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id + }); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } diff --git a/module/config/triggerConfig.mjs b/module/config/triggerConfig.mjs index 3a1f6b1a..35609bd1 100644 --- a/module/config/triggerConfig.mjs +++ b/module/config/triggerConfig.mjs @@ -1,12 +1,42 @@ +/* hints and returns are intentionally not translated. They are programatical terms and best understood in english */ export const triggers = { dualityRoll: { id: 'dualityRoll', + usesActor: true, + args: ['roll', 'actor'], label: 'DAGGERHEART.CONFIG.Triggers.dualityRoll.label', - hint: 'DAGGERHEART.CONFIG.Triggers.dualityRoll.hint' + hint: 'this: Action, roll: DhRoll, actor: DhActor', + returns: '{ updates: [{ key, value, total }] }' }, fearRoll: { id: 'fearRoll', + usesActor: true, + args: ['roll', 'actor'], label: 'DAGGERHEART.CONFIG.Triggers.fearRoll.label', - hint: 'DAGGERHEART.CONFIG.Triggers.fearRoll.hint' + hint: 'this: Action, roll: DhRoll, actor: DhActor', + returns: '{ updates: [{ key, value, total }] }' + }, + postDamageReduction: { + id: 'postDamageReduction', + usesActor: true, + args: ['damageUpdates', 'actor'], + label: 'DAGGERHEART.CONFIG.Triggers.postDamageReduction.label', + hint: 'damageUpdates: ResourceUpdates, actor: DhActor', + returns: '{ updates: [{ originActor: this.actor, updates: [{ key, value, total }] }] }' + } +}; + +export const triggerActorTargetType = { + any: { + id: 'any', + label: 'DAGGERHEART.CONFIG.TargetTypes.any' + }, + self: { + id: 'self', + label: 'DAGGERHEART.CONFIG.TargetTypes.self' + }, + other: { + id: 'other', + label: 'DAGGERHEART.CONFIG.TargetTypes.other' } }; diff --git a/module/data/fields/triggerField.mjs b/module/data/fields/triggerField.mjs index 9decf263..8378ea19 100644 --- a/module/data/fields/triggerField.mjs +++ b/module/data/fields/triggerField.mjs @@ -6,7 +6,15 @@ export default class TriggerField extends foundry.data.fields.SchemaField { nullable: false, blank: false, initial: CONFIG.DH.TRIGGER.triggers.dualityRoll.id, - choices: CONFIG.DH.TRIGGER.triggers + choices: CONFIG.DH.TRIGGER.triggers, + label: 'DAGGERHEART.CONFIG.Triggers.triggerType' + }), + triggeringActorType: new foundry.data.fields.StringField({ + nullable: false, + blank: false, + initial: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id, + choices: CONFIG.DH.TRIGGER.triggerActorTargetType, + label: 'DAGGERHEART.CONFIG.Triggers.triggeringActorType' }), command: new foundry.data.fields.JavaScriptField({ async: true }) }, diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index a9b6a760..60d29792 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -139,15 +139,19 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { super.prepareBaseData(); for (const action of this.actions ?? []) { + if (!action.actor) continue; + const actionsToRegister = []; for (let i = 0; i < action.triggers.length; i++) { const trigger = action.triggers[i]; - const fn = new foundry.utils.AsyncFunction('roll', 'actor', `{${trigger.command}\n}`); + const { args } = CONFIG.DH.TRIGGER.triggers[trigger.trigger]; + const fn = new foundry.utils.AsyncFunction(...args, `{${trigger.command}\n}`); actionsToRegister.push(fn.bind(action)); if (i === action.triggers.length - 1) game.system.registeredTriggers.registerTriggers( trigger.trigger, action.actor?.uuid, + trigger.triggeringActorType, this.parent.uuid, actionsToRegister ); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index f6666a5e..1a4153ad 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -646,6 +646,19 @@ export default class DhpActor extends Actor { } } + const results = await game.system.registeredTriggers.runTrigger( + CONFIG.DH.TRIGGER.triggers.postDamageReduction.id, + this, + updates, + this + ); + + if (results?.length) { + const resourceMap = new ResourceUpdateMap(results[0].originActor); + for (var result of results) resourceMap.addResources(result.updates); + resourceMap.updateResources(); + } + updates.forEach( u => (u.value = diff --git a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json index 359ad45d..770ac30f 100644 --- a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json +++ b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json @@ -84,6 +84,7 @@ "triggers": [ { "trigger": "dualityRoll", + "triggeringActorType": "self", "command": "/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst diceMatch = dice.includes(resource);\n\nif (!diceMatch) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst choice = await foundry.applications.api.DialogV2.wait({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: game.i18n.localize('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContent'),\n buttons: [\n {\n action: 'hope',\n label: game.i18n.localize('DAGGERHEART.GENERAL.hope'),\n icon: 'fa-solid fa-hands-holding'\n },\n {\n action: 'stress',\n label: game.i18n.localize('DAGGERHEART.GENERAL.stress'),\n icon: 'fa-solid fa-bolt-lightning'\n }\n ]\n});\n \nif (!choice) return;\n\n/* Return resource update according to choice */\nconst isHope = choice === 'hope';\nconst updates = [{ key: choice, value: isHope ? 1 : -1, total: isHope ? -1 : 1, enabled: true }];\nreturn { updates };" } ] diff --git a/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json b/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json index 9e46e6ba..78593c62 100644 --- a/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json +++ b/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json @@ -17,7 +17,16 @@ "description": "
When you cause an adversary to mark 1 or more Hit Points, you can spend 2 Hope to increase your Evasion by the number of Hit Points they marked. This bonus lasts until after the next attack made against you.
", "chatDisplay": true, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "hope", + "value": 2, + "itemId": null, + "step": null, + "consumeOnSuccess": false + } + ], "uses": { "value": null, "max": "", @@ -30,8 +39,15 @@ "amount": null }, "name": "Spend Hope", - "img": "icons/skills/melee/maneuver-daggers-paired-orange.webp", - "range": "" + "img": "icons/skills/melee/maneuver-sword-katana-yellow.webp", + "range": "", + "triggers": [ + { + "trigger": "postDamageReduction", + "triggeringActorType": "other", + "command": "/* Check if sufficient hope */\nif (this.actor.system.resources.hope.value < 2) return;\n\n/* Check if hit point damage was dealt */\nconst hpDamage = damageUpdates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);\nif (hpDamage.value < 0) return;\n\n/* Dialog to give player choice */\nconst confirmed = await foundry.applications.api.DialogV2.confirm({\n window: { title: this.item?.name ?? '' },\n content: game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.ferocityContent', { bonus: hpDamage.value }),\n});\n\nif (!confirmed) return;\n\n/* Create the effect */\nthis.actor.createEmbeddedDocuments('ActiveEffect', [{\n name: this.item.name,\n img: 'icons/skills/melee/maneuver-sword-katana-yellow.webp',\n description: game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.ferocityEffectDescription', { bonus: hpDamage.value }),\n changes: [{ key: 'system.evasion', mode: 2, value: hpDamage.value }]\n}]);\n\n/* Update hope */\nreturn { updates: [{ \n originActor: this.actor, \n updates: [{\n key: CONFIG.DH.GENERAL.healingTypes.hope.id,\n value: -2,\n total: 2\n }] \n}]}" + } + ] } }, "attribution": { diff --git a/styles/less/sheets/actions/actions.less b/styles/less/sheets/actions/actions.less index 9a4908e5..6796006c 100644 --- a/styles/less/sheets/actions/actions.less +++ b/styles/less/sheets/actions/actions.less @@ -2,23 +2,43 @@ .trigger-data { width: 100%; display: flex; - align-items: center; + justify-content: space-between; gap: 8px; - select { + .trigger-data-inner { flex: 1; + display: flex; + flex-direction: column; + + select { + flex: 1; + } + + .select-section { + flex: 1; + display: flex; + gap: 8px; + } + + .programmer-section { + flex: 3; + display: flex; + flex-direction: column; + + .hint-section { + display: flex; + gap: 4px; + + .hint { + flex: 1; + flex-wrap: wrap; + } + } + } } - .hint-section { - flex: 3; - display: flex; - align-items: center; - gap: 4px; - - .hint { - flex: 1; - flex-wrap: wrap; - } + .expand-trigger { + font-size: 18px; } } diff --git a/templates/sheets-settings/action-settings/trigger.hbs b/templates/sheets-settings/action-settings/trigger.hbs index 20561c2c..b048461e 100644 --- a/templates/sheets-settings/action-settings/trigger.hbs +++ b/templates/sheets-settings/action-settings/trigger.hbs @@ -10,12 +10,23 @@