diff options
Diffstat (limited to 'html/broughlike/index.html')
-rw-r--r-- | html/broughlike/index.html | 548 |
1 files changed, 37 insertions, 511 deletions
diff --git a/html/broughlike/index.html b/html/broughlike/index.html index ef547e7..4de1d9d 100644 --- a/html/broughlike/index.html +++ b/html/broughlike/index.html @@ -8,6 +8,7 @@ <style> body { background-color: #f0f0f0; + font-size: x-large; } canvas { width: 90vw; @@ -19,520 +20,45 @@ margin: 0 auto; background-color: #f0f0f0; } + @keyframes shake { + 0% { transform: translate(0.5px, 0.5px) rotate(0deg); } + 10% { transform: translate(-0.5px, -1px) rotate(-0.5deg); } + 20% { transform: translate(-1.5px, 0px) rotate(0.5deg); } + 30% { transform: translate(1.5px, 1px) rotate(0deg); } + 40% { transform: translate(0.5px, -0.5px) rotate(0.5deg); } + 50% { transform: translate(-0.5px, 1px) rotate(-0.5deg); } + 60% { transform: translate(-1.5px, 0.5px) rotate(0deg); } + 70% { transform: translate(1.5px, 0.5px) rotate(-0.5deg); } + 80% { transform: translate(-0.5px, -0.5px) rotate(0.5deg); } + 90% { transform: translate(0.5px, 1px) rotate(0deg); } + 100% { transform: translate(0.5px, -1px) rotate(-0.5deg); } + } + .shake { + animation: shake 0.5s; + animation-iteration-count: 1; + animation-timing-function: steps(10, end); + } + .header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 0.5em; + background-color: #f0f0f0; + } + .toggleButton { + padding: 0.75em; + margin: 0.5em; + } </style> </head> <body> - <p><a href="about.html">About</a></p> + <div class="header"> + <p><a href="about.html">About</a></p> + <div> + <button class="toggleButton" id="toggleShake" onclick="toggleShake()">Turn Shake Off</button> + </div> + </div> <canvas id="gameCanvas"></canvas> - <script> - - const COLORS = { - grid: '#2d2d2d', - walls: '#2d2d2d', - exit: 'teal', - diamond: 'gold', - pentagon: 'blueviolet', - player: 'rgba(0, 237, 209, ', - enemy: 'rgba(255, 155, 28, ', - combatDotPlayer: '#00edd1', - combatDotEnemy: '#ff731c' - }; - - const GRID_SIZE = 6; - const PLAYER_HEALTH = 10; - const PLAYER_MAX_HEALTH = 12; - const PLAYER_BASE_DAMAGE = 1; - const ENEMY_CHASE_DISTANCE = 4; - const MAX_ENEMIES_ON_LEVEL = 4; - const MAX_ENEMY_HEALTH = 7; - const MIN_ENEMY_HEALTH = 2; - const WALLS_MIN = 5; - const WALLS_MAX = 20; - const ITEMS_MIN = 0; - const ITEMS_MAX = 3; - const DOTS_PER_HIT = 5; - - const canvas = document.getElementById('gameCanvas'); - const ctx = canvas.getContext('2d'); - let tileSize = canvas.width / GRID_SIZE; - - const player = { - x: 0, - y: 0, - health: PLAYER_HEALTH, - score: 0, - damage: PLAYER_BASE_DAMAGE, - totalDamageDone: 0, - totalDamageTaken: 0, - cellsTraveled: 0 - }; - - const exit = { x: Math.floor(Math.random() * GRID_SIZE), y: Math.floor(Math.random() * GRID_SIZE) }; - let walls = []; - let enemies = []; - let items = []; - let combatDots = {}; - - function isValidMove(newX, newY) { - return ( - newX >= 0 && newX < GRID_SIZE && - newY >= 0 && newY < GRID_SIZE && - !walls.some(wall => wall.x === newX && wall.y === newY) - ); - } - - function generateExit() { - let distance = 0; - do { - exit.x = Math.floor(Math.random() * GRID_SIZE); - exit.y = Math.floor(Math.random() * GRID_SIZE); - distance = Math.abs(exit.x - player.x) + Math.abs(exit.y - player.y); - } while (distance < 4); - } - - function generateEnemies() { - enemies = []; - const numEnemies = Math.floor(Math.random() * (MAX_ENEMIES_ON_LEVEL + 1)); // Between 0 and the constant value. - 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); - } while ( - (enemyX === player.x && enemyY === player.y) || - (enemyX === exit.x && enemyY === exit.y) || - walls.some(wall => wall.x === enemyX && wall.y === enemyY) - ); - enemies.push({ - x: enemyX, - y: enemyY, - health: Math.floor(Math.random() * (MAX_ENEMY_HEALTH - MIN_ENEMY_HEALTH + 1)) + MIN_ENEMY_HEALTH - }); - } - } - - function generateWalls() { - walls = []; - let numWalls = Math.floor(Math.random() * (WALLS_MAX - WALLS_MIN + 1)) + WALLS_MIN; - while (walls.length < numWalls) { - const wallX = Math.floor(Math.random() * GRID_SIZE); - const wallY = Math.floor(Math.random() * GRID_SIZE); - - if ( - (wallX === player.x && wallY === player.y) || - (wallX === exit.x && wallY === exit.y) || - enemies.some(enemy => enemy.x === wallX && enemy.y === wallY) || - (wallX === 0 && wallY === 0) || // Don't block the player spawn - items.some(item => item.x === wallX && item.y === wallY) - ) continue; - - if (!walls.some(wall => wall.x === wallX && wall.y === wallY)) { - walls.push({ x: wallX, y: wallY }); - } - } - - if (!isPassable()) { - generateWalls(); - } - } - - function generateItems() { - items = []; - const numItems = Math.floor(Math.random() * (ITEMS_MAX - ITEMS_MIN + 1)) + 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); - } while ( - (itemX === player.x && itemY === player.y) || - (itemX === exit.x && itemY === exit.y) || - walls.some(wall => wall.x === itemX && wall.y === itemY) || - enemies.some(enemy => enemy.x === itemX && enemy.y === itemY) - ); - const itemType = Math.random() < 0.5 ? 'diamond' : 'pentagon'; // 50% chance for each type - items.push({ x: itemX, y: itemY, type: itemType }); - } - } - - function isPassable() { - const visited = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(false)); - - function dfs(x, y) { - if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) return false; - if (visited[x][y]) return false; - if (walls.some(wall => wall.x === x && wall.y === y)) return false; - visited[x][y] = true; - if (x === exit.x && y === exit.y) return true; - - return dfs(x + 1, y) || dfs(x - 1, y) || dfs(x, y + 1) || dfs(x, y - 1); - } - - return dfs(player.x, player.y); - } - - 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++) { - ctx.strokeRect(col * tileSize, row * tileSize, tileSize, tileSize); - } - } - } - - function drawExit() { - const x = exit.x * tileSize + tileSize / 2; - const y = exit.y * tileSize + tileSize / 2; - ctx.beginPath(); - ctx.moveTo(x, y - tileSize / 3); - ctx.lineTo(x + tileSize / 3, y + tileSize / 3); - ctx.lineTo(x - tileSize / 3, y + tileSize / 3); - ctx.closePath(); - ctx.fillStyle = COLORS.exit; - ctx.fill(); - } - - function drawWalls() { - ctx.fillStyle = COLORS.walls; - walls.forEach(wall => { - ctx.fillRect(wall.x * tileSize, wall.y * tileSize, tileSize, tileSize); - }); - } - - function drawItems() { - items.forEach(item => { - const x = item.x * tileSize + tileSize / 2; - const y = item.y * tileSize + tileSize / 2; - ctx.fillStyle = item.type === 'diamond' ? COLORS.diamond : COLORS.pentagon; - ctx.beginPath(); - if (item.type === 'diamond') { - ctx.moveTo(x, y - tileSize / 4); - ctx.lineTo(x + tileSize / 4, y); - ctx.lineTo(x, y + tileSize / 4); - ctx.lineTo(x - tileSize / 4, y); - } else { - const sides = 5; - const radius = tileSize / 4; - for (let i = 0; i < sides; i++) { - const angle = (i * 2 * Math.PI) / sides - Math.PI / 2; - const pointX = x + radius * Math.cos(angle); - const pointY = y + radius * Math.sin(angle); - if (i === 0) ctx.moveTo(pointX, pointY); - else ctx.lineTo(pointX, pointY); - } - } - ctx.closePath(); - ctx.fill(); - }); - } - - function drawCharacterBorder(x, y, radius, damageTaken) { - const dashLength = 5; - const gapLength = Math.max(1, damageTaken * 2); // More damage, larger gaps - - ctx.lineWidth = 2; - ctx.strokeStyle = '#2d2d2d'; - ctx.setLineDash([dashLength, gapLength]); - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI); - ctx.stroke(); - ctx.setLineDash([]); // Reset to a solid line - } - - 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 radius = tileSize / 3; - const damageTaken = MAX_ENEMY_HEALTH - enemy.health; - - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI); - ctx.fillStyle = `${COLORS.enemy}${opacity})`; - ctx.fill(); - - drawCharacterBorder(x, y, radius, damageTaken); - }); - } - - 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 - ctx.beginPath(); - for (let i = 0; i < 6; i++) { - const angle = (Math.PI / 3) * i; - const hexX = x + radius * Math.cos(angle); - const hexY = y + radius * Math.sin(angle); - if (i === 0) { - ctx.moveTo(hexX, hexY); - } else { - ctx.lineTo(hexX, hexY); - } - } - ctx.closePath(); - ctx.fillStyle = `${COLORS.player}${playerOpacity})`; - ctx.fill(); - ctx.lineWidth = 2; - ctx.strokeStyle = '#2d2d2d'; - ctx.stroke(); - } - - function drawCombatDots() { - for (const key in combatDots) { - const [cellX, cellY] = key.split(',').map(Number); - const dots = combatDots[key]; - dots.forEach(dot => { - ctx.beginPath(); - ctx.arc(cellX * tileSize + dot.x, cellY * tileSize + dot.y, 2, 0, Math.PI * 2); - ctx.fillStyle = dot.color; - ctx.fill(); - ctx.closePath(); - }); - } - } - - function handleItemCollection() { - const collectedItem = items.find(item => item.x === player.x && item.y === player.y); - if (collectedItem) { - if (collectedItem.type === 'diamond') { - player.damage += 3; - 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); - console.log("Collected pentagon! Healed " + healAmount + " health."); - } - items = items.filter(item => item !== collectedItem); // Remove collected item - } - } - - // Function to add dots for damage in combat (including adjacent cells) - function addCombatDots(x, y, color) { - const cellsToFill = [ - [x, y], // Center - [x - 1, y], // Left - [x + 1, y], // Right - [x, y - 1], // Up - [x, y + 1], // Down - [x - 1, y - 1], // Top-left - [x + 1, y - 1], // Top-right - [x - 1, y + 1], // Bottom-left - [x + 1, y + 1] // Bottom-right - ]; - - cellsToFill.forEach(([cellX, cellY]) => { - if (cellX >= 0 && cellX < GRID_SIZE && cellY >= 0 && cellY < GRID_SIZE) { - const key = `${cellX},${cellY}`; - if (!combatDots[key]) { - combatDots[key] = []; - } - for (let i = 0; i < DOTS_PER_HIT; i++) { - combatDots[key].push({ - x: Math.random() * tileSize, - y: Math.random() * tileSize, - color: color - }); - } - } - }); - } - - function movePlayer(dx, dy) { - const newX = player.x + dx; - const newY = player.y + dy; - if (isValidMove(newX, newY)) { - const enemyInTargetCell = enemies.find(enemy => enemy.x === newX && enemy.y === newY); - if (!enemyInTargetCell) { - if (newX !== player.x || newY !== player.y) player.cellsTraveled++; - player.x = newX; - player.y = newY; - handleItemCollection(); // Check if the player collected an item - checkPlayerAtExit(); // Check if player reached the exit after moving - } else { - // Enemy in the target cell, stay in place and do combat - handleDamage(player, enemyInTargetCell); - } - } - moveEnemies(); - render(); - } - - // Chase logic (naive) - function moveEnemies() { - enemies.forEach(enemy => { - const distance = Math.abs(enemy.x - player.x) + Math.abs(enemy.y - player.y); - if (distance <= ENEMY_CHASE_DISTANCE) { - let dx = 0, dy = 0; - if (enemy.x < player.x && isValidMove(enemy.x + 1, enemy.y)) dx = 1; - else if (enemy.x > player.x && isValidMove(enemy.x - 1, enemy.y)) dx = -1; - else if (enemy.y < player.y && isValidMove(enemy.x, enemy.y + 1)) dy = 1; - else if (enemy.y > player.x && isValidMove(enemy.x, enemy.y - 1)) dy = -1; - - if (!enemies.some(e => e.x === enemy.x + dx && e.y === enemy.y)) { - enemy.x += dx; - enemy.y += dy; - } - } - }); - } - - function handleDamage(player, enemy) { - const enemyMisses = Math.random() < 0.2; // 1 in 5 chance the enemy misses you - const cellX = player.x; - const cellY = player.y; - - if (!enemyMisses) { - player.health--; - player.totalDamageTaken++; - addCombatDots(cellX, cellY, COLORS.combatDotPlayer); // Add dots for player damage - console.log("Enemy hit! Player health: " + player.health); - } else { - console.log("Enemy missed!"); - } - - enemy.health = enemy.health - player.damage; - player.totalDamageDone++; - addCombatDots(cellX, cellY, COLORS.combatDotEnemy); // Add dots for enemy damage - console.log("Player hit! Enemy health: " + enemy.health); - - if (enemy.health <= 0) { - enemies = enemies.filter(e => e !== enemy); - } - - if (player.health <= 0) { - alert(`Dead\n\nScore: ${player.score}\nDistance Traveled: ${player.cellsTraveled}\nTotal Damage Dealt: ${player.totalDamageDone}\nTotal Damage Received: ${player.totalDamageTaken}`); - resetGame(); - } - } - - function resetGame() { - player.health = PLAYER_HEALTH; - player.damage = PLAYER_BASE_DAMAGE; - player.bonusDamageTurns = 0; - player.totalDamageDone = 0; - player.totalDamageTaken = 0; - player.cellsTraveled = 0; - player.score = 0; - player.x = 0; - player.y = 0; - combatDots = {}; - generateExit(); - generateEnemies(); - generateItems(); - generateWalls(); - render(); - } - - function checkPlayerAtExit() { - if (player.x === exit.x && player.y === exit.y) { - player.score += 1; - player.damage = PLAYER_BASE_DAMAGE; - console.group("Level complete! " + player.score); - console.log("Score: " + player.score); - console.log("Current health: " + player.health); - console.log("Distance Traveled: " + player.cellsTraveled); - console.log("Total Damage Dealt: " + player.totalDamageDone); - console.log("Total Damage Received: " + player.totalDamageTaken); - console.groupEnd(); - combatDots = {}; - generateExit(); - generateEnemies(); - generateItems(); - generateWalls(); - render(); - } - } - - function render() { - drawGrid(); - drawExit(); - drawItems(); - drawEnemies(); - drawPlayer(); - drawWalls(); - drawCombatDots(); - } - - const directionMap = { - ArrowUp: [0, -1], - ArrowDown: [0, 1], - ArrowLeft: [-1, 0], - ArrowRight: [1, 0], - w: [0, -1], - s: [0, 1], - a: [-1, 0], - d: [1, 0], - h: [-1, 0], - j: [0, 1], - k: [0, -1], - l: [1, 0] - }; - - document.addEventListener('keydown', (e) => { - const direction = directionMap[e.key]; - if (direction) { - movePlayer(...direction); - checkPlayerAtExit(); - render(); - } - }); - - let touchStartX = 0; - let touchStartY = 0; - - canvas.addEventListener('touchstart', (e) => { - e.preventDefault(); // Prevent scrolling on touchstart - touchStartX = e.touches[0].clientX; - touchStartY = e.touches[0].clientY; - }); - - canvas.addEventListener('touchend', (e) => { - e.preventDefault(); // Prevent scrolling on touchend - const touchEndX = e.changedTouches[0].clientX; - const touchEndY = e.changedTouches[0].clientY; - const dx = touchEndX - touchStartX; - const dy = touchEndY - touchStartY; - - if (Math.abs(dx) > Math.abs(dy)) { - // Horizontal swipe - if (dx > 0) { - movePlayer(1, 0); // Swipe right - } else { - movePlayer(-1, 0); // Swipe left - } - } else { - // Vertical swipe - if (dy > 0) { - movePlayer(0, 1); // Swipe down - } else { - movePlayer(0, -1); // Swipe up - } - } - - render(); - }, { passive: false }); // TIL you can use passive set to false to help make preventDefault actually work? Feels like superstition - - 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 - render(); - }; - - window.addEventListener('resize', resizeCanvas); - resizeCanvas(); - - // Initial level setup - generateExit(); - generateEnemies(); - generateItems(); - generateWalls(); - render(); - </script> + <script type="module" src="./broughlike.js"></script> </body> </html> \ No newline at end of file |