Compare commits

...

2 Commits

Author SHA1 Message Date
uberwald b35b684d50 NEgative values for HP and weapon bonuses 2026-06-06 16:11:36 +02:00
uberwald f6fb0b68b8 Fix spell rolls again
Release Creation / build (release) Successful in 47s
2026-05-25 20:41:00 +02:00
6 changed files with 339 additions and 262 deletions
+41 -28
View File
@@ -526,19 +526,31 @@ Hooks.on("createChatMessage", async (message) => {
}) })
} }
// Si le défenseur est un personnage qui perd, proposer Grit/Luck (seulement s'il a des points) // Reaction phase — both sides may use grit/luck/shield/mulligan before the outcome is resolved.
// Seulement si l'utilisateur actuel est le propriétaire du défenseur // After a mulligan reroll (either side), the comparison restarts so both sides can react to the new numbers.
let defenderHandledBonus = false let defenderHandledBonus = false
let attackerHandledBonus = false
let shieldReaction = null let shieldReaction = null
let shieldBlocked = false let shieldBlocked = false
const isSpellOrMiracle = attackRollType === "spell-attack" || attackRollType === "miracle-attack" const isSpellOrMiracle = attackRollType === "spell-attack" || attackRollType === "miracle-attack"
if (defender && defenseRoll < attackRoll && isPrimaryController(defender) && !isSpellOrMiracle) {
// These persist across mulligan restarts (once used they stay consumed)
const shieldData = LethalFantasyUtils.getShieldReactionData(defender) const shieldData = LethalFantasyUtils.getShieldReactionData(defender)
let canRerollDefense = LethalFantasyUtils.hasD30Reroll(defenseD30message) let canRerollDefense = LethalFantasyUtils.hasD30Reroll(defenseD30message)
let canShieldReact = !!shieldData let canShieldReact = !!shieldData
let canAdHocShield = !shieldData let canAdHocShield = !shieldData
let attackRollFinal = attackRoll
let canRerollAttack = LethalFantasyUtils.hasD30Reroll(attackD30message)
let mulliganRestart = false
while (defenseRoll < attackRoll) { do {
mulliganRestart = false
defenderHandledBonus = false
attackerHandledBonus = false
// ── Defense reaction loop ──────────────────────────────────────────────
if (defender && defenseRoll < attackRollFinal && isPrimaryController(defender) && !isSpellOrMiracle) {
while (defenseRoll < attackRollFinal) {
const currentGrit = Number(defender.system?.grit?.current) || 0 const currentGrit = Number(defender.system?.grit?.current) || 0
const currentLuck = Number(defender.system?.luck?.current) || 0 const currentLuck = Number(defender.system?.luck?.current) || 0
const buttons = [] const buttons = []
@@ -571,7 +583,7 @@ Hooks.on("createChatMessage", async (message) => {
if (canRerollDefense) { if (canRerollDefense) {
buttons.push({ buttons.push({
action: "rerollDefense", action: "rerollDefense",
label: "Re-roll defense", label: "Re-roll defense (Mulligan)",
icon: "fa-solid fa-rotate-right", icon: "fa-solid fa-rotate-right",
callback: () => "rerollDefense" callback: () => "rerollDefense"
}) })
@@ -585,7 +597,6 @@ Hooks.on("createChatMessage", async (message) => {
callback: () => "shieldReact" callback: () => "shieldReact"
}) })
} else if (canAdHocShield) { } else if (canAdHocShield) {
// No pre-configured shield — offer ad-hoc shield option (useful for monsters)
buttons.push({ buttons.push({
action: "adHocShield", action: "adHocShield",
label: "Roll ad-hoc shield (choose dice + DR)", label: "Roll ad-hoc shield (choose dice + DR)",
@@ -607,7 +618,7 @@ Hooks.on("createChatMessage", async (message) => {
content: ` content: `
<div class="grit-luck-dialog"> <div class="grit-luck-dialog">
<div class="combat-status"> <div class="combat-status">
<p><strong>${attackerName}</strong> rolled <strong>${attackRoll}</strong></p> <p><strong>${attackerName}</strong> rolled <strong>${attackRollFinal}</strong></p>
<p><strong>${defenderName}</strong> currently has <strong>${defenseRoll}</strong></p> <p><strong>${defenderName}</strong> currently has <strong>${defenseRoll}</strong></p>
${defenseD30message ? `<p class="bonus-info">D30 special: ${defenseD30message.description}</p>` : ""} ${defenseD30message ? `<p class="bonus-info">D30 special: ${defenseD30message.description}</p>` : ""}
</div> </div>
@@ -623,21 +634,21 @@ Hooks.on("createChatMessage", async (message) => {
defenderHandledBonus = true defenderHandledBonus = true
if (choice === "grit") { if (choice === "grit") {
const bonusRoll = await LethalFantasyUtils.rollBonusDie("1d6", defender, (total) => `<p><strong>${defenderName}</strong> spends 1 Grit and rolls <strong>${total}</strong> for defense.</p>`) const bonusRoll = await LethalFantasyUtils.rollBonusDie("1d6", defender, total => `<p><strong>${defenderName}</strong> spends 1 Grit and rolls <strong>${total}</strong> for defense.</p>`)
defenseRoll += bonusRoll defenseRoll += bonusRoll
await defender.update({ "system.grit.current": currentGrit - 1 }) await defender.update({ "system.grit.current": currentGrit - 1 })
continue continue
} }
if (choice === "luck") { if (choice === "luck") {
const bonusRoll = await LethalFantasyUtils.rollBonusDie("1d6", defender, (total) => `<p><strong>${defenderName}</strong> spends 1 Luck and rolls <strong>${total}</strong> for defense.</p>`) const bonusRoll = await LethalFantasyUtils.rollBonusDie("1d6", defender, total => `<p><strong>${defenderName}</strong> spends 1 Luck and rolls <strong>${total}</strong> for defense.</p>`)
defenseRoll += bonusRoll defenseRoll += bonusRoll
await defender.update({ "system.luck.current": currentLuck - 1 }) await defender.update({ "system.luck.current": currentLuck - 1 })
continue continue
} }
if (choice === "bonusDie") { if (choice === "bonusDie") {
const bonusDie = await LethalFantasyUtils.promptCombatBonusDie(defenderName, "attack", defenseRoll, attackRoll) const bonusDie = await LethalFantasyUtils.promptCombatBonusDie(defenderName, "attack", defenseRoll, attackRollFinal)
if (!bonusDie) continue if (!bonusDie) continue
const bonusRoll = await LethalFantasyUtils.rollBonusDie(bonusDie, defender, (total, formula) => `<p><strong>${defenderName}</strong> adds <strong>${formula.toUpperCase()}</strong> and rolls <strong>${total}</strong> for defense.</p>`) const bonusRoll = await LethalFantasyUtils.rollBonusDie(bonusDie, defender, (total, formula) => `<p><strong>${defenderName}</strong> adds <strong>${formula.toUpperCase()}</strong> and rolls <strong>${total}</strong> for defense.</p>`)
defenseRoll += bonusRoll defenseRoll += bonusRoll
@@ -650,8 +661,10 @@ Hooks.on("createChatMessage", async (message) => {
canRerollDefense = false canRerollDefense = false
if (!reroll) continue if (!reroll) continue
defenseRoll = reroll.options?.rollTotal || reroll.total || oldDefenseRoll defenseRoll = reroll.options?.rollTotal || reroll.total || oldDefenseRoll
await createReactionMessage(defender, `<p><strong>${defenderName}</strong> uses Mulligan and re-rolls defense: <strong>${oldDefenseRoll}</strong> → <strong>${defenseRoll}</strong>.</p>`) await createReactionMessage(defender, `<p><strong>${defenderName}</strong> uses Mulligan and re-rolls defense: <strong>${oldDefenseRoll}</strong> → <strong>${defenseRoll}</strong>. Both sides may now react to the new numbers.</p>`)
continue // Restart the full comparison so both sides can react to the new roll
mulliganRestart = true
break
} }
if (choice === "shieldReact" && canShieldReact) { if (choice === "shieldReact" && canShieldReact) {
@@ -660,7 +673,7 @@ Hooks.on("createChatMessage", async (message) => {
defenseRoll = newDefenseTotal defenseRoll = newDefenseTotal
canShieldReact = false canShieldReact = false
if (newDefenseTotal >= attackRoll) { if (newDefenseTotal >= attackRollFinal) {
shieldBlocked = true shieldBlocked = true
shieldReaction = { shieldReaction = {
damageReduction: shieldData.damageReduction, damageReduction: shieldData.damageReduction,
@@ -669,19 +682,19 @@ Hooks.on("createChatMessage", async (message) => {
} }
await createReactionMessage( await createReactionMessage(
defender, defender,
`<p><strong>${defenderName}</strong> rolls <strong>${shieldData.label}</strong> and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal}${attackRoll}). <strong>Shield blocked the attack!</strong> Both armor DR and shield DR <strong>${shieldData.damageReduction}</strong> will apply to damage.</p>` `<p><strong>${defenderName}</strong> rolls <strong>${shieldData.label}</strong> and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal}${attackRollFinal}). <strong>Shield blocked the attack!</strong> Both armor DR and shield DR <strong>${shieldData.damageReduction}</strong> will apply to damage.</p>`
) )
} else { } else {
shieldReaction = null shieldReaction = null
await createReactionMessage( await createReactionMessage(
defender, defender,
`<p><strong>${defenderName}</strong> rolls <strong>${shieldData.label}</strong> and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal} < ${attackRoll}). Shield did not block — normal hit, armor DR only.</p>` `<p><strong>${defenderName}</strong> rolls <strong>${shieldData.label}</strong> and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal} < ${attackRollFinal}). Shield did not block — normal hit, armor DR only.</p>`
) )
} }
} }
if (choice === "adHocShield") { if (choice === "adHocShield") {
const adHoc = await LethalFantasyUtils.promptAdHocShield(defenderName, attackRoll, defenseRoll) const adHoc = await LethalFantasyUtils.promptAdHocShield(defenderName, attackRollFinal, defenseRoll)
if (!adHoc) continue if (!adHoc) continue
const shieldBonus = await LethalFantasyUtils.rollBonusDie(adHoc.formula, defender) const shieldBonus = await LethalFantasyUtils.rollBonusDie(adHoc.formula, defender)
const newDefenseTotal = defenseRoll + shieldBonus const newDefenseTotal = defenseRoll + shieldBonus
@@ -689,7 +702,7 @@ Hooks.on("createChatMessage", async (message) => {
canShieldReact = false canShieldReact = false
canAdHocShield = false canAdHocShield = false
if (newDefenseTotal >= attackRoll) { if (newDefenseTotal >= attackRollFinal) {
shieldBlocked = true shieldBlocked = true
shieldReaction = { shieldReaction = {
damageReduction: adHoc.damageReduction, damageReduction: adHoc.damageReduction,
@@ -698,27 +711,23 @@ Hooks.on("createChatMessage", async (message) => {
} }
await createReactionMessage( await createReactionMessage(
defender, defender,
`<p><strong>${defenderName}</strong> rolls <strong>${adHoc.formula.toUpperCase()}</strong> shield and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal}${attackRoll}). <strong>Shield blocked the attack!</strong> Both armor DR and shield DR <strong>${adHoc.damageReduction}</strong> will apply to damage.</p>` `<p><strong>${defenderName}</strong> rolls <strong>${adHoc.formula.toUpperCase()}</strong> shield and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal}${attackRollFinal}). <strong>Shield blocked the attack!</strong> Both armor DR and shield DR <strong>${adHoc.damageReduction}</strong> will apply to damage.</p>`
) )
} else { } else {
shieldReaction = null shieldReaction = null
await createReactionMessage( await createReactionMessage(
defender, defender,
`<p><strong>${defenderName}</strong> rolls <strong>${adHoc.formula.toUpperCase()}</strong> shield and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal} < ${attackRoll}). Shield did not block — normal hit, armor DR only.</p>` `<p><strong>${defenderName}</strong> rolls <strong>${adHoc.formula.toUpperCase()}</strong> shield and adds <strong>${shieldBonus}</strong> to defense (${newDefenseTotal} < ${attackRollFinal}). Shield did not block — normal hit, armor DR only.</p>`
) )
} }
} }
} }
} }
let attackRollFinal = attackRoll if (mulliganRestart) continue
let attackerHandledBonus = false
// Si l'attaquant est un personnage qui perd et a du Grit // ── Attack reaction loop ───────────────────────────────────────────────
// Seulement si l'utilisateur actuel est le propriétaire de l'attaquant (pas le MJ)
if (!defenderHandledBonus && attacker && attackRollFinal <= defenseRoll && isPrimaryController(attacker)) { if (!defenderHandledBonus && attacker && attackRollFinal <= defenseRoll && isPrimaryController(attacker)) {
let canRerollAttack = LethalFantasyUtils.hasD30Reroll(attackD30message)
while (attackRollFinal <= defenseRoll) { while (attackRollFinal <= defenseRoll) {
const currentGrit = Number(attacker.system?.grit?.current) || 0 const currentGrit = Number(attacker.system?.grit?.current) || 0
const buttons = [] const buttons = []
@@ -742,7 +751,7 @@ Hooks.on("createChatMessage", async (message) => {
if (canRerollAttack && attackRerollContext) { if (canRerollAttack && attackRerollContext) {
buttons.push({ buttons.push({
action: "rerollAttack", action: "rerollAttack",
label: "Re-roll attack", label: "Re-roll attack (Mulligan)",
icon: "fa-solid fa-rotate-right", icon: "fa-solid fa-rotate-right",
callback: () => "rerollAttack" callback: () => "rerollAttack"
}) })
@@ -777,7 +786,7 @@ Hooks.on("createChatMessage", async (message) => {
attackerHandledBonus = true attackerHandledBonus = true
if (choice === "grit") { if (choice === "grit") {
const attackBonus = await LethalFantasyUtils.rollBonusDie("1d6", attacker, (total) => `<p><strong>${attackerName}</strong> spends 1 Grit and rolls <strong>${total}</strong> for attack.</p>`) const attackBonus = await LethalFantasyUtils.rollBonusDie("1d6", attacker, total => `<p><strong>${attackerName}</strong> spends 1 Grit and rolls <strong>${total}</strong> for attack.</p>`)
attackRollFinal += attackBonus attackRollFinal += attackBonus
await attacker.update({ "system.grit.current": currentGrit - 1 }) await attacker.update({ "system.grit.current": currentGrit - 1 })
continue continue
@@ -797,10 +806,14 @@ Hooks.on("createChatMessage", async (message) => {
canRerollAttack = false canRerollAttack = false
if (!reroll) continue if (!reroll) continue
attackRollFinal = reroll.options?.rollTotal || reroll.total || oldAttackRoll attackRollFinal = reroll.options?.rollTotal || reroll.total || oldAttackRoll
await createReactionMessage(attacker, `<p><strong>${attackerName}</strong> uses Mulligan and re-rolls attack: <strong>${oldAttackRoll}</strong> → <strong>${attackRollFinal}</strong>.</p>`) await createReactionMessage(attacker, `<p><strong>${attackerName}</strong> uses Mulligan and re-rolls attack: <strong>${oldAttackRoll}</strong> → <strong>${attackRollFinal}</strong>. Both sides may now react to the new numbers.</p>`)
// Restart the full comparison so both sides can react to the new roll
mulliganRestart = true
break
} }
} }
} }
} while (mulliganRestart)
const shieldDamageReduction = shieldBlocked ? shieldReaction.damageReduction : 0 const shieldDamageReduction = shieldBlocked ? shieldReaction.damageReduction : 0
const outcome = shieldBlocked ? "shielded-hit" : (attackRollFinal > defenseRoll ? "hit" : "miss") const outcome = shieldBlocked ? "shielded-hit" : (attackRollFinal > defenseRoll ? "hit" : "miss")
+49
View File
@@ -108,6 +108,10 @@
} }
], ],
"description": "Possible Flawless or Legendary Defense or Add D20E to Defense" "description": "Possible Flawless or Legendary Defense or Add D20E to Defense"
},
"saving_throws": {
"type": "save_auto_success",
"description": "Saving Throw Succeeds Regardless of Opposing Roll"
} }
}, },
"29": { "29": {
@@ -145,6 +149,11 @@
"type": "gain_grit", "type": "gain_grit",
"amount": 1, "amount": 1,
"description": "Gain 1 Grit" "description": "Gain 1 Grit"
},
"saving_throws": {
"type": "gain_grit",
"amount": 1,
"description": "Gain 1 Grit"
} }
}, },
"28": { "28": {
@@ -199,6 +208,12 @@
"amount": 1, "amount": 1,
"target": "skill", "target": "skill",
"description": "Add 1 to Skill Roll" "description": "Add 1 to Skill Roll"
},
"saving_throws": {
"type": "bonus_flat",
"amount": 1,
"target": "save",
"description": "Add 1 to Saving Throw"
} }
}, },
"21": { "21": {
@@ -239,6 +254,12 @@
"ranged_defense": { "ranged_defense": {
"type": "recover_pain", "type": "recover_pain",
"description": "Defender Recovers or ignores any flash of pain" "description": "Defender Recovers or ignores any flash of pain"
},
"saving_throws": {
"type": "bonus_dice",
"dice": "D6",
"target": "save",
"description": "Granted D6 (1-6) Saving Throw Modifier for this Saving Throw Attempt"
} }
}, },
"20": { "20": {
@@ -349,6 +370,12 @@
} }
], ],
"description": "Possible 20/20 defense that avoids Any Attack Except a Lethal Strike or adds D12 to defense" "description": "Possible 20/20 defense that avoids Any Attack Except a Lethal Strike or adds D12 to defense"
},
"saving_throws": {
"type": "bonus_flat",
"amount": 20,
"target": "save",
"description": "20 Added to Saving Throw"
} }
}, },
"15": { "15": {
@@ -391,6 +418,12 @@
"shield_bash" "shield_bash"
], ],
"description": "Kick, Punch or Shield Bash" "description": "Kick, Punch or Shield Bash"
},
"saving_throws": {
"type": "bonus_flat",
"amount": 1,
"target": "save",
"description": "Add 1 to Saving Throw"
} }
}, },
"13": {}, "13": {},
@@ -445,6 +478,12 @@
"shield_bash" "shield_bash"
], ],
"description": "Kick, Punch or Shield Bash" "description": "Kick, Punch or Shield Bash"
},
"saving_throws": {
"type": "bonus_flat",
"amount": 1,
"target": "save",
"description": "Add 1 to Saving Throw"
} }
}, },
"8": { "8": {
@@ -475,6 +514,10 @@
"ranged_defense": { "ranged_defense": {
"type": "mulligan", "type": "mulligan",
"description": "Mulligan, Can Choose to Re-Roll This Defense" "description": "Mulligan, Can Choose to Re-Roll This Defense"
},
"saving_throws": {
"type": "mulligan",
"description": "Mulligan, Can Re-Roll This Saving Throw"
} }
}, },
"7": { "7": {
@@ -528,6 +571,12 @@
"shield_bash" "shield_bash"
], ],
"description": "Kick, Punch, or Shield Bash" "description": "Kick, Punch, or Shield Bash"
},
"saving_throws": {
"type": "bonus_flat",
"amount": 1,
"target": "save",
"description": "Add 1 to Saving Throw"
} }
}, },
"3": { "3": {
+8 -3
View File
@@ -25,7 +25,8 @@ export default class D30Roll {
RANGED_DEFENSE: "ranged_defense", RANGED_DEFENSE: "ranged_defense",
ARCANE_SPELL_ATTACK: "arcane_spell_attack", ARCANE_SPELL_ATTACK: "arcane_spell_attack",
ARCANE_SPELL_DEFENSE: "arcane_spell_defense", ARCANE_SPELL_DEFENSE: "arcane_spell_defense",
SKILL_ROLLS: "skill_rolls" SKILL_ROLLS: "skill_rolls",
SAVING_THROWS: "saving_throws"
} }
/** /**
@@ -137,11 +138,15 @@ export default class D30Roll {
} }
// Skill types // Skill types
if (externalType === "skill" || externalType === "monster-skill" || if (externalType === "skill" || externalType === "monster-skill" || externalType === "challenge") {
externalType === "save" || externalType === "challenge") {
return this.ROLL_TYPES.SKILL_ROLLS return this.ROLL_TYPES.SKILL_ROLLS
} }
// Saving throw types
if (externalType === "save") {
return options.isSpellSave ? this.ROLL_TYPES.ARCANE_SPELL_DEFENSE : this.ROLL_TYPES.SAVING_THROWS
}
// If no match, return null // If no match, return null
console.warn(`D30Roll | Unknown external roll type: ${externalType}`) console.warn(`D30Roll | Unknown external roll type: ${externalType}`)
return null return null
+14 -4
View File
@@ -609,7 +609,7 @@ export default class LethalFantasyRoll extends Roll {
rollD30.total, rollD30.total,
options.rollType, options.rollType,
options.rollTarget?.weapon, options.rollTarget?.weapon,
{ isRanged: isRangedForD30 } { isRanged: isRangedForD30, isSpellSave: saveSpell }
) )
options.D30message = d30Message options.D30message = d30Message
} }
@@ -787,9 +787,18 @@ export default class LethalFantasyRoll extends Roll {
let buttons = [] let buttons = []
if (currentAction) { if (currentAction) {
if (currentAction.type === "weapon") { if (currentAction.type === "weapon") {
let weaponLabel = "Roll progression dice"
if (currentAction.rangedMode) {
// Compute loading count from the speed formula (e.g. "3+1d6" → load=3)
const speedStr = currentAction.system?.speed?.[currentAction.rangedMode] ?? ""
const rangedLoad = currentAction.rangedLoad ?? (Number(speedStr.split("+")[0]) || 0)
if (rangedLoad > 0 && !currentAction.weaponLoaded) {
weaponLabel = "Load weapon"
}
}
buttons.push({ buttons.push({
action: "roll", action: "roll",
label: "Roll progression dice", label: weaponLabel,
callback: (event, button) => { callback: (event, button) => {
let pos = $('#combat-action-dialog').position() let pos = $('#combat-action-dialog').position()
game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos) game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
@@ -908,14 +917,15 @@ export default class LethalFantasyRoll extends Roll {
if (rollContext === "rollLethargyDice") { if (rollContext === "rollLethargyDice") {
if (currentAction.spellStatus === "castingTime") { if (currentAction.spellStatus === "castingTime") {
let time = currentAction.type === "spell" ? currentAction.system.castingTime : currentAction.system.prayerTime let time = currentAction.type === "spell" ? currentAction.system.castingTime : currentAction.system.prayerTime
if (currentAction.castingTime <= time) { if (currentAction.castingTime < time) {
let message = `Casting time : ${currentAction.name}, count : ${currentAction.castingTime}/${time}` let message = `Casting time : ${currentAction.name}, count : ${currentAction.castingTime}/${time}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.castingTime += 1 currentAction.castingTime += 1
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction)) await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
return return
} else { } else {
let message = `Spell/Miracle ${currentAction.name} ready to be cast on next second !` // Last counting second — announce ready and transition immediately (no extra second consumed)
let message = `Casting time : ${currentAction.name}, count : ${time}/${time} — ready to cast next second !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.castingTime = 1 currentAction.castingTime = 1
currentAction.spellStatus = "toBeCasted" currentAction.spellStatus = "toBeCasted"
+1 -1
View File
@@ -65,7 +65,7 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
} }
schema.hp = new fields.SchemaField({ schema.hp = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }), value: new fields.NumberField({ ...requiredInteger, initial: 1 }),
max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }), max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
painDamage: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), painDamage: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
wounds: new fields.ArrayField(new fields.SchemaField(woundFieldSchema), { wounds: new fields.ArrayField(new fields.SchemaField(woundFieldSchema), {
+3 -3
View File
@@ -45,9 +45,9 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
}) })
schema.bonuses = new fields.SchemaField({ schema.bonuses = new fields.SchemaField({
attackBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }), attackBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0 }),
damageBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }), damageBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0 }),
defenseBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }) defenseBonus: new fields.NumberField({ ...requiredInteger, required: true, initial: 0 })
}) })
schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 }) schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 })