diff options
author | elioat <elioat@tilde.institute> | 2024-12-27 08:08:29 -0500 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2024-12-27 08:08:29 -0500 |
commit | ea50f9ee0875b9230174996af0f92922f747b974 (patch) | |
tree | 9c419f1af57bd6195a559137d9a004aabc4fb96a | |
parent | 6ea87b4e0a8cddca95c0a4f2b36264e7430e74aa (diff) | |
download | tour-ea50f9ee0875b9230174996af0f92922f747b974.tar.gz |
*
-rw-r--r-- | html/rogue/index.html | 3 | ||||
-rw-r--r-- | html/rogue/js/camera.js | 19 | ||||
-rw-r--r-- | html/rogue/js/config.js | 8 | ||||
-rw-r--r-- | html/rogue/js/game.js | 155 | ||||
-rw-r--r-- | html/rogue/js/hexGrid.js | 68 | ||||
-rw-r--r-- | html/rogue/js/player.js | 30 |
6 files changed, 165 insertions, 118 deletions
diff --git a/html/rogue/index.html b/html/rogue/index.html index 79aac67..46a49e3 100644 --- a/html/rogue/index.html +++ b/html/rogue/index.html @@ -19,6 +19,9 @@ </head> <body> <canvas id="gameCanvas"></canvas> + <script src="js/config.js"></script> + <script src="js/hexGrid.js"></script> + <script src="js/camera.js"></script> <script src="js/player.js"></script> <script src="js/game.js"></script> </body> diff --git a/html/rogue/js/camera.js b/html/rogue/js/camera.js new file mode 100644 index 0000000..e7b2475 --- /dev/null +++ b/html/rogue/js/camera.js @@ -0,0 +1,19 @@ +const Camera = { + x: 0, + y: 0, + + centerOn(hex) { + const pixelCoord = HexGrid.toPixel(hex); + this.x = pixelCoord.x - state.canvas.width / 2; + this.y = pixelCoord.y - state.canvas.height / 2; + }, + + smoothFollow(target) { + const targetPixel = HexGrid.toPixel(target); + const targetX = targetPixel.x - state.canvas.width / 2; + const targetY = targetPixel.y - state.canvas.height / 2; + + this.x += (targetX - this.x) * 0.1; + this.y += (targetY - this.y) * 0.1; + } +}; \ No newline at end of file diff --git a/html/rogue/js/config.js b/html/rogue/js/config.js new file mode 100644 index 0000000..aea067f --- /dev/null +++ b/html/rogue/js/config.js @@ -0,0 +1,8 @@ +const Config = { + colors: { + BACKGROUND: 'rgba(135, 206, 235, 0.3)', // Pale blue + GRID: '#333333', // Off-black for grid lines + PLAYER: 'red', // Player color + HEX_FILL: '#ffffff' // White fill for passable hexes + } +}; \ No newline at end of file diff --git a/html/rogue/js/game.js b/html/rogue/js/game.js index 0dde858..40c8826 100644 --- a/html/rogue/js/game.js +++ b/html/rogue/js/game.js @@ -1,94 +1,47 @@ -// Constants for hex grid -const HEX_SIZE = 20; // Fixed hex size -const HEX_WIDTH = HEX_SIZE * 2; -const HEX_HEIGHT = Math.sqrt(3) * HEX_SIZE; -const GRID_SIZE = 100; // Number of hexes in each dimension -const HEX_COLOR = '#333333'; // Off-black color for hex grid +const FPS = 60; +const FRAME_TIME = 1000 / FPS; +let lastFrameTime = 0; -// Game state const state = { canvas: null, - ctx: null, - camera: { x: 0, y: 0 }, // Camera position in pixel coordinates - hexGrid: [], // Will store hex grid data + ctx: null }; -// Initialize the game function init() { state.canvas = document.getElementById('gameCanvas'); state.ctx = state.canvas.getContext('2d'); - // Initialize player player.init(); function resize() { state.canvas.width = window.innerWidth; state.canvas.height = window.innerHeight; - // Center camera on player initially - centerCameraOnHex(player.position); + Camera.centerOn(player.position); } window.addEventListener('resize', resize); resize(); state.canvas.addEventListener('click', handleClick); - requestAnimationFrame(gameLoop); -} - -// Center camera on a specific hex -function centerCameraOnHex(hex) { - const pixelCoord = hexToPixel(hex); - state.camera.x = pixelCoord.x - state.canvas.width / 2; - state.camera.y = pixelCoord.y - state.canvas.height / 2; -} - -// Smoothly move camera to target position -function updateCamera() { - const currentPos = player.getCurrentPosition(); - const targetPixel = hexToPixel(currentPos); - const targetCameraX = targetPixel.x - state.canvas.width / 2; - const targetCameraY = targetPixel.y - state.canvas.height / 2; - - // Smooth camera movement - state.camera.x += (targetCameraX - state.camera.x) * 0.1; - state.camera.y += (targetCameraY - state.camera.y) * 0.1; -} - -// Convert hex coordinates to pixel coordinates -function hexToPixel(hex) { - const x = HEX_SIZE * (3/2 * hex.q); - const y = HEX_SIZE * (Math.sqrt(3)/2 * hex.q + Math.sqrt(3) * hex.r); - return {x, y}; -} - -// Convert pixel coordinates to hex coordinates (adjusted for camera position) -function pixelToHex(screenX, screenY) { - const x = screenX + state.camera.x; - const y = screenY + state.camera.y; - const q = (2/3 * x) / HEX_SIZE; - const r = (-1/3 * x + Math.sqrt(3)/3 * y) / HEX_SIZE; - return hexRound(q, r); + requestAnimationFrame(gameLoop); } // Draw a single hex -function drawHex(ctx, x, y) { - // Adjust position for camera - const screenX = x - state.camera.x; - const screenY = y - state.camera.y; +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 padding) - if (screenX < -HEX_WIDTH || screenX > state.canvas.width + HEX_WIDTH || - screenY < -HEX_HEIGHT || screenY > state.canvas.height + HEX_HEIGHT) { + // 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.strokeStyle = HEX_COLOR; - ctx.lineWidth = 1; ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = 2 * Math.PI / 6 * i; - const xPos = screenX + HEX_SIZE * Math.cos(angle); - const yPos = screenY + HEX_SIZE * Math.sin(angle); + const xPos = screenX + HexGrid.SIZE * Math.cos(angle); + const yPos = screenY + HexGrid.SIZE * Math.sin(angle); if (i === 0) { ctx.moveTo(xPos, yPos); } else { @@ -96,77 +49,57 @@ function drawHex(ctx, x, y) { } } 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(); } -// Calculate which hexes should be drawn -function calculateViewportHexes() { - const hexes = []; - const halfGrid = Math.floor(GRID_SIZE / 2); - - for (let r = -halfGrid; r < halfGrid; r++) { - for (let q = -halfGrid; q < halfGrid; q++) { - hexes.push({q, r}); - } +function gameLoop(currentTime) { + if (currentTime - lastFrameTime < FRAME_TIME) { + requestAnimationFrame(gameLoop); + return; } - return hexes; -} + + const deltaTime = currentTime - lastFrameTime; + lastFrameTime = currentTime; -// Main game loop -function gameLoop() { + // Clear the entire canvas first state.ctx.clearRect(0, 0, state.canvas.width, state.canvas.height); - // Update game state + // Then fill with background color + state.ctx.fillStyle = Config.colors.BACKGROUND; + state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height); + player.update(); - updateCamera(); + Camera.smoothFollow(player.getCurrentPosition()); - // Draw hex grid - const viewportHexes = calculateViewportHexes(); - viewportHexes.forEach(hex => { - const pixel = hexToPixel(hex); - drawHex(state.ctx, pixel.x, pixel.y); + HexGrid.getViewportHexes().forEach(hex => { + const pixel = HexGrid.toPixel(hex); + drawHex(state.ctx, pixel.x, pixel.y, hex); }); - // Draw player - player.draw(state.ctx, hexToPixel, state.camera, HEX_SIZE); + player.draw(state.ctx, HexGrid.toPixel.bind(HexGrid), Camera, HexGrid.SIZE); requestAnimationFrame(gameLoop); } -// Handle click/tap with camera offset function handleClick(event) { const rect = state.canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; - const hexCoord = pixelToHex(x, y); + const hexCoord = HexGrid.fromPixel(x + Camera.x, y + Camera.y); player.moveTo(hexCoord); } -// Round hex coordinates to nearest hex -function hexRound(q, r) { - let x = q; - let z = r; - let y = -x-z; - - let rx = Math.round(x); - let ry = Math.round(y); - let rz = Math.round(z); - - const x_diff = Math.abs(rx - x); - const y_diff = Math.abs(ry - y); - const z_diff = Math.abs(rz - z); - - if (x_diff > y_diff && x_diff > z_diff) { - rx = -ry-rz; - } else if (y_diff > z_diff) { - ry = -rx-rz; - } else { - rz = -rx-ry; - } - - return {q: rx, r: rz}; -} - -// Start the game init(); \ No newline at end of file diff --git a/html/rogue/js/hexGrid.js b/html/rogue/js/hexGrid.js new file mode 100644 index 0000000..c45c981 --- /dev/null +++ b/html/rogue/js/hexGrid.js @@ -0,0 +1,68 @@ +// Hex grid utilities and calculations +const HexGrid = { + SIZE: 40, + COLOR: Config.colors.GRID, + GRID_SIZE: 10, + IMPASSABLE_COLOR: Config.colors.BACKGROUND, + + get WIDTH() { return this.SIZE * 2 }, + get HEIGHT() { return Math.sqrt(3) * this.SIZE }, + + // Convert hex coordinates to pixel coordinates + toPixel(hex) { + const x = this.SIZE * (3/2 * hex.q); + const y = this.SIZE * (Math.sqrt(3)/2 * hex.q + Math.sqrt(3) * hex.r); + return {x, y}; + }, + + // Convert pixel coordinates to hex coordinates + fromPixel(x, y) { + const q = (2/3 * x) / this.SIZE; + const r = (-1/3 * x + Math.sqrt(3)/3 * y) / this.SIZE; + return this.round(q, r); + }, + + // Round hex coordinates to nearest hex + round(q, r) { + let x = q; + let z = r; + let y = -x-z; + + let rx = Math.round(x); + let ry = Math.round(y); + let rz = Math.round(z); + + const x_diff = Math.abs(rx - x); + const y_diff = Math.abs(ry - y); + const z_diff = Math.abs(rz - z); + + if (x_diff > y_diff && x_diff > z_diff) { + rx = -ry-rz; + } else if (y_diff > z_diff) { + ry = -rx-rz; + } else { + rz = -rx-ry; + } + + return {q: rx, r: rz}; + }, + + // Calculate visible hexes + getViewportHexes() { + const hexes = []; + const halfGrid = Math.floor(this.GRID_SIZE / 2); + + for (let r = -halfGrid; r < halfGrid; r++) { + for (let q = -halfGrid; q < halfGrid; q++) { + hexes.push({q, r}); + } + } + return hexes; + }, + + // Add this method to check if a hex is passable + isPassable(hex) { + const halfGrid = Math.floor(this.GRID_SIZE / 2); + return Math.abs(hex.q) < halfGrid && Math.abs(hex.r) < halfGrid; + } +}; \ No newline at end of file diff --git a/html/rogue/js/player.js b/html/rogue/js/player.js index 5b4a875..9a25715 100644 --- a/html/rogue/js/player.js +++ b/html/rogue/js/player.js @@ -14,6 +14,12 @@ const player = { return this; }, + // Check if a hex coordinate is within grid bounds + isValidHex(hex) { + const halfGrid = Math.floor(HexGrid.GRID_SIZE / 2); + return Math.abs(hex.q) < halfGrid && Math.abs(hex.r) < halfGrid; + }, + // Get neighbors that share an edge with the given hex getEdgeNeighbors(hex) { const directions = [ @@ -25,10 +31,13 @@ const player = { {q: 1, r: -1} // Northeast ]; - return directions.map(dir => ({ - q: hex.q + dir.q, - r: hex.r + dir.r - })); + // Only return neighbors that are within grid bounds + return directions + .map(dir => ({ + q: hex.q + dir.q, + r: hex.r + dir.r + })) + .filter(hex => this.isValidHex(hex)); }, // Find path from current position to target @@ -65,10 +74,17 @@ const player = { // Start moving to a target hex moveTo(targetHex) { + // Only start new movement if we're not already moving and target is valid if (!this.target) { + // Check if target is within grid bounds + if (!this.isValidHex(targetHex)) { + return; // Ignore movement request if target is out of bounds + } + const path = this.findPath(targetHex); if (path) { - this.path = path.slice(1); // Remove starting position + // Filter out any path points that would go out of bounds + this.path = path.slice(1).filter(hex => this.isValidHex(hex)); if (this.path.length > 0) { this.target = this.path.shift(); this.movementProgress = 0; @@ -117,14 +133,14 @@ const player = { const screenX = pixelPos.x - camera.x; const screenY = pixelPos.y - camera.y; - ctx.fillStyle = 'red'; + ctx.fillStyle = Config.colors.PLAYER; ctx.beginPath(); ctx.arc(screenX, screenY, HEX_SIZE/3, 0, Math.PI * 2); ctx.fill(); // Optionally, draw the remaining path if (this.path.length > 0) { - ctx.strokeStyle = 'rgba(255,0,0,0.3)'; + ctx.strokeStyle = Config.colors.PLAYER + '4D'; // 30% opacity version of player color ctx.beginPath(); let lastPos = this.target || this.position; this.path.forEach(point => { |