549 lines
24 KiB
JavaScript
549 lines
24 KiB
JavaScript
/* OGame Galaxy Viewer v2 — logica frontend */
|
||
"use strict";
|
||
|
||
const $ = (id) => document.getElementById(id);
|
||
const state = { galaxy: 1, system: 1, maxGalaxy: 0, maxSystem: 0, boot: null };
|
||
|
||
/* (utility, avatar e stelle ora in common.js) */
|
||
|
||
/* ---------------- bootstrap ---------------- */
|
||
async function loadBootstrap() {
|
||
try {
|
||
const b = await api("/api/bootstrap");
|
||
state.boot = b;
|
||
state.maxGalaxy = b.server.galaxies;
|
||
state.maxSystem = b.server.systems;
|
||
$("universe-name").textContent = b.server.name;
|
||
$("universe-sub").textContent =
|
||
`[s${b.server.number}-${b.server.community}] · velocità ${b.server.speed}x · v${b.server.version}`;
|
||
$("max-galaxy").textContent = b.server.galaxies;
|
||
$("max-system").textContent = b.server.systems;
|
||
$("meta-data-age").textContent = b.fetched.players
|
||
? `Dati: ${b.fetched.players} UTC` : "";
|
||
$("meta-reports").innerHTML =
|
||
`Spie archiviate: <b>${b.reportStats.count}</b> su ${b.reportStats.planets} pianeti`;
|
||
} catch (e) {
|
||
setMsg("err", "Impossibile contattare il server: " + e.message);
|
||
}
|
||
}
|
||
|
||
/* ---------------- galassia ---------------- */
|
||
async function loadGalaxy() {
|
||
$("nav-status").textContent = `Caricamento G${state.galaxy}:S${state.system}…`;
|
||
try {
|
||
const data = await api(`/api/galaxy?g=${state.galaxy}&s=${state.system}`);
|
||
renderGalaxy(data);
|
||
$("nav-status").textContent = "";
|
||
$("inp-galaxy").value = state.galaxy;
|
||
$("inp-system").value = state.system;
|
||
} catch (e) {
|
||
setMsg("err", e.message);
|
||
$("nav-status").textContent = "";
|
||
}
|
||
}
|
||
const stClass = (s) => "st-" + (s || "");
|
||
|
||
function renderGalaxy(data) {
|
||
const body = $("galaxy-body");
|
||
body.innerHTML = "";
|
||
let any = false;
|
||
for (const cell of data.cells) {
|
||
const tr = document.createElement("tr");
|
||
|
||
let td = document.createElement("td");
|
||
td.className = "c-pos";
|
||
td.textContent = cell.pos;
|
||
tr.appendChild(td);
|
||
|
||
td = document.createElement("td");
|
||
if (cell.planet) {
|
||
any = true;
|
||
const mine = cell.planet.mine ? " planet-mine" : "";
|
||
const ro = cell.planet.report_only;
|
||
const roTag = ro
|
||
? ` <span class="ro-tag" title="Noto solo da un rapporto di spionaggio">da rapporto</span>` : "";
|
||
const ptitle = ro ? "Pianeta noto solo da un rapporto di spionaggio" : "Apri dettagli e rapporti";
|
||
td.innerHTML =
|
||
`<div class="pcell${mine}" title="${ptitle}">` +
|
||
`<span class="p-avatar">${artSVG(cell.planet.coords, 26)}</span>` +
|
||
`<span><span class="pname">${esc(cell.planet.name)}</span><br>` +
|
||
`<span class="pcoords">${esc(cell.planet.coords)}</span>${roTag}</span></div>`;
|
||
td.querySelector(".pcell").onclick = () => openPlanet(cell.pos);
|
||
}
|
||
tr.appendChild(td);
|
||
|
||
td = document.createElement("td");
|
||
if (cell.moon) {
|
||
td.innerHTML = `<span class="mcell">${artSVG(cell.planet.coords, 22, true)}` +
|
||
`<span>${Number(cell.moon.size).toLocaleString("it")}</span></span>`;
|
||
td.querySelector(".mcell").onclick = () => openPlanet(cell.pos, "moon");
|
||
}
|
||
tr.appendChild(td);
|
||
|
||
td = document.createElement("td");
|
||
if (cell.planet && cell.player) {
|
||
const p = cell.player;
|
||
const g = p.rank_total ?? "–";
|
||
const m = p.rank_military ?? "–";
|
||
td.innerHTML =
|
||
`<span class="pname-status player-link ${stClass(p.status)}" ` +
|
||
`title="Tutti i pianeti e i rapporti di questo giocatore">${esc(p.name)}</span>` +
|
||
`<span class="pranks" title="Posizioni in classifica — Generale: #${g} · Armamenti: #${m}">` +
|
||
`<span class="rk">${icon("trophy", 11)} ${g}</span><span class="rk">${icon("swords", 11)} ${m}</span></span>`;
|
||
const pn = td.querySelector(".player-link");
|
||
if (pn) pn.onclick = () => openPlayer(p.id);
|
||
}
|
||
tr.appendChild(td);
|
||
|
||
td = document.createElement("td");
|
||
if (cell.player && cell.player.alliance_tag) {
|
||
td.innerHTML = `<span class="ally-tag">[${esc(cell.player.alliance_tag)}]</span>`;
|
||
}
|
||
tr.appendChild(td);
|
||
|
||
td = document.createElement("td");
|
||
td.className = "c-rep";
|
||
if (cell.reports && cell.reports.length) {
|
||
const n = cell.reports.length;
|
||
const hasMoon = cell.reports.some((r) => r.moon);
|
||
td.innerHTML = `<span class="rep-badge${hasMoon ? " rep-moon" : ""}" ` +
|
||
`title="${n} rapporto/i di spionaggio">${icon("badge_spy", 12)} ${n}</span>`;
|
||
td.querySelector(".rep-badge").onclick = () => openPlanet(cell.pos);
|
||
}
|
||
tr.appendChild(td);
|
||
body.appendChild(tr);
|
||
}
|
||
$("empty-state").classList.toggle("hidden", any);
|
||
}
|
||
|
||
/* ---------------- popup: pianeta ---------------- */
|
||
function popupHead(avatar, title, sub) {
|
||
$("popup-avatar").innerHTML = avatar;
|
||
$("popup-title").textContent = title;
|
||
$("popup-sub").textContent = sub;
|
||
}
|
||
|
||
async function openPlanet(pos, focusMoon) {
|
||
return openPlanetAt(`${state.galaxy}:${state.system}:${pos}`, focusMoon);
|
||
}
|
||
|
||
// Apre il pianeta date le coordinate `g:s:p` (può essere in un altro sistema,
|
||
// senza spostare la vista della galassia).
|
||
async function openPlanetAt(coords, focusMoon) {
|
||
const [g, s, p] = String(coords).split(":").map(Number);
|
||
_lastPlanetCoords = `${g}:${s}:${p}`;
|
||
try {
|
||
const data = await api(`/api/planet?g=${g}&s=${s}&p=${p}`);
|
||
renderPlanetPopup(data, focusMoon);
|
||
$("overlay").classList.remove("hidden");
|
||
} catch (e) {
|
||
setMsg("err", e.message);
|
||
}
|
||
}
|
||
|
||
// Apre la finestra giocatore: tutti i pianeti + tutti i rapporti.
|
||
async function openPlayer(pid) {
|
||
try {
|
||
const d = await api(`/api/player?id=${encodeURIComponent(pid)}`);
|
||
renderPlayerPopup(d);
|
||
$("overlay").classList.remove("hidden");
|
||
} catch (e) {
|
||
setMsg("err", e.message);
|
||
}
|
||
}
|
||
|
||
const STATUS_LABEL = { "": "attivo", i: "inattivo", v: "in vacanza", vi: "vacanza + inattivo", a: "staff" };
|
||
const CAT_ICON = {
|
||
buildings: "cat_buildings", lfbuildings: "cat_lf",
|
||
research: "cat_research", ships: "cat_ships", defense: "cat_defense",
|
||
};
|
||
const ITEM_ICON = {
|
||
buildings: { 1: "b_1", 2: "b_2", 3: "b_3", 4: "b_4", 12: "b_12", 14: "b_14", 15: "b_15",
|
||
21: "b_21", 22: "b_22", 23: "b_23", 24: "b_24", 31: "b_31", 33: "b_33",
|
||
34: "b_34", 36: "b_36", 41: "b_41", 42: "b_42", 43: "b_43", 44: "b_44" },
|
||
research: { 106: "r_106", 108: "r_108", 109: "r_109", 110: "r_110", 111: "r_111",
|
||
113: "r_113", 114: "r_114", 115: "r_115", 117: "r_117", 118: "r_118",
|
||
120: "r_120", 121: "r_121", 122: "r_122", 123: "r_123", 124: "r_124",
|
||
199: "r_199" },
|
||
ships: { 202: "ship_202", 203: "ship_203", 204: "ship_204", 205: "ship_205",
|
||
206: "ship_206", 207: "ship_207", 208: "ship_208", 209: "ship_209",
|
||
210: "ship_210", 211: "ship_211", 212: "ship_212", 213: "ship_213",
|
||
214: "ship_214", 215: "ship_215", 217: "ship_217", 218: "ship_218", 219: "ship_219" },
|
||
defense: { 401: "d_401", 402: "d_402", 403: "d_403", 404: "d_404", 405: "d_405",
|
||
406: "d_406", 407: "d_407", 408: "d_408", 502: "d_502", 503: "d_503" },
|
||
lfbuildings: { 11101: "lf_11101", 11102: "lf_11102" },
|
||
};
|
||
function itemIconName(cat, id) {
|
||
const t = ITEM_ICON[cat] || {};
|
||
return t[id] || CAT_ICON[cat] || "cat_ships";
|
||
}
|
||
|
||
function renderPlanetPopup(data, focusMoon) {
|
||
const pl = data.planet, player = data.player;
|
||
_lastPlanet = data;
|
||
_lastPlayer = null;
|
||
const mine = player.id === String(state.boot.myPlayer.id) ? " (tu)" : "";
|
||
const coords = pl.coords;
|
||
const hasMoon = !!(pl.moon);
|
||
popupHead(artSVG(coords, 52), `${pl.name} [${coords}]`, "Pianeta · clicca un rapporto per i dettagli");
|
||
let h = "";
|
||
|
||
h += `<div class="kv">`;
|
||
h += `<span class="chip"><span class="k">Giocatore</span> ` +
|
||
`<span class="v ${stClass(player.status)}">${esc(player.name)}</span>${mine}</span>`;
|
||
h += `<span class="chip"><span class="k">Stato</span> <span class="v">${STATUS_LABEL[player.status] || "—"}</span></span>`;
|
||
if (player.rank_total || player.rank_military) {
|
||
h += `<span class="chip"><span class="k">Classifica</span> <span class="v">` +
|
||
`${icon("trophy", 13)} #${player.rank_total ?? "—"} · ${icon("swords", 13)} #${player.rank_military ?? "—"}</span></span>`;
|
||
}
|
||
if (player.alliance_tag) {
|
||
h += `<span class="chip"><span class="k">Alleanza</span> ` +
|
||
`<span class="v">[${esc(player.alliance_tag)}]</span> ${esc(player.alliance_name || "")}</span>`;
|
||
}
|
||
if (hasMoon) {
|
||
h += `<span class="chip"><span class="k">Luna</span> <span class="v">` +
|
||
`${esc(pl.moon.name)} · ${Number(pl.moon.size).toLocaleString("it")} km</span></span>`;
|
||
}
|
||
h += `<span class="chip"><span class="k">Pianeti</span> <span class="v">${1 + (data.other_planets || []).length}</span></span>`;
|
||
if (pl.report_only) {
|
||
h += `<span class="chip"><span class="k">Origine</span> <span class="v">solo da rapporto</span></span>`;
|
||
}
|
||
h += `</div>`;
|
||
|
||
if (data.other_planets && data.other_planets.length) {
|
||
h += `<div class="sec"><h4>Altri pianeti di ${esc(player.name)}</h4><div class="kv">`;
|
||
h += data.other_planets.map((o) =>
|
||
`<span class="chip chip-other" data-go="${esc(o.coords)}">${artSVG(o.coords, 17)}` +
|
||
`${esc(o.name)} [${esc(o.coords)}]${o.moon ? " " + artSVG(o.coords, 13, true) : ""}</span>`).join("");
|
||
h += `</div></div>`;
|
||
}
|
||
|
||
const rpt = (list, moon) =>
|
||
`<div class="sec"><h4>${moon ? "Spionaggi sulla luna" : "Spionaggi sul pianeta"}` +
|
||
` <span class="count-pill">${list.length}</span></h4>` +
|
||
(list.length
|
||
? `<div class="rpt-grid">${list.map(rptCard).join("")}</div>`
|
||
: `<div class="empty-note">${moon
|
||
? "La luna non è mai stata spiata."
|
||
: "Nessun rapporto salvato. Spia in gioco e incolla l'API string qui sopra."}</div>`) +
|
||
`</div>`;
|
||
|
||
// sezione pianeta: sempre visibile (lista o invito a importare)
|
||
h += rpt(data.reports, false);
|
||
// sezione luna: solo se esiste una luna o ci sono spionaggi sulla luna
|
||
if (hasMoon || data.moon_reports.length) h += rpt(data.moon_reports, true);
|
||
|
||
$("popup-body").innerHTML = h;
|
||
$("popup-body").querySelectorAll(".chip-other").forEach((c) => {
|
||
c.onclick = () => { closePopup(); goToCoords(c.dataset.go); };
|
||
});
|
||
$("popup-body").querySelectorAll(".rpt-card").forEach((c) => {
|
||
c.onclick = () => openReport(c.dataset.sr);
|
||
});
|
||
if (focusMoon === "moon") {
|
||
const secs = $("popup-body").querySelectorAll(".sec h4");
|
||
for (const s of secs) if (s.textContent.includes("luna")) { s.scrollIntoView({ block: "start" }); break; }
|
||
}
|
||
}
|
||
|
||
/* ---------------- popup: giocatore ---------------- */
|
||
function renderPlayerPopup(d) {
|
||
const player = d.player, planets = d.planets || [], reports = d.reports || [];
|
||
_lastPlayer = d;
|
||
_lastPlanet = null;
|
||
const mine = String(player.id) === String(state.boot.myPlayer.id) ? " (tu)" : "";
|
||
const avatar = d.avatar || (planets[0] && planets[0].coords) || (player.name + player.id);
|
||
popupHead(artSVG(avatar, 52), player.name + mine,
|
||
`${planets.length} pianeti · ${reports.length} rapporti di spionaggio`);
|
||
let h = "";
|
||
|
||
h += `<div class="kv">`;
|
||
h += `<span class="chip"><span class="k">Stato</span> <span class="v ${stClass(player.status)}">` +
|
||
`${STATUS_LABEL[player.status] || "—"}</span></span>`;
|
||
if (player.rank_total || player.rank_military) {
|
||
h += `<span class="chip"><span class="k">Classifica</span> <span class="v">` +
|
||
`${icon("trophy", 13)} #${player.rank_total ?? "—"} · ${icon("swords", 13)} #${player.rank_military ?? "—"}</span></span>`;
|
||
}
|
||
if (player.alliance_tag) {
|
||
h += `<span class="chip"><span class="k">Alleanza</span> ` +
|
||
`<span class="v">[${esc(player.alliance_tag)}]</span> ${esc(player.alliance_name || "")}</span>`;
|
||
}
|
||
h += `<span class="chip"><span class="k">Spie</span> <span class="v">${reports.length}</span></span>`;
|
||
h += `</div>`;
|
||
|
||
// pianeti del giocatore
|
||
h += `<div class="sec"><h4>Pianeti <span class="count-pill">${planets.length}</span></h4>`;
|
||
h += `<div class="kv">` + planets.map((o) =>
|
||
`<span class="chip chip-other" data-coords="${esc(o.coords)}">${artSVG(o.coords, 17)}` +
|
||
`${esc(o.name)} [${esc(o.coords)}]${o.moon ? " " + artSVG(o.coords, 13, true) : ""}</span>`
|
||
).join("") + `</div></div>`;
|
||
|
||
// tutti i rapporti del giocatore (pianeti e lune)
|
||
h += `<div class="sec"><h4>Rapporti di spionaggio <span class="count-pill">${reports.length}</span></h4>` +
|
||
(reports.length
|
||
? `<div class="rpt-grid">${reports.map(rptCard).join("")}</div>`
|
||
: `<div class="empty-note">Nessun rapporto salvato su questo giocatore.</div>`) +
|
||
`</div>`;
|
||
|
||
$("popup-body").innerHTML = h;
|
||
$("popup-body").querySelectorAll(".chip-other").forEach((c) => {
|
||
c.onclick = () => openPlanetAt(c.dataset.coords);
|
||
});
|
||
$("popup-body").querySelectorAll(".rpt-card").forEach((c) => {
|
||
c.onclick = () => openReport(c.dataset.sr);
|
||
});
|
||
}
|
||
|
||
/* ---------------- card rapporto ---------------- */
|
||
function actClass(a) {
|
||
if (a === null || a === undefined) return "act-none";
|
||
if (a <= 0) return "act-0";
|
||
if (a <= 15) return "act-15";
|
||
if (a <= 30) return "act-30";
|
||
return "act-60";
|
||
}
|
||
function actLabel(a) {
|
||
if (a === null || a === undefined) return "attività sconosciuta";
|
||
if (a <= 0) return "inattivo ≥7gg";
|
||
return `attivo ≤ ${a} min`;
|
||
}
|
||
function fmtEv(evt) {
|
||
if (!evt) return "";
|
||
const d = new Date(evt);
|
||
return isNaN(d) ? evt : d.toLocaleString("it-IT", { dateStyle: "medium", timeStyle: "short" });
|
||
}
|
||
|
||
function rptCard(rec) {
|
||
const c = rec.categories || {};
|
||
const loot = rec.loot_percentage !== null && rec.loot_percentage !== undefined;
|
||
const parts = [];
|
||
if (c.ships) parts.push(`${icon("cat_ships", 13)} ${c.ships}`);
|
||
if (c.defense) parts.push(`${icon("cat_defense", 13)} ${c.defense}`);
|
||
if (c.buildings) parts.push(`${icon("cat_buildings", 13)} ${c.buildings}`);
|
||
if (c.research) parts.push(`${icon("cat_research", 13)} ${c.research}`);
|
||
return `<div class="rpt-card" data-sr="${esc(rec.sr_id)}">` +
|
||
`<div class="top"><span class="act-dot ${actClass(rec.activity)}"></span>` +
|
||
`<span class="rpt-date">${fmtEv(rec.event_time)}</span>` +
|
||
`<span class="rpt-kind ${rec.moon ? "moon" : "planet"}">${rec.moon ? "luna" : "pianeta"}</span></div>` +
|
||
`<div class="rpt-body"><div class="rpt-target">${esc(rec.defender_planet_name || "?")}` +
|
||
(rec.coords ? ` <span class="rpt-coords">[${esc(rec.coords)}]</span>` : "") +
|
||
`</div><div class="rpt-from">da ${esc(rec.attacker_name || "?")}` +
|
||
(loot ? ` · bottino ${rec.loot_percentage}%` : "") + `</div></div>` +
|
||
`<div class="rpt-foot"><span class="meta-dots">${parts.map((p) => `<span class="m">${p}</span>`).join("") || "—"}</span>` +
|
||
`<span class="rpt-open">Apri ›</span></div></div>`;
|
||
}
|
||
|
||
/* ---------------- dettaglio rapporto ---------------- */
|
||
let _lastPlanet = null;
|
||
let _lastPlanetCoords = null;
|
||
let _lastPlayer = null;
|
||
|
||
async function openReport(srId) {
|
||
try {
|
||
const d = await api(`/api/reports/${srId}`);
|
||
renderReportDetail(d.meta, d.data);
|
||
} catch (e) {
|
||
setMsg("err", e.message);
|
||
}
|
||
}
|
||
|
||
async function deleteReport(srId) {
|
||
if (!confirm("Eliminare definitivamente questo rapporto di spionaggio?")) return;
|
||
try {
|
||
await api(`/api/reports/${srId}`, { method: "DELETE" });
|
||
if (_lastPlanetCoords) {
|
||
await openPlanetAt(_lastPlanetCoords); // ricarica il pianeta: lista aggiornata
|
||
} else if (_lastPlayer && _lastPlayer.player) {
|
||
await openPlayer(_lastPlayer.player.id); // ricarica il giocatore: lista aggiornata
|
||
} else {
|
||
closePopup();
|
||
}
|
||
loadGalaxy(); // aggiorna i badge "spie" nella galassia
|
||
} catch (e) {
|
||
setMsg("err", "Eliminazione non riuscita: " + e.message);
|
||
}
|
||
}
|
||
|
||
function renderReportDetail(meta, rep) {
|
||
const gen = rep.generic || {}, det = rep.details || {}, res = det.resources || {};
|
||
const coords = meta.coords || gen.defender_planet_coordinates || "";
|
||
popupHead(artSVG(coords, 52), `Spionaggio — ${meta.defender_planet_name || "?"}`,
|
||
`${coords}${meta.planet_type === 3 ? " (luna)" : ""} · ${fmtEv(meta.event_time)}`);
|
||
let h = "";
|
||
h += `<div class="rpt-actions">` +
|
||
`<button class="back-btn" id="btn-back">${icon("ui_chev_l", 14)} Indietro al pianeta</button>` +
|
||
`<button class="del-btn" id="btn-del">${icon("ui_trash", 14)} Elimina rapporto</button>` +
|
||
`</div>`;
|
||
|
||
h += `<div class="kv">`;
|
||
h += `<span class="chip"><span class="k">Difensore</span> <span class="v">${esc(meta.defender_name || "?")}</span></span>`;
|
||
h += `<span class="chip"><span class="k">Attaccante</span> <span class="v">${esc(gen.attacker_name || "?")}</span></span>`;
|
||
h += `<span class="chip"><span class="k">Attività</span> <span class="v">${actLabel(meta.activity)}</span></span>`;
|
||
if (meta.loot_percentage !== null && meta.loot_percentage !== undefined)
|
||
h += `<span class="chip"><span class="k">Bottino</span> <span class="v">${meta.loot_percentage}%</span></span>`;
|
||
if (gen.spy_fail_chance !== undefined)
|
||
h += `<span class="chip"><span class="k">Rischio fall.</span> <span class="v">${gen.spy_fail_chance}%</span></span>`;
|
||
if (gen.debris_metal || gen.debris_crystal)
|
||
h += `<span class="chip"><span class="k">Campo detriti</span> <span class="v">${icon("extra_debris", 13)} ${num(gen.debris_metal)} / ${num(gen.debris_crystal)}</span></span>`;
|
||
h += `</div>`;
|
||
|
||
const rt = (lab, ico, val, cls) =>
|
||
`<div class="res-tile ${cls}"><span class="r-ico">${ico}</span><span>` +
|
||
`<div class="r-lab">${lab}</div><div class="r-val">${val}</div></span></div>`;
|
||
h += `<div class="res-grid">`;
|
||
h += rt("Metallo", icon("res_metal", 20), num(res.metal), "res-metal");
|
||
h += rt("Cristallo", icon("res_crystal", 20), num(res.crystal), "res-crystal");
|
||
h += rt("Deuterio", icon("res_deuterium", 20), num(res.deuterium), "res-deuterium");
|
||
if (res.population) h += rt("Popolazione", icon("res_pop", 20), num(res.population), "res-pop");
|
||
if (res.food) h += rt("Cibo", icon("res_food", 20), num(res.food), "res-food");
|
||
h += `</div>`;
|
||
|
||
const failed = Object.entries({
|
||
"edifici": gen.failed_buildings, "ricerca": gen.failed_research,
|
||
"navi": gen.failed_ships, "difese": gen.failed_defense,
|
||
}).filter(([, v]) => v).map(([k]) => k);
|
||
if (failed.length)
|
||
h += `<div class="warn">${icon("ui_warn", 14)} Spionaggio fallito su: ${failed.join(", ")} — dati incompleti.</div>`;
|
||
|
||
h += techSection("Edifici", "buildings", det.buildings, (b) => [b.building_type, `L. ${b.level}`], "liv.");
|
||
h += techSection("Edifici Lifeform", "lfbuildings", det.lfbuildings, (b) => [b.lfbuilding_type, `L. ${b.level}`], "liv.");
|
||
h += techSection("Ricerca", "research", det.research, (b) => [b.research_type, `L. ${b.level}`], "liv.");
|
||
h += techSection("Navi", "ships", det.ships, (b) => [b.ship_type, num(b.count)], "unità");
|
||
h += techSection("Difese", "defense", det.defense, (b) => [b.defense_type, num(b.count)], "unità");
|
||
|
||
$("popup-body").innerHTML = h;
|
||
$("btn-back").onclick = () => {
|
||
if (_lastPlanet) renderPlanetPopup(_lastPlanet);
|
||
else if (_lastPlayer) renderPlayerPopup(_lastPlayer);
|
||
else closePopup();
|
||
};
|
||
$("btn-del").onclick = () => deleteReport(meta.sr_id);
|
||
}
|
||
|
||
function techSection(title, cat, rows, get, valLabel) {
|
||
if (!rows || !rows.length) return "";
|
||
const cards = rows.map((it) => {
|
||
const [tid, val] = get(it);
|
||
return `<div class="tcard cat-${cat}" title="${esc(itemName(cat, tid))}">` +
|
||
`<span class="tico">${icon(itemIconName(cat, tid), 19)}</span>` +
|
||
`<span class="tname">${esc(itemName(cat, tid))}</span>` +
|
||
`<span class="tval">${val}</span></div>`;
|
||
}).join("");
|
||
return `<div class="sec"><h4>${title} <span class="count-pill">${rows.length}</span></h4>` +
|
||
`<div class="tech-grid">${cards}</div></div>`;
|
||
}
|
||
|
||
/* ---------------- nomi e icone elementi ---------------- */
|
||
const NAMES = {
|
||
buildings: {
|
||
1: "Miniera di metallo", 2: "Miniera di cristallo", 3: "Sintetizzatore di deuterio",
|
||
4: "Centrale solare", 12: "Centrale a fusione", 14: "Fabbrica di robot",
|
||
15: "Fabbrica di naniti", 21: "Cantiere spaziale", 22: "Deposito di metallo",
|
||
23: "Deposito di cristallo", 24: "Serbatoio di deuterio", 31: "Laboratorio di ricerca",
|
||
33: "Terraformer", 34: "Deposito dell'alleanza", 36: "Dock di riparazione",
|
||
41: "Base lunare", 42: "Falanx sensoriale", 43: "Portale quantico", 44: "Silo missilistico",
|
||
},
|
||
research: {
|
||
106: "Tecnologia spionaggio", 108: "Tecnologia informatica", 109: "Tecnologia delle armi",
|
||
110: "Tecnologia scudi", 111: "Tecnologia corazzatura", 113: "Tecnologia energetica",
|
||
114: "Tecnologia iperspazio", 115: "Motore a combustione", 117: "Motore a impulso",
|
||
118: "Motore iperspaziale", 120: "Tecnologia laser", 121: "Tecnologia ionica",
|
||
122: "Tecnologia al plasma", 123: "Rete di ricerca intergalattica",
|
||
124: "Astrofisica", 199: "Tecnologia gravitoni",
|
||
},
|
||
ships: {
|
||
202: "Cargo leggero", 203: "Cargo pesante", 204: "Caccia leggero", 205: "Caccia pesante",
|
||
206: "Incrociatore", 207: "Nave da battaglia", 208: "Nave colonizzatrice",
|
||
209: "Riciclatore", 210: "Sonda di spionaggio", 211: "Bombardiere", 212: "Satellite solare",
|
||
213: "Corazzata", 214: "Morte Nera", 215: "Incrociatore da battaglia", 217: "Crawler",
|
||
218: "Reaper", 219: "Pathfinder",
|
||
},
|
||
defense: {
|
||
401: "Lanciamissili", 402: "Laser leggero", 403: "Laser pesante", 404: "Cannone Gauss",
|
||
405: "Cannone ionico", 406: "Cannone al plasma", 407: "Cupola piccola", 408: "Cupola grande",
|
||
502: "Missili anti-balistici", 503: "Missili interplanetari",
|
||
},
|
||
lfbuildings: { 11101: "Campo di coltivazione", 11102: "Centro di ricerca" },
|
||
};
|
||
function itemName(cat, id) {
|
||
const t = NAMES[cat] || {};
|
||
return t[id] || (cat === "lfbuildings" ? "Edificio Lifeform #" + id : cat + " #" + id);
|
||
}
|
||
|
||
/* ---------------- navigazione ---------------- */
|
||
function setSystem(s) { state.system = Math.max(1, Math.min(state.maxSystem, s)); loadGalaxy(); }
|
||
function setGalaxy(g) { state.galaxy = Math.max(1, Math.min(state.maxGalaxy, g)); loadGalaxy(); }
|
||
function closePopup() { $("overlay").classList.add("hidden"); _lastPlanet = null; _lastPlayer = null; }
|
||
function goToCoords(c) {
|
||
const [g, s] = c.split(":").map(Number);
|
||
closePopup();
|
||
state.galaxy = Math.max(1, Math.min(state.maxGalaxy, g || 1));
|
||
state.system = Math.max(1, Math.min(state.maxSystem, s || 1));
|
||
loadGalaxy();
|
||
}
|
||
|
||
/* ---------------- messaggi / import ---------------- */
|
||
function setMsg(kind, text, ms = 6000) {
|
||
const el = $("import-msg");
|
||
el.className = kind;
|
||
el.textContent = text;
|
||
if (ms) setTimeout(() => { el.textContent = ""; }, ms);
|
||
}
|
||
|
||
async function importReport() {
|
||
const token = $("inp-token").value.trim();
|
||
if (!token) { setMsg("err", "Incolla prima l'API string."); return; }
|
||
const btn = $("btn-import");
|
||
btn.disabled = true;
|
||
setMsg("ok", "Recupero del rapporto…");
|
||
try {
|
||
const res = await api("/api/reports", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ token }),
|
||
});
|
||
$("inp-token").value = "";
|
||
setMsg("ok",
|
||
res.created
|
||
? `Importato: ${res.report.defender_name} — ${res.coords}${res.planet_type === 3 ? " (luna)" : ""}`
|
||
: `Rapporto già in archivio (${res.coords}).`);
|
||
loadGalaxy();
|
||
} catch (e) {
|
||
setMsg("err", e.message);
|
||
} finally {
|
||
btn.disabled = false;
|
||
}
|
||
}
|
||
|
||
/* ---------------- eventi ---------------- */
|
||
$("btn-import").onclick = importReport;
|
||
$("inp-token").addEventListener("keydown", (e) => { if (e.key === "Enter") importReport(); });
|
||
$("btn-prev-sys").onclick = () => setSystem(state.system - 1);
|
||
$("btn-next-sys").onclick = () => setSystem(state.system + 1);
|
||
$("btn-refresh").onclick = () => loadGalaxy();
|
||
$("inp-system").addEventListener("change", (e) => setSystem(parseInt(e.target.value, 10) || 1));
|
||
$("inp-galaxy").addEventListener("change", (e) => setGalaxy(parseInt(e.target.value, 10) || 1));
|
||
$("popup-close").onclick = closePopup;
|
||
$("overlay").addEventListener("click", (e) => { if (e.target === $("overlay")) closePopup(); });
|
||
document.addEventListener("keydown", (e) => {
|
||
if (e.key === "Escape" && !$("overlay").classList.contains("hidden")) closePopup();
|
||
});
|
||
|
||
/* ---------------- init ---------------- */
|
||
initStars();
|
||
const il = document.getElementById("import-label");
|
||
if (il) il.innerHTML = icon("ui_import", 15) + ' <span>Nuovo rapporto</span>';
|
||
const tli = document.querySelector(".tl-ico");
|
||
if (tli) tli.innerHTML = icon("trophy", 14);
|
||
const pcl = document.getElementById("popup-close");
|
||
if (pcl) pcl.innerHTML = icon("ui_close", 16);
|
||
const _qs = new URLSearchParams(location.search);
|
||
const _g = parseInt(_qs.get("g") || "", 10);
|
||
const _s = parseInt(_qs.get("s") || "", 10);
|
||
(async () => {
|
||
await loadBootstrap();
|
||
if (_g) state.galaxy = Math.max(1, Math.min(state.maxGalaxy, _g));
|
||
if (_s) state.system = Math.max(1, Math.min(state.maxSystem, _s));
|
||
await loadGalaxy();
|
||
})();
|