mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +01:00
Added ferocity trigger
This commit is contained in:
parent
ce2e5871bc
commit
4b86bb229a
11 changed files with 165 additions and 42 deletions
|
|
@ -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,
|
||||
|
|
|
|||
21
lang/en.json
21
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": {
|
||||
|
|
|
|||
|
|
@ -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) });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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 };"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -17,7 +17,16 @@
|
|||
"description": "<p class=\"Body-Foundation\">When you cause an adversary to mark 1 or more Hit Points, you can <strong>spend 2 Hope</strong> to increase your Evasion by the number of Hit Points they marked. This bonus lasts until after the next attack made against you.</p>",
|
||||
"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": {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,23 @@
|
|||
<legend><a data-action="removeTrigger" data-index="{{index}}"><i class="fa-solid fa-trash"></i></a></legend>
|
||||
|
||||
<div class="trigger-data">
|
||||
{{formInput @root.fields.triggers.element.fields.trigger value=trigger.trigger name=(concat "triggers." index ".trigger") blank=false localize=true}}
|
||||
<div class="hint-section">
|
||||
<strong>{{localize "Context: "}}</strong>
|
||||
<span class="hint">{{localize trigger.hint}}</span>
|
||||
<div class="trigger-data-inner">
|
||||
<div class="select-section">
|
||||
{{formGroup @root.fields.triggers.element.fields.trigger value=trigger.trigger name=(concat "triggers." index ".trigger") blank=false localize=true}}
|
||||
{{#if trigger.usesActor}}{{formGroup @root.fields.triggers.element.fields.triggeringActorType value=trigger.triggeringActorType name=(concat "triggers." index ".triggeringActorType") blank=false localize=true}}{{/if}}
|
||||
</div>
|
||||
<div class="programmer-section">
|
||||
<div class="hint-section">
|
||||
<strong>{{localize "Context: "}}</strong>
|
||||
<span class="hint">{{localize trigger.hint}}</span>
|
||||
</div>
|
||||
<div class="hint-section">
|
||||
<strong>{{localize "Return: "}}</strong>
|
||||
<span class="hint">{{localize trigger.returns}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a data-action="expandTrigger" data-index="{{index}}"><i class="fa-solid fa-angle-down"></i></a>
|
||||
<a class="expand-trigger" data-action="expandTrigger" data-index="{{index}}"><i class="fa-solid fa-angle-down"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="code-mirror-wrapper {{#if trigger.revealed}}revealed{{/if}}">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue