quake2ts 0.0.296 → 0.0.298

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/packages/cgame/dist/index.cjs +3 -1
  3. package/packages/cgame/dist/index.cjs.map +1 -1
  4. package/packages/cgame/dist/index.js +3 -1
  5. package/packages/cgame/dist/index.js.map +1 -1
  6. package/packages/client/dist/browser/index.global.js +13 -13
  7. package/packages/client/dist/browser/index.global.js.map +1 -1
  8. package/packages/client/dist/cjs/index.cjs +20 -11
  9. package/packages/client/dist/cjs/index.cjs.map +1 -1
  10. package/packages/client/dist/esm/index.js +20 -11
  11. package/packages/client/dist/esm/index.js.map +1 -1
  12. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  13. package/packages/engine/dist/browser/index.global.js +8 -8
  14. package/packages/engine/dist/browser/index.global.js.map +1 -1
  15. package/packages/engine/dist/cjs/index.cjs +11 -2
  16. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  17. package/packages/engine/dist/esm/index.js +11 -2
  18. package/packages/engine/dist/esm/index.js.map +1 -1
  19. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  20. package/packages/engine/dist/types/demo/parser.d.ts.map +1 -1
  21. package/packages/game/dist/browser/index.global.js +4 -4
  22. package/packages/game/dist/browser/index.global.js.map +1 -1
  23. package/packages/game/dist/cjs/index.cjs +67 -20
  24. package/packages/game/dist/cjs/index.cjs.map +1 -1
  25. package/packages/game/dist/esm/index.js +67 -20
  26. package/packages/game/dist/esm/index.js.map +1 -1
  27. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
  28. package/packages/game/dist/types/ai/movement.d.ts.map +1 -1
  29. package/packages/shared/dist/browser/index.global.js +1 -1
  30. package/packages/shared/dist/browser/index.global.js.map +1 -1
  31. package/packages/shared/dist/cjs/index.cjs +1342 -1038
  32. package/packages/shared/dist/cjs/index.cjs.map +1 -1
  33. package/packages/shared/dist/esm/index.js +1340 -1038
  34. package/packages/shared/dist/esm/index.js.map +1 -1
  35. package/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
  36. package/packages/shared/dist/types/pmove/jump.d.ts +2 -0
  37. package/packages/shared/dist/types/pmove/jump.d.ts.map +1 -1
  38. package/packages/shared/dist/types/pmove/move.d.ts +20 -0
  39. package/packages/shared/dist/types/pmove/move.d.ts.map +1 -1
  40. package/packages/shared/dist/types/pmove/pmove.d.ts +5 -1
  41. package/packages/shared/dist/types/pmove/pmove.d.ts.map +1 -1
  42. package/packages/shared/dist/types/pmove/types.d.ts +25 -1
  43. package/packages/shared/dist/types/pmove/types.d.ts.map +1 -1
  44. package/packages/tools/dist/tsconfig.tsbuildinfo +1 -1
@@ -1842,206 +1842,301 @@ var PmType = /* @__PURE__ */ ((PmType2) => {
1842
1842
  PmType2[PmType2["Freeze"] = 6] = "Freeze";
1843
1843
  return PmType2;
1844
1844
  })(PmType || {});
1845
- var PlayerButton = /* @__PURE__ */ ((PlayerButton2) => {
1846
- PlayerButton2[PlayerButton2["None"] = 0] = "None";
1847
- PlayerButton2[PlayerButton2["Attack"] = 1] = "Attack";
1848
- PlayerButton2[PlayerButton2["Use"] = 2] = "Use";
1849
- PlayerButton2[PlayerButton2["Holster"] = 4] = "Holster";
1850
- PlayerButton2[PlayerButton2["Jump"] = 8] = "Jump";
1851
- PlayerButton2[PlayerButton2["Crouch"] = 16] = "Crouch";
1852
- PlayerButton2[PlayerButton2["Any"] = 128] = "Any";
1853
- return PlayerButton2;
1845
+ var PlayerButton = /* @__PURE__ */ ((PlayerButton3) => {
1846
+ PlayerButton3[PlayerButton3["None"] = 0] = "None";
1847
+ PlayerButton3[PlayerButton3["Attack"] = 1] = "Attack";
1848
+ PlayerButton3[PlayerButton3["Use"] = 2] = "Use";
1849
+ PlayerButton3[PlayerButton3["Holster"] = 4] = "Holster";
1850
+ PlayerButton3[PlayerButton3["Jump"] = 8] = "Jump";
1851
+ PlayerButton3[PlayerButton3["Crouch"] = 16] = "Crouch";
1852
+ PlayerButton3[PlayerButton3["Any"] = 128] = "Any";
1853
+ return PlayerButton3;
1854
1854
  })(PlayerButton || {});
1855
1855
 
1856
- // src/pmove/pmove.ts
1857
- function applyPmoveFriction(params) {
1858
- const {
1859
- velocity,
1860
- frametime,
1861
- onGround,
1862
- groundIsSlick,
1863
- onLadder,
1864
- waterlevel,
1865
- pmFriction,
1866
- pmStopSpeed,
1867
- pmWaterFriction
1868
- } = params;
1869
- const speed = lengthVec3(velocity);
1870
- if (speed < 1) {
1871
- return { x: 0, y: 0, z: velocity.z };
1856
+ // src/pmove/jump.ts
1857
+ var DEFAULT_JUMP_HEIGHT = 270;
1858
+ function hasButton(buttons, button) {
1859
+ return (buttons & button) !== 0;
1860
+ }
1861
+ function checkJump(params) {
1862
+ const { pmFlags, pmType, buttons, waterlevel, onGround, velocity, origin, jumpHeight = DEFAULT_JUMP_HEIGHT } = params;
1863
+ if (pmFlags & 16 /* TimeLand */) {
1864
+ return { pmFlags, onGround, velocity, origin, jumpSound: false, jumped: false };
1872
1865
  }
1873
- let drop = 0;
1874
- if (onGround && !groundIsSlick || onLadder) {
1875
- const control = speed < pmStopSpeed ? pmStopSpeed : speed;
1876
- const friction = pmFriction;
1877
- drop += control * friction * frametime;
1866
+ const holdingJump = hasButton(buttons, 8 /* Jump */);
1867
+ let nextFlags = pmFlags;
1868
+ let nextOnGround = onGround;
1869
+ let jumpSound = false;
1870
+ let jumped = false;
1871
+ let nextVelocity = velocity;
1872
+ let nextOrigin = origin;
1873
+ if (!holdingJump) {
1874
+ nextFlags = removePmFlag(nextFlags, 2 /* JumpHeld */);
1875
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
1878
1876
  }
1879
- if (waterlevel > 0 && !onLadder) {
1880
- drop += speed * pmWaterFriction * waterlevel * frametime;
1877
+ if (hasPmJumpHold(nextFlags)) {
1878
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
1881
1879
  }
1882
- let newspeed = speed - drop;
1883
- if (newspeed < 0) {
1884
- newspeed = 0;
1880
+ if (pmType === 4 /* Dead */) {
1881
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
1885
1882
  }
1886
- if (newspeed === speed) {
1887
- return velocity;
1883
+ if (waterlevel >= 2 /* Waist */) {
1884
+ nextOnGround = false;
1885
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
1888
1886
  }
1889
- const scale = newspeed / speed;
1890
- return scaleVec3(velocity, scale);
1887
+ if (!nextOnGround) {
1888
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
1889
+ }
1890
+ nextFlags = addPmFlag(nextFlags, 2 /* JumpHeld */);
1891
+ nextFlags = removePmFlag(nextFlags, 4 /* OnGround */);
1892
+ nextOnGround = false;
1893
+ jumpSound = true;
1894
+ jumped = true;
1895
+ const z = velocity.z + jumpHeight;
1896
+ const finalZ = z < jumpHeight ? jumpHeight : z;
1897
+ nextVelocity = { ...velocity, z: finalZ };
1898
+ nextOrigin = { ...origin, z: origin.z + 1 };
1899
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
1891
1900
  }
1892
- function applyPmoveAccelerate(params) {
1893
- const { velocity, wishdir, wishspeed, accel, frametime } = params;
1894
- const currentSpeed = dotVec3(velocity, wishdir);
1895
- const addSpeed = wishspeed - currentSpeed;
1896
- if (addSpeed <= 0) {
1897
- return velocity;
1901
+ function hasPmJumpHold(flags) {
1902
+ return (flags & 2 /* JumpHeld */) !== 0;
1903
+ }
1904
+
1905
+ // src/pmove/currents.ts
1906
+ var DEFAULT_GROUND_CURRENT_SCALE = 100;
1907
+ var DEFAULT_FORWARD_LADDER_CLAMP = 200;
1908
+ var DEFAULT_SIDE_LADDER_CLAMP = 150;
1909
+ var LADDER_HORIZONTAL_CAP = 25;
1910
+ var LADDER_ASCEND_PITCH_THRESHOLD = 15;
1911
+ var LADDER_TRACE_DISTANCE = 1;
1912
+ var UP_VECTOR = { x: 0, y: 0, z: 1 };
1913
+ var DEFAULT_TRACE = (_, end) => ({
1914
+ fraction: 1,
1915
+ endpos: end,
1916
+ allsolid: false,
1917
+ startsolid: false
1918
+ });
1919
+ function currentVectorFromContents(contents) {
1920
+ let x = 0;
1921
+ let y = 0;
1922
+ let z = 0;
1923
+ if (contents & CONTENTS_CURRENT_0) {
1924
+ x += 1;
1898
1925
  }
1899
- let accelSpeed = accel * frametime * wishspeed;
1900
- if (accelSpeed > addSpeed) {
1901
- accelSpeed = addSpeed;
1926
+ if (contents & CONTENTS_CURRENT_90) {
1927
+ y += 1;
1902
1928
  }
1903
- return {
1904
- x: velocity.x + wishdir.x * accelSpeed,
1905
- y: velocity.y + wishdir.y * accelSpeed,
1906
- z: velocity.z + wishdir.z * accelSpeed
1907
- };
1908
- }
1909
- function applyPmoveAirAccelerate(params) {
1910
- const { velocity, wishdir, wishspeed, accel, frametime } = params;
1911
- const wishspd = Math.min(wishspeed, 30);
1912
- const currentSpeed = dotVec3(velocity, wishdir);
1913
- const addSpeed = wishspd - currentSpeed;
1914
- if (addSpeed <= 0) {
1915
- return velocity;
1929
+ if (contents & CONTENTS_CURRENT_180) {
1930
+ x -= 1;
1916
1931
  }
1917
- let accelSpeed = accel * wishspeed * frametime;
1918
- if (accelSpeed > addSpeed) {
1919
- accelSpeed = addSpeed;
1932
+ if (contents & CONTENTS_CURRENT_270) {
1933
+ y -= 1;
1920
1934
  }
1921
- return {
1922
- x: velocity.x + wishdir.x * accelSpeed,
1923
- y: velocity.y + wishdir.y * accelSpeed,
1924
- z: velocity.z + wishdir.z * accelSpeed
1925
- };
1926
- }
1927
- function pmoveCmdScale(cmd, maxSpeed) {
1928
- const forward = Math.abs(cmd.forwardmove);
1929
- const side = Math.abs(cmd.sidemove);
1930
- const up = Math.abs(cmd.upmove);
1931
- const max = Math.max(forward, side, up);
1932
- if (max === 0) {
1933
- return 0;
1935
+ if (contents & CONTENTS_CURRENT_UP) {
1936
+ z += 1;
1934
1937
  }
1935
- const total = Math.sqrt(cmd.forwardmove * cmd.forwardmove + cmd.sidemove * cmd.sidemove + cmd.upmove * cmd.upmove);
1936
- return maxSpeed * max / (127 * total);
1937
- }
1938
- function buildAirGroundWish(params) {
1939
- const { forward, right, cmd, maxSpeed } = params;
1940
- let wishvel = {
1941
- x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
1942
- y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
1943
- z: 0
1944
- };
1945
- let wishspeed = lengthVec3(wishvel);
1946
- if (wishspeed > maxSpeed) {
1947
- const scale = maxSpeed / wishspeed;
1948
- wishvel = scaleVec3(wishvel, scale);
1949
- wishspeed = maxSpeed;
1938
+ if (contents & CONTENTS_CURRENT_DOWN) {
1939
+ z -= 1;
1950
1940
  }
1951
- return {
1952
- wishdir: wishspeed === 0 ? wishvel : normalizeVec3(wishvel),
1953
- wishspeed
1954
- };
1941
+ if (x === 0 && y === 0 && z === 0) {
1942
+ return ZERO_VEC3;
1943
+ }
1944
+ return { x, y, z };
1955
1945
  }
1956
- function buildWaterWish(params) {
1957
- const { forward, right, cmd, maxSpeed } = params;
1958
- let wishvel = {
1959
- x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
1960
- y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
1961
- z: forward.z * cmd.forwardmove + right.z * cmd.sidemove
1962
- };
1963
- if (cmd.upmove > 10) {
1964
- wishvel = addVec3(wishvel, { x: 0, y: 0, z: cmd.upmove });
1965
- } else if (cmd.upmove < -10) {
1966
- wishvel = addVec3(wishvel, { x: 0, y: 0, z: cmd.upmove });
1967
- } else if (Math.abs(cmd.forwardmove) < 10 && Math.abs(cmd.sidemove) < 10) {
1968
- wishvel = addVec3(wishvel, { x: 0, y: 0, z: -60 });
1969
- } else {
1970
- wishvel = addVec3(wishvel, { x: 0, y: 0, z: 10 });
1946
+ function waterCurrentVelocity(params) {
1947
+ const { watertype, waterlevel, onGround, waterSpeed } = params;
1948
+ if ((watertype & MASK_CURRENT) === 0) {
1949
+ return ZERO_VEC3;
1971
1950
  }
1972
- let wishspeed = lengthVec3(wishvel);
1973
- if (wishspeed > maxSpeed) {
1974
- const scale = maxSpeed / wishspeed;
1975
- wishvel = scaleVec3(wishvel, scale);
1976
- wishspeed = maxSpeed;
1951
+ const direction = currentVectorFromContents(watertype);
1952
+ if (direction === ZERO_VEC3) {
1953
+ return ZERO_VEC3;
1977
1954
  }
1978
- wishspeed *= 0.5;
1979
- return {
1980
- wishdir: wishspeed === 0 ? wishvel : normalizeVec3(wishvel),
1981
- wishspeed
1982
- };
1983
- }
1984
-
1985
- // src/pmove/slide.ts
1986
- var DEFAULT_MAX_CLIP_PLANES = 5;
1987
- var DEFAULT_MAX_BUMPS = 4;
1988
- var DEFAULT_STEP_SIZE = 18;
1989
- var MIN_STEP_NORMAL = 0.7;
1990
- var SLIDEMOVE_BLOCKED_FLOOR = 1;
1991
- var SLIDEMOVE_BLOCKED_WALL = 2;
1992
- function resolveSlideMove(initialVelocity, planesEncountered, overbounce, maxClipPlanes = DEFAULT_MAX_CLIP_PLANES, primalVelocity = initialVelocity) {
1993
- if (planesEncountered.length === 0) {
1994
- return { velocity: initialVelocity, planes: [], stopped: false };
1955
+ let scale = waterSpeed;
1956
+ if (waterlevel === 1 /* Feet */ && onGround) {
1957
+ scale *= 0.5;
1995
1958
  }
1996
- const planes = [];
1997
- let velocity = initialVelocity;
1998
- for (const plane of planesEncountered) {
1999
- if (planes.length >= maxClipPlanes) {
2000
- return { velocity: ZERO_VEC3, planes, stopped: true };
2001
- }
2002
- const duplicate = planes.find((existing) => dotVec3(existing, plane) > 0.99);
2003
- if (duplicate) {
2004
- continue;
2005
- }
2006
- planes.push(plane);
2007
- let clipped;
2008
- let i = 0;
2009
- for (; i < planes.length; i++) {
2010
- const candidate = clipVelocityVec3(velocity, planes[i], overbounce);
2011
- let j = 0;
2012
- for (; j < planes.length; j++) {
2013
- if (j === i) continue;
2014
- if (dotVec3(candidate, planes[j]) < 0) break;
2015
- }
2016
- if (j === planes.length) {
2017
- clipped = candidate;
2018
- break;
2019
- }
2020
- }
2021
- if (clipped) {
2022
- velocity = clipped;
2023
- } else {
2024
- if (planes.length !== 2) {
2025
- return { velocity: ZERO_VEC3, planes, stopped: true };
2026
- }
2027
- const dir = crossVec3(planes[0], planes[1]);
2028
- const d = dotVec3(dir, velocity);
2029
- velocity = scaleVec3(dir, d);
2030
- }
2031
- if (dotVec3(velocity, primalVelocity) <= 0) {
2032
- return { velocity: ZERO_VEC3, planes, stopped: true };
2033
- }
1959
+ return scaleVec3(direction, scale);
1960
+ }
1961
+ function groundCurrentVelocity(params) {
1962
+ const { groundContents, scale = DEFAULT_GROUND_CURRENT_SCALE } = params;
1963
+ const direction = currentVectorFromContents(groundContents);
1964
+ if (direction === ZERO_VEC3) {
1965
+ return ZERO_VEC3;
2034
1966
  }
2035
- const stopped = velocity.x === 0 && velocity.y === 0 && velocity.z === 0;
2036
- return { velocity, planes, stopped };
1967
+ return scaleVec3(direction, scale);
2037
1968
  }
2038
- function slideMove(params) {
1969
+ function applyPmoveAddCurrents(params) {
2039
1970
  const {
2040
- origin: initialOrigin,
2041
- velocity: initialVelocity,
2042
- frametime,
2043
- overbounce,
2044
- trace,
1971
+ wishVelocity,
1972
+ onLadder,
1973
+ onGround,
1974
+ waterlevel,
1975
+ watertype,
1976
+ groundContents,
1977
+ cmd,
1978
+ viewPitch,
1979
+ maxSpeed,
1980
+ ladderMod,
1981
+ waterSpeed,
1982
+ forward,
1983
+ origin,
1984
+ mins,
1985
+ maxs,
1986
+ trace = DEFAULT_TRACE
1987
+ } = params;
1988
+ let adjusted = wishVelocity;
1989
+ if (onLadder) {
1990
+ adjusted = applyLadderAdjustments({
1991
+ wishVelocity: adjusted,
1992
+ cmd,
1993
+ waterlevel,
1994
+ viewPitch,
1995
+ maxSpeed,
1996
+ ladderMod,
1997
+ onGround,
1998
+ forward,
1999
+ origin,
2000
+ mins,
2001
+ maxs,
2002
+ trace
2003
+ });
2004
+ }
2005
+ const waterVelocity = waterCurrentVelocity({ watertype, waterlevel, onGround, waterSpeed });
2006
+ if (waterVelocity !== ZERO_VEC3) {
2007
+ adjusted = addVec3(adjusted, waterVelocity);
2008
+ }
2009
+ if (onGround) {
2010
+ const groundVelocity = groundCurrentVelocity({ groundContents });
2011
+ if (groundVelocity !== ZERO_VEC3) {
2012
+ adjusted = addVec3(adjusted, groundVelocity);
2013
+ }
2014
+ }
2015
+ return adjusted;
2016
+ }
2017
+ function applyLadderAdjustments(params) {
2018
+ const { wishVelocity, cmd, waterlevel, viewPitch, maxSpeed, ladderMod, onGround, forward, origin, mins, maxs, trace } = params;
2019
+ const buttons = cmd.buttons ?? 0;
2020
+ let adjusted = { ...wishVelocity };
2021
+ if ((buttons & (8 /* Jump */ | 16 /* Crouch */)) !== 0) {
2022
+ const ladderSpeed = isAtLeastWaistDeep(waterlevel) ? maxSpeed : DEFAULT_FORWARD_LADDER_CLAMP;
2023
+ adjusted = {
2024
+ ...adjusted,
2025
+ z: buttons & 8 /* Jump */ ? ladderSpeed : -ladderSpeed
2026
+ };
2027
+ } else if (cmd.forwardmove) {
2028
+ const clamped = clamp(cmd.forwardmove, -DEFAULT_FORWARD_LADDER_CLAMP, DEFAULT_FORWARD_LADDER_CLAMP);
2029
+ if (cmd.forwardmove > 0) {
2030
+ const climb = viewPitch < LADDER_ASCEND_PITCH_THRESHOLD ? clamped : -clamped;
2031
+ adjusted = { ...adjusted, z: climb };
2032
+ } else {
2033
+ if (!onGround) {
2034
+ adjusted = { ...adjusted, x: 0, y: 0 };
2035
+ }
2036
+ adjusted = { ...adjusted, z: clamped };
2037
+ }
2038
+ } else {
2039
+ adjusted = { ...adjusted, z: 0 };
2040
+ }
2041
+ if (!onGround) {
2042
+ if (cmd.sidemove) {
2043
+ let sideSpeed = clamp(cmd.sidemove, -DEFAULT_SIDE_LADDER_CLAMP, DEFAULT_SIDE_LADDER_CLAMP);
2044
+ if (waterlevel < 2 /* Waist */) {
2045
+ sideSpeed *= ladderMod;
2046
+ }
2047
+ const flatForward = normalizeVec3({ x: forward.x, y: forward.y, z: 0 });
2048
+ if (flatForward.x !== 0 || flatForward.y !== 0) {
2049
+ const spot = addVec3(origin, scaleVec3(flatForward, LADDER_TRACE_DISTANCE));
2050
+ const tr = trace(origin, spot, mins, maxs);
2051
+ if (tr.fraction !== 1 && !tr.allsolid && tr.contents !== void 0 && (tr.contents & CONTENTS_LADDER) !== 0 && tr.planeNormal) {
2052
+ const right = crossVec3(tr.planeNormal, UP_VECTOR);
2053
+ adjusted = { ...adjusted, x: 0, y: 0 };
2054
+ adjusted = addVec3(adjusted, scaleVec3(right, -sideSpeed));
2055
+ }
2056
+ }
2057
+ } else {
2058
+ adjusted = {
2059
+ ...adjusted,
2060
+ x: clampHorizontal(adjusted.x),
2061
+ y: clampHorizontal(adjusted.y)
2062
+ };
2063
+ }
2064
+ }
2065
+ return adjusted;
2066
+ }
2067
+ function clamp(value, min, max) {
2068
+ return Math.max(min, Math.min(max, value));
2069
+ }
2070
+ function clampHorizontal(value) {
2071
+ if (value < -LADDER_HORIZONTAL_CAP) {
2072
+ return -LADDER_HORIZONTAL_CAP;
2073
+ }
2074
+ if (value > LADDER_HORIZONTAL_CAP) {
2075
+ return LADDER_HORIZONTAL_CAP;
2076
+ }
2077
+ return value;
2078
+ }
2079
+
2080
+ // src/pmove/slide.ts
2081
+ var DEFAULT_MAX_CLIP_PLANES = 5;
2082
+ var DEFAULT_MAX_BUMPS = 4;
2083
+ var DEFAULT_STEP_SIZE = 18;
2084
+ var MIN_STEP_NORMAL = 0.7;
2085
+ var SLIDEMOVE_BLOCKED_FLOOR = 1;
2086
+ var SLIDEMOVE_BLOCKED_WALL = 2;
2087
+ function resolveSlideMove(initialVelocity, planesEncountered, overbounce, maxClipPlanes = DEFAULT_MAX_CLIP_PLANES, primalVelocity = initialVelocity) {
2088
+ if (planesEncountered.length === 0) {
2089
+ return { velocity: initialVelocity, planes: [], stopped: false };
2090
+ }
2091
+ const planes = [];
2092
+ let velocity = initialVelocity;
2093
+ for (const plane of planesEncountered) {
2094
+ if (planes.length >= maxClipPlanes) {
2095
+ return { velocity: ZERO_VEC3, planes, stopped: true };
2096
+ }
2097
+ const duplicate = planes.find((existing) => dotVec3(existing, plane) > 0.99);
2098
+ if (duplicate) {
2099
+ continue;
2100
+ }
2101
+ planes.push(plane);
2102
+ let clipped;
2103
+ let i = 0;
2104
+ for (; i < planes.length; i++) {
2105
+ const candidate = clipVelocityVec3(velocity, planes[i], overbounce);
2106
+ let j = 0;
2107
+ for (; j < planes.length; j++) {
2108
+ if (j === i) continue;
2109
+ if (dotVec3(candidate, planes[j]) < 0) break;
2110
+ }
2111
+ if (j === planes.length) {
2112
+ clipped = candidate;
2113
+ break;
2114
+ }
2115
+ }
2116
+ if (clipped) {
2117
+ velocity = clipped;
2118
+ } else {
2119
+ if (planes.length !== 2) {
2120
+ return { velocity: ZERO_VEC3, planes, stopped: true };
2121
+ }
2122
+ const dir = crossVec3(planes[0], planes[1]);
2123
+ const d = dotVec3(dir, velocity);
2124
+ velocity = scaleVec3(dir, d);
2125
+ }
2126
+ if (dotVec3(velocity, primalVelocity) <= 0) {
2127
+ return { velocity: ZERO_VEC3, planes, stopped: true };
2128
+ }
2129
+ }
2130
+ const stopped = velocity.x === 0 && velocity.y === 0 && velocity.z === 0;
2131
+ return { velocity, planes, stopped };
2132
+ }
2133
+ function slideMove(params) {
2134
+ const {
2135
+ origin: initialOrigin,
2136
+ velocity: initialVelocity,
2137
+ frametime,
2138
+ overbounce,
2139
+ trace,
2045
2140
  maxBumps = DEFAULT_MAX_BUMPS,
2046
2141
  maxClipPlanes = DEFAULT_MAX_CLIP_PLANES,
2047
2142
  mins,
@@ -2140,250 +2235,45 @@ function stepSlideMove(params) {
2140
2235
  };
2141
2236
  }
2142
2237
 
2143
- // src/pmove/stuck.ts
2144
- var AXES = ["x", "y", "z"];
2145
- var SIDE_CHECKS = [
2146
- { normal: [0, 0, 1], mins: [-1, -1, 0], maxs: [1, 1, 0] },
2147
- { normal: [0, 0, -1], mins: [-1, -1, 0], maxs: [1, 1, 0] },
2148
- { normal: [1, 0, 0], mins: [0, -1, -1], maxs: [0, 1, 1] },
2149
- { normal: [-1, 0, 0], mins: [0, -1, -1], maxs: [0, 1, 1] },
2150
- { normal: [0, 1, 0], mins: [-1, 0, -1], maxs: [1, 0, 1] },
2151
- { normal: [0, -1, 0], mins: [-1, 0, -1], maxs: [1, 0, 1] }
2152
- ];
2153
- var ZERO_VEC = { x: 0, y: 0, z: 0 };
2154
- function cloneMutable(vec) {
2155
- return { x: vec.x, y: vec.y, z: vec.z };
2156
- }
2157
- function tupleToVec3(tuple) {
2158
- return { x: tuple[0], y: tuple[1], z: tuple[2] };
2159
- }
2160
- function adjustAxis(vec, axis, delta) {
2161
- if (delta === 0) return;
2162
- switch (axis) {
2163
- case "x":
2164
- vec.x += delta;
2165
- break;
2166
- case "y":
2167
- vec.y += delta;
2168
- break;
2169
- case "z":
2170
- vec.z += delta;
2171
- break;
2172
- }
2173
- }
2174
- function setAxis(vec, axis, value) {
2175
- switch (axis) {
2176
- case "x":
2177
- vec.x = value;
2178
- break;
2179
- case "y":
2180
- vec.y = value;
2181
- break;
2182
- case "z":
2183
- vec.z = value;
2184
- break;
2185
- }
2186
- }
2187
- function axisValue(vec, axis) {
2188
- switch (axis) {
2189
- case "x":
2190
- return vec.x;
2191
- case "y":
2192
- return vec.y;
2193
- case "z":
2194
- default:
2195
- return vec.z;
2196
- }
2197
- }
2198
- function boundValue(code, axis, mins, maxs) {
2199
- if (code === -1) {
2200
- return axisValue(mins, axis);
2201
- }
2202
- if (code === 1) {
2203
- return axisValue(maxs, axis);
2204
- }
2205
- return 0;
2206
- }
2207
- function applySideOffset(base, side, mins, maxs) {
2208
- const result = cloneMutable(base);
2209
- for (let i = 0; i < AXES.length; i++) {
2210
- const axis = AXES[i];
2211
- const normal = side.normal[i];
2212
- if (normal < 0) {
2213
- adjustAxis(result, axis, axisValue(mins, axis));
2214
- } else if (normal > 0) {
2215
- adjustAxis(result, axis, axisValue(maxs, axis));
2216
- }
2217
- }
2218
- return result;
2219
- }
2220
- function buildSideBounds(side, mins, maxs) {
2221
- const localMins = cloneMutable(ZERO_VEC);
2222
- const localMaxs = cloneMutable(ZERO_VEC);
2223
- for (let i = 0; i < AXES.length; i++) {
2224
- const axis = AXES[i];
2225
- setAxis(localMins, axis, boundValue(side.mins[i], axis, mins, maxs));
2226
- setAxis(localMaxs, axis, boundValue(side.maxs[i], axis, mins, maxs));
2227
- }
2228
- return { mins: localMins, maxs: localMaxs };
2229
- }
2230
- function addEpsilon(source, axis, direction) {
2231
- if (!axis || direction === 0) {
2232
- return source;
2233
- }
2234
- const clone = cloneMutable(source);
2235
- adjustAxis(clone, axis, direction);
2236
- return clone;
2237
- }
2238
- function addEpsilonImmutable(vec, axis, direction) {
2239
- if (!axis || direction === 0) {
2240
- return vec;
2241
- }
2242
- switch (axis) {
2243
- case "x":
2244
- return { ...vec, x: vec.x + direction };
2245
- case "y":
2246
- return { ...vec, y: vec.y + direction };
2247
- case "z":
2248
- default:
2249
- return { ...vec, z: vec.z + direction };
2250
- }
2251
- }
2252
- function fixStuckObjectGeneric(params) {
2253
- const { origin, mins, maxs, trace } = params;
2254
- const initial = trace(origin, mins, maxs, origin);
2255
- if (!initial.startsolid) {
2256
- return { result: "good-position", origin: { ...origin } };
2257
- }
2258
- const candidates = [];
2259
- for (let i = 0; i < SIDE_CHECKS.length; i++) {
2260
- const side = SIDE_CHECKS[i];
2261
- const { mins: localMins, maxs: localMaxs } = buildSideBounds(side, mins, maxs);
2262
- let start = applySideOffset(origin, side, mins, maxs);
2263
- let tr = trace(start, localMins, localMaxs, start);
2264
- let epsilonAxis;
2265
- let epsilonDir = 0;
2266
- if (tr.startsolid) {
2267
- for (let axisIndex = 0; axisIndex < AXES.length; axisIndex++) {
2268
- if (side.normal[axisIndex] !== 0) {
2269
- continue;
2270
- }
2271
- const axis = AXES[axisIndex];
2272
- let epsilonStart = cloneMutable(start);
2273
- adjustAxis(epsilonStart, axis, 1);
2274
- tr = trace(epsilonStart, localMins, localMaxs, epsilonStart);
2275
- if (!tr.startsolid) {
2276
- start = epsilonStart;
2277
- epsilonAxis = axis;
2278
- epsilonDir = 1;
2279
- break;
2280
- }
2281
- epsilonStart = cloneMutable(start);
2282
- adjustAxis(epsilonStart, axis, -1);
2283
- tr = trace(epsilonStart, localMins, localMaxs, epsilonStart);
2284
- if (!tr.startsolid) {
2285
- start = epsilonStart;
2286
- epsilonAxis = axis;
2287
- epsilonDir = -1;
2288
- break;
2289
- }
2290
- }
2291
- }
2292
- if (tr.startsolid) {
2293
- continue;
2294
- }
2295
- const otherSide = SIDE_CHECKS[i ^ 1];
2296
- let oppositeStart = applySideOffset(origin, otherSide, mins, maxs);
2297
- oppositeStart = addEpsilon(oppositeStart, epsilonAxis, epsilonDir);
2298
- tr = trace(start, localMins, localMaxs, oppositeStart);
2299
- if (tr.startsolid) {
2300
- continue;
2301
- }
2302
- const normal = tupleToVec3(side.normal);
2303
- const end = addVec3(tr.endpos ?? oppositeStart, scaleVec3(normal, 0.125));
2304
- const delta = subtractVec3(end, oppositeStart);
2305
- let newOrigin = addVec3(origin, delta);
2306
- newOrigin = addEpsilonImmutable(newOrigin, epsilonAxis, epsilonDir);
2307
- const validation = trace(newOrigin, mins, maxs, newOrigin);
2308
- if (validation.startsolid) {
2309
- continue;
2310
- }
2311
- candidates.push({ origin: newOrigin, distance: lengthSquaredVec3(delta) });
2312
- }
2313
- if (candidates.length === 0) {
2314
- return { result: "no-good-position", origin: { ...origin } };
2315
- }
2316
- candidates.sort((a, b) => a.distance - b.distance);
2317
- return { result: "fixed", origin: { ...candidates[0].origin } };
2318
- }
2319
-
2320
- // src/pmove/currents.ts
2321
- var DEFAULT_GROUND_CURRENT_SCALE = 100;
2322
- var DEFAULT_FORWARD_LADDER_CLAMP = 200;
2323
- var DEFAULT_SIDE_LADDER_CLAMP = 150;
2324
- var LADDER_HORIZONTAL_CAP = 25;
2325
- var LADDER_ASCEND_PITCH_THRESHOLD = 15;
2326
- var LADDER_TRACE_DISTANCE = 1;
2327
- var UP_VECTOR = { x: 0, y: 0, z: 1 };
2328
- var DEFAULT_TRACE = (_, end) => ({
2329
- fraction: 1,
2330
- endpos: end,
2331
- allsolid: false,
2332
- startsolid: false
2333
- });
2334
- function currentVectorFromContents(contents) {
2335
- let x = 0;
2336
- let y = 0;
2337
- let z = 0;
2338
- if (contents & CONTENTS_CURRENT_0) {
2339
- x += 1;
2340
- }
2341
- if (contents & CONTENTS_CURRENT_90) {
2342
- y += 1;
2343
- }
2344
- if (contents & CONTENTS_CURRENT_180) {
2345
- x -= 1;
2346
- }
2347
- if (contents & CONTENTS_CURRENT_270) {
2348
- y -= 1;
2349
- }
2350
- if (contents & CONTENTS_CURRENT_UP) {
2351
- z += 1;
2352
- }
2353
- if (contents & CONTENTS_CURRENT_DOWN) {
2354
- z -= 1;
2355
- }
2356
- if (x === 0 && y === 0 && z === 0) {
2357
- return ZERO_VEC3;
2358
- }
2359
- return { x, y, z };
2360
- }
2361
- function waterCurrentVelocity(params) {
2362
- const { watertype, waterlevel, onGround, waterSpeed } = params;
2363
- if ((watertype & MASK_CURRENT) === 0) {
2364
- return ZERO_VEC3;
2365
- }
2366
- const direction = currentVectorFromContents(watertype);
2367
- if (direction === ZERO_VEC3) {
2368
- return ZERO_VEC3;
2369
- }
2370
- let scale = waterSpeed;
2371
- if (waterlevel === 1 /* Feet */ && onGround) {
2372
- scale *= 0.5;
2373
- }
2374
- return scaleVec3(direction, scale);
2375
- }
2376
- function groundCurrentVelocity(params) {
2377
- const { groundContents, scale = DEFAULT_GROUND_CURRENT_SCALE } = params;
2378
- const direction = currentVectorFromContents(groundContents);
2379
- if (direction === ZERO_VEC3) {
2380
- return ZERO_VEC3;
2381
- }
2382
- return scaleVec3(direction, scale);
2383
- }
2384
- function applyPmoveAddCurrents(params) {
2238
+ // src/pmove/move.ts
2239
+ var DEFAULT_AIR_ACCELERATE = 1;
2240
+ var WATER_DRIFT_SPEED = 60;
2241
+ var DEFAULT_STEP_OVERBOUNCE = 1.01;
2242
+ function applyPmoveAirMove(params) {
2385
2243
  const {
2386
- wishVelocity,
2244
+ origin,
2245
+ frametime,
2246
+ mins,
2247
+ maxs,
2248
+ trace,
2249
+ overbounce = DEFAULT_STEP_OVERBOUNCE,
2250
+ stepSize,
2251
+ maxBumps,
2252
+ maxClipPlanes,
2253
+ hasTime,
2254
+ forward,
2255
+ right,
2256
+ cmd,
2257
+ pmFlags,
2258
+ onGround,
2259
+ gravity,
2260
+ pmType,
2261
+ pmAccelerate,
2262
+ pmAirAccelerate = DEFAULT_AIR_ACCELERATE,
2263
+ pmMaxSpeed,
2264
+ pmDuckSpeed,
2265
+ onLadder,
2266
+ waterlevel,
2267
+ watertype,
2268
+ groundContents,
2269
+ viewPitch,
2270
+ ladderMod,
2271
+ pmWaterSpeed
2272
+ } = params;
2273
+ let velocity = { ...params.velocity };
2274
+ let wishvel = buildPlanarWishVelocity(forward, right, cmd);
2275
+ wishvel = applyPmoveAddCurrents({
2276
+ wishVelocity: wishvel,
2387
2277
  onLadder,
2388
2278
  onGround,
2389
2279
  waterlevel,
@@ -2391,223 +2281,354 @@ function applyPmoveAddCurrents(params) {
2391
2281
  groundContents,
2392
2282
  cmd,
2393
2283
  viewPitch,
2394
- maxSpeed,
2284
+ maxSpeed: hasPmFlag(pmFlags, 1 /* Ducked */) ? pmDuckSpeed : pmMaxSpeed,
2395
2285
  ladderMod,
2396
- waterSpeed,
2286
+ waterSpeed: pmWaterSpeed,
2397
2287
  forward,
2398
2288
  origin,
2399
2289
  mins,
2400
2290
  maxs,
2401
- trace = DEFAULT_TRACE
2402
- } = params;
2403
- let adjusted = wishVelocity;
2291
+ trace
2292
+ });
2293
+ const ducked = hasPmFlag(pmFlags, 1 /* Ducked */);
2294
+ const maxSpeed = ducked ? pmDuckSpeed : pmMaxSpeed;
2295
+ let wishdir = wishvel;
2296
+ let wishspeed = lengthVec3(wishdir);
2297
+ if (wishspeed !== 0) {
2298
+ wishdir = normalizeVec3(wishdir);
2299
+ }
2300
+ if (wishspeed > maxSpeed) {
2301
+ const scale = maxSpeed / wishspeed;
2302
+ wishvel = scaleVec3(wishvel, scale);
2303
+ wishspeed = maxSpeed;
2304
+ if (wishspeed !== 0) {
2305
+ wishdir = normalizeVec3(wishvel);
2306
+ }
2307
+ }
2404
2308
  if (onLadder) {
2405
- adjusted = applyLadderAdjustments({
2406
- wishVelocity: adjusted,
2407
- cmd,
2408
- waterlevel,
2409
- viewPitch,
2410
- maxSpeed,
2411
- ladderMod,
2412
- onGround,
2413
- forward,
2309
+ velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: pmAccelerate, frametime });
2310
+ if (Math.abs(wishvel.z) < Number.EPSILON) {
2311
+ velocity = dampVerticalVelocity(velocity, gravity, frametime);
2312
+ }
2313
+ return runStepSlideMove({
2414
2314
  origin,
2315
+ velocity,
2316
+ frametime,
2415
2317
  mins,
2416
2318
  maxs,
2417
- trace
2319
+ trace,
2320
+ overbounce,
2321
+ stepSize,
2322
+ maxBumps,
2323
+ maxClipPlanes,
2324
+ hasTime
2418
2325
  });
2419
2326
  }
2420
- const waterVelocity = waterCurrentVelocity({ watertype, waterlevel, onGround, waterSpeed });
2421
- if (waterVelocity !== ZERO_VEC3) {
2422
- adjusted = addVec3(adjusted, waterVelocity);
2423
- }
2424
2327
  if (onGround) {
2425
- const groundVelocity = groundCurrentVelocity({ groundContents });
2426
- if (groundVelocity !== ZERO_VEC3) {
2427
- adjusted = addVec3(adjusted, groundVelocity);
2328
+ velocity = { ...velocity, z: 0 };
2329
+ velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: pmAccelerate, frametime });
2330
+ if (gravity > 0) {
2331
+ velocity = { ...velocity, z: 0 };
2332
+ } else {
2333
+ velocity = { ...velocity, z: velocity.z - gravity * frametime };
2428
2334
  }
2429
- }
2430
- return adjusted;
2431
- }
2432
- function applyLadderAdjustments(params) {
2433
- const { wishVelocity, cmd, waterlevel, viewPitch, maxSpeed, ladderMod, onGround, forward, origin, mins, maxs, trace } = params;
2434
- const buttons = cmd.buttons ?? 0;
2435
- let adjusted = { ...wishVelocity };
2436
- if ((buttons & (8 /* Jump */ | 16 /* Crouch */)) !== 0) {
2437
- const ladderSpeed = isAtLeastWaistDeep(waterlevel) ? maxSpeed : DEFAULT_FORWARD_LADDER_CLAMP;
2438
- adjusted = {
2439
- ...adjusted,
2440
- z: buttons & 8 /* Jump */ ? ladderSpeed : -ladderSpeed
2441
- };
2442
- } else if (cmd.forwardmove) {
2443
- const clamped = clamp(cmd.forwardmove, -DEFAULT_FORWARD_LADDER_CLAMP, DEFAULT_FORWARD_LADDER_CLAMP);
2444
- if (cmd.forwardmove > 0) {
2445
- const climb = viewPitch < LADDER_ASCEND_PITCH_THRESHOLD ? clamped : -clamped;
2446
- adjusted = { ...adjusted, z: climb };
2447
- } else {
2448
- if (!onGround) {
2449
- adjusted = { ...adjusted, x: 0, y: 0 };
2450
- }
2451
- adjusted = { ...adjusted, z: clamped };
2452
- }
2453
- } else {
2454
- adjusted = { ...adjusted, z: 0 };
2455
- }
2456
- if (!onGround) {
2457
- if (cmd.sidemove) {
2458
- let sideSpeed = clamp(cmd.sidemove, -DEFAULT_SIDE_LADDER_CLAMP, DEFAULT_SIDE_LADDER_CLAMP);
2459
- if (waterlevel < 2 /* Waist */) {
2460
- sideSpeed *= ladderMod;
2461
- }
2462
- const flatForward = normalizeVec3({ x: forward.x, y: forward.y, z: 0 });
2463
- if (flatForward.x !== 0 || flatForward.y !== 0) {
2464
- const spot = addVec3(origin, scaleVec3(flatForward, LADDER_TRACE_DISTANCE));
2465
- const tr = trace(origin, spot, mins, maxs);
2466
- if (tr.fraction !== 1 && !tr.allsolid && tr.contents !== void 0 && (tr.contents & CONTENTS_LADDER) !== 0 && tr.planeNormal) {
2467
- const right = crossVec3(tr.planeNormal, UP_VECTOR);
2468
- adjusted = { ...adjusted, x: 0, y: 0 };
2469
- adjusted = addVec3(adjusted, scaleVec3(right, -sideSpeed));
2470
- }
2471
- }
2472
- } else {
2473
- adjusted = {
2474
- ...adjusted,
2475
- x: clampHorizontal(adjusted.x),
2476
- y: clampHorizontal(adjusted.y)
2335
+ if (velocity.x === 0 && velocity.y === 0) {
2336
+ return {
2337
+ origin,
2338
+ velocity,
2339
+ planes: [],
2340
+ blocked: 0,
2341
+ stopped: true,
2342
+ stepped: false,
2343
+ stepHeight: 0
2477
2344
  };
2478
2345
  }
2346
+ return runStepSlideMove({
2347
+ origin,
2348
+ velocity,
2349
+ frametime,
2350
+ mins,
2351
+ maxs,
2352
+ trace,
2353
+ overbounce,
2354
+ stepSize,
2355
+ maxBumps,
2356
+ maxClipPlanes,
2357
+ hasTime
2358
+ });
2479
2359
  }
2480
- return adjusted;
2481
- }
2482
- function clamp(value, min, max) {
2483
- return Math.max(min, Math.min(max, value));
2484
- }
2485
- function clampHorizontal(value) {
2486
- if (value < -LADDER_HORIZONTAL_CAP) {
2487
- return -LADDER_HORIZONTAL_CAP;
2360
+ if (pmAirAccelerate > 0) {
2361
+ velocity = applyPmoveAirAccelerate({
2362
+ velocity,
2363
+ wishdir,
2364
+ wishspeed,
2365
+ accel: pmAirAccelerate,
2366
+ frametime
2367
+ });
2368
+ } else {
2369
+ velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: DEFAULT_AIR_ACCELERATE, frametime });
2488
2370
  }
2489
- if (value > LADDER_HORIZONTAL_CAP) {
2490
- return LADDER_HORIZONTAL_CAP;
2371
+ if (pmType !== 1 /* Grapple */) {
2372
+ velocity = { ...velocity, z: velocity.z - gravity * frametime };
2491
2373
  }
2492
- return value;
2374
+ return runStepSlideMove({
2375
+ origin,
2376
+ velocity,
2377
+ frametime,
2378
+ mins,
2379
+ maxs,
2380
+ trace,
2381
+ overbounce,
2382
+ stepSize,
2383
+ maxBumps,
2384
+ maxClipPlanes,
2385
+ hasTime
2386
+ });
2493
2387
  }
2494
-
2495
- // src/pmove/fly.ts
2496
- var FLY_FRICTION_MULTIPLIER = 1.5;
2497
- var BUTTON_VERTICAL_SCALE = 0.5;
2498
- var DEFAULT_OVERBOUNCE = 1.01;
2499
- function applyPmoveFlyMove(params) {
2388
+ function applyPmoveWaterMove(params) {
2500
2389
  const {
2501
2390
  origin,
2502
- cmd,
2503
2391
  frametime,
2504
- pmFriction,
2505
- pmStopSpeed,
2506
- pmMaxSpeed,
2507
- pmAccelerate,
2508
- pmWaterSpeed,
2509
- doclip,
2510
- forward,
2511
- right,
2512
2392
  mins,
2513
2393
  maxs,
2514
2394
  trace,
2515
- overbounce = DEFAULT_OVERBOUNCE,
2395
+ overbounce = DEFAULT_STEP_OVERBOUNCE,
2516
2396
  stepSize,
2517
2397
  maxBumps,
2518
- maxClipPlanes
2519
- } = params;
2520
- let velocity = applyFlyFriction({ velocity: params.velocity, pmFriction, pmStopSpeed, frametime });
2521
- const wishdirVelocity = buildFlyWishVelocity({
2522
- cmd,
2398
+ maxClipPlanes,
2399
+ hasTime,
2523
2400
  forward,
2524
2401
  right,
2402
+ cmd,
2403
+ pmFlags,
2404
+ onGround,
2525
2405
  pmMaxSpeed,
2526
- pmWaterSpeed
2406
+ pmDuckSpeed,
2407
+ pmWaterAccelerate,
2408
+ pmWaterSpeed,
2409
+ onLadder,
2410
+ watertype,
2411
+ groundContents,
2412
+ waterlevel,
2413
+ viewPitch,
2414
+ ladderMod
2415
+ } = params;
2416
+ let velocity = { ...params.velocity };
2417
+ let wishvel = buildFullWishVelocity(forward, right, cmd);
2418
+ if (isIdleInWater(cmd, onGround)) {
2419
+ wishvel = { ...wishvel, z: wishvel.z - WATER_DRIFT_SPEED };
2420
+ } else {
2421
+ if (hasButton2(cmd, 16 /* Crouch */)) {
2422
+ wishvel = addVec3(wishvel, { x: 0, y: 0, z: -pmWaterSpeed * 0.5 });
2423
+ } else if (hasButton2(cmd, 8 /* Jump */)) {
2424
+ wishvel = addVec3(wishvel, { x: 0, y: 0, z: pmWaterSpeed * 0.5 });
2425
+ }
2426
+ }
2427
+ wishvel = applyPmoveAddCurrents({
2428
+ wishVelocity: wishvel,
2429
+ onLadder,
2430
+ onGround,
2431
+ waterlevel,
2432
+ watertype,
2433
+ groundContents,
2434
+ cmd,
2435
+ viewPitch,
2436
+ maxSpeed: hasPmFlag(pmFlags, 1 /* Ducked */) ? pmDuckSpeed : pmMaxSpeed,
2437
+ ladderMod,
2438
+ waterSpeed: pmWaterSpeed,
2439
+ forward,
2440
+ origin,
2441
+ mins,
2442
+ maxs,
2443
+ trace
2527
2444
  });
2528
- if (wishdirVelocity.wishspeed > 0) {
2529
- velocity = applyPmoveAccelerate({
2530
- velocity,
2531
- wishdir: wishdirVelocity.wishdir,
2532
- wishspeed: wishdirVelocity.accelSpeed,
2533
- accel: pmAccelerate,
2534
- frametime
2535
- });
2445
+ let wishdir = wishvel;
2446
+ let wishspeed = lengthVec3(wishdir);
2447
+ if (wishspeed !== 0) {
2448
+ wishdir = normalizeVec3(wishdir);
2536
2449
  }
2537
- if (!doclip) {
2538
- const originDelta = scaleVec3(velocity, frametime);
2539
- const nextOrigin = addVec3(origin, originDelta);
2540
- return {
2541
- origin: nextOrigin,
2542
- velocity,
2543
- planes: [],
2544
- blocked: 0,
2545
- stopped: velocity.x === 0 && velocity.y === 0 && velocity.z === 0,
2546
- stepped: false,
2547
- stepHeight: 0
2548
- };
2450
+ if (wishspeed > pmMaxSpeed) {
2451
+ const scale = pmMaxSpeed / wishspeed;
2452
+ wishvel = scaleVec3(wishvel, scale);
2453
+ wishspeed = pmMaxSpeed;
2454
+ if (wishspeed !== 0) {
2455
+ wishdir = normalizeVec3(wishvel);
2456
+ }
2549
2457
  }
2550
- if (!trace || !mins || !maxs) {
2551
- throw new Error("applyPmoveFlyMove: doclip=true requires trace/mins/maxs");
2458
+ wishspeed *= 0.5;
2459
+ const ducked = hasPmFlag(pmFlags, 1 /* Ducked */);
2460
+ if (ducked && wishspeed > pmDuckSpeed) {
2461
+ const scale = pmDuckSpeed / wishspeed;
2462
+ wishvel = scaleVec3(wishvel, scale);
2463
+ wishspeed = pmDuckSpeed;
2464
+ if (wishspeed !== 0) {
2465
+ wishdir = normalizeVec3(wishvel);
2466
+ }
2552
2467
  }
2553
- return stepSlideMove({
2468
+ velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: pmWaterAccelerate, frametime });
2469
+ return runStepSlideMove({
2554
2470
  origin,
2555
2471
  velocity,
2556
2472
  frametime,
2557
- overbounce,
2558
- trace,
2559
2473
  mins,
2560
2474
  maxs,
2475
+ trace,
2476
+ overbounce,
2561
2477
  stepSize,
2562
2478
  maxBumps,
2563
- maxClipPlanes
2479
+ maxClipPlanes,
2480
+ hasTime
2564
2481
  });
2565
2482
  }
2566
- function applyFlyFriction(params) {
2567
- const { velocity, pmFriction, pmStopSpeed, frametime } = params;
2568
- const speed = lengthVec3(velocity);
2569
- if (speed < 1) {
2570
- return { x: 0, y: 0, z: 0 };
2571
- }
2572
- const friction = pmFriction * FLY_FRICTION_MULTIPLIER;
2573
- const control = speed < pmStopSpeed ? pmStopSpeed : speed;
2574
- const drop = control * friction * frametime;
2575
- let newspeed = speed - drop;
2576
- if (newspeed < 0) {
2577
- newspeed = 0;
2578
- }
2579
- if (newspeed === speed) {
2580
- return velocity;
2581
- }
2582
- return scaleVec3(velocity, newspeed / speed);
2583
- }
2584
- function buildFlyWishVelocity(params) {
2585
- const { cmd, forward, right, pmMaxSpeed, pmWaterSpeed } = params;
2586
- const forwardNorm = normalizeVec3(forward);
2587
- const rightNorm = normalizeVec3(right);
2588
- const wishvel = {
2589
- x: forwardNorm.x * cmd.forwardmove + rightNorm.x * cmd.sidemove,
2590
- y: forwardNorm.y * cmd.forwardmove + rightNorm.y * cmd.sidemove,
2591
- z: forwardNorm.z * cmd.forwardmove + rightNorm.z * cmd.sidemove
2592
- };
2593
- let adjusted = wishvel;
2594
- const buttons = cmd.buttons ?? 0;
2595
- if (buttons & 8 /* Jump */) {
2596
- adjusted = addVec3(adjusted, { x: 0, y: 0, z: pmWaterSpeed * BUTTON_VERTICAL_SCALE });
2597
- }
2598
- if (buttons & 16 /* Crouch */) {
2599
- adjusted = addVec3(adjusted, { x: 0, y: 0, z: -pmWaterSpeed * BUTTON_VERTICAL_SCALE });
2483
+ function applyPmoveWalkMove(params) {
2484
+ const {
2485
+ origin,
2486
+ frametime,
2487
+ mins,
2488
+ maxs,
2489
+ trace,
2490
+ overbounce = DEFAULT_STEP_OVERBOUNCE,
2491
+ stepSize,
2492
+ maxBumps,
2493
+ maxClipPlanes,
2494
+ hasTime,
2495
+ forward,
2496
+ right,
2497
+ cmd,
2498
+ pmFlags,
2499
+ onGround,
2500
+ gravity,
2501
+ pmAccelerate,
2502
+ pmMaxSpeed,
2503
+ pmDuckSpeed,
2504
+ onLadder,
2505
+ waterlevel,
2506
+ watertype,
2507
+ groundContents,
2508
+ viewPitch,
2509
+ ladderMod,
2510
+ pmWaterSpeed
2511
+ } = params;
2512
+ let velocity = { ...params.velocity };
2513
+ let wishvel = buildPlanarWishVelocity(forward, right, cmd);
2514
+ wishvel = applyPmoveAddCurrents({
2515
+ wishVelocity: wishvel,
2516
+ onLadder,
2517
+ onGround,
2518
+ waterlevel,
2519
+ watertype,
2520
+ groundContents,
2521
+ cmd,
2522
+ viewPitch,
2523
+ maxSpeed: hasPmFlag(pmFlags, 1 /* Ducked */) ? pmDuckSpeed : pmMaxSpeed,
2524
+ ladderMod,
2525
+ waterSpeed: pmWaterSpeed,
2526
+ forward,
2527
+ origin,
2528
+ mins,
2529
+ maxs,
2530
+ trace
2531
+ });
2532
+ const ducked = hasPmFlag(pmFlags, 1 /* Ducked */);
2533
+ const maxSpeed = ducked ? pmDuckSpeed : pmMaxSpeed;
2534
+ let wishdir = wishvel;
2535
+ let wishspeed = lengthVec3(wishdir);
2536
+ if (wishspeed !== 0) {
2537
+ wishdir = normalizeVec3(wishdir);
2600
2538
  }
2601
- let wishspeed = lengthVec3(adjusted);
2602
- let wishdir = wishspeed === 0 ? { x: 0, y: 0, z: 0 } : normalizeVec3(adjusted);
2603
- if (wishspeed > pmMaxSpeed) {
2604
- const scale = pmMaxSpeed / wishspeed;
2605
- adjusted = scaleVec3(adjusted, scale);
2606
- wishspeed = pmMaxSpeed;
2607
- wishdir = wishspeed === 0 ? { x: 0, y: 0, z: 0 } : normalizeVec3(adjusted);
2539
+ if (wishspeed > maxSpeed) {
2540
+ const scale = maxSpeed / wishspeed;
2541
+ wishvel = scaleVec3(wishvel, scale);
2542
+ wishspeed = maxSpeed;
2543
+ if (wishspeed !== 0) {
2544
+ wishdir = normalizeVec3(wishvel);
2545
+ }
2608
2546
  }
2609
- const accelSpeed = wishspeed * 2;
2610
- return { wishdir, wishspeed, accelSpeed };
2547
+ velocity = { ...velocity, z: 0 };
2548
+ velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: pmAccelerate, frametime });
2549
+ if (gravity > 0) {
2550
+ velocity = { ...velocity, z: 0 };
2551
+ } else {
2552
+ velocity = { ...velocity, z: velocity.z - gravity * frametime };
2553
+ }
2554
+ if (velocity.x === 0 && velocity.y === 0) {
2555
+ return {
2556
+ origin,
2557
+ velocity,
2558
+ planes: [],
2559
+ blocked: 0,
2560
+ stopped: true,
2561
+ stepped: false,
2562
+ stepHeight: 0
2563
+ };
2564
+ }
2565
+ return runStepSlideMove({
2566
+ origin,
2567
+ velocity,
2568
+ frametime,
2569
+ mins,
2570
+ maxs,
2571
+ trace,
2572
+ overbounce,
2573
+ stepSize,
2574
+ maxBumps,
2575
+ maxClipPlanes,
2576
+ hasTime
2577
+ });
2578
+ }
2579
+ function buildPlanarWishVelocity(forward, right, cmd) {
2580
+ return {
2581
+ x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
2582
+ y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
2583
+ z: 0
2584
+ };
2585
+ }
2586
+ function buildFullWishVelocity(forward, right, cmd) {
2587
+ return {
2588
+ x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
2589
+ y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
2590
+ z: forward.z * cmd.forwardmove + right.z * cmd.sidemove
2591
+ };
2592
+ }
2593
+ function hasButton2(cmd, button) {
2594
+ return (cmd.buttons ?? 0) & button ? true : false;
2595
+ }
2596
+ function isIdleInWater(cmd, onGround) {
2597
+ const noMove = cmd.forwardmove === 0 && cmd.sidemove === 0;
2598
+ const noButtons = (cmd.buttons ?? 0) & (8 /* Jump */ | 16 /* Crouch */) ? false : true;
2599
+ return noMove && noButtons && !onGround;
2600
+ }
2601
+ function dampVerticalVelocity(velocity, gravity, frametime) {
2602
+ let z = velocity.z;
2603
+ const delta = gravity * frametime;
2604
+ if (z > 0) {
2605
+ z -= delta;
2606
+ if (z < 0) {
2607
+ z = 0;
2608
+ }
2609
+ } else {
2610
+ z += delta;
2611
+ if (z > 0) {
2612
+ z = 0;
2613
+ }
2614
+ }
2615
+ return { ...velocity, z };
2616
+ }
2617
+ function runStepSlideMove(params) {
2618
+ const { origin, velocity, frametime, mins, maxs, trace, overbounce = DEFAULT_STEP_OVERBOUNCE, stepSize, maxBumps, maxClipPlanes, hasTime } = params;
2619
+ return stepSlideMove({
2620
+ origin,
2621
+ velocity,
2622
+ frametime,
2623
+ trace,
2624
+ mins,
2625
+ maxs,
2626
+ overbounce,
2627
+ stepSize,
2628
+ maxBumps,
2629
+ maxClipPlanes,
2630
+ hasTime
2631
+ });
2611
2632
  }
2612
2633
 
2613
2634
  // src/pmove/water.ts
@@ -2639,184 +2660,23 @@ function getWaterLevel(params) {
2639
2660
  return { waterlevel, watertype };
2640
2661
  }
2641
2662
 
2642
- // src/pmove/jump.ts
2643
- var DEFAULT_JUMP_HEIGHT = 270;
2644
- function hasButton(buttons, button) {
2645
- return (buttons & button) !== 0;
2646
- }
2647
- function checkJump(params) {
2648
- const { pmFlags, pmType, buttons, waterlevel, onGround, velocity, jumpHeight = DEFAULT_JUMP_HEIGHT } = params;
2649
- if (pmFlags & 16 /* TimeLand */) {
2650
- return { pmFlags, onGround, velocity, jumpSound: false, jumped: false };
2651
- }
2652
- const holdingJump = hasButton(buttons, 8 /* Jump */);
2653
- let nextFlags = pmFlags;
2654
- let nextOnGround = onGround;
2655
- let jumpSound = false;
2656
- let jumped = false;
2657
- let nextVelocity = velocity;
2658
- if (!holdingJump) {
2659
- nextFlags = removePmFlag(nextFlags, 2 /* JumpHeld */);
2660
- return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, jumpSound, jumped };
2661
- }
2662
- if (hasPmJumpHold(nextFlags)) {
2663
- return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, jumpSound, jumped };
2664
- }
2665
- if (pmType === 4 /* Dead */) {
2666
- return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, jumpSound, jumped };
2667
- }
2668
- if (waterlevel >= 2 /* Waist */) {
2669
- nextOnGround = false;
2670
- return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, jumpSound, jumped };
2671
- }
2672
- if (!nextOnGround) {
2673
- return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, jumpSound, jumped };
2674
- }
2675
- nextFlags = addPmFlag(nextFlags, 2 /* JumpHeld */);
2676
- nextFlags = removePmFlag(nextFlags, 4 /* OnGround */);
2677
- nextOnGround = false;
2678
- jumpSound = true;
2679
- jumped = true;
2680
- const z = velocity.z + jumpHeight;
2681
- const finalZ = z < jumpHeight ? jumpHeight : z;
2682
- nextVelocity = { ...velocity, z: finalZ };
2683
- return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, jumpSound, jumped };
2684
- }
2685
- function hasPmJumpHold(flags) {
2686
- return (flags & 2 /* JumpHeld */) !== 0;
2687
- }
2688
-
2689
- // src/pmove/dimensions.ts
2690
- function createVec3(x, y, z) {
2691
- return { x, y, z };
2692
- }
2693
- function computePlayerDimensions(pmType, pmFlags) {
2694
- const minsBase = createVec3(-16, -16, 0);
2695
- const maxsBase = createVec3(16, 16, 16);
2696
- if (pmType === 5 /* Gib */) {
2697
- return {
2698
- mins: minsBase,
2699
- maxs: maxsBase,
2700
- viewheight: 8
2701
- };
2702
- }
2703
- const ducked = pmType === 4 /* Dead */ || (pmFlags & 1 /* Ducked */) !== 0;
2704
- const mins = createVec3(minsBase.x, minsBase.y, -24);
2705
- const maxs = createVec3(maxsBase.x, maxsBase.y, ducked ? 4 : 32);
2706
- return {
2707
- mins,
2708
- maxs,
2709
- viewheight: ducked ? -2 : 22
2710
- };
2711
- }
2712
-
2713
- // src/pmove/duck.ts
2714
- var CROUCH_MAX_Z = 4;
2715
- var STAND_MAX_Z = 32;
2716
- var ABOVE_WATER_OFFSET = 8;
2717
- function checkDuckState(params) {
2718
- const { pmType } = params;
2719
- if (pmType === 5 /* Gib */) {
2720
- const dims2 = computePlayerDimensions(pmType, params.pmFlags);
2721
- return { pmFlags: params.pmFlags, ducked: hasPmFlag(params.pmFlags, 1 /* Ducked */), changed: false, ...dims2 };
2722
- }
2723
- let flags = params.pmFlags;
2724
- let changed = false;
2725
- if (pmType === 4 /* Dead */) {
2726
- if (!hasPmFlag(flags, 1 /* Ducked */)) {
2727
- flags = addPmFlag(flags, 1 /* Ducked */);
2728
- changed = true;
2729
- }
2730
- } else if (shouldDuck(params)) {
2731
- if (!hasPmFlag(flags, 1 /* Ducked */) && !isDuckBlocked(params)) {
2732
- flags = addPmFlag(flags, 1 /* Ducked */);
2733
- changed = true;
2734
- }
2735
- } else if (hasPmFlag(flags, 1 /* Ducked */) && !isStandBlocked(params)) {
2736
- flags = removePmFlag(flags, 1 /* Ducked */);
2737
- changed = true;
2738
- }
2739
- const dims = computePlayerDimensions(pmType, flags);
2740
- const ducked = pmType === 4 /* Dead */ || hasPmFlag(flags, 1 /* Ducked */);
2741
- return { pmFlags: flags, ducked, changed, ...dims };
2742
- }
2743
- function shouldDuck(params) {
2744
- if ((params.buttons & 16 /* Crouch */) === 0) {
2745
- return false;
2746
- }
2747
- if (params.onLadder || params.n64Physics) {
2748
- return false;
2749
- }
2750
- if (params.hasGroundEntity) {
2751
- return true;
2752
- }
2753
- if (params.waterlevel <= 1 /* Feet */ && !isAboveWater(params)) {
2754
- return true;
2755
- }
2756
- return false;
2757
- }
2758
- function isDuckBlocked(params) {
2759
- const trace = params.trace({
2760
- start: params.origin,
2761
- end: params.origin,
2762
- mins: params.mins,
2763
- maxs: withZ(params.maxs, CROUCH_MAX_Z),
2764
- mask: MASK_SOLID
2765
- });
2766
- return trace.allsolid;
2767
- }
2768
- function isStandBlocked(params) {
2769
- const trace = params.trace({
2770
- start: params.origin,
2771
- end: params.origin,
2772
- mins: params.mins,
2773
- maxs: withZ(params.maxs, STAND_MAX_Z),
2774
- mask: MASK_SOLID
2775
- });
2776
- return trace.allsolid;
2777
- }
2778
- function isAboveWater(params) {
2779
- const below = { x: params.origin.x, y: params.origin.y, z: params.origin.z - ABOVE_WATER_OFFSET };
2780
- const solidTrace = params.trace({
2781
- start: params.origin,
2782
- end: below,
2783
- mins: params.mins,
2784
- maxs: params.maxs,
2785
- mask: MASK_SOLID
2786
- });
2787
- if (solidTrace.fraction < 1) {
2788
- return false;
2789
- }
2790
- const waterTrace = params.trace({
2791
- start: params.origin,
2792
- end: below,
2793
- mins: params.mins,
2794
- maxs: params.maxs,
2795
- mask: MASK_WATER
2796
- });
2797
- return waterTrace.fraction < 1;
2798
- }
2799
- function withZ(vec, z) {
2800
- return { x: vec.x, y: vec.y, z };
2801
- }
2802
-
2803
- // src/pmove/categorize.ts
2804
- var GROUND_PROBE_DISTANCE = 0.25;
2805
- var LADDER_BYPASS_VELOCITY = 180;
2806
- var TRICK_VELOCITY_THRESHOLD = 100;
2807
- var SLANTED_NORMAL_THRESHOLD = 0.7;
2808
- var TRICK_NORMAL_THRESHOLD = 0.9;
2809
- var TRICK_PM_TIME = 64;
2810
- var LAND_PM_TIME = 128;
2811
- var IMPACT_CLIP_OVERBOUNCE = 1.01;
2812
- var WATERJUMP_CLEAR = 8 /* TimeWaterJump */ | 16 /* TimeLand */ | 32 /* TimeTeleport */ | 1024 /* TimeTrick */;
2813
- function categorizePosition(params) {
2814
- const {
2815
- pmType,
2816
- n64Physics,
2817
- velocity,
2818
- startVelocity,
2819
- origin,
2663
+ // src/pmove/categorize.ts
2664
+ var GROUND_PROBE_DISTANCE = 0.25;
2665
+ var LADDER_BYPASS_VELOCITY = 180;
2666
+ var TRICK_VELOCITY_THRESHOLD = 100;
2667
+ var SLANTED_NORMAL_THRESHOLD = 0.7;
2668
+ var TRICK_NORMAL_THRESHOLD = 0.9;
2669
+ var TRICK_PM_TIME = 64;
2670
+ var LAND_PM_TIME = 128;
2671
+ var IMPACT_CLIP_OVERBOUNCE = 1.01;
2672
+ var WATERJUMP_CLEAR = 8 /* TimeWaterJump */ | 16 /* TimeLand */ | 32 /* TimeTeleport */ | 1024 /* TimeTrick */;
2673
+ function categorizePosition(params) {
2674
+ const {
2675
+ pmType,
2676
+ n64Physics,
2677
+ velocity,
2678
+ startVelocity,
2679
+ origin,
2820
2680
  mins,
2821
2681
  maxs,
2822
2682
  viewheight,
@@ -2890,304 +2750,744 @@ function categorizePosition(params) {
2890
2750
  };
2891
2751
  }
2892
2752
 
2893
- // src/pmove/move.ts
2894
- var DEFAULT_AIR_ACCELERATE = 1;
2895
- var WATER_DRIFT_SPEED = 60;
2896
- var DEFAULT_STEP_OVERBOUNCE = 1.01;
2897
- function applyPmoveAirMove(params) {
2898
- const {
2899
- origin,
2900
- frametime,
2901
- mins,
2902
- maxs,
2903
- trace,
2904
- overbounce = DEFAULT_STEP_OVERBOUNCE,
2905
- stepSize,
2906
- maxBumps,
2907
- maxClipPlanes,
2908
- hasTime,
2909
- forward,
2910
- right,
2911
- cmd,
2912
- pmFlags,
2913
- onGround,
2914
- gravity,
2915
- pmType,
2916
- pmAccelerate,
2917
- pmAirAccelerate = DEFAULT_AIR_ACCELERATE,
2918
- pmMaxSpeed,
2919
- pmDuckSpeed,
2920
- onLadder,
2921
- waterlevel,
2922
- watertype,
2923
- groundContents,
2924
- viewPitch,
2925
- ladderMod,
2926
- pmWaterSpeed
2927
- } = params;
2928
- let velocity = { ...params.velocity };
2929
- let wishvel = buildPlanarWishVelocity(forward, right, cmd);
2930
- wishvel = applyPmoveAddCurrents({
2931
- wishVelocity: wishvel,
2932
- onLadder,
2933
- onGround,
2934
- waterlevel,
2935
- watertype,
2936
- groundContents,
2937
- cmd,
2938
- viewPitch,
2939
- maxSpeed: hasPmFlag(pmFlags, 1 /* Ducked */) ? pmDuckSpeed : pmMaxSpeed,
2940
- ladderMod,
2941
- waterSpeed: pmWaterSpeed,
2942
- forward,
2943
- origin,
2753
+ // src/pmove/dimensions.ts
2754
+ function createVec3(x, y, z) {
2755
+ return { x, y, z };
2756
+ }
2757
+ function computePlayerDimensions(pmType, pmFlags) {
2758
+ const minsBase = createVec3(-16, -16, 0);
2759
+ const maxsBase = createVec3(16, 16, 16);
2760
+ if (pmType === 5 /* Gib */) {
2761
+ return {
2762
+ mins: minsBase,
2763
+ maxs: maxsBase,
2764
+ viewheight: 8
2765
+ };
2766
+ }
2767
+ const ducked = pmType === 4 /* Dead */ || (pmFlags & 1 /* Ducked */) !== 0;
2768
+ const mins = createVec3(minsBase.x, minsBase.y, -24);
2769
+ const maxs = createVec3(maxsBase.x, maxsBase.y, ducked ? 4 : 32);
2770
+ return {
2944
2771
  mins,
2945
2772
  maxs,
2946
- trace
2947
- });
2948
- const ducked = hasPmFlag(pmFlags, 1 /* Ducked */);
2949
- const maxSpeed = ducked ? pmDuckSpeed : pmMaxSpeed;
2950
- let wishdir = wishvel;
2951
- let wishspeed = lengthVec3(wishdir);
2952
- if (wishspeed !== 0) {
2953
- wishdir = normalizeVec3(wishdir);
2773
+ viewheight: ducked ? -2 : 22
2774
+ };
2775
+ }
2776
+
2777
+ // src/pmove/duck.ts
2778
+ var CROUCH_MAX_Z = 4;
2779
+ var STAND_MAX_Z = 32;
2780
+ var ABOVE_WATER_OFFSET = 8;
2781
+ function checkDuckState(params) {
2782
+ const { pmType } = params;
2783
+ if (pmType === 5 /* Gib */) {
2784
+ const dims2 = computePlayerDimensions(pmType, params.pmFlags);
2785
+ return { pmFlags: params.pmFlags, ducked: hasPmFlag(params.pmFlags, 1 /* Ducked */), changed: false, ...dims2 };
2954
2786
  }
2955
- if (wishspeed > maxSpeed) {
2956
- const scale = maxSpeed / wishspeed;
2957
- wishvel = scaleVec3(wishvel, scale);
2958
- wishspeed = maxSpeed;
2959
- if (wishspeed !== 0) {
2960
- wishdir = normalizeVec3(wishvel);
2787
+ let flags = params.pmFlags;
2788
+ let changed = false;
2789
+ if (pmType === 4 /* Dead */) {
2790
+ if (!hasPmFlag(flags, 1 /* Ducked */)) {
2791
+ flags = addPmFlag(flags, 1 /* Ducked */);
2792
+ changed = true;
2961
2793
  }
2962
- }
2963
- if (onLadder) {
2964
- velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: pmAccelerate, frametime });
2965
- if (Math.abs(wishvel.z) < Number.EPSILON) {
2966
- velocity = dampVerticalVelocity(velocity, gravity, frametime);
2794
+ } else if (shouldDuck(params)) {
2795
+ if (!hasPmFlag(flags, 1 /* Ducked */) && !isDuckBlocked(params)) {
2796
+ flags = addPmFlag(flags, 1 /* Ducked */);
2797
+ changed = true;
2967
2798
  }
2968
- return runStepSlideMove({
2969
- origin,
2970
- velocity,
2971
- frametime,
2972
- mins,
2973
- maxs,
2974
- trace,
2975
- overbounce,
2976
- stepSize,
2977
- maxBumps,
2978
- maxClipPlanes,
2979
- hasTime
2980
- });
2799
+ } else if (hasPmFlag(flags, 1 /* Ducked */) && !isStandBlocked(params)) {
2800
+ flags = removePmFlag(flags, 1 /* Ducked */);
2801
+ changed = true;
2981
2802
  }
2982
- if (onGround) {
2983
- velocity = { ...velocity, z: 0 };
2984
- velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: pmAccelerate, frametime });
2985
- if (gravity > 0) {
2986
- velocity = { ...velocity, z: 0 };
2987
- } else {
2988
- velocity = { ...velocity, z: velocity.z - gravity * frametime };
2989
- }
2990
- if (velocity.x === 0 && velocity.y === 0) {
2991
- return {
2992
- origin,
2993
- velocity,
2994
- planes: [],
2995
- blocked: 0,
2996
- stopped: true,
2997
- stepped: false,
2998
- stepHeight: 0
2999
- };
3000
- }
3001
- return runStepSlideMove({
3002
- origin,
3003
- velocity,
3004
- frametime,
3005
- mins,
3006
- maxs,
3007
- trace,
3008
- overbounce,
3009
- stepSize,
3010
- maxBumps,
3011
- maxClipPlanes,
3012
- hasTime
3013
- });
2803
+ const dims = computePlayerDimensions(pmType, flags);
2804
+ const ducked = pmType === 4 /* Dead */ || hasPmFlag(flags, 1 /* Ducked */);
2805
+ return { pmFlags: flags, ducked, changed, ...dims };
2806
+ }
2807
+ function shouldDuck(params) {
2808
+ if ((params.buttons & 16 /* Crouch */) === 0) {
2809
+ return false;
3014
2810
  }
3015
- if (pmAirAccelerate > 0) {
3016
- velocity = applyPmoveAirAccelerate({
3017
- velocity,
3018
- wishdir,
3019
- wishspeed,
3020
- accel: pmAirAccelerate,
3021
- frametime
3022
- });
2811
+ if (params.onLadder || params.n64Physics) {
2812
+ return false;
2813
+ }
2814
+ if (params.hasGroundEntity) {
2815
+ return true;
2816
+ }
2817
+ if (params.waterlevel <= 1 /* Feet */ && !isAboveWater(params)) {
2818
+ return true;
2819
+ }
2820
+ return false;
2821
+ }
2822
+ function isDuckBlocked(params) {
2823
+ const trace = params.trace({
2824
+ start: params.origin,
2825
+ end: params.origin,
2826
+ mins: params.mins,
2827
+ maxs: withZ(params.maxs, CROUCH_MAX_Z),
2828
+ mask: MASK_SOLID
2829
+ });
2830
+ return trace.allsolid;
2831
+ }
2832
+ function isStandBlocked(params) {
2833
+ const trace = params.trace({
2834
+ start: params.origin,
2835
+ end: params.origin,
2836
+ mins: params.mins,
2837
+ maxs: withZ(params.maxs, STAND_MAX_Z),
2838
+ mask: MASK_SOLID
2839
+ });
2840
+ return trace.allsolid;
2841
+ }
2842
+ function isAboveWater(params) {
2843
+ const below = { x: params.origin.x, y: params.origin.y, z: params.origin.z - ABOVE_WATER_OFFSET };
2844
+ const solidTrace = params.trace({
2845
+ start: params.origin,
2846
+ end: below,
2847
+ mins: params.mins,
2848
+ maxs: params.maxs,
2849
+ mask: MASK_SOLID
2850
+ });
2851
+ if (solidTrace.fraction < 1) {
2852
+ return false;
2853
+ }
2854
+ const waterTrace = params.trace({
2855
+ start: params.origin,
2856
+ end: below,
2857
+ mins: params.mins,
2858
+ maxs: params.maxs,
2859
+ mask: MASK_WATER
2860
+ });
2861
+ return waterTrace.fraction < 1;
2862
+ }
2863
+ function withZ(vec, z) {
2864
+ return { x: vec.x, y: vec.y, z };
2865
+ }
2866
+
2867
+ // src/pmove/pmove.ts
2868
+ var FRAMETIME = 0.025;
2869
+ function applyPmoveFriction(params) {
2870
+ const {
2871
+ velocity,
2872
+ frametime,
2873
+ onGround,
2874
+ groundIsSlick,
2875
+ onLadder,
2876
+ waterlevel,
2877
+ pmFriction,
2878
+ pmStopSpeed,
2879
+ pmWaterFriction
2880
+ } = params;
2881
+ const speed = lengthVec3(velocity);
2882
+ if (speed < 1) {
2883
+ return { x: 0, y: 0, z: velocity.z };
2884
+ }
2885
+ let drop = 0;
2886
+ if (onGround && !groundIsSlick || onLadder) {
2887
+ const control = speed < pmStopSpeed ? pmStopSpeed : speed;
2888
+ const friction = pmFriction;
2889
+ drop += control * friction * frametime;
2890
+ }
2891
+ if (waterlevel > 0 && !onLadder) {
2892
+ drop += speed * pmWaterFriction * waterlevel * frametime;
2893
+ }
2894
+ let newspeed = speed - drop;
2895
+ if (newspeed < 0) {
2896
+ newspeed = 0;
2897
+ }
2898
+ if (newspeed === speed) {
2899
+ return velocity;
2900
+ }
2901
+ const scale = newspeed / speed;
2902
+ return scaleVec3(velocity, scale);
2903
+ }
2904
+ function applyPmoveAccelerate(params) {
2905
+ const { velocity, wishdir, wishspeed, accel, frametime } = params;
2906
+ const currentSpeed = dotVec3(velocity, wishdir);
2907
+ const addSpeed = wishspeed - currentSpeed;
2908
+ if (addSpeed <= 0) {
2909
+ return velocity;
2910
+ }
2911
+ let accelSpeed = accel * frametime * wishspeed;
2912
+ if (accelSpeed > addSpeed) {
2913
+ accelSpeed = addSpeed;
2914
+ }
2915
+ return {
2916
+ x: velocity.x + wishdir.x * accelSpeed,
2917
+ y: velocity.y + wishdir.y * accelSpeed,
2918
+ z: velocity.z + wishdir.z * accelSpeed
2919
+ };
2920
+ }
2921
+ function applyPmoveAirAccelerate(params) {
2922
+ const { velocity, wishdir, wishspeed, accel, frametime } = params;
2923
+ const wishspd = Math.min(wishspeed, 30);
2924
+ const currentSpeed = dotVec3(velocity, wishdir);
2925
+ const addSpeed = wishspd - currentSpeed;
2926
+ if (addSpeed <= 0) {
2927
+ return velocity;
2928
+ }
2929
+ let accelSpeed = accel * wishspeed * frametime;
2930
+ if (accelSpeed > addSpeed) {
2931
+ accelSpeed = addSpeed;
2932
+ }
2933
+ return {
2934
+ x: velocity.x + wishdir.x * accelSpeed,
2935
+ y: velocity.y + wishdir.y * accelSpeed,
2936
+ z: velocity.z + wishdir.z * accelSpeed
2937
+ };
2938
+ }
2939
+ function pmoveCmdScale(cmd, maxSpeed) {
2940
+ const forward = Math.abs(cmd.forwardmove);
2941
+ const side = Math.abs(cmd.sidemove);
2942
+ const up = Math.abs(cmd.upmove);
2943
+ const max = Math.max(forward, side, up);
2944
+ if (max === 0) {
2945
+ return 0;
2946
+ }
2947
+ const total = Math.sqrt(cmd.forwardmove * cmd.forwardmove + cmd.sidemove * cmd.sidemove + cmd.upmove * cmd.upmove);
2948
+ return maxSpeed * max / (127 * total);
2949
+ }
2950
+ function buildAirGroundWish(params) {
2951
+ const { forward, right, cmd, maxSpeed } = params;
2952
+ let wishvel = {
2953
+ x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
2954
+ y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
2955
+ z: 0
2956
+ };
2957
+ let wishspeed = lengthVec3(wishvel);
2958
+ if (wishspeed > maxSpeed) {
2959
+ const scale = maxSpeed / wishspeed;
2960
+ wishvel = scaleVec3(wishvel, scale);
2961
+ wishspeed = maxSpeed;
2962
+ }
2963
+ return {
2964
+ wishdir: wishspeed === 0 ? wishvel : normalizeVec3(wishvel),
2965
+ wishspeed
2966
+ };
2967
+ }
2968
+ function buildWaterWish(params) {
2969
+ const { forward, right, cmd, maxSpeed } = params;
2970
+ let wishvel = {
2971
+ x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
2972
+ y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
2973
+ z: forward.z * cmd.forwardmove + right.z * cmd.sidemove
2974
+ };
2975
+ if (cmd.upmove > 10) {
2976
+ wishvel = addVec3(wishvel, { x: 0, y: 0, z: cmd.upmove });
2977
+ } else if (cmd.upmove < -10) {
2978
+ wishvel = addVec3(wishvel, { x: 0, y: 0, z: cmd.upmove });
2979
+ } else if (Math.abs(cmd.forwardmove) < 10 && Math.abs(cmd.sidemove) < 10) {
2980
+ wishvel = addVec3(wishvel, { x: 0, y: 0, z: -60 });
3023
2981
  } else {
3024
- velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: DEFAULT_AIR_ACCELERATE, frametime });
2982
+ wishvel = addVec3(wishvel, { x: 0, y: 0, z: 10 });
2983
+ }
2984
+ let wishspeed = lengthVec3(wishvel);
2985
+ if (wishspeed > maxSpeed) {
2986
+ const scale = maxSpeed / wishspeed;
2987
+ wishvel = scaleVec3(wishvel, scale);
2988
+ wishspeed = maxSpeed;
2989
+ }
2990
+ wishspeed *= 0.5;
2991
+ return {
2992
+ wishdir: wishspeed === 0 ? wishvel : normalizeVec3(wishvel),
2993
+ wishspeed
2994
+ };
2995
+ }
2996
+ function runPmove(state, imports) {
2997
+ if (state.pmType === 4 /* Dead */) {
2998
+ return state;
2999
+ }
3000
+ let nextState = { ...state };
3001
+ const catResult = categorizePosition({
3002
+ pmType: nextState.pmType,
3003
+ pmFlags: nextState.pmFlags,
3004
+ pmTime: 0,
3005
+ n64Physics: false,
3006
+ velocity: nextState.velocity,
3007
+ startVelocity: nextState.velocity,
3008
+ origin: nextState.origin,
3009
+ mins: nextState.mins,
3010
+ maxs: nextState.maxs,
3011
+ viewheight: nextState.viewHeight,
3012
+ trace: imports.trace,
3013
+ pointContents: imports.pointcontents
3014
+ });
3015
+ nextState.pmFlags = catResult.pmFlags;
3016
+ nextState.waterlevel = catResult.waterlevel;
3017
+ nextState.watertype = catResult.watertype;
3018
+ const duckResult = checkDuckState({
3019
+ pmType: nextState.pmType,
3020
+ pmFlags: nextState.pmFlags,
3021
+ buttons: nextState.cmd.buttons,
3022
+ waterlevel: nextState.waterlevel,
3023
+ hasGroundEntity: (nextState.pmFlags & 4 /* OnGround */) !== 0,
3024
+ onLadder: false,
3025
+ n64Physics: false,
3026
+ origin: nextState.origin,
3027
+ mins: nextState.mins,
3028
+ maxs: nextState.maxs,
3029
+ trace: (params) => {
3030
+ return imports.trace(params.start, params.end, params.mins, params.maxs);
3031
+ }
3032
+ });
3033
+ nextState.pmFlags = duckResult.pmFlags;
3034
+ nextState.mins = duckResult.mins;
3035
+ nextState.maxs = duckResult.maxs;
3036
+ nextState.viewHeight = duckResult.viewheight;
3037
+ const jumpResult = checkJump({
3038
+ pmFlags: nextState.pmFlags,
3039
+ pmType: nextState.pmType,
3040
+ buttons: nextState.cmd.buttons,
3041
+ waterlevel: nextState.waterlevel,
3042
+ onGround: (nextState.pmFlags & 4 /* OnGround */) !== 0,
3043
+ velocity: nextState.velocity,
3044
+ origin: nextState.origin
3045
+ });
3046
+ nextState.pmFlags = jumpResult.pmFlags;
3047
+ nextState.velocity = jumpResult.velocity;
3048
+ nextState.origin = jumpResult.origin;
3049
+ if (jumpResult.onGround !== ((nextState.pmFlags & 4 /* OnGround */) !== 0)) {
3050
+ if (jumpResult.onGround) {
3051
+ nextState.pmFlags = addPmFlag(nextState.pmFlags, 4 /* OnGround */);
3052
+ } else {
3053
+ nextState.pmFlags = removePmFlag(nextState.pmFlags, 4 /* OnGround */);
3054
+ }
3055
+ }
3056
+ const onGround = (nextState.pmFlags & 4 /* OnGround */) !== 0;
3057
+ const velocityBeforeFriction = nextState.velocity;
3058
+ nextState.velocity = applyPmoveFriction({
3059
+ velocity: nextState.velocity,
3060
+ frametime: FRAMETIME,
3061
+ onGround,
3062
+ groundIsSlick: false,
3063
+ onLadder: false,
3064
+ // Defaulting to false for now as ladder logic is complex
3065
+ waterlevel: nextState.waterlevel,
3066
+ pmFriction: 6,
3067
+ // Default
3068
+ pmStopSpeed: 100,
3069
+ // Default
3070
+ pmWaterFriction: 1
3071
+ // Default
3072
+ });
3073
+ const { forward, right } = angleVectors(nextState.viewAngles);
3074
+ if (nextState.pmType === 2 /* NoClip */) {
3075
+ const wishvel = {
3076
+ x: forward.x * nextState.cmd.forwardmove + right.x * nextState.cmd.sidemove,
3077
+ y: forward.y * nextState.cmd.forwardmove + right.y * nextState.cmd.sidemove,
3078
+ z: nextState.cmd.upmove
3079
+ };
3080
+ const scale = FRAMETIME;
3081
+ nextState.velocity = wishvel;
3082
+ nextState.origin = {
3083
+ x: nextState.origin.x + wishvel.x * scale,
3084
+ y: nextState.origin.y + wishvel.y * scale,
3085
+ z: nextState.origin.z + wishvel.z * scale
3086
+ };
3087
+ } else if (nextState.waterlevel >= 2) {
3088
+ const outcome = applyPmoveWaterMove({
3089
+ origin: nextState.origin,
3090
+ velocity: nextState.velocity,
3091
+ frametime: FRAMETIME,
3092
+ mins: nextState.mins,
3093
+ maxs: nextState.maxs,
3094
+ trace: imports.trace,
3095
+ cmd: nextState.cmd,
3096
+ forward,
3097
+ right,
3098
+ pmFlags: nextState.pmFlags,
3099
+ onGround,
3100
+ pmMaxSpeed: 300,
3101
+ pmDuckSpeed: 100,
3102
+ pmWaterAccelerate: 4,
3103
+ pmWaterSpeed: 400,
3104
+ onLadder: false,
3105
+ watertype: nextState.watertype,
3106
+ groundContents: 0,
3107
+ // Should be passed in?
3108
+ waterlevel: nextState.waterlevel,
3109
+ viewPitch: nextState.viewAngles.x,
3110
+ ladderMod: 1,
3111
+ stepSize: 18
3112
+ // Added stepSize for consistency, though water move might not use it heavily
3113
+ });
3114
+ nextState.origin = outcome.origin;
3115
+ nextState.velocity = outcome.velocity;
3116
+ } else if ((nextState.pmFlags & 4 /* OnGround */) === 0) {
3117
+ const outcome = applyPmoveAirMove({
3118
+ origin: nextState.origin,
3119
+ velocity: nextState.velocity,
3120
+ frametime: FRAMETIME,
3121
+ mins: nextState.mins,
3122
+ maxs: nextState.maxs,
3123
+ trace: imports.trace,
3124
+ cmd: nextState.cmd,
3125
+ forward,
3126
+ right,
3127
+ pmFlags: nextState.pmFlags,
3128
+ onGround,
3129
+ gravity: nextState.gravity,
3130
+ pmType: nextState.pmType,
3131
+ pmAccelerate: 10,
3132
+ pmAirAccelerate: 1,
3133
+ pmMaxSpeed: 300,
3134
+ pmDuckSpeed: 100,
3135
+ onLadder: false,
3136
+ waterlevel: nextState.waterlevel,
3137
+ watertype: nextState.watertype,
3138
+ groundContents: 0,
3139
+ viewPitch: nextState.viewAngles.x,
3140
+ ladderMod: 1,
3141
+ pmWaterSpeed: 400,
3142
+ stepSize: 18
3143
+ // Added stepSize
3144
+ });
3145
+ nextState.origin = outcome.origin;
3146
+ nextState.velocity = outcome.velocity;
3147
+ } else {
3148
+ const outcome = applyPmoveWalkMove({
3149
+ origin: nextState.origin,
3150
+ velocity: nextState.velocity,
3151
+ frametime: FRAMETIME,
3152
+ mins: nextState.mins,
3153
+ maxs: nextState.maxs,
3154
+ trace: imports.trace,
3155
+ cmd: nextState.cmd,
3156
+ forward,
3157
+ right,
3158
+ pmFlags: nextState.pmFlags,
3159
+ onGround,
3160
+ gravity: nextState.gravity,
3161
+ pmType: nextState.pmType,
3162
+ pmAccelerate: 10,
3163
+ pmMaxSpeed: 300,
3164
+ pmDuckSpeed: 100,
3165
+ onLadder: false,
3166
+ waterlevel: nextState.waterlevel,
3167
+ watertype: nextState.watertype,
3168
+ groundContents: 0,
3169
+ viewPitch: nextState.viewAngles.x,
3170
+ ladderMod: 1,
3171
+ pmWaterSpeed: 400,
3172
+ stepSize: 18
3173
+ // Added stepSize
3174
+ });
3175
+ nextState.origin = outcome.origin;
3176
+ nextState.velocity = outcome.velocity;
3177
+ }
3178
+ const catResultEnd = categorizePosition({
3179
+ pmType: nextState.pmType,
3180
+ pmFlags: nextState.pmFlags,
3181
+ pmTime: 0,
3182
+ n64Physics: false,
3183
+ velocity: nextState.velocity,
3184
+ startVelocity: nextState.velocity,
3185
+ origin: nextState.origin,
3186
+ mins: nextState.mins,
3187
+ maxs: nextState.maxs,
3188
+ viewheight: nextState.viewHeight,
3189
+ trace: imports.trace,
3190
+ pointContents: imports.pointcontents
3191
+ });
3192
+ nextState.pmFlags = catResultEnd.pmFlags;
3193
+ nextState.waterlevel = catResultEnd.waterlevel;
3194
+ nextState.watertype = catResultEnd.watertype;
3195
+ return nextState;
3196
+ }
3197
+
3198
+ // src/pmove/stuck.ts
3199
+ var AXES = ["x", "y", "z"];
3200
+ var SIDE_CHECKS = [
3201
+ { normal: [0, 0, 1], mins: [-1, -1, 0], maxs: [1, 1, 0] },
3202
+ { normal: [0, 0, -1], mins: [-1, -1, 0], maxs: [1, 1, 0] },
3203
+ { normal: [1, 0, 0], mins: [0, -1, -1], maxs: [0, 1, 1] },
3204
+ { normal: [-1, 0, 0], mins: [0, -1, -1], maxs: [0, 1, 1] },
3205
+ { normal: [0, 1, 0], mins: [-1, 0, -1], maxs: [1, 0, 1] },
3206
+ { normal: [0, -1, 0], mins: [-1, 0, -1], maxs: [1, 0, 1] }
3207
+ ];
3208
+ var ZERO_VEC = { x: 0, y: 0, z: 0 };
3209
+ function cloneMutable(vec) {
3210
+ return { x: vec.x, y: vec.y, z: vec.z };
3211
+ }
3212
+ function tupleToVec3(tuple) {
3213
+ return { x: tuple[0], y: tuple[1], z: tuple[2] };
3214
+ }
3215
+ function adjustAxis(vec, axis, delta) {
3216
+ if (delta === 0) return;
3217
+ switch (axis) {
3218
+ case "x":
3219
+ vec.x += delta;
3220
+ break;
3221
+ case "y":
3222
+ vec.y += delta;
3223
+ break;
3224
+ case "z":
3225
+ vec.z += delta;
3226
+ break;
3227
+ }
3228
+ }
3229
+ function setAxis(vec, axis, value) {
3230
+ switch (axis) {
3231
+ case "x":
3232
+ vec.x = value;
3233
+ break;
3234
+ case "y":
3235
+ vec.y = value;
3236
+ break;
3237
+ case "z":
3238
+ vec.z = value;
3239
+ break;
3240
+ }
3241
+ }
3242
+ function axisValue(vec, axis) {
3243
+ switch (axis) {
3244
+ case "x":
3245
+ return vec.x;
3246
+ case "y":
3247
+ return vec.y;
3248
+ case "z":
3249
+ default:
3250
+ return vec.z;
3251
+ }
3252
+ }
3253
+ function boundValue(code, axis, mins, maxs) {
3254
+ if (code === -1) {
3255
+ return axisValue(mins, axis);
3256
+ }
3257
+ if (code === 1) {
3258
+ return axisValue(maxs, axis);
3259
+ }
3260
+ return 0;
3261
+ }
3262
+ function applySideOffset(base, side, mins, maxs) {
3263
+ const result = cloneMutable(base);
3264
+ for (let i = 0; i < AXES.length; i++) {
3265
+ const axis = AXES[i];
3266
+ const normal = side.normal[i];
3267
+ if (normal < 0) {
3268
+ adjustAxis(result, axis, axisValue(mins, axis));
3269
+ } else if (normal > 0) {
3270
+ adjustAxis(result, axis, axisValue(maxs, axis));
3271
+ }
3272
+ }
3273
+ return result;
3274
+ }
3275
+ function buildSideBounds(side, mins, maxs) {
3276
+ const localMins = cloneMutable(ZERO_VEC);
3277
+ const localMaxs = cloneMutable(ZERO_VEC);
3278
+ for (let i = 0; i < AXES.length; i++) {
3279
+ const axis = AXES[i];
3280
+ setAxis(localMins, axis, boundValue(side.mins[i], axis, mins, maxs));
3281
+ setAxis(localMaxs, axis, boundValue(side.maxs[i], axis, mins, maxs));
3282
+ }
3283
+ return { mins: localMins, maxs: localMaxs };
3284
+ }
3285
+ function addEpsilon(source, axis, direction) {
3286
+ if (!axis || direction === 0) {
3287
+ return source;
3288
+ }
3289
+ const clone = cloneMutable(source);
3290
+ adjustAxis(clone, axis, direction);
3291
+ return clone;
3292
+ }
3293
+ function addEpsilonImmutable(vec, axis, direction) {
3294
+ if (!axis || direction === 0) {
3295
+ return vec;
3296
+ }
3297
+ switch (axis) {
3298
+ case "x":
3299
+ return { ...vec, x: vec.x + direction };
3300
+ case "y":
3301
+ return { ...vec, y: vec.y + direction };
3302
+ case "z":
3303
+ default:
3304
+ return { ...vec, z: vec.z + direction };
3305
+ }
3306
+ }
3307
+ function fixStuckObjectGeneric(params) {
3308
+ const { origin, mins, maxs, trace } = params;
3309
+ const initial = trace(origin, mins, maxs, origin);
3310
+ if (!initial.startsolid) {
3311
+ return { result: "good-position", origin: { ...origin } };
3312
+ }
3313
+ const candidates = [];
3314
+ for (let i = 0; i < SIDE_CHECKS.length; i++) {
3315
+ const side = SIDE_CHECKS[i];
3316
+ const { mins: localMins, maxs: localMaxs } = buildSideBounds(side, mins, maxs);
3317
+ let start = applySideOffset(origin, side, mins, maxs);
3318
+ let tr = trace(start, localMins, localMaxs, start);
3319
+ let epsilonAxis;
3320
+ let epsilonDir = 0;
3321
+ if (tr.startsolid) {
3322
+ for (let axisIndex = 0; axisIndex < AXES.length; axisIndex++) {
3323
+ if (side.normal[axisIndex] !== 0) {
3324
+ continue;
3325
+ }
3326
+ const axis = AXES[axisIndex];
3327
+ let epsilonStart = cloneMutable(start);
3328
+ adjustAxis(epsilonStart, axis, 1);
3329
+ tr = trace(epsilonStart, localMins, localMaxs, epsilonStart);
3330
+ if (!tr.startsolid) {
3331
+ start = epsilonStart;
3332
+ epsilonAxis = axis;
3333
+ epsilonDir = 1;
3334
+ break;
3335
+ }
3336
+ epsilonStart = cloneMutable(start);
3337
+ adjustAxis(epsilonStart, axis, -1);
3338
+ tr = trace(epsilonStart, localMins, localMaxs, epsilonStart);
3339
+ if (!tr.startsolid) {
3340
+ start = epsilonStart;
3341
+ epsilonAxis = axis;
3342
+ epsilonDir = -1;
3343
+ break;
3344
+ }
3345
+ }
3346
+ }
3347
+ if (tr.startsolid) {
3348
+ continue;
3349
+ }
3350
+ const otherSide = SIDE_CHECKS[i ^ 1];
3351
+ let oppositeStart = applySideOffset(origin, otherSide, mins, maxs);
3352
+ oppositeStart = addEpsilon(oppositeStart, epsilonAxis, epsilonDir);
3353
+ tr = trace(start, localMins, localMaxs, oppositeStart);
3354
+ if (tr.startsolid) {
3355
+ continue;
3356
+ }
3357
+ const normal = tupleToVec3(side.normal);
3358
+ const end = addVec3(tr.endpos ?? oppositeStart, scaleVec3(normal, 0.125));
3359
+ const delta = subtractVec3(end, oppositeStart);
3360
+ let newOrigin = addVec3(origin, delta);
3361
+ newOrigin = addEpsilonImmutable(newOrigin, epsilonAxis, epsilonDir);
3362
+ const validation = trace(newOrigin, mins, maxs, newOrigin);
3363
+ if (validation.startsolid) {
3364
+ continue;
3365
+ }
3366
+ candidates.push({ origin: newOrigin, distance: lengthSquaredVec3(delta) });
3025
3367
  }
3026
- if (pmType !== 1 /* Grapple */) {
3027
- velocity = { ...velocity, z: velocity.z - gravity * frametime };
3368
+ if (candidates.length === 0) {
3369
+ return { result: "no-good-position", origin: { ...origin } };
3028
3370
  }
3029
- return runStepSlideMove({
3030
- origin,
3031
- velocity,
3032
- frametime,
3033
- mins,
3034
- maxs,
3035
- trace,
3036
- overbounce,
3037
- stepSize,
3038
- maxBumps,
3039
- maxClipPlanes,
3040
- hasTime
3041
- });
3371
+ candidates.sort((a, b) => a.distance - b.distance);
3372
+ return { result: "fixed", origin: { ...candidates[0].origin } };
3042
3373
  }
3043
- function applyPmoveWaterMove(params) {
3374
+
3375
+ // src/pmove/fly.ts
3376
+ var FLY_FRICTION_MULTIPLIER = 1.5;
3377
+ var BUTTON_VERTICAL_SCALE = 0.5;
3378
+ var DEFAULT_OVERBOUNCE = 1.01;
3379
+ function applyPmoveFlyMove(params) {
3044
3380
  const {
3045
3381
  origin,
3382
+ cmd,
3046
3383
  frametime,
3384
+ pmFriction,
3385
+ pmStopSpeed,
3386
+ pmMaxSpeed,
3387
+ pmAccelerate,
3388
+ pmWaterSpeed,
3389
+ doclip,
3390
+ forward,
3391
+ right,
3047
3392
  mins,
3048
3393
  maxs,
3049
3394
  trace,
3050
- overbounce = DEFAULT_STEP_OVERBOUNCE,
3395
+ overbounce = DEFAULT_OVERBOUNCE,
3051
3396
  stepSize,
3052
3397
  maxBumps,
3053
- maxClipPlanes,
3054
- hasTime,
3055
- forward,
3056
- right,
3057
- cmd,
3058
- pmFlags,
3059
- onGround,
3060
- pmMaxSpeed,
3061
- pmDuckSpeed,
3062
- pmWaterAccelerate,
3063
- pmWaterSpeed,
3064
- onLadder,
3065
- watertype,
3066
- groundContents,
3067
- waterlevel,
3068
- viewPitch,
3069
- ladderMod
3398
+ maxClipPlanes
3070
3399
  } = params;
3071
- let velocity = { ...params.velocity };
3072
- let wishvel = buildFullWishVelocity(forward, right, cmd);
3073
- if (isIdleInWater(cmd, onGround)) {
3074
- wishvel = { ...wishvel, z: wishvel.z - WATER_DRIFT_SPEED };
3075
- } else {
3076
- if (hasButton2(cmd, 16 /* Crouch */)) {
3077
- wishvel = addVec3(wishvel, { x: 0, y: 0, z: -pmWaterSpeed * 0.5 });
3078
- } else if (hasButton2(cmd, 8 /* Jump */)) {
3079
- wishvel = addVec3(wishvel, { x: 0, y: 0, z: pmWaterSpeed * 0.5 });
3080
- }
3081
- }
3082
- wishvel = applyPmoveAddCurrents({
3083
- wishVelocity: wishvel,
3084
- onLadder,
3085
- onGround,
3086
- waterlevel,
3087
- watertype,
3088
- groundContents,
3400
+ let velocity = applyFlyFriction({ velocity: params.velocity, pmFriction, pmStopSpeed, frametime });
3401
+ const wishdirVelocity = buildFlyWishVelocity({
3089
3402
  cmd,
3090
- viewPitch,
3091
- maxSpeed: hasPmFlag(pmFlags, 1 /* Ducked */) ? pmDuckSpeed : pmMaxSpeed,
3092
- ladderMod,
3093
- waterSpeed: pmWaterSpeed,
3094
3403
  forward,
3095
- origin,
3096
- mins,
3097
- maxs,
3098
- trace
3404
+ right,
3405
+ pmMaxSpeed,
3406
+ pmWaterSpeed
3099
3407
  });
3100
- let wishdir = wishvel;
3101
- let wishspeed = lengthVec3(wishdir);
3102
- if (wishspeed !== 0) {
3103
- wishdir = normalizeVec3(wishdir);
3408
+ if (wishdirVelocity.wishspeed > 0) {
3409
+ velocity = applyPmoveAccelerate({
3410
+ velocity,
3411
+ wishdir: wishdirVelocity.wishdir,
3412
+ wishspeed: wishdirVelocity.accelSpeed,
3413
+ accel: pmAccelerate,
3414
+ frametime
3415
+ });
3104
3416
  }
3105
- if (wishspeed > pmMaxSpeed) {
3106
- const scale = pmMaxSpeed / wishspeed;
3107
- wishvel = scaleVec3(wishvel, scale);
3108
- wishspeed = pmMaxSpeed;
3109
- if (wishspeed !== 0) {
3110
- wishdir = normalizeVec3(wishvel);
3111
- }
3417
+ if (!doclip) {
3418
+ const originDelta = scaleVec3(velocity, frametime);
3419
+ const nextOrigin = addVec3(origin, originDelta);
3420
+ return {
3421
+ origin: nextOrigin,
3422
+ velocity,
3423
+ planes: [],
3424
+ blocked: 0,
3425
+ stopped: velocity.x === 0 && velocity.y === 0 && velocity.z === 0,
3426
+ stepped: false,
3427
+ stepHeight: 0
3428
+ };
3112
3429
  }
3113
- wishspeed *= 0.5;
3114
- const ducked = hasPmFlag(pmFlags, 1 /* Ducked */);
3115
- if (ducked && wishspeed > pmDuckSpeed) {
3116
- const scale = pmDuckSpeed / wishspeed;
3117
- wishvel = scaleVec3(wishvel, scale);
3118
- wishspeed = pmDuckSpeed;
3119
- if (wishspeed !== 0) {
3120
- wishdir = normalizeVec3(wishvel);
3121
- }
3430
+ if (!trace || !mins || !maxs) {
3431
+ throw new Error("applyPmoveFlyMove: doclip=true requires trace/mins/maxs");
3122
3432
  }
3123
- velocity = applyPmoveAccelerate({ velocity, wishdir, wishspeed, accel: pmWaterAccelerate, frametime });
3124
- return runStepSlideMove({
3433
+ return stepSlideMove({
3125
3434
  origin,
3126
3435
  velocity,
3127
3436
  frametime,
3437
+ overbounce,
3438
+ trace,
3128
3439
  mins,
3129
3440
  maxs,
3130
- trace,
3131
- overbounce,
3132
3441
  stepSize,
3133
3442
  maxBumps,
3134
- maxClipPlanes,
3135
- hasTime
3443
+ maxClipPlanes
3136
3444
  });
3137
3445
  }
3138
- function buildPlanarWishVelocity(forward, right, cmd) {
3139
- return {
3140
- x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
3141
- y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
3142
- z: 0
3143
- };
3446
+ function applyFlyFriction(params) {
3447
+ const { velocity, pmFriction, pmStopSpeed, frametime } = params;
3448
+ const speed = lengthVec3(velocity);
3449
+ if (speed < 1) {
3450
+ return { x: 0, y: 0, z: 0 };
3451
+ }
3452
+ const friction = pmFriction * FLY_FRICTION_MULTIPLIER;
3453
+ const control = speed < pmStopSpeed ? pmStopSpeed : speed;
3454
+ const drop = control * friction * frametime;
3455
+ let newspeed = speed - drop;
3456
+ if (newspeed < 0) {
3457
+ newspeed = 0;
3458
+ }
3459
+ if (newspeed === speed) {
3460
+ return velocity;
3461
+ }
3462
+ return scaleVec3(velocity, newspeed / speed);
3144
3463
  }
3145
- function buildFullWishVelocity(forward, right, cmd) {
3146
- return {
3147
- x: forward.x * cmd.forwardmove + right.x * cmd.sidemove,
3148
- y: forward.y * cmd.forwardmove + right.y * cmd.sidemove,
3149
- z: forward.z * cmd.forwardmove + right.z * cmd.sidemove
3464
+ function buildFlyWishVelocity(params) {
3465
+ const { cmd, forward, right, pmMaxSpeed, pmWaterSpeed } = params;
3466
+ const forwardNorm = normalizeVec3(forward);
3467
+ const rightNorm = normalizeVec3(right);
3468
+ const wishvel = {
3469
+ x: forwardNorm.x * cmd.forwardmove + rightNorm.x * cmd.sidemove,
3470
+ y: forwardNorm.y * cmd.forwardmove + rightNorm.y * cmd.sidemove,
3471
+ z: forwardNorm.z * cmd.forwardmove + rightNorm.z * cmd.sidemove
3150
3472
  };
3151
- }
3152
- function hasButton2(cmd, button) {
3153
- return (cmd.buttons ?? 0) & button ? true : false;
3154
- }
3155
- function isIdleInWater(cmd, onGround) {
3156
- const noMove = cmd.forwardmove === 0 && cmd.sidemove === 0;
3157
- const noButtons = (cmd.buttons ?? 0) & (8 /* Jump */ | 16 /* Crouch */) ? false : true;
3158
- return noMove && noButtons && !onGround;
3159
- }
3160
- function dampVerticalVelocity(velocity, gravity, frametime) {
3161
- let z = velocity.z;
3162
- const delta = gravity * frametime;
3163
- if (z > 0) {
3164
- z -= delta;
3165
- if (z < 0) {
3166
- z = 0;
3167
- }
3168
- } else {
3169
- z += delta;
3170
- if (z > 0) {
3171
- z = 0;
3172
- }
3473
+ let adjusted = wishvel;
3474
+ const buttons = cmd.buttons ?? 0;
3475
+ if (buttons & 8 /* Jump */) {
3476
+ adjusted = addVec3(adjusted, { x: 0, y: 0, z: pmWaterSpeed * BUTTON_VERTICAL_SCALE });
3173
3477
  }
3174
- return { ...velocity, z };
3175
- }
3176
- function runStepSlideMove(params) {
3177
- const { origin, velocity, frametime, mins, maxs, trace, overbounce = DEFAULT_STEP_OVERBOUNCE, stepSize, maxBumps, maxClipPlanes, hasTime } = params;
3178
- return stepSlideMove({
3179
- origin,
3180
- velocity,
3181
- frametime,
3182
- trace,
3183
- mins,
3184
- maxs,
3185
- overbounce,
3186
- stepSize,
3187
- maxBumps,
3188
- maxClipPlanes,
3189
- hasTime
3190
- });
3478
+ if (buttons & 16 /* Crouch */) {
3479
+ adjusted = addVec3(adjusted, { x: 0, y: 0, z: -pmWaterSpeed * BUTTON_VERTICAL_SCALE });
3480
+ }
3481
+ let wishspeed = lengthVec3(adjusted);
3482
+ let wishdir = wishspeed === 0 ? { x: 0, y: 0, z: 0 } : normalizeVec3(adjusted);
3483
+ if (wishspeed > pmMaxSpeed) {
3484
+ const scale = pmMaxSpeed / wishspeed;
3485
+ adjusted = scaleVec3(adjusted, scale);
3486
+ wishspeed = pmMaxSpeed;
3487
+ wishdir = wishspeed === 0 ? { x: 0, y: 0, z: 0 } : normalizeVec3(adjusted);
3488
+ }
3489
+ const accelSpeed = wishspeed * 2;
3490
+ return { wishdir, wishspeed, accelSpeed };
3191
3491
  }
3192
3492
 
3193
3493
  // src/pmove/special.ts
@@ -4131,7 +4431,7 @@ var U_MODEL4 = 1 << 2;
4131
4431
  var U_REMOVE = 32768;
4132
4432
 
4133
4433
  // src/pmove/apply.ts
4134
- var FRAMETIME = 0.025;
4434
+ var FRAMETIME2 = 0.025;
4135
4435
  var MASK_WATER2 = 33554432;
4136
4436
  var WaterLevel3 = {
4137
4437
  None: 0,
@@ -4185,7 +4485,7 @@ var applyPmove = (state, cmd, trace, pointContents2) => {
4185
4485
  const { forward, right } = angleVectors(adjustedAngles);
4186
4486
  const frictionedVelocity = applyPmoveFriction({
4187
4487
  velocity,
4188
- frametime: FRAMETIME,
4488
+ frametime: FRAMETIME2,
4189
4489
  onGround,
4190
4490
  groundIsSlick: false,
4191
4491
  onLadder: false,
@@ -4211,12 +4511,12 @@ var applyPmove = (state, cmd, trace, pointContents2) => {
4211
4511
  wishspeed: wish.wishspeed,
4212
4512
  // Water movement uses ground acceleration (10), not air acceleration (1)
4213
4513
  accel: onGround || waterLevel >= 2 ? 10 : 1,
4214
- frametime: FRAMETIME
4514
+ frametime: FRAMETIME2
4215
4515
  });
4216
4516
  const traceResult = trace(origin, {
4217
- x: origin.x + finalVelocity.x * FRAMETIME,
4218
- y: origin.y + finalVelocity.y * FRAMETIME,
4219
- z: origin.z + finalVelocity.z * FRAMETIME
4517
+ x: origin.x + finalVelocity.x * FRAMETIME2,
4518
+ y: origin.y + finalVelocity.y * FRAMETIME2,
4519
+ z: origin.z + finalVelocity.z * FRAMETIME2
4220
4520
  });
4221
4521
  return {
4222
4522
  ...newState,
@@ -5084,6 +5384,7 @@ export {
5084
5384
  applyPmoveAirMove,
5085
5385
  applyPmoveFlyMove,
5086
5386
  applyPmoveFriction,
5387
+ applyPmoveWalkMove,
5087
5388
  applyPmoveWaterMove,
5088
5389
  assertContract,
5089
5390
  attenuationToDistanceMultiplier,
@@ -5159,6 +5460,7 @@ export {
5159
5460
  removePmFlag,
5160
5461
  resolveSlideMove,
5161
5462
  rotatePointAroundVector,
5463
+ runPmove,
5162
5464
  scaleVec3,
5163
5465
  slerpVec3,
5164
5466
  slideClipVelocityVec3,