Codex CLI commited on
Commit
bf6239d
·
1 Parent(s): 118fa8d

feat(player): add crouch functionality with speed and aim adjustments

Browse files
Files changed (8) hide show
  1. index.html +1 -1
  2. src/config.js +5 -1
  3. src/events.js +2 -0
  4. src/globals.js +2 -1
  5. src/hud.js +4 -1
  6. src/main.js +2 -2
  7. src/player.js +4 -2
  8. src/weapon.js +6 -3
index.html CHANGED
@@ -189,7 +189,7 @@
189
  <div id="overlay-content">
190
  <h1>Orcs In The Forest</h1>
191
  <p>Click to Start</p>
192
- <p style="font-size: 14px;">Move: WASD | Look: Mouse | Fire: Click | Reload: R | Grenade: Hold G then release | Light: F | Pause: ESC</p>
193
  </div>
194
  </div>
195
 
 
189
  <div id="overlay-content">
190
  <h1>Orcs In The Forest</h1>
191
  <p>Click to Start</p>
192
+ <p style="font-size: 14px;">Move: WASD | Look: Mouse | Fire: Click | Reload: R | Grenade: Hold G then release | Crouch: C | Light: F | Pause: ESC</p>
193
  </div>
194
  </div>
195
 
src/config.js CHANGED
@@ -21,9 +21,12 @@ export const CFG = {
21
  player: {
22
  speed: 8,
23
  sprintMult: 1.5,
 
24
  radius: 0.8,
25
  health: 100,
26
- jumpVel: 5
 
 
27
  },
28
  flashlight: {
29
  on: true,
@@ -86,6 +89,7 @@ export const CFG = {
86
  spreadMoveMult: 2.2, // walking
87
  spreadSprintMult: 3.2, // sprinting
88
  spreadAirMult: 4.0, // not grounded
 
89
  magSize: 24,
90
  reloadTime: 1.6,
91
  recoilKick: 0.065,
 
21
  player: {
22
  speed: 8,
23
  sprintMult: 1.5,
24
+ crouchMult: 0.6,
25
  radius: 0.8,
26
  health: 100,
27
+ jumpVel: 5,
28
+ eyeHeight: 1.8,
29
+ crouchEyeHeight: 1.2
30
  },
31
  flashlight: {
32
  on: true,
 
89
  spreadMoveMult: 2.2, // walking
90
  spreadSprintMult: 3.2, // sprinting
91
  spreadAirMult: 4.0, // not grounded
92
+ spreadCrouchMult: 0.6, // crouching for precision
93
  magSize: 24,
94
  reloadTime: 1.6,
95
  recoilKick: 0.065,
src/events.js CHANGED
@@ -41,6 +41,7 @@ export function setupEvents({ startGame, restartGame, beginReload, updateWeaponA
41
  case 'KeyS': G.input.s = true; break;
42
  case 'KeyD': G.input.d = true; break;
43
  case 'ShiftLeft': G.input.sprint = true; break;
 
44
  case 'KeyG':
45
  if (G.state === 'playing') {
46
  G.input.grenade = true;
@@ -82,6 +83,7 @@ export function setupEvents({ startGame, restartGame, beginReload, updateWeaponA
82
  case 'KeyS': G.input.s = false; break;
83
  case 'KeyD': G.input.d = false; break;
84
  case 'ShiftLeft': G.input.sprint = false; break;
 
85
  case 'KeyG':
86
  if (G.input.grenade) {
87
  G.input.grenade = false;
 
41
  case 'KeyS': G.input.s = true; break;
42
  case 'KeyD': G.input.d = true; break;
43
  case 'ShiftLeft': G.input.sprint = true; break;
44
+ case 'KeyC': G.input.crouch = true; break;
45
  case 'KeyG':
46
  if (G.state === 'playing') {
47
  G.input.grenade = true;
 
83
  case 'KeyS': G.input.s = false; break;
84
  case 'KeyD': G.input.d = false; break;
85
  case 'ShiftLeft': G.input.sprint = false; break;
86
+ case 'KeyC': G.input.crouch = false; break;
87
  case 'KeyG':
88
  if (G.input.grenade) {
89
  G.input.grenade = false;
src/globals.js CHANGED
@@ -30,6 +30,7 @@ export const G = {
30
  d: false,
31
  shoot: false,
32
  sprint: false,
 
33
  jump: false,
34
  grenade: false
35
  },
@@ -63,7 +64,7 @@ export const G = {
63
  reserve: Infinity,
64
  reloading: false,
65
  reloadTimer: 0,
66
- anchor: { depth: 0.92, right: 0.48, bottom: 0.42 },
67
  // Dynamic aim spread (NDC units) and helpers
68
  spread: 0,
69
  targetSpread: 0,
 
30
  d: false,
31
  shoot: false,
32
  sprint: false,
33
+ crouch: false,
34
  jump: false,
35
  grenade: false
36
  },
 
64
  reserve: Infinity,
65
  reloading: false,
66
  reloadTimer: 0,
67
+ anchor: { depth: 0.80, right: 0.417, bottom: 0.365 },
68
  // Dynamic aim spread (NDC units) and helpers
69
  spread: 0,
70
  targetSpread: 0,
src/hud.js CHANGED
@@ -124,7 +124,10 @@ export function updateCrosshair(delta) {
124
  if (!ch.root || !ch.left || !ch.right || !ch.top || !ch.bottom) return;
125
 
126
  // Convert NDC spread to pixel gap (approximate using viewport width)
127
- const ndc = Math.max(G.weapon.spread, CFG.gun.spreadMin || CFG.gun.bloom || 0);
 
 
 
128
  const baseGap = 6; // px baseline gap
129
  const gapPx = baseGap + ndc * 0.5 * window.innerWidth; // half-width maps NDC to px
130
  const armLen = 10 + Math.min(20, ndc * window.innerWidth * 0.4); // grow a bit with spread
 
124
  if (!ch.root || !ch.left || !ch.right || !ch.top || !ch.bottom) return;
125
 
126
  // Convert NDC spread to pixel gap (approximate using viewport width)
127
+ // When crouched, the effective minimum spread is reduced
128
+ const baseMin = (CFG.gun.spreadMin || CFG.gun.bloom || 0);
129
+ const crouchMin = G.input.crouch ? baseMin * (CFG.gun.spreadCrouchMult || 1) : baseMin;
130
+ const ndc = Math.max(G.weapon.spread, crouchMin);
131
  const baseGap = 6; // px baseline gap
132
  const gapPx = baseGap + ndc * 0.5 * window.innerWidth; // half-width maps NDC to px
133
  const armLen = 10 + Math.min(20, ndc * window.innerWidth * 0.4); // grow a bit with spread
src/main.js CHANGED
@@ -55,7 +55,7 @@ function init() {
55
  G.random = makeRandom(CFG.seed);
56
 
57
  // Player
58
- const startY = getTerrainHeight(0, 0) + 1.8;
59
  G.player = {
60
  pos: new THREE.Vector3(0, startY, 0),
61
  vel: new THREE.Vector3(),
@@ -103,7 +103,7 @@ function startGame() {
103
  G.player.health = CFG.player.health;
104
  G.player.score = 0;
105
  G.player.alive = true;
106
- G.player.pos.set(0, getTerrainHeight(0, 0) + 1.8, 0);
107
  G.player.vel.set(0, 0, 0);
108
  G.camera.position.copy(G.player.pos);
109
  G.damageFlash = 0;
 
55
  G.random = makeRandom(CFG.seed);
56
 
57
  // Player
58
+ const startY = getTerrainHeight(0, 0) + (CFG.player.eyeHeight || 1.8);
59
  G.player = {
60
  pos: new THREE.Vector3(0, startY, 0),
61
  vel: new THREE.Vector3(),
 
103
  G.player.health = CFG.player.health;
104
  G.player.score = 0;
105
  G.player.alive = true;
106
+ G.player.pos.set(0, getTerrainHeight(0, 0) + (CFG.player.eyeHeight || 1.8), 0);
107
  G.player.vel.set(0, 0, 0);
108
  G.camera.position.copy(G.player.pos);
109
  G.damageFlash = 0;
src/player.js CHANGED
@@ -28,7 +28,8 @@ export function updatePlayer(delta) {
28
 
29
  if (MOVE.length() > 0) {
30
  MOVE.normalize();
31
- const speed = G.player.speed * (G.input.sprint ? CFG.player.sprintMult : 1);
 
32
  MOVE.multiplyScalar(speed * delta);
33
  }
34
 
@@ -61,7 +62,8 @@ export function updatePlayer(delta) {
61
  NEXT.y += G.player.yVel * delta;
62
 
63
  // Grounded against terrain height
64
- const groundEye = getTerrainHeight(NEXT.x, NEXT.z) + 1.8;
 
65
  if (NEXT.y <= groundEye) {
66
  NEXT.y = groundEye;
67
  G.player.yVel = 0;
 
28
 
29
  if (MOVE.length() > 0) {
30
  MOVE.normalize();
31
+ let speed = G.player.speed * (G.input.sprint ? CFG.player.sprintMult : 1);
32
+ if (G.input.crouch) speed *= (CFG.player.crouchMult || 1);
33
  MOVE.multiplyScalar(speed * delta);
34
  }
35
 
 
62
  NEXT.y += G.player.yVel * delta;
63
 
64
  // Grounded against terrain height
65
+ const eye = G.input.crouch ? (CFG.player.crouchEyeHeight || 1.8) : (CFG.player.eyeHeight || 1.8);
66
+ const groundEye = getTerrainHeight(NEXT.x, NEXT.z) + eye;
67
  if (NEXT.y <= groundEye) {
68
  NEXT.y = groundEye;
69
  G.player.yVel = 0;
src/weapon.js CHANGED
@@ -148,10 +148,12 @@ export function updateWeapon(delta) {
148
 
149
  const moving = G.input.w || G.input.a || G.input.s || G.input.d;
150
  const sprinting = moving && G.input.sprint;
 
151
  // Reduce sway/bob intensity and slightly lower frequencies
152
  G.weapon.swayT += delta * (sprinting ? 9 : (moving ? 7 : 2.5));
153
- const bobAmp = sprinting ? 0.008 : (moving ? 0.006 : 0.0035);
154
- const swayAmp = sprinting ? 0.0045 : (moving ? 0.0035 : 0.002);
 
155
 
156
  const bobX = Math.sin(G.weapon.swayT * 1.8) * bobAmp;
157
  const bobY = Math.cos(G.weapon.swayT * 3.6) * bobAmp * 0.6;
@@ -163,7 +165,8 @@ export function updateWeapon(delta) {
163
  const base = CFG.gun.spreadMin ?? CFG.gun.bloom ?? 0;
164
  const moveMult = moving ? (sprinting ? (CFG.gun.spreadSprintMult || 1) : (CFG.gun.spreadMoveMult || 1)) : 1;
165
  const airMult = G.player.grounded ? 1 : (CFG.gun.spreadAirMult || 1);
166
- const target = Math.min(CFG.gun.spreadMax || 0.02, base * moveMult * airMult);
 
167
  G.weapon.targetSpread = target;
168
  const decay = CFG.gun.spreadDecay || 6.0;
169
  // Exponential approach to target
 
148
 
149
  const moving = G.input.w || G.input.a || G.input.s || G.input.d;
150
  const sprinting = moving && G.input.sprint;
151
+ const crouching = !!G.input.crouch;
152
  // Reduce sway/bob intensity and slightly lower frequencies
153
  G.weapon.swayT += delta * (sprinting ? 9 : (moving ? 7 : 2.5));
154
+ let bobAmp = sprinting ? 0.008 : (moving ? 0.006 : 0.0035);
155
+ let swayAmp = sprinting ? 0.0045 : (moving ? 0.0035 : 0.002);
156
+ if (crouching) { bobAmp *= 0.7; swayAmp *= 0.7; }
157
 
158
  const bobX = Math.sin(G.weapon.swayT * 1.8) * bobAmp;
159
  const bobY = Math.cos(G.weapon.swayT * 3.6) * bobAmp * 0.6;
 
165
  const base = CFG.gun.spreadMin ?? CFG.gun.bloom ?? 0;
166
  const moveMult = moving ? (sprinting ? (CFG.gun.spreadSprintMult || 1) : (CFG.gun.spreadMoveMult || 1)) : 1;
167
  const airMult = G.player.grounded ? 1 : (CFG.gun.spreadAirMult || 1);
168
+ const crouchMult = crouching ? (CFG.gun.spreadCrouchMult || 1) : 1;
169
+ const target = Math.min(CFG.gun.spreadMax || 0.02, base * moveMult * airMult * crouchMult);
170
  G.weapon.targetSpread = target;
171
  const decay = CFG.gun.spreadDecay || 6.0;
172
  // Exponential approach to target