Merged with main

This commit is contained in:
WBHarry 2026-06-05 16:00:09 +02:00
commit d78927d0c2
275 changed files with 5076 additions and 4132 deletions

View file

@ -1,4 +1,5 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
import { triggerChatRollFx } from '../helpers/utils.mjs';
import DHRoll from './dhRoll.mjs';
export default class D20Roll extends DHRoll {
@ -224,4 +225,15 @@ export default class D20Roll extends DHRoll {
resetFormula() {
return (this._formula = this.constructor.getFormula(this.terms));
}
async reroll(options) {
const result = await super.reroll(options);
if (this instanceof game.system.api.dice.DualityRoll) return result;
if (options?.liveRoll) {
await triggerChatRollFx([result]);
}
return result;
}
}

View file

@ -1,5 +1,5 @@
import DamageDialog from '../applications/dialogs/damageDialog.mjs';
import { parseRallyDice } from '../helpers/utils.mjs';
import { parseRallyDice, triggerChatRollFx } from '../helpers/utils.mjs';
import DHRoll from './dhRoll.mjs';
export default class DamageRoll extends DHRoll {
@ -18,7 +18,12 @@ export default class DamageRoll extends DHRoll {
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));
const parts = [];
for (const roll of config.roll) {
parts.push(this.postEvaluate(roll));
roll.roll = JSON.stringify(roll.roll.toJSON());
}
config.damage = this.unifyDamageRoll(parts);
}
@ -38,25 +43,24 @@ export default class DamageRoll extends DHRoll {
const chatMessage = config.source?.message
? ui.chat.collection.get(config.source.message)
: getDocumentClass('ChatMessage').applyMode({}, config.rollMode ?? 'public');
const diceRolls = [];
if (game.modules.get('dice-so-nice')?.active) {
const pool = foundry.dice.terms.PoolTerm.fromRolls(
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
),
diceRoll = Roll.fromTerms([pool]);
await game.dice3d.showForRoll(
diceRoll,
game.user,
true,
chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
chatMessage.blind
);
config.mute = true;
const pool = foundry.dice.terms.PoolTerm.fromRolls(
Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll))
);
diceRolls.push(Roll.fromTerms([pool]));
}
await triggerChatRollFx(diceRolls, {
whisper: chatMessage.whisper?.length > 0 ? chatMessage.whisper : null,
blind: chatMessage.blind
});
await super.buildPost(roll, config, message);
if (config.source?.message) {
chatMessage.update({ 'system.damage': config.damage });
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
}
}
@ -319,9 +323,10 @@ export default class DamageRoll extends DHRoll {
const newIndex = parsedDiceTerms[dice].results.length;
await term.reroll(`/r1=${termResult.result}`);
const diceRolls = [];
if (game.modules.get('dice-so-nice')?.active) {
const newResult = parsedDiceTerms[dice].results[newIndex];
const diceSoNiceRoll = {
diceRolls.push({
_evaluated: true,
dice: [
new foundry.dice.terms.Die({
@ -332,11 +337,10 @@ export default class DamageRoll extends DHRoll {
})
],
options: { appearance: {} }
};
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
});
}
await triggerChatRollFx(diceRolls);
await parsedRoll.evaluate();
const results = parsedRoll.dice[dice].results.map(result => ({

View file

@ -1,4 +1,5 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
import { triggerChatRollFx } from '../helpers/utils.mjs';
export default class DHRoll extends Roll {
baseTerms = [];
@ -36,6 +37,7 @@ export default class DHRoll extends Roll {
static async buildConfigure(config = {}, message = {}) {
config.hooks = [...this.getHooks(), ''];
config.dialog ??= {};
config.damageOptions ??= {};
for (const hook of config.hooks) {
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
@ -75,9 +77,7 @@ export default class DHRoll extends Roll {
}
if (config.skips?.createMessage) {
if (game.modules.get('dice-so-nice')?.active) {
await game.dice3d.showForRoll(roll, game.user, true);
}
await triggerChatRollFx([roll]);
} else if (!config.source?.message) {
config.message = await this.toMessage(roll, config);
}
@ -85,6 +85,7 @@ export default class DHRoll extends Roll {
static postEvaluate(roll, config = {}) {
return {
...roll.options.roll,
total: roll.total,
formula: roll.formula,
dice: roll.dice.map(d => ({
@ -141,8 +142,8 @@ export default class DHRoll extends Roll {
const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options });
return foundry.applications.handlebars.renderTemplate(template, {
...chatData,
roll: this,
...chatData,
parent: chatData.parent,
targetMode: chatData.targetMode,
areas: chatData.action?.areas,
@ -257,7 +258,7 @@ export default class DHRoll extends Roll {
if (!roll.terms[i].isDeterministic) continue;
const termTotal = roll.terms[i].total;
if (typeof termTotal === 'number') {
const multiplier = roll.terms[i - 1]?.operator === ' - ' ? -1 : 1;
const multiplier = roll.terms[i - 1]?.operator === '-' ? -1 : 1;
modifierTotal += multiplier * termTotal;
}
}

View file

@ -1,4 +1,4 @@
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
import { updateResourcesForDualityReroll } from '../helpers.mjs';
export default class DualityDie extends foundry.dice.terms.Die {
constructor(options) {
@ -12,24 +12,6 @@ export default class DualityDie extends foundry.dice.terms.Die {
return roll.withHope ? 1 : roll.withFear ? -1 : 0;
}
#updateResources(oldDuality, newDuality, actor) {
const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return;
const updates = [];
const hope = (newDuality >= 0 ? 1 : 0) - (oldDuality >= 0 ? 1 : 0);
const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0);
const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0);
if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true });
if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true });
if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true });
const resourceUpdates = new ResourceUpdateMap(actor);
resourceUpdates.addResources(updates);
resourceUpdates.updateResources();
}
async reroll(modifier, options) {
const oldDuality = this.#getDualityState(options.liveRoll.roll);
await super.reroll(modifier, options);
@ -57,7 +39,7 @@ export default class DualityDie extends foundry.dice.terms.Die {
if (options.liveRoll.isReaction) return;
const newDuality = this.#getDualityState(options.liveRoll.roll);
this.#updateResources(oldDuality, newDuality, options.liveRoll.actor);
updateResourcesForDualityReroll(oldDuality, newDuality, options.liveRoll.actor);
}
}

View file

@ -1,6 +1,8 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
import D20Roll from './d20Roll.mjs';
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
import { updateResourcesForDualityReroll } from './helpers.mjs';
export default class DualityRoll extends D20Roll {
_advantageNumber = 1;
@ -128,32 +130,34 @@ export default class DualityRoll extends D20Roll {
}
createBaseDice() {
if (
this.dice[0] instanceof game.system.api.dice.diceTypes.HopeDie &&
this.dice[1] instanceof game.system.api.dice.diceTypes.FearDie
) {
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
return;
}
this.terms = [this.terms[0], this.terms[1], this.terms[2]];
this.terms[0] = new game.system.api.dice.diceTypes.HopeDie({
faces: this.data.rules.roll?.hopeFaces ?? 12
faces: this.terms[0]?.faces ?? this.data.rules.dualityRoll?.defaultHopeDice ?? 12
});
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
this.terms[2] = new game.system.api.dice.diceTypes.FearDie({
faces: this.data.rules.roll?.fearFaces ?? 12
faces: this.terms[2]?.faces ?? this.data.rules.dualityRoll?.defaultFearDice ?? 12
});
}
applyAdvantage() {
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'];
this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
advDie
);
const advDieClass = this.hasAdvantage
? game.system.api.dice.diceTypes.AdvantageDie
: this.hasDisadvantage
? game.system.api.dice.diceTypes.DisadvantageDie
: null;
if (advDieClass) {
const advDie = new advDieClass({ faces: this.advantageFaces, number: this.advantageNumber });
if (this.terms.length < 4) {
if (this.advantageNumber > 1) advDie.modifiers = ['kh'];
this.terms.push(
new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }),
advDie
);
} else {
this.terms[4] = advDie;
}
}
if (this.rallyFaces)
this.terms.push(
@ -378,4 +382,40 @@ export default class DualityRoll extends D20Roll {
if (currentCombatant?.actorId == config.data.id) ui.combat.setCombatantSpotlight(currentCombatant.id);
}
}
async reroll(options) {
const oldDuality = this.withHope ? 1 : this.withFear ? -1 : 0;
const rerolled = DualityRoll.fromData((await super.reroll(options)).toJSON());
if (options?.liveRoll) {
if (game.modules.get('dice-so-nice')?.active) {
const diceAppearance = await getDiceSoNicePresets(
rerolled,
rerolled.dHope.denomination,
rerolled.dFear.denomination
);
rerolled.dHope.options.appearance = diceAppearance.hope.appearance;
rerolled.dFear.options.appearance = diceAppearance.fear.appearance;
if (rerolled.dAdvantage) rerolled.dAdvantage.options.appearance = diceAppearance.advantage.appearance;
if (rerolled.dDisadvantage)
rerolled.dDisadvantage.options.appearance = diceAppearance.disadvantage.appearance;
await game.dice3d.showForRoll(rerolled, game.user, true);
} else {
foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
}
if (this.options.actionType === 'reaction') return;
const newDuality = rerolled.withHope ? 1 : rerolled.withFear ? -1 : 0;
const actor = await foundry.utils.fromUuid(this.options.source.actor);
updateResourcesForDualityReroll(oldDuality, newDuality, actor);
}
return rerolled;
}
fromJSON(json) {
return super.fromJSON(json);
}
}

19
module/dice/helpers.mjs Normal file
View file

@ -0,0 +1,19 @@
import { ResourceUpdateMap } from '../data/action/baseAction.mjs';
export function updateResourcesForDualityReroll(oldDuality, newDuality, actor) {
const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return;
const updates = [];
const hope = (newDuality >= 0 ? 1 : 0) - (oldDuality >= 0 ? 1 : 0);
const stress = (newDuality === 0 ? 1 : 0) - (oldDuality === 0 ? 1 : 0);
const fear = (newDuality === -1 ? 1 : 0) - (oldDuality === -1 ? 1 : 0);
if (hope !== 0) updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true });
if (stress !== 0) updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true });
if (fear !== 0) updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true });
const resourceUpdates = new ResourceUpdateMap(actor);
resourceUpdates.addResources(updates);
resourceUpdates.updateResources();
}