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
|