Spaces:
Running
Running
Codex CLI
commited on
Commit
·
bf6239d
1
Parent(s):
118fa8d
feat(player): add crouch functionality with speed and aim adjustments
Browse files- index.html +1 -1
- src/config.js +5 -1
- src/events.js +2 -0
- src/globals.js +2 -1
- src/hud.js +4 -1
- src/main.js +2 -2
- src/player.js +4 -2
- 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.
|
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 |
-
|
|
|
|
|
|
|
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 |
-
|
|
|
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
|
|
|
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 |
-
|
154 |
-
|
|
|
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
|
|
|
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
|