import { tryUpgradeWorldMap, tryUpgradePlot } from "./zoo.js"; import { tryUpgradeTruck } from "./conveyor.js"; import { getTruckUpgradeCost } from "./economy.js"; import { playSound } from "./audio.js"; import { t, errorMessage, sellZoneTitle, sellZoneShortLabel } from "./texts-fr.js"; import { GameConfig } from "./config.js"; import { handleWorldMapTruckDrop, handleSellZoneDrop } from "./ui-render-dom-drops.js"; /** * @param {HTMLElement} panelWorld * @param {{ state: import("./types.js").GameState }} setup * @returns {{ worldMapEl: HTMLElement, worldMapTruckEl: HTMLElement, worldMapNpcTrucksEl: HTMLElement }} */ function buildWorldMapWrap(panelWorld, setup) { const { state } = setup; const worldMapWrap = document.createElement("div"); worldMapWrap.className = "world-map-wrap world-map-wrap-square"; const worldMapEl = document.createElement("div"); worldMapEl.className = "world-map world-map-biomes"; const mapLevel = state.worldMapLevel ?? 1; const zoom = Math.min(0.65 + (mapLevel - 1) * 0.2, 1.45); worldMapEl.style.transformOrigin = "50% 50%"; worldMapEl.style.transform = `scale(${zoom})`; worldMapWrap.appendChild(worldMapEl); const worldMapTruckEl = document.createElement("div"); worldMapTruckEl.className = "world-map-truck"; worldMapTruckEl.setAttribute("aria-hidden", "true"); worldMapWrap.appendChild(worldMapTruckEl); const worldMapNpcTrucksEl = document.createElement("div"); worldMapNpcTrucksEl.className = "world-map-trucks"; worldMapNpcTrucksEl.setAttribute("aria-hidden", "true"); worldMapWrap.appendChild(worldMapNpcTrucksEl); panelWorld.appendChild(worldMapWrap); return { worldMapEl, worldMapTruckEl, worldMapNpcTrucksEl }; } /** * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void }} setup * @returns {HTMLElement} */ function buildWorldMapUpgradeZone(setup) { const { state, setState, setError } = setup; const worldMapUpgradeZone = document.createElement("div"); worldMapUpgradeZone.className = "world-map-upgrade-zone"; worldMapUpgradeZone.setAttribute("aria-label", "Agrandir la carte"); worldMapUpgradeZone.title = "Agrandir la carte"; worldMapUpgradeZone.innerHTML = "🗺️Agrandir carte"; worldMapUpgradeZone.setAttribute("role", "button"); worldMapUpgradeZone.setAttribute("tabindex", "0"); worldMapUpgradeZone.addEventListener("click", () => { const [ok, reason] = tryUpgradeWorldMap(state); if (!ok) { setError(String(t.upgradeWorldMapFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); } else { setError(""); playSound("worldMapUpgrade"); } setState(); }); worldMapUpgradeZone.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); worldMapUpgradeZone.click(); } }); return worldMapUpgradeZone; } /** * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void }} setup * @returns {HTMLElement} */ function buildWorldMapTruckDropZone(setup) { const worldMapTruckDropZone = document.createElement("div"); worldMapTruckDropZone.className = "world-map-truck-drop-zone"; worldMapTruckDropZone.setAttribute("aria-label", "Camion pour acheter un œuf"); worldMapTruckDropZone.title = "Glissez un œuf ici pour l'acheter"; worldMapTruckDropZone.innerHTML = "🚚Acheter œuf"; worldMapTruckDropZone.addEventListener("dragover", (e) => { e.preventDefault(); const hasOffer = e.dataTransfer.types.includes("application/x-builazoo-eggtype") || e.dataTransfer.types.includes("application/x-builazoo-baby-offer") || e.dataTransfer.types.includes("application/x-builazoo-animal-offer"); e.dataTransfer.dropEffect = hasOffer ? "copy" : "none"; if (hasOffer) worldMapTruckDropZone.classList.add("dragover"); }); worldMapTruckDropZone.addEventListener("dragleave", () => worldMapTruckDropZone.classList.remove("dragover")); worldMapTruckDropZone.addEventListener("drop", (ev) => handleWorldMapTruckDrop(ev, setup)); return worldMapTruckDropZone; } /** * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void }} setup * @returns {{ worldMapUpgradeZone: HTMLElement, worldMapCounters: HTMLElement, worldMapTruckDropZone: HTMLElement, worldMapActions: HTMLElement }} */ function buildWorldMapActions(setup) { const worldMapActions = document.createElement("div"); worldMapActions.className = "world-map-actions"; const worldMapUpgradeZone = buildWorldMapUpgradeZone(setup); worldMapActions.appendChild(worldMapUpgradeZone); const worldMapCounters = document.createElement("div"); worldMapCounters.className = "world-map-counters"; worldMapCounters.setAttribute("aria-label", "Compteurs carte du monde"); worldMapActions.appendChild(worldMapCounters); const worldMapTruckDropZone = buildWorldMapTruckDropZone(setup); worldMapActions.appendChild(worldMapTruckDropZone); return { worldMapUpgradeZone, worldMapCounters, worldMapTruckDropZone, worldMapActions }; } /** * @param {HTMLElement} panelWorld * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void }} setup * @returns {{ worldMapEl: HTMLElement, worldMapTruckEl: HTMLElement, worldMapNpcTrucksEl: HTMLElement, worldMapUpgradeZone: HTMLElement, worldMapCounters: HTMLElement }} */ export function buildWorldMapSection(panelWorld, setup) { const wrapResult = buildWorldMapWrap(panelWorld, setup); const actionsResult = buildWorldMapActions(setup); panelWorld.appendChild(actionsResult.worldMapActions); return { ...wrapResult, worldMapUpgradeZone: actionsResult.worldMapUpgradeZone, worldMapCounters: actionsResult.worldMapCounters }; } /** * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void }} setup * @returns {HTMLElement} */ function buildPlotUpgradeZone(setup) { const { state, setState, setError } = setup; const plotUpgradeZone = document.createElement("div"); plotUpgradeZone.className = "plot-upgrade-zone"; plotUpgradeZone.setAttribute("aria-label", t.upgradePlot); plotUpgradeZone.title = t.upgradePlot; plotUpgradeZone.innerHTML = "📐Agrandir zoo"; plotUpgradeZone.setAttribute("role", "button"); plotUpgradeZone.setAttribute("tabindex", "0"); plotUpgradeZone.addEventListener("click", () => { const [ok, reason] = tryUpgradePlot(state); if (!ok) { setError(String(t.upgradePlotFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); } else { setError(""); playSound("plotUpgrade"); } setState(); }); plotUpgradeZone.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); plotUpgradeZone.click(); } }); return plotUpgradeZone; } /** * @param {HTMLElement} panelZoo * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, lastActionWasDropRef: { current: boolean }, sellZoneJustDroppedRef: { current: boolean } }} setup * @returns {{ gridEl: HTMLElement, plotUpgradeZone: HTMLElement, sellZone: HTMLElement }} */ export function buildGridSection(panelZoo, setup) { const gridWrap = document.createElement("div"); gridWrap.className = "grid-wrap"; const gridEl = document.createElement("div"); gridEl.className = "grid"; gridWrap.appendChild(gridEl); const plotUpgradeZone = buildPlotUpgradeZone(setup); gridWrap.appendChild(plotUpgradeZone); const sellZone = document.createElement("div"); sellZone.className = "sell-zone"; sellZone.setAttribute("aria-label", sellZoneTitle); sellZone.title = sellZoneTitle; sellZone.innerHTML = "🚚" + sellZoneShortLabel + ""; attachSellZoneListeners(sellZone, setup); gridWrap.appendChild(sellZone); const visitorsLayer = document.createElement("div"); visitorsLayer.className = "visitors-layer"; visitorsLayer.setAttribute("aria-hidden", "true"); gridWrap.appendChild(visitorsLayer); panelZoo.appendChild(gridWrap); return { gridEl, plotUpgradeZone, sellZone }; } /** * @param {HTMLElement} sellZone * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, lastActionWasDropRef: { current: boolean }, sellZoneJustDroppedRef: { current: boolean } }} setup * @returns {void} */ function attachSellZoneListeners(sellZone, setup) { sellZone.addEventListener("dragover", (e) => { e.preventDefault(); const hasCell = e.dataTransfer.types.includes("text/plain"); e.dataTransfer.dropEffect = hasCell ? "move" : "none"; if (hasCell) sellZone.classList.add("dragover"); }); sellZone.addEventListener("dragleave", () => sellZone.classList.remove("dragover")); sellZone.addEventListener("drop", (e) => handleSellZoneDrop(e, setup)); sellZone.addEventListener("click", () => handleSellZoneClick(setup)); } /** * @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, sellZoneJustDroppedRef: { current: boolean } }} setup * @returns {void} */ function handleSellZoneClick(setup) { if (setup.sellZoneJustDroppedRef.current) { setup.sellZoneJustDroppedRef.current = false; return; } const state = setup.state; const truckLevel = state.truckLevel ?? 1; const truckMax = (GameConfig.Truck && GameConfig.Truck.MaxLevel) || 5; if (truckLevel >= truckMax) return; if (state.coins < getTruckUpgradeCost(truckLevel)) return; const [ok, reason] = tryUpgradeTruck(state); if (!ok) { setup.setError(String(t.upgradeConveyorFailed).replace("%s", errorMessage[reason] ?? reason)); playSound("error"); } else { setup.setError(""); playSound("truckUpgrade"); } setup.setState(); }