refactor: extract inline HTML to templates, split oversized files, fix bugs
- Extract all inline HTML from JS into 21 Handlebars templates (chat/, dialogs/, ui/) - Split utils.mjs (1507) into barrel + helpers.mjs, combat.mjs, d30.mjs - Split roll.mjs (1632) into barrel + roll-base.mjs, roll-prompt.mjs, roll-combat.mjs, roll-damage.mjs - Split lethal-fantasy.mjs (1426) into bootstrap + chat-reaction.mjs - Fix: missing async on injectDiceTray (free-roll.mjs:29 SyntaxError) - Fix: weapon._id fallback for deserialized chat-message weapon objects - Fix: missing await on rollModifier.evaluate() calls in roll-combat.mjs - Fix: choices→choicesList ReferenceError in utils.mjs - Fix: add 12 missing i18n keys (chooseWeapon, chooseSave, attackRoll, etc.) - Fix: restore sideLabel in bonus-die-select.hbs - Clean: remove dead messageContent param, console.log→log() - Style: barrel files preserve existing import paths
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<div class="dice-breakdown">
|
||||
{{#each diceResults}}
|
||||
<span class="dice-item">
|
||||
<span class="dice-type">{{this.dice}}</span>
|
||||
<span class="dice-separator">→</span>
|
||||
<span class="dice-value">{{this.value}}</span>
|
||||
</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if D30message}}
|
||||
<div class="d30-result">
|
||||
<span class="d30-value">D30 → {{D30result}}</span> — {{D30message.description}}
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -0,0 +1,30 @@
|
||||
<div class="attack-result {{#if isAttackWin}}attack-success{{else}}attack-failure{{/if}}">
|
||||
<h3><i class="fa-solid {{#if isAttackWin}}fa-sword{{else}}fa-shield{{/if}}"></i> Combat Result</h3>
|
||||
<div class="combat-comparison">
|
||||
<div class="combat-side attacker {{#if isAttackWin}}winner{{else}}loser{{/if}}">
|
||||
<div class="side-label">{{localize "LETHALFANTASY.Combat.attacker"}}</div>
|
||||
<div class="side-info">
|
||||
<div class="side-name">{{attackerName}}</div>
|
||||
<div class="side-roll">{{attackRoll}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="combat-vs">VS</div>
|
||||
<div class="combat-side defender {{#if isAttackWin}}loser{{else}}winner{{/if}}">
|
||||
<div class="side-label">{{localize "LETHALFANTASY.Combat.defender"}}</div>
|
||||
<div class="side-info">
|
||||
<div class="side-name">{{defenderName}}</div>
|
||||
<div class="side-roll">{{defenseRoll}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="combat-result-text">
|
||||
{{#if (equals outcome "shielded-hit")}}
|
||||
<i class="fa-solid fa-shield"></i> <strong>{{defenderName}}</strong> has blocked with shield — apply armor DR + shield DR <strong>{{shieldDamageReduction}}</strong>.
|
||||
{{else if isAttackWin}}
|
||||
<i class="fa-solid fa-circle-check"></i> <strong>{{attackerName}}</strong> hits <strong>{{defenderName}}</strong>!
|
||||
{{else}}
|
||||
<i class="fa-solid fa-shield-halved"></i> <strong>{{defenderName}}</strong> avoided the attack!
|
||||
{{/if}}
|
||||
</div>
|
||||
{{{damageButton}}}
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
{{#if (equals type "weapon")}}
|
||||
<div class="attack-result-damage single-btn">
|
||||
<button class="roll-damage-btn"
|
||||
data-attacker-id="{{attackerId}}"
|
||||
data-defender-id="{{defenderId}}"
|
||||
data-defender-token-id="{{defenderTokenId}}"
|
||||
data-extra-shield-dr="{{shieldDamageReduction}}"
|
||||
data-weapon-id="{{attackWeaponId}}"
|
||||
data-damage-type="medium"
|
||||
data-d30-bleed="{{d30Bleed}}"
|
||||
data-d30-damage-mult="{{d30DamageMultiplier}}"
|
||||
data-d30-dr-mult="{{d30DrMultiplier}}">
|
||||
<i class="fa-solid fa-dice-d20"></i> Damage
|
||||
</button>
|
||||
</div>
|
||||
{{else if (equals type "monster")}}
|
||||
<div class="attack-result-damage single-btn">
|
||||
<button class="roll-damage-btn"
|
||||
data-attacker-id="{{attackerId}}"
|
||||
data-defender-id="{{defenderId}}"
|
||||
data-defender-token-id="{{defenderTokenId}}"
|
||||
data-extra-shield-dr="{{shieldDamageReduction}}"
|
||||
data-attack-key="{{attackRollKey}}"
|
||||
data-damage-type="monster"
|
||||
data-d30-bleed="{{d30Bleed}}"
|
||||
data-d30-damage-mult="{{d30DamageMultiplier}}"
|
||||
data-d30-dr-mult="{{d30DrMultiplier}}">
|
||||
<i class="fa-solid fa-burst"></i> Damage
|
||||
</button>
|
||||
</div>
|
||||
{{else if (equals type "spell")}}
|
||||
<div class="attack-result-damage spell-damage">
|
||||
{{#each tiers}}
|
||||
<button class="roll-damage-btn"
|
||||
data-attacker-id="{{../attackerId}}"
|
||||
data-defender-id="{{../defenderId}}"
|
||||
data-defender-token-id="{{../defenderTokenId}}"
|
||||
data-damage-type="spell"
|
||||
data-damage-formula="{{this.formula}}"
|
||||
data-d30-bleed="{{../d30Bleed}}"
|
||||
data-d30-damage-mult="{{../d30DamageMultiplier}}"
|
||||
data-d30-dr-mult="{{../d30DrMultiplier}}">
|
||||
<i class="fa-solid fa-wand-magic-sparkles"></i> {{this.label}} ({{this.formula}})
|
||||
</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -0,0 +1 @@
|
||||
<section class="dice-rolls">{{{rollHTML}}}</section>
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="lf-free-roll-card">
|
||||
<div class="lf-frc-header">
|
||||
<i class="fa-solid fa-dice"></i>
|
||||
<span class="lf-frc-title-text">{{titleText}}</span>
|
||||
<span class="lf-frc-badge">{{badge}}</span>
|
||||
</div>
|
||||
<div class="lf-frc-dice">
|
||||
{{#each dieChips}}
|
||||
<div class="{{this.classes}}">
|
||||
<span class="lf-frc-die-type">{{this.label}}</span>
|
||||
<span class="lf-frc-die-sep">→</span>
|
||||
<span class="lf-frc-die-val">{{this.value}}{{#if this.exploded}}<i class="fa-solid fa-burst lf-dt-explode-icon"></i>{{/if}}</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="lf-frc-total-bar">
|
||||
<span class="lf-frc-total-label">{{totalLabel}}</span>
|
||||
<span class="lf-frc-total-value">{{total}}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,42 @@
|
||||
{{#if (equals type "aetherSpend")}}
|
||||
<p>🔮 <strong>{{actorName}}</strong> casts <em>{{spellName}}{{#if tierLabel}}{{tierLabel}}{{/if}}</em> — spends <strong>{{value}}</strong> Aether <span style="color:#888;">({{oldValue}} → {{newValue}})</span>.</p>
|
||||
{{else if (equals type "graceSpend")}}
|
||||
<p>✨ <strong>{{actorName}}</strong> invokes <em>{{spellName}}</em> — spends <strong>{{value}}</strong> Grace <span style="color:#888;">({{oldValue}} → {{newValue}})</span>.</p>
|
||||
{{else if (equals type "bleedingNotice")}}
|
||||
<p><strong>Bleeding:</strong> Wound of {{value}} HP for {{value}} seconds.</p>
|
||||
{{else if (equals type "d30BonusRoll")}}
|
||||
<p>D30 bonus: rolled <strong>{{formula}}</strong> = <strong>{{value}}</strong></p>
|
||||
{{else if (equals type "mulligan")}}
|
||||
<p><strong>{{actorName}}</strong> uses Mulligan and re-rolls {{side}}: <strong>{{oldRoll}}</strong> → <strong>{{newRoll}}</strong>.</p>
|
||||
{{> chat/dice-breakdown diceResults=diceResults D30result=D30result D30message=D30message}}
|
||||
<p>Both sides may now react to the new numbers.</p>
|
||||
{{else}}
|
||||
<p>
|
||||
<strong>{{actorName}}</strong>
|
||||
{{#if (equals type "grit")}}
|
||||
spends 1 {{resource}} and rolls <strong>{{value}}</strong> for {{side}}.
|
||||
{{else if (equals type "luck")}}
|
||||
spends 1 {{resource}} and rolls <strong>{{value}}</strong> for {{side}}.
|
||||
{{else if (equals type "bonusDie")}}
|
||||
adds <strong>{{formula}}</strong> and rolls <strong>{{value}}</strong> for {{side}}.
|
||||
{{else if (equals type "d30Bonus")}}
|
||||
gains <strong>+{{value}}</strong> from D30 bonus die for {{side}}.
|
||||
{{else if (equals type "d30Auto")}}
|
||||
uses <strong>{{specialName}}</strong> from D30 — {{#if (equals side "defense")}}defense automatically succeeds!{{else}}attack automatically hits!{{/if}}
|
||||
{{else if (equals type "d30Flag")}}
|
||||
D30 — <strong>{{specialName}}</strong> triggered for {{actorName}}!
|
||||
{{else if (equals type "d30Bleed")}}
|
||||
D30 — <strong>Bleeding/Internal Injury</strong> on hit! Damage past DR will cause a bleeding wound.
|
||||
{{else if (equals type "d30DamageMultiplier")}}
|
||||
D30 — <strong>x{{value}} damage</strong> before damage reduction!
|
||||
{{else if (equals type "d30DRMultiplier")}}
|
||||
D30 — Defense grants <strong>x{{value}} DR</strong> (choose which DR types to multiply when damage is applied).
|
||||
{{else if (equals type "shieldBlock")}}
|
||||
rolls <strong>{{shieldLabel}}</strong> and adds <strong>{{shieldBonus}}</strong> to defense ({{newTotal}} ≥ {{opposingRoll}}). <strong>Shield blocked the attack!</strong> Both armor DR and shield DR <strong>{{shieldDR}}</strong> will apply to damage.
|
||||
{{else if (equals type "shieldFail")}}
|
||||
rolls <strong>{{shieldLabel}}</strong> and adds <strong>{{shieldBonus}}</strong> to defense ({{newTotal}} < {{opposingRoll}}). Shield did not block — normal hit, armor DR only.
|
||||
{{else if (equals type "generic")}}
|
||||
{{{body}}}
|
||||
{{/if}}
|
||||
</p>
|
||||
{{/if}}
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<div class="combat-status">
|
||||
<p><strong>{{defenderName}}</strong> uses a shield (not equipped)</p>
|
||||
<p>{{localize "LETHALFANTASY.Combat.attackRoll"}}: <strong>{{attackRoll}}</strong> — {{localize "LETHALFANTASY.Combat.currentDefense"}}: <strong>{{defenseRoll}}</strong></p>
|
||||
</div>
|
||||
<div class="weapon-selection" style="margin-top:8px;">
|
||||
<label for="shield-dice">{{localize "LETHALFANTASY.Combat.shieldDice"}}:</label>
|
||||
<select id="shield-dice" name="shieldDice" style="width: 100%; margin-top: 4px;">
|
||||
{{#each choices}}
|
||||
<option value="{{this}}">{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="weapon-selection" style="margin-top:8px;">
|
||||
<label for="shield-dr">{{localize "LETHALFANTASY.Combat.shieldDR"}}:</label>
|
||||
<input id="shield-dr" name="shieldDR" type="number" min="0" value="0" style="width: 100%; margin-top: 4px;" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<div class="combat-status">
|
||||
<p><strong>{{attackerName}}</strong> currently has <strong>{{currentAttackRollWithBonus}}</strong></p>
|
||||
<p><strong>{{defenderName}}</strong> rolled <strong>{{defenseRoll}}</strong></p>
|
||||
{{#if totalBonus}}<p class="bonus-info">Bonus already added: +{{totalBonus}}</p>{{/if}}
|
||||
</div>
|
||||
<p class="offer-text">You are losing! Spend Grit to add 1D6 to your attack?</p>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<div class="combat-status">
|
||||
<p><strong>{{attackerName}}</strong> currently has <strong>{{attackRoll}}</strong></p>
|
||||
<p><strong>{{defenderName}}</strong> rolled <strong>{{defenseRoll}}</strong></p>
|
||||
{{#if d30message}}<p class="bonus-info">D30 special: {{d30message.description}}</p>{{/if}}
|
||||
</div>
|
||||
<p class="offer-text">{{offerText}}</p>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<div class="combat-status">
|
||||
<p><strong>{{actorName}}</strong> currently has <strong>{{currentRoll}}</strong></p>
|
||||
<p>{{sideLabel}} opposing roll: <strong>{{opposingRoll}}</strong></p>
|
||||
</div>
|
||||
<div class="weapon-selection">
|
||||
<label for="bonus-die">{{localize "LETHALFANTASY.Combat.chooseBonusDie"}}:</label>
|
||||
<select id="bonus-die" name="bonusDie" style="width: 100%; margin-top: 8px;">
|
||||
{{#each choices}}
|
||||
<option value="{{this}}">{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<p>{{message}}</p>
|
||||
@@ -0,0 +1,16 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<p><strong>D30 DR Multiplier ×{{multiplier}}</strong></p>
|
||||
<p>Choose which DR types to multiply:</p>
|
||||
<label style="display:block;margin:0.3rem 0">
|
||||
<input type="checkbox" id="d30-dr-base" {{#if canBase}}checked{{/if}} {{#unless baseEnabled}}disabled{{/unless}}>
|
||||
Base DR (Armor/Natural): {{baseDR}} → ×{{multiplier}} = {{baseDRMultiplied}}
|
||||
</label>
|
||||
<label style="display:block;margin:0.3rem 0">
|
||||
<input type="checkbox" id="d30-dr-shield" {{#if canShield}}checked{{/if}} {{#unless shieldEnabled}}disabled{{/unless}}>
|
||||
Shield DR: {{shieldDR}} → ×{{multiplier}} = {{shieldDRMultiplied}}
|
||||
</label>
|
||||
<label style="display:block;margin:0.3rem 0">
|
||||
<input type="checkbox" id="d30-dr-magic" {{#if canMagic}}checked{{/if}} {{#unless magicEnabled}}disabled{{/unless}}>
|
||||
Magic DR: {{magicDR}} → ×{{multiplier}} = {{magicDRMultiplied}}
|
||||
</label>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<p><strong>D30 result:</strong> {{description}}</p>
|
||||
<p>{{localize "LETHALFANTASY.D30.chooseEffect"}}</p>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<p><strong>{{itemName}}</strong> has multiple damage tiers.</p>
|
||||
<p>Choose which damage to use when the attack lands:</p>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="grit-luck-dialog">
|
||||
<div class="combat-status">
|
||||
<p><strong>{{attackerName}}</strong> {{attackStatus}} <strong>{{attackRoll}}</strong></p>
|
||||
<p><strong>{{defenderName}}</strong> {{defenseStatus}} <strong>{{defenseRoll}}</strong></p>
|
||||
{{#if d30message}}<p class="bonus-info">D30 special: {{d30message.description}}</p>{{/if}}
|
||||
</div>
|
||||
<p class="offer-text">{{offerText}}</p>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="defense-request-dialog">
|
||||
<div class="attack-info">
|
||||
<p><strong>{{attackerName}}</strong> attacks <strong>{{defenderName}}</strong> with <strong>{{weaponName}}</strong>!</p>
|
||||
<p>{{localize "LETHALFANTASY.Combat.attackRoll"}}: <strong>{{attackRoll}}</strong></p>
|
||||
</div>
|
||||
<div class="weapon-selection">
|
||||
<label for="defense-weapon">{{localize "LETHALFANTASY.Combat.chooseWeapon"}}:</label>
|
||||
<select id="defense-weapon" name="weaponId" style="width: 100%; margin-top: 8px;">
|
||||
{{#each weapons}}
|
||||
<option value="{{this.id}}">{{this.name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="defense-request-dialog">
|
||||
<div class="attack-info">
|
||||
<p><strong>{{attackerName}}</strong> attacks <strong>{{defenderName}}</strong> with <strong>{{weaponName}}</strong>!</p>
|
||||
<p>{{localize "LETHALFANTASY.Combat.attackRoll"}}: <strong>{{attackRoll}}</strong></p>
|
||||
</div>
|
||||
<div class="weapon-selection">
|
||||
<label for="defense-attack">{{localize "LETHALFANTASY.Combat.chooseWeapon"}}:</label>
|
||||
<select id="defense-attack" name="attackKey" style="width: 100%; margin-top: 8px;">
|
||||
{{#each attacks}}
|
||||
<option value="{{this.key}}">{{this.name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="defense-request-dialog">
|
||||
<div class="attack-info">
|
||||
<p><strong>{{attackerName}}</strong> targets <strong>{{defenderName}}</strong> with <strong>{{weaponName}}</strong>!</p>
|
||||
<p>{{localize "LETHALFANTASY.Combat.attackRoll"}}: <strong>{{attackRoll}}</strong></p>
|
||||
</div>
|
||||
<div class="weapon-selection">
|
||||
<label for="save-type">{{localize "LETHALFANTASY.Combat.chooseSave"}}:</label>
|
||||
<select id="save-type" name="saveKey" style="width: 100%; margin-top: 8px;">
|
||||
{{#each saves}}
|
||||
<option value="{{this.id}}">{{this.label}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<p>Select the power level for <strong>{{itemName}}</strong>:</p>
|
||||
@@ -0,0 +1,7 @@
|
||||
<div style="padding:0.5rem 0">
|
||||
<p style="margin-bottom:0.6rem">{{msg}}</p>
|
||||
<div style="display:flex;align-items:center;gap:0.5rem">
|
||||
<label style="font-weight:bold">{{label}}</label>
|
||||
<input type="number" name="manualDr" value="0" min="0" style="width:5rem"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="lf-dt-row">
|
||||
<span class="lf-dt-label"><i class="fa-solid fa-dice"></i></span>
|
||||
<select class="lf-dt-count" title="{{countTitle}}">
|
||||
{{#each countOptions}}
|
||||
<option value="{{this}}">{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<div class="lf-dt-dice">
|
||||
{{#each diceButtons}}
|
||||
<button type="button" class="lf-dt-die-btn" data-die="{{this.value}}" title="{{this.label}}">{{this.label}}</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
<label class="lf-dt-explode-label" title="{{explodeTitle}}">
|
||||
<input type="checkbox" class="lf-dt-explode" />
|
||||
<i class="fa-solid fa-explosion"></i>
|
||||
</label>
|
||||
</div>
|
||||
Reference in New Issue
Block a user