Merged with main

This commit is contained in:
WBHarry 2025-07-19 15:49:49 +02:00
commit ccba480250
26 changed files with 437 additions and 251 deletions

View file

@ -92,7 +92,7 @@ export default class D20Roll extends DHRoll {
configureModifiers() {
this.applyAdvantage();
this.baseTerms = foundry.utils.deepClone(this.dice);
this.options.roll.modifiers = this.applyBaseBonus();
@ -140,28 +140,21 @@ export default class D20Roll extends DHRoll {
return modifiers;
}
static async buildEvaluate(roll, config = {}, message = {}) {
if (config.evaluate !== false) await roll.evaluate();
this.postEvaluate(roll, config);
}
static postEvaluate(roll, config = {}) {
super.postEvaluate(roll, config);
const data = super.postEvaluate(roll, config);
if (config.targets?.length) {
config.targets.forEach(target => {
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
target.hit = this.isCritical || roll.total >= difficulty;
});
} else if (config.roll.difficulty)
config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty;
config.roll.advantage = {
} else if (config.roll.difficulty) data.success = roll.isCritical || roll.total >= config.roll.difficulty;
data.advantage = {
type: config.roll.advantage,
dice: roll.dAdvantage?.denomination,
value: roll.dAdvantage?.total
};
config.roll.isCritical = roll.isCritical;
config.roll.extra = roll.dice
data.isCritical = roll.isCritical;
data.extra = roll.dice
.filter(d => !roll.baseTerms.includes(d))
.map(d => {
return {
@ -169,7 +162,8 @@ export default class D20Roll extends DHRoll {
value: d.total
};
});
config.roll.modifierTotal = this.calculateTotalModifiers(roll);
data.modifierTotal = this.calculateTotalModifiers(roll);
return data;
}
resetFormula() {

View file

@ -10,10 +10,24 @@ export default class DamageRoll extends DHRoll {
static DefaultDialog = DamageDialog;
static async postEvaluate(roll, config = {}) {
super.postEvaluate(roll, config);
config.roll.type = config.type;
config.roll.modifierTotal = this.calculateTotalModifiers(roll);
static async buildEvaluate(roll, config = {}, message = {}) {
if (config.evaluate !== false) {
for (const roll of config.roll) await roll.roll.evaluate();
}
roll._evaluated = true;
const parts = config.roll.map(r => this.postEvaluate(r));
config.roll = this.unifyDamageRoll(parts);
}
static postEvaluate(roll, config = {}) {
return {
...roll,
...super.postEvaluate(roll.roll, config),
damageTypes: [...(roll.damageTypes ?? [])],
roll: roll.roll,
type: config.type,
modifierTotal: this.calculateTotalModifiers(roll.roll)
};
}
static async buildPost(roll, config, message) {
@ -24,12 +38,50 @@ export default class DamageRoll extends DHRoll {
}
}
applyBaseBonus() {
static unifyDamageRoll(rolls) {
const unified = {};
rolls.forEach(r => {
const resource = unified[r.applyTo] ?? { formula: '', total: 0, parts: [] };
resource.formula += `${resource.formula !== '' ? ' + ' : ''}${r.formula}`;
resource.total += r.total;
resource.parts.push(r);
unified[r.applyTo] = resource;
});
return unified;
}
static formatGlobal(rolls) {
let formula, total;
const applyTo = new Set(rolls.flatMap(r => r.applyTo));
if (applyTo.size > 1) {
const data = {};
rolls.forEach(r => {
if (data[r.applyTo]) {
data[r.applyTo].formula += ` + ${r.formula}`;
data[r.applyTo].total += r.total;
} else {
data[r.applyTo] = {
formula: r.formula,
total: r.total
};
}
});
formula = Object.entries(data).reduce((a, [k, v]) => a + ` ${k}: ${v.formula}`, '');
total = Object.entries(data).reduce((a, [k, v]) => a + ` ${k}: ${v.total}`, '');
} else {
formula = rolls.map(r => r.formula).join(' + ');
total = rolls.reduce((a, c) => a + c.total, 0);
}
return { formula, total };
}
applyBaseBonus(part) {
const modifiers = [],
type = this.options.messageType ?? 'damage';
type = this.options.messageType ?? 'damage',
options = part ?? this.options;
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`));
});
const weapons = ['primaryWeapon', 'secondaryWeapon'];
@ -42,13 +94,36 @@ export default class DamageRoll extends DHRoll {
}
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) {
const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }),
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);
this.terms.push(...this.formatModifier(criticalBonus));
constructFormulaPart(config, part) {
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));
});
}
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));
}
}

View file

@ -47,7 +47,7 @@ export default class DHRoll extends Roll {
static async buildEvaluate(roll, config = {}, message = {}) {
if (config.evaluate !== false) await roll.evaluate();
this.postEvaluate(roll, config);
config.roll = this.postEvaluate(roll, config);
}
static async buildPost(roll, config, message) {
@ -57,25 +57,27 @@ export default class DHRoll extends Roll {
// Create Chat Message
if (config.source?.message) {
if (Object.values(config.roll)?.length) {
const pool = foundry.dice.terms.PoolTerm.fromRolls(
Object.values(config.roll).flatMap(r => r.parts.map(p => p.roll))
);
roll = Roll.fromTerms([pool]);
}
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
} else {
config.message = await this.toMessage(roll, config);
}
} else config.message = await this.toMessage(roll, config);
}
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({
return {
total: roll.total,
formula: roll.formula,
dice: roll.dice.map(d => ({
dice: d.denomination,
total: d.total,
formula: d.formula,
results: d.results
});
});
}))
};
}
static async toMessage(roll, config) {
@ -118,8 +120,9 @@ export default class DHRoll extends Roll {
return [];
}
addModifiers() {
this.options.roll.modifiers?.forEach(m => {
addModifiers(roll) {
roll = roll ?? this.options.roll;
roll.modifiers?.forEach(m => {
this.terms.push(...this.formatModifier(m.value));
});
}

View file

@ -63,24 +63,22 @@ export default class DualityRoll extends D20Roll {
}
setRallyChoices() {
return this.data?.parent?.effects.reduce((a,c) => {
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
if(change) a.push({ value: c.id, label: change.value });
return a;
}, []);
return this.data?.parent?.effects.reduce((a, c) => {
const change = c.changes.find(ch => ch.key === 'system.bonuses.rally');
if (change) a.push({ value: c.id, label: change.value });
return a;
}, []);
}
get dRally() {
if(!this.rallyFaces) return null;
if(this.hasDisadvantage || this.hasAdvantage)
return this.dice[3];
else
return this.dice[2];
if (!this.rallyFaces) return null;
if (this.hasDisadvantage || this.hasAdvantage) return this.dice[3];
else return this.dice[2];
}
get rallyFaces() {
const rallyChoice = this.rallyChoices?.find(r => r.value === this._rallyIndex)?.label;
return rallyChoice ? this.getFaces(rallyChoice) : null;
return rallyChoice ? this.getFaces(rallyChoice) : null;
}
get isCritical() {
@ -129,13 +127,13 @@ export default class DualityRoll extends D20Roll {
if (this.hasAdvantage || this.hasDisadvantage) {
const dieFaces = this.advantageFaces,
advDie = new foundry.dice.terms.Die({ faces: dieFaces, number: this.advantageNumber });
if(this.advantageNumber > 1) advDie.modifiers = ['kh'];
if (this.advantageNumber > 1) advDie.modifiers = ['kh'];
this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
advDie
);
}
if(this.rallyFaces)
if (this.rallyFaces)
this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
new foundry.dice.terms.Die({ faces: this.rallyFaces })
@ -161,29 +159,31 @@ export default class DualityRoll extends D20Roll {
}
static postEvaluate(roll, config = {}) {
super.postEvaluate(roll, config);
config.roll.hope = {
const data = super.postEvaluate(roll, config);
data.hope = {
dice: roll.dHope.denomination,
value: roll.dHope.total
};
config.roll.fear = {
data.fear = {
dice: roll.dFear.denomination,
value: roll.dFear.total
};
config.roll.rally = {
data.rally = {
dice: roll.dRally?.denomination,
value: roll.dRally?.total
};
config.roll.result = {
data.result = {
duality: roll.withHope ? 1 : roll.withFear ? -1 : 0,
total: roll.dHope.total + roll.dFear.total,
label: roll.totalLabel
};
if(roll._rallyIndex && roll.data?.parent)
if (roll._rallyIndex && roll.data?.parent)
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
setDiceSoNiceForDualityRoll(roll, config.roll.advantage.type);
setDiceSoNiceForDualityRoll(roll, data.advantage.type);
return data;
}
}