Initial commit

**Motivations:**
- Initialisation du versionning git pour le projet

**Root causes:**
- N/A (Nouveau projet)

**Correctifs:**
- N/A

**Evolutions:**
- Structure initiale du projet
- Ajout du .gitignore

**Pages affectées:**
- Tous les fichiers
This commit is contained in:
2026-03-03 22:24:17 +01:00
commit e031c9a1d2
155 changed files with 22334 additions and 0 deletions

178
web/js/main-bootstrap.js Normal file
View File

@@ -0,0 +1,178 @@
/**
* 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<unknown> }, 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<import("./types.js").GameState>}
*/
export function bootstrapNoKeys() {
const s = defaultState();
const nowUnix = Math.floor(Date.now() / 1000);
refreshOffers(s, nowUnix);
return Promise.resolve(s);
}
/**
* @param {{ worldZoos?: Array<unknown> }} zoosData
* @param {HTMLElement} rootEl
* @param {(zooId: string) => void} setMyZooId
* @returns {Promise<import("./types.js").GameState>}
*/
export function bootstrapShowRegisterPanel(zoosData, rootEl, setMyZooId) {
return new Promise((resolve) => {
rootEl.innerHTML = "<div class=\"boot-panel\"><h1>Construis un zoo</h1><p>Créer un compte (pseudo, pas de mot de passe)</p><input id=\"boot-pseudo\" type=\"text\" placeholder=\"Pseudo\" maxlength=\"32\" /><button id=\"boot-submit\">Créer</button><p id=\"boot-err\" class=\"boot-err\"></p></div>";
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<unknown> }} zoosData
* @returns {Promise<import("./types.js").GameState>}
*/
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<unknown> }} 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<unknown> }} zoosData
* @returns {Promise<import("./types.js").GameState>}
*/
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<import("./types.js").GameState>}
*/
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;
}