/** * Bootstrap helpers: load state from API or show register/connect UI. */ import { defaultState, normalizeZooWeights } from "./state.js"; import { refreshOffers, getPlayerZooWeights } from "./conveyor.js"; import { ensureBotState } from "./bot-zoo.js"; import { loadZoos, loadMyZoo, register, createMyZoo } from "./api-client.js"; import { getOrCreateKeyPair } from "./auth-client.js"; /** * @param {import("./types.js").GameState} gameState * @param {{ zoosData: { worldZoos?: Array }, playerZooId?: string, playerName?: string, playerX?: number, playerY?: number }} opts * @returns {void} */ export function applyWorldZoos(gameState, opts) { const { zoosData, playerZooId, playerName, playerX, playerY } = opts; const playerWeights = getPlayerZooWeights(gameState); const playerEntry = { id: "player", name: playerName || "Mon zoo", x: playerX ?? 25, y: playerY ?? 50, animalWeights: playerWeights, }; const others = (zoosData.worldZoos || []) .filter((z) => z.id !== playerZooId) .map((z) => ({ ...z, animalWeights: normalizeZooWeights(z.animalWeights), botState: z.game_state ?? undefined, })); others.forEach((zoo) => ensureBotState(zoo, false)); gameState.worldZoos = [playerEntry, ...others]; } /** * @returns {Promise} */ export function bootstrapNoKeys() { const s = defaultState(); const nowUnix = Math.floor(Date.now() / 1000); refreshOffers(s, nowUnix); return Promise.resolve(s); } /** * @param {{ worldZoos?: Array }} zoosData * @param {HTMLElement} rootEl * @param {(zooId: string) => void} setMyZooId * @returns {Promise} */ export function bootstrapShowRegisterPanel(zoosData, rootEl, setMyZooId) { return new Promise((resolve) => { rootEl.innerHTML = "

Construis un zoo

Créer un compte (pseudo, pas de mot de passe)

"; const errEl = document.getElementById("boot-err"); document.getElementById("boot-submit").addEventListener("click", async () => { const pseudo = document.getElementById("boot-pseudo").value.trim(); if (pseudo.length < 2) { errEl.textContent = "Pseudo min. 2 caractères"; return; } errEl.textContent = ""; try { await register(pseudo); const s = defaultState(); const nowUnix = Math.floor(Date.now() / 1000); refreshOffers(s, nowUnix); const created = await createMyZoo(pseudo, s); setMyZooId(created.zooId); applyWorldZoos(s, { zoosData, playerZooId: created.zooId, playerName: created.name, playerX: created.x, playerY: created.y }); s.myZooId = created.zooId; s.playerName = created.name; s.playerX = created.x; s.playerY = created.y; resolve(s); } catch (e) { errEl.textContent = e.message || "Erreur"; } }); }); } /** * @param {{ worldZoos?: Array }} zoosData * @returns {Promise} */ export async function bootstrapMe404(zoosData) { const s = defaultState(); const nowUnix = Math.floor(Date.now() / 1000); refreshOffers(s, nowUnix); const created = await createMyZoo("Mon zoo", s); const myZooId = created.zooId; applyWorldZoos(s, { zoosData, playerZooId: myZooId, playerName: created.name, playerX: created.x, playerY: created.y }); s.myZooId = myZooId; s.playerName = created.name; s.playerX = created.x; s.playerY = created.y; return s; } /** * @param {{ game_state: import("./types.js").GameState; zooId: string; name: string; x: number; y: number }} me * @param {{ worldZoos?: Array }} zoosData * @returns {import("./types.js").GameState} */ export function bootstrapMeWithGameState(me, zoosData) { const s = me.game_state; const myZooId = me.zooId; s.myZooId = myZooId; s.playerName = me.name; s.playerX = me.x; s.playerY = me.y; if (s.coins < 100) s.coins = 200; const nowUnix = Math.floor(Date.now() / 1000); if (s.conveyorOffers === null || s.conveyorOffers === undefined || s.conveyorOffers.length === 0) { refreshOffers(s, nowUnix); } applyWorldZoos(s, { zoosData, playerZooId: myZooId, playerName: me.name, playerX: me.x, playerY: me.y }); return s; } /** * @param {{ name?: string }} me * @param {{ worldZoos?: Array }} zoosData * @returns {Promise} */ export async function bootstrapMeCreateZoo(me, zoosData) { const s = defaultState(); const nowUnix = Math.floor(Date.now() / 1000); refreshOffers(s, nowUnix); const created = await createMyZoo(me.name || "Mon zoo", s); const myZooId = created.zooId; applyWorldZoos(s, { zoosData, playerZooId: myZooId, playerName: created.name, playerX: created.x, playerY: created.y }); s.myZooId = myZooId; s.playerName = created.name; s.playerX = created.x; s.playerY = created.y; return s; } /** * @param {(zooId: string) => void} setMyZooId * @param {HTMLElement} rootEl * @returns {Promise} */ export async function bootstrapFromApi(setMyZooId, rootEl) { const keys = await getOrCreateKeyPair(); if (!keys) { return bootstrapNoKeys(); } let zoosData = { worldZoos: [], mapWidth: 100, mapHeight: 100 }; try { zoosData = await loadZoos(); } catch (e) { console.warn("loadZoos failed", e); } const meRes = await loadMyZoo().catch((e) => { console.warn("loadMyZoo failed", e); return { status: 401 }; }); if (meRes.status === 401) { return bootstrapShowRegisterPanel(zoosData, rootEl, setMyZooId); } if (meRes.status === 404) { const s = await bootstrapMe404(zoosData); setMyZooId(s.myZooId ?? "player"); return s; } const me = meRes.data; if (me.game_state && typeof me.game_state === "object") { setMyZooId(me.zooId); return bootstrapMeWithGameState(me, zoosData); } const s = await bootstrapMeCreateZoo(me, zoosData); setMyZooId(s.myZooId ?? "player"); return s; }