/* global game, ui, Roll, foundry, mergeObject */ // Minimal helper for Wrath & Glory voidship play // Reads data/voidships.json and creates Vehicle actors representing starships. // Also provides shield tracking, critical hit table creation, and simple actions. Hooks.once("ready", () => { game.wgVoidships = { async importShips(folderName="Voidships") { const module = game.modules.get("wg-voidships-foundry"); const path = `${module?.path}/data/voidships.json`; const ships = await (await fetch(path)).json(); let folder = game.folders.find(f => f.type==="Actor" && f.name===folderName); if (!folder) folder = await Folder.create({name: folderName, type: "Actor"}); for (const s of ships) { const name = s.name; const data = { name, type: "vehicle", folder: folder.id, system: { mnvr: Math.max(0, s.handling), value: 20, traits: { list: [{name:"voidship"}] }, combat: { size: "colossal", speed: s.speedVU || 10, // in VU; informational wounds: { value: 0, bonus: 0, max: s.hull || 20 }, resilience: { bonus: 0, total: s.armour || 10 }, defence: { bonus: s.defence || 3 }, fly: 0 }, notes: `Mounts: ${s.mounts || ""}\nTraits: ${s.traits || ""}` }, flags: { "wg-voidships": { shields: { current: s.shields||0, max: s.shields||0 }, speedVU: s.speedVU||10, handling: s.handling||0, cr: s.cr||5, tur: s.tur||0, mounts: s.mounts||"", traits: s.traits||"", components: s.components||[] } }, prototypeToken: { name, width: 4, height: 4, displayName: CONST.TOKEN_DISPLAY_MODES.OWNER_HOVER } }; const existing = game.actors.find(a => a.name===name); const actor = existing ?? await Actor.create(data); if (existing) await actor.update(data); // Add a simple Item that tracks Void Shields if (!actor.items.find(i => i.name==="Void Shields")) { await actor.createEmbeddedDocuments("Item", [{ name: "Void Shields", type: "gear", system: { description: `

Tracks starship shield layers (SHD). Current: ${s.shields||0}/${s.shields||0}

`, value: 0, rarity: "unique", keywords: "VOIDSHIP" } }]); } } ui.notifications.info(`Imported ${ships.length} voidships into folder "${folderName}".`); }, async ensureCriticalHitsTable() { const name = "Voidship Critical Hits (d66)"; let table = game.tables.getName(name); if (table) return table; const module = game.modules.get("wg-voidships-foundry"); const rows = await (await fetch(`${module?.path}/data/critical_hits.json`)).json(); const results = rows.map(r => ({ type: CONST.TABLE_RESULT_TYPES.TEXT, text: `${r.d66}${r.name}
${r.effect}`, range: [1,1], weight: 1 })); table = await RollTable.create({name, results, displayRoll: true, formula: "1d36"}); ui.notifications.info("Created roll table: " + name); return table; }, // Utilities for shields and damage async applyHit(a, hits=1, dmg=0) { // Apply hits to shields first (per-hit, not per-damage) const shields = foundry.utils.getProperty(a, "flags.wg-voidships.shields") ?? {current:0,max:0}; let absorbed = 0, remaining = hits; while (remaining>0 && shields.current>0) { shields.current -= 1; absorbed += 1; remaining -= 1; } if (absorbed>0) ui.notifications.info(`${a.name}: ${absorbed} hit(s) absorbed by Void Shields.`); if (remaining>0 && dmg>0) { // Simple damage: reduce by Resilience then apply Wounds const res = foundry.utils.getProperty(a, "system.combat.resilience.total") ?? 10; const net = Math.max(0, dmg - res); if (net>0) { const cur = a.system.combat.wounds.value; await a.update({"system.combat.wounds.value": cur + net}); ChatMessage.create({content:`${a.name} suffers ${net} HULL damage (after AR ${res}).`}); } else { ChatMessage.create({content:`${a.name} shrugs the damage (AR ${res}).`}); } } await a.update({"flags.wg-voidships.shields.current": shields.current}); }, async rechargeShields(a, amount=null) { const max = foundry.utils.getProperty(a, "flags.wg-voidships.shields.max") ?? 0; const cur = foundry.utils.getProperty(a, "flags.wg-voidships.shields.current") ?? 0; const val = Math.min(max, amount==null ? max : cur + amount); await a.update({"flags.wg-voidships.shields.current": val}); ui.notifications.info(`${a.name} shields set to ${val}/${max}.`); }, async setShields(a, val) { const max = foundry.utils.getProperty(a, "flags.wg-voidships.shields.max") ?? 0; val = Math.max(0, Math.min(max, val)); await a.update({"flags.wg-voidships.shields.current": val}); ui.notifications.info(`${a.name} shields now ${val}/${max}.`); } }; }); // Macros (global helpers) game.wgVoidshipsMacros = { async importVoidships() { return game.wgVoidships.importShips(); }, async createCritTable() { return game.wgVoidships.ensureCriticalHitsTable(); }, async applyHit() { const a = canvas.tokens.controlled[0]?.actor ?? game.user.character; if (!a) return ui.notifications.warn("Select a voidship token or set a default character."); const hits = Number(await Dialog.prompt({title:"Hits", content:"How many hits to apply?", label:"Apply", callback: html => html.querySelector("input")?.value, render: html => html.append('') })) || 1; const dmg = Number(await Dialog.prompt({title:"Damage", content:"Base damage (after weapon calc)?", label:"Apply", callback: html => html.querySelector("input")?.value, render: html => html.append('') })) || 0; return game.wgVoidships.applyHit(a, hits, dmg); }, async rechargeShields() { const a = canvas.tokens.controlled[0]?.actor ?? game.user.character; if (!a) return ui.notifications.warn("Select a voidship token or set a default character."); return game.wgVoidships.rechargeShields(a); }, async setShields() { const a = canvas.tokens.controlled[0]?.actor ?? game.user.character; if (!a) return ui.notifications.warn("Select a voidship token or set a default character."); const cur = foundry.utils.getProperty(a, "flags.wg-voidships.shields.current") ?? 0; const max = foundry.utils.getProperty(a, "flags.wg-voidships.shields.max") ?? 0; const val = Number(await Dialog.prompt({title:"Set Shields", content:`0..${max}`, label:"Set", callback: html => html.querySelector("input")?.value, render: html => html.append(``) })) || cur; return game.wgVoidships.setShields(a, val); } };