mirror of
https://github.com/migatu/vtt_work.git
synced 2026-07-14 13:34:42 +00:00
sync: wg-voidships-builder v1.4.11
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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":[]}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user