// Rôle : charge et normalise les données publiques des jeux. export const INITIAL_MHWILDS_STATE = { loaded: false, loading: false, error: "", translations: {}, monsters: [], endemic: [], lists: [], filterOptions: { monsters: [], endemic: [] }, filterOptionKeys: { monsters: "", endemic: "" } }; export const INITIAL_DIABLO4_STATE = { loaded: false, loading: false, error: "", affixes: [], filterOptions: { affixes: [] }, categoryMap: {}, filterOptionKeys: { affixes: "" } }; export async function loadMhwildsData() { const [monstersResponse, endemicResponse, listIndexResponse, translationsResponse] = await Promise.all([ fetch("/data/mhwilds/monsters.json"), fetch("/data/mhwilds/endemic_life.json"), fetch("/data/mhwilds/lists/index.json"), fetch("/data/mhwilds/i18n/fr.json") ]); if (!monstersResponse.ok || !endemicResponse.ok || !listIndexResponse.ok || !translationsResponse.ok) { throw new Error("Impossible de charger les données Monster Hunter Wilds."); } const [monstersJson, endemicJson, listIndexJson, translations] = await Promise.all([ monstersResponse.json(), endemicResponse.json(), listIndexResponse.json(), translationsResponse.json() ]); const lists = await loadGameLists("mhwilds", listIndexJson); const monsterFilterKey = Object.keys(monstersJson).find((key) => key !== "monsters") || ""; const endemicFilterKey = Object.keys(endemicJson).find((key) => key !== "endemicLife" && key !== "aquaticLife") || ""; return { loaded: true, loading: false, error: "", translations, monsters: monstersJson.monsters || [], endemic: [...(endemicJson.endemicLife || []), ...(endemicJson.aquaticLife || [])], lists, filterOptions: { monsters: monstersJson[monsterFilterKey] || [], endemic: endemicJson[endemicFilterKey] || [] }, filterOptionKeys: { monsters: monsterFilterKey, endemic: endemicFilterKey } }; } async function loadGameLists(gameId, indexPayload) { const entries = Array.isArray(indexPayload?.lists) ? indexPayload.lists : []; const results = await Promise.all(entries.map(async (entry) => { const response = await fetch(`/data/${gameId}/lists/${entry.file}`); if (!response.ok) throw new Error(`Impossible de charger la liste ${entry.file}.`); const payload = await response.json(); return normalizeGameListFile(entry, payload); })); return results; } function normalizeGameListFile(entry, payload) { const id = String(entry?.id || entry?.file || "").replace(/\.json$/i, ""); const categories = Object.entries(payload || {}).filter(([, items]) => Array.isArray(items)).map(([categoryId, items]) => ({ id: categoryId, title: formatListTitle(categoryId), items: Array.isArray(items) ? items.map(normalizeGameListItem).filter((item) => item.name) : [] })).filter((category) => category.items.length); return { id, title: payload?.titre || entry?.title || formatListTitle(id), file: entry?.file || `${id}.json`, categories, itemCount: categories.reduce((count, category) => count + category.items.length, 0) }; } function normalizeGameListItem(item) { const hasQuantity = item?.quantite !== undefined || item?.quantity !== undefined; const quantity = Number.parseInt(item?.quantite ?? item?.quantity ?? 1, 10); return { name: String(item?.nom || item?.name || item?.label || "").trim(), description: String(item?.description || item?.quete || item?.quest || item?.source || "").trim(), quantity: Number.isFinite(quantity) ? quantity : 1, hasQuantity }; } function formatListTitle(value) { return String(value || "") .replace(/_/g, " ") .replace(/\b\p{L}/gu, (letter) => letter.toUpperCase()); } export async function loadDiablo4Data() { const response = await fetch("/data/diablo4/affixes_types.json"); if (!response.ok) throw new Error("Impossible de charger les données Diablo IV."); const payload = await response.json(); const filterKey = Object.keys(payload).find((key) => key !== "affixes") || ""; const categories = payload[filterKey] || []; return { loaded: true, loading: false, error: "", affixes: payload.affixes || [], filterOptions: { affixes: categories }, categoryMap: Object.fromEntries(categories.map((category) => [category.id, category])), filterOptionKeys: { affixes: filterKey } }; }