diff options
-rw-r--r-- | html/voxels/js/game.js | 162 |
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 |