Initial release

This commit is contained in:
2026-05-08 23:05:29 +02:00
parent c0223977d2
commit 2953481a1c
157 changed files with 1940 additions and 392 deletions
+42 -4
View File
@@ -97,15 +97,18 @@ export default class MGNERoll {
const modifier = Number.parseInt(dialogData.modifier ?? 0, 10) || 0
const spendOmen = Boolean(dialogData.spendOmen)
const dr = (Number.parseInt(dialogData.dr ?? baseDR, 10) || baseDR) - (spendOmen ? 4 : 0)
// Re-read omens after dialog close to avoid race condition (omen could have changed)
const currentOmensAfterDialog = actor.system.omens?.current ?? 0
const canSpendOmen = spendOmen && currentOmensAfterDialog > 0
const dr = (Number.parseInt(dialogData.dr ?? baseDR, 10) || baseDR) - (canSpendOmen ? 4 : 0)
const abilityValue = actor.system.abilities?.[abilityId]?.value ?? 0
const sign = modifier >= 0 ? "+" : "-"
const formula = modifier === 0 ? `1d20 + ${abilityValue}` : `1d20 + ${abilityValue} ${sign} ${Math.abs(modifier)}`
const roll = await (new Roll(formula)).evaluate()
const natural = roll.dice?.[0]?.results?.[0]?.result ?? roll.total
if (spendOmen && (actor.system.omens?.current ?? 0) > 0) {
await actor.update({ "system.omens.current": Math.max(0, actor.system.omens.current - 1) })
if (canSpendOmen) {
await actor.update({ "system.omens.current": Math.max(0, currentOmensAfterDialog - 1) })
}
const critical = natural === 20
@@ -126,6 +129,15 @@ export default class MGNERoll {
specialText = rollType === "attack" ? t("MGNE.Roll.AttackFumble") : rollType === "defense" ? t("MGNE.Roll.DefenseFumble") : pickRandom(SYSTEM.tables.mishaps)
}
const actorOmens = actor.system.omens?.current ?? 0
let omenNeutralizeReminder = ""
let omenRerollReminder = ""
if (actorOmens > 0) {
if (critical) omenNeutralizeReminder = f("MGNE.Roll.OmenNeutralizeCrit", { omens: actorOmens })
else if (fumble) omenNeutralizeReminder = f("MGNE.Roll.OmenNeutralizeFumble", { omens: actorOmens })
else omenRerollReminder = f("MGNE.Roll.OmenRerollReminder", { omens: actorOmens })
}
const showDamageButton = rollType === "attack" && (success || critical) && !!item
const contentHtml = await renderCard({
mode: "check",
@@ -137,6 +149,8 @@ export default class MGNERoll {
total: roll.total,
outcome,
specialText,
omenNeutralizeReminder,
omenRerollReminder,
showDamageButton,
damageActorId: showDamageButton ? actor.id : null,
damageItemId: showDamageButton ? item.id : null,
@@ -183,7 +197,30 @@ export default class MGNERoll {
const multiplier = damageBonus?.multiplier ?? 1
const baseFormula = item.system.damage || "1"
const formula = multiplier > 1 ? `${multiplier} * (${baseFormula})` : baseFormula
const roll = await (new Roll(formula)).evaluate()
const actorOmens = actor.system.omens?.current ?? 0
let maximize = false
if (actorOmens > 0) {
const choice = await foundry.applications.api.DialogV2.wait({
window: { title: f("MGNE.Roll.ItemDamageLabel", { item: item.name }) },
classes: ["mgne", "roll-dialog"],
content: `<section class="mgne-roll-dialog"><p>${f("MGNE.RollDialog.OmenMaximizePrompt", { omens: actorOmens })}</p></section>`,
buttons: [
{ action: "roll", label: t("MGNE.Common.Roll"), icon: "fa-solid fa-dice", callback: () => "roll" },
{ action: "maximize", label: t("MGNE.RollDialog.SpendOmenMaximize"), icon: "fa-solid fa-star", callback: () => "maximize" },
],
rejectClose: false,
})
if (choice === null) return null
maximize = choice === "maximize"
}
const roll = await (new Roll(formula)).evaluate(maximize ? { maximize: true } : {})
if (maximize) {
// Re-read omens after dialog to avoid overwriting concurrent changes
const currentOmens = actor.system.omens?.current ?? 0
await actor.update({ "system.omens.current": Math.max(0, currentOmens - 1) })
}
const isCritical = multiplier > 1
const contentHtml = await renderCard({
mode: "damage",
@@ -195,6 +232,7 @@ export default class MGNERoll {
total: roll.total,
outcome: t("MGNE.Roll.OutcomeRolled"),
specialText: isCritical ? t("MGNE.Roll.CriticalDamageApplied") : "",
omenMaximized: maximize,
showApplyButton: true,
damageTotal: roll.total,
damageCritical: isCritical,