// 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 const THRUST_ACCELERATION = 0.01; // Reduced from 0.05 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 let playerState = { position: { x: 0, y: 0, z: 0 }, velocity: { x: 0, y: 0, z: 0 }, 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 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 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 player controls export function updatePlayerControls(controls) { // Handle thrust (space bar) if (controls.thrust) { playerState.thrust = Math.min(playerState.thrust + THRUST_ACCELERATION, MAX_THRUST); } else { // Apply deceleration when no thrust input if (playerState.thrust > 0) { playerState.thrust = Math.max(playerState.thrust - DECELERATION, 0); } } // 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 { // 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; } } // Secondary weapon (single shot with cooldown) if (controls.secondary && currentTime - playerState.weapons.secondary.lastFired >= SECONDARY_COOLDOWN) { fireSecondaryWeapon(); playerState.weapons.secondary.lastFired = currentTime; } // 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; } } // Update physics export function updatePhysics(deltaTime) { // Calculate forward and right vectors based on rotation 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) }; const right = { x: Math.cos(playerState.rotation.y), y: 0, z: -Math.sin(playerState.rotation.y) }; const up = { x: 0, y: 1, z: 0 }; // 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 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) }; 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' }); } 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 { 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)) } }; }