197 lines
5.9 KiB
JavaScript
197 lines
5.9 KiB
JavaScript
const MODULE_ID = "sla-gear-cache";
|
|
const DRAFT_KEY = "cacheDraft";
|
|
|
|
let CACHE_TABLES = null;
|
|
|
|
function getHtmlRoot(html) {
|
|
return html instanceof HTMLElement ? html : html?.[0] ?? null;
|
|
}
|
|
|
|
async function loadTables() {
|
|
if (CACHE_TABLES) return CACHE_TABLES;
|
|
const response = await fetch("modules/sla-gear-cache/data/cache-tables.json");
|
|
CACHE_TABLES = await response.json();
|
|
return CACHE_TABLES;
|
|
}
|
|
|
|
function defaultState() {
|
|
return {
|
|
cacheType: "ammo",
|
|
itemCount: 3,
|
|
items: []
|
|
};
|
|
}
|
|
|
|
function getState() {
|
|
return foundry.utils.mergeObject(defaultState(), game.settings.get(MODULE_ID, DRAFT_KEY) ?? {});
|
|
}
|
|
|
|
async function setState(state) {
|
|
await game.settings.set(MODULE_ID, DRAFT_KEY, foundry.utils.mergeObject(defaultState(), state ?? {}));
|
|
}
|
|
|
|
function sumItems(items) {
|
|
return items.reduce((totals, item) => {
|
|
totals.weight += Number(item.weight || 0) * Number(item.quantity || 0);
|
|
totals.cost += Number(item.cost || 0) * Number(item.quantity || 0);
|
|
return totals;
|
|
}, { weight: 0, cost: 0 });
|
|
}
|
|
|
|
function renderCacheHtml(state) {
|
|
const items = state.items ?? [];
|
|
const rows = items.map((item) => `<li>${foundry.utils.escapeHTML(item.name)} x${item.quantity} | ${item.weight} kg | ${item.cost} cR</li>`).join("");
|
|
const totals = sumItems(items);
|
|
return `
|
|
<section class="sla-gear-cache-output">
|
|
<h2>SLA Gear Cache - ${foundry.utils.escapeHTML(state.cacheType)}</h2>
|
|
<ul>${rows}</ul>
|
|
<p><strong>Total Weight:</strong> ${totals.weight.toFixed(1)} kg | <strong>Total Value:</strong> ${totals.cost.toFixed(0)} cR</p>
|
|
</section>
|
|
`;
|
|
}
|
|
|
|
async function randomCache(cacheType, itemCount) {
|
|
const tables = await loadTables();
|
|
const pool = tables[cacheType] ?? [];
|
|
const items = [];
|
|
for (let index = 0; index < Math.max(1, Number(itemCount || 1)); index += 1) {
|
|
const picked = foundry.utils.deepClone(pool[Math.floor(Math.random() * pool.length)]);
|
|
if (!picked) continue;
|
|
items.push(picked);
|
|
}
|
|
return {
|
|
cacheType,
|
|
itemCount: Number(itemCount || 3),
|
|
items
|
|
};
|
|
}
|
|
|
|
function selectedActor() {
|
|
if (canvas?.tokens?.controlled?.length) return canvas.tokens.controlled[0].actor;
|
|
return game.user.character ?? null;
|
|
}
|
|
|
|
async function addItemsToActor(actor, items) {
|
|
if (!actor) {
|
|
ui.notifications.warn("Select a token or character first.");
|
|
return;
|
|
}
|
|
|
|
const docs = items.map((item) => ({
|
|
name: item.name,
|
|
type: item.type ?? "item",
|
|
system: {
|
|
quantity: item.quantity,
|
|
weight: item.weight,
|
|
cost: item.cost,
|
|
description: { value: "Generated by SLA Gear Cache." }
|
|
}
|
|
}));
|
|
|
|
await actor.createEmbeddedDocuments("Item", docs);
|
|
ui.notifications.info(`Added ${docs.length} generated cache item(s) to ${actor.name}.`);
|
|
}
|
|
|
|
class SlaGearCacheApp extends FormApplication {
|
|
static get defaultOptions() {
|
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
id: "sla-gear-cache-app",
|
|
title: "SLA Gear Cache",
|
|
template: "modules/sla-gear-cache/templates/gear-cache.hbs",
|
|
classes: ["sla-gear-cache"],
|
|
width: 560,
|
|
height: "auto",
|
|
closeOnSubmit: false,
|
|
submitOnChange: false,
|
|
resizable: true
|
|
});
|
|
}
|
|
|
|
async getData() {
|
|
const state = getState();
|
|
return {
|
|
state,
|
|
items: state.items ?? [],
|
|
totals: sumItems(state.items ?? []),
|
|
cacheTypes: [
|
|
{ value: "ammo", label: "Ammo Cache", selected: state.cacheType === "ammo" },
|
|
{ value: "medical", label: "Medical Cache", selected: state.cacheType === "medical" },
|
|
{ value: "breach", label: "Breach Kit", selected: state.cacheType === "breach" },
|
|
{ value: "surveillance", label: "Surveillance Pack", selected: state.cacheType === "surveillance" }
|
|
]
|
|
};
|
|
}
|
|
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
html.find("[data-action='randomize']").on("click", async () => {
|
|
const state = await this._collectState();
|
|
const nextState = await randomCache(state.cacheType, state.itemCount);
|
|
await setState(nextState);
|
|
this.render(false);
|
|
});
|
|
html.find("[data-action='chat']").on("click", async () => {
|
|
const state = await this._collectState();
|
|
await setState(state);
|
|
await ChatMessage.create({
|
|
user: game.user.id,
|
|
speaker: { alias: "SLA Gear Cache" },
|
|
content: renderCacheHtml(state)
|
|
});
|
|
});
|
|
html.find("[data-action='actor']").on("click", async () => {
|
|
const state = await this._collectState();
|
|
await setState(state);
|
|
await addItemsToActor(selectedActor(), state.items ?? []);
|
|
});
|
|
}
|
|
|
|
async _collectState() {
|
|
const fd = new foundry.applications.ux.FormDataExtended(this.form);
|
|
return foundry.utils.mergeObject(getState(), fd.object);
|
|
}
|
|
|
|
async _updateObject(_event, formData) {
|
|
await setState(formData);
|
|
ui.notifications.info("SLA Gear Cache saved.");
|
|
}
|
|
|
|
static open() {
|
|
return new SlaGearCacheApp().render(true);
|
|
}
|
|
}
|
|
|
|
function installActorButton(app, html) {
|
|
if (!game.user?.isGM) return;
|
|
const root = getHtmlRoot(html);
|
|
const header = root?.querySelector(".directory-header .header-actions, .directory-header .action-buttons");
|
|
if (!header || header.querySelector(".sla-gear-cache-open")) return;
|
|
const button = document.createElement("button");
|
|
button.type = "button";
|
|
button.className = "sla-gear-cache-open";
|
|
button.innerHTML = `<i class="fas fa-box-open"></i> Gear Cache`;
|
|
button.addEventListener("click", () => SlaGearCacheApp.open());
|
|
header.appendChild(button);
|
|
}
|
|
|
|
Hooks.once("init", () => {
|
|
game.settings.register(MODULE_ID, DRAFT_KEY, {
|
|
scope: "world",
|
|
config: false,
|
|
type: Object,
|
|
default: defaultState()
|
|
});
|
|
|
|
game.settings.registerMenu(MODULE_ID, "gearCacheMenu", {
|
|
name: "SLA Gear Cache",
|
|
label: "Open Gear Cache",
|
|
hint: "Generate SLA ammo, medical, breach, and surveillance stash bundles.",
|
|
icon: "fas fa-box-open",
|
|
type: SlaGearCacheApp,
|
|
restricted: true
|
|
});
|
|
});
|
|
|
|
Hooks.on("renderActorDirectory", installActorButton);
|