diff --git a/src/Player.js b/src/Player.js index 800b038..6eba022 100644 --- a/src/Player.js +++ b/src/Player.js @@ -6,6 +6,7 @@ import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockCont export class Player { constructor(camera, colliders) { this.camera = camera; + this.camera.rotation.order = 'YXZ'; // Standard FPS rotation order to prevent gimbal lock this.colliders = colliders; // Player stats @@ -35,6 +36,14 @@ export class Player { this.battery = 100.0; this.baseDrain = 0.5; // Drain per second at base intensity + // Animation + this.headBobTimer = 0; + this.lastStepTime = 0; + + // Audio + this.ctx = new (window.AudioContext || window.webkitAudioContext)(); + this.audioEnabled = false; + // Animation this.headBobTimer = 0; @@ -59,6 +68,12 @@ export class Player { setupInput() { const onKeyDown = (event) => { + // Resume Audio Context on first interaction + if (this.ctx && this.ctx.state === 'suspended') { + this.ctx.resume(); + this.audioEnabled = true; + } + switch (event.code) { case 'KeyW': this.moveForward = true; break; case 'KeyA': this.moveLeft = true; break; @@ -166,8 +181,21 @@ export class Player { } } - // Keep player on ground - this.camera.position.y = this.height; + // Keep player on ground (Base height) + const baseHeight = this.height; + playerPos.y = baseHeight; + + // Audio Only Trigger + if (this.moveForward || this.moveBackward || this.moveLeft || this.moveRight) { + // Step every 0.6 seconds + this.lastStepTime += dt; + if (this.lastStepTime > 0.6) { + this.lastStepTime = 0; + this.playFootstep(); + } + } else { + this.lastStepTime = 0.5; // Ready to step properly next time + } // Flashlight flicker effect (subtle) & Battery Logic if (this.flashlight && this.flashlightOn) { @@ -216,4 +244,39 @@ export class Player { } } } + + playFootstep() { + if (!this.ctx) return; + + const t = this.ctx.currentTime; + const osc = this.ctx.createOscillator(); + const gain = this.ctx.createGain(); + const filter = this.ctx.createBiquadFilter(); + + // Noise buffer for texture + const bufferSize = this.ctx.sampleRate * 0.1; // 0.1s duration + const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate); + const data = buffer.getChannelData(0); + for (let i = 0; i < bufferSize; i++) { + data[i] = (Math.random() * 2 - 1) * 0.5; + } + const noise = this.ctx.createBufferSource(); + noise.buffer = buffer; + + // Filter to make it sound dull (floor/carpet) + filter.type = 'lowpass'; + filter.frequency.setValueAtTime(400, t); // Low muffled thud + + // Envelope + gain.gain.setValueAtTime(0.3, t); + gain.gain.exponentialRampToValueAtTime(0.01, t + 0.1); + + // Connect + noise.connect(filter); + filter.connect(gain); + gain.connect(this.ctx.destination); + + noise.start(); + noise.stop(t + 0.1); + } }