const FPS = 60; const FRAME_TIME = 1000 / FPS; let lastFrameTime = 0; const state = { canvas: null, ctx: null }; function init() { state.canvas = document.getElementById('gameCanvas'); state.ctx = state.canvas.getContext('2d'); player.init(); function resize() { state.canvas.width = window.innerWidth; state.canvas.height = window.innerHeight; Camera.centerOn(player.position); } window.addEventListener('resize', resize); resize(); state.canvas.addEventListener('click', handleClick); requestAnimationFrame(gameLoop); FogOfWar.init(); Items.init(); } function drawHex(ctx, x, y, hex) { const screenX = x - Camera.x; const screenY = y - Camera.y; // Only draw if hex is visible on screen (with some padding) if (screenX < -HexGrid.WIDTH || screenX > state.canvas.width + HexGrid.WIDTH || screenY < -HexGrid.HEIGHT || screenY > state.canvas.height + HexGrid.HEIGHT) { return; } ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = 2 * Math.PI / 6 * i; const xPos = screenX + HexGrid.SIZE * Math.cos(angle); const yPos = screenY + HexGrid.SIZE * Math.sin(angle); if (i === 0) { ctx.moveTo(xPos, yPos); } else { ctx.lineTo(xPos, yPos); } } ctx.closePath(); // Fill hex with appropriate color if (HexGrid.isPassable(hex)) { ctx.fillStyle = Config.colors.HEX_FILL; } else { ctx.fillStyle = Config.colors.BACKGROUND; } ctx.fill(); // Draw border ctx.strokeStyle = HexGrid.COLOR; ctx.lineWidth = 1; ctx.stroke(); } function gameLoop(currentTime) { if (currentTime - lastFrameTime < Config.game.FRAME_TIME) { requestAnimationFrame(gameLoop); return; } const deltaTime = currentTime - lastFrameTime; lastFrameTime = currentTime; // Clear the entire canvas first state.ctx.clearRect(0, 0, state.canvas.width, state.canvas.height); // Then fill with background color state.ctx.fillStyle = Config.colors.BACKGROUND; state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height); player.update(); Camera.smoothFollow(player.getCurrentPosition()); // Update fog of war when player moves if (player.hasMoved) { FogOfWar.updateVisibility(player.position); player.hasMoved = false; } // Draw layers in correct order HexGrid.getViewportHexes().forEach(hex => { const pixel = HexGrid.toPixel(hex); drawHex(state.ctx, pixel.x, pixel.y, hex); }); // Draw items Items.draw(state.ctx, HexGrid.toPixel.bind(HexGrid), Camera, HexGrid.SIZE); // Draw player player.draw(state.ctx, HexGrid.toPixel.bind(HexGrid), Camera, HexGrid.SIZE); // Draw fog of war FogOfWar.draw(state.ctx); // Draw inventory UI last InventoryUI.draw(state.ctx); requestAnimationFrame(gameLoop); } function handleClick(event) { if (InventoryUI.isOpen) { InventoryUI.toggleInventory(); return; } const rect = state.canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const hexCoord = HexGrid.fromPixel(x + Camera.x, y + Camera.y); // Check if clicking on player's position if (hexCoord.q === player.position.q && hexCoord.r === player.position.r) { InventoryUI.toggleInventory(); } else { player.moveTo(hexCoord); } } init();