export default class LethalFantasyUtils { /* -------------------------------------------- */ static async loadCompendiumData(compendium) { const pack = game.packs.get(compendium) return await pack?.getDocuments() ?? [] } /* -------------------------------------------- */ static async loadCompendium(compendium, filter = item => true) { let compendiumData = await LethalFantasyUtils.loadCompendiumData(compendium) return compendiumData.filter(filter) } /* -------------------------------------------- */ static pushCombatOptions(html, options) { options.push({ name: "Reset Progression", condition: true, icon: '', callback: target => { game.combat.resetProgression(target.data('combatant-id')); } }) } /* -------------------------------------------- */ static setHookListeners() { Hooks.on('renderTokenHUD', async (hud, html, token) => { 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'); } }) $(html).find('.loss-hp-hud-click').click((event) => { event.preventDefault(); let hpLoss = event.currentTarget.dataset.hpValue; if (token) { let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id); console.log(tokenFull, token) let actor = tokenFull.actor; actor.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'); } }) }) } /* -------------------------------------------- */ static handleSocketEvent(msg = {}) { console.log(`handleSocketEvent !`, msg) let actor switch (msg.type) { case "rollInitiative": actor = game.actors.get(msg.actorId) actor.system.rollInitiative(msg.combatId, msg.combatantId) break case "rollProgressionDice": actor = game.actors.get(msg.actorId) actor.system.rollProgressionDice(msg.combatId, msg.combatantId, msg.rollProgressionCount) break } } static 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) { console.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"); }); } static getLethargyDice(level) { for (let s of SYSTEM.SPELL_LETHARGY_DICE) { if (Number(level) <= s.maxLevel) { return s.dice } } } /* -------------------------------------------- */ static async applyDamage(message, event) { // Récupérer les données du message let combatantId = event.currentTarget.dataset.combatantId if (!combatantId || !game.combat) { ui.notifications.error("No combatant selected") return } let combatant = game.combat.combatants.get(combatantId) if (!combatant) { ui.notifications.error("Combatant not found") return } let targetActor = combatant.token?.actor || game.actors.get(combatant.actorId) if (!targetActor) { ui.notifications.error("Target actor not found") return } // Récupérer les données de dégâts du message let damageTotal = message.rolls[0]?.total || 0 let weaponName = message.rolls[0]?.options?.rollName || "Unknown Weapon" // Calculer les DR let armorDR = targetActor.computeDamageReduction() || 0 let shieldDR = targetActor.getShieldDR() || 0 let totalDR = armorDR + shieldDR // Créer le dialogue const content = await foundry.applications.handlebars.renderTemplate( "systems/fvtt-lethal-fantasy/templates/apply-damage-dialog.hbs", { targetName: targetActor.name, weaponName: weaponName, damageTotal: damageTotal, armorDR: armorDR, shieldDR: shieldDR, totalDR: totalDR, damageNoDR: damageTotal, damageWithArmor: Math.max(0, damageTotal - armorDR), damageWithAll: Math.max(0, damageTotal - totalDR) } ) const result = await foundry.applications.api.DialogV2.wait({ window: { title: "Apply Damage" }, classes: ["lethalfantasy"], position: { width: 280 }, content, buttons: [ { action: "noDR", label: "No DR", callback: () => ({ drType: "none", damage: damageTotal }) }, { action: "armorDR", label: "With Armor DR", callback: () => ({ drType: "armor", damage: Math.max(0, damageTotal - armorDR) }) }, { action: "allDR", label: "With Armor + Shield DR", callback: () => ({ drType: "all", damage: Math.max(0, damageTotal - totalDR) }) }, { action: "cancel", label: "Cancel", callback: () => null } ], rejectClose: false }) if (result && result.damage !== undefined) { await targetActor.applyDamage(-result.damage) // Message de confirmation let drText = "" if (result.drType === "armor") { drText = ` (Armor DR: ${armorDR})` } else if (result.drType === "all") { drText = ` (Total DR: ${totalDR})` } ChatMessage.create({ user: game.user.id, speaker: { alias: targetActor.name }, rollMode: "gmroll", content: `${targetActor.name} takes ${result.damage} damage${drText} from ${weaponName}` }) } } }