mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Feature/allow action healing multiple resources (#437)
* Healing updates * Remove comments
This commit is contained in:
parent
f55698af02
commit
fad64c9a35
21 changed files with 130 additions and 98 deletions
10
lang/en.json
10
lang/en.json
|
|
@ -713,7 +713,7 @@
|
|||
"abbreviation": "HO"
|
||||
},
|
||||
"armorStack": {
|
||||
"name": "Armor Stack",
|
||||
"name": "Armor Slot",
|
||||
"abbreviation": "AS"
|
||||
},
|
||||
"fear": {
|
||||
|
|
@ -1029,6 +1029,9 @@
|
|||
},
|
||||
"damageRoll": {
|
||||
"name": "Damage Roll"
|
||||
},
|
||||
"healingRoll": {
|
||||
"name": "Healing Roll"
|
||||
}
|
||||
},
|
||||
"Duration": {
|
||||
|
|
@ -1671,8 +1674,9 @@
|
|||
"subclassFeatureTitle": "Subclass Feature"
|
||||
},
|
||||
"healingRoll": {
|
||||
"title": "Heal - {healing}",
|
||||
"heal": "Heal"
|
||||
"title": "Heal - {damage}",
|
||||
"heal": "Heal",
|
||||
"applyHealing": "Apply Healing"
|
||||
},
|
||||
"reroll": {
|
||||
"confirmTitle": "Reroll Dice",
|
||||
|
|
|
|||
|
|
@ -38,17 +38,15 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||
return game.i18n.localize(`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.isHealing ? 'healing' : 'damage'}Roll.name`);
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.config = CONFIG.DH;
|
||||
context.title = this.config.title
|
||||
? this.config.title
|
||||
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||
// context.extraFormula = this.config.extraFormula;
|
||||
context.title = this.config.title ?? this.title;
|
||||
context.formula = this.roll.constructFormula(this.config);
|
||||
context.isHealing = this.config.isHealing;
|
||||
context.directDamage = this.config.directDamage;
|
||||
context.selectedRollMode = this.config.selectedRollMode;
|
||||
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
// TO DELETE ?
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
|
|
|||
|
|
@ -118,7 +118,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
{ key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
|
||||
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
|
||||
];
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
html.querySelectorAll('.duality-action-damage').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollDamage(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.duality-action-healing').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollHealing(event, data.message))
|
||||
);
|
||||
html.querySelectorAll('.target-save-container').forEach(element =>
|
||||
element.addEventListener('click', event => this.onRollSave(event, data.message))
|
||||
);
|
||||
|
|
@ -92,17 +89,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
}
|
||||
}
|
||||
|
||||
async onRollHealing(event, message) {
|
||||
event.stopPropagation();
|
||||
const actor = await this.getActor(message.system.source.actor);
|
||||
if (!actor || !game.user.isGM) return true;
|
||||
if (message.system.source.item && message.system.source.action) {
|
||||
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
|
||||
if (!action || !action?.rollHealing) return;
|
||||
await action.rollHealing(event, message);
|
||||
}
|
||||
}
|
||||
|
||||
async onRollSave(event, message) {
|
||||
event.stopPropagation();
|
||||
const actor = await this.getActor(message.system.source.actor),
|
||||
|
|
@ -160,7 +146,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
return {
|
||||
isHit,
|
||||
targets: isHit
|
||||
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
|
||||
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId))
|
||||
: Array.from(game.user.targets)
|
||||
};
|
||||
}
|
||||
|
|
@ -222,22 +208,13 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
});
|
||||
}
|
||||
|
||||
if(message.system.hasHealing)
|
||||
target.actor.takeHealing(damages);
|
||||
else
|
||||
target.actor.takeDamage(damages);
|
||||
}
|
||||
}
|
||||
|
||||
async onHealing(event, message) {
|
||||
event.stopPropagation();
|
||||
const targets = Array.from(game.user.targets);
|
||||
|
||||
if (targets.length === 0)
|
||||
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
|
||||
|
||||
for (var target of targets) {
|
||||
target.actor.takeHealing(message.system.roll);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle visibility of target containers.
|
||||
* @param {MouseEvent} event
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ export const abilityCosts = {
|
|||
},
|
||||
armor: {
|
||||
id: 'armor',
|
||||
label: 'Armor Stack',
|
||||
label: 'Armor Slot',
|
||||
group: 'TYPES.Actor.character'
|
||||
},
|
||||
fear: {
|
||||
|
|
|
|||
|
|
@ -169,8 +169,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
},
|
||||
dialog: {},
|
||||
type: this.type,
|
||||
hasDamage: !!this.damage?.parts?.length,
|
||||
hasHealing: !!this.healing,
|
||||
hasDamage: this.damage?.parts?.length && this.type !== 'healing',
|
||||
hasHealing: this.damage?.parts?.length && this.type === 'healing',
|
||||
hasEffect: !!this.effects?.length,
|
||||
hasSave: this.hasSave,
|
||||
selectedRollMode: game.settings.get('core', 'rollMode'),
|
||||
|
|
|
|||
|
|
@ -47,11 +47,12 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
formulas = this.formatFormulas(formulas, systemData);
|
||||
|
||||
const config = {
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: game.i18n.localize(this.name) }),
|
||||
title: game.i18n.format(`DAGGERHEART.UI.Chat.${ this.type === 'healing' ? 'healing' : 'damage'}Roll.title`, { damage: game.i18n.localize(this.name) }),
|
||||
roll: formulas,
|
||||
targets: systemData.targets?.filter(t => t.hit) ?? data.targets,
|
||||
hasSave: this.hasSave,
|
||||
isCritical: systemData.roll?.isCritical ?? false,
|
||||
isHealing: this.type === 'healing',
|
||||
source: systemData.source,
|
||||
data: this.getRollData(),
|
||||
event
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import DHBaseAction from './baseAction.mjs';
|
||||
import DHDamageAction from './damageAction.mjs';
|
||||
|
||||
export default class DHHealingAction extends DHBaseAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll'];
|
||||
export default class DHHealingAction extends DHDamageAction {
|
||||
static extraSchemas = [...super.extraSchemas, 'roll'];
|
||||
|
||||
static getRollType(parent) {
|
||||
return 'spellcast';
|
||||
}
|
||||
|
||||
/* static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll'];
|
||||
|
||||
static getRollType(parent) {
|
||||
return 'spellcast';
|
||||
|
|
@ -44,5 +51,5 @@ export default class DHHealingAction extends DHBaseAction {
|
|||
|
||||
get modifiers() {
|
||||
return [];
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
|
|||
),
|
||||
targetSelection: new fields.BooleanField({ initial: true }),
|
||||
hasSave: new fields.BooleanField({ initial: false }),
|
||||
isHealing: new fields.BooleanField({ initial: false }),
|
||||
onSave: new fields.StringField(),
|
||||
source: new fields.SchemaField({
|
||||
actor: new fields.StringField(),
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@ import { DHDamageData } from './damageField.mjs';
|
|||
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class HealingField extends fields.EmbeddedDataField {
|
||||
export default class HealingField extends fields.SchemaField {
|
||||
constructor(options, context = {}) {
|
||||
super(DHDamageData, options, context);
|
||||
const healingFields = {
|
||||
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
|
||||
};
|
||||
super(healingFields, options, context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,14 @@ export default class TargetField extends fields.SchemaField {
|
|||
if (!this.target?.type) return [];
|
||||
let targets;
|
||||
if (this.target?.type === CONFIG.DH.ACTIONS.targetTypes.self.id)
|
||||
targets = TargetField.formatTarget.call(this, this.actor.token ?? this.actor.prototypeToken);
|
||||
targets = [this.actor.token ?? this.actor.prototypeToken];
|
||||
else {
|
||||
targets = Array.from(game.user.targets);
|
||||
if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) {
|
||||
targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t));
|
||||
if (this.target.amount && targets.length > this.target.amount) targets = [];
|
||||
}
|
||||
}
|
||||
config.targets = targets.map(t => TargetField.formatTarget.call(this, t));
|
||||
const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets);
|
||||
if (config.isFastForward && !hasTargets)
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ export function ActionMixin(Base) {
|
|||
}
|
||||
);
|
||||
const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
|
||||
const newAction = parent.actions.get(action.id);
|
||||
const newAction = created.system.actions.get(action.id);
|
||||
if (!newAction) return null;
|
||||
if (renderSheet) newAction.sheet.render({ force: true });
|
||||
return newAction;
|
||||
|
|
@ -215,10 +215,7 @@ export function ActionMixin(Base) {
|
|||
await this.parent.updateSource({ [path]: updates }, options);
|
||||
result = this.parent;
|
||||
} else {
|
||||
/* Fix me - For some reason updating the "healing" section in particular doesn't work without this */
|
||||
await this.item.update({ [path]: updates }, options);
|
||||
await this.item.updateSource({ [path]: updates }, options);
|
||||
result = this.item;
|
||||
result = await this.item.update({ [path]: updates }, options);
|
||||
}
|
||||
|
||||
return this.inCollection
|
||||
|
|
|
|||
|
|
@ -83,10 +83,11 @@ export default class DamageRoll extends DHRoll {
|
|||
|
||||
applyBaseBonus(part) {
|
||||
const modifiers = [],
|
||||
type = this.options.messageType ?? 'damage',
|
||||
type = this.options.messageType ?? (this.options.isHealing ? 'healing' : 'damage'),
|
||||
options = part ?? this.options;
|
||||
|
||||
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
|
||||
if(!this.options.isHealing) {
|
||||
options.damageTypes?.forEach(t => {
|
||||
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
||||
});
|
||||
|
|
@ -95,6 +96,7 @@ export default class DamageRoll extends DHRoll {
|
|||
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
|
||||
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
|
||||
});
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,7 +471,7 @@ export default class DhpActor extends Actor {
|
|||
|
||||
await this.modifyResource(updates);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, damages) === false) return null;
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, updates) === false) return null;
|
||||
}
|
||||
|
||||
calculateDamage(baseDamage, type) {
|
||||
|
|
@ -498,14 +498,28 @@ export default class DhpActor extends Actor {
|
|||
return reduction === Infinity ? 0 : reduction;
|
||||
}
|
||||
|
||||
async takeHealing(resources) {
|
||||
const updates = Object.entries(resources).map(([key, value]) => ({
|
||||
key: key,
|
||||
value: !(key === 'fear' || this.system?.resources?.[key]?.isReversed === false)
|
||||
? value.total * -1
|
||||
: value.total
|
||||
}));
|
||||
async takeHealing(healings) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, healings) === false) return null;
|
||||
|
||||
const updates = [];
|
||||
Object.entries(healings).forEach(([key, healing]) => {
|
||||
healing.parts.forEach(part => {
|
||||
const update = updates.find(u => u.key === key);
|
||||
if (update)
|
||||
update.value += part.total;
|
||||
else updates.push({ value: part.total, key });
|
||||
});
|
||||
});
|
||||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value =
|
||||
!(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false) ? u.value * -1 : u.value)
|
||||
);
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null;
|
||||
}
|
||||
|
||||
async modifyResource(resources) {
|
||||
|
|
@ -547,6 +561,7 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(updates).forEach(async key => {
|
||||
const u = updates[key];
|
||||
if (key === 'items') {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export const getCommandTarget = (options = {}) => {
|
|||
};
|
||||
|
||||
export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, hopeFaces, fearFaces, advantageFaces) => {
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
if (!game.modules.get('dice-so-nice')?.active) return;
|
||||
const diceSoNicePresets = await getDiceSoNicePresets(hopeFaces, fearFaces, advantageFaces, advantageFaces);
|
||||
rollResult.dice[0].options = diceSoNicePresets.hope;
|
||||
rollResult.dice[1].options = diceSoNicePresets.fear;
|
||||
|
|
@ -64,7 +64,6 @@ export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, ho
|
|||
rollResult.dice[2].options =
|
||||
advantageState === 1 ? diceSoNicePresets.advantage : diceSoNicePresets.disadvantage;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const chunkify = (array, chunkSize, mappingFunc) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
|
||||
<fieldset class="one-column">
|
||||
<legend>
|
||||
{{#if (eq @root.source.type 'healing')}}
|
||||
{{localize "DAGGERHEART.GENERAL.healing"}}
|
||||
{{else}}
|
||||
{{localize "DAGGERHEART.GENERAL.damage"}}
|
||||
{{/if}}
|
||||
{{#unless (eq path 'system.attack.')}}<a><i class="fa-solid fa-plus icon-button" data-action="addDamage"></i></a>{{/unless}}
|
||||
</legend>
|
||||
{{#if @root.hasBaseDamage}}
|
||||
|
|
@ -37,7 +41,7 @@
|
|||
{{/if}}
|
||||
<div class="nest-inputs">
|
||||
{{formField ../fields.applyTo value=dmg.applyTo name=(concat ../path "damage.parts." realIndex ".applyTo") localize=true}}
|
||||
{{#if (eq dmg.applyTo 'hitPoints')}}
|
||||
{{#if (and (eq dmg.applyTo 'hitPoints') (ne @root.source.type 'healing'))}}
|
||||
{{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." realIndex ".type") localize=true}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
{{#with (lookup @root.config.GENERAL.healingTypes applyTo)}}
|
||||
{{localize label}}
|
||||
{{/with}}
|
||||
{{#unless @root.isHealing}}
|
||||
{{#if damageTypes}}
|
||||
{{#each damageTypes as | type | }}
|
||||
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
{{/with}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
{{#if fields.roll}}{{> 'systems/daggerheart/templates/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}}
|
||||
{{#if fields.save}}{{> 'systems/daggerheart/templates/actionTypes/save.hbs' fields=fields.save.fields source=source.save}}{{/if}}
|
||||
{{#if fields.damage}}{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage}}{{/if}}
|
||||
{{#if fields.healing}}{{> 'systems/daggerheart/templates/actionTypes/healing.hbs' fields=fields.healing.fields source=source.healing}}{{/if}}
|
||||
{{!-- {{#if fields.healing}}{{> 'systems/daggerheart/templates/actionTypes/healing.hbs' fields=fields.healing.fields source=source.healing}}{{/if}} --}}
|
||||
{{#if fields.resource}}{{> 'systems/daggerheart/templates/actionTypes/resource.hbs' fields=fields.resource.fields source=source.resource}}{{/if}}
|
||||
{{#if fields.documentUUID}}{{> 'systems/daggerheart/templates/actionTypes/uuid.hbs' fields=fields.documentUUID source=source.documentUUID}}{{/if}}
|
||||
{{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{{!-- TO DO DELETE ? --}}
|
||||
{{> 'systems/daggerheart/templates/ui/chat/parts/damage-chat.hbs' damage=this}}
|
||||
{{> 'systems/daggerheart/templates/ui/chat/parts/target-chat.hbs'}}
|
||||
<div class="dice-roll daggerheart chat roll">
|
||||
|
|
|
|||
|
|
@ -164,7 +164,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<fieldset class="dice-roll daggerheart chat roll expanded{{#unless damage.roll}} hidden{{/unless}}" data-action="expandRoll">
|
||||
<legend class="dice-flavor">{{localize "DAGGERHEART.GENERAL.damage"}}</legend>
|
||||
<legend class="dice-flavor">
|
||||
{{#if hasHealing}}
|
||||
{{localize "DAGGERHEART.GENERAL.healing"}}
|
||||
{{else}}
|
||||
{{localize "DAGGERHEART.GENERAL.damage"}}
|
||||
{{/if}}
|
||||
</legend>
|
||||
<div class="dice-result">
|
||||
<div class="dice-tooltip">
|
||||
<div class="wrapper">
|
||||
|
|
@ -177,16 +183,28 @@
|
|||
<div class="dice-roll daggerheart chat roll">
|
||||
<div class="dice-result">
|
||||
<div class="dice-actions{{#unless (or hasDamage hasHealing)}} duality-alone{{/unless}}">
|
||||
{{#if hasDamage}}
|
||||
{{#if (or hasDamage hasHealing)}}
|
||||
{{#if damage.roll}}
|
||||
<button class="duality-action damage-button" data-target-hit="true" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.UI.Chat.damageRoll.dealDamage"}}</span></button>
|
||||
<button class="duality-action damage-button" data-target-hit="true" data-value="{{roll.total}}"><span>
|
||||
{{#if hasHealing}}
|
||||
{{localize "DAGGERHEART.UI.Chat.healingRoll.applyHealing"}}
|
||||
{{else}}
|
||||
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.UI.Chat.attackRoll.rollDamage"}}</span></button>
|
||||
{{localize "DAGGERHEART.UI.Chat.damageRoll.dealDamage"}}
|
||||
{{/if}}
|
||||
</span></button>
|
||||
{{else}}
|
||||
<button class="duality-action duality-action-damage" data-value="{{roll.total}}"><span>
|
||||
{{#if hasHealing}}
|
||||
{{localize "DAGGERHEART.UI.Chat.attackRoll.rollHealing"}}
|
||||
{{else}}
|
||||
{{localize "DAGGERHEART.UI.Chat.attackRoll.rollDamage"}}
|
||||
{{/if}}
|
||||
</span></button>
|
||||
{{/if}}
|
||||
{{!-- {{else}}
|
||||
{{#if hasHealing}}
|
||||
<button class="duality-action duality-action-healing" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.UI.Chat.attackRoll.rollHealing"}}</span></button>
|
||||
{{/if}}
|
||||
{{/if}} --}}
|
||||
{{/if}}
|
||||
{{#if hasEffect}}
|
||||
<button class="duality-action-effect" data-value="{{roll.total}}"><span>{{localize "DAGGERHEART.UI.Chat.attackRoll.applyEffect"}}</span></button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue