if (typeof FAIRHOPPER_WS_SERVER === "undefined") { var FAIRHOPPER_WS_SERVER = "ws://127.0.0.1:8011"; } let ws = null; let playerOnDestinationModal = null; const BOARD_ICONS = { PLAYER: "😀", PLAYER_ON_DESTINATION: "😎", OBSTACLE: "🔥", DESTINATION: "🏠", }; function createBoard(board) { let html = ""; for (let y = 0; y < board.height; y++) { let colHtml = ""; for (let x = 0; x < board.width; x++) { colHtml += `
 
`; } html += `
${colHtml}
`; } document.getElementById("board-content").innerHTML = html; } function findCell(position) { return document.getElementById(`cell-${position.x}-${position.y}`); } function renderCellContent(position, content) { const cell = findCell(position); if (cell) { cell.innerText = content; } } function renderPlayerList(players) { document.getElementById("players-content").innerHTML = players .filter((player) => player.active) .map((player) => { const onDestination = player.state === "ON_DESTINATION"; return `
  • ${player.name} (${player.move_count}) ${onDestination ? "✅" : ""}
  • `; }) .join(""); } function renderPlayers(players) { players .filter((player) => player.active) .forEach((player) => { const cell = findCell(player.position); const onDestination = player.state === "ON_DESTINATION"; const playerIcon = onDestination ? BOARD_ICONS.PLAYER_ON_DESTINATION : BOARD_ICONS.PLAYER; if (cell) { cell.innerHTML = `
    ${player.name}
    ${playerIcon} `; } }); } function getLayerObjectsOfType(layers, type) { let objects = []; layers.forEach((layer) => { objects = objects.concat(layer.objects.filter((obj) => obj.type === type)); }); return objects; } function renderObstacles(layers) { const objects = getLayerObjectsOfType(layers, "OBSTACLE"); objects.forEach((obj) => { renderCellContent(obj.position, BOARD_ICONS.OBSTACLE); }); } function renderDestination(position) { renderCellContent(position, BOARD_ICONS.DESTINATION); } function renderGameDump(data) { createBoard(data.board); renderObstacles(data.layers); renderDestination(data.destination.position); renderPlayerList(data.players); renderPlayers(data.players); } function playerReachedDestination(data) { const dlgElement = document.getElementById("player-on-destination-modal"); dlgElement.querySelector(".player-name").textContent = data.player.name; dlgElement.querySelector(".move-count").textContent = data.player.move_count; playerOnDestinationModal.show(); } function productSelectionDone() { playerOnDestinationModal.hide(); } function wsConnect() { console.log("Attempting to connect to", FAIRHOPPER_WS_SERVER); ws = new WebSocket(FAIRHOPPER_WS_SERVER); ws.onopen = () => { console.log("WS connected"); }; ws.onmessage = (e) => { const wsMessage = JSON.parse(e.data); console.log("WS message received:", wsMessage); switch (wsMessage.message) { case "game_dump": renderGameDump(wsMessage.data); break; case "player_reached_destination": playerReachedDestination(wsMessage.data); break; case "product_selection_done": productSelectionDone(); break; default: console.error("Unknown message:", wsMessage); } }; ws.onclose = (e) => { ws = null; setTimeout(() => { wsConnect(); }, 1000); }; ws.onerror = (err) => { console.error("Socket encountered error:", err.message, "Closing socket"); ws.close(); }; } function finishProductSelection() { if (!ws) { return; } const wsMessage = { message: "product_selection_done", data: null, }; ws.send(JSON.stringify(wsMessage)); } window.onload = () => { const dlgElement = document.getElementById("player-on-destination-modal"); playerOnDestinationModal = new bootstrap.Modal(dlgElement); document.getElementById("finish-product-selection").onclick = () => { finishProductSelection(); }; wsConnect(); };