diff options
Diffstat (limited to 'html')
-rw-r--r-- | html/plains/about.html | 37 | ||||
-rw-r--r-- | html/plains/enemies.js | 96 | ||||
-rw-r--r-- | html/plains/game.js | 252 | ||||
-rw-r--r-- | html/plains/index.html | 3 |
4 files changed, 150 insertions, 238 deletions
diff --git a/html/plains/about.html b/html/plains/about.html new file mode 100644 index 0000000..3790fef --- /dev/null +++ b/html/plains/about.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Plains</title> + <meta name="description" content="Help little black square rescue the villagers, and battle the monsters."> + <style> + body { + background-color: #f0f0f0; + font-size: x-large; + padding: 1em 2em; + } + </style> +</head> +<body> + <h1>How to play</h1> + <p>Help little black square rescue the villagers, and battle the monsters.</p> + <ul> + <li>Rescue the other villagers from the monsters.</li> + <li>You start in the village.</li> + <li>Monsters will mostly stay away from the village.</li> + <li>Use the arrow keys to move.</li> + <li>Use the z key to attack.</li> + <li>Use the x key to hide in your protective shell. Whil hiding you won't be able to move.</li> + <li>Use the e key to cycle between your push attack and your sword.</li> + <li>Earn the sword by rescuing some villagers!</li> + <li>The push attack will knock enemies back, but won't harm them.</li> + <li>The sword slices. This is dangerous, and will harm enemies.</li> + <li>Be careful of the enemies, they will chase you around!</li> + <li>If you defeate an enemy they may drop some yellow gems. Collect them to restore your health.</li> + <li>Hold down the shift key to run faster, you only have so much stamina, though, so after a while you'll have to slow down again.</li> + <li>Hold down the space bar to strafe.</li> + </ul> + <p><a href="index.html">Play the game!</a></p> +</body> +</html> \ No newline at end of file diff --git a/html/plains/enemies.js b/html/plains/enemies.js index c2299f0..3d40c3e 100644 --- a/html/plains/enemies.js +++ b/html/plains/enemies.js @@ -1,10 +1,9 @@ -// ============= Enemy System ============= const blueShades = [ - 'rgb(0, 0, 255)', // Bright blue - 'rgb(0, 0, 200)', // Medium blue - 'rgb(0, 0, 150)', // Darker blue - 'rgb(0, 0, 100)', // Even darker blue - 'rgb(0, 0, 50)' // Very dark blue + 'rgb(0, 0, 255)', + 'rgb(0, 0, 200)', + 'rgb(0, 0, 150)', + 'rgb(0, 0, 100)', + 'rgb(0, 0, 50)' ]; const generateEnemies = (villagers, collisionMap) => { @@ -15,11 +14,11 @@ const generateEnemies = (villagers, collisionMap) => { villagers.forEach(villager => { if (villager.status === 'rescued') return; - // Generate 2-5 enemies per villager + // 2 - 5 enemies per villager const numEnemies = 2 + Math.floor(Math.random() * 4); for (let i = 0; i < numEnemies; i++) { - // Place within 2-3 cells of villager + // make sure to place an enemy within 2 - 3 cells of a villager const radius = 2 + Math.floor(Math.random()); const angle = Math.random() * Math.PI * 2; @@ -27,12 +26,11 @@ const generateEnemies = (villagers, collisionMap) => { const cellY = villager.cellY + Math.floor(Math.sin(angle) * radius); const cellKey = `${cellX},${cellY}`; - // Skip if cell is occupied if (occupiedCells.has(cellKey)) { continue; } - // Generate random color between orange and dark red + // random color between orange and dark red const red = 150 + Math.floor(Math.random() * 105); // 150-255 const green = Math.floor(Math.random() * 100); // 0-100 const blue = 0; @@ -103,11 +101,11 @@ const handleEnemyDamage = (enemy, damage, knockbackForce = 0, angle = 0) => { const gridSize = CONFIG.display.grid.size; enemy.hp -= damage; - // Apply stun when hit + // stun the enemy when you hit them enemy.stunned = true; - enemy.stunEndTime = animationTime + 500; // 500ms stun duration + enemy.stunEndTime = animationTime + 500; - // Apply knockback if there's a force + // knock the enemy back when you hit them if (knockbackForce > 0) { const knockbackDistance = gridSize * 0.5; enemy.knockback = { @@ -129,7 +127,7 @@ const handleEnemyDamage = (enemy, damage, knockbackForce = 0, angle = 0) => { return damage > 0 && enemy.hp <= 0; }; -// Add this new function to find nearest lost villager +// find the nearest lost villager, zoot to 'em const findNearestLostVillager = (enemyX, enemyY, villagers) => { let nearestVillager = null; let shortestDistance = Infinity; @@ -157,7 +155,6 @@ const updateEnemies = (enemies, deltaTime) => { return enemies.filter(enemy => enemy.hp > 0).map(enemy => { const baseSpeed = CONFIG.enemies.patrol.speed.base * deltaTime / 1000; - // Handle knockback animation if (enemy.knockback.active) { const progress = (animationTime - enemy.knockback.startTime) / enemy.knockback.duration; @@ -166,12 +163,10 @@ const updateEnemies = (enemies, deltaTime) => { enemy.x = enemy.knockback.targetX; enemy.y = enemy.knockback.targetY; - // Calculate distance from villager after knockback const dvx = enemy.x - enemy.targetVillager.x; const dvy = enemy.y - enemy.targetVillager.y; const distanceToVillager = Math.sqrt(dvx * dvx + dvy * dvy); - // If too far from villager, set returning state if (distanceToVillager > gridSize * 3) { return { ...enemy, @@ -182,7 +177,7 @@ const updateEnemies = (enemies, deltaTime) => { }; } } else { - // Smooth easing function (ease-out) + // ease out const t = 1 - Math.pow(1 - progress, 3); enemy.x = enemy.knockback.startX + (enemy.knockback.targetX - enemy.knockback.startX) * t; enemy.y = enemy.knockback.startY + (enemy.knockback.targetY - enemy.knockback.startY) * t; @@ -196,17 +191,17 @@ const updateEnemies = (enemies, deltaTime) => { }; } - // If enemy is returning to villager + // IF enemy is returning to villager if (enemy.isReturning) { const dvx = enemy.targetVillager.x - enemy.x; const dvy = enemy.targetVillager.y - enemy.y; const distanceToVillager = Math.sqrt(dvx * dvx + dvy * dvy); - // If back within range, resume normal behavior + // IF back within range, go back to normal behavior if (distanceToVillager <= gridSize * 2) { enemy.isReturning = false; } else { - // Move towards villager + // Move to the villager const angle = Math.atan2(dvy, dvx); const returnSpeed = baseSpeed * CONFIG.enemies.return.speedMultiplier; @@ -220,23 +215,23 @@ const updateEnemies = (enemies, deltaTime) => { } } - // Check if stun has worn off + // Still stunned? if (enemy.stunned && animationTime >= enemy.stunEndTime) { enemy.stunned = false; } - // Check if current target villager was rescued + // Was your villager rescued?? if (enemy.targetVillager.status === 'rescued') { - // Find new target villager + // find a new villager const newTarget = findNearestLostVillager(enemy.x, enemy.y, state.villagers); if (newTarget) { - // Update enemy with new target + // zoot towards that villager enemy.targetVillager = newTarget; - enemy.isReturning = true; // Make enemy return to new villager + enemy.isReturning = true; enemy.isChasing = false; } else { - // No more villagers to guard, enemy becomes defeated + // no more villagers, get real sad return { ...enemy, color: CONFIG.enemies.colors.defeated @@ -244,22 +239,20 @@ const updateEnemies = (enemies, deltaTime) => { } } - // Calculate distance to player + // Distance to player const dx = state.player.x - enemy.x; const dy = state.player.y - enemy.y; const distanceToPlayer = Math.sqrt(dx * dx + dy * dy); if (distanceToPlayer <= aggroRange) { - // Check if in attack range (half grid cell) + const attackRange = gridSize * 0.5; - // If on attack cooldown, keep distance if (enemy.attackCooldown) { if (animationTime >= enemy.attackCooldownUntil) { enemy.attackCooldown = false; } else if (distanceToPlayer < gridSize) { - // Retreat if too close during cooldown - const retreatAngle = Math.atan2(dy, dx) + Math.PI; // Opposite direction + const retreatAngle = Math.atan2(dy, dx) + Math.PI; // retreat in the opposite direction const retreatSpeed = baseSpeed * CONFIG.enemies.chase.speedMultiplier; return { ...enemy, @@ -270,7 +263,6 @@ const updateEnemies = (enemies, deltaTime) => { } } - // If in attack range and not on cooldown, initiate attack if (distanceToPlayer <= attackRange && !enemy.attacking && !enemy.attackCooldown) { enemy.attacking = true; enemy.attackStartPosition = { x: enemy.x, y: enemy.y }; @@ -279,22 +271,20 @@ const updateEnemies = (enemies, deltaTime) => { y: state.player.y }; enemy.attackStartTime = animationTime; - - // Change enemy color to a random blue shade enemy.color = blueShades[Math.floor(Math.random() * blueShades.length)]; } - // Handle attack animation and damage + // attack animation and damage if (enemy.attacking) { - const attackDuration = 200; // 200ms attack + const attackDuration = 200; const progress = (animationTime - enemy.attackStartTime) / attackDuration; if (progress >= 1) { enemy.attacking = false; enemy.attackCooldown = true; - enemy.attackCooldownUntil = animationTime + 500; // 0.5 second cooldown + enemy.attackCooldownUntil = animationTime + 500; - // Check if attack hit the player + // did the attack hit the player? const finalDx = state.player.x - enemy.x; const finalDy = state.player.y - enemy.y; const finalDistance = Math.sqrt(finalDx * finalDx + finalDy * finalDy); @@ -315,7 +305,7 @@ const updateEnemies = (enemies, deltaTime) => { }; } - // Fast lunge animation with ease-in + // lunge towards the player const t = progress * progress; // Quadratic ease-in return { ...enemy, @@ -326,7 +316,6 @@ const updateEnemies = (enemies, deltaTime) => { }; } - // Normal chase behavior if not attacking const angle = Math.atan2(dy, dx); const chaseSpeed = baseSpeed * CONFIG.enemies.chase.speedMultiplier; @@ -337,12 +326,11 @@ const updateEnemies = (enemies, deltaTime) => { isChasing: true }; } else { - // Get current distance from villager + const dvx = enemy.x - enemy.targetVillager.x; const dvy = enemy.y - enemy.targetVillager.y; const distanceToVillager = Math.sqrt(dvx * dvx + dvy * dvy); - // If too far from villager, start returning if (distanceToVillager > gridSize * 3) { return { ...enemy, @@ -351,12 +339,11 @@ const updateEnemies = (enemies, deltaTime) => { }; } - // Patrol behavior - move in small circles around current position if (!enemy.patrolAngle) { enemy.patrolAngle = Math.random() * Math.PI * 2; } - enemy.patrolAngle += baseSpeed * 0.02; // Small angle change for circular movement + enemy.patrolAngle += baseSpeed * 0.02; return { ...enemy, @@ -370,24 +357,21 @@ const updateEnemies = (enemies, deltaTime) => { }; const renderEnemies = (ctx, enemies) => { - // Pre-create the health circle gradient once + // I tried to generate this per enemy, but it was wildly inefficient const healthGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 2); healthGradient.addColorStop(0, 'rgba(255, 0, 0, 0.8)'); healthGradient.addColorStop(0.7, 'rgba(200, 0, 0, 0.6)'); healthGradient.addColorStop(1, 'rgba(150, 0, 0, 0.4)'); enemies.forEach(enemy => { - // Draw enemy body ctx.beginPath(); ctx.arc(enemy.x, enemy.y, enemy.size, 0, Math.PI * 2); ctx.fillStyle = enemy.stunned ? 'rgb(150, 150, 150)' : enemy.color; ctx.fill(); - // Add a glowing effect + // she be radiant const glowSize = enemy.stunned ? 1.1 : (enemy.attacking ? 1.6 : enemy.isChasing ? 1.4 : 1.2); const glowIntensity = enemy.stunned ? 0.1 : (enemy.attacking ? 0.5 : enemy.isChasing ? 0.3 : 0.2); - - // Create glow gradient only once per enemy const glowGradient = ctx.createRadialGradient( enemy.x, enemy.y, enemy.size * 0.5, enemy.x, enemy.y, enemy.size * glowSize @@ -400,7 +384,6 @@ const renderEnemies = (ctx, enemies) => { ctx.fillStyle = glowGradient; ctx.fill(); - // Draw HP circles if (enemy.hp > 0) { const circleRadius = 3; const circleSpacing = 8; @@ -408,7 +391,6 @@ const renderEnemies = (ctx, enemies) => { const startX = enemy.x - ((totalCircles - 1) * circleSpacing) / 2; const circleY = enemy.y - enemy.size - 15; - // Batch draw the circle borders ctx.beginPath(); for (let i = 0; i < totalCircles; i++) { const circleX = startX + (i * circleSpacing); @@ -419,7 +401,6 @@ const renderEnemies = (ctx, enemies) => { ctx.lineWidth = 1; ctx.stroke(); - // Batch draw the filled circles ctx.beginPath(); for (let i = 0; i < totalCircles; i++) { const circleX = startX + (i * circleSpacing); @@ -433,22 +414,21 @@ const renderEnemies = (ctx, enemies) => { }; const generateDiamonds = () => { - const diamondCount = Math.floor(Math.random() * 3); // 0 to 2 diamonds + const diamondCount = Math.floor(Math.random() * 3); const diamonds = []; for (let i = 0; i < diamondCount; i++) { diamonds.push({ - x: enemy.x + (Math.random() - 0.5) * 20, // Random position around the enemy + x: enemy.x + (Math.random() - 0.5) * 20, y: enemy.y + (Math.random() - 0.5) * 20, - size: 10, // Size of the diamond - collected: false // Track if collected + size: 10, + collected: false }); } return diamonds; }; -// Export the functions window.enemySystem = { generateEnemies, updateEnemies, diff --git a/html/plains/game.js b/html/plains/game.js index 2b4f342..5b23e5b 100644 --- a/html/plains/game.js +++ b/html/plains/game.js @@ -1,4 +1,3 @@ -// ============= Utility Functions ============= const lerp = (start, end, t) => { return start * (1 - t) + end * t; }; @@ -13,7 +12,6 @@ const worldToGrid = (x, y) => ({ y: Math.floor(y / CONFIG.display.grid.size) }); -// Add the villager-related functions here const generateVillagers = () => { const villagers = []; const occupiedCells = new Set(); @@ -21,7 +19,7 @@ const generateVillagers = () => { const villageSize = CONFIG.world.village.size; const worldSize = CONFIG.display.grid.worldSize; - // First, place one villager near the village + // place one villager near the village, so that hopefully the player can find them easily const nearVillageX = villageSize + Math.floor(Math.random() * 2); const nearVillageY = villageSize + Math.floor(Math.random() * 2); @@ -33,24 +31,24 @@ const generateVillagers = () => { status: 'lost', cellX: nearVillageX, cellY: nearVillageY, - bobSpeed: 0.005 + Math.random() * 0.005, // Random speed for rescued villagers - bobAmplitude: 2 + Math.random() * 2, // Random amplitude for rescued villagers - lostBobSpeed: 0.005 + Math.random() * 0.005, // Random speed for lost villagers - lostBobAmplitude: 2 + Math.random() * 2 // Random amplitude for lost villagers + bobSpeed: 0.005 + Math.random() * 0.005, + bobAmplitude: 2 + Math.random() * 2, + lostBobSpeed: 0.005 + Math.random() * 0.005, + lostBobAmplitude: 2 + Math.random() * 2 }); occupiedCells.add(`${nearVillageX},${nearVillageY}`); - // Place remaining villagers while (villagers.length < CONFIG.world.villagers.total) { const cellX = villageSize + Math.floor(Math.random() * (worldSize - villageSize)); const cellY = villageSize + Math.floor(Math.random() * (worldSize - villageSize)); const cellKey = `${cellX},${cellY}`; - // Skip if cell is occupied or has a tree + // try not to put villagers into trees...but this doesn't always work if (occupiedCells.has(cellKey) || state.collisionMap.has(cellKey)) { continue; } + // look...sometimes copy and paste is easy and good villagers.push({ x: (cellX * gridSize) + (gridSize / 2), y: (cellY * gridSize) + (gridSize / 2), @@ -59,10 +57,10 @@ const generateVillagers = () => { status: 'lost', cellX, cellY, - bobSpeed: 0.005 + Math.random() * 0.005, // Random speed for rescued villagers - bobAmplitude: 2 + Math.random() * 2, // Random amplitude for rescued villagers - lostBobSpeed: 0.005 + Math.random() * 0.005, // Random speed for lost villagers - lostBobAmplitude: 2 + Math.random() * 2 // Random amplitude for lost villagers + bobSpeed: 0.005 + Math.random() * 0.005, + bobAmplitude: 2 + Math.random() * 2, + lostBobSpeed: 0.005 + Math.random() * 0.005, + lostBobAmplitude: 2 + Math.random() * 2 }); occupiedCells.add(cellKey); } @@ -108,7 +106,7 @@ const drawVillagerShape = (ctx, x, y, shape, size) => { ctx.closePath(); }; -// ============= Configuration ============= + const CONFIG = { display: { fps: 60, @@ -145,21 +143,21 @@ const CONFIG = { }, dash: { duration: 3000, // 3 seconds of use - cooldown: 1000, // 1 second cooldown - exhaustedAt: 0 // Track when dash was exhausted + cooldown: 3000, // 3 second cooldown + exhaustedAt: 0 }, idle: { - startDelay: 1500, // Start idle animation after 1.5 seconds - lookSpeed: 0.001, // Speed of the looking animation - lookRadius: 0.4 // How far to look around (in radians) + startDelay: 1500, // Get board after 1.5 seconds + lookSpeed: 0.001, + lookRadius: 0.4 }, equipment: { - swordUnlockCount: 5, // Number of rescues needed to unlock sword + swordUnlockCount: 5, // The number of villagers you need to rescue to unlock the sword unlockAnimation: { - duration: 1500, // Duration of unlock animation in ms - glowColor: 'rgba(255, 215, 0, 0.6)', // Golden glow + duration: 1500, + glowColor: 'rgba(255, 215, 0, 0.6)', messageText: 'Sword Unlocked!', - messageColor: '#FFD700' // Gold color for text + messageColor: '#FFD700' } } }, @@ -202,7 +200,7 @@ const CONFIG = { groundColor: '#f2f2f2' }, villagers: { - total: 25, // Total number of villagers to spawn + total: 25, colors: [ '#4B0082', // Indigo '#483D8B', // DarkSlateBlue @@ -212,15 +210,15 @@ const CONFIG = { '#4682B4' // SteelBlue ], shapes: ['square', 'triangle', 'pentagon', 'hexagon'], - size: 30, // Same as player size + size: 30, rescueMessage: 'Congratulations! You rescued all the villagers!', - messageDisplayTime: 5000 // How long to show the completion message (ms) + messageDisplayTime: 5000 }, wilderness: { groundColor: '#e6ffe6', vegetation: { tree: { - frequency: 0.1, // Chance per grid cell + frequency: 0.1, // Chance per grid cell colors: [ 'rgba(100, 144, 79, 1)', 'rgba(85, 128, 64, 1)', @@ -245,7 +243,7 @@ const CONFIG = { margin: 10, variation: 0.5, offset: 0.5, - singleColor: 0.7 // % chance that all dots in a cell will be the same color + singleColor: 0.7 // % chance that all dots in a cell will be the same color } }, flower: { @@ -258,7 +256,7 @@ const CONFIG = { pattern: { size: 12, spacing: 16, - rotation: Math.PI / 6, // Base rotation of pattern + rotation: Math.PI / 6, // rotation of pattern margin: 10, variation: 0.2 } @@ -270,10 +268,10 @@ const CONFIG = { spacing: 8, length: 6, angle: Math.PI / 4, - variation: 0.4, // Slight randomness in angle + variation: 0.4, // we can have some randomness, as a treat margin: 4 }, - spreadFactor: 0.6 // Add this for grass spreading + spreadFactor: 0.6 } } } @@ -313,15 +311,15 @@ const CONFIG = { |