import { SYSTEM } from "./config/system.mjs" export default class FTLNomadUtils { static registerSettings() { game.settings.register("fvtt-ftl-nomad", "settings-era", { name: game.i18n.localize("FTLNOMAD.Settings.era"), hint: game.i18n.localize("FTLNOMAD.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 FTLNomadUtils.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 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")`); } static getTechAgeKeyFromLabel(label) { return Object.keys(SYSTEM.TECH_AGES).find(key => game.i18n.localize(SYSTEM.TECH_AGES[key].label) === label); } static async importPsionics() { // Create a psionics folder if it doesn't exist const psionicsFolder = game.folders.getName("Psionics") || await Folder.create({ name: "Psionics", type: "Item" }) if (!psionicsFolder) { console.error("Failed to create Psionics folder"); return; } // Load the psionics JSON file const psionicsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/psionics.json") .then(response => response.json()) .catch(error => { console.error("Failed to load psionics data:", error); return []; }); // Import each psionic ability for (const psionic of psionicsData) { // Check if the psionic already exists const existingPsionic = game.items.find(i => i.name === psionic.name && i.type === "psionic"); if (existingPsionic) { console.warn(`Psionic ${psionic.name} already exists, skipping import.`); continue; } // Create the psionic item await Item.create({ name: psionic.name, type: "psionic", img: "systems/fvtt-ftl-nomad/assets/icons/icon_psionic.svg", system: { description: psionic.description, }, folder: psionicsFolder.id }); } } static async importTalents() { // Create a talents folder if it doesn't exist const talentsFolder = game.folders.getName("Talents") || await Folder.create({ name: "Talents", type: "Item" }) if (!talentsFolder) { console.error("Failed to create Talents folder"); return; } // Load the talents JSON file const talentsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/talents.json") .then(response => response.json()) .catch(error => { console.error("Failed to load talents data:", error); return []; }); // Import each talent for (const talent of talentsData) { // Check if the talent already exists const existingTalent = game.items.find(i => i.name === talent.name && i.type === "talent"); if (existingTalent) { console.warn(`Talent ${talent.name} already exists, skipping import.`); continue; } talent.advantage = false; if (talent.description.includes(" +1D")) { talent.advantage = true } // Create the talent item await Item.create({ name: talent.name, type: "talent", img: "systems/fvtt-ftl-nomad/assets/icons/icon_talent.svg", system: { description: talent.description, isAdvantage: talent.advantage, }, folder: talentsFolder.id }); } } static async importImplants() { // Create a implants folder if it doesn't exist const implantsFolder = game.folders.getName("Implants") || await Folder.create({ name: "Implants", type: "Item" }) if (!implantsFolder) { console.error("Failed to create Implants folder"); return; } // Load the implants JSON file const implantsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/implants.json") .then(response => response.json()) .catch(error => { console.error("Failed to load implants data:", error); return []; }); // Import each implant for (const implant of implantsData) { // Check if the implant already exists const existingImplant = game.items.find(i => i.name === implant.name && i.type === "implant"); if (existingImplant) { console.warn(`Implant ${implant.name} already exists, skipping import.`); continue; } if (!Number(implant.cost)) { implant.cost = 1000000; } // Create the implant item await Item.create({ name: implant.name, type: "implant", img: "systems/fvtt-ftl-nomad/assets/icons/icon_implant.svg", system: { cost: implant.cost, loss: implant.loss, techAge: this.getTechAgeKeyFromLabel(implant.tech_age), description: implant.description, }, folder: implantsFolder.id }); } } static async importDrugs() { // Create a drugs folder if it doesn't exist const drugsFolder = game.folders.getName("Drugs") || await Folder.create({ name: "Drugs", type: "Item" }) if (!drugsFolder) { console.error("Failed to create Drugs folder"); return; } // Load the drugs JSON file const drugsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/drugs.json") .then(response => response.json()) .catch(error => { console.error("Failed to load drugs data:", error); return []; }); // Import each drug for (const drug of drugsData) { // Check if the drug already exists const existingDrug = game.items.find(i => i.name === drug.name && i.type === "drug"); if (existingDrug) { console.warn(`Drug ${drug.name} already exists, skipping import.`); continue; } // Create the drug item await Item.create({ name: drug.name, type: "equipment", img: "systems/fvtt-ftl-nomad/assets/icons/icon_equipment.svg", system: { description: drug.description, cost: drug.cost, techAge: this.getTechAgeKeyFromLabel(drug.tech_age), }, folder: drugsFolder.id }); } } static async importEquipments() { // Create a equipments folder if it doesn't exist const equipmentsFolder = game.folders.getName("Equipments") || await Folder.create({ name: "Equipments", type: "Item" }) if (!equipmentsFolder) { console.error("Failed to create Equipments folder"); return; } // Load the equipments JSON file const equipmentsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/exploration_equipment.json") .then(response => response.json()) .catch(error => { console.error("Failed to load equipments data:", error); return []; }); // Import each equipment for (const equipment of equipmentsData) { // Check if the equipment already exists const existingEquipment = game.items.find(i => i.name === equipment.name && i.type === "equipment"); if (existingEquipment) { console.warn(`Equipment ${equipment.name} already exists, skipping import.`); continue; } if (!Number(equipment.cost)) { equipment.cost = 1000000; } // Create the equipment item await Item.create({ name: equipment.name, type: "equipment", img: "systems/fvtt-ftl-nomad/assets/icons/icon_equipment.svg", system: { description: equipment.description, cost: equipment.cost, techAge: this.getTechAgeKeyFromLabel(equipment.tech_age), }, folder: equipmentsFolder.id }); } } static async importDistanceWeapons() { // Create a distance weapons folder if it doesn't exist const distanceWeaponsFolder = game.folders.getName("Vehicle Weapons") || await Folder.create({ name: "Vehicle Weapons", type: "Item" }) if (!distanceWeaponsFolder) { console.error("Failed to create Distance Weapons folder"); return; } // Load the distance weapons JSON file const distanceWeaponsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/vehicle_weapons.json") .then(response => response.json()) .catch(error => { console.error("Failed to load distance weapons data:", error); return []; }); // Import each distance weapon for (const weapon of distanceWeaponsData) { // Check if the weapon already exists const existingWeapon = game.items.find(i => i.name === weapon.name && i.type === "weapon"); if (existingWeapon) { console.warn(`Weapon ${weapon.name} already exists, skipping import.`); continue; } // Create the weapon item await Item.create({ name: weapon.name, type: "weapon", img: "systems/fvtt-ftl-nomad/assets/icons/icon_weapon.svg", system: { description: weapon.description, damage: weapon.damage, techAge: this.getTechAgeKeyFromLabel(weapon.tech_age), weaponType: SYSTEM.WEAPON_TYPES.vehicle.id, rangeType: weapon.range.toLowerCase(), enc: 0, aspect: weapon.aspects.join(", "), cost: weapon.cost || 0, ammoCost: weapon.ammo_cost || 0, magazine: weapon.mag || 1, }, folder: distanceWeaponsFolder.id }); } } static async importStarships() { // Create a starships folder if it doesn't exist const starshipsFolder = game.folders.getName("Starships") || await Folder.create({ name: "Starships", type: "Actor" }) if (!starshipsFolder) { console.error("Failed to create Starships folder"); return; } // Load the starships JSON file const starshipsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/starships.json") .then(response => response.json()) .catch(error => { console.error("Failed to load starships data:", error); return []; }); // Import each starship for (const starship of starshipsData) { // Check if the starship already exists const existingStarship = game.actors.find(a => a.name === starship.name && a.type === "starship"); if (existingStarship) { console.warn(`Starship ${starship.name} already exists, skipping import.`); continue; } // Create the starship actor await Actor.create({ name: starship.name, type: "starship", img: "systems/fvtt-ftl-nomad/assets/icons/icon_starship.svg", system: { description: starship.description, agility: starship.agility, hullType: starship.hullType.toLowerCase(), endurance: starship.endurance, armor: starship.armor, crew: starship.crew, cargo: starship.cargo, guns: starship.guns, travelMultiplier: starship.travelMultiplier, cost: starship.cost || 0, monthlyCost: starship.monthlyCost || 0, damages: starship.damages, }, folder: starshipsFolder.id }); } } static async importVehicles() { // Create a vehicles folder if it doesn't exist const vehiclesFolder = game.folders.getName("Vehicles") || await Folder.create({ name: "Vehicles", type: "Actor" }) if (!vehiclesFolder) { console.error("Failed to create Vehicles folder"); return; } // Load the vehicles JSON file const vehiclesData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/vehicles.json") .then(response => response.json()) .catch(error => { console.error("Failed to load vehicles data:", error); return []; }); // Import each vehicle for (const vehicle of vehiclesData) { // Check if the vehicle already exists const existingVehicle = game.items.find(i => i.name === vehicle.name && i.type === "vehicle"); if (existingVehicle) { console.warn(`Vehicle ${vehicle.name} already exists, skipping import.`); continue; } // Create the vehicle item await Actor.create({ name: vehicle.name, type: "vehicle", img: "systems/fvtt-ftl-nomad/assets/icons/icon_vehicle.svg", system: { description: vehicle.description, agility: vehicle.agility, armor: vehicle.armor, cargo: vehicle.cargo, crew: vehicle.crew, force: vehicle.force, range: vehicle.range, speed: vehicle.speed, techAge: this.getTechAgeKeyFromLabel(vehicle.tech_age), tonnage: vehicle.tonnage, damages: vehicle.damages, cost: vehicle.cost || 0, }, folder: vehiclesFolder.id }); } } static async importArmors() { // Create an armors folder if it doesn't exist const armorsFolder = game.folders.getName("Armors") || await Folder.create({ name: "Armors", type: "Item" }) if (!armorsFolder) { console.error("Failed to create Armors folder"); return; } // Load the armors JSON file const armorsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/armors.json") .then(response => response.json()) .catch(error => { console.error("Failed to load armors data:", error); return []; }); // Import each armor for (const armor of armorsData) { // Check if the armor already exists const existingArmor = game.items.find(i => i.name === armor.name && i.type === "armor"); if (existingArmor) { console.warn(`Armor ${armor.name} already exists, skipping import.`); continue; } // Create the armor item await Item.create({ name: armor.name, type: "armor", img: "systems/fvtt-ftl-nomad/assets/icons/icon_armor.svg", system: { description: armor.description, enc: armor.enc || 0, techAge: this.getTechAgeKeyFromLabel(armor.tech_age), cost: armor.cost || 0, protection: armor.protection || 0, }, folder: armorsFolder.id }); } } static async importGrenadeWeapons() { // Create a grenade weapons folder if it doesn't exist const grenadeWeaponsFolder = game.folders.getName("Grenade") || await Folder.create({ name: "Grenade", type: "Item" }) if (!grenadeWeaponsFolder) { console.error("Failed to create Grenade Weapons folder"); return; } // Load the grenade weapons JSON file const grenadeWeaponsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/grenades.json") .then(response => response.json()) .catch(error => { console.error("Failed to load grenade weapons data:", error); return []; }); // Import each grenade weapon for (const weapon of grenadeWeaponsData) { // Check if the weapon already exists const existingWeapon = game.items.find(i => i.name === weapon.name && i.type === "weapon"); if (existingWeapon) { console.warn(`Weapon ${weapon.name} already exists, skipping import.`); continue; } // Create the weapon item await Item.create({ name: weapon.name, type: "weapon", img: "systems/fvtt-ftl-nomad/assets/icons/icon_weapon.svg", system: { description: weapon.description, damage: weapon.damage, techAge: this.getTechAgeKeyFromLabel(weapon.tech_age), weaponType: SYSTEM.WEAPON_TYPES.grenade.id, rangeType: SYSTEM.WEAPON_RANGE.thrownweapon.id, enc: 0, aspect: weapon.aspects.join(", "), cost: weapon.cost || 0, ammoCost: 0, }, folder: grenadeWeaponsFolder.id }); } } static async importMeleeWeapons() { // Create a melee weapons folder if it doesn't exist const meleeWeaponsFolder = game.folders.getName("Melee Weapons") || await Folder.create({ name: "Melee Weapons", type: "Item" }) if (!meleeWeaponsFolder) { console.error("Failed to create Melee Weapons folder"); return; } // Load the melee weapons JSON file const meleeWeaponsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/weapon_melee.json") .then(response => response.json()) .catch(error => { console.error("Failed to load melee weapons data:", error); return []; }); console.log("Melee Weapons Data", meleeWeaponsData); // Import each melee weapon for (const weapon of meleeWeaponsData) { // Check if the weapon already exists const existingWeapon = game.items.find(i => i.name === weapon.name && i.type === "weapon"); if (existingWeapon) { console.warn(`Weapon ${weapon.name} already exists, skipping import.`); continue; } // Create the weapon item await Item.create({ name: weapon.name, type: "weapon", img: "systems/fvtt-ftl-nomad/assets/icons/icon_weapon.svg", system: { description: weapon.description, damage: weapon.damage, techAge: this.getTechAgeKeyFromLabel(weapon.tech_age), weaponType: SYSTEM.WEAPON_TYPES.melee.id, rangeType: SYSTEM.WEAPON_RANGE.melee.id, enc: 0, aspect: weapon.aspects.join(", "), cost: weapon.cost || 0, ammoCost: 0, }, folder: meleeWeaponsFolder.id }); } } }