Files
fvtt-lethal-fantasy/module/utils/helpers.mjs
T
uberwald 25648aa2a3
Release Creation / build (release) Successful in 45s
refactor: remove D30 choice dialog, extract defense reaction buttons, fix bugs
- 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
2026-06-29 11:44:46 +02:00

276 lines
9.0 KiB
JavaScript

import { SYSTEM } from "../config/system.mjs"
export function log(...args) {
if (game?.settings?.get(game.system.id, "debug")) {
console.log(...args)
}
}
export async function loadCompendiumData(compendium) {
const pack = game.packs.get(compendium)
return await pack?.getDocuments() ?? []
}
export async function loadCompendium(compendium, filter = item => true) {
let compendiumData = await loadCompendiumData(compendium)
return compendiumData.filter(filter)
}
export function pushCombatOptions(html, options) {
options.push({ name: "Reset Progression", condition: true, icon: '<i class="fas fa-rotate-right"></i>', callback: target => { game.combat.resetProgression(target.data('combatant-id')); } })
}
export function setHookListeners() {
Hooks.on('renderTokenHUD', async (hud, html, data) => {
if (html.querySelector(".lethal-hp-loss-hud")) return
// The token/actor is on the HUD application instance, not the third param.
// hud.token / hud.object gives the Token (PlaceableObject), which has .actor.
const hudActor = hud.token?.actor ?? hud.object?.actor
if (!hudActor) return
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();
_toggleHudWraps("hp-loss")
})
$(html).find('.loss-hp-hud-click').click(async (event) => {
event.preventDefault();
await hudActor.applyDamage(Number(event.currentTarget.dataset.hpValue));
_disableHudWraps("hp-loss")
})
// 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();
_toggleHudWraps("hp-gain")
})
$(html).find('.gain-hp-hud-click').click(async (event) => {
event.preventDefault();
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
const luckGritButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/luck-grit-hud.hbs', {})
$(html).find('div.left').append(luckGritButton);
$(html).find('.lethal-luck-grit-hud').click((event) => {
event.preventDefault();
let wrap = $(html).find('.luck-grit-wrap')[0]
if (wrap.classList.contains("luck-grit-hud-disabled")) {
wrap.classList.add('luck-grit-hud-active');
wrap.classList.remove('luck-grit-hud-disabled');
} else {
wrap.classList.remove('luck-grit-hud-active');
wrap.classList.add('luck-grit-hud-disabled');
}
})
$(html).find('.luck-grit-btn').click(async (event) => {
event.preventDefault();
const resource = event.currentTarget.dataset.resource;
const amount = Number(event.currentTarget.dataset.amount);
const current = Number(foundry.utils.getProperty(hudActor.system, `${resource}.current`)) || 0;
const newValue = Math.max(0, current + amount);
await hudActor.update({ [`system.${resource}.current`]: newValue });
$(html).find('.luck-grit-wrap')[0].classList.remove('luck-grit-hud-active');
$(html).find('.luck-grit-wrap')[0].classList.add('luck-grit-hud-disabled');
})
})
}
export function registerHandlebarsHelpers() {
Handlebars.registerHelper('isNull', function (val) {
return val == null;
});
Handlebars.registerHelper('match', function (val, search) {
if (val && search) {
return val?.match(search);
}
return false
});
Handlebars.registerHelper('exists', function (val) {
return val != null && val !== undefined;
});
Handlebars.registerHelper('isEmpty', function (list) {
if (list) return list.length === 0;
else return false;
});
Handlebars.registerHelper('notEmpty', function (list) {
return list.length > 0;
});
Handlebars.registerHelper('isNegativeOrNull', function (val) {
return val <= 0;
});
Handlebars.registerHelper('isNegative', function (val) {
return val < 0;
});
Handlebars.registerHelper('isPositive', function (val) {
return val > 0;
});
Handlebars.registerHelper('equals', function (val1, val2) {
return val1 === val2;
});
Handlebars.registerHelper('neq', function (val1, val2) {
return val1 !== val2;
});
Handlebars.registerHelper('gt', function (val1, val2) {
return val1 > val2;
})
Handlebars.registerHelper('lt', function (val1, val2) {
return val1 < val2;
})
Handlebars.registerHelper('gte', function (val1, val2) {
return val1 >= val2;
})
Handlebars.registerHelper('lte', function (val1, val2) {
return val1 <= val2;
})
Handlebars.registerHelper('and', function (val1, val2) {
return val1 && val2;
})
Handlebars.registerHelper('or', function (val1, val2) {
return val1 || val2;
})
Handlebars.registerHelper('or3', function (val1, val2, val3) {
return val1 || val2 || val3;
})
Handlebars.registerHelper('for', function (from, to, incr, block) {
let accum = '';
for (let i = from; i < to; i += incr)
accum += block.fn(i);
return accum;
})
Handlebars.registerHelper('not', function (cond) {
return !cond;
})
Handlebars.registerHelper('count', function (list) {
return list.length;
})
Handlebars.registerHelper('countKeys', function (obj) {
return Object.keys(obj).length;
})
Handlebars.registerHelper('isEnabled', function (configKey) {
return game.settings.get("bol", configKey);
})
Handlebars.registerHelper('split', function (str, separator, keep) {
return str.split(separator)[keep];
})
// If you need to add Handlebars helpers, here are a few useful examples:
Handlebars.registerHelper('concat', function () {
let outStr = '';
for (let arg in arguments) {
if (typeof arguments[arg] != 'object') {
outStr += arguments[arg];
}
}
return outStr;
})
Handlebars.registerHelper('add', function (a, b) {
return parseInt(a) + parseInt(b);
});
Handlebars.registerHelper('mul', function (a, b) {
return parseInt(a) * parseInt(b);
})
Handlebars.registerHelper('sub', function (a, b) {
return parseInt(a) - parseInt(b);
})
Handlebars.registerHelper('abbrev2', function (a) {
return a.substring(0, 2);
})
Handlebars.registerHelper('abbrev3', function (a) {
return a.substring(0, 3);
})
Handlebars.registerHelper('valueAtIndex', function (arr, idx) {
return arr[idx];
})
Handlebars.registerHelper('includesKey', function (items, type, key) {
return items.filter(i => i.type === type).map(i => i.system.key).includes(key);
})
Handlebars.registerHelper('includes', function (array, val) {
return array.includes(val);
})
Handlebars.registerHelper('eval', function (expr) {
return eval(expr);
})
Handlebars.registerHelper('isOwnerOrGM', function (actor) {
log("Testing actor", actor.isOwner, game.userId)
return actor.isOwner || game.isGM;
})
Handlebars.registerHelper('upperCase', function (text) {
if (typeof text !== 'string') return text
return text.toUpperCase()
})
Handlebars.registerHelper('upperFirst', function (text) {
if (typeof text !== 'string') return text
return text.charAt(0).toUpperCase() + text.slice(1)
})
Handlebars.registerHelper('upperFirstOnly', function (text) {
if (typeof text !== 'string') return text
return text.charAt(0).toUpperCase()
})
// Handle v12 removal of this helper
Handlebars.registerHelper('select', function (selected, options) {
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
const rgx = new RegExp(' value=[\"\']' + escapedValue + '[\"\']');
const html = options.fn(this);
return html.replace(rgx, "$& selected");
});
}
export function getLethargyDice(level) {
for (let s of SYSTEM.SPELL_LETHARGY_DICE) {
if (Number(level) <= s.maxLevel) {
return s.dice
}
}
}