about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2024-12-28 19:51:08 -0500
committerelioat <elioat@tilde.institute>2024-12-28 19:51:08 -0500
commit426c199bb4074fc33861e656d70ec7fa5fdc0265 (patch)
treeec3d4ed3e298ac69f1a89cdd32590b687c3039c6
parent17efb47faea5001269fce2188edc646e9de214e7 (diff)
downloadtour-426c199bb4074fc33861e656d70ec7fa5fdc0265.tar.gz
*
-rw-r--r--html/voxels/js/game.js162
1 files changed, 137 insertions, 25 deletions
diff --git a/html/voxels/js/game.js b/html/voxels/js/game.js
index 98d3476..4547634 100644
--- a/html/voxels/js/game.js
+++ b/html/voxels/js/game.js
@@ -16,9 +16,17 @@ class IsometricGame {
             targetY: 0,
             size: 20,
             path: [], // Array to store waypoints
-            currentWaypoint: null
+            currentWaypoint: null,
+            jumpHeight: 0,
+            jumpProgress: 0,
+            isJumping: false,
+            startX: 0,
+            startY: 0
         };
         
+        // Add particle system
+        this.particles = [];
+        
         // Handle window resize
         this.resizeCanvas();
         window.addEventListener('resize', () => this.resizeCanvas());
@@ -86,41 +94,44 @@ class IsometricGame {
         // Convert player grid position to isometric coordinates
         const iso = this.toIsometric(this.player.x, this.player.y);
         
-        // Draw player shadow (slightly offset and darker)
+        // Apply jump height offset
+        const jumpOffset = this.player.jumpHeight || 0;
+        
+        // Draw player shadow (gets smaller when jumping)
+        const shadowScale = Math.max(0.2, 1 - (jumpOffset / this.tileHeight));
         this.ctx.beginPath();
         this.ctx.ellipse(
             iso.x + this.offsetX,
             iso.y + this.offsetY + 2,
-            this.player.size * 0.8,
-            this.player.size * 0.3,
+            this.player.size * 0.8 * shadowScale,
+            this.player.size * 0.3 * shadowScale,
             0,
             0,
             Math.PI * 2
         );
-        this.ctx.fillStyle = 'rgba(0,0,0,0.2)';
+        this.ctx.fillStyle = `rgba(0,0,0,${0.2 * shadowScale})`;
         this.ctx.fill();
 
-        // Draw player "body" (rectangle)
+        // Draw player body with jump offset
         this.ctx.beginPath();
         this.ctx.fillStyle = '#ff4444';
         this.ctx.strokeStyle = '#aa0000';
         
-        // Draw a thin rectangle for the body
         const bodyHeight = this.player.size * 2;
         const bodyWidth = this.player.size * 0.8;
         
         this.ctx.save();
-        this.ctx.translate(iso.x + this.offsetX, iso.y + this.offsetY);
-        this.ctx.scale(1, 0.5); // Apply isometric perspective
+        this.ctx.translate(iso.x + this.offsetX, iso.y + this.offsetY - jumpOffset);
+        this.ctx.scale(1, 0.5);
         this.ctx.fillRect(-bodyWidth/2, -bodyHeight, bodyWidth, bodyHeight);
         this.ctx.strokeRect(-bodyWidth/2, -bodyHeight, bodyWidth, bodyHeight);
         this.ctx.restore();
 
-        // Draw player head (circle)
+        // Draw player head with jump offset
         this.ctx.beginPath();
         this.ctx.ellipse(
             iso.x + this.offsetX,
-            iso.y + this.offsetY - this.player.size,
+            iso.y + this.offsetY - this.player.size - jumpOffset,
             this.player.size,
             this.player.size * 0.5,
             0,
@@ -157,28 +168,45 @@ class IsometricGame {
     }
     
     updatePlayer() {
-        const speed = 0.1;
+        const jumpDuration = 0.1; // Faster jump for snappier movement
+        const maxJumpHeight = this.tileHeight;
 
         // If we don't have a current waypoint but have a path, get next waypoint
         if (!this.player.currentWaypoint && this.player.path.length > 0) {
             this.player.currentWaypoint = this.player.path.shift();
+            this.player.isJumping = true;
+            this.player.jumpProgress = 0;
+            
+            // Store starting position for interpolation
+            this.player.startX = this.player.x;
+            this.player.startY = this.player.y;
         }
 
         // Move towards current waypoint
         if (this.player.currentWaypoint) {
-            const dx = this.player.currentWaypoint.x - this.player.x;
-            const dy = this.player.currentWaypoint.y - this.player.y;
-            
-            // Move towards waypoint
-            this.player.x += dx * speed;
-            this.player.y += dy * speed;
-
-            // Check if we're close enough to waypoint to consider it reached
-            const distance = Math.sqrt(dx * dx + dy * dy);
-            if (distance < 0.1) {
-                this.player.x = this.player.currentWaypoint.x;
-                this.player.y = this.player.currentWaypoint.y;
-                this.player.currentWaypoint = null;
+            // Update jump animation
+            if (this.player.isJumping) {
+                this.player.jumpProgress += jumpDuration;
+                
+                // Clamp progress to 1
+                if (this.player.jumpProgress > 1) this.player.jumpProgress = 1;
+                
+                // Parabolic jump arc
+                this.player.jumpHeight = Math.sin(this.player.jumpProgress * Math.PI) * maxJumpHeight;
+                
+                // Precise interpolation between points
+                this.player.x = this.player.startX + (this.player.currentWaypoint.x - this.player.startX) * this.player.jumpProgress;
+                this.player.y = this.player.startY + (this.player.currentWaypoint.y - this.player.startY) * this.player.jumpProgress;
+                
+                // Landing
+                if (this.player.jumpProgress >= 1) {
+                    this.player.isJumping = false;
+                    this.player.jumpHeight = 0;
+                    this.player.x = this.player.currentWaypoint.x;
+                    this.player.y = this.player.currentWaypoint.y;
+                    this.createDustParticles(this.player.x, this.player.y);
+                    this.player.currentWaypoint = null;
+                }
             }
         }
     }
@@ -219,12 +247,96 @@ class IsometricGame {
         
         // Draw game elements
         this.drawGrid();
+        this.updateParticles();
+        this.drawParticles();
         this.updatePlayer();
         this.drawPlayer();
         
         // Continue game loop
         requestAnimationFrame(() => this.gameLoop());
     }
+
+    // Add new particle system methods
+    createDustParticles(x, y) {
+        const particleCount = 12; // Increased for more particles
+        for (let i = 0; i < particleCount; i++) {
+            // Randomize the angle slightly
+            const baseAngle = (Math.PI * 2 * i) / particleCount;
+            const randomAngle = baseAngle + (Math.random() - 0.5) * 0.5;
+            
+            // Random speed and size variations
+            const speed = 0.3 + Math.random() * 0.4;
+            const initialSize = (this.player.size * 0.15) + (Math.random() * this.player.size * 0.15);
+            
+            // Random grey color
+            const greyValue = 220 + Math.floor(Math.random() * 35);
+            const color = `rgb(${greyValue}, ${greyValue}, ${greyValue})`;
+            
+            this.particles.push({
+                x: x,
+                y: y,
+                dx: Math.cos(randomAngle) * speed,
+                dy: Math.sin(randomAngle) * speed,
+                life: 0.8 + Math.random() * 0.4, // Random initial life
+                size: initialSize,
+                color: color,
+                initialSize: initialSize,
+                rotationSpeed: (Math.random() - 0.5) * 0.2,
+                rotation: Math.random() * Math.PI * 2
+            });
+        }
+    }
+
+    updateParticles() {
+        for (let i = this.particles.length - 1; i >= 0; i--) {
+            const particle = this.particles[i];
+            
+            // Update position with slight gravity effect
+            particle.x += particle.dx;
+            particle.y += particle.dy;
+            particle.dy += 0.01; // Slight upward drift
+            
+            // Update rotation
+            particle.rotation += particle.rotationSpeed;
+            
+            // Non-linear fade out
+            particle.life -= 0.03;
+            particle.size = particle.initialSize * (particle.life * 1.5); // Grow slightly as they fade
+            
+            // Remove dead particles
+            if (particle.life <= 0) {
+                this.particles.splice(i, 1);
+            }
+        }
+    }
+
+    drawParticles() {
+        for (const particle of this.particles) {
+            const iso = this.toIsometric(particle.x, particle.y);
+            
+            this.ctx.save();
+            this.ctx.translate(iso.x + this.offsetX, iso.y + this.offsetY);
+            this.ctx.rotate(particle.rotation);
+            
+            // Draw a slightly irregular dust puff
+            this.ctx.beginPath();
+            const points = 5;
+            for (let i = 0; i < points * 2; i++) {
+                const angle = (i * Math.PI) / points;
+                const radius = particle.size * (i % 2 ? 0.7 : 1);
+                const x = Math.cos(angle) * radius;
+                const y = Math.sin(angle) * radius;
+                if (i === 0) this.ctx.moveTo(x, y);
+                else this.ctx.lineTo(x, y);
+            }
+            this.ctx.closePath();
+            
+            this.ctx.fillStyle = `rgba(${particle.color.slice(4, -1)}, ${particle.life * 0.5})`;
+            this.ctx.fill();
+            
+            this.ctx.restore();
+        }
+    }
 }
 
 // Start the game when the page loads