diff options
Diffstat (limited to 'html/space')
-rw-r--r-- | html/space/game.js | 8 | ||||
-rw-r--r-- | html/space/gameState.js | 71 | ||||
-rw-r--r-- | html/space/index.html | 1 | ||||
-rw-r--r-- | html/space/input.js | 110 | ||||
-rw-r--r-- | html/space/physics.js | 279 | ||||
-rw-r--r-- | html/space/renderer.js | 202 |
6 files changed, 433 insertions, 238 deletions
diff --git a/html/space/game.js b/html/space/game.js index 0a106cf..ecd7abc 100644 --- a/html/space/game.js +++ b/html/space/game.js @@ -10,10 +10,12 @@ let isRunning = true; // Initialize all systems function init() { + console.log('Initializing game...'); initRenderer(); initInput(); initPhysics(); initGameState(); + console.log('Game initialized'); } // Main game loop using requestAnimationFrame @@ -29,9 +31,15 @@ function gameLoop(timestamp) { updateGameState(deltaTime); render(); + // Debug output + if (Math.random() < 0.01) { // Only log occasionally to avoid spam + console.log('Game loop running, deltaTime:', deltaTime); + } + requestAnimationFrame(gameLoop); } // Start the game +console.log('Starting game...'); init(); requestAnimationFrame(gameLoop); \ No newline at end of file diff --git a/html/space/gameState.js b/html/space/gameState.js index 85f56b0..620bc1f 100644 --- a/html/space/gameState.js +++ b/html/space/gameState.js @@ -6,11 +6,14 @@ import { getPlayerState } from './physics.js'; const planets = []; const enemyShips = []; const projectiles = []; +let lastEnemySpawn = 0; // Space dimensions const SPACE_SIZE = 10000; // Increased from implicit 2000 const PLANET_DISTANCE = 5000; // Increased from 1000 const ENEMY_SPAWN_DISTANCE = 3000; // Increased from 500 +const ENEMY_SPAWN_INTERVAL = 5000; // 5 seconds +const MAX_ENEMIES = 5; // Initialize game state export function initGameState() { @@ -27,6 +30,11 @@ export function initGameState() { color: '#e74c3c' }); + // Reset other state + enemyShips.length = 0; + projectiles.length = 0; + lastEnemySpawn = Date.now(); + // Create initial enemy ships for (let i = 0; i < 5; i++) { createEnemyShip(); @@ -56,8 +64,28 @@ function createEnemyShip() { // Update game state export function updateGameState(deltaTime) { + const currentTime = Date.now(); const player = getPlayerState(); + + // Spawn enemies + if (currentTime - lastEnemySpawn > ENEMY_SPAWN_INTERVAL && + enemyShips.length < MAX_ENEMIES) { + spawnEnemy(); + lastEnemySpawn = currentTime; + } + + // Update projectiles + projectiles.forEach((projectile, index) => { + projectile.position.x += projectile.velocity.x * deltaTime; + projectile.position.y += projectile.velocity.y * deltaTime; + projectile.position.z += projectile.velocity.z * deltaTime; + // Remove if too old + if (currentTime - projectile.createdAt > 5000) { + projectiles.splice(index, 1); + } + }); + // Update enemy ships enemyShips.forEach((ship, index) => { // Move ships @@ -85,28 +113,10 @@ export function updateGameState(deltaTime) { if (inputState.fireSecondary) { createProjectile('secondary'); } - - // Update projectiles - projectiles.forEach((projectile, index) => { - projectile.position.x += projectile.velocity.x * deltaTime; - projectile.position.y += projectile.velocity.y * deltaTime; - projectile.position.z += projectile.velocity.z * deltaTime; - - // Remove projectiles that are too far away - const distance = Math.sqrt( - Math.pow(projectile.position.x - player.position.x, 2) + - Math.pow(projectile.position.y - player.position.y, 2) + - Math.pow(projectile.position.z - player.position.z, 2) - ); - - if (distance > SPACE_SIZE/2) { - projectiles.splice(index, 1); - } - }); } // Create a new projectile -function createProjectile(type) { +export function createProjectile(type) { const player = getPlayerState(); const speed = type === 'primary' ? 10 : 7.5; // Reduced from 20/15 const damage = type === 'primary' ? 25 : 10; @@ -124,7 +134,28 @@ function createProjectile(type) { y: sinX * speed, z: cosY * cosX * speed }, - damage + damage, + createdAt: Date.now() + }); +} + +// Spawn a new enemy ship +function spawnEnemy() { + const angle = Math.random() * Math.PI * 2; + const distance = ENEMY_SPAWN_DISTANCE; + + enemyShips.push({ + position: { + x: Math.cos(angle) * distance, + y: 0, + z: Math.sin(angle) * distance + }, + velocity: { + x: (Math.random() - 0.5) * 0.5, + y: (Math.random() - 0.5) * 0.5, + z: (Math.random() - 0.5) * 0.5 + }, + health: 100 }); } diff --git a/html/space/index.html b/html/space/index.html index 52dffa0..9db977d 100644 --- a/html/space/index.html +++ b/html/space/index.html @@ -16,6 +16,7 @@ </style> </head> <body> + <canvas id="gameCanvas"></canvas> <script type="module" src="game.js"></script> </body> </html> \ No newline at end of file diff --git a/html/space/input.js b/html/space/input.js index dadecb9..19ea56c 100644 --- a/html/space/input.js +++ b/html/space/input.js @@ -1,16 +1,9 @@ -// Input module using keyboard controls -const keys = { - w: false, - a: false, - s: false, - d: false, - ArrowUp: false, - ArrowDown: false, - ArrowLeft: false, - ArrowRight: false, - ' ': false, // space - e: false -}; +// Input handling module +import { updatePlayerControls } from './physics.js'; + +let keys = {}; +let mouseX = 0; +let mouseY = 0; // Input state that other modules can read export const inputState = { @@ -24,48 +17,67 @@ export const inputState = { // Initialize input handlers export function initInput() { - window.addEventListener('keydown', handleKeyDown); - window.addEventListener('keyup', handleKeyUp); -} + // Keyboard event listeners + document.addEventListener('keydown', (e) => { + keys[e.key.toLowerCase()] = true; + }); -// Update input state based on current key presses -export function updateInput() { - // Reset input state - inputState.thrust = 0; - inputState.strafe = 0; - inputState.yaw = 0; - inputState.pitch = 0; - inputState.firePrimary = false; - inputState.fireSecondary = false; + document.addEventListener('keyup', (e) => { + keys[e.key.toLowerCase()] = false; + }); - // Thrust controls (W/S) - if (keys.w) inputState.thrust = 1; // Forward thrust - if (keys.s) inputState.thrust = -1; // Backward thrust + // Mouse movement for heading + document.addEventListener('mousemove', (e) => { + // Calculate mouse position relative to center of canvas + const canvas = document.querySelector('canvas'); + const rect = canvas.getBoundingClientRect(); + const centerX = rect.left + rect.width / 2; + const centerY = rect.top + rect.height / 2; + + mouseX = (e.clientX - centerX) / (rect.width / 2); + mouseY = (e.clientY - centerY) / (rect.height / 2); + }); - // Strafe controls (A/D) - if (keys.a) inputState.strafe = -1; // Left strafe - if (keys.d) inputState.strafe = 1; // Right strafe + // Mouse click for primary weapon + document.addEventListener('mousedown', (e) => { + if (e.button === 0) { // Left click + keys['fire'] = true; + } + }); - // Rotation controls (Arrow keys) - if (keys.ArrowLeft) inputState.yaw = -1; - if (keys.ArrowRight) inputState.yaw = 1; - if (keys.ArrowUp) inputState.pitch = -1; - if (keys.ArrowDown) inputState.pitch = 1; + document.addEventListener('mouseup', (e) => { + if (e.button === 0) { // Left click + keys['fire'] = false; + } + }); - // Weapons - if (keys[' ']) inputState.firePrimary = true; - if (keys.e) inputState.fireSecondary = true; -} + // E key for secondary weapon + document.addEventListener('keydown', (e) => { + if (e.key.toLowerCase() === 'e') { + keys['secondary'] = true; + } + }); -// Event handlers -function handleKeyDown(e) { - if (keys.hasOwnProperty(e.key)) { - keys[e.key] = true; - } + document.addEventListener('keyup', (e) => { + if (e.key.toLowerCase() === 'e') { + keys['secondary'] = false; + } + }); } -function handleKeyUp(e) { - if (keys.hasOwnProperty(e.key)) { - keys[e.key] = false; - } +// Update controls based on current input state +export function updateInput() { + const controls = { + thrust: keys[' '] || false, // Space bar for thrust + up: keys['w'] || false, // W for upward strafe + down: keys['s'] || false, // S for downward strafe + left: keys['a'] || false, // A for left strafe + right: keys['d'] || false, // D for right strafe + fire: keys['fire'] || false, + secondary: keys['secondary'] || false, + mouseX, + mouseY + }; + + updatePlayerControls(controls); } \ No newline at end of file diff --git a/html/space/physics.js b/html/space/physics.js index 6b71601..d4dfe55 100644 --- a/html/space/physics.js +++ b/html/space/physics.js @@ -1,5 +1,6 @@ // Physics module for handling movement and collisions import { inputState } from './input.js'; +import { createProjectile } from './gameState.js'; // Constants const MAX_THRUST = 0.5; // Reduced from 2 @@ -8,113 +9,245 @@ const DECELERATION = 0.001; // Reduced from 0.01 const BASE_ROTATION_SPEED = 0.001; // Reduced from 0.005 const ROTATION_ACCELERATION = 0.0005; // Reduced from 0.002 const ROTATION_DECELERATION = 0.0002; // Reduced from 0.001 +const MOUSE_SENSITIVITY = 0.03; // Increased from 0.01 for sharper turns +const MAX_SPEED = 1.0; // Maximum speed in any direction + +// Weapon constants +export const PRIMARY_COOLDOWN = 100; // ms between primary shots +export const SECONDARY_COOLDOWN = 2000; // ms between secondary shots +export const PRIMARY_BURST_COUNT = 3; // Number of shots in primary burst +export const PRIMARY_BURST_DELAY = 50; // ms between burst shots // Player state -const player = { +let playerState = { position: { x: 0, y: 0, z: 0 }, velocity: { x: 0, y: 0, z: 0 }, - rotation: { x: 0, y: 0, z: 0 }, - rotationSpeed: { x: 0, y: 0 } // Added for smooth rotation + rotation: { x: 0, y: 0 }, + thrust: 0, + strafe: 0, + weapons: { + primary: { + lastFired: 0, + burstCount: 0, + burstTimer: 0 + }, + secondary: { + lastFired: 0 + } + } }; // Initialize physics export function initPhysics() { // Reset player state - player.position = { x: 0, y: 0, z: 0 }; - player.velocity = { x: 0, y: 0, z: 0 }; - player.rotation = { x: 0, y: 0, z: 0 }; - player.rotationSpeed = { x: 0, y: 0 }; + playerState.position = { x: 0, y: 0, z: 0 }; + playerState.velocity = { x: 0, y: 0, z: 0 }; + playerState.rotation = { x: 0, y: 0 }; + playerState.thrust = 0; + playerState.strafe = 0; } -// Helper function to apply force in a direction -function applyForce(direction, magnitude, deltaTime) { - const force = { - x: direction.x * magnitude * deltaTime, - y: direction.y * magnitude * deltaTime, - z: direction.z * magnitude * deltaTime - }; - - player.velocity.x += force.x; - player.velocity.y += force.y; - player.velocity.z += force.z; +// Helper function to limit speed in a direction +function limitSpeed(velocity, maxSpeed) { + const speed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z); + if (speed > maxSpeed) { + const scale = maxSpeed / speed; + velocity.x *= scale; + velocity.y *= scale; + velocity.z *= scale; + } } -// Update physics -export function updatePhysics(deltaTime) { - // Update rotation speed based on input - if (inputState.yaw !== 0) { - player.rotationSpeed.y += inputState.yaw * ROTATION_ACCELERATION * deltaTime; +// Update player controls +export function updatePlayerControls(controls) { + // Handle thrust (space bar) + if (controls.thrust) { + playerState.thrust = Math.min(playerState.thrust + THRUST_ACCELERATION, MAX_THRUST); } else { - // Smoothly decelerate rotation - player.rotationSpeed.y *= (1 - ROTATION_DECELERATION * deltaTime); + // Apply deceleration when no thrust input + if (playerState.thrust > 0) { + playerState.thrust = Math.max(playerState.thrust - DECELERATION, 0); + } } - if (inputState.pitch !== 0) { - player.rotationSpeed.x += inputState.pitch * ROTATION_ACCELERATION * deltaTime; + // Handle vertical strafing (W/S) + if (controls.up) { + playerState.verticalStrafe = Math.min(playerState.verticalStrafe + THRUST_ACCELERATION, MAX_THRUST); + } else if (controls.down) { + playerState.verticalStrafe = Math.max(playerState.verticalStrafe - THRUST_ACCELERATION, -MAX_THRUST); } else { - // Smoothly decelerate rotation - player.rotationSpeed.x *= (1 - ROTATION_DECELERATION * deltaTime); + // Apply deceleration when no vertical strafe input + if (playerState.verticalStrafe > 0) { + playerState.verticalStrafe = Math.max(playerState.verticalStrafe - DECELERATION, 0); + } else if (playerState.verticalStrafe < 0) { + playerState.verticalStrafe = Math.min(playerState.verticalStrafe + DECELERATION, 0); + } + } + + // Handle horizontal strafing (A/D) + if (controls.left) { + playerState.horizontalStrafe = Math.min(playerState.horizontalStrafe + THRUST_ACCELERATION, MAX_THRUST); + } else if (controls.right) { + playerState.horizontalStrafe = Math.max(playerState.horizontalStrafe - THRUST_ACCELERATION, -MAX_THRUST); + } else { + // Apply deceleration when no horizontal strafe input + if (playerState.horizontalStrafe > 0) { + playerState.horizontalStrafe = Math.max(playerState.horizontalStrafe - DECELERATION, 0); + } else if (playerState.horizontalStrafe < 0) { + playerState.horizontalStrafe = Math.min(playerState.horizontalStrafe + DECELERATION, 0); + } + } + + // Handle mouse-based rotation with smoothing + const targetRotationY = controls.mouseX * MOUSE_SENSITIVITY; + const targetRotationX = controls.mouseY * MOUSE_SENSITIVITY; + + // Smooth rotation using lerp with faster response + playerState.rotation.y += (targetRotationY - playerState.rotation.y) * 0.2; + playerState.rotation.x += (targetRotationX - playerState.rotation.x) * 0.2; + + // Clamp pitch rotation + playerState.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, playerState.rotation.x)); + + // Handle weapons with cooldowns + const currentTime = Date.now(); + + // Primary weapon (burst fire) + if (controls.fire) { + const primary = playerState.weapons.primary; + if (currentTime - primary.lastFired >= PRIMARY_COOLDOWN && primary.burstCount === 0) { + primary.burstCount = PRIMARY_BURST_COUNT; + primary.burstTimer = currentTime; + firePrimaryWeapon(); + primary.lastFired = currentTime; + } } - // Apply rotation based on current rotation speed - player.rotation.y += player.rotationSpeed.y * BASE_ROTATION_SPEED * deltaTime; - player.rotation.x += player.rotationSpeed.x * BASE_ROTATION_SPEED * deltaTime; + // Secondary weapon (single shot with cooldown) + if (controls.secondary && currentTime - playerState.weapons.secondary.lastFired >= SECONDARY_COOLDOWN) { + fireSecondaryWeapon(); + playerState.weapons.secondary.lastFired = currentTime; + } - // Calculate forward and right vectors based on current rotation - const cosY = Math.cos(player.rotation.y); - const sinY = Math.sin(player.rotation.y); - const cosX = Math.cos(player.rotation.x); - const sinX = Math.sin(player.rotation.x); + // Handle burst fire timing + const primary = playerState.weapons.primary; + if (primary.burstCount > 0 && currentTime - primary.burstTimer >= PRIMARY_BURST_DELAY) { + firePrimaryWeapon(); + primary.burstCount--; + primary.burstTimer = currentTime; + } +} - // Forward vector (in the direction the ship is facing) +// Update physics +export function updatePhysics(deltaTime) { + // Calculate forward and right vectors based on rotation const forward = { - x: sinY * cosX, - y: sinX, - z: cosY * cosX + x: Math.sin(playerState.rotation.y) * Math.cos(playerState.rotation.x), + y: -Math.sin(playerState.rotation.x), + z: Math.cos(playerState.rotation.y) * Math.cos(playerState.rotation.x) }; - // Right vector (perpendicular to forward) const right = { - x: cosY, + x: Math.cos(playerState.rotation.y), y: 0, - z: -sinY + z: -Math.sin(playerState.rotation.y) }; - // Apply thrust in the direction the ship is facing - if (inputState.thrust !== 0) { - applyForce(forward, inputState.thrust * THRUST_ACCELERATION, deltaTime); - } + const up = { x: 0, y: 1, z: 0 }; - // Apply strafing force - if (inputState.strafe !== 0) { - applyForce(right, inputState.strafe * THRUST_ACCELERATION, deltaTime); - } + // Apply thrust in forward direction + const thrustVelocity = { + x: forward.x * playerState.thrust * deltaTime, + y: forward.y * playerState.thrust * deltaTime, + z: forward.z * playerState.thrust * deltaTime + }; - // Apply damping to simulate space friction (very slight) - const currentSpeed = Math.sqrt( - player.velocity.x * player.velocity.x + - player.velocity.y * player.velocity.y + - player.velocity.z * player.velocity.z - ); - - if (currentSpeed > 0) { - const speedDamping = 1 - (DECELERATION * deltaTime); - player.velocity.x *= speedDamping; - player.velocity.y *= speedDamping; - player.velocity.z *= speedDamping; - } + // Apply horizontal strafe + const horizontalStrafeVelocity = { + x: right.x * playerState.horizontalStrafe * deltaTime, + y: 0, + z: right.z * playerState.horizontalStrafe * deltaTime + }; + + // Apply vertical strafe + const verticalStrafeVelocity = { + x: 0, + y: up.y * playerState.verticalStrafe * deltaTime, + z: 0 + }; + + // Add velocities + playerState.velocity.x += thrustVelocity.x + horizontalStrafeVelocity.x + verticalStrafeVelocity.x; + playerState.velocity.y += thrustVelocity.y + horizontalStrafeVelocity.y + verticalStrafeVelocity.y; + playerState.velocity.z += thrustVelocity.z + horizontalStrafeVelocity.z + verticalStrafeVelocity.z; + + // Limit total speed + limitSpeed(playerState.velocity, MAX_SPEED); + + // Apply velocity to position + playerState.position.x += playerState.velocity.x * deltaTime; + playerState.position.y += playerState.velocity.y * deltaTime; + playerState.position.z += playerState.velocity.z * deltaTime; + + // Apply friction/drag + const drag = 0.99; + playerState.velocity.x *= drag; + playerState.velocity.y *= drag; + playerState.velocity.z *= drag; +} + +// Weapon firing +function firePrimaryWeapon() { + const forward = { + x: Math.sin(playerState.rotation.y) * Math.cos(playerState.rotation.x), + y: -Math.sin(playerState.rotation.x), + z: Math.cos(playerState.rotation.y) * Math.cos(playerState.rotation.x) + }; - // Update position - player.position.x += player.velocity.x * deltaTime; - player.position.y += player.velocity.y * deltaTime; - player.position.z += player.velocity.z * deltaTime; + createProjectile({ + position: { ...playerState.position }, + velocity: { + x: forward.x * 10 + playerState.velocity.x, + y: forward.y * 10 + playerState.velocity.y, + z: forward.z * 10 + playerState.velocity.z + }, + type: 'primary' + }); } -// Get player state for rendering +function fireSecondaryWeapon() { + const forward = { + x: Math.sin(playerState.rotation.y) * Math.cos(playerState.rotation.x), + y: -Math.sin(playerState.rotation.x), + z: Math.cos(playerState.rotation.y) * Math.cos(playerState.rotation.x) + }; + + createProjectile({ + position: { ...playerState.position }, + velocity: { + x: forward.x * 5 + playerState.velocity.x, + y: forward.y * 5 + playerState.velocity.y, + z: forward.z * 5 + playerState.velocity.z + }, + type: 'secondary' + }); +} + +// Get current player state export function getPlayerState() { + return playerState; +} + +// Get weapon cooldown states +export function getWeaponStates() { + const currentTime = Date.now(); return { - position: { ...player.position }, - rotation: { ...player.rotation }, - velocity: { ...player.velocity } + primary: { + cooldown: Math.max(0, PRIMARY_COOLDOWN - (currentTime - playerState.weapons.primary.lastFired)), + burstCount: playerState.weapons.primary.burstCount + }, + secondary: { + cooldown: Math.max(0, SECONDARY_COOLDOWN - (currentTime - playerState.weapons.secondary.lastFired)) + } }; } \ No newline at end of file diff --git a/html/space/renderer.js b/html/space/renderer.js index 6af271d..04646cf 100644 --- a/html/space/renderer.js +++ b/html/space/renderer.js @@ -1,14 +1,22 @@ // Renderer module using HTML5 Canvas -import { getPlayerState } from './physics.js'; +import { getPlayerState, getWeaponStates } from './physics.js'; import { getGameState } from './gameState.js'; +// Import weapon constants +import { + PRIMARY_COOLDOWN, + SECONDARY_COOLDOWN, + PRIMARY_BURST_COUNT, + PRIMARY_BURST_DELAY +} from './physics.js'; + let canvas; let ctx; let width; let height; // Star field -const stars = []; +let starfield = []; // Declare starfield array const NUM_STARS = 2000; // Increased from 1000 const STAR_FIELD_DEPTH = 20000; // Increased from 2000 @@ -24,60 +32,33 @@ const TARGET_LOCK_THRESHOLD = 20; // Pixels from center to consider locked // Initialize the renderer export function initRenderer() { - canvas = document.createElement('canvas'); - document.body.appendChild(canvas); + console.log('Initializing renderer...'); + canvas = document.getElementById('gameCanvas'); ctx = canvas.getContext('2d'); - // Make canvas fullscreen - function resize() { - width = window.innerWidth; - height = window.innerHeight; - canvas.width = width; - canvas.height = height; - } - - window.addEventListener('resize', resize); - resize(); + // Set canvas size + width = canvas.width = window.innerWidth; + height = canvas.height = window.innerHeight; - // Initialize star field - for (let i = 0; i < NUM_STARS; i++) { - stars.push({ - x: (Math.random() - 0.5) * width * 4, // Increased spread - y: (Math.random() - 0.5) * height * 4, // Increased spread - z: Math.random() * STAR_FIELD_DEPTH, - size: Math.random() * 2 - }); - } + // Initialize starfield + console.log('Creating starfield with', NUM_STARS, 'stars...'); + starfield = Array.from({ length: NUM_STARS }, () => ({ + x: (Math.random() - 0.5) * STAR_FIELD_DEPTH, + y: (Math.random() - 0.5) * STAR_FIELD_DEPTH, + z: Math.random() * STAR_FIELD_DEPTH, + size: Math.random() * 2 + 1 + })); + console.log('Starfield initialized'); } // Project 3D point to 2D screen coordinates -function projectPoint(point, player) { - // Calculate relative position to player - const relativeX = point.x - player.position.x; - const relativeY = point.y - player.position.y; - const relativeZ = point.z - player.position.z; - - // Apply player rotation - const cosY = Math.cos(player.rotation.y); - const sinY = Math.sin(player.rotation.y); - const cosX = Math.cos(player.rotation.x); - const sinX = Math.sin(player.rotation.x); - - // Rotate around Y axis (yaw) - let rotatedX = relativeX * cosY - relativeZ * sinY; - let rotatedZ = relativeX * sinY + relativeZ * cosY; - - // Rotate around X axis (pitch) - let rotatedY = relativeY * cosX - rotatedZ * sinX; - rotatedZ = relativeY * sinX + rotatedZ * cosX; - - // Project to screen - if (rotatedZ <= 0) return null; // Behind camera - - const scale = 1000 / rotatedZ; +function projectPoint(x, y, z) { + if (z <= 0) return null; // Behind camera + + const scale = 2000 / z; // Increased scale factor return { - x: width/2 + rotatedX * scale, - y: height/2 + rotatedY * scale, + x: width/2 + x * scale, + y: height/2 + y * scale, scale }; } @@ -88,7 +69,7 @@ function getTargetLock(player, gameState) { const centerY = height / 2; for (const ship of gameState.enemyShips) { - const projected = projectPoint(ship.position, player); + const projected = projectPoint(ship.x - player.x, ship.y - player.y, ship.z - player.z); if (projected) { const distance = Math.sqrt( Math.pow(projected.x - centerX, 2) + @@ -135,8 +116,8 @@ function drawRadar(player, gameState, targetLock) { // Draw planets gameState.planets.forEach(planet => { - const dx = (planet.position.x - player.position.x) * RADAR_SCALE; - const dy = (planet.position.z - player.position.z) * RADAR_SCALE; + const dx = (planet.x - player.x) * RADAR_SCALE; + const dy = (planet.z - player.z) * RADAR_SCALE; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < RADAR_RADIUS) { @@ -154,8 +135,8 @@ function drawRadar(player, gameState, targetLock) { // Draw enemy ships gameState.enemyShips.forEach(ship => { - const dx = (ship.position.x - player.position.x) * RADAR_SCALE; - const dy = (ship.position.z - player.position.z) * RADAR_SCALE; + const dx = (ship.x - player.x) * RADAR_SCALE; + const dy = (ship.z - player.z) * RADAR_SCALE; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < RADAR_RADIUS) { @@ -181,18 +162,18 @@ function drawSpeedIndicator(player, targetLock) { ctx.fillStyle = targetLock ? TARGET_LOCK_COLOR : HUD_COLOR; ctx.font = '14px monospace'; - // Calculate speed + // Calculate speed from velocity components const speed = Math.sqrt( - player.velocity.x * player.velocity.x + - player.velocity.y * player.velocity.y + - player.velocity.z * player.velocity.z + player.vx * player.vx + + player.vy * player.vy + + player.vz * player.vz ); // Draw speed ctx.fillText(`Speed: ${speed.toFixed(2)}`, 20, height - 40); - // Draw direction - const direction = Math.atan2(player.velocity.x, player.velocity.z); + // Draw direction (using x and z components for heading) + const direction = Math.atan2(player.vx, player.vz); ctx.fillText(`Heading: ${(direction * 180 / Math.PI).toFixed(1)}°`, 20, height - 20); ctx.restore(); @@ -255,6 +236,39 @@ function drawTargetingReticle(player, gameState) { ctx.restore(); } +// Draw weapon cooldown indicators +function drawWeaponCooldowns() { + const weaponStates = getWeaponStates(); + ctx.save(); + ctx.fillStyle = HUD_COLOR; + ctx.font = '14px monospace'; + + // Primary weapon cooldown (bottom left) + const primaryCooldown = weaponStates.primary.cooldown / PRIMARY_COOLDOWN; + ctx.fillText('Primary:', 20, height - 80); + ctx.fillStyle = `rgba(0, 255, 0, ${primaryCooldown})`; + ctx.fillRect(20, height - 70, 100, 10); + ctx.strokeStyle = HUD_COLOR; + ctx.strokeRect(20, height - 70, 100, 10); + + // Secondary weapon cooldown (bottom left, below primary) + const secondaryCooldown = weaponStates.secondary.cooldown / SECONDARY_COOLDOWN; + ctx.fillStyle = HUD_COLOR; + ctx.fillText('Secondary:', 20, height - 50); + ctx.fillStyle = `rgba(0, 255, 0, ${secondaryCooldown})`; + ctx.fillRect(20, height - 40, 100, 10); + ctx.strokeStyle = HUD_COLOR; + ctx.strokeRect(20, height - 40, 100, 10); + + // Draw burst indicator for primary weapon + if (weaponStates.primary.burstCount > 0) { + ctx.fillStyle = HUD_COLOR; + ctx.fillText(`Burst: ${weaponStates.primary.burstCount}`, 20, height - 20); + } + + ctx.restore(); +} + // Main render function export function render() { const player = getPlayerState(); @@ -262,49 +276,44 @@ export function render() { const targetLock = getTargetLock(player, gameState); // Clear canvas - ctx.fillStyle = 'black'; - ctx.fillRect(0, 0, width, height); + ctx.fillStyle = '#000000'; + ctx.fillRect(0, 0, canvas.width, canvas.height); - // Draw star field - ctx.fillStyle = 'white'; - stars.forEach(star => { + // Draw starfield + let starsRendered = 0; + starfield.forEach(star => { // Calculate star position relative to player - const relativeX = star.x - player.position.x; - const relativeY = star.y - player.position.y; - const relativeZ = star.z - player.position.z; - + let relativeX = star.x - player.x; + let relativeY = star.y - player.y; + let relativeZ = star.z - player.z; + // Apply player rotation - const cosY = Math.cos(player.rotation.y); - const sinY = Math.sin(player.rotation.y); - const cosX = Math.cos(player.rotation.x); - const sinX = Math.sin(player.rotation.x); - - // Rotate around Y axis (yaw) - let rotatedX = relativeX * cosY - relativeZ * sinY; - let rotatedZ = relativeX * sinY + relativeZ * cosY; - - // Rotate around X axis (pitch) - let rotatedY = relativeY * cosX - rotatedZ * sinX; - rotatedZ = relativeY * sinX + rotatedZ * cosX; - - // Project to screen - if (rotatedZ <= 0) return; // Behind camera - - const scale = STAR_FIELD_DEPTH / rotatedZ; - const x = width/2 + rotatedX * scale; - const y = height/2 + rotatedY * scale; - const size = star.size * scale; + let rotatedX = relativeX * Math.cos(player.rotation) - relativeY * Math.sin(player.rotation); + let rotatedY = relativeX * Math.sin(player.rotation) + relativeY * Math.cos(player.rotation); + let rotatedZ = relativeZ; - if (x >= 0 && x <= width && y >= 0 && y <= height) { - ctx.beginPath(); - ctx.arc(x, y, size, 0, Math.PI * 2); - ctx.fill(); + // Project to screen coordinates + if (rotatedZ > 0) { + const projected = projectPoint(rotatedX, rotatedY, rotatedZ); + if (projected) { + const brightness = Math.min(1, 2000 / rotatedZ); + ctx.fillStyle = `rgba(255, 255, 255, ${brightness})`; + ctx.beginPath(); + ctx.arc(projected.x, projected.y, star.size * brightness, 0, Math.PI * 2); + ctx.fill(); + starsRendered++; + } } }); + + // Debug output + if (Math.random() < 0.01) { // Only log occasionally to avoid spam + console.log('Stars rendered:', starsRendered); + } // Draw planets gameState.planets.forEach(planet => { - const projected = projectPoint(planet.position, player); + const projected = projectPoint(planet.x - player.x, planet.y - player.y, planet.z - player.z); if (projected) { const radius = planet.radius * projected.scale; ctx.fillStyle = planet.color; @@ -316,7 +325,7 @@ export function render() { // Draw enemy ships gameState.enemyShips.forEach(ship => { - const projected = projectPoint(ship.position, player); + const projected = projectPoint(ship.x - player.x, ship.y - player.y, ship.z - player.z); if (projected) { const size = 20 * projected.scale; ctx.fillStyle = '#ff0000'; @@ -331,7 +340,7 @@ export function render() { // Draw projectiles gameState.projectiles.forEach(projectile => { - const projected = projectPoint(projectile.position, player); + const projected = projectPoint(projectile.x - player.x, projectile.y - player.y, projectile.z - player.z); if (projected) { const size = 3 * projected.scale; ctx.fillStyle = projectile.type === 'primary' ? '#ffff00' : '#00ffff'; @@ -345,4 +354,5 @@ export function render() { drawRadar(player, gameState, targetLock); drawSpeedIndicator(player, targetLock); drawTargetingReticle(player, gameState); + drawWeaponCooldowns(); } \ No newline at end of file |