import CthulhuEternalRoll from "./documents/roll.mjs" export default class CthulhuEternalUtils { static registerSettings() { game.settings.register("fvtt-cthulhu-eternal", "settings-era", { name: game.i18n.localize("CTHULHUETERNAL.Settings.era"), hint: game.i18n.localize("CTHULHUETERNAL.Settings.eraHint"), default: "jazz", scope: "world", type: String, choices: SYSTEM.AVAILABLE_SETTINGS, config: true, onChange: _ => window.location.reload() }); } static async loadCompendiumData(compendium) { const pack = game.packs.get(compendium) return await pack?.getDocuments() ?? [] } static async loadCompendium(compendium, filter = item => true) { let compendiumData = await CthulhuEternalUtils.loadCompendiumData(compendium) return compendiumData.filter(filter) } static registerHandlebarsHelpers() { Handlebars.registerHelper('isNull', function (val) { return val == null; }); 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('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() }) Handlebars.registerHelper('isCreature', function (key) { return key === "creature" || key === "daemon"; }) // 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 async nudgeRoll(rollMessage) { let dialogContext = rollMessage.rolls[0]?.options let actor = game.actors.get(dialogContext.actorId) dialogContext.wpValue = actor.system.wp.value dialogContext.rollResultIndex = rollMessage.rolls[0].total - 1 dialogContext.minValue = Math.max(rollMessage.rolls[0].total - (dialogContext.wpValue * 5), 1) dialogContext.maxValue = Math.min(rollMessage.rolls[0].total + (dialogContext.wpValue * 5), 100) dialogContext.wpCost = 0 // Build options table for the select operator between minValue and maxValue dialogContext.nudgeOptions = Array.from({ length: dialogContext.maxValue - dialogContext.minValue + 1 }, (_, i) => dialogContext.minValue + i) console.log(dialogContext) const content = await renderTemplate("systems/fvtt-cthulhu-eternal/templates/nudge-dialog.hbs", dialogContext) const title = game.i18n.localize("CTHULHUETERNAL.Roll.nudgeRoll") const rollContext = await foundry.applications.api.DialogV2.wait({ window: { title: title }, classes: ["fvtt-cthulhu-eternal"], content, buttons: [ { action: "apply", label: game.i18n.localize("CTHULHUETERNAL.Roll.applyNudge"), callback: (event, button, dialog) => { const output = Array.from(button.form.elements).reduce((obj, input) => { if (input.name) obj[input.name] = input.value return obj }, {}) return output }, }, { action: "cancel", label: game.i18n.localize("CTHULHUETERNAL.Roll.cancel"), callback: (event, button, dialog) => { } } ], actions: { }, rejectClose: false, // Click on Close button will not launch an error render: (event, dialog) => { $(".nudged-score-select").change(event => { dialogContext.nudgedValue = Number(event.target.value)+1 dialogContext.wpCost = Math.ceil(Math.abs(rollMessage.rolls[0].total - dialogContext.nudgedValue) / 5) $("#nudged-wp-cost").val(dialogContext.wpCost) }) } }) // If the user cancels the dialog, exit if (rollContext === null || dialogContext.wpCost === 0) { return } const roll = new CthulhuEternalRoll(String(dialogContext.nudgedValue)) await roll.evaluate() roll.options = dialogContext roll.options.isNudgedRoll = true roll.options.isNudge = false roll.displayRollResult(roll, dialogContext, dialogContext.rollData) roll.toMessage() actor.system.modifyWP(-dialogContext.wpCost) } static setupCSSRootVariables() { const era = game.settings.get("fvtt-cthulhu-eternal", "settings-era") let eraCSS = SYSTEM.ERA_CSS[era]; if (!eraCSS) eraCSS = SYSTEM.ERA_CSS["jazz"]; document.documentElement.style.setProperty('--font-size-standard', eraCSS.baseFontSize); document.documentElement.style.setProperty('--font-size-title', eraCSS.titleFontSize); document.documentElement.style.setProperty('--font-size-result', eraCSS.titleFontSize); document.documentElement.style.setProperty('--font-primary', eraCSS.primaryFont); document.documentElement.style.setProperty('--font-secondary', eraCSS.secondaryFont); document.documentElement.style.setProperty('--font-title', eraCSS.titleFont); document.documentElement.style.setProperty('--img-icon-color-filter', eraCSS.imgFilter); document.documentElement.style.setProperty('--background-image-base', `linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url("../assets/ui/${era}_background_main.webp")`); } }