mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-07-21 18:09:54 +02:00
Compare commits
11 commits
578e6e6c76
...
907bb8be50
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
907bb8be50 | ||
|
|
2ef78f2c89 | ||
|
|
23ebe6f6b5 | ||
|
|
6ae07bd3a7 | ||
|
|
b833be4f1f | ||
|
|
c5a64c2def | ||
|
|
37f64ed023 | ||
|
|
42ec4f8c30 | ||
|
|
ed30fd2122 | ||
|
|
1f12a98c63 | ||
|
|
ce47c63ce6 |
20 changed files with 480 additions and 376 deletions
|
|
@ -51,7 +51,11 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
const context = await super._prepareContext(_options);
|
const context = await super._prepareContext(_options);
|
||||||
context.config = CONFIG.DH;
|
context.config = CONFIG.DH;
|
||||||
context.title = this.config.title ?? this.title;
|
context.title = this.config.title ?? this.title;
|
||||||
context.formula = this.roll.constructFormula(this.config);
|
|
||||||
|
const { damageFormula, resourceFormulas } = this.roll.constructFormulas(this.config);
|
||||||
|
context.damageFormula = damageFormula;
|
||||||
|
context.resourceFormulas = resourceFormulas;
|
||||||
|
|
||||||
context.hasHealing = this.config.hasHealing;
|
context.hasHealing = this.config.hasHealing;
|
||||||
context.directDamage = this.config.directDamage;
|
context.directDamage = this.config.directDamage;
|
||||||
context.selectedMessageMode = this.config.selectedMessageMode;
|
context.selectedMessageMode = this.config.selectedMessageMode;
|
||||||
|
|
@ -73,7 +77,8 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
|
|
||||||
static updateRollConfiguration(_event, _, formData) {
|
static updateRollConfiguration(_event, _, formData) {
|
||||||
const data = foundry.utils.expandObject(formData.object);
|
const data = foundry.utils.expandObject(formData.object);
|
||||||
foundry.utils.mergeObject(this.config.roll, data.roll);
|
foundry.utils.mergeObject(this.config.damageFormula, data.damageFormula);
|
||||||
|
foundry.utils.mergeObject(this.config.resourceFormulas, data.resourceFormulas);
|
||||||
foundry.utils.mergeObject(this.config.modifiers, data.modifiers);
|
foundry.utils.mergeObject(this.config.modifiers, data.modifiers);
|
||||||
this.config.selectedMessageMode = data.selectedMessageMode;
|
this.config.selectedMessageMode = data.selectedMessageMode;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { getUnusedDamageTypes } from '../../helpers/utils.mjs';
|
import { DHDamageData } from '../../data/fields/action/damageField.mjs';
|
||||||
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
||||||
|
|
||||||
const { ApplicationV2 } = foundry.applications.api;
|
const { ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -31,8 +31,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
removeElement: this.removeElement,
|
removeElement: this.removeElement,
|
||||||
removeTransformActor: this.removeTransformActor,
|
removeTransformActor: this.removeTransformActor,
|
||||||
editEffect: this.editEffect,
|
editEffect: this.editEffect,
|
||||||
addDamage: this.addDamage,
|
addDamage: this.#onAddDamage,
|
||||||
removeDamage: this.removeDamage,
|
removeDamage: this.#onRemoveDamage,
|
||||||
|
addDamageResource: this.#onAddDamageResource,
|
||||||
|
removeDamageResource: this.#onRemoveDamageResource,
|
||||||
editDoc: this.editDoc,
|
editDoc: this.editDoc,
|
||||||
addTrigger: this.addTrigger,
|
addTrigger: this.addTrigger,
|
||||||
removeTrigger: this.removeTrigger,
|
removeTrigger: this.removeTrigger,
|
||||||
|
|
@ -157,9 +159,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
context.tabs = this._getTabs(this.constructor.TABS);
|
context.tabs = this._getTabs(this.constructor.TABS);
|
||||||
context.config = CONFIG.DH;
|
context.config = CONFIG.DH;
|
||||||
if (this.action.damage) {
|
if (this.action.damage) {
|
||||||
context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length;
|
const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes);
|
||||||
|
context.allDamageTypesUsed = allKeys.every(k => k in this.action._source.damage.resources);
|
||||||
if (this.action.damage.hasOwnProperty('includeBase') && this.action.type === 'attack')
|
if (this.action.damage?.main?.hasOwnProperty('includeBase') && this.action.type === 'attack')
|
||||||
context.hasBaseDamage = !!this.action.parent.attack;
|
context.hasBaseDamage = !!this.action.parent.attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,53 +301,71 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
static addDamage(_event) {
|
/** @this DHActionBaseConfig */
|
||||||
if (!this.action.damage.parts) return;
|
static #onAddDamage() {
|
||||||
|
if (!this.action.damage || this.action.damage?.main) return;
|
||||||
|
|
||||||
const choices = getUnusedDamageTypes(this.action._source.damage.parts);
|
const data = this.action.toObject();
|
||||||
|
data.damage.main = {
|
||||||
|
...DHDamageData.schema.getInitialValue(),
|
||||||
|
applyTo: 'hitPoints',
|
||||||
|
type: 'physical'
|
||||||
|
};
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @this DHActionBaseConfig */
|
||||||
|
static #onRemoveDamage() {
|
||||||
|
if (!this.action.damage?.main) return;
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.damage.main = null;
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @this DHActionBaseConfig */
|
||||||
|
static #onAddDamageResource(_event) {
|
||||||
|
if (!this.action.damage) return;
|
||||||
|
|
||||||
|
const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes);
|
||||||
|
const unused = allKeys.filter(k => !(k in this.action._source.damage.resources));
|
||||||
|
const choices = unused.map(k => ({ value: k, label: _loc(CONFIG.DH.GENERAL.healingTypes[k].label) }));
|
||||||
const content = new foundry.data.fields.StringField({
|
const content = new foundry.data.fields.StringField({
|
||||||
label: game.i18n.localize('Damage Type'),
|
label: _loc('DAGGERHEART.GENERAL.damageType'),
|
||||||
choices,
|
choices,
|
||||||
required: true
|
required: true
|
||||||
}).toFormGroup(
|
}).toFormGroup({}, {
|
||||||
{},
|
name: 'type',
|
||||||
{
|
localize: true,
|
||||||
name: 'type',
|
nameAttr: 'value',
|
||||||
localize: true,
|
labelAttr: 'label'
|
||||||
nameAttr: 'value',
|
}).outerHTML;
|
||||||
labelAttr: 'label'
|
|
||||||
}
|
|
||||||
).outerHTML;
|
|
||||||
|
|
||||||
const callback = (_, button) => {
|
const callback = (_, button) => {
|
||||||
const data = this.action.toObject();
|
const data = this.action.toObject();
|
||||||
const type = choices[button.form.elements.type.value].value;
|
const type = choices[button.form.elements.type.value].value;
|
||||||
const part = this.action.schema.fields.damage.fields.parts.element.getInitialValue();
|
data.damage.resources[type] = {
|
||||||
part.applyTo = type;
|
...this.action.schema.fields.damage.fields.resources.element.getInitialValue(),
|
||||||
if (type === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
applyTo: type
|
||||||
part.type = this.action.schema.fields.damage.fields.parts.element.fields.type.element.initial;
|
};
|
||||||
|
|
||||||
data.damage.parts[type] = part;
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const typeDialog = new foundry.applications.api.DialogV2({
|
const typeDialog = new foundry.applications.api.DialogV2({
|
||||||
buttons: [
|
buttons: [
|
||||||
foundry.utils.mergeObject(
|
{
|
||||||
{
|
action: 'ok',
|
||||||
action: 'ok',
|
label: 'Confirm',
|
||||||
label: 'Confirm',
|
icon: 'fas fa-check',
|
||||||
icon: 'fas fa-check',
|
default: true,
|
||||||
default: true
|
callback
|
||||||
},
|
}
|
||||||
{ callback: callback }
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
content: content,
|
content: content,
|
||||||
rejectClose: false,
|
rejectClose: false,
|
||||||
modal: false,
|
modal: false,
|
||||||
window: {
|
window: {
|
||||||
title: game.i18n.localize('Add Damage')
|
/** @todo localize */
|
||||||
|
title: 'Add Damage'
|
||||||
},
|
},
|
||||||
position: { width: 300 }
|
position: { width: 300 }
|
||||||
});
|
});
|
||||||
|
|
@ -353,12 +373,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
|
||||||
typeDialog.render(true);
|
typeDialog.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static removeDamage(_event, button) {
|
/** @this DHActionBaseConfig */
|
||||||
if (!this.action.damage.parts) return;
|
static #onRemoveDamageResource(_event, button) {
|
||||||
|
if (!this.action.damage?.resources) return;
|
||||||
const data = this.action.toObject();
|
const data = this.action.toObject();
|
||||||
const key = button.dataset.key;
|
const key = button.dataset.key;
|
||||||
delete data.damage.parts[key];
|
data.damage.resources[key] = _del;
|
||||||
data.damage.parts[`${key}`] = _del;
|
|
||||||
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { DHDamageData } from '../../data/fields/action/damageField.mjs';
|
||||||
import DHBaseActorSettings from '../sheets/api/actor-setting.mjs';
|
import DHBaseActorSettings from '../sheets/api/actor-setting.mjs';
|
||||||
|
|
||||||
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
|
||||||
|
|
@ -8,8 +9,10 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
classes: ['adversary-settings'],
|
classes: ['adversary-settings'],
|
||||||
position: { width: 455, height: 'auto' },
|
position: { width: 455, height: 'auto' },
|
||||||
actions: {
|
actions: {
|
||||||
addExperience: DHAdversarySettings.#addExperience,
|
addExperience: DHAdversarySettings.#onAddExperience,
|
||||||
removeExperience: DHAdversarySettings.#removeExperience
|
removeExperience: DHAdversarySettings.#onRemoveExperience,
|
||||||
|
addDamage: this.#onAddDamage,
|
||||||
|
removeDamage: this.#onRemoveDamage
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -71,7 +74,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
* Adds a new experience entry to the actor.
|
* Adds a new experience entry to the actor.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #addExperience() {
|
static async #onAddExperience() {
|
||||||
const newExperience = {
|
const newExperience = {
|
||||||
name: 'Experience',
|
name: 'Experience',
|
||||||
modifier: 0
|
modifier: 0
|
||||||
|
|
@ -83,7 +86,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
* Removes an experience entry from the actor.
|
* Removes an experience entry from the actor.
|
||||||
* @type {ApplicationClickAction}
|
* @type {ApplicationClickAction}
|
||||||
*/
|
*/
|
||||||
static async #removeExperience(_, target) {
|
static async #onRemoveExperience(_, target) {
|
||||||
const experience = this.actor.system.experiences[target.dataset.experience];
|
const experience = this.actor.system.experiences[target.dataset.experience];
|
||||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
window: {
|
window: {
|
||||||
|
|
@ -98,4 +101,28 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
||||||
|
|
||||||
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
|
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @this DHAdversarySettings
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static #onAddDamage() {
|
||||||
|
this.actor.update({
|
||||||
|
'system.attack.damage.main': {
|
||||||
|
...DHDamageData.schema.getInitialValue(),
|
||||||
|
applyTo: 'hitPoints',
|
||||||
|
type: 'physical'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @this DHAdversarySettings
|
||||||
|
* @type {ApplicationClickAction}
|
||||||
|
*/
|
||||||
|
static #onRemoveDamage() {
|
||||||
|
this.actor.update({
|
||||||
|
'system.attack.damage.main': null
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
||||||
|
|
||||||
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
|
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
|
||||||
for (const { value, valueAlt, type } of damage.parts) {
|
for (const { value, valueAlt, type } of [damage.main, ...damage.resources].filter(d => !!d)) {
|
||||||
const usedValue = useAltDamage ? valueAlt : value;
|
const usedValue = useAltDamage ? valueAlt : value;
|
||||||
const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
|
const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
|
||||||
const str = damageString
|
const str = damageString
|
||||||
|
|
@ -82,7 +82,7 @@ export default class DHAttackAction extends DHDamageAction {
|
||||||
x: game.i18n.localize('DAGGERHEART.GENERAL.damage')
|
x: game.i18n.localize('DAGGERHEART.GENERAL.damage')
|
||||||
});
|
});
|
||||||
|
|
||||||
const icons = Array.from(type)
|
const icons = Array.from(type ?? [])
|
||||||
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -429,11 +429,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasDamage() {
|
get hasDamage() {
|
||||||
return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type !== 'healing';
|
return this.type !== 'healing' && Boolean(this.damage.main) || Boolean(this.damage.resources.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasHealing() {
|
get hasHealing() {
|
||||||
return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type === 'healing';
|
return this.type === 'healing' && Boolean(this.damage.main) || Boolean(this.damage.resources.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasSave() {
|
get hasSave() {
|
||||||
|
|
@ -470,10 +470,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.damage && source.damage.resources === undefined) {
|
if (source.damage?.parts && !source.damage.resources && !source.damage.main) {
|
||||||
|
source.damage.main = null;
|
||||||
source.damage.resources = {};
|
source.damage.resources = {};
|
||||||
for (const [partKey, part] of Object.entries(source.damage.parts)) {
|
for (const [partKey, part] of Object.entries(source.damage.parts)) {
|
||||||
if (partKey === 'hitPoints') {
|
if (partKey === 'hitPoints' && source.type !== 'healing') {
|
||||||
source.damage.main = {
|
source.damage.main = {
|
||||||
...part,
|
...part,
|
||||||
includeBase: source.damage.includeBase,
|
includeBase: source.damage.includeBase,
|
||||||
|
|
@ -484,6 +485,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
source.damage.resources[partKey] = part;
|
source.damage.resources[partKey] = part;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete source.damage.parts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,8 @@ export default class DHDamageAction extends DHBaseAction {
|
||||||
* @returns Formula string
|
* @returns Formula string
|
||||||
*/
|
*/
|
||||||
getDamageFormula() {
|
getDamageFormula() {
|
||||||
const strings = [];
|
if (!this.damage.main) return '';
|
||||||
for (const { value } of this.damage.parts) {
|
|
||||||
strings.push(Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.join(' + ');
|
return Roll.replaceFormulaData(this.damage.main.value.getFormula(), this.actor?.getRollData() ?? {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,13 +84,11 @@ export default class DhpAdversary extends DhCreature {
|
||||||
type: 'attack'
|
type: 'attack'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: {
|
main: {
|
||||||
hitPoints: {
|
type: ['physical'],
|
||||||
type: ['physical'],
|
applyTo: 'hitPoints',
|
||||||
applyTo: 'hitPoints',
|
value: {
|
||||||
value: {
|
multiplier: 'flat'
|
||||||
multiplier: 'flat'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,15 +104,13 @@ export default class DhCharacter extends DhCreature {
|
||||||
trait: 'strength'
|
trait: 'strength'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: {
|
main: {
|
||||||
hitPoints: {
|
type: ['physical'],
|
||||||
type: ['physical'],
|
applyTo: 'hitPoints',
|
||||||
applyTo: 'hitPoints',
|
value: {
|
||||||
value: {
|
custom: {
|
||||||
custom: {
|
enabled: true,
|
||||||
enabled: true,
|
formula: '@profd4'
|
||||||
formula: '@profd4'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,19 +188,28 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
static migrateData(source) {
|
static migrateData(source) {
|
||||||
if (source.hasDamage && !source.damage.types) {
|
if (source.hasDamage && !source.damage.resources === undefined) {
|
||||||
source.damage = {
|
const getRoll = key => {
|
||||||
types: Object.keys(source.damage).reduce((acc, key) => {
|
const damageData = source.damage[key];
|
||||||
const damageData = source.damage[key];
|
const oldRoll = damageData.parts[0]?.roll;
|
||||||
const oldRoll = damageData.parts[0]?.roll;
|
return oldRoll ? {
|
||||||
acc[key] = oldRoll ? {
|
...oldRoll,
|
||||||
...oldRoll,
|
options: {
|
||||||
options: {
|
...oldRoll.options,
|
||||||
...oldRoll.options,
|
damageTypes: damageData.parts[0].damageTypes ?? []
|
||||||
damageTypes: damageData.parts[0].damageTypes ?? []
|
}
|
||||||
}
|
} : null;
|
||||||
} : null;
|
};
|
||||||
|
|
||||||
|
source.damage = {
|
||||||
|
main: source.damage.hitPoints ? getRoll('hitPoints') : null,
|
||||||
|
resources: Object.keys(source.damage).reduce((acc, key) => {
|
||||||
|
if (key === 'hitPoints') return acc;
|
||||||
|
|
||||||
|
const roll = getRoll(key);
|
||||||
|
if (!roll) return acc;
|
||||||
|
|
||||||
|
acc[key] = roll;
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,27 +11,29 @@ export class ChatDamageData extends foundry.abstract.DataModel {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
damage: new fields.JSONField({validate: ChatDamageData.#validateRoll}),
|
main: new fields.JSONField({ nullable: true, validate: ChatDamageData.#validateRoll}),
|
||||||
resources: new fields.TypedObjectField(new fields.JSONField({validate: ChatDamageData.#validateRoll}))
|
resources: new fields.TypedObjectField(new fields.JSONField({validate: ChatDamageData.#validateRoll}))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get active() {
|
get active() {
|
||||||
return Boolean(Object.keys(this.types).length);
|
return !!this.main || Boolean(Object.keys(this.resources).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static #validateRoll(rollJSON) {
|
static #validateRoll(rollJSON) {
|
||||||
const roll = JSON.parse(rollJSON);
|
if (rollJSON) {
|
||||||
if (!roll.evaluated) throw new Error('Roll objects added to ChatMessage documents must be evaluated');
|
const roll = JSON.parse(rollJSON);
|
||||||
|
if (!roll.evaluated) throw new Error('Roll objects added to ChatMessage documents must be evaluated');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_prepareRolls() {
|
_prepareRolls() {
|
||||||
for (const key of Object.keys(this.types)) {
|
if (this.main) {
|
||||||
const type = this.types[key];
|
this.main = Roll.fromData(this.main);
|
||||||
try {
|
}
|
||||||
this.types[key] = Roll.fromData(type);
|
|
||||||
this.types[key].options.modifierTotal = CONFIG.Dice.daggerheart.DHRoll.calculateTotalModifiers(type);
|
for (const key of Object.keys(this.resources)) {
|
||||||
} catch {}
|
this.resources[key] = Roll.fromData(this.resources[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,10 @@ export default class DamageField extends fields.SchemaField {
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(options, context = {}) {
|
constructor(options, context = {}) {
|
||||||
const damageFields = {
|
super({
|
||||||
main: new fields.EmbeddedDataField(DHDamageData),
|
main: new fields.EmbeddedDataField(DHDamageData, { nullable: true }),
|
||||||
resources: new IterableTypedObjectField(DHResourceData)
|
resources: new IterableTypedObjectField(DHResourceData)
|
||||||
};
|
}, options, context);
|
||||||
super(damageFields, options, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -32,25 +31,23 @@ export default class DamageField extends fields.SchemaField {
|
||||||
this.hasRoll &&
|
this.hasRoll &&
|
||||||
DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id &&
|
DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id &&
|
||||||
!force
|
!force
|
||||||
)
|
) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let formulas = this.damage.parts.map(p => ({
|
const damageFormula = this.damage.main ?
|
||||||
formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor),
|
DamageField.formatFormulas.call(this, [this.damage.main], config)[0] : null;
|
||||||
damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type,
|
const resourceFormulas = DamageField.formatFormulas.call(this, this.damage.resources, config);
|
||||||
applyTo: p.applyTo
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (!formulas.length) return false;
|
if (!damageFormula && !resourceFormulas.length) return false;
|
||||||
|
|
||||||
formulas = DamageField.formatFormulas.call(this, formulas, config);
|
|
||||||
|
|
||||||
messageId = config.message?._id ?? messageId;
|
messageId = config.message?._id ?? messageId;
|
||||||
const message = game.messages.get(messageId);
|
const message = game.messages.get(messageId);
|
||||||
const damageConfig = {
|
const damageConfig = {
|
||||||
dialog: {},
|
dialog: {},
|
||||||
...config,
|
...config,
|
||||||
roll: formulas,
|
damageFormula,
|
||||||
|
resourceFormulas,
|
||||||
data: this.getRollData(),
|
data: this.getRollData(),
|
||||||
isCritical: Boolean(message?.system.roll?.isCritical)
|
isCritical: Boolean(message?.system.roll?.isCritical)
|
||||||
};
|
};
|
||||||
|
|
@ -83,7 +80,7 @@ export default class DamageField extends fields.SchemaField {
|
||||||
|
|
||||||
const targetDamage = [];
|
const targetDamage = [];
|
||||||
const damagePromises = [];
|
const damagePromises = [];
|
||||||
for (let target of targets) {
|
for (const target of targets) {
|
||||||
const actor = foundry.utils.fromUuidSync(target.actorId);
|
const actor = foundry.utils.fromUuidSync(target.actorId);
|
||||||
if (!actor) continue;
|
if (!actor) continue;
|
||||||
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
|
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
|
||||||
|
|
@ -105,14 +102,12 @@ export default class DamageField extends fields.SchemaField {
|
||||||
actor.takeHealing(config.damage.types).then(updates => targetDamage.push({ token, updates }))
|
actor.takeHealing(config.damage.types).then(updates => targetDamage.push({ token, updates }))
|
||||||
);
|
);
|
||||||
else {
|
else {
|
||||||
const configDamage = foundry.utils.deepClone(config.damage.types);
|
const configDamage = config.damage.clone();
|
||||||
const hpDamageMultiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1;
|
configDamage.main &&= configDamage.main.toJSON();
|
||||||
const hpDamageTakenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier;
|
if (configDamage.main) {
|
||||||
if (configDamage.hitPoints) {
|
const multiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1;
|
||||||
configDamage.hitPoints = configDamage.hitPoints.toJSON();
|
const takenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier;
|
||||||
configDamage.hitPoints.total = Math.ceil(
|
configDamage.main.total = Math.ceil(configDamage.main.total * multiplier * takenMultiplier);
|
||||||
configDamage.hitPoints.total * hpDamageMultiplier * hpDamageTakenMultiplier
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
damagePromises.push(
|
damagePromises.push(
|
||||||
|
|
@ -175,11 +170,17 @@ export default class DamageField extends fields.SchemaField {
|
||||||
/**
|
/**
|
||||||
* Prepare formulas for Damage Roll
|
* Prepare formulas for Damage Roll
|
||||||
* Must be called within Action context or similar.
|
* Must be called within Action context or similar.
|
||||||
* @param {object[]} formulas Array of formatted formulas object
|
* @param {DHResourceData[]} damageData Array of DHResourceData
|
||||||
* @param {object} data Action getRollData
|
* @param {object} data Action getRollData
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static formatFormulas(formulas, data) {
|
static formatFormulas(damageData, data) {
|
||||||
|
const formulas = damageData.map(x => ({
|
||||||
|
formula: DamageField.getFormulaValue.call(this, x, data).getFormula(this.actor),
|
||||||
|
damageTypes: x.applyTo === 'hitPoints' && !x.type.size ? new Set(['physical']) : x.type,
|
||||||
|
applyTo: x.applyTo
|
||||||
|
}));
|
||||||
|
|
||||||
const formattedFormulas = [];
|
const formattedFormulas = [];
|
||||||
formulas.forEach(formula => {
|
formulas.forEach(formula => {
|
||||||
if (isNaN(formula.formula))
|
if (isNaN(formula.formula))
|
||||||
|
|
@ -190,6 +191,7 @@ export default class DamageField extends fields.SchemaField {
|
||||||
if (same) same.formula += ` + ${formula.formula}`;
|
if (same) same.formula += ` + ${formula.formula}`;
|
||||||
else formattedFormulas.push(formula);
|
else formattedFormulas.push(formula);
|
||||||
});
|
});
|
||||||
|
|
||||||
return formattedFormulas;
|
return formattedFormulas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,17 +281,6 @@ export class DHResourceData extends foundry.abstract.DataModel {
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
return {
|
return {
|
||||||
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
||||||
type: new fields.SetField(
|
|
||||||
new fields.StringField({
|
|
||||||
choices: CONFIG.DH.GENERAL.damageTypes,
|
|
||||||
initial: 'physical',
|
|
||||||
nullable: false,
|
|
||||||
required: true
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
label: game.i18n.localize('DAGGERHEART.GENERAL.type')
|
|
||||||
}
|
|
||||||
),
|
|
||||||
applyTo: new fields.StringField({
|
applyTo: new fields.StringField({
|
||||||
choices: CONFIG.DH.GENERAL.healingTypes,
|
choices: CONFIG.DH.GENERAL.healingTypes,
|
||||||
required: true,
|
required: true,
|
||||||
|
|
@ -321,7 +312,18 @@ export class DHDamageData extends DHResourceData {
|
||||||
choices: CONFIG.DH.GENERAL.groupAttackRange,
|
choices: CONFIG.DH.GENERAL.groupAttackRange,
|
||||||
blank: true,
|
blank: true,
|
||||||
label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label'
|
label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label'
|
||||||
})
|
}),
|
||||||
|
type: new fields.SetField(
|
||||||
|
new fields.StringField({
|
||||||
|
choices: CONFIG.DH.GENERAL.damageTypes,
|
||||||
|
initial: 'physical',
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
label: game.i18n.localize('DAGGERHEART.GENERAL.type')
|
||||||
|
}
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,13 +67,11 @@ export default class DHWeapon extends AttachableItem {
|
||||||
type: 'attack'
|
type: 'attack'
|
||||||
},
|
},
|
||||||
damage: {
|
damage: {
|
||||||
parts: {
|
main: {
|
||||||
hitPoints: {
|
type: ['physical'],
|
||||||
type: ['physical'],
|
value: {
|
||||||
value: {
|
multiplier: 'prof',
|
||||||
multiplier: 'prof',
|
dice: 'd8'
|
||||||
dice: 'd8'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,11 +228,11 @@ export default class DHWeapon extends AttachableItem {
|
||||||
game.i18n.localize(`DAGGERHEART.CONFIG.Burden.${burden}`)
|
game.i18n.localize(`DAGGERHEART.CONFIG.Burden.${burden}`)
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const { value, type } of attack.damage.parts) {
|
for (const { value, type } of [attack.damage.main, ...attack.damage.resources]) {
|
||||||
const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice];
|
const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice];
|
||||||
if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString());
|
if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString());
|
||||||
|
|
||||||
if (type.size > 0) {
|
if (type?.size) {
|
||||||
const typeTags = Array.from(type)
|
const typeTags = Array.from(type)
|
||||||
.map(t => game.i18n.localize(`DAGGERHEART.CONFIG.DamageType.${t}.abbreviation`))
|
.map(t => game.i18n.localize(`DAGGERHEART.CONFIG.DamageType.${t}.abbreviation`))
|
||||||
.join(' | ');
|
.join(' | ');
|
||||||
|
|
@ -258,10 +256,10 @@ export default class DHWeapon extends AttachableItem {
|
||||||
if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`));
|
if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`));
|
||||||
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`));
|
||||||
|
|
||||||
for (const { value, type } of damage.parts) {
|
for (const { value, type } of [damage.main, ...damage.resources].filter(d => !!d)) {
|
||||||
const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {});
|
const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {});
|
||||||
|
|
||||||
const icons = Array.from(type)
|
const icons = Array.from(type ?? [])
|
||||||
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,27 @@ export default class DamageRoll extends DHRoll {
|
||||||
|
|
||||||
static DefaultDialog = DamageDialog;
|
static DefaultDialog = DamageDialog;
|
||||||
|
|
||||||
|
static createRollInstance(config) {
|
||||||
|
return new this(undefined, config.data, config);
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
static async buildEvaluate(roll, config = {}) {
|
static async buildEvaluate(roll, config = {}) {
|
||||||
if (config.dialog.configure === false) roll.constructFormula(config);
|
if (config.dialog.configure === false) roll.constructFormulas(config);
|
||||||
|
|
||||||
for (const roll of config.roll) {
|
const evaluateRoll = async roll => {
|
||||||
await roll.roll.evaluate();
|
await roll.roll.evaluate();
|
||||||
roll.roll.options = { damageTypes: roll.damageTypes ? [...roll.damageTypes] : [] };
|
roll.roll.options = { damageTypes: roll.damageTypes ? [...roll.damageTypes] : [] };
|
||||||
|
return roll.roll;
|
||||||
|
}
|
||||||
|
|
||||||
if (!config.damage?.types) config.damage = { types: {} };
|
config.damage.main = await evaluateRoll(config.damageFormula);
|
||||||
config.damage.types[roll.applyTo] = roll.roll;
|
config.damage.main.options = { damageTypes:
|
||||||
|
config.damageFormula.damageTypes ? [...config.damageFormula.damageTypes] : []
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const roll of config.resourceFormulas) {
|
||||||
|
config.damage.resources[roll.applyTo] = await evaluateRoll(roll);
|
||||||
}
|
}
|
||||||
|
|
||||||
roll._evaluated = true;
|
roll._evaluated = true;
|
||||||
|
|
@ -51,7 +62,8 @@ export default class DamageRoll extends DHRoll {
|
||||||
if (config.source?.message) {
|
if (config.source?.message) {
|
||||||
chatMessage.update({ 'system.damage': {
|
chatMessage.update({ 'system.damage': {
|
||||||
...config.damage.toObject(),
|
...config.damage.toObject(),
|
||||||
types: config.damage.types
|
main: config.damage.main,
|
||||||
|
resources: config.damage.resources
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,62 +137,72 @@ export default class DamageRoll extends DHRoll {
|
||||||
return changeKeys;
|
return changeKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructFormula(config) {
|
constructFormulas(config) {
|
||||||
|
return {
|
||||||
|
damageFormula: this.constructFormula(this.options.damageFormula, config, true),
|
||||||
|
resourceFormulas: this.options.resourceFormulas.map(x => this.constructFormula(x, config))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructFormula(formulaData, config, isDamage) {
|
||||||
this.options.isCritical = config.isCritical;
|
this.options.isCritical = config.isCritical;
|
||||||
for (const [index, part] of this.options.roll.entries()) {
|
|
||||||
const isHitpointPart = part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id;
|
|
||||||
part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data));
|
|
||||||
part.roll.terms = Roll.parse(part.roll.formula, config.data);
|
|
||||||
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
|
||||||
part.modifiers = this.applyBaseBonus(part);
|
|
||||||
this.addModifiers(part);
|
|
||||||
part.modifiers?.forEach(m => {
|
|
||||||
part.roll.terms.push(...this.formatModifier(m.value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To Remove When Reaction System */
|
const isHitpointPart = formulaData.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id;
|
||||||
if (index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
formulaData.roll = new Roll(Roll.replaceFormulaData(formulaData.formula, config.data));
|
||||||
for (const mod in config.modifiers) {
|
formulaData.roll.terms = Roll.parse(formulaData.roll.formula, config.data);
|
||||||
const modifier = config.modifiers[mod];
|
if (formulaData.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||||
if (modifier.beforeCrit === true && (modifier.enabled || modifier.value)) modifier.callback(part);
|
formulaData.modifiers = this.applyBaseBonus(formulaData);
|
||||||
}
|
this.addModifiers(formulaData);
|
||||||
}
|
formulaData.modifiers?.forEach(m => {
|
||||||
|
formulaData.roll.terms.push(...this.formatModifier(m.value));
|
||||||
if (part.extraFormula) {
|
});
|
||||||
part.roll.terms.push(
|
|
||||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
|
||||||
...this.constructor.parse(part.extraFormula, this.options.data)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.damageOptions.groupAttack?.numAttackers > 1 && isHitpointPart) {
|
|
||||||
const damageTypes = [foundry.dice.terms.Die, foundry.dice.terms.NumericTerm];
|
|
||||||
for (const term of part.roll.terms) {
|
|
||||||
if (damageTypes.some(type => term instanceof type)) {
|
|
||||||
term.number *= config.damageOptions.groupAttack.numAttackers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.isCritical && isHitpointPart) {
|
|
||||||
const total = part.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0);
|
|
||||||
if (total > 0) {
|
|
||||||
part.roll.terms.push(...this.formatModifier(total));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To Remove When Reaction System */
|
|
||||||
if (index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
|
||||||
for (const mod in config.modifiers) {
|
|
||||||
const modifier = config.modifiers[mod];
|
|
||||||
if (!modifier.beforeCrit && (modifier.enabled || modifier.value)) modifier.callback(part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
part.roll._formula = this.constructor.getFormula(part.roll.terms);
|
|
||||||
}
|
}
|
||||||
return this.options.roll;
|
|
||||||
|
/* To Remove When Reaction System */
|
||||||
|
if (isDamage && formulaData.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||||
|
for (const mod in config.modifiers) {
|
||||||
|
const modifier = config.modifiers[mod];
|
||||||
|
if (
|
||||||
|
modifier.beforeCrit === true &&
|
||||||
|
(modifier.enabled || modifier.value)
|
||||||
|
) modifier.callback(formulaData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formulaData.extraFormula) {
|
||||||
|
formulaData.roll.terms.push(
|
||||||
|
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||||
|
...this.constructor.parse(formulaData.extraFormula, this.options.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.damageOptions.groupAttack?.numAttackers > 1 && isHitpointPart) {
|
||||||
|
const damageTypes = [foundry.dice.terms.Die, foundry.dice.terms.NumericTerm];
|
||||||
|
for (const term of formulaData.roll.terms) {
|
||||||
|
if (damageTypes.some(type => term instanceof type)) {
|
||||||
|
term.number *= config.damageOptions.groupAttack.numAttackers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.isCritical && isHitpointPart) {
|
||||||
|
const total = formulaData.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0);
|
||||||
|
if (total > 0) {
|
||||||
|
formulaData.roll.terms.push(...this.formatModifier(total));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To Remove When Reaction System */
|
||||||
|
if (isDamage && formulaData.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||||
|
for (const mod in config.modifiers) {
|
||||||
|
const modifier = config.modifiers[mod];
|
||||||
|
if (!modifier.beforeCrit && (modifier.enabled || modifier.value)) modifier.callback(formulaData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formulaData.roll._formula = this.constructor.getFormula(formulaData.roll.terms);
|
||||||
|
|
||||||
|
return formulaData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To Remove When Reaction System */
|
/* To Remove When Reaction System */
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,10 @@ export default class DHRoll extends BaseRoll {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static createRollInstance(config) {
|
||||||
|
return new this(config.roll.formula, config.data, config);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Partial<RollConfig>} config
|
* @param {Partial<RollConfig>} config
|
||||||
* @returns {Promise<RollConfig>}
|
* @returns {Promise<RollConfig>}
|
||||||
|
|
@ -58,7 +62,7 @@ export default class DHRoll extends BaseRoll {
|
||||||
|
|
||||||
this.temporaryModifierBuilder(config);
|
this.temporaryModifierBuilder(config);
|
||||||
|
|
||||||
let roll = new this(config.roll.formula, config.data, config);
|
let roll = this.createRollInstance(config);
|
||||||
if (config.dialog.configure !== false) {
|
if (config.dialog.configure !== false) {
|
||||||
// Open Roll Dialog
|
// Open Roll Dialog
|
||||||
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
const DialogClass = config.dialog?.class ?? this.DefaultDialog;
|
||||||
|
|
|
||||||
|
|
@ -656,62 +656,66 @@ export default class DhpActor extends Actor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updates = [];
|
if (damages.main) {
|
||||||
|
damages.main.total = this.calculateDamage(damages.main.total, damages.main.damageTypes);
|
||||||
Object.entries(damages).forEach(([key, damage]) => {
|
}
|
||||||
if (key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
|
||||||
damage.total = this.calculateDamage(damage.total, damage.damageTypes);
|
|
||||||
const update = updates.find(u => u.key === key);
|
|
||||||
if (update) {
|
|
||||||
update.value += damage.total;
|
|
||||||
update.damageTypes.add(...new Set(damage.damageTypes));
|
|
||||||
} else updates.push({ value: damage.total, key, damageTypes: new Set(damage.damageTypes) });
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null;
|
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null;
|
||||||
|
|
||||||
if (!updates.length) return;
|
// Convert deducted resources and damage to a record of updates, merging damage to hp with hp marked
|
||||||
|
const updates = [];
|
||||||
|
for (const [key, damage] of Object.entries(damages.resources)) {
|
||||||
|
updates.push({ key, value: damage.total });
|
||||||
|
}
|
||||||
|
if (damages.main) {
|
||||||
|
const existing = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
||||||
|
const value = this.convertDamageToThreshold(damages.main.total) + (existing?.value ?? 0);
|
||||||
|
const damageTypes = new Set(damages.main.options.damageTypes);
|
||||||
|
if (existing) {
|
||||||
|
existing.value = value;
|
||||||
|
existing.damageTypes = damageTypes;
|
||||||
|
} else {
|
||||||
|
updates.push({ value, damageTypes, key: CONFIG.DH.GENERAL.healingTypes.hitPoints.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!updates.some(u => u.value !== 0)) return; // early return if nothing to do
|
||||||
|
|
||||||
const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
||||||
if (hpDamage?.value) {
|
if (hpDamage && this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.total, hpDamage.damageTypes)) {
|
||||||
hpDamage.value = this.convertDamageToThreshold(hpDamage.value);
|
const armorSlotResult = await this.owner.query(
|
||||||
if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) {
|
'armorSlot',
|
||||||
const armorSlotResult = await this.owner.query(
|
{
|
||||||
'armorSlot',
|
actorId: this.uuid,
|
||||||
{
|
damage: hpDamage.value,
|
||||||
actorId: this.uuid,
|
type: [...hpDamage.damageTypes]
|
||||||
damage: hpDamage.value,
|
},
|
||||||
type: [...hpDamage.damageTypes]
|
{
|
||||||
},
|
timeout: 30000
|
||||||
{
|
}
|
||||||
timeout: 30000
|
);
|
||||||
}
|
if (armorSlotResult) {
|
||||||
);
|
const { modifiedDamage, armorChanges, stressSpent } = armorSlotResult;
|
||||||
if (armorSlotResult) {
|
hpDamage.value = modifiedDamage;
|
||||||
const { modifiedDamage, armorChanges, stressSpent } = armorSlotResult;
|
for (const armorChange of armorChanges) {
|
||||||
updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
|
updates.push({ value: armorChange.amount, key: 'armor', uuid: armorChange.uuid });
|
||||||
for (const armorChange of armorChanges) {
|
}
|
||||||
updates.push({ value: armorChange.amount, key: 'armor', uuid: armorChange.uuid });
|
if (stressSpent) {
|
||||||
}
|
const stressUpdate = updates.find(u => u.key === 'stress');
|
||||||
if (stressSpent) {
|
if (stressUpdate) stressUpdate.value += stressSpent;
|
||||||
const stressUpdate = updates.find(u => u.key === 'stress');
|
else updates.push({ value: stressSpent, key: 'stress' });
|
||||||
if (stressUpdate) stressUpdate.value += stressSpent;
|
|
||||||
else updates.push({ value: stressSpent, key: 'stress' });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.type === 'adversary') {
|
} else if (hpDamage && this.type === 'adversary') {
|
||||||
const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => {
|
const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => {
|
||||||
return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value);
|
return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value);
|
||||||
}, 0);
|
}, 0);
|
||||||
hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0);
|
hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
hpDamage.value &&
|
hpDamage.value &&
|
||||||
this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]
|
this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]
|
||||||
) {
|
) {
|
||||||
hpDamage.value -= 1;
|
hpDamage.value -= 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -700,19 +700,6 @@ export async function RefreshFeatures(
|
||||||
return refreshedActors;
|
return refreshedActors;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUnusedDamageTypes(parts) {
|
|
||||||
const usedKeys = Object.keys(parts);
|
|
||||||
return Object.keys(CONFIG.DH.GENERAL.healingTypes).reduce((acc, key) => {
|
|
||||||
if (!usedKeys.includes(key))
|
|
||||||
acc.push({
|
|
||||||
value: key,
|
|
||||||
label: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label)
|
|
||||||
});
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns resolved armor sources ordered by application order */
|
/** Returns resolved armor sources ordered by application order */
|
||||||
export function getArmorSources(actor) {
|
export function getArmorSources(actor) {
|
||||||
const rawArmorSources = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData);
|
const rawArmorSources = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData);
|
||||||
|
|
|
||||||
|
|
@ -1,92 +1,114 @@
|
||||||
|
{{#unless (eq @root.source.type 'healing')}}
|
||||||
<fieldset class="one-column">
|
<fieldset class="one-column">
|
||||||
<legend class="with-icon">
|
<legend class="with-icon">
|
||||||
{{#if (eq @root.source.type 'healing')}}
|
|
||||||
{{localize "DAGGERHEART.GENERAL.healing"}}
|
|
||||||
{{else}}
|
|
||||||
{{localize "DAGGERHEART.GENERAL.damage"}}
|
{{localize "DAGGERHEART.GENERAL.damage"}}
|
||||||
|
{{#if source.main}}
|
||||||
|
<a data-action="removeDamage"><i class="fa-solid fa-trash icon-button"></i></a>
|
||||||
|
{{else}}
|
||||||
|
<a data-action="addDamage"><i class="fa-solid fa-plus icon-button"></i></a>
|
||||||
|
{{/if}}
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
{{#if source.main}}
|
||||||
|
<div class="nest-inputs">
|
||||||
|
{{#if @root.hasBaseDamage}}
|
||||||
|
{{formField @root.fields.damage.fields.main.fields.includeBase value=@root.source.damage.includeBase name="damage.main.includeBase" classes="checkbox" localize=true }}
|
||||||
|
{{/if}}
|
||||||
|
{{#unless (eq @root.source.type 'healing')}}
|
||||||
|
{{formField baseFields.main.fields.direct value=source.main.direct name=(concat path "damage.main.direct") localize=true classes="checkbox"}}
|
||||||
|
{{/unless}}
|
||||||
|
{{#if (and @root.isNPC (not (eq path 'system.attack.')))}}
|
||||||
|
{{formField baseFields.main.fields.groupAttack value=source.main.groupAttack name=(concat path "damage.main.groupAttack") localize=true classes="select"}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{> damageData damage=source.main fields=fields.main.fields basePath=(concat path "damage.main")}}
|
||||||
|
{{#if horde}}
|
||||||
|
{{> hordeDamage source=source.main fields=fields.main.fields basePath=(concat path "damage.main")}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if (ne @root.source.type 'healing')}}
|
||||||
|
{{formField fields.main.fields.type value=source.main.type name=(concat path "damage.main.type") localize=true}}
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#unless (eq path 'system.attack.')}}<a data-action="addDamage" {{#if @root.allDamageTypesUsed}}disabled{{/if}}><i class="fa-solid fa-plus icon-button"></i></a>{{/unless}}
|
</fieldset>
|
||||||
</legend>
|
{{/unless}}
|
||||||
<div class="nest-inputs">
|
|
||||||
{{#if @root.hasBaseDamage}}
|
|
||||||
{{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase name="damage.includeBase" classes="checkbox" localize=true }}
|
|
||||||
{{/if}}
|
|
||||||
{{#unless (eq @root.source.type 'healing')}}
|
|
||||||
{{formField baseFields.direct value=source.direct name=(concat path "damage.direct") localize=true classes="checkbox"}}
|
|
||||||
{{/unless}}
|
|
||||||
{{#if (and @root.isNPC (not (eq path 'system.attack.')))}}
|
|
||||||
{{formField baseFields.groupAttack value=source.groupAttack name=(concat path "damage.groupAttack") localize=true classes="select"}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- Handlebars uses Symbol.Iterator to produce index|key. This isn't compatible with our parts object, so we instead use applyTo, which is the same value --}}
|
{{#unless (eq path 'system.attack.')}}
|
||||||
{{#each source.parts as |dmg key|}}
|
{{! In the future, consider allowing this even on NPCs}}
|
||||||
<div class="nest-inputs">
|
<fieldset class="one-column">
|
||||||
<fieldset{{#if dmg.base}} disabled{{/if}} class="one-column{{#if ../path}} no-style{{/if}}">
|
<legend class="with-icon">
|
||||||
<legend class="with-icon">
|
{{#if (eq @root.source.type 'healing')}}
|
||||||
{{localize (concat "DAGGERHEART.CONFIG.HealingType." dmg.applyTo ".name")}}
|
{{localize "DAGGERHEART.GENERAL.healing"}}
|
||||||
{{#unless (or dmg.base ../path)}}
|
{{else}}
|
||||||
<a data-action="removeDamage" data-key="{{dmg.applyTo}}"><i class="fas fa-trash"></i></a>
|
{{localize "DAGGERHEART.GENERAL.Resource.plural"}}
|
||||||
{{/unless}}
|
{{/if}}
|
||||||
</legend>
|
{{#unless @root.allDamageTypesUsed}}<a data-action="addDamageResource"><i class="fa-solid fa-plus icon-button"></i></a>{{/unless}}
|
||||||
|
</legend>
|
||||||
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base))}}
|
{{#each source.resources as |dmg key|}}
|
||||||
{{formField ../fields.resultBased value=dmg.resultBased name=(concat "damage.parts." dmg.applyTo ".resultBased") localize=true classes="checkbox"}}
|
<div class="nest-inputs">
|
||||||
{{/if}}
|
<fieldset{{#if dmg.base}} disabled{{/if}} class="one-column">
|
||||||
{{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}}
|
<legend class="with-icon">
|
||||||
<div class="nest-inputs">
|
{{localize (concat "DAGGERHEART.CONFIG.HealingType." dmg.applyTo ".name")}}
|
||||||
<fieldset class="one-column">
|
{{#unless (or dmg.base ../path)}}
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}}</legend>
|
<a data-action="removeDamageResource" data-key="{{key}}"><i class="fas fa-trash"></i></a>
|
||||||
{{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}}
|
{{/unless}}
|
||||||
</fieldset>
|
</legend>
|
||||||
<fieldset class="one-column">
|
{{> damageData damage=dmg fields=../fields.resources.element.fields basePath=(concat ../path "damage.resources." dmg.applyTo)}}
|
||||||
<legend>{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}}</legend>
|
</fieldset>
|
||||||
{{> formula fields=../fields.valueAlt.fields type=../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" key=dmg.applyTo path=../path}}
|
</div>
|
||||||
</fieldset>
|
{{/each}}
|
||||||
</div>
|
</fieldset>
|
||||||
{{else}}
|
{{/unless}}
|
||||||
{{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if (and (eq dmg.applyTo 'hitPoints') (ne @root.source.type 'healing'))}}
|
|
||||||
{{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." dmg.applyTo ".type") localize=true}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if ../horde}}
|
|
||||||
<fieldset class="one-column">
|
|
||||||
<legend>{{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}}</legend>
|
|
||||||
<div class="nest-inputs">
|
|
||||||
<input type="hidden" name="{{../path}}damage.parts.{{dmg.applyTo}}.valueAlt.multiplier" value="flat">
|
|
||||||
{{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }}
|
|
||||||
{{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.dice") classes="inline-child" localize=true}}
|
|
||||||
{{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.bonus") localize=true classes="inline-child"}}
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
{{/if}}
|
|
||||||
<input type="hidden" name="{{concat ../path "damage.parts." dmg.applyTo ".base"}}" value="{{dmg.base}}">
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
{{#*inline "formula"}}
|
{{#*inline "formula"}}
|
||||||
{{#unless dmg.base}}
|
{{#unless isBase}}
|
||||||
{{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." key "." target ".custom.enabled") classes="checkbox" localize=true}}
|
{{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat basePath ".custom.enabled") classes="checkbox" localize=true}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{#if source.custom.enabled}}
|
{{#if source.custom.enabled}}
|
||||||
{{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." key "." target ".custom.formula") localize=true}}
|
{{formField fields.custom.fields.formula value=source.custom.formula name=(concat basePath ".custom.formula") localize=true}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="nest-inputs">
|
<div class="nest-inputs">
|
||||||
{{#unless @root.isNPC}}
|
{{#unless @root.isNPC}}
|
||||||
{{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." key "." target ".multiplier") localize=true}}
|
{{formField fields.multiplier value=source.multiplier name=(concat basePath ".multiplier") localize=true}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." key "." target ".flatMultiplier") localize=true }}{{/if}}
|
{{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat basePath ".flatMultiplier") localize=true }}{{/if}}
|
||||||
{{formField fields.dice value=source.dice name=(concat path "damage.parts." key "." target ".dice") localize=true}}
|
{{formField fields.dice value=source.dice name=(concat basePath ".dice") localize=true}}
|
||||||
{{formField fields.bonus value=source.bonus name=(concat path "damage.parts." key "." target ".bonus") localize=true}}
|
{{formField fields.bonus value=source.bonus name=(concat basePath ".bonus") localize=true}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if @root.isNPC}}
|
{{#if @root.isNPC}}
|
||||||
<input type="hidden" name="{{path}}damage.parts.{{key}}.{{target}}.multiplier" value="flat">
|
<input type="hidden" name="{{basePath}}.{{target}}.multiplier" value="flat">
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/inline}}
|
||||||
|
|
||||||
|
{{#*inline "damageData"}}
|
||||||
|
{{#if (and (not @root.isNPC) @root.hasRoll (not damage.base))}}
|
||||||
|
{{formField fields.resultBased value=damage.resultBased name=(concat basePath ".resultBased") localize=true classes="checkbox"}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if (and (not @root.isNPC) @root.hasRoll (not damage.base) damage.resultBased)}}
|
||||||
|
<div class="nest-inputs">
|
||||||
|
<fieldset class="one-column">
|
||||||
|
<legend>{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}}</legend>
|
||||||
|
{{> formula key=damage.applyTo fields=fields.value.fields type=fields.type isBase=damage.base source=damage.value basePath=(concat basePath ".value")}}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="one-column">
|
||||||
|
<legend>{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}}</legend>
|
||||||
|
{{> formula key=damage.applyTo fields=fields.valueAlt.fields type=fields.type isBase=damage.base source=damage.valueAlt basePath=(concat basePath ".valueAlt")}}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
{{> formula key=damage.applyTo fields=fields.value.fields type=fields.type isBase=damage.base source=damage.value basePath=(concat basePath ".value")}}
|
||||||
|
{{/if}}
|
||||||
|
<input type="hidden" name="{{concat basePath ".base"}}" value="{{damage.base}}">
|
||||||
|
{{/inline}}
|
||||||
|
|
||||||
|
{{#*inline "hordeDamage"}}
|
||||||
|
<fieldset class="one-column">
|
||||||
|
<legend>{{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}}</legend>
|
||||||
|
<div class="nest-inputs">
|
||||||
|
<input type="hidden" name="{{basePath}}.valueAlt.multiplier" value="flat">
|
||||||
|
{{formField fields.valueAlt.fields.flatMultiplier value=source.valueAlt.flatMultiplier name=(concat basePath ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }}
|
||||||
|
{{formField fields.valueAlt.fields.dice value=source.valueAlt.dice name=(concat basePath ".valueAlt.dice") classes="inline-child" localize=true}}
|
||||||
|
{{formField fields.valueAlt.fields.bonus value=source.valueAlt.bonus name=(concat basePath ".valueAlt.bonus") localize=true classes="inline-child"}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
{{/inline}}
|
{{/inline}}
|
||||||
|
|
@ -16,31 +16,10 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#each @root.formula}}
|
{{> formula @root.damageFormula path="damageFormula"}}
|
||||||
<div class="damage-formula">
|
|
||||||
<span class="damage-resource"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{roll.formula}}</span>
|
{{#each @root.resourceFormulas}}
|
||||||
<span class="damage-details">
|
{{> formula path=(concat "resourceFormulas." @key)}}
|
||||||
{{#with (lookup @root.config.GENERAL.healingTypes applyTo)}}
|
|
||||||
{{localize label}}
|
|
||||||
{{/with}}
|
|
||||||
{{#unless @root.hasHealing}}
|
|
||||||
{{#if damageTypes}}
|
|
||||||
{{#each damageTypes as | type | }}
|
|
||||||
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
|
||||||
<i class="fa-solid {{icon}}"></i>
|
|
||||||
{{/with}}
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
{{/unless}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="bonuses form-group flexrow">
|
|
||||||
<input type="text" value="{{extraFormula}}" name="roll.{{ @index }}.extraFormula" placeholder="{{localize "DAGGERHEART.GENERAL.situationalBonus"}}">
|
|
||||||
<button class="critical-chip" data-action="toggleCritical">
|
|
||||||
<span><i class="{{#if @root.isCritical}}fa-solid{{else}}fa-regular{{/if}} fa-circle"></i></span>
|
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.criticalShort"}}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
{{#if damageOptions.groupAttack}}
|
{{#if damageOptions.groupAttack}}
|
||||||
|
|
@ -87,4 +66,31 @@
|
||||||
<span class="label">{{localize "DAGGERHEART.GENERAL.roll"}}</span>
|
<span class="label">{{localize "DAGGERHEART.GENERAL.roll"}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{{#*inline "formula"}}
|
||||||
|
<div class="damage-formula">
|
||||||
|
<span class="damage-resource"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{roll.formula}}</span>
|
||||||
|
<span class="damage-details">
|
||||||
|
{{#with (lookup @root.config.GENERAL.healingTypes applyTo)}}
|
||||||
|
{{localize label}}
|
||||||
|
{{/with}}
|
||||||
|
{{#unless @root.hasHealing}}
|
||||||
|
{{#if damageTypes}}
|
||||||
|
{{#each damageTypes as | type | }}
|
||||||
|
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
||||||
|
<i class="fa-solid {{icon}}"></i>
|
||||||
|
{{/with}}
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{/unless}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="bonuses form-group flexrow">
|
||||||
|
<input type="text" value="{{extraFormula}}" name={{concat path ".extraFormula"}} placeholder="{{localize "DAGGERHEART.GENERAL.situationalBonus"}}">
|
||||||
|
<button class="critical-chip" data-action="toggleCritical">
|
||||||
|
<span><i class="{{#if @root.isCritical}}fa-solid{{else}}fa-regular{{/if}} fa-circle"></i></span>
|
||||||
|
<span class="label">{{localize "DAGGERHEART.GENERAL.criticalShort"}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{/inline}}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
>
|
>
|
||||||
{{#if fields.roll}}{{> 'systems/daggerheart/templates/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}}
|
{{#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.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 baseFields=fields.damage.fields }}{{/if}}
|
{{#if fields.damage}}{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=fields.damage.fields source=source.damage baseFields=fields.damage.fields }}{{/if}}
|
||||||
{{#if fields.macro}}{{> 'systems/daggerheart/templates/actionTypes/macro.hbs' fields=fields.macro source=source.macro}}{{/if}}
|
{{#if fields.macro}}{{> 'systems/daggerheart/templates/actionTypes/macro.hbs' fields=fields.macro source=source.macro}}{{/if}}
|
||||||
{{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}}
|
{{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}}
|
||||||
{{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}}
|
{{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -22,5 +22,5 @@
|
||||||
</div>
|
</div>
|
||||||
{{formGroup systemFields.criticalThreshold value=document._source.system.criticalThreshold label="DAGGERHEART.ACTIONS.Settings.criticalThreshold" name="system.criticalThreshold" localize=true}}
|
{{formGroup systemFields.criticalThreshold value=document._source.system.criticalThreshold label="DAGGERHEART.ACTIONS.Settings.criticalThreshold" name="system.criticalThreshold" localize=true}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." baseFields=systemFields.attack.fields.damage.fields horde=(eq document._source.system.type 'horde')}}
|
{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields source=document.system.attack.damage path="system.attack." baseFields=systemFields.attack.fields.damage.fields horde=(eq document._source.system.type 'horde')}}
|
||||||
</section>
|
</section>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue