diff options
-rw-r--r-- | html/broughlike/broughlike.js | 150 | ||||
-rw-r--r-- | html/broughlike/config.js | 32 | ||||
-rw-r--r-- | html/broughlike/index.html | 2 |
3 files changed, 93 insertions, 91 deletions
diff --git a/html/broughlike/broughlike.js b/html/broughlike/broughlike.js index e492a0c..5f0863f 100644 --- a/html/broughlike/broughlike.js +++ b/html/broughlike/broughlike.js @@ -1,45 +1,16 @@ -const COLORS = { - grid: '#2d2d2d', - walls: '#2d2d2d', - exit: 'teal', - diamond: 'gold', - pentagon: 'blueviolet', - player: 'rgba(0, 237, 209, ', - enemy: 'rgba(255, 155, 28, ', - boss: 'rgba(255, 14, 0, ', - combatDotPlayer: '#00edd1', - combatDotEnemy: '#ff731c', - combatDotBoss: '#b70030' -}; - -const GRID_SIZE = 6; -const PLAYER_HEALTH = 12; -const PLAYER_MAX_HEALTH = 16; -const PLAYER_BASE_DAMAGE = 1; -const ENEMY_CHASE_DISTANCE = 4; -const ENEMY_BOSS_OCCURRENCE = 10; -const MIN_ENEMIES_ON_LEVEL = 1; -const MAX_ENEMIES_ON_LEVEL = 4; -const MAX_ENEMY_HEALTH = 7; -const MIN_ENEMY_HEALTH = 2; -const WALLS_MIN = 7; -const WALLS_MAX = 20; -const ITEMS_MIN = 1; -const ITEMS_MAX = 3; -const DOTS_PER_HIT = 7; - -let highScore = localStorage.getItem('highScore') || 0; +import { CONFIG, COLORS } from './config.js'; const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); -let tileSize = canvas.width / GRID_SIZE; +let tileSize = canvas.width / CONFIG.GRID_SIZE; +let highScore = localStorage.getItem('highScore') || 0; const player = { x: 0, y: 0, - health: PLAYER_HEALTH, + health: CONFIG.PLAYER_HEALTH, score: 0, - damage: PLAYER_BASE_DAMAGE, + damage: CONFIG.PLAYER_BASE_DAMAGE, totalDamageDone: 0, totalDamageTaken: 0, cellsTraveled: 0, @@ -47,7 +18,7 @@ const player = { itemsCollected: 0, }; -const exit = { x: Math.floor(Math.random() * GRID_SIZE), y: Math.floor(Math.random() * GRID_SIZE) }; +const exit = { x: Math.floor(Math.random() * CONFIG.GRID_SIZE), y: Math.floor(Math.random() * CONFIG.GRID_SIZE) }; let walls = []; let enemies = []; let items = []; @@ -55,8 +26,8 @@ let combatDots = {}; function isValidMove(newX, newY) { return ( - newX >= 0 && newX < GRID_SIZE && - newY >= 0 && newY < GRID_SIZE && + newX >= 0 && newX < CONFIG.GRID_SIZE && + newY >= 0 && newY < CONFIG.GRID_SIZE && !walls.some(wall => wall.x === newX && wall.y === newY) ); } @@ -64,8 +35,8 @@ function isValidMove(newX, newY) { function generateExit() { let distance = 0; do { - exit.x = Math.floor(Math.random() * GRID_SIZE); - exit.y = Math.floor(Math.random() * GRID_SIZE); + exit.x = Math.floor(Math.random() * CONFIG.GRID_SIZE); + exit.y = Math.floor(Math.random() * CONFIG.GRID_SIZE); distance = Math.abs(exit.x - player.x) + Math.abs(exit.y - player.y); } while (distance < 4); } @@ -75,13 +46,13 @@ function generateEnemies() { // Generate between 0 and MAX_ENEMIES_ON_LEVEL enemies if the player's score is 4 or lower // Generate between MIN_ENEMIES_ON_LEVEL and MAX_ENEMIES_ON_LEVEL enemies if the player's score is 5 or higher const numEnemies = player.score > 4 - ? Math.floor(Math.random() * (MAX_ENEMIES_ON_LEVEL - MIN_ENEMIES_ON_LEVEL + 1)) + MIN_ENEMIES_ON_LEVEL - : Math.floor(Math.random() * (MAX_ENEMIES_ON_LEVEL + 1)); + ? Math.floor(Math.random() * (CONFIG.MAX_ENEMIES_ON_LEVEL - CONFIG.MIN_ENEMIES_ON_LEVEL + 1)) + CONFIG.MIN_ENEMIES_ON_LEVEL + : Math.floor(Math.random() * (CONFIG.MAX_ENEMIES_ON_LEVEL + 1)); for (let i = 0; i < numEnemies; i++) { let enemyX, enemyY; do { - enemyX = Math.floor(Math.random() * GRID_SIZE); - enemyY = Math.floor(Math.random() * GRID_SIZE); + enemyX = Math.floor(Math.random() * CONFIG.GRID_SIZE); + enemyY = Math.floor(Math.random() * CONFIG.GRID_SIZE); } while ( (enemyX === player.x && enemyY === player.y) || (enemyX === exit.x && enemyY === exit.y) || @@ -92,12 +63,12 @@ function generateEnemies() { color: COLORS.enemy, x: enemyX, y: enemyY, - health: Math.floor(Math.random() * (MAX_ENEMY_HEALTH - MIN_ENEMY_HEALTH + 1)) + MIN_ENEMY_HEALTH + health: Math.floor(Math.random() * (CONFIG.MAX_ENEMY_HEALTH - CONFIG.MIN_ENEMY_HEALTH + 1)) + CONFIG.MIN_ENEMY_HEALTH }); } // Generate a boss enemy every ENEMY_BOSS_OCCURRENCE levels - if (player.score % ENEMY_BOSS_OCCURRENCE === 0 && player.score > 0) { + if (player.score % CONFIG.ENEMY_BOSS_OCCURRENCE === 0 && player.score > 0) { let bossX, bossY; do { bossX = exit.x; // Boss enemies always appear at the exit @@ -110,17 +81,17 @@ function generateEnemies() { color: COLORS.boss, x: bossX, y: bossY, - health: MAX_ENEMY_HEALTH + 2 + health: CONFIG.MAX_ENEMY_HEALTH + 2 }); } } function generateWallsNaive() { walls = []; - let numWalls = Math.floor(Math.random() * (WALLS_MAX - WALLS_MIN + 1)) + WALLS_MIN; + let numWalls = Math.floor(Math.random() * (CONFIG.WALLS_MAX - CONFIG.WALLS_MIN + 1)) + CONFIG.WALLS_MIN; while (walls.length < numWalls) { - const wallX = Math.floor(Math.random() * GRID_SIZE); - const wallY = Math.floor(Math.random() * GRID_SIZE); + const wallX = Math.floor(Math.random() * CONFIG.GRID_SIZE); + const wallY = Math.floor(Math.random() * CONFIG.GRID_SIZE); if ( (wallX === player.x && wallY === player.y) || // Check that a wall is not placed on the starting position @@ -141,9 +112,9 @@ function generateWallsNaive() { function generateWallsDrunkardsWalk() { walls = []; - const numWalls = Math.floor(Math.random() * (WALLS_MAX - WALLS_MIN + 1)) + WALLS_MIN; - let wallX = Math.floor(Math.random() * GRID_SIZE); - let wallY = Math.floor(Math.random() * GRID_SIZE); + const numWalls = Math.floor(Math.random() * (CONFIG.WALLS_MAX - CONFIG.WALLS_MIN + 1)) + CONFIG.WALLS_MIN; + let wallX = Math.floor(Math.random() * CONFIG.GRID_SIZE); + let wallY = Math.floor(Math.random() * CONFIG.GRID_SIZE); while (walls.length < numWalls) { if ( @@ -160,9 +131,9 @@ function generateWallsDrunkardsWalk() { const direction = Math.floor(Math.random() * 4); switch (direction) { case 0: wallX = Math.max(0, wallX - 1); break; // Move left - case 1: wallX = Math.min(GRID_SIZE - 1, wallX + 1); break; // Move right + case 1: wallX = Math.min(CONFIG.GRID_SIZE - 1, wallX + 1); break; // Move right case 2: wallY = Math.max(0, wallY - 1); break; // Move up - case 3: wallY = Math.min(GRID_SIZE - 1, wallY + 1); break; // Move down + case 3: wallY = Math.min(CONFIG.GRID_SIZE - 1, wallY + 1); break; // Move down } } @@ -173,11 +144,11 @@ function generateWallsDrunkardsWalk() { function generateWallsCellularAutomata() { walls = []; - const map = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0)); + const map = Array(CONFIG.GRID_SIZE).fill().map(() => Array(CONFIG.GRID_SIZE).fill(0)); // Initialize a map with random walls - for (let x = 0; x < GRID_SIZE; x++) { - for (let y = 0; y < GRID_SIZE; y++) { + for (let x = 0; x < CONFIG.GRID_SIZE; x++) { + for (let y = 0; y < CONFIG.GRID_SIZE; y++) { if (Math.random() < 0.4) { map[x][y] = 1; } @@ -186,11 +157,11 @@ function generateWallsCellularAutomata() { for (let i = 0; i < 4; i++) { // Create a new map to store the next state of the cellular automata - const newMap = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0)); + const newMap = Array(CONFIG.GRID_SIZE).fill().map(() => Array(CONFIG.GRID_SIZE).fill(0)); // Iterate over each cell in the grid - for (let x = 0; x < GRID_SIZE; x++) { - for (let y = 0; y < GRID_SIZE; y++) { + for (let x = 0; x < CONFIG.GRID_SIZE; x++) { + for (let y = 0; y < CONFIG.GRID_SIZE; y++) { // Count the number of neighboring walls const neighbors = countNeighbors(map, x, y); @@ -209,8 +180,8 @@ function generateWallsCellularAutomata() { } // Convert map to walls array - for (let x = 0; x < GRID_SIZE; x++) { - for (let y = 0; y < GRID_SIZE; y++) { + for (let x = 0; x < CONFIG.GRID_SIZE; x++) { + for (let y = 0; y < CONFIG.GRID_SIZE; y++) { if (map[x][y] === 1 && (x !== player.x || y !== player.y) && (x !== exit.x || y !== exit.y) && @@ -233,7 +204,7 @@ function countNeighbors(map, x, y) { if (dx === 0 && dy === 0) continue; const nx = x + dx; const ny = y + dy; - if (nx >= 0 && nx < GRID_SIZE && ny >= 0 && ny < GRID_SIZE) { + if (nx >= 0 && nx < CONFIG.GRID_SIZE && ny >= 0 && ny < CONFIG.GRID_SIZE) { count += map[nx][ny]; } else { count++; // Consider out-of-bounds bits as walls @@ -245,15 +216,14 @@ function countNeighbors(map, x, y) { function generateWallsRSP() { walls = []; - const MIN_ROOM_SIZE = 2; function splitNode(x, y, width, height) { const splitHorizontally = Math.random() > 0.5; - const max = (splitHorizontally ? height : width) - MIN_ROOM_SIZE; + const max = (splitHorizontally ? height : width) - CONFIG.MIN_ROOM_SIZE; - if (max <= MIN_ROOM_SIZE) return [{ x, y, width, height }]; + if (max <= CONFIG.MIN_ROOM_SIZE) return [{ x, y, width, height }]; - const split = Math.floor(Math.random() * (max - MIN_ROOM_SIZE)) + MIN_ROOM_SIZE; + const split = Math.floor(Math.random() * (max - CONFIG.MIN_ROOM_SIZE)) + CONFIG.MIN_ROOM_SIZE; if (splitHorizontally) { return [ @@ -276,7 +246,7 @@ function generateWallsRSP() { return { x: roomX, y: roomY, width: roomWidth, height: roomHeight }; } - const nodes = splitNode(0, 0, GRID_SIZE, GRID_SIZE); + const nodes = splitNode(0, 0, CONFIG.GRID_SIZE, CONFIG.GRID_SIZE); const rooms = nodes.map(createRoom); rooms.forEach(room => { @@ -313,12 +283,12 @@ function generateWalls() { function generateItems() { items = []; - const numItems = Math.floor(Math.random() * (ITEMS_MAX - ITEMS_MIN + 1)) + ITEMS_MIN; + const numItems = Math.floor(Math.random() * (CONFIG.ITEMS_MAX - CONFIG.ITEMS_MIN + 1)) + CONFIG.ITEMS_MIN; for (let i = 0; i < numItems; i++) { let itemX, itemY; do { - itemX = Math.floor(Math.random() * GRID_SIZE); - itemY = Math.floor(Math.random() * GRID_SIZE); + itemX = Math.floor(Math.random() * CONFIG.GRID_SIZE); + itemY = Math.floor(Math.random() * CONFIG.GRID_SIZE); } while ( (itemX === player.x && itemY === player.y) || (itemX === exit.x && itemY === exit.y) || @@ -332,10 +302,10 @@ function generateItems() { } function isPassable() { - const visited = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(false)); + const visited = Array(CONFIG.GRID_SIZE).fill().map(() => Array(CONFIG.GRID_SIZE).fill(false)); function dfs(x, y) { - if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) return false; // Are the coordinates in bounds? + if (x < 0 || x >= CONFIG.GRID_SIZE || y < 0 || y >= CONFIG.GRID_SIZE) return false; // Are the coordinates in bounds? if (visited[x][y]) return false; if (walls.some(wall => wall.x === x && wall.y === y)) return false; visited[x][y] = true; @@ -349,8 +319,8 @@ function isPassable() { function drawGrid() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.strokeStyle = COLORS.grid; - for (let row = 0; row < GRID_SIZE; row++) { - for (let col = 0; col < GRID_SIZE; col++) { + for (let row = 0; row < CONFIG.GRID_SIZE; row++) { + for (let col = 0; col < CONFIG.GRID_SIZE; col++) { ctx.strokeRect(col * tileSize, row * tileSize, tileSize, tileSize); } } @@ -419,9 +389,9 @@ function drawEnemies() { enemies.forEach(enemy => { const x = enemy.x * tileSize + tileSize / 2; const y = enemy.y * tileSize + tileSize / 2; - const opacity = enemy.health / MAX_ENEMY_HEALTH; // Opacity based on health + const opacity = enemy.health / CONFIG.MAX_ENEMY_HEALTH; // Opacity based on health const radius = tileSize / 3; - const damageTaken = MAX_ENEMY_HEALTH - enemy.health; + const damageTaken = CONFIG.MAX_ENEMY_HEALTH - enemy.health; ctx.beginPath(); ctx.arc(x, y, radius, 0, 2 * Math.PI); @@ -436,7 +406,7 @@ function drawPlayer() { const x = player.x * tileSize + tileSize / 2; const y = player.y * tileSize + tileSize / 2; const radius = tileSize / 3; - const playerOpacity = player.health / PLAYER_HEALTH; // Opacity based on health + const playerOpacity = player.health / CONFIG.PLAYER_HEALTH; // Opacity based on health ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = (Math.PI / 3) * i; @@ -479,7 +449,7 @@ function handleItemCollection() { console.log("Collected diamond! +3 damage on this level."); } else if (collectedItem.type === 'pentagon') { const healAmount = Math.floor(Math.random() * 2) + 1; - player.health = Math.min(player.health + healAmount, PLAYER_MAX_HEALTH); + player.health = Math.min(player.health + healAmount, CONFIG.PLAYER_MAX_HEALTH); console.log("Collected pentagon! Healed " + healAmount + " health."); } items = items.filter(item => item !== collectedItem); // Remove collected item @@ -500,12 +470,12 @@ function addCombatDots(x, y, color) { ]; cellsToFill.forEach(([cellX, cellY]) => { - if (cellX >= 0 && cellX < GRID_SIZE && cellY >= 0 && cellY < GRID_SIZE) { + if (cellX >= 0 && cellX < CONFIG.GRID_SIZE && cellY >= 0 && cellY < CONFIG.GRID_SIZE) { const key = `${cellX},${cellY}`; if (!combatDots[key]) { combatDots[key] = []; } - for (let i = 0; i < DOTS_PER_HIT; i++) { + for (let i = 0; i < CONFIG.DOTS_PER_HIT; i++) { combatDots[key].push({ x: Math.random() * tileSize, y: Math.random() * tileSize, @@ -539,11 +509,11 @@ function movePlayer(dx, dy) { function moveEnemies() { enemies.forEach(enemy => { const distanceToPlayer = Math.abs(enemy.x - player.x) + Math.abs(enemy.y - player.y); - const distanceToExit = Math.abs(enemy.x - exit.x) + Math.abs(enemy.y - exit.y); + const distanceToExit = Math.abs(enemy.x - exit.x) + Math.abs(enemy.y - exit.y); // FIXME: necessary? // If the enemy is closer to the exit than the player, move towards the exit // Bosses are more aggressive about chasing the player - const target = distanceToPlayer <= (enemy.isBoss ? ENEMY_CHASE_DISTANCE + 2 : ENEMY_CHASE_DISTANCE) ? player : exit; + const target = distanceToPlayer <= (enemy.isBoss ? CONFIG.ENEMY_CHASE_DISTANCE + 2 : CONFIG.ENEMY_CHASE_DISTANCE) ? player : exit; const path = findPath(enemy, target); if (path.length > 1) { @@ -590,8 +560,8 @@ function findPath(start, goal) { // Check if the new position is within the level and if it is passable if ( - newX >= 0 && newX < GRID_SIZE && - newY >= 0 && newY < GRID_SIZE && + newX >= 0 && newX < CONFIG.GRID_SIZE && + newY >= 0 && newY < CONFIG.GRID_SIZE && // Have we already been here? !visited.has(key) && // Is it a wall? @@ -659,7 +629,7 @@ function handleDamage(player, enemy) { enemies = enemies.filter(e => e !== enemy); if (enemy.isBoss) { // Defeating a boss restores 3 player health - player.health = Math.min(player.health + 3, PLAYER_MAX_HEALTH); + player.health = Math.min(player.health + 3, CONFIG.PLAYER_MAX_HEALTH); console.log("Defeated a boss! Healed " + 3 + " health."); } } @@ -679,8 +649,8 @@ function resetGame() { if (canvas.classList.contains('shake')) { canvas.classList.remove('shake'); } - player.health = PLAYER_HEALTH; - player.damage = PLAYER_BASE_DAMAGE; + player.health = CONFIG.PLAYER_HEALTH; + player.damage = CONFIG.PLAYER_BASE_DAMAGE; player.bonusDamageTurns = 0; player.totalDamageDone = 0; player.totalDamageTaken = 0; @@ -701,7 +671,7 @@ function resetGame() { function checkPlayerAtExit() { if (player.x === exit.x && player.y === exit.y) { player.score += 1; - player.damage = PLAYER_BASE_DAMAGE; + player.damage = CONFIG.PLAYER_BASE_DAMAGE; console.groupCollapsed("Level complete! " + player.score); console.log("Score: " + player.score); console.log("Current health: " + player.health); @@ -794,7 +764,7 @@ const resizeCanvas = () => { const rect = canvas.getBoundingClientRect(); canvas.width = rect.width; canvas.height = rect.height; - tileSize = canvas.width / GRID_SIZE; // Update tile size based on canvas dimensions + tileSize = canvas.width / CONFIG.GRID_SIZE; // Update tile size based on canvas dimensions render(); }; diff --git a/html/broughlike/config.js b/html/broughlike/config.js new file mode 100644 index 0000000..5897c27 --- /dev/null +++ b/html/broughlike/config.js @@ -0,0 +1,32 @@ +export const COLORS = { + grid: '#2d2d2d', + walls: '#2d2d2d', + exit: 'teal', + diamond: 'gold', + pentagon: 'blueviolet', + player: 'rgba(0, 237, 209, ', + enemy: 'rgba(255, 155, 28, ', + boss: 'rgba(255, 14, 0, ', + combatDotPlayer: '#00edd1', + combatDotEnemy: '#ff731c', + combatDotBoss: '#b70030' +}; + +export const CONFIG = { + GRID_SIZE: 6, + PLAYER_HEALTH: 12, + PLAYER_MAX_HEALTH: 16, + PLAYER_BASE_DAMAGE: 1, + ENEMY_CHASE_DISTANCE: 4, + ENEMY_BOSS_OCCURRENCE: 10, + MIN_ENEMIES_ON_LEVEL: 1, + MAX_ENEMIES_ON_LEVEL: 4, + MAX_ENEMY_HEALTH: 7, + MIN_ENEMY_HEALTH: 2, + MIN_ROOM_SIZE: 4, + WALLS_MIN: 7, + WALLS_MAX: 20, + ITEMS_MIN: 1, + ITEMS_MAX: 3, + DOTS_PER_HIT: 7 +}; diff --git a/html/broughlike/index.html b/html/broughlike/index.html index 62a43ac..4de1d9d 100644 --- a/html/broughlike/index.html +++ b/html/broughlike/index.html @@ -59,6 +59,6 @@ </div> </div> <canvas id="gameCanvas"></canvas> - <script src="./broughlike.js"></script> + <script type="module" src="./broughlike.js"></script> </body> </html> \ No newline at end of file |