From c2a93cbd86d202ad1e8eac7b287bcbfaa0ebc993 Mon Sep 17 00:00:00 2001 From: Michal Tuszowski Date: Fri, 19 Sep 2025 17:28:20 +0200 Subject: [PATCH] sync: wg-voidships-builder v1.4.11 --- wg-voidships-builder/module.json | 2 +- .../packs/wg-voidship-items.db | 21 +- .../scripts/voidship-actor.js | 259 ++++++++---------- 3 files changed, 115 insertions(+), 167 deletions(-) diff --git a/wg-voidships-builder/module.json b/wg-voidships-builder/module.json index 2eeab77..2f51569 100644 --- a/wg-voidships-builder/module.json +++ b/wg-voidships-builder/module.json @@ -2,7 +2,7 @@ "id": "wg-voidships-builder", "title": "Wrath & Glory: Voidships — Builder", "description": "Składanie okrętów jako Actorów przez dodawanie wyposażenia (gear) z kompendium.", - "version": "1.4.10", + "version": "1.4.11", "compatibility": { "minimum": "12", "verified": "13" diff --git a/wg-voidships-builder/packs/wg-voidship-items.db b/wg-voidships-builder/packs/wg-voidship-items.db index 0d09ffc..e1e4245 100644 --- a/wg-voidships-builder/packs/wg-voidship-items.db +++ b/wg-voidships-builder/packs/wg-voidship-items.db @@ -1,16 +1,5 @@ -{"_id":"BRIDGECLV0000001","name":"Command Bridge (Civilian)","type":"gear","img":"icons/svg/book.svg","flags":{"wg-voidships-builder":{"component":{"kind":"bridge","subtype":"civilian","tags":["bridge","internal"]},"voidOnly":true,"voidship":{"slot":"Internal","use":{"space":2,"power":2},"mods":{"armour":0,"integrity":0,"manoeuvre":1,"detection":1,"speed":0,"space":0,"power":0}}}},"system":{"quantity":1,"rarity":"Common","description":"Standard command bridge and cogitators.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{},"mods":{}}},"effects":[]} -{"_id":"REACTORPLSM00001","name":"Plasma Reactor (Civilian)","type":"gear","img":"icons/svg/energy.svg","flags":{"wg-voidships-builder":{"component":{"kind":"reactor","subtype":"plasma","tags":["reactor","internal"]},"voidOnly":true,"voidship":{"slot":"Internal","use":{"space":4,"power":-10},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":10}}}},"system":{"quantity":1,"rarity":"Common","description":"Primary plasma reactor for voidship operations.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{"power":0,"space":0},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0},"slot":"Internal"}},"effects":[]} -{"_id":"GELLARSTD0000001","name":"Gellar Field Projector","type":"gear","img":"icons/svg/temple.svg","flags":{"wg-voidships-builder":{"component":{"kind":"gellar","subtype":"standard","tags":["gellar","internal"]},"voidOnly":true,"voidship":{"slot":"Internal","use":{"space":2,"power":3},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":0}}}},"system":{"quantity":1,"rarity":"Common","description":"Essential protection in the Immaterium.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{},"mods":{}}},"effects":[]} -{"_id":"WARPDRVMILO00001","name":"Miloslav Warp Drive (Civilian)","type":"gear","img":"icons/svg/wing.svg","flags":{"wg-voidships-builder":{"component":{"kind":"drive","subtype":"warp","tags":["drive","warp","internal"]},"voidOnly":true,"voidship":{"slot":"Internal","use":{"space":3,"power":6},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":0}}}},"system":{"quantity":1,"rarity":"Common","description":"Reliable civilian warp drive.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{},"mods":{}}},"effects":[]} -{"_id":"AUGURARRAY000001","name":"Augur Array (Civilian)","type":"gear","img":"icons/svg/mystery-man.svg","flags":{"wg-voidships-builder":{"component":{"kind":"sensor","subtype":"augur","tags":["sensor","auspex","aetherics"]},"voidOnly":true,"voidship":{"slot":"Aetherics","use":{"space":1,"power":2},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":2,"speed":0,"space":0,"power":0}}}},"system":{"quantity":1,"rarity":"Common","description":"Standard augur/sensor package.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{},"mods":{}}},"effects":[]} -{"_id":"HULLPLATE0000001","name":"Reinforced Hull Plating","type":"gear","img":"icons/svg/armor.svg","flags":{"wg-voidships-builder":{"component":{"kind":"armour","subtype":"plating","tags":["hull","armour","internal"]},"voidOnly":true,"voidship":{"slot":"Internal","use":{"space":1,"power":0},"mods":{"armour":2,"integrity":4,"manoeuvre":-1,"detection":0,"speed":0,"space":0,"power":0}}}},"system":{"quantity":1,"rarity":"Common","description":"Additional armour plates and bracing.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{},"mods":{}}},"effects":[]} -{"_id":"CARGOHOLD0000001","name":"Cargo Hold & Lighter Bay","type":"gear","img":"icons/svg/crate.svg","flags":{"wg-voidships-builder":{"component":{"kind":"cargo-bay","subtype":"standard","tags":["cargo-bay","internal"]},"voidOnly":true,"voidship":{"slot":"Internal","use":{"space":-5,"power":1},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":-5,"power":0}}}},"system":{"quantity":1,"rarity":"Common","description":"General-purpose cargo hold with lighter handling.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{},"mods":{}}},"effects":[]} -{"_id":"AUXPLASMA0000001","name":"Auxiliary Plasma Generator","type":"gear","img":"icons/svg/energy.svg","flags":{"wg-voidships-builder":{"component":{"kind":"plasma","subtype":"aux-generator","tags":["aux-plasma","internal"]},"voidOnly":true,"voidship":{"slot":"Internal","use":{"space":2,"power":-6},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":6}}}},"system":{"quantity":1,"rarity":"Common","description":"Auxiliary plasma reactor increasing available power.","value":0,"keywords":[],"effect":"","test":"","voidship":{"use":{"power":0},"mods":{}}},"effects":[]} -{"_id":"ITEMREACTCRU0001","name":"Plasma Reactor (Cruiser) — Mars-Pattern","type":"gear","img":"icons/svg/energy.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"reactor","subtype":"cruiser","tags":["reactor","plasma"]}}},"system":{"description":"Standardowy reaktor plazmowy klasy krążownik (Mars-Pattern). Generuje moc dla wszystkich systemów okrętu.","voidship":{"slot":"Internal","use":{"space":20,"power":0},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":60}}},"effects":[]} -{"_id":"ITEMREACTLCR0001","name":"Plasma Reactor (Light Cruiser) — Mars-Pattern","type":"gear","img":"icons/svg/energy.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"reactor","subtype":"light-cruiser","tags":["reactor","plasma"]}}},"system":{"description":"Reaktor plazmowy dla lekkich krążowników (Mars-Pattern).","voidship":{"slot":"Internal","use":{"space":18,"power":0},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":55}}},"effects":[]} -{"_id":"ITEMREACTFRI0001","name":"Plasma Reactor (Frigate) — Jovian-Pattern","type":"gear","img":"icons/svg/energy.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"reactor","subtype":"frigate","tags":["reactor","plasma"]}}},"system":{"description":"Reaktor plazmowy okrętów klasy fregata (Jovian-Pattern).","voidship":{"slot":"Internal","use":{"space":15,"power":0},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":45}}},"effects":[]} -{"_id":"ITEMREACTESC0001","name":"Plasma Reactor (Escort) — Jovian-Pattern","type":"gear","img":"icons/svg/energy.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"reactor","subtype":"escort","tags":["reactor","plasma"]}}},"system":{"description":"Lżejszy reaktor plazmowy dla eskort.","voidship":{"slot":"Internal","use":{"space":12,"power":0},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":40}}},"effects":[]} -{"_id":"ITEMMACROUNI0001","name":"Macrobattery (Universal) — Mars-Pattern","type":"gear","img":"icons/svg/dice-target.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"macro","tags":["weapon","macro"]}}},"system":{"description":"Uniwersalna makrobateria. Może być montowana na dowolnym stanowisku broni.","voidship":{"allowedSlots":["prow","dorsal","port","starboard"],"use":{"space":8,"power":4},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":0}}},"effects":[]} -{"_id":"ITEMWPMACBRD0001","name":"Macrobattery (Broadside) — Mars-Pattern","type":"gear","img":"icons/svg/dice-target.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"macro-broadside","tags":["weapon","macro"]}}},"system":{"description":"Makrobateria burtowa. Montaż wyłącznie na burtach.","voidship":{"allowedSlots":["port","starboard"],"use":{"space":9,"power":4},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":0}}},"effects":[]} -{"_id":"ITEMTORPPROW0001","name":"Torpedo Tubes — Jovian-Pattern","type":"gear","img":"icons/svg/anchor.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"torpedo","tags":["weapon","torpedo"]}}},"system":{"description":"Wyrzutnie torpedowe montowane w dziobie.","voidship":{"allowedSlots":["prow"],"use":{"space":10,"power":5},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":0}}},"effects":[]} -{"_id":"ITEMLANCEDSL0001","name":"Lance Battery — Starflare-Pattern","type":"gear","img":"icons/svg/sun.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"lance","tags":["weapon","lance"]}}},"system":{"description":"Bateria lanc. Preferowane stanowiska: dorsal/port/starboard.","voidship":{"allowedSlots":["dorsal","port","starboard"],"use":{"space":10,"power":6},"mods":{"armour":0,"integrity":0,"manoeuvre":0,"detection":0,"speed":0,"space":0,"power":0}}},"effects":[]} +{"_id":"ITEMREACESCJOV01","name":"Plasma Reactor (Escort) — Jovian-Pattern","type":"gear","img":"icons/svg/energy.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"reactor","subtype":"escort","tags":["reactor","plasma"]},"voidship":{"slot":"Internal","use":{"space":6,"power":0},"mods":{"power":28}}}},"system":{"description":"Lżejszy reaktor plazmowy dla eskort. Generuje ~28 Power, zajmuje 6 Space."},"effects":[]} +{"_id":"ITEMTORPPROW0001","name":"Torpedo Tubes — Jovian-Pattern","type":"gear","img":"icons/svg/anchor.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"torpedo","tags":["weapon","torpedo"],"allowedSlots":["prow"]},"voidship":{"allowedSlots":["prow"],"use":{"space":10,"power":5},"mods":{}}}},"system":{"description":"Wyrzutnie torpedowe montowane w dziobie. Zużycie 10 Space, 5 Power."},"effects":[]} +{"_id":"ITEMLANCEDSL0001","name":"Lance Battery — Starflare-Pattern","type":"gear","img":"icons/svg/sun.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"lance","tags":["weapon","lance"],"allowedSlots":["dorsal","port","starboard"]},"voidship":{"allowedSlots":["dorsal","port","starboard"],"use":{"space":10,"power":6},"mods":{}}}},"system":{"description":"Bateria lanc. Montaż: Dorsal/Port/Starboard. Zużycie 10 Space, 6 Power."},"effects":[]} +{"_id":"ITEMWPMACBRD0001","name":"Macrobattery (Broadside) — Mars-Pattern","type":"gear","img":"icons/svg/dice-target.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"macro-broadside","tags":["weapon","macro"],"allowedSlots":["port","starboard"]},"voidship":{"allowedSlots":["port","starboard"],"use":{"space":9,"power":4},"mods":{}}}},"system":{"description":"Makrobateria burtowa. Montaż wyłącznie Port/Starboard. Zużycie 9 Space, 4 Power."},"effects":[]} +{"_id":"ITEMMACROUNI0001","name":"Macrobattery (Universal) — Mars-Pattern","type":"gear","img":"icons/svg/dice-target.svg","flags":{"wg-voidships-builder":{"voidOnly":true,"component":{"kind":"weapon","subtype":"macro","tags":["weapon","macro"],"allowedSlots":["prow","dorsal","port","starboard"]},"voidship":{"allowedSlots":["prow","dorsal","port","starboard"],"use":{"space":8,"power":4},"mods":{}}}},"system":{"description":"Uniwersalna makrobateria (prow/dorsal/port/starboard). Zużycie 8 Space, 4 Power."},"effects":[]} diff --git a/wg-voidships-builder/scripts/voidship-actor.js b/wg-voidships-builder/scripts/voidship-actor.js index e0c2c03..6d26258 100644 --- a/wg-voidships-builder/scripts/voidship-actor.js +++ b/wg-voidships-builder/scripts/voidship-actor.js @@ -11,31 +11,26 @@ function normalizeSlotName(s) { const v = String(s || "").trim().toLowerCase(); return VB_WEAPON_SLOTS.includes(v) ? v : null; } - function normalizeAllowedSlots(allowed) { if (!allowed) return []; if (Array.isArray(allowed)) return allowed.map(normalizeSlotName).filter(Boolean); - // string "prow|dorsal|port" albo "prow,dorsal" - return String(allowed) - .split(/[|,; ]+/g) - .map(normalizeSlotName) - .filter(Boolean); + return String(allowed).split(/[|,; ]+/g).map(normalizeSlotName).filter(Boolean); +} +function getVSH(it) { + // canonical: flags; fallback: legacy system.voidship (skopiujemy do flags przy dropie) + return it.getFlag(VB_MOD, "voidship") || it.system?.voidship || null; } - function countUsedWeaponSlots(actor) { const used = { prow: 0, dorsal: 0, port: 0, starboard: 0, keel: 0 }; for (const it of actor.items ?? []) { - const vs = it.system?.voidship; - if (!vs) continue; - const slot = normalizeSlotName(vs.slot); + const v = getVSH(it); const eq = (it.system?.equipped ?? true); + const slot = normalizeSlotName(v?.slot); if (eq && slot && (slot in used)) used[slot] += 1; } return used; } - function pickFirstFreeSlot(allowed, hullSlots, used) { - // Kolejność zgodnie z wymaganiem: "pierwszy wolny pasujący" const order = ["prow", "dorsal", "port", "starboard", "keel"]; for (const s of order) { if (!allowed.includes(s)) continue; @@ -130,8 +125,6 @@ const defaultHull = { class: actor.name, size: "Cruiser", slots: { prow: 0, dors const defaultBase = { armour: 0, integrity: 0, manoeuvre: 0, detection: 0, speed: 0, space: 0, power: 0 }; const defaultCrew = { command: 0, pilot: 0, sensors: 0, engineering: 0, gunnery: 0, security: 0, flightDeck: 0, passengers: 0 }; -const hull = actor.getFlag(VB_MOD, "hull") ?? flagsNs.hull ?? defaultHull; -const base = actor.getFlag(VB_MOD, "base") ?? flagsNs.base ?? defaultBase; const crew = actor.getFlag(VB_MOD, "crew") ?? flagsNs.crew ?? defaultCrew; const rtRaw = actor.getFlag(VB_MOD, "rtRaw") ?? flagsNs.rtRaw ?? {}; @@ -157,115 +150,91 @@ const rtRaw = actor.getFlag(VB_MOD, "rtRaw") ?? flagsNs.rtRaw ?? {}; source: rt.source ?? rtRaw.source ?? "" }; - // ===== KOMPONENTY: czytamy Z FLAG ===== - const items = actor.items.contents; - const inferSlotFromTags = (tags=[]) => { - const low = (Array.isArray(tags)?tags:[]).map(t => String(t).toLowerCase()); - const pref = ["prow","dorsal","port","starboard","keel","aetherics","internal"]; - const hit = pref.find(p => low.includes(p)); - return hit ? (hit[0].toUpperCase()+hit.slice(1)) : "Internal"; - }; + // --- komponenty z itemów (flags canonical) --- +const items = actor.items.contents || []; +const components = []; +const slotsUsed = { prow: 0, dorsal: 0, port: 0, starboard: 0, keel: 0 }; +const mods = { armour: 0, integrity: 0, manoeuvre: 0, detection: 0, speed: 0, space: 0, power: 0 }; +const useTotal = { space: 0, power: 0 }; - const components = []; - for (const i of items) { - const fmod = i.flags?.[VB_MOD] || {}; - const vflag = fmod.voidship || null; // <-- TU trzymamy liczby - const meta = fmod.component || {}; // kind/subtype/tags - // Dopuszczamy też stare itemy z system.voidship (jeśli coś przetrwa) - const vsys = i.system?.voidship || null; +for (const it of items) { + const v = getVSH(it); + if (!v) continue; + const c = foundry.utils.duplicate(v); + c.name = it.name; + c.itemId = it.id; + c.equipped = (it.system?.equipped ?? true); + c.slot = normalizeSlotName(c.slot) || "Internal"; + c.allowedSlots = normalizeAllowedSlots(c.allowedSlots || foundry.utils.getProperty(it.flags, `${VB_MOD}.component.allowedSlots`)); - // komponent, jeśli ma vflag LUB meta.tags LUB vsys - const isComponent = !!(vflag || vsys || (Array.isArray(meta.tags) && meta.tags.length)); - if (!isComponent) continue; + // defaults + c.use = { space: 0, power: 0, ...(c.use || {}) }; + c.mods = { armour: 0, integrity: 0, manoeuvre: 0, detection: 0, speed: 0, space: 0, power: 0, ...(c.mods || {}) }; - const vf = vflag || vsys || {}; - const use = { - space: Number(vf?.use?.space ?? 0), - power: Number(vf?.use?.power ?? 0) - }; - const mods = { - armour: Number(vf?.mods?.armour ?? 0), - integrity: Number(vf?.mods?.integrity ?? 0), - manoeuvre: Number(vf?.mods?.manoeuvre ?? 0), - detection: Number(vf?.mods?.detection ?? 0), - speed: Number(vf?.mods?.speed ?? 0), - space: Number(vf?.mods?.space ?? 0), - power: Number(vf?.mods?.power ?? 0) - }; - let slot = vf.slot || meta.slot || inferSlotFromTags(meta.tags); - const equipped = (i.system?.equipped ?? true) && (vf.disabled !== true); + components.push(c); - components.push({ - itemId: i.id, - name: i.name, - slot, - equipped, - use, mods, - tags: meta.tags || [] - }); + if (c.equipped) { + // sumuj mody + for (const k of Object.keys(mods)) { + const val = Number(c.mods[k] || 0); + if (Number.isFinite(val)) mods[k] += val; + } + // sumuj zużycie/produkcję + const us = Number(c.use.space || 0); + const up = Number(c.use.power || 0); + if (Number.isFinite(us)) useTotal.space += us; + if (Number.isFinite(up)) useTotal.power += up; + + // slot usage (tylko jeśli to broń z legalnym slotem) + if (VB_WEAPON_SLOTS.includes(c.slot)) { + slotsUsed[c.slot] = (slotsUsed[c.slot] || 0) + 1; + } } +} - // Zliczanie slotów (tylko equipped) - const slotsUsed = components.reduce((acc, c) => { - if (!c.equipped) return acc; - const key = String(c.slot || "Internal").toLowerCase(); - if (["prow","dorsal","port","starboard","keel"].includes(key)) acc[key] = (acc[key] || 0) + 1; - return acc; - }, { prow: 0, dorsal: 0, port: 0, starboard: 0, keel: 0 }); +const base = actor.getFlag(VB_MOD, "base") || { armour:0, integrity:0, manoeuvre:0, detection:0, speed:0, space:0, power:0 }; - // Capacity: bazowe + mody z komponentów (equipped) vs użycie (equipped) - const modsSum = components.reduce((acc, c) => { - if (!c.equipped) return acc; - for (const k of Object.keys(acc)) acc[k] += Number(c.mods[k] || 0); - return acc; - }, { armour:0, integrity:0, manoeuvre:0, detection:0, speed:0, space:0, power:0 }); +// total = base + mods +const totals = { + armour: (Number(base.armour)||0) + mods.armour, + integrity: (Number(base.integrity)||0) + mods.integrity, + manoeuvre: (Number(base.manoeuvre)||0) + mods.manoeuvre, + detection: (Number(base.detection)||0) + mods.detection, + speed: (Number(base.speed)||0) + mods.speed, + space: (Number(base.space)||0) + mods.space, + power: (Number(base.power)||0) + mods.power +}; +totals.spaceFree = totals.space - useTotal.space; +totals.powerFree = totals.power - useTotal.power; - const useSum = components.reduce((acc, c) => { - if (!c.equipped) return acc; - acc.space += Number(c.use.space || 0); - acc.power += Number(c.use.power || 0); - return acc; - }, { space: 0, power: 0 }); +// warnings +const hull = actor.getFlag(VB_MOD, "hull") || { slots: {} }; +const warnings = {}; +for (const s of VB_WEAPON_SLOTS) { + if ((slotsUsed[s]||0) > (hull.slots?.[s]||0)) { warnings.slots = "One or more weapon slot groups exceed hull limits."; break; } +} +if (totals.spaceFree < 0) warnings.capacity = "Not enough Space for installed components."; +if (totals.powerFree < 0) warnings.capacity = "Not enough Power for installed components."; - const totals = { - armour: (Number(base.armour)||0) + modsSum.armour, - integrity: (Number(base.integrity)||0) + modsSum.integrity, - manoeuvre: (Number(base.manoeuvre)||0) + modsSum.manoeuvre, - detection: (Number(base.detection)||0) + modsSum.detection, - speed: (Number(base.speed)||0) + modsSum.speed, - space: (Number(base.space)||0) + modsSum.space, - power: (Number(base.power)||0) + modsSum.power - }; - totals.spaceFree = totals.space - useSum.space; - totals.powerFree = totals.power - useSum.power; - - // Ostrzeżenia - const warnings = {}; - const overSlots = - (slotsUsed.prow > (hull.slots?.prow || 0)) || - (slotsUsed.dorsal > (hull.slots?.dorsal || 0)) || - (slotsUsed.port > (hull.slots?.port || 0)) || - (slotsUsed.starboard > (hull.slots?.starboard || 0)) || - (slotsUsed.keel > (hull.slots?.keel || 0)); - if (overSlots) warnings.slots = "One or more weapon slot groups exceed hull limits."; - - if (totals.spaceFree < 0 || totals.powerFree < 0) - warnings.capacity = "Not enough Space/Power for installed components."; - - // wstrzyknij do template +// zaszyj data.hull = hull; data.base = base; -data.crew = crew; -data.rtCard = rtCard; data.components = components; data.slotsUsed = slotsUsed; data.totals = totals; -data.mods = modsSum; // ← DODANE: żeby HBS miał liczby modów -data.warnings = warnings; +data.mods = mods; +data.useTotal = useTotal; +// extra debug +data.debug = { + usedWeaponSlots: slotsUsed, + itemsCount: items.length, + componentsCount: components.length, + itemsNoVSH: items.filter(i => !getVSH(i)).map(i => ({id:i.id, name:i.name})) +}; + +return data; - dbg("sheet.getData", { base, hull, totals, slotsUsed, componentsCount: components.length }); - return data; } // ---- listeners & UI ---- @@ -343,85 +312,75 @@ data.warnings = warnings; }); } -// --- robust drop handlers: flag-only storage (survives W&G) --- -async _onDrop(event) { - event.preventDefault(); - let raw = {}; - try { raw = JSON.parse(event.dataTransfer?.getData("text/plain") || "{}"); } - catch { ui.notifications.error("Drop failed: cannot parse drop payload."); return; } - if (raw?.type === "Item") return this._onDropItem(event, raw); - dbg("drop.ignored", raw); -} -/** Drop itemów na arkusz: tylko komponenty voidship. - * - Jeżeli item ma allowedSlots (broń): przydziel "pierwszy wolny pasujący" i zapisz do system.voidship.slot - * - Jeżeli nie ma allowedSlots: zachowaj istniejący system.voidship.slot (albo slot "Internal" dla nie-broni) - */ async _onDropItem(event, data) { event.preventDefault(); - // 1) Rozwiąż źródło dropu (UUID/pack/data) możliwie bezpiecznie + // 1) Rozwiąż źródło (uuid/pack/data) let doc = null; try { if (data?.uuid) doc = await fromUuid(data.uuid); else if (data?.pack && data?._id) { const pack = game.packs.get(data.pack); if (pack) doc = await pack.getDocument(data._id); - } else if (data?.data || data?.type) { - // Foundry potrafi zrzucać raw "data" + } else if (data) { doc = await Item.implementation.fromDropData(data); } - } catch (e) { /* fall-through */ } + } catch (e) { /* ignore */ } - // 2) Wyprowadź "raw" do stworzenia osadzonego itemu const raw = doc?.toObject?.(false) ?? data?.data ?? data; - if (!raw?.system) { - ui.notifications.error("Drop failed: cannot resolve item data (no UUID/DATA)."); - return false; - } + if (!raw?.type) { ui.notifications.error("Drop failed: cannot resolve item data (no UUID/DATA)."); return false; } - // 3) Sprawdź, czy to komponent voidship (wymagamy system.voidship) - const isComponent = !!raw.system?.voidship || !!raw.flags?.["wg-voidships-builder"]?.component; - if (!isComponent) { + // 2) Czy to komponent? + const flagsIn = foundry.utils.getProperty(raw, `flags.${VB_MOD}`) || {}; + const legacySys = foundry.utils.getProperty(raw, "system.voidship") || null; + const hasCompTag = !!flagsIn.component; + const hasLegacy = !!legacySys; + if (!hasCompTag && !hasLegacy) { ui.notifications.warn("This item is not a voidship component."); return false; } - // 4) Upewnij się, że istnieje sekcja voidship - raw.system.voidship = raw.system.voidship || {}; - const vsh = raw.system.voidship; + // 3) Skonstruuj flags.${VB_MOD}.voidship (kanoniczne) + const vshIn = flagsIn.voidship || legacySys || {}; + const flagsOut = foundry.utils.duplicate(flagsIn); + flagsOut.voidship = foundry.utils.mergeObject({ + slot: vshIn.slot || null, + use: { space: 0, power: 0, ...(vshIn.use || {}) }, + mods: { armour: 0, integrity: 0, manoeuvre: 0, detection: 0, speed: 0, space: 0, power: 0, ...(vshIn.mods || {}) }, + allowedSlots: normalizeAllowedSlots(vshIn.allowedSlots || flagsIn?.component?.allowedSlots || []) + }, {}, { inplace: false }); - // 5) Wyciągnij allowedSlots (preferuj z system.voidship.allowedSlots; ewentualnie z flags.component.allowedSlots) - const fromFlags = foundry.utils.getProperty(raw, "flags.wg-voidships-builder.component.allowedSlots"); - const allowed = normalizeAllowedSlots(vsh.allowedSlots ?? fromFlags); - - // 6) Jeżeli to broń (ma ograniczenia slotów), wybierz pierwszy wolny pasujący i wpisz do vsh.slot. + // 4) Auto-przydział slotu dla broni (jeśli allowedSlots != pusty) + const allowed = flagsOut.voidship.allowedSlots || []; if (allowed.length > 0) { - const hull = this.actor.getFlag("wg-voidships-builder", "hull") || {}; + const hull = this.actor.getFlag(VB_MOD, "hull") || {}; const hullSlots = hull.slots || {}; const used = countUsedWeaponSlots(this.actor); - const chosen = pickFirstFreeSlot(allowed, hullSlots, used); if (!chosen) { ui.notifications.warn("No free matching weapon slot for this component."); return false; } - vsh.slot = chosen; // przypisz ostateczny slot montażu + flagsOut.voidship.slot = chosen; } else { - // Nie-broń: zostaw istniejący slot albo nadaj "Internal" - vsh.slot = vsh.slot || "Internal"; + // nie-broń + flagsOut.voidship.slot = flagsOut.voidship.slot || "Internal"; } - // 7) Domyślnie traktuj jako "equipped" (zamontowane), jeśli schema na to pozwala - if (raw.system.equipped === undefined) raw.system.equipped = true; + // 5) wpisz flags i domyślne "equipped" + raw.flags = raw.flags || {}; + raw.flags[VB_MOD] = flagsOut; + if (raw.system?.equipped === undefined) { + raw.system = raw.system || {}; + raw.system.equipped = true; + } - // 8) Utwórz item na aktorze + // 6) Utwórz na aktorze await this.actor.createEmbeddedDocuments("Item", [raw]); - // 9) Przelicz i odśwież this.render(true); return true; } - } // Register the sheet