Files
vtt_work/wg-greyknights/gk-greyknight-0.4.6-patch/scripts/auto-loadout.js
T

126 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// gk-greyknight — auto-attach loadout (dedupe + legacy scrub) v0.4.6
Hooks.once("ready", () => {
const PACK_KEY = "gk-greyknight.gk-items"; // moduleId.packName
const ITEMS = {
ability: "Aegis of Titan (Ability)",
nemesis: "Nemesis Force Halberd",
storm: "Grey Knight Armour Integrated Storm Bolter",
aegisPA: "Aegis Power Armour",
aegisTerm: "Terminator Aegis (Grey Knights)"
};
const FLAG_SCOPE = "gk-greyknight"; const FLAG_DONE = "autoLoadoutDone";
// --- REMOVE legacy createItem handlers that emit 'Choice 1/2' or 'ORDO MALLEUS' dialogs ---
const removeLegacy = () => {
const list = (Hooks.events && Hooks.events.createItem) || Hooks._hooks?.createItem || [];
// copy to avoid mutating while iterating
const arr = Array.from(list);
for (const cb of arr) {
const src = (cb && cb.toString && cb.toString()) || "";
if (/ORDO\s+MALLEUS/i.test(src) || /Choice\s*1/i.test(src) || /macro-gk-auto-loadout-on-archetype/i.test(src)) {
try { Hooks.off("createItem", cb); console.warn("gk-greyknight: removed legacy createItem hook"); } catch(e){}
}
}
};
removeLegacy();
async function fetchFromCompendium(name) {
const pack = game.packs.get(PACK_KEY);
if (!pack) return null;
const index = await pack.getIndex();
const hit = index.find(e => e.name === name);
if (!hit) return null;
const doc = await pack.getDocument(hit._id);
return doc?.toObject();
}
async function ensureOnActor(actor, name, equip=false) {
let have = actor.items.getName(name);
if (have) {
if (typeof have.system?.equipped !== "undefined") await have.update({"system.equipped": !!equip});
return have;
}
let src = await fetchFromCompendium(name);
if (!src) { const world = game.items.getName(name); if (world) src = world.toObject(); }
if (!src) return null;
if (equip && src.system && typeof src.system.equipped !== "undefined") src.system.equipped = true;
const [created] = await actor.createEmbeddedDocuments("Item", [src]);
return created;
}
async function setEquipped(actor, name, on) {
const it = actor.items.getName(name);
if (it && typeof it.system?.equipped !== "undefined") await it.update({"system.equipped": !!on});
}
if (window._gkAutoLoadoutHookId) { Hooks.off("createItem", window._gkAutoLoadoutHookId); window._gkAutoLoadoutHookId = null; }
window._gkAutoLoadoutHookId = Hooks.on("createItem", async (item, opts, userId) => {
try {
if (item?.type !== "archetype") return;
if (item?.name !== "Grey Knight (Archetype)") return;
if (game.userId !== userId) return; // only the originating client
const actor = item.parent; if (!actor) return;
// If a legacy dialog already fired very early, bail out to avoid double prompts
if (actor.getFlag(FLAG_SCOPE, FLAG_DONE)) return;
await actor.setFlag(FLAG_SCOPE, FLAG_DONE, true);
const content = `
<style>
.gk-choices .opt{display:flex;gap:.6rem;align-items:flex-start;padding:.35rem .5rem;border:1px solid #6663;border-radius:6px;margin:.35rem 0}
.gk-choices .opt label{font-weight:600}
.gk-choices .hint{font-size:.9em;opacity:.85;margin-left:1.7rem}
</style>
<div class="gk-choices">
<div class="opt">
<input type="radio" name="mode" value="incog" id="gk-incog">
<label for="gk-incog">Incognito (bez Aegis)</label>
</div>
<div class="hint">Brak Aegis na starcie; dodam Ability i broń, ale nie będą <i>equipped</i>.</div>
<div class="opt">
<input type="radio" name="mode" value="war" id="gk-war" checked>
<label for="gk-war">WarPlate — Aegis Power Armour</label>
</div>
<div class="hint">Wyposaża: Aegis Power Armour + Nemesis + Integrated Storm Bolter. Włącza synergię Aegis.</div>
<div class="opt">
<input type="radio" name="mode" value="term" id="gk-term">
<label for="gk-term">Terminator — Terminator Aegis</label>
</div>
<div class="hint">Wyposaża: Terminator Aegis + Nemesis + Integrated Storm Bolter. Włącza synergię Aegis.</div>
</div>`;
const choice = await new Promise(resolve => {
new Dialog({
title: "Grey Knight — Wybierz Loadout",
content,
buttons: {
ok: {label: "Zastosuj", icon:"<i class='fas fa-check'></i>", callback: html => resolve(html.find('input[name=\"mode\"]:checked').val() || "war")},
cancel: {label:"Anuluj", callback: ()=>resolve(null)}
},
default: "ok"
}).render(true);
});
if (!choice) return;
await ensureOnActor(actor, ITEMS.ability, false);
await ensureOnActor(actor, ITEMS.nemesis, choice!=="incog");
await ensureOnActor(actor, ITEMS.storm, choice!=="incog");
await setEquipped(actor, ITEMS.aegisPA, false);
await setEquipped(actor, ITEMS.aegisTerm, false);
if (choice === "war") await ensureOnActor(actor, ITEMS.aegisPA, true);
else if (choice === "term") await ensureOnActor(actor, ITEMS.aegisTerm, true);
const abil = actor.items.getName(ITEMS.ability);
const anyAegisOn = !!actor.items.find(i => ["armour","armor"].includes(i.type) && /Aegis/.test(i.name) && i.system?.equipped);
if (abil?.effects?.length) for (const ef of abil.effects) if (ef.name?.toLowerCase().includes("synergy")) await ef.update({disabled: !anyAegisOn});
ui.notifications.info(`GK loadout: ${choice.toUpperCase()} ustawiony.`);
} catch (e) { console.error("gk-greyknight auto-loadout error:", e); }
});
console.log("gk-greyknight: auto-loadout hook active (id=", window._gkAutoLoadoutHookId, ")");
});