refactor: remove D30 choice dialog, extract defense reaction buttons, fix bugs
Release Creation / build (release) Successful in 45s
Release Creation / build (release) Successful in 45s
- Remove D30 choice dialog — auto-roll bonus dice, flag special effects - Fix d30ChangedAttack infinite loop in defense do-while (missing reset) - Fix chat button dataset attributes (rollType/rollTarget/rollAvantage) - Extract buildDefenseReactionButtons from both defense loops - Merge Aether/Grace deduction via _deductResourceOnCast helper - Extract HP HUD toggling (_toggleHudWraps/_disableHudWraps) - Fix SYSTEM.EQUIPMENT_CATEGORIES typo in equipment model - Add missing imports to combat.mjs - Remove dead d30Auto branches, _buildSpecialLabel, d30-special-choice.hbs
This commit is contained in:
+44
-135
@@ -124,11 +124,6 @@ export async function handleAttackBoosted(msg) {
|
||||
await ChatMessage.create({content: msg, speaker: ChatMessage.getSpeaker({actor: defender})})
|
||||
}
|
||||
}
|
||||
if (d30Result.specialEffect === "auto") {
|
||||
updatedDefenseRoll = attackRollFinal + 1
|
||||
const msg = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/chat/reaction-message.hbs", {type:"d30Auto", actorName:defenderName, specialName:d30Result.specialName || "Special Defense", side:"defense"})
|
||||
await ChatMessage.create({content: msg, speaker: ChatMessage.getSpeaker({actor: defender})})
|
||||
}
|
||||
if (d30Result.specialEffect === "flag") {
|
||||
const msg = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/chat/reaction-message.hbs", {type:"d30Flag", actorName:defenderName, specialName:d30Result.specialName || "Special Effect"})
|
||||
await ChatMessage.create({content: msg, speaker: ChatMessage.getSpeaker({actor: defender})})
|
||||
@@ -143,63 +138,8 @@ export async function handleAttackBoosted(msg) {
|
||||
// Show the defense reaction dialog — while-loop for multiple reactions
|
||||
if (defender) {
|
||||
while (updatedDefenseRoll < attackRollFinal) {
|
||||
const currentGrit = Number(defender.system?.grit?.current) || 0
|
||||
const currentLuck = Number(defender.system?.luck?.current) || 0
|
||||
const buttons = []
|
||||
|
||||
if (currentGrit > 0) {
|
||||
buttons.push({
|
||||
action: "grit",
|
||||
type: "button",
|
||||
label: `Spend 1 Grit (+1D6) [${currentGrit} left]`,
|
||||
icon: "fa-solid fa-fist-raised",
|
||||
callback: () => "grit"
|
||||
})
|
||||
}
|
||||
|
||||
if (currentLuck > 0) {
|
||||
buttons.push({
|
||||
action: "luck",
|
||||
type: "button",
|
||||
label: `Spend 1 Luck (+1D6) [${currentLuck} left]`,
|
||||
icon: "fa-solid fa-clover",
|
||||
callback: () => "luck"
|
||||
})
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
action: "bonusDie",
|
||||
type: "button",
|
||||
label: "Add bonus die",
|
||||
icon: "fa-solid fa-dice",
|
||||
callback: () => "bonusDie"
|
||||
})
|
||||
|
||||
if (canShieldReact) {
|
||||
buttons.push({
|
||||
action: "shieldReact",
|
||||
type: "button",
|
||||
label: `Roll shield (${shieldLabel})`,
|
||||
icon: "fa-solid fa-shield",
|
||||
callback: () => "shieldReact"
|
||||
})
|
||||
} else if (canAdHoc) {
|
||||
buttons.push({
|
||||
action: "adHocShield",
|
||||
type: "button",
|
||||
label: "Roll ad-hoc shield (choose dice + DR)",
|
||||
icon: "fa-solid fa-shield-halved",
|
||||
callback: () => "adHocShield"
|
||||
})
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
action: "continue",
|
||||
type: "button",
|
||||
label: "Continue (no defense bonus)",
|
||||
icon: "fa-solid fa-forward",
|
||||
callback: () => "continue"
|
||||
})
|
||||
const shieldData = canShieldReact ? { label: shieldLabel, formula: shieldFormula, damageReduction: shieldDr } : null
|
||||
const buttons = buildDefenseReactionButtons(defender, { canRerollDefense: false, shieldData, canShieldReact, canAdHocShield: canAdHoc })
|
||||
|
||||
const choice = await foundry.applications.api.DialogV2.wait({
|
||||
window: { title: "Defense reactions — attack boosted" },
|
||||
@@ -223,13 +163,13 @@ export async function handleAttackBoosted(msg) {
|
||||
if (choice === "grit") {
|
||||
const bonusRoll = await rollBonusDie("1d6", defender)
|
||||
updatedDefenseRoll += bonusRoll
|
||||
await defender.update({ "system.grit.current": currentGrit - 1 })
|
||||
await defender.update({ "system.grit.current": Math.max(0, (Number(defender.system?.grit?.current) || 0) - 1) })
|
||||
const gritRmContent = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/chat/reaction-message.hbs", {type:"grit", actorName:defenderName, resource:"Grit", value:bonusRoll, side:"defense"})
|
||||
await ChatMessage.create({content: gritRmContent, speaker: ChatMessage.getSpeaker({actor: defender})})
|
||||
} else if (choice === "luck") {
|
||||
const bonusRoll = await rollBonusDie("1d6", defender)
|
||||
updatedDefenseRoll += bonusRoll
|
||||
await defender.update({ "system.luck.current": currentLuck - 1 })
|
||||
await defender.update({ "system.luck.current": Math.max(0, (Number(defender.system?.luck?.current) || 0) - 1) })
|
||||
const luckRmContent = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/chat/reaction-message.hbs", {type:"luck", actorName:defenderName, resource:"Luck", value:bonusRoll, side:"defense"})
|
||||
await ChatMessage.create({content: luckRmContent, speaker: ChatMessage.getSpeaker({actor: defender})})
|
||||
} else if (choice === "bonusDie") {
|
||||
@@ -364,6 +304,19 @@ export async function showDefenseRequest(msg) {
|
||||
|
||||
const isMonster = defender.type === "monster"
|
||||
|
||||
const _storeNextDefenseData = (opts = {}) => {
|
||||
game.lethalFantasy = game.lethalFantasy || {}
|
||||
game.lethalFantasy.nextDefenseData = {
|
||||
attackerId, attackRoll, attackerName, defenderName,
|
||||
attackWeaponId, attackRollType, attackRollKey,
|
||||
attackD30result, attackD30message, attackRerollContext,
|
||||
damageTier: msg.damageTier,
|
||||
defenderId: defender.id, defenderTokenId,
|
||||
...(msg.attackNaturalRoll !== undefined && { attackNaturalRoll: msg.attackNaturalRoll }),
|
||||
...(opts.isRanged !== undefined && { isRanged: opts.isRanged })
|
||||
}
|
||||
}
|
||||
|
||||
log(`[LF] showDefenseRequest | attackRollType=${attackRollType} isMonster=${isMonster} defender=${defender?.name}`)
|
||||
|
||||
// Spell/miracle attacks use saving throws instead of weapon defense
|
||||
@@ -398,22 +351,7 @@ export async function showDefenseRequest(msg) {
|
||||
if (result) {
|
||||
game.lethalFantasy = game.lethalFantasy || {}
|
||||
game.lethalFantasy.spellDefense = true // pré-cocher "Save against spell" dans le dialog
|
||||
game.lethalFantasy.nextDefenseData = {
|
||||
attackerId,
|
||||
attackRoll,
|
||||
attackerName,
|
||||
defenderName,
|
||||
attackWeaponId,
|
||||
attackRollType,
|
||||
attackRollKey,
|
||||
attackD30result,
|
||||
attackD30message,
|
||||
attackRerollContext,
|
||||
attackNaturalRoll: msg.attackNaturalRoll,
|
||||
damageTier: msg.damageTier,
|
||||
defenderId: defender.id,
|
||||
defenderTokenId
|
||||
}
|
||||
_storeNextDefenseData()
|
||||
if (isMonster) {
|
||||
await defender.system.prepareMonsterRoll("save", result)
|
||||
} else {
|
||||
@@ -462,25 +400,7 @@ export async function showDefenseRequest(msg) {
|
||||
|
||||
// Si l'utilisateur a validé, lancer le jet de défense
|
||||
if (result) {
|
||||
// Stocker temporairement les données pour le hook preCreateChatMessage
|
||||
game.lethalFantasy = game.lethalFantasy || {}
|
||||
game.lethalFantasy.nextDefenseData = {
|
||||
attackerId,
|
||||
attackRoll,
|
||||
attackerName,
|
||||
defenderName,
|
||||
attackWeaponId,
|
||||
attackRollType,
|
||||
attackRollKey,
|
||||
attackD30result,
|
||||
attackD30message,
|
||||
attackRerollContext,
|
||||
attackNaturalRoll: msg.attackNaturalRoll,
|
||||
damageTier: msg.damageTier,
|
||||
defenderId: defender.id,
|
||||
defenderTokenId,
|
||||
isRanged: msg.isRanged
|
||||
}
|
||||
_storeNextDefenseData({ isRanged: msg.isRanged })
|
||||
|
||||
await defender.system.prepareMonsterRoll("monster-defense", result)
|
||||
}
|
||||
@@ -497,23 +417,7 @@ export async function showDefenseRequest(msg) {
|
||||
actorImage: defender.img,
|
||||
})
|
||||
if (roll) {
|
||||
game.lethalFantasy = game.lethalFantasy || {}
|
||||
game.lethalFantasy.nextDefenseData = {
|
||||
attackerId,
|
||||
attackRoll,
|
||||
attackerName,
|
||||
defenderName,
|
||||
attackWeaponId,
|
||||
attackRollType,
|
||||
attackRollKey,
|
||||
attackD30result,
|
||||
attackD30message,
|
||||
attackRerollContext,
|
||||
damageTier: msg.damageTier,
|
||||
defenderId: defender.id,
|
||||
defenderTokenId,
|
||||
isRanged: true
|
||||
}
|
||||
_storeNextDefenseData({ isRanged: true })
|
||||
await roll.toMessage({}, { messageMode: roll.options.rollMode })
|
||||
}
|
||||
return
|
||||
@@ -558,25 +462,7 @@ export async function showDefenseRequest(msg) {
|
||||
|
||||
// Si l'utilisateur a validé, lancer le jet de défense
|
||||
if (result) {
|
||||
// Stocker temporairement les données pour le hook preCreateChatMessage
|
||||
game.lethalFantasy = game.lethalFantasy || {}
|
||||
game.lethalFantasy.nextDefenseData = {
|
||||
attackerId,
|
||||
attackRoll,
|
||||
attackerName,
|
||||
defenderName,
|
||||
attackWeaponId,
|
||||
attackRollType,
|
||||
attackRollKey,
|
||||
attackD30result,
|
||||
attackD30message,
|
||||
attackRerollContext,
|
||||
attackNaturalRoll: msg.attackNaturalRoll,
|
||||
damageTier: msg.damageTier,
|
||||
defenderId: defender.id,
|
||||
defenderTokenId,
|
||||
isRanged: msg.isRanged
|
||||
}
|
||||
_storeNextDefenseData({ isRanged: msg.isRanged })
|
||||
|
||||
log("Storing defense data for character:", defender.id)
|
||||
|
||||
@@ -584,6 +470,29 @@ export async function showDefenseRequest(msg) {
|
||||
}
|
||||
}
|
||||
|
||||
export function buildDefenseReactionButtons(defender, { canRerollDefense = false, shieldData = null, canShieldReact = false, canAdHocShield = false } = {}) {
|
||||
const currentGrit = Number(defender.system?.grit?.current) || 0
|
||||
const currentLuck = Number(defender.system?.luck?.current) || 0
|
||||
const buttons = []
|
||||
if (currentGrit > 0) {
|
||||
buttons.push({ action: "grit", type: "button", label: `Spend 1 Grit (+1D6) [${currentGrit} left]`, icon: "fa-solid fa-fist-raised", callback: () => "grit" })
|
||||
}
|
||||
if (currentLuck > 0) {
|
||||
buttons.push({ action: "luck", type: "button", label: `Spend 1 Luck (+1D6) [${currentLuck} left]`, icon: "fa-solid fa-clover", callback: () => "luck" })
|
||||
}
|
||||
buttons.push({ action: "bonusDie", type: "button", label: "Add bonus die", icon: "fa-solid fa-dice", callback: () => "bonusDie" })
|
||||
if (canRerollDefense) {
|
||||
buttons.push({ action: "rerollDefense", type: "button", label: "Re-roll defense (Mulligan)", icon: "fa-solid fa-rotate-right", callback: () => "rerollDefense" })
|
||||
}
|
||||
if (canShieldReact && shieldData) {
|
||||
buttons.push({ action: "shieldReact", type: "button", label: `Roll shield (${shieldData.label})`, icon: "fa-solid fa-shield", callback: () => "shieldReact" })
|
||||
} else if (canAdHocShield) {
|
||||
buttons.push({ action: "adHocShield", type: "button", label: "Roll ad-hoc shield (choose dice + DR)", icon: "fa-solid fa-shield-halved", callback: () => "adHocShield" })
|
||||
}
|
||||
buttons.push({ action: "continue", type: "button", label: "Continue (no defense bonus)", icon: "fa-solid fa-forward", callback: () => "continue" })
|
||||
return buttons
|
||||
}
|
||||
|
||||
export function getCombatBonusDiceChoices() {
|
||||
return ["1d4", "1d6", "1d8", "1d10", "1d12", "1d20", "1d20e"]
|
||||
}
|
||||
|
||||
+19
-74
@@ -28,63 +28,34 @@ export async function processD30BonusDice(d30Message, side, naturalRoll = null,
|
||||
return { modifier, specialEffect: null, specialName: null }
|
||||
}
|
||||
|
||||
// ── Choice type ── present all options to the player
|
||||
// ── Choice type ── auto-roll bonus dice, alert about special effects
|
||||
if (d30Message.type === "choice") {
|
||||
// If we can't show dialogs (wrong client), skip — the primary client
|
||||
// will communicate its choice result via socket. Auto-rolling here
|
||||
// would give a different modifier on each client, causing divergence.
|
||||
// Non-controlling client can't roll dice here — the controlling client
|
||||
// sends the updated values via socket.
|
||||
if (!canDialog) {
|
||||
return { modifier: 0, specialEffect: null, specialName: null }
|
||||
}
|
||||
|
||||
const buttons = d30Message.choices.map(c => {
|
||||
let label
|
||||
let icon
|
||||
if (c.type === "bonus_dice") {
|
||||
label = `Roll ${c.dice.toUpperCase()} and add to ${side}`
|
||||
icon = "fa-solid fa-dice"
|
||||
} else if (c.type === "special_strike") {
|
||||
label = _buildSpecialLabel(c, naturalRoll)
|
||||
icon = "fa-solid fa-star"
|
||||
} else if (c.type === "special_defense") {
|
||||
label = _buildSpecialLabel(c, naturalRoll)
|
||||
icon = "fa-solid fa-shield-halved"
|
||||
} else {
|
||||
label = c.type.replace(/_/g, " ").replace(/\b\w/g, l => l.toUpperCase())
|
||||
icon = "fa-solid fa-question"
|
||||
}
|
||||
return {
|
||||
action: c.type,
|
||||
type: "button",
|
||||
label,
|
||||
icon,
|
||||
callback: () => c
|
||||
}
|
||||
})
|
||||
|
||||
const choice = await foundry.applications.api.DialogV2.wait({
|
||||
window: { title: "D30 Special — Choose Effect" },
|
||||
classes: ["lethalfantasy"],
|
||||
content: await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/dialogs/d30-special-choice.hbs", {
|
||||
description: d30Message.description
|
||||
}),
|
||||
buttons,
|
||||
rejectClose: false
|
||||
})
|
||||
|
||||
if (!choice) return { modifier: 0, specialEffect: null, specialName: null }
|
||||
|
||||
if (choice.type === "bonus_dice") {
|
||||
const modifier = await _rollD30BonusDie(choice.dice, actor)
|
||||
return { modifier, specialEffect: null, specialName: null }
|
||||
// Auto-roll bonus dice (like d6E on 27 — no dialog)
|
||||
const bonusChoice = d30Message.choices.find(c => c.type === "bonus_dice")
|
||||
let modifier = 0
|
||||
if (bonusChoice) {
|
||||
modifier = await _rollD30BonusDie(bonusChoice.dice, actor)
|
||||
}
|
||||
|
||||
if (choice.type === "special_strike" || choice.type === "special_defense") {
|
||||
return { modifier: 0, specialEffect: "auto", specialName: _buildSpecialName(choice, naturalRoll) }
|
||||
// Inform about special strike/defense or other effects (informational only)
|
||||
const specialChoice = d30Message.choices.find(c => c.type === "special_strike" || c.type === "special_defense")
|
||||
if (specialChoice) {
|
||||
return { modifier, specialEffect: "flag", specialName: _buildSpecialName(specialChoice, naturalRoll) }
|
||||
}
|
||||
|
||||
// Non-standard choice (spell_calamity, etc.) — report it
|
||||
return { modifier: 0, specialEffect: "flag", specialName: choice.type }
|
||||
const nonStandardChoice = d30Message.choices.find(c => c.type !== "bonus_dice")
|
||||
if (nonStandardChoice) {
|
||||
return { modifier, specialEffect: "flag", specialName: _buildSpecialName(nonStandardChoice, naturalRoll) }
|
||||
}
|
||||
|
||||
return { modifier, specialEffect: null, specialName: null }
|
||||
}
|
||||
|
||||
// ── Combo type (bleed / internal injury) — flag for wound creation
|
||||
@@ -130,32 +101,6 @@ export async function _rollD30BonusDie(formula, actor, silent = false) {
|
||||
return roll.total
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a human-readable label for a special strike/defense choice in the D30 prompt.
|
||||
* @param {Object} specialChoice The choice object with type and options
|
||||
* @param {number|null} naturalRoll The natural D20 roll
|
||||
* @returns {string} Display label
|
||||
*/
|
||||
export function _buildSpecialLabel(specialChoice, naturalRoll) {
|
||||
if (specialChoice.type === "special_strike") {
|
||||
if (specialChoice.options.includes("lethal")) {
|
||||
if (naturalRoll === 20) return "Lethal Strike (auto-hit)"
|
||||
if (naturalRoll >= 16 && naturalRoll <= 19) return "Vital Strike (auto-hit)"
|
||||
return "Lethal/Vital Strike (auto-hit)"
|
||||
}
|
||||
if (specialChoice.options.includes("vicious")) return "Vicious Strike (auto-hit)"
|
||||
return "Special Strike (auto-hit)"
|
||||
}
|
||||
if (specialChoice.type === "special_defense") {
|
||||
if (specialChoice.options.includes("perfect_spell")) return "Perfect Spell Defense (auto-block)"
|
||||
if (specialChoice.options.includes("flawless")) return "Flawless Defense (auto-block)"
|
||||
if (specialChoice.options.includes("legendary")) return "Legendary Defense (auto-block)"
|
||||
if (specialChoice.options.includes("perfect")) return "Perfect Defense (auto-block)"
|
||||
return "Special Defense (auto-block)"
|
||||
}
|
||||
return "Special Effect"
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the special effect name based on the D30 result and natural roll.
|
||||
* @param {Object} specialChoice The choice object with type and options
|
||||
@@ -179,5 +124,5 @@ export function _buildSpecialName(specialChoice, naturalRoll) {
|
||||
if (specialChoice.options.includes("perfect")) return "Perfect Defense"
|
||||
return "Special Defense"
|
||||
}
|
||||
return "Special Effect"
|
||||
return specialChoice.type.replace(/_/g, " ").replace(/\b\w/g, l => l.toUpperCase())
|
||||
}
|
||||
|
||||
+33
-61
@@ -28,83 +28,55 @@ export function setHookListeners() {
|
||||
// hud.token / hud.object gives the Token (PlaceableObject), which has .actor.
|
||||
const hudActor = hud.token?.actor ?? hud.object?.actor
|
||||
if (!hudActor) return
|
||||
// HP Loss Button (existing)
|
||||
const _toggleHudWraps = (prefix) => {
|
||||
const enable = $(html).find(`.${prefix}-wrap`)[0].classList.contains(`${prefix}-hud-disabled`)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const w = $(html).find(`.${prefix}-wrap`)[i]
|
||||
w.classList.toggle(`${prefix}-hud-active`, enable)
|
||||
w.classList.toggle(`${prefix}-hud-disabled`, !enable)
|
||||
}
|
||||
}
|
||||
const _disableHudWraps = (prefix) => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const w = $(html).find(`.${prefix}-wrap`)[i]
|
||||
w.classList.remove(`${prefix}-hud-active`)
|
||||
w.classList.add(`${prefix}-hud-disabled`)
|
||||
}
|
||||
}
|
||||
|
||||
// HP Loss Button
|
||||
const lossHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/loss-hp-hud.hbs', {})
|
||||
$(html).find('div.left').append(lossHPButton);
|
||||
$(html).find('img.lethal-hp-loss-hud').click((event) => {
|
||||
event.preventDefault();
|
||||
let hpMenu = $(html).find('.hp-loss-wrap')[0]
|
||||
if (hpMenu.classList.contains("hp-loss-hud-disabled")) {
|
||||
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-disabled');
|
||||
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-disabled');
|
||||
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-disabled');
|
||||
} else {
|
||||
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
|
||||
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
|
||||
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
|
||||
}
|
||||
_toggleHudWraps("hp-loss")
|
||||
})
|
||||
$(html).find('.loss-hp-hud-click').click(async (event) => {
|
||||
event.preventDefault();
|
||||
let hpLoss = event.currentTarget.dataset.hpValue;
|
||||
await hudActor.applyDamage(Number(hpLoss));
|
||||
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
|
||||
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
|
||||
$(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
|
||||
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
|
||||
await hudActor.applyDamage(Number(event.currentTarget.dataset.hpValue));
|
||||
_disableHudWraps("hp-loss")
|
||||
})
|
||||
|
||||
// HP Gain Button (new)
|
||||
// HP Gain Button
|
||||
const gainHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/gain-hp-hud.hbs', {})
|
||||
$(html).find('div.left').append(gainHPButton);
|
||||
$(html).find('img.lethal-hp-gain-hud').click((event) => {
|
||||
event.preventDefault();
|
||||
let hpMenu = $(html).find('.hp-gain-wrap')[0]
|
||||
if (hpMenu.classList.contains("hp-gain-hud-disabled")) {
|
||||
$(html).find('.hp-gain-wrap')[0].classList.add('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[0].classList.remove('hp-gain-hud-disabled');
|
||||
$(html).find('.hp-gain-wrap')[1].classList.add('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[1].classList.remove('hp-gain-hud-disabled');
|
||||
$(html).find('.hp-gain-wrap')[2].classList.add('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[2].classList.remove('hp-gain-hud-disabled');
|
||||
} else {
|
||||
$(html).find('.hp-gain-wrap')[0].classList.remove('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[0].classList.add('hp-gain-hud-disabled');
|
||||
$(html).find('.hp-gain-wrap')[1].classList.remove('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[1].classList.add('hp-gain-hud-disabled');
|
||||
$(html).find('.hp-gain-wrap')[2].classList.remove('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[2].classList.add('hp-gain-hud-disabled');
|
||||
}
|
||||
_toggleHudWraps("hp-gain")
|
||||
})
|
||||
$(html).find('.gain-hp-hud-click').click(async (event) => {
|
||||
event.preventDefault();
|
||||
let hpGain = event.currentTarget.dataset.hpValue;
|
||||
await hudActor.applyDamage(Number(hpGain)); // Positive value to add HP
|
||||
// Clear bleeding wounds on heal — regardless of heal amount, any
|
||||
// healing is enough to stop bleeding (field dressing / magic / rest).
|
||||
const wounds = foundry.utils.duplicate(hudActor.system.hp.wounds || [])
|
||||
const hadBleeding = wounds.some(w => w.description === "Bleeding")
|
||||
if (hadBleeding) {
|
||||
await hudActor.update({
|
||||
"system.hp.wounds": wounds.map(w =>
|
||||
w.description === "Bleeding" ? { value: 0, duration: 0 } : w
|
||||
)
|
||||
})
|
||||
}
|
||||
$(html).find('.hp-gain-wrap')[0].classList.remove('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[0].classList.add('hp-gain-hud-disabled');
|
||||
$(html).find('.hp-gain-wrap')[1].classList.remove('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[1].classList.add('hp-gain-hud-disabled');
|
||||
$(html).find('.hp-gain-wrap')[2].classList.remove('hp-gain-hud-active');
|
||||
$(html).find('.hp-gain-wrap')[2].classList.add('hp-gain-hud-disabled');
|
||||
await hudActor.applyDamage(Number(event.currentTarget.dataset.hpValue));
|
||||
// Clear bleeding wounds on heal
|
||||
const wounds = foundry.utils.duplicate(hudActor.system.hp.wounds || [])
|
||||
if (wounds.some(w => w.description === "Bleeding")) {
|
||||
await hudActor.update({
|
||||
"system.hp.wounds": wounds.map(w =>
|
||||
w.description === "Bleeding" ? { value: 0, duration: 0 } : w
|
||||
)
|
||||
})
|
||||
}
|
||||
_disableHudWraps("hp-gain")
|
||||
})
|
||||
|
||||
// Luck/Grit Buttons
|
||||
|
||||
Reference in New Issue
Block a user