Initial release
This commit is contained in:
@@ -27,6 +27,7 @@ export default class MGNEActorSheet extends HandlebarsApplicationMixin(foundry.a
|
||||
syncArtifact: MGNEActorSheet.onSyncArtifact,
|
||||
resetDaily: MGNEActorSheet.onResetDaily,
|
||||
rollResonancePerDay: MGNEActorSheet.onRollResonancePerDay,
|
||||
clearResonationBlock: MGNEActorSheet.onClearResonationBlock,
|
||||
quickRest: MGNEActorSheet.onQuickRest,
|
||||
fullRest: MGNEActorSheet.onFullRest,
|
||||
},
|
||||
@@ -146,6 +147,10 @@ export default class MGNEActorSheet extends HandlebarsApplicationMixin(foundry.a
|
||||
return this.document.rollResonancePerDay()
|
||||
}
|
||||
|
||||
static async onClearResonationBlock() {
|
||||
return this.document.update({ "system.resonance.blocked": false })
|
||||
}
|
||||
|
||||
static async onQuickRest() {
|
||||
return this.document.quickRest()
|
||||
}
|
||||
|
||||
@@ -171,6 +171,10 @@ export default class MGNEActor extends Actor {
|
||||
ui.notifications.warn(f("MGNE.Notification.ItemBurnedOut", { item: item.name }))
|
||||
return null
|
||||
}
|
||||
if (this.system.resonance?.blocked) {
|
||||
ui.notifications.warn(f("MGNE.Notification.ResonationBlocked", { actor: this.name }))
|
||||
return null
|
||||
}
|
||||
if ((this.system.resonance?.used ?? 0) >= (this.system.resonance?.max ?? 0)) {
|
||||
ui.notifications.warn(f("MGNE.Notification.ResonancePerDayReached", { actor: this.name }))
|
||||
return null
|
||||
@@ -187,7 +191,10 @@ export default class MGNEActor extends Actor {
|
||||
await this.update({ "system.resonance.used": (this.system.resonance.used ?? 0) + 1 })
|
||||
|
||||
if (!result.success) {
|
||||
await this.applyDamage(1, { sourceItem: item, ignoreArmor: true, chat: false })
|
||||
const feedbackRoll = await (new Roll("1d2")).evaluate()
|
||||
await this.applyDamage(feedbackRoll.total, { sourceItem: item, ignoreArmor: true, chat: true })
|
||||
await this.update({ "system.resonance.blocked": true })
|
||||
ui.notifications.warn(f("MGNE.Notification.ResonationFeedbackBlocked", { actor: this.name }))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -245,6 +252,7 @@ export default class MGNEActor extends Actor {
|
||||
await this.update({
|
||||
"system.hp.value": newHp,
|
||||
"system.omens.current": omenRoll.total,
|
||||
"system.resonance.blocked": false,
|
||||
})
|
||||
|
||||
await MGNERoll.createRestCard({
|
||||
@@ -297,6 +305,7 @@ export default class MGNEActor extends Actor {
|
||||
"system.omens.current": omenRoll.total,
|
||||
"system.resonance.max": resonanceMax,
|
||||
"system.resonance.used": 0,
|
||||
"system.resonance.blocked": false,
|
||||
"system.artifactSync.used": 0,
|
||||
"system.survival.salvationUsed": false,
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user