mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 15:39:02 +01:00
Damages parts roll
This commit is contained in:
parent
a336220281
commit
a7d2916e93
7 changed files with 145 additions and 38 deletions
|
|
@ -46,7 +46,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
context.title = this.config.title
|
context.title = this.config.title
|
||||||
? this.config.title
|
? this.config.title
|
||||||
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||||
context.extraFormula = this.config.extraFormula;
|
// context.extraFormula = this.config.extraFormula;
|
||||||
context.formula = this.roll.constructFormula(this.config);
|
context.formula = this.roll.constructFormula(this.config);
|
||||||
context.directDamage = this.config.directDamage;
|
context.directDamage = this.config.directDamage;
|
||||||
context.selectedRollMode = this.config.selectedRollMode;
|
context.selectedRollMode = this.config.selectedRollMode;
|
||||||
|
|
@ -61,7 +61,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
||||||
|
|
||||||
static updateRollConfiguration(_event, _, formData) {
|
static updateRollConfiguration(_event, _, formData) {
|
||||||
const { ...rest } = foundry.utils.expandObject(formData.object);
|
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||||
this.config.extraFormula = rest.extraFormula;
|
foundry.utils.mergeObject(this.config.roll, rest.roll)
|
||||||
this.config.selectedRollMode = rest.selectedRollMode;
|
this.config.selectedRollMode = rest.selectedRollMode;
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { setsEqual } from '../../helpers/utils.mjs';
|
||||||
import DHBaseAction from './baseAction.mjs';
|
import DHBaseAction from './baseAction.mjs';
|
||||||
|
|
||||||
export default class DHDamageAction extends DHBaseAction {
|
export default class DHDamageAction extends DHBaseAction {
|
||||||
|
|
@ -18,27 +19,53 @@ export default class DHDamageAction extends DHBaseAction {
|
||||||
return formulaValue;
|
return formulaValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatFormulas(formulas, systemData) {
|
||||||
|
const formattedFormulas = [];
|
||||||
|
formulas.forEach(formula => {
|
||||||
|
if (isNaN(formula.formula)) formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(systemData));
|
||||||
|
const same = formattedFormulas.find(f => setsEqual(f.damageTypes, formula.damageTypes) && f.applyTo === formula.applyTo);
|
||||||
|
if(same)
|
||||||
|
same.formula += ` + ${formula.formula}`;
|
||||||
|
else
|
||||||
|
formattedFormulas.push(formula);
|
||||||
|
})
|
||||||
|
return formattedFormulas;
|
||||||
|
}
|
||||||
|
|
||||||
async rollDamage(event, data) {
|
async rollDamage(event, data) {
|
||||||
const systemData = data.system ?? data;
|
const systemData = data.system ?? data;
|
||||||
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
|
/* let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
|
||||||
damageTypes = [...new Set(this.damage.parts.reduce((a, c) => a.concat([...c.type]), []))];
|
damageTypes = [...new Set(this.damage.parts.reduce((a, c) => a.concat([...c.type]), []))];
|
||||||
|
|
||||||
damageTypes = !damageTypes.length ? ['physical'] : damageTypes;
|
damageTypes = !damageTypes.length ? ['physical'] : damageTypes;
|
||||||
|
|
||||||
if (!formula || formula == '') return;
|
if (!formula || formula == '') return; */
|
||||||
let roll = { formula: formula, total: formula };
|
|
||||||
|
|
||||||
if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(systemData));
|
let formulas = this.damage.parts.map(p => ({
|
||||||
|
formula: this.getFormulaValue(p, data).getFormula(this.actor),
|
||||||
|
damageTypes: p.type,
|
||||||
|
applyTo: p.applyTo
|
||||||
|
}));
|
||||||
|
|
||||||
|
if(!formulas.length) return;
|
||||||
|
|
||||||
|
formulas = this.formatFormulas(formulas, systemData);
|
||||||
|
|
||||||
|
/* let roll = { formula: formula, total: formula };
|
||||||
|
|
||||||
|
if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(systemData)); */
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }),
|
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }),
|
||||||
roll: { formula },
|
// roll: { formula },
|
||||||
|
// roll: { formulas },
|
||||||
|
roll: formulas,
|
||||||
targets: systemData.targets.filter(t => t.hit) ?? data.targets,
|
targets: systemData.targets.filter(t => t.hit) ?? data.targets,
|
||||||
hasSave: this.hasSave,
|
hasSave: this.hasSave,
|
||||||
isCritical: systemData.roll?.isCritical ?? false,
|
isCritical: systemData.roll?.isCritical ?? false,
|
||||||
source: systemData.source,
|
source: systemData.source,
|
||||||
data: this.getRollData(),
|
data: this.getRollData(),
|
||||||
damageTypes,
|
// damageTypes,
|
||||||
event
|
event
|
||||||
};
|
};
|
||||||
if (this.hasSave) config.onSave = this.save.damageMod;
|
if (this.hasSave) config.onSave = this.save.damageMod;
|
||||||
|
|
@ -49,6 +76,7 @@ export default class DHDamageAction extends DHBaseAction {
|
||||||
config.directDamage = true;
|
config.directDamage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
// roll = CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||||
|
CONFIG.Dice.daggerheart.DamageRoll.build(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,13 @@ export default class DamageRoll extends DHRoll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBaseBonus() {
|
applyBaseBonus(part) {
|
||||||
const modifiers = [],
|
const modifiers = [],
|
||||||
type = this.options.messageType ?? 'damage';
|
type = this.options.messageType ?? 'damage',
|
||||||
|
options = part ?? this.options;
|
||||||
|
|
||||||
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
|
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
|
||||||
this.options.damageTypes?.forEach(t => {
|
options.damageTypes?.forEach(t => {
|
||||||
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
|
||||||
});
|
});
|
||||||
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
const weapons = ['primaryWeapon', 'secondaryWeapon'];
|
||||||
|
|
@ -42,13 +43,70 @@ export default class DamageRoll extends DHRoll {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructFormula(config) {
|
constructFormula(config) {
|
||||||
super.constructFormula(config);
|
this.options.roll.forEach( part => {
|
||||||
|
part.roll = new Roll(part.formula);
|
||||||
|
this.constructFormulaPart(config, part)
|
||||||
|
})
|
||||||
|
return this.options.roll;
|
||||||
|
}
|
||||||
|
|
||||||
if (config.isCritical) {
|
constructFormulaPart(config, part) {
|
||||||
const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }),
|
part.roll.terms = Roll.parse(part.roll.formula, config.data);
|
||||||
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);
|
|
||||||
this.terms.push(...this.formatModifier(criticalBonus));
|
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));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return (this._formula = this.constructor.getFormula(this.terms));
|
|
||||||
|
if (part.extraFormula) {
|
||||||
|
part.roll.terms.push(
|
||||||
|
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||||
|
...this.constructor.parse(part.extraFormula, this.options.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.isCritical && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) {
|
||||||
|
const tmpRoll = Roll.fromTerms(part.roll.terms)._evaluateSync({ maximize: true }),
|
||||||
|
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);
|
||||||
|
part.roll.terms.push(...this.formatModifier(criticalBonus));
|
||||||
|
}
|
||||||
|
return (part.roll._formula = this.constructor.getFormula(part.roll.terms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async evaluate({minimize=false, maximize=false, allowStrings=false, allowInteractive=true, ...options}={}) {
|
||||||
|
if ( this._evaluated ) {
|
||||||
|
throw new Error(`The ${this.constructor.name} has already been evaluated and is now immutable`);
|
||||||
|
}
|
||||||
|
this._evaluated = true;
|
||||||
|
if ( CONFIG.debug.dice ) console.debug(`Evaluating roll with formula "${this.formula}"`);
|
||||||
|
|
||||||
|
// Migration path for async rolls
|
||||||
|
if ( "async" in options ) {
|
||||||
|
foundry.utils.logCompatibilityWarning("The async option for Roll#evaluate has been removed. "
|
||||||
|
+ "Use Roll#evaluateSync for synchronous roll evaluation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.roll.forEach( async part => {
|
||||||
|
await part.roll.evaluate({minimize, maximize, allowStrings, allowInteractive, ...options})
|
||||||
|
})
|
||||||
|
// return this._evaluate({minimize, maximize, allowStrings, allowInteractive});
|
||||||
|
}
|
||||||
|
|
||||||
|
static postEvaluate(roll, config = {}) {
|
||||||
|
if (!config.roll) config.roll = {};
|
||||||
|
config.roll.total = roll.total;
|
||||||
|
config.roll.formula = roll.formula;
|
||||||
|
config.roll.dice = [];
|
||||||
|
roll.dice.forEach(d => {
|
||||||
|
config.roll.dice.push({
|
||||||
|
dice: d.denomination,
|
||||||
|
total: d.total,
|
||||||
|
formula: d.formula,
|
||||||
|
results: d.results
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export default class DHRoll extends Roll {
|
||||||
|
|
||||||
static async buildEvaluate(roll, config = {}, message = {}) {
|
static async buildEvaluate(roll, config = {}, message = {}) {
|
||||||
if (config.evaluate !== false) await roll.evaluate();
|
if (config.evaluate !== false) await roll.evaluate();
|
||||||
this.postEvaluate(roll, config);
|
config.roll = this.postEvaluate(roll);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async buildPost(roll, config, message) {
|
static async buildPost(roll, config, message) {
|
||||||
|
|
@ -63,19 +63,17 @@ export default class DHRoll extends Roll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static postEvaluate(roll, config = {}) {
|
static postEvaluate(roll) {
|
||||||
if (!config.roll) config.roll = {};
|
return {
|
||||||
config.roll.total = roll.total;
|
total: roll.total,
|
||||||
config.roll.formula = roll.formula;
|
formula: roll.formula,
|
||||||
config.roll.dice = [];
|
dice: roll.dice.map(d => ({
|
||||||
roll.dice.forEach(d => {
|
|
||||||
config.roll.dice.push({
|
|
||||||
dice: d.denomination,
|
dice: d.denomination,
|
||||||
total: d.total,
|
total: d.total,
|
||||||
formula: d.formula,
|
formula: d.formula,
|
||||||
results: d.results
|
results: d.results
|
||||||
});
|
}))
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toMessage(roll, config) {
|
static async toMessage(roll, config) {
|
||||||
|
|
@ -118,8 +116,9 @@ export default class DHRoll extends Roll {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
addModifiers() {
|
addModifiers(roll) {
|
||||||
this.options.roll.modifiers?.forEach(m => {
|
roll = roll ?? this.options.roll;
|
||||||
|
roll.modifiers?.forEach(m => {
|
||||||
this.terms.push(...this.formatModifier(m.value));
|
this.terms.push(...this.formatModifier(m.value));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -311,3 +311,15 @@ export const itemAbleRollParse = (value, actor, item) => {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const arraysEqual = (a, b) =>
|
||||||
|
a.length === b.length &&
|
||||||
|
[...new Set([...a, ...b])].every(
|
||||||
|
v => a.filter(e => e === v).length === b.filter(e => e === v).length
|
||||||
|
);
|
||||||
|
|
||||||
|
export const setsEqual = (a, b) =>
|
||||||
|
a.size === b.size &&
|
||||||
|
[...a].every(
|
||||||
|
value => b.has(value)
|
||||||
|
);
|
||||||
|
|
@ -22,8 +22,12 @@
|
||||||
{{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true classes="inline-child"}}
|
{{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true classes="inline-child"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{formField ../fields.applyTo value=dmg.applyTo name=(concat ../path "damage.parts." realIndex ".applyTo") localize=true}}
|
<div class="nest-inputs">
|
||||||
{{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}}
|
{{formField ../fields.applyTo value=dmg.applyTo name=(concat ../path "damage.parts." realIndex ".applyTo") localize=true}}
|
||||||
|
{{#if (eq dmg.applyTo 'hitPoints')}}
|
||||||
|
{{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
{{#if ../horde}}
|
{{#if ../horde}}
|
||||||
<fieldset class="one-column">
|
<fieldset class="one-column">
|
||||||
<legend>{{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}}</legend>
|
<legend>{{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}}</legend>
|
||||||
|
|
@ -57,8 +61,12 @@
|
||||||
{{> formula fields=../../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
|
{{> formula fields=../../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{formField ../../fields.applyTo value=dmg.applyTo name=(concat "damage.parts." realIndex ".applyTo") localize=true}}
|
<div class="nest-inputs">
|
||||||
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}}
|
{{formField ../../fields.applyTo value=dmg.applyTo name=(concat "damage.parts." realIndex ".applyTo") localize=true}}
|
||||||
|
{{#if (eq dmg.applyTo 'hitPoints')}}
|
||||||
|
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
<input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
|
<input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
|
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
<header class="dialog-header">
|
<header class="dialog-header">
|
||||||
<h1>{{title}}</h1>
|
<h1>{{title}}</h1>
|
||||||
</header>
|
</header>
|
||||||
<span class="formula-label"><b>Formula:</b> {{@root.formula}}</span>
|
{{#each @root.formula}}
|
||||||
<div class="form-group">
|
<span class="formula-label"><b>Formula:</b> {{roll.formula}}</span>
|
||||||
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus">
|
<div class="form-group">
|
||||||
</div>
|
<input type="text" value="{{extraFormula}}" name="roll.{{ @index }}.extraFormula" placeholder="Situational Bonus">
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
<div class="damage-section-controls">
|
<div class="damage-section-controls">
|
||||||
{{#if directDamage}}
|
{{#if directDamage}}
|
||||||
<select class="roll-mode-select" name="selectedRollMode">
|
<select class="roll-mode-select" name="selectedRollMode">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue