Import GK_Foundry_GreyKnight_v5.zip do wg-greyknights (2025-08-21T19:02:44Z)

This commit is contained in:
2025-08-21 19:02:44 +00:00
parent c725793b46
commit edb3f0dffb
10 changed files with 956 additions and 0 deletions
@@ -0,0 +1,20 @@
// GK Auto Loadout on Archetype Assignment
// Rejestruje Hook createItem: jeśli do aktora dodano 'Grey Knight (Archetype)', uruchomi 'GK Loadout Manager'.
(() => {
if (window._gkAutoLoadoutHook) { ui.notifications.info("GK Auto-Loadout: już aktywne."); return; }
window._gkAutoLoadoutHook = Hooks.on("createItem", async (item, opts, userId) => {
try {
if (item?.type !== "archetype") return;
if (item?.name !== "Grey Knight (Archetype)") return;
const actor = item.parent;
if (!actor || game.userId !== userId) return;
const m = game.macros.getName("GK Loadout Manager") || game.macros.getName("GK-Loadout-Manager") || null;
if (m) {
await m.execute();
} else {
ui.notifications.warn("Brak makra 'GK Loadout Manager'. Utwórz je i uruchom ponownie.");
}
} catch (e) { console.error("GK Auto-Loadout error", e); }
});
ui.notifications.info("GK Auto-Loadout: hook zarejestrowany (createItem).");
})();
@@ -0,0 +1,35 @@
// Aegis Glow Toggle (robust) — v13
(async () => {
const tok = canvas.tokens.controlled[0] ?? null;
const actor = tok?.actor ?? game.user.character;
const token = tok ?? canvas.tokens.placeables.find(t => t.actor?.id === actor?.id);
if (!actor || !token) return ui.notifications.warn("Wybierz token lub przypisz postać do użytkownika.");
const aegisArm = actor.items.find(i => ["armour","armor"].includes(i.type) && /Aegis/.test(i.name));
if (!aegisArm) return ui.notifications.warn("Nie znaleziono Aegis armour na postaci.");
const eff = aegisArm.effects.find(e => e.name?.toLowerCase().startsWith("aegis warding runes"));
if (!eff) return ui.notifications.warn("Nie znaleziono efektu toggle na Aegis armour.");
const wasDisabled = eff.disabled ?? true;
await eff.update({ disabled: !wasDisabled });
const FLAG_SCOPE = "wng-gk"; const FLAG_KEY = "prevLight";
const setGlowOn = async () => {
const prev = token.document.getFlag(FLAG_SCOPE, FLAG_KEY);
if (!prev) await token.document.setFlag(FLAG_SCOPE, FLAG_KEY, foundry.utils.deepClone(token.document.light));
await token.document.update({
light: { color:"#66ccff", coloration:2, alpha:0.7, dim:16, bright:0, angle:360,
luminosity:0.5, attenuation:0.35, saturation:0, contrast:0, shadows:0,
animation:{ type:"pulse", speed:3, intensity:4, reverse:false } }
});
await canvas.perception.update({ lighting: { refresh: true } });
};
const setGlowOff = async () => {
const prev = token.document.getFlag(FLAG_SCOPE, FLAG_KEY);
if (prev) { await token.document.update({ light: prev }); await token.document.unsetFlag(FLAG_SCOPE, FLAG_KEY); }
else { await token.document.update({ light: { dim:0, bright:0, color:null } }); }
await canvas.perception.update({ lighting: { refresh: true } });
};
if (wasDisabled) { await setGlowOn(); ChatMessage.create({ content: `<b>Aegis:</b> Warding Runes <span style="color:#66ccff">ENGAGED</span>. (Akcja)` }); }
else { await setGlowOff(); ChatMessage.create({ content: `<b>Aegis:</b> Warding Runes DISENGAGED.` }); }
})();
@@ -0,0 +1,68 @@
// GK Import World Items (from JSONs into Items Directory)
(async () => {
const FILES = [
"fvtt-Item-archetype-grey-knight_v5.json",
"fvtt-Item-ability-aegis-of-titan_v5.json",
"fvtt-Item-nemesis-force-halberd_v4.json",
"fvtt-Item-gk-armour-integrated-storm-bolter_v2.json",
"fvtt-Item-aegis-power-armour_v2.json",
"fvtt-Item-terminator-aegis-gk_v4.json"
];
const pick = await new Promise(resolve => {
const content = `
<p>Folder z JSON-ami (np. <code>worlds/TwojSwiat/foundry</code>)</p>
<div class="form-group">
<input type="text" name="path" style="width:100%" placeholder="worlds/TwojSwiat/foundry">
<button type="button" class="browse"><i class="fas fa-folder-open"></i> Browse</button>
</div>
<p><label><input type="checkbox" name="overwrite" checked> Nadpisz istniejące itemy o tych nazwach</label></p>`;
const dlg = new Dialog({
title: "GK Import World Items",
content,
buttons: {
go: { label: "Importuj", icon: '<i class="fas fa-file-import"></i>', callback: html => resolve({ok:true, html}) },
cancel: { label: "Anuluj", callback: () => resolve({ok:false}) }
},
render: html => {
html.find("button.browse").on("click", () => {
const fp = new FilePicker({
type: "any",
callback: path => html.find('input[name="path"]').val(path.replace(/\/[^\/]*$/, ""))
});
fp.browse();
});
},
default: "go"
});
dlg.render(true);
});
if (!pick?.ok) return;
const dir = pick.html.find('input[name="path"]').val()?.trim();
const overwrite = pick.html.find('input[name="overwrite"]')[0].checked;
if (!dir) return ui.notifications.warn("Podaj ścieżkę folderu w danych świata.");
const ensureFolder = async (name, color="#4b6eaf") => {
let f = game.folders.find(x => x.type === "Item" && x.name === name);
if (!f) f = await Folder.create({name, type:"Item", color});
return f;
};
const worldFolder = await ensureFolder("Grey Knight Pack", "#4b6eaf");
const fetchJSON = async (path) => {
const res = await fetch(`/${path}`);
if (!res.ok) throw new Error(`Nie mogę pobrać: ${path} (HTTP ${res.status})`);
return await res.json();
};
let created = 0, updated = 0, skipped = 0;
for (const fn of FILES) {
try {
const full = `${dir}/${fn}`;
const data = await fetchJSON(full);
if (data._id) delete data._id;
data.folder = worldFolder.id;
const existing = game.items.getName(data.name);
if (existing) {
if (overwrite) { await existing.update(data); updated++; } else { skipped++; }
} else { await Item.create(data); created++; }
} catch (e) { console.error(`Błąd importu ${fn}`, e); ui.notifications.warn(`Błąd importu: ${fn} (konsola).`); }
}
ui.notifications.info(`GK Import: utworzono ${created}, zaktualizowano ${updated}, pominięto ${skipped}.`);
})();
@@ -0,0 +1,64 @@
// GK Loadout Manager (attach from Items Directory, set loadout, wire Synergy)
(async () => {
const NAMES = {
archetype: "Grey Knight (Archetype)",
ability: "Aegis of Titan (Ability)",
nemesis: "Nemesis Force Halberd",
storm: "Grey Knight Armour Integrated Storm Bolter",
aegis: "Aegis Power Armour",
term: "Terminator Aegis (Grey Knights)"
};
const WORLD_FOLDER_NAME = "Grey Knight Pack";
const actor = canvas.tokens.controlled[0]?.actor ?? game.user.character;
if (!actor) return ui.notifications.warn("Zaznacz token albo przypisz postać.");
const mode = await Dialog.prompt({
title: "Grey Knight Loadout Manager",
content: `<p>Wybierz lub zmień loadout:</p>
<label><input type="radio" name="m" value="incognito"> Incognito (bez Aegis)</label><br/>
<label><input type="radio" name="m" value="war" checked> War-Plate (Aegis Power Armour)</label><br/>
<label><input type="radio" name="m" value="term"> Terminator (Terminator Aegis)</label>`,
label: "OK",
callback: html => html.find('input[name="m"]:checked').val()
});
if (!mode) return;
const findWorldItem = (name) => {
const inFolder = game.items.filter(i => i.name === name && i.folder?.name === WORLD_FOLDER_NAME);
if (inFolder.length) return inFolder[0];
return game.items.getName(name) ?? null;
};
const ensureOnActor = async (name) => {
const have = actor.items.getName(name);
if (have) return have;
const worldIt = findWorldItem(name);
if (!worldIt) { ui.notifications.warn(`Brak w Items Directory: ${name}`); return null; }
const created = await actor.createEmbeddedDocuments("Item", [worldIt.toObject()]);
return created[0];
};
for (const key of Object.keys(NAMES)) await ensureOnActor(NAMES[key]);
const setEquipped = async (name, on) => { const it = actor.items.getName(name); if (it) await it.update({"system.equipped": !!on}); };
await setEquipped(NAMES.aegis, false);
await setEquipped(NAMES.term, false);
await setEquipped(NAMES.storm, false);
await setEquipped(NAMES.nemesis, false);
if (mode === "war") {
await setEquipped(NAMES.aegis, true);
await setEquipped(NAMES.storm, true);
await setEquipped(NAMES.nemesis, true);
} else if (mode === "term") {
await setEquipped(NAMES.term, true);
await setEquipped(NAMES.storm, true);
await setEquipped(NAMES.nemesis, true);
}
const anyAegisEquipped = !!actor.items.find(i => ["armour","armor"].includes(i.type) && /Aegis/.test(i.name) && i.system?.equipped);
const abilItem = actor.items.getName(NAMES.ability);
if (abilItem) for (const ef of abilItem.effects ?? []) if (ef.name?.includes("Aegis Synergy")) await ef.update({ disabled: !anyAegisEquipped });
ui.notifications.info(`Loadout ustawiony: ${mode.toUpperCase()}.`);
})();