about summary refs log tree commit diff stats
path: root/html
diff options
context:
space:
mode:
Diffstat (limited to 'html')
-rw-r--r--html/plains/about.html37
-rw-r--r--html/plains/enemies.js96
-rw-r--r--html/plains/game.js252
-rw-r--r--html/plains/index.html3
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 = {
               
}, chase: { range: 2, - speedMultiplier: 1.5 // 50% faster than base speed + speedMultiplier: 1.5 }, return: { - speedMultiplier: 1.25 // 25% faster than base speed + speedMultiplier: 1.25 } } }; @@ -331,7 +329,6 @@ CONFIG.sword.colors = CONFIG.effects.colors; CONFIG.bubble.colors = CONFIG.effects.colors; -// ============= Global State ============= let GAME_WIDTH = window.innerWidth; let GAME_HEIGHT = window.innerHeight; let lastFrameTime = 0; @@ -341,7 +338,6 @@ const CAMERA_DEADZONE_X = GAME_WIDTH * CONFIG.display.camera.deadzoneMultiplierX const CAMERA_DEADZONE_Y = GAME_HEIGHT * CONFIG.display.camera.deadzoneMultiplierY; -// ============= State Management ============= const createInitialState = () => ({ player: { x: CONFIG.player.size, @@ -367,8 +363,8 @@ const createInitialState = () => ({ isInvulnerable: false, invulnerableUntil: 0, isDead: false, - diamonds: 0, // Track number of collected diamonds - lastRenderedCircles: 4, // Add this line + diamonds: 0, + lastRenderedCircles: 4 }, particles: [], footprints: [], @@ -384,17 +380,15 @@ const createInitialState = () => ({ gameComplete: false, gameCompleteMessageShown: false, enemies: [], - diamonds: [] // Array to hold diamond objects + diamonds: [] }); let state = createInitialState(); -// ============= Input Handling ============= const keys = new Set(); const handleKeyDown = (e) => { - // If player is dead, only handle restart if (state.player.isDead) { if (e.code === 'Space') { window.location.reload(); @@ -461,7 +455,7 @@ const inputHandlers = { if (state.player.isDefending) return state; if (state.player.equipment === 'sword') { - if (!state.player.swordUnlocked) return state; // Prevent sword usage if not unlocked + if (!state.player.swordUnlocked) return state; // can't swing a sword you haven't earned yet! if (!state.player.isSwinging) { return { ...state, @@ -479,7 +473,7 @@ const inputHandlers = { }, handleEquipmentSwitch: (state) => { - if (!state.player.swordUnlocked) return state; // Can't switch if sword isn't unlocked + if (!state.player.swordUnlocked) return state; // can't switch to a sword you haven't earned yet! const equipment = ['sword', 'unarmed']; const currentIndex = equipment.indexOf(state.player.equipment); @@ -494,7 +488,6 @@ const inputHandlers = { }; -// ============= Movement System ============= const calculateMovement = (keys) => { let dx = 0; let dy = 0; @@ -508,7 +501,6 @@ const calculateMovement = (keys) => { return { moving: false }; } - // Update last input time when moving state.player.lastInputTime = animationTime; const length = Math.sqrt(dx * dx + dy * dy); @@ -519,9 +511,8 @@ const calculateMovement = (keys) => { const newDirection = isStrafing ? { ...state.player.direction } : // strafe - { x: normalizedDx, y: normalizedDy }; // normal movement + { x: normalizedDx, y: normalizedDy }; // normal - // Update base direction when not strafing if (!isStrafing) { state.player.baseDirection = { ...newDirection }; } @@ -540,9 +531,9 @@ const isPositionBlocked = (x, y) => { if (!state.collisionMap.has(key)) return false; const obstacle = state.collisionMap.get(key); - const obstacleRadius = CONFIG.player.size / 2; // Use player size for all collision + const obstacleRadius = CONFIG.player.size / 2; - // Distance check from center of grid cell + // check distance from the center of a grid cell const dx = x - obstacle.x; const dy = y - obstacle.y; const distanceSquared = dx * dx + dy * dy; @@ -565,7 +556,6 @@ const movementSystem = { const movement = calculateMovement(keys); if (!movement.moving) { - // Reset dash when not moving return { ...state, player: { @@ -591,7 +581,7 @@ const movementSystem = { } isDashing = true; - // Check if dash duration is exhausted + // Are you tired of dashing? if (animationTime - dashStartTime >= CONFIG.player.dash.duration) { isDashing = false; dashExhausted = true; @@ -629,25 +619,22 @@ const movementSystem = { max: CONFIG.display.grid.size * CONFIG.display.grid.worldSize }; - // After calculating new position, clamp it to world bounds const newX = state.player.x + movement.dx * speed; const newY = state.player.y + movement.dy * speed; const clampedX = Math.max(worldBounds.min, Math.min(worldBounds.max, newX)); const clampedY = Math.max(worldBounds.min, Math.min(worldBounds.max, newY)); - // Check for collisions at the new position const playerRadius = CONFIG.player.size / 2; - const checkPoints = [ - { x: newX - playerRadius, y: newY - playerRadius }, // Top-left - { x: newX + playerRadius, y: newY - playerRadius }, // Top-right - { x: newX - playerRadius, y: newY + playerRadius }, // Bottom-left - { x: newX + playerRadius, y: newY + playerRadius } // Bottom-right - ]; + // const checkPoints = [ + // { x: newX - playerRadius, y: newY - playerRadius }, // Top-left + // { x: newX + playerRadius, y: newY - playerRadius }, // Top-right + // { x: newX - playerRadius, y: newY + playerRadius }, // Bottom-left + // { x: newX + playerRadius, y: newY + playerRadius } // Bottom-right + // ]; - const wouldCollide = checkCollision(newX, newY, playerRadius * 0.8); // Use 80% of player radius for better feel + const wouldCollide = checkCollision(newX, newY, playerRadius * 0.8); - // Only update position if there's no collision const finalX = wouldCollide ? state.player.x : clampedX; const finalY = wouldCollide ? state.player.y : clampedY; @@ -671,7 +658,6 @@ const movementSystem = { }; -// ============= Weapon Systems ============= const updateBubble = (bubble, animationTime) => { const age = animationTime - bubble.createdAt; const ageRatio = age / CONFIG.bubble.lifetime; @@ -749,7 +735,6 @@ const createBubbleAttack = (state, animationTime) => { }; }; -// Update the collision helper to handle different damage amounts const checkEnemyCollision = (enemy, sourceX, sourceY, range, damage = 0) => { if (enemy.stunned) return false; @@ -770,7 +755,6 @@ const weaponSystems = { const updatedBubbles = state.player.bubbles .filter(bubble => animationTime - bubble.createdAt < CONFIG.bubble.lifetime) .map(bubble => { - // Check for enemy collisions with 0 damage state.enemies.forEach(enemy => { checkEnemyCollision(enemy, bubble.x, bubble.y, CONFIG.bubble.size, 0); }); @@ -793,13 +777,12 @@ const weaponSystems = { }; }, - updateSwordSwing: (state, animationTime) => { + updateSwordSwing: (state) => { if (!state.player.isSwinging) return state; const newAngle = state.player.swordAngle + CONFIG.sword.swingSpeed; const swingComplete = newAngle > Math.atan2(state.player.direction.y, state.player.direction.x) + Math.PI / 2; - // Check all enemies for sword collision with 1 damage const updatedEnemies = state.enemies.map(enemy => { checkEnemyCollision(enemy, state.player.x, state.player.y, CONFIG.sword.length, 1); return enemy; @@ -818,7 +801,6 @@ const weaponSystems = { }; -// ============= Particle Systems ============= const createParticle = (x, y, angle) => ({ x, y, @@ -839,7 +821,6 @@ const createFootprint = (x, y, direction) => ({ }); -// ============= Rendering System ============= const renderPlayer = () => { ctx.save(); @@ -1049,11 +1030,9 @@ const renderPlayer = () => { // Draw the eyeball...square const dotSize = CONFIG.player.directionIndicator.size; - // Calculate cooldown progress - const timeSinceLastBubble = animationTime - state.player.lastBubbleTime; + // const timeSinceLastBubble = animationTime - state.player.lastBubbleTime; // const cooldownProgress = Math.min(timeSinceLastBubble / CONFIG.bubble.cooldown, 1); - // Set opacity based on cooldown's progress const dotOpacity = getDotOpacity(state, animationTime); ctx.fillStyle = CONFIG.player.directionIndicator.color.replace( @@ -1068,7 +1047,6 @@ const renderPlayer = () => { dotSize ); } else { - // Draw player square ctx.fillStyle = CONFIG.player.color; ctx.fillRect( state.player.x - CONFIG.player.size / 2, @@ -1077,7 +1055,7 @@ const renderPlayer = () => { CONFIG.player.size ); - // Draw direction indicator square with cooldown opacity + // direction indicator square with cooldown opacity const dotSize = CONFIG.player.directionIndicator.size; const dotDistance = CONFIG.player.size / 3; const dotX = state.player.x + state.player.direction.x * dotDistance; @@ -1086,13 +1064,9 @@ const renderPlayer = () => { const timeSinceLastBubble = animationTime - state.player.lastBubbleTime; // const cooldownProgress = Math.min(timeSinceLastBubble / CONFIG.bubble.cooldown, 1); - // Set opacity based on cooldown's progress const dotOpacity = getDotOpacity(state, animationTime); - ctx.fillStyle = CONFIG.player.directionIndicator.color.replace( - '1)', // Replace the full opacity with our calculated opacity - `${dotOpacity})` - ); + ctx.fillStyle = CONFIG.player.directionIndicator.color.replace('1)', `${dotOpacity})`); ctx.fillRect( dotX - dotSize/2, @@ -1106,10 +1080,8 @@ const renderPlayer = () => { }; const renderPlayerHUD = (ctx) => { - // Save the current context state ctx.save(); - // Switch to screen coordinates ctx.setTransform(1, 0, 0, 1, 0, 0); const circleRadius = 15; @@ -1119,22 +1091,17 @@ const renderPlayerHUD = (ctx) => { const totalCircles = 5; const hpPerCircle = state.player.maxHp / totalCircles; - // Track if we need to create explosion particles const currentFilledCircles = Math.ceil(state.player.hp / hpPerCircle); if (currentFilledCircles < state.player.lastRenderedCircles) { - // Create explosion particles for the depleted circle const circleIndex = currentFilledCircles; const particleX = startX + circleIndex * circleSpacing; createHealthCircleExplosion(particleX, startY); } state.player.lastRenderedCircles = currentFilledCircles; - // Draw circles for (let i = 0; i < totalCircles; i++) { const circleX = startX + i * circleSpacing; - const circleY = startY; - - // Calculate how full this circle should be + const circleY = startY; const circleStartHp = i * hpPerCircle; const circleEndHp = (i + 1) * hpPerCircle; let fillAmount = 1; @@ -1145,16 +1112,13 @@ const renderPlayerHUD = (ctx) => { fillAmount = (state.player.hp - circleStartHp) / hpPerCircle; } - // Draw outer circle (border) ctx.beginPath(); ctx.arc(circleX, circleY, circleRadius, 0, Math.PI * 2); ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; ctx.lineWidth = 2; ctx.stroke(); - // Draw fill if (fillAmount > 0) { - // Create gradient for filled portion const gradient = ctx.createRadialGradient( circleX, circleY, 0, circleX, circleY, circleRadius @@ -1171,11 +1135,9 @@ const renderPlayerHUD = (ctx) => { } } - // Restore the context state ctx.restore(); }; -// Add this new function to create the explosion effect const createHealthCircleExplosion = (screenX, screenY) => { const numParticles = 20; const colors = [ @@ -1184,7 +1146,6 @@ const createHealthCircleExplosion = (screenX, screenY) => { 'rgba(150, 0, 0, 0.4)' ]; - // Convert screen coordinates to world coordinates const worldX = screenX - state.camera.x; const worldY = screenY - state.camera.y; @@ -1237,13 +1198,11 @@ const render = () => { const worldSize = gridSize * CONFIG.display.grid.worldSize; const villageSize = CONFIG.world.village.size * gridSize; - // Calculate visible area const startX = Math.floor((-state.camera.x) / gridSize) * gridSize; const startY = Math.floor((-state.camera.y) / gridSize) * gridSize; const endX = startX + GAME_WIDTH + gridSize; const endY = startY + GAME_HEIGHT + gridSize; - // Draw void background ctx.fillStyle = CONFIG.display.grid.voidColor; ctx.fillRect( startX, startY, @@ -1254,13 +1213,10 @@ const render = () => { ctx.fillStyle = CONFIG.world.wilderness.groundColor; ctx.fillRect(0, 0, worldSize, worldSize); - // Then draw the village ground in the top-left + // Then draw the village ctx.fillStyle = CONFIG.world.village.groundColor; ctx.fillRect(0, 0, villageSize, villageSize); - // After drawing village and wilderness grounds, but before grid: - - // The shore gradient const shoreWidth = 60; const shoreColor = 'rgba(179, 220, 255, 0.3)'; @@ -1294,19 +1250,15 @@ const render = () => { ctx.fillStyle = rightShore; ctx.fillRect(worldSize - shoreWidth, 0, shoreWidth, worldSize); - // Draw grid inside of the world + ctx.strokeStyle = CONFIG.display.grid.color; ctx.lineWidth = 1; - - // Draw vertical lines for (let x = 0; x < worldSize; x += gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, worldSize); ctx.stroke(); } - - // Draw horizontal lines for (let y = 0; y < worldSize; y += gridSize) { ctx.beginPath(); ctx.moveTo(0, y); @@ -1314,7 +1266,7 @@ const render = () => { ctx.stroke(); } - // Draw vegetation in the wilderness + // Now add vegetation for (let x = startX; x < endX; x += gridSize) { for (let y = startY; y < endY; y += gridSize) { if (x >= worldSize || y >= worldSize) continue; @@ -1337,13 +1289,9 @@ const render = () => { addToCollisionMap(cellX, cellY, 'tree'); } - // Generate number of sides for this tree const sides = Math.floor(10 + seededRandom(cellX * 3, cellY * 3) * 13); // 10 to 22 sides - - // Choose color for this tree const colorIndex = Math.floor(seededRandom(cellX * 4, cellY * 4) * CONFIG.world.wilderness.vegetation.tree.colors.length); ctx.fillStyle = CONFIG.world.wilderness.vegetation.tree.colors[colorIndex]; - ctx.beginPath(); for (let i = 0; i < sides; i++) { @@ -1371,26 +1319,20 @@ const render = () => { CONFIG.world.wilderness.vegetation.mushroom.frequency) { const config = CONFIG.world.wilderness.vegetation.mushroom; - // Determine if cell uses single color const useSingleColor = seededRandom(cellX * 31, cellY * 31) < config.pattern.singleColor; const cellColorIndex = Math.floor(seededRandom(cellX * 13, cellY * 13) * config.colors.length); - // Create regular grid of circles with slight variation for (let i = config.pattern.margin; i < gridSize - config.pattern.margin; i += config.pattern.spacing) { for (let j = config.pattern.margin; j < gridSize - config.pattern.margin; j += config.pattern.spacing) { - // Offset every other row for more natural pattern const offsetX = (Math.floor(j / config.pattern.spacing) % 2) * (config.pattern.spacing * config.pattern.offset); const px = x + i + offsetX; - const py = y + j; - - // Add variation to position + const py = y + j; const variation = { x: (seededRandom(cellX * i, cellY * j) - 0.5) * config.pattern.variation * config.pattern.spacing, y: (seededRandom(cellX * j, cellY * i) - 0.5) * config.pattern.variation * config.pattern.spacing }; - // Choose color for this dot const colorIndex = useSingleColor ? cellColorIndex : Math.floor(seededRandom(cellX * i, cellY * j) * config.colors.length); ctx.fillStyle = config.colors[colorIndex]; @@ -1412,29 +1354,23 @@ const render = () => { CONFIG.world.wilderness.vegetation.flower.frequency) { const config = CONFIG.world.wilderness.vegetation.flower; - // Determine base color for this cell const colorIndex = Math.floor(seededRandom(cellX * 13, cellY * 13) * config.colors.length); ctx.fillStyle = config.colors[colorIndex]; - // Calculate base rotation for this cell const baseRotation = config.pattern.rotation + (seededRandom(cellX * 14, cellY * 14) - 0.5) * config.pattern.variation; - // Draw tessellating triangle pattern for (let i = config.pattern.margin; i < gridSize - config.pattern.margin; i += config.pattern.spacing) { for (let j = config.pattern.margin; j < gridSize - config.pattern.margin; j += config.pattern.spacing) { - // Offset every other row const offsetX = (Math.floor(j / config.pattern.spacing) % 2) * (config.pattern.spacing / 2); const px = x + i + offsetX; const py = y + j; - // Add slight position variation const variation = { x: (seededRandom(cellX * i, cellY * j) - 0.5) * 4, y: (seededRandom(cellX * j, cellY * i) - 0.5) * 4 }; - // Draw triangle ctx.beginPath(); ctx.save(); ctx.translate(px + variation.x, py + variation.y); @@ -1451,6 +1387,7 @@ const render = () => { } } } + // Grass else if (random < CONFIG.world.wilderness.vegetation.tree.frequency + CONFIG.world.wilderness.vegetation.mushroom.frequency + @@ -1462,21 +1399,17 @@ const render = () => { const config = CONFIG.world.wilderness.vegetation.grass; - // Draw hatching pattern ctx.strokeStyle = config.colors[0]; ctx.lineWidth = 1; - // Calculate base angle with slight variation const baseAngle = config.hatch.angle + (seededRandom(cellX * 20, cellY * 20) - 0.5) * config.hatch.variation; - // Create hatching pattern for (let i = config.hatch.margin; i < gridSize - config.hatch.margin; i += config.hatch.spacing) { for (let j = config.hatch.margin; j < gridSize - config.hatch.margin; j += config.hatch.spacing) { const hatchX = x + i; const hatchY = y + j; - // Add slight position variation const offsetX = (seededRandom(cellX * i, cellY * j) - 0.5) * 2; const offsetY = (seededRandom(cellX * j, cellY * i) - 0.5) * 2; @@ -1496,17 +1429,16 @@ const render = () => { } } - // After drawing vegetation but before drawing the player, add: + // After drawing vegetation but before drawing the player state.villagers.forEach(villager => { ctx.save(); ctx.fillStyle = villager.color; - // Add slight bobbing motion for rescued villagers + // Bobbing if (villager.status === 'rescued') { const bobOffset = Math.sin(animationTime * villager.bobSpeed) * villager.bobAmplitude; drawVillagerShape(ctx, villager.x, villager.y + bobOffset, villager.shape, CONFIG.world.villagers.size); } else if (villager.status === 'lost') { - // Horizontal bobbing for lost villagers const lostBobOffset = Math.sin(animationTime * villager.lostBobSpeed) * villager.lostBobAmplitude; drawVillagerShape(ctx, villager.x + lostBobOffset, villager.y, villager.shape, CONFIG.world.villagers.size); } else { @@ -1517,10 +1449,7 @@ const render = () => { ctx.restore(); }); - // Render enemies enemySystem.renderEnemies(ctx, state.enemies); - - // Draw player renderPlayer(); state.footprints.forEach(footprint => { @@ -1549,7 +1478,6 @@ const render = () => { ctx.restore(); }); - // Render particles state.particles = state.particles.filter(particle => { const age = animationTime - particle.createdAt; if (age >= particle.lifetime) return false; @@ -1568,22 +1496,18 @@ const render = () => { return true; }); - // Render HUD elements renderPlayerHUD(ctx); - // Update player invulnerability if (state.player.isInvulnerable && animationTime >= state.player.invulnerableUntil) { state.player.isInvulnerable = false; } - // Render diamonds renderDiamonds(); ctx.restore(); }; -// ============= Game Loop ============= const updatePlayer = () => { Object.assign(state, weaponSystems.updateBubbles(state, animationTime)); Object.assign(state, weaponSystems.updateSwordSwing(state, animationTime)); @@ -1593,7 +1517,6 @@ const updatePlayer = () => { return (animationTime - footprint.createdAt) < CONFIG.footprints.lifetime; }); - // Update player direction for idle animation if (!keys.size && !state.player.isSwinging && !state.player.isDefending) { const idleTime = animationTime - state.player.lastInputTime; @@ -1607,17 +1530,14 @@ const updatePlayer = () => { y: Math.sin(newAngle) }; } else { - // Reset direction to base direction when not idle state.player.direction = { ...state.player.baseDirection }; } } else { - // Update last input time when other actions occur if (state.player.isSwinging || state.player.isDefending) { state.player.lastInputTime = animationTime; } } - // Check for villager collisions state.villagers.forEach(villager => { if (villager.status === 'rescued') return; @@ -1629,25 +1549,21 @@ const updatePlayer = () => { villager.status = 'rescued'; state.player.rescuedCount++; - // Check for sword unlock if (state.player.rescuedCount >= CONFIG.player.equipment.swordUnlockCount && !state.player.swordUnlocked) { state.player.swordUnlocked = true; state.player.equipment = 'sword'; // Auto-equip sword showSwordUnlockAnimation(); } - // Assign new position in village when rescued const villageSize = CONFIG.world.village.size * CONFIG.display.grid.size; const margin = CONFIG.world.villagers.size; villager.x = margin + Math.random() * (villageSize - margin * 2); villager.y = margin + Math.random() * (villageSize - margin * 2); - // Check if all villagers are rescued const allRescued = state.villagers.every(v => v.status === 'rescued'); if (allRescued && !state.gameComplete) { state.gameComplete = true; - // Show completion message const message = document.createElement('div'); message.textContent = CONFIG.world.villagers.rescueMessage; message.style.position = 'fixed'; @@ -1680,7 +1596,6 @@ const gameLoop = (currentTime) => { if (deltaTime >= FRAME_TIME) { animationTime += FRAME_TIME; - // Check for player death if (state.player.hp <= 0 && !state.player.isDead) { state.player.isDead = true; showGameOver(); @@ -1688,7 +1603,7 @@ const gameLoop = (currentTime) => { updatePlayer(); state.enemies = enemySystem.updateEnemies(state.enemies, deltaTime); - collectDiamonds(); // Add this line to check for diamond collection + collectDiamonds(); render(); lastFrameTime = currentTime; @@ -1698,7 +1613,9 @@ const gameLoop = (currentTime) => { }; -// ============= Setup & Initialization ============= + + + const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); @@ -1723,16 +1640,13 @@ resizeCanvas(); state.villagers = generateVillagers(); state.enemies = enemySystem.generateEnemies(state.villagers, state.collisionMap); -// Start the game loop requestAnimationFrame(gameLoop); const getDotOpacity = (state, animationTime) => { - // Get bubble cooldown opacity const timeSinceLastBubble = animationTime - state.player.lastBubbleTime; const bubbleCooldownProgress = Math.min(timeSinceLastBubble / CONFIG.bubble.cooldown, 1); const bubbleOpacity = 0.1 + (bubbleCooldownProgress * 0.9); - // Get dash cooldown opacity let dashOpacity = 1; if (state.player.dashExhausted) { const timeSinceExhaustion = animationTime - state.player.lastDashEnd; @@ -1740,7 +1654,7 @@ const getDotOpacity = (state, animationTime) => { dashOpacity = 0.1 + (Math.min(dashCooldownProgress, 1) * 0.9); } else if (state.player.isDashing) { const dashProgress = (animationTime - state.player.dashStartTime) / CONFIG.player.dash.duration; - dashOpacity = 1 - (dashProgress * 0.7); // Fade to 0.3 during dash + dashOpacity = 1 - (dashProgress * 0.7); } let blinkOpacity = 1; @@ -1751,12 +1665,10 @@ const getDotOpacity = (state, animationTime) => { } } - // Return the lowest opacity of all systems return Math.min(bubbleOpacity, dashOpacity, blinkOpacity); }; const checkCollision = (x, y, size) => { - // Check corners of player square const halfSize = size / 2; const corners = [ { x: x - halfSize, y: y - halfSize }, // Top-left @@ -1769,15 +1681,10 @@ const checkCollision = (x, y, size) => { }; const createNaturalCluster = (centerX, centerY, config, cellX, cellY, i) => { - // Base angle and distance const angle = seededRandom(cellX * 8 + i, cellY * 8) * Math.PI * 2; - - // Make clusters denser in the middle and sparser on edges const distanceFromCenter = seededRandom(cellX * 9 + i, cellY * 9); - // Use square root to bias towards center const distance = Math.sqrt(distanceFromCenter) * config.size.max; - // Add some variation const variation = { x: (seededRandom(cellX * 10 + i, cellY * 10) - 0.5) * config.size.max, y: (seededRandom(cellX * 11 + i, cellY * 11) - 0.5) * config.size.max @@ -1789,7 +1696,7 @@ const createNaturalCluster = (centerX, centerY, config, cellX, cellY, i) => { size: config.size.min + seededRandom(cellX * 3 + i, cellY * 3) * (config.size.max - config.size.min) * - // Make items on the edge slightly smaller + // This makes stuff on the edge a little smaller (1 - (distance / config.cluster.spread) * 0.3) }; }; @@ -1797,7 +1704,6 @@ const createNaturalCluster = (centerX, centerY, config, cellX, cellY, i) => { const renderTree = (ctx, x, y, size, isTop = false) => { ctx.fillStyle = CONFIG.world.wilderness.vegetation.tree.color; if (isTop) { - // Draw only the top 2/3 of the tree ctx.beginPath(); ctx.arc( x + CONFIG.display.grid.size/2, @@ -1807,7 +1713,6 @@ const renderTree = (ctx, x, y, size, isTop = false) => { ); ctx.fill(); } else { - // Draw only the bottom 2/3 of the tree ctx.beginPath(); ctx.arc( x + CONFIG.display.grid.size/2, @@ -1888,13 +1793,12 @@ const getCellInfo = (x, y) => { const createFloatingMessage = (messageConfig) => { const { text, - duration = null, // null means permanent + duration = null, // null for permanent backgroundColor = 'rgba(0, 0, 0, 0.8)', glowColor = null, - glowDuration = 2000 // 2s default for glow animation + glowDuration = 2000 } = messageConfig; - // Create container const container = document.createElement('div'); container.style.position = 'fixed'; container.style.top = '50%'; @@ -1905,7 +1809,6 @@ const createFloatingMessage = (messageConfig) => { container.style.borderRadius = '10px'; container.style.zIndex = '1000'; - // Add message text const message = document.createElement('div'); message.textContent = text; message.style.color = 'white'; @@ -1913,7 +1816,6 @@ const createFloatingMessage = (messageConfig) => { message.style.textAlign = 'center'; container.appendChild(message); - // Add glow animation if specified if (glowColor) { container.style.animation = `glow ${glowDuration}ms ease-in-out`; @@ -1927,17 +1829,14 @@ const createFloatingMessage = (messageConfig) => { `; document.head.appendChild(style); - // Clean up style element after animation setTimeout(() => { document.head.removeChild(style); if (!duration) { - // For permanent messages, set final glow state after animation container.style.boxShadow = `0 0 30px 10px ${glowColor}`; } }, glowDuration); } - // Add to document and remove after duration (if specified) document.body.appendChild(container); if (duration) { setTimeout(() => { @@ -1945,29 +1844,26 @@ const createFloatingMessage = (messageConfig) => { }, duration); } - return container; // Return container for manual removal if needed + return container; }; -// Update showGameOver to be permanent const showGameOver = () => { createFloatingMessage({ text: 'Game Over\nPress Space to restart', - duration: null, // Permanent message + duration: null, glowColor: 'rgba(255, 0, 0, 0.6)' }); }; -// Update showSwordUnlockAnimation with explicit timing const showSwordUnlockAnimation = () => { createFloatingMessage({ text: CONFIG.player.equipment.unlockAnimation.messageText, duration: CONFIG.player.equipment.unlockAnimation.duration, glowColor: CONFIG.player.equipment.unlockAnimation.glowColor, - glowDuration: 2000 // Explicit glow animation duration + glowDuration: 2000 }); }; -// Update the player's HP when collecting diamonds const collectDiamonds = () => { state.diamonds.forEach(diamond => { const dx = state.player.x - diamond.x; @@ -1975,12 +1871,11 @@ const collectDiamonds = () => { const distance = Math.sqrt(dx * dx + dy * dy); if (distance < (CONFIG.player.size / 2 + diamond.size / 2) && !diamond.collected) { - diamond.collected = true; // Mark diamond as collected + diamond.collected = true; state.player.hp = Math.min(state.player.hp + 1, state.player.maxHp); // Restore 1 HP, max out at maxHp } }); - // Remove collected diamonds from the state state.diamonds = state.diamonds.filter(diamond => !diamond.collected); }; @@ -1988,7 +1883,6 @@ const renderDiamonds = () => { state.diamonds.forEach(diamond => { ctx.save(); - // Add pulsing glow effect const pulseIntensity = 0.5 + Math.sin(animationTime * 0.005) * 0.3; // Pulsing between 0.2 and 0.8 // Outer glow @@ -2011,7 +1905,7 @@ const renderDiamonds = () => { ctx.fillStyle = `rgba(255, 223, 0, ${0.8 + pulseIntensity * 0.2})`; // Brighter gold ctx.fill(); - // Shine effect + // Shiny ctx.beginPath(); ctx.arc( diamond.x - diamond.size * 0.2, diff --git a/html/plains/index.html b/html/plains/index.html index 0ca2807..af634ad 100644 --- a/html/plains/index.html +++ b/html/plains/index.html @@ -4,7 +4,7 @@ <title>Plains</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <meta name="description" content="Little black square wanted to be a fucking Jedi."> + <meta name="description" content="Help little black square rescue the villagers, and battle the monsters."> <style> * { margin: 0; @@ -24,6 +24,7 @@ </head> <body> <canvas id="gameCanvas"></canvas> + <a href="about.html" style="position: absolute; top: 12px; right: 12px; color: black; text-decoration: underline; font-size: 16px;">About</a> <script src="enemies.js"></script> <script src="game.js"></script> </body>