Nombreuses corrections sur les fiches settlement/NPC
This commit is contained in:
336
module/rolls.mjs
336
module/rolls.mjs
@@ -242,7 +242,7 @@ export async function rollWeaponAttack(actor, weapon, options = {}) {
|
||||
const actorSys = actor.system
|
||||
|
||||
const isRanged = sys.proficiencyGroup === "bows" || sys.proficiencyGroup === "throwing"
|
||||
const skillKey = isRanged ? "shooting" : "fighting"
|
||||
const skillKey = (sys.skillOverride && SYSTEM.SKILLS[sys.skillOverride]) ? sys.skillOverride : (isRanged ? "shooting" : "fighting")
|
||||
const skillDef = SYSTEM.SKILLS[skillKey]
|
||||
const defaultAttr = skillDef.attribute
|
||||
|
||||
@@ -339,8 +339,11 @@ export async function rollWeaponDamage(actor, weapon, options = {}) {
|
||||
const colorEmoji = hasDeadly ? "⬛" : hasBrutal ? "🔴" : "⬜"
|
||||
const colorLabel = hasDeadly ? "Black" : hasBrutal ? "Red" : "White"
|
||||
|
||||
const mightRank = actorSys.attributes.might.rank
|
||||
const baseDamageDice = sys.usesMight ? Math.max(mightRank + sys.damageMod, 1) : Math.max(sys.damageMod, 1)
|
||||
const isRangedDmg = sys.proficiencyGroup === "bows" || sys.proficiencyGroup === "throwing"
|
||||
const dmgSkillKey = (sys.skillOverride && SYSTEM.SKILLS[sys.skillOverride]) ? sys.skillOverride : (isRangedDmg ? "shooting" : "fighting")
|
||||
const dmgAttrKey = SYSTEM.SKILLS[dmgSkillKey].attribute
|
||||
const dmgAttrRank = actorSys.attributes[dmgAttrKey].rank
|
||||
const baseDamageDice = sys.usesMight ? Math.max(dmgAttrRank + sys.damageMod, 1) : Math.max(sys.damageMod, 1)
|
||||
const totalDice = Math.max(baseDamageDice + sv + damageBonus + autoDamageBonus, 1)
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
@@ -951,3 +954,330 @@ export async function rollInitiativeCheck(actor, options = {}) {
|
||||
|
||||
return { successes, dv: 0, isSuccess: null }
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// NPC SKILL ROLL
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Roll an NPC skill check (skillnpc item) and post to chat.
|
||||
*
|
||||
* @param {Actor} actor The NPC/creature actor
|
||||
* @param {Item} skillItem The skillnpc item
|
||||
* @param {object} options
|
||||
*/
|
||||
export async function rollNPCSkill(actor, skillItem, options = {}) {
|
||||
const { bonus = 0, colorOverride, visibility } = options
|
||||
const sys = skillItem.system
|
||||
|
||||
const colorType = colorOverride || sys.colorDiceType
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
const colorEmoji = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜"
|
||||
|
||||
const totalDice = Math.max(sys.dicePool + bonus, 1)
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, false)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const modLine = bonus !== 0
|
||||
? `<div class="oh-roll-mods">${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}</div>`
|
||||
: ""
|
||||
|
||||
const content = `
|
||||
<div class="oh-roll-card">
|
||||
<div class="oh-roll-header">
|
||||
<img src="${skillItem.img}" class="oh-card-weapon-img" alt="${skillItem.name}" />
|
||||
<span>${skillItem.name} — ${actor.name}</span>
|
||||
</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${colorEmoji} ${totalDice}d6 (${threshold}+)</span>
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result">
|
||||
<span class="oh-roll-successes">${successes} ${game.i18n.localize("OATHHAMMER.Roll.Successes")}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls: rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
|
||||
return { successes }
|
||||
}
|
||||
|
||||
/**
|
||||
* NPC weapon attack roll — uses NPC's flat attackBonus as dice pool.
|
||||
* Rolls white dice (4+) with optional bonus modifier.
|
||||
*/
|
||||
export async function rollNPCWeaponAttack(actor, weapon, options = {}) {
|
||||
const { bonus = 0, visibility } = options
|
||||
const sys = actor.system
|
||||
const basePool = sys.attackBonus ?? 0
|
||||
const totalDice = Math.max(basePool + bonus, 1)
|
||||
const threshold = 4
|
||||
const colorEmoji = "⬜"
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, false)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const modParts = []
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const content = `
|
||||
<div class="oh-roll-card oh-weapon-card">
|
||||
<div class="oh-roll-header">
|
||||
<img src="${weapon.img}" class="oh-card-weapon-img" alt="${weapon.name}" />
|
||||
<span>${weapon.name} — ${game.i18n.localize("OATHHAMMER.Dialog.Attack")} (${actor.name})</span>
|
||||
</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${colorEmoji} ${totalDice}d6 (${threshold}+)</span>
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result">
|
||||
<span class="oh-roll-successes">${successes} ${game.i18n.localize("OATHHAMMER.Roll.Successes")}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls: rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
return { successes }
|
||||
}
|
||||
|
||||
/**
|
||||
* NPC weapon damage roll — uses NPC damageBonus + weapon damageMod as dice pool.
|
||||
*/
|
||||
export async function rollNPCWeaponDamage(actor, weapon, options = {}) {
|
||||
const { bonus = 0, visibility } = options
|
||||
const sys = actor.system
|
||||
const basePool = (sys.damageBonus ?? 0) + (weapon.system.damageMod ?? 0)
|
||||
const totalDice = Math.max(basePool + bonus, 1)
|
||||
const threshold = 4
|
||||
const colorEmoji = "⬜"
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, false)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const modParts = []
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const content = `
|
||||
<div class="oh-roll-card oh-weapon-card">
|
||||
<div class="oh-roll-header">
|
||||
<img src="${weapon.img}" class="oh-card-weapon-img" alt="${weapon.name}" />
|
||||
<span>${weapon.name} — ${game.i18n.localize("OATHHAMMER.Dialog.Damage")} (${actor.name})</span>
|
||||
</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${colorEmoji} ${totalDice}d6 (${threshold}+)</span>
|
||||
<span>${game.i18n.localize("OATHHAMMER.Label.Damage")}: ${weapon.system.damageLabel}</span>
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result">
|
||||
<span class="oh-roll-successes">${successes} ${game.i18n.localize("OATHHAMMER.Roll.Successes")}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls: rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
return { successes }
|
||||
}
|
||||
|
||||
/**
|
||||
* NPC armor dice roll — rolls actor's armorDice.value dice with armorDice.colorDiceType color.
|
||||
*/
|
||||
export async function rollNPCArmor(actor, options = {}) {
|
||||
const { bonus = 0, colorOverride, visibility } = options
|
||||
const sys = actor.system
|
||||
|
||||
const basePool = sys.armorDice?.value ?? 0
|
||||
const colorType = colorOverride || sys.armorDice?.colorDiceType || "white"
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
const colorEmoji = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜"
|
||||
const totalDice = Math.max(basePool + bonus, 1)
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, false)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const modLine = bonus !== 0
|
||||
? `<div class="oh-roll-mods">${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}</div>`
|
||||
: ""
|
||||
|
||||
const label = game.i18n.localize("OATHHAMMER.Label.ArmorDice")
|
||||
const content = `
|
||||
<div class="oh-roll-card oh-armor-card">
|
||||
<div class="oh-roll-header">
|
||||
<img src="${actor.img}" class="oh-card-weapon-img" alt="${actor.name}" />
|
||||
<span>${label} — ${actor.name}</span>
|
||||
</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${colorEmoji} ${totalDice}d6 (${threshold}+)</span>
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result ${successes > 0 ? "roll-success" : ""}">
|
||||
<span class="oh-roll-successes">${successes} ${game.i18n.localize("OATHHAMMER.Roll.Successes")}</span>
|
||||
<span class="oh-roll-verdict">−${successes} ${game.i18n.localize("OATHHAMMER.Roll.Damage")}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls: rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
return { successes }
|
||||
}
|
||||
|
||||
/**
|
||||
* NPC spell cast — flat dice pool, no arcane stress, posts DV success/failure to chat.
|
||||
*/
|
||||
export async function rollNPCSpell(actor, spell, options = {}) {
|
||||
const { dicePool = 3, bonus = 0, colorOverride, visibility } = options
|
||||
const dv = spell.system.difficultyValue ?? 1
|
||||
const colorType = colorOverride || "white"
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
const colorEmoji = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜"
|
||||
const totalDice = Math.max(dicePool + bonus, 1)
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, false)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
const isSuccess = successes >= dv
|
||||
|
||||
const modLine = bonus !== 0
|
||||
? `<div class="oh-roll-mods">${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}</div>`
|
||||
: ""
|
||||
|
||||
const resultClass = isSuccess ? "roll-success" : "roll-failure"
|
||||
const resultLabel = isSuccess
|
||||
? game.i18n.localize("OATHHAMMER.Roll.Success")
|
||||
: game.i18n.localize("OATHHAMMER.Roll.Failure")
|
||||
|
||||
const content = `
|
||||
<div class="oh-roll-card oh-spell-card">
|
||||
<div class="oh-roll-header">
|
||||
<img src="${spell.img}" class="oh-card-weapon-img" alt="${spell.name}" />
|
||||
<span>${spell.name} (DV ${dv}) — ${actor.name}</span>
|
||||
</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${colorEmoji} ${totalDice}d6 (${threshold}+)</span>
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result ${resultClass}">
|
||||
<span class="oh-roll-successes">${successes} / ${dv}</span>
|
||||
<span class="oh-roll-verdict">${resultLabel}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
return { successes, dv, isSuccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* NPC miracle invocation — flat dice pool, no blocked tracking, posts DV success/failure to chat.
|
||||
*/
|
||||
export async function rollNPCMiracle(actor, miracle, options = {}) {
|
||||
const { dicePool = 3, bonus = 0, visibility } = options
|
||||
const dv = 1
|
||||
const threshold = 4
|
||||
const colorEmoji = "⬜"
|
||||
const totalDice = Math.max(dicePool + bonus, 1)
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, false)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
const isSuccess = successes >= dv
|
||||
|
||||
const modLine = bonus !== 0
|
||||
? `<div class="oh-roll-mods">${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}</div>`
|
||||
: ""
|
||||
|
||||
const resultClass = isSuccess ? "roll-success" : "roll-failure"
|
||||
const resultLabel = isSuccess
|
||||
? game.i18n.localize("OATHHAMMER.Roll.Success")
|
||||
: game.i18n.localize("OATHHAMMER.Roll.Failure")
|
||||
|
||||
const content = `
|
||||
<div class="oh-roll-card oh-miracle-card">
|
||||
<div class="oh-roll-header">
|
||||
<img src="${miracle.img}" class="oh-card-weapon-img" alt="${miracle.name}" />
|
||||
<span>${miracle.name} — ${actor.name}</span>
|
||||
</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${colorEmoji} ${totalDice}d6 (${threshold}+)</span>
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result ${resultClass}">
|
||||
<span class="oh-roll-successes">${successes} / ${dv}</span>
|
||||
<span class="oh-roll-verdict">${resultLabel}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
return { successes, dv, isSuccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* NPC attack damage roll — flat dice pool from the npcattack item, no Might.
|
||||
*/
|
||||
export async function rollNPCAttackDamage(actor, attack, options = {}) {
|
||||
const { bonus = 0, visibility } = options
|
||||
const sys = attack.system
|
||||
const colorType = sys.colorDiceType || "white"
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
const colorEmoji = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜"
|
||||
const totalDice = Math.max((sys.damageDice ?? 1) + bonus, 1)
|
||||
const ap = sys.ap ?? 0
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, false)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const modParts = []
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
if (ap > 0) modParts.push(`AP ${ap}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const content = `
|
||||
<div class="oh-roll-card oh-weapon-card">
|
||||
<div class="oh-roll-header">
|
||||
<img src="${attack.img}" class="oh-card-weapon-img" alt="${attack.name}" />
|
||||
<span>${attack.name} — ${game.i18n.localize("OATHHAMMER.Dialog.Damage")} (${actor.name})</span>
|
||||
</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${colorEmoji} ${totalDice}d6 (${threshold}+)</span>
|
||||
${ap > 0 ? `<span>AP ${ap}</span>` : ""}
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result">
|
||||
<span class="oh-roll-successes">${successes} ${game.i18n.localize("OATHHAMMER.Roll.Successes")}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
return { successes }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user