@quake2ts/shared 0.0.1

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 (209) hide show
  1. package/dist/browser/index.global.js +2 -0
  2. package/dist/browser/index.global.js.map +1 -0
  3. package/dist/cjs/index.cjs +6569 -0
  4. package/dist/cjs/index.cjs.map +1 -0
  5. package/dist/esm/index.js +6200 -0
  6. package/dist/esm/index.js.map +1 -0
  7. package/dist/tsconfig.tsbuildinfo +1 -0
  8. package/dist/types/audio/constants.d.ts +24 -0
  9. package/dist/types/audio/constants.d.ts.map +1 -0
  10. package/dist/types/bsp/collision.d.ts +201 -0
  11. package/dist/types/bsp/collision.d.ts.map +1 -0
  12. package/dist/types/bsp/contents.d.ts +72 -0
  13. package/dist/types/bsp/contents.d.ts.map +1 -0
  14. package/dist/types/bsp/spatial.d.ts +13 -0
  15. package/dist/types/bsp/spatial.d.ts.map +1 -0
  16. package/dist/types/index.d.ts +38 -0
  17. package/dist/types/index.d.ts.map +1 -0
  18. package/dist/types/inventory-helpers.d.ts +19 -0
  19. package/dist/types/inventory-helpers.d.ts.map +1 -0
  20. package/dist/types/io/binaryStream.d.ts +38 -0
  21. package/dist/types/io/binaryStream.d.ts.map +1 -0
  22. package/dist/types/io/binaryWriter.d.ts +26 -0
  23. package/dist/types/io/binaryWriter.d.ts.map +1 -0
  24. package/dist/types/io/index.d.ts +4 -0
  25. package/dist/types/io/index.d.ts.map +1 -0
  26. package/dist/types/io/messageBuilder.d.ts +21 -0
  27. package/dist/types/io/messageBuilder.d.ts.map +1 -0
  28. package/dist/types/items/ammo.d.ts +40 -0
  29. package/dist/types/items/ammo.d.ts.map +1 -0
  30. package/dist/types/items/index.d.ts +8 -0
  31. package/dist/types/items/index.d.ts.map +1 -0
  32. package/dist/types/items/powerups.d.ts +31 -0
  33. package/dist/types/items/powerups.d.ts.map +1 -0
  34. package/dist/types/items/weaponInfo.d.ts +5 -0
  35. package/dist/types/items/weaponInfo.d.ts.map +1 -0
  36. package/dist/types/items/weapons.d.ts +27 -0
  37. package/dist/types/items/weapons.d.ts.map +1 -0
  38. package/dist/types/math/angles.d.ts +19 -0
  39. package/dist/types/math/angles.d.ts.map +1 -0
  40. package/dist/types/math/anorms.d.ts +2 -0
  41. package/dist/types/math/anorms.d.ts.map +1 -0
  42. package/dist/types/math/color.d.ts +12 -0
  43. package/dist/types/math/color.d.ts.map +1 -0
  44. package/dist/types/math/mat4.d.ts +7 -0
  45. package/dist/types/math/mat4.d.ts.map +1 -0
  46. package/dist/types/math/random.d.ts +60 -0
  47. package/dist/types/math/random.d.ts.map +1 -0
  48. package/dist/types/math/vec3.d.ts +79 -0
  49. package/dist/types/math/vec3.d.ts.map +1 -0
  50. package/dist/types/net/driver.d.ts +10 -0
  51. package/dist/types/net/driver.d.ts.map +1 -0
  52. package/dist/types/net/index.d.ts +3 -0
  53. package/dist/types/net/index.d.ts.map +1 -0
  54. package/dist/types/net/netchan.d.ts +85 -0
  55. package/dist/types/net/netchan.d.ts.map +1 -0
  56. package/dist/types/pmove/apply.d.ts +5 -0
  57. package/dist/types/pmove/apply.d.ts.map +1 -0
  58. package/dist/types/pmove/categorize.d.ts +36 -0
  59. package/dist/types/pmove/categorize.d.ts.map +1 -0
  60. package/dist/types/pmove/config.d.ts +5 -0
  61. package/dist/types/pmove/config.d.ts.map +1 -0
  62. package/dist/types/pmove/constants.d.ts +76 -0
  63. package/dist/types/pmove/constants.d.ts.map +1 -0
  64. package/dist/types/pmove/currents.d.ts +58 -0
  65. package/dist/types/pmove/currents.d.ts.map +1 -0
  66. package/dist/types/pmove/dimensions.d.ts +14 -0
  67. package/dist/types/pmove/dimensions.d.ts.map +1 -0
  68. package/dist/types/pmove/duck.d.ts +39 -0
  69. package/dist/types/pmove/duck.d.ts.map +1 -0
  70. package/dist/types/pmove/fly.d.ts +34 -0
  71. package/dist/types/pmove/fly.d.ts.map +1 -0
  72. package/dist/types/pmove/index.d.ts +18 -0
  73. package/dist/types/pmove/index.d.ts.map +1 -0
  74. package/dist/types/pmove/jump.d.ts +28 -0
  75. package/dist/types/pmove/jump.d.ts.map +1 -0
  76. package/dist/types/pmove/move.d.ts +78 -0
  77. package/dist/types/pmove/move.d.ts.map +1 -0
  78. package/dist/types/pmove/pmove.d.ts +40 -0
  79. package/dist/types/pmove/pmove.d.ts.map +1 -0
  80. package/dist/types/pmove/slide.d.ts +63 -0
  81. package/dist/types/pmove/slide.d.ts.map +1 -0
  82. package/dist/types/pmove/snap.d.ts +40 -0
  83. package/dist/types/pmove/snap.d.ts.map +1 -0
  84. package/dist/types/pmove/special.d.ts +39 -0
  85. package/dist/types/pmove/special.d.ts.map +1 -0
  86. package/dist/types/pmove/stuck.d.ts +21 -0
  87. package/dist/types/pmove/stuck.d.ts.map +1 -0
  88. package/dist/types/pmove/types.d.ts +72 -0
  89. package/dist/types/pmove/types.d.ts.map +1 -0
  90. package/dist/types/pmove/view.d.ts +19 -0
  91. package/dist/types/pmove/view.d.ts.map +1 -0
  92. package/dist/types/pmove/water.d.ts +21 -0
  93. package/dist/types/pmove/water.d.ts.map +1 -0
  94. package/dist/types/protocol/bitpack.d.ts +17 -0
  95. package/dist/types/protocol/bitpack.d.ts.map +1 -0
  96. package/dist/types/protocol/configstrings.d.ts +73 -0
  97. package/dist/types/protocol/configstrings.d.ts.map +1 -0
  98. package/dist/types/protocol/constants.d.ts +36 -0
  99. package/dist/types/protocol/constants.d.ts.map +1 -0
  100. package/dist/types/protocol/contracts.d.ts +17 -0
  101. package/dist/types/protocol/contracts.d.ts.map +1 -0
  102. package/dist/types/protocol/crc.d.ts +5 -0
  103. package/dist/types/protocol/crc.d.ts.map +1 -0
  104. package/dist/types/protocol/cvar.d.ts +15 -0
  105. package/dist/types/protocol/cvar.d.ts.map +1 -0
  106. package/dist/types/protocol/effects.d.ts +33 -0
  107. package/dist/types/protocol/effects.d.ts.map +1 -0
  108. package/dist/types/protocol/entity.d.ts +46 -0
  109. package/dist/types/protocol/entity.d.ts.map +1 -0
  110. package/dist/types/protocol/entityEvent.d.ts +13 -0
  111. package/dist/types/protocol/entityEvent.d.ts.map +1 -0
  112. package/dist/types/protocol/entityState.d.ts +26 -0
  113. package/dist/types/protocol/entityState.d.ts.map +1 -0
  114. package/dist/types/protocol/index.d.ts +19 -0
  115. package/dist/types/protocol/index.d.ts.map +1 -0
  116. package/dist/types/protocol/layout.d.ts +9 -0
  117. package/dist/types/protocol/layout.d.ts.map +1 -0
  118. package/dist/types/protocol/ops.d.ts +44 -0
  119. package/dist/types/protocol/ops.d.ts.map +1 -0
  120. package/dist/types/protocol/player-state.d.ts +40 -0
  121. package/dist/types/protocol/player-state.d.ts.map +1 -0
  122. package/dist/types/protocol/player.d.ts +28 -0
  123. package/dist/types/protocol/player.d.ts.map +1 -0
  124. package/dist/types/protocol/renderFx.d.ts +23 -0
  125. package/dist/types/protocol/renderFx.d.ts.map +1 -0
  126. package/dist/types/protocol/stats.d.ts +61 -0
  127. package/dist/types/protocol/stats.d.ts.map +1 -0
  128. package/dist/types/protocol/tempEntity.d.ts +67 -0
  129. package/dist/types/protocol/tempEntity.d.ts.map +1 -0
  130. package/dist/types/protocol/usercmd.d.ts +33 -0
  131. package/dist/types/protocol/usercmd.d.ts.map +1 -0
  132. package/dist/types/protocol/writeUserCmd.d.ts +4 -0
  133. package/dist/types/protocol/writeUserCmd.d.ts.map +1 -0
  134. package/dist/types/replay/index.d.ts +3 -0
  135. package/dist/types/replay/index.d.ts.map +1 -0
  136. package/dist/types/replay/io.d.ts +7 -0
  137. package/dist/types/replay/io.d.ts.map +1 -0
  138. package/dist/types/replay/schema.d.ts +41 -0
  139. package/dist/types/replay/schema.d.ts.map +1 -0
  140. package/dist/types/testing.d.ts +6 -0
  141. package/dist/types/testing.d.ts.map +1 -0
  142. package/package.json +43 -0
  143. package/src/audio/constants.ts +35 -0
  144. package/src/bsp/collision.ts +1075 -0
  145. package/src/bsp/contents.ts +108 -0
  146. package/src/bsp/spatial.ts +116 -0
  147. package/src/index.ts +37 -0
  148. package/src/inventory-helpers.ts +81 -0
  149. package/src/io/binaryStream.ts +159 -0
  150. package/src/io/binaryWriter.ts +146 -0
  151. package/src/io/index.ts +3 -0
  152. package/src/io/messageBuilder.ts +117 -0
  153. package/src/items/ammo.ts +47 -0
  154. package/src/items/index.ts +8 -0
  155. package/src/items/powerups.ts +32 -0
  156. package/src/items/weaponInfo.ts +45 -0
  157. package/src/items/weapons.ts +28 -0
  158. package/src/math/angles.ts +135 -0
  159. package/src/math/anorms.ts +165 -0
  160. package/src/math/color.ts +42 -0
  161. package/src/math/mat4.ts +58 -0
  162. package/src/math/random.ts +182 -0
  163. package/src/math/vec3.ts +379 -0
  164. package/src/net/driver.ts +9 -0
  165. package/src/net/index.ts +2 -0
  166. package/src/net/netchan.ts +451 -0
  167. package/src/pmove/apply.ts +151 -0
  168. package/src/pmove/categorize.ts +162 -0
  169. package/src/pmove/config.ts +5 -0
  170. package/src/pmove/constants.ts +94 -0
  171. package/src/pmove/currents.ts +287 -0
  172. package/src/pmove/dimensions.ts +40 -0
  173. package/src/pmove/duck.ts +154 -0
  174. package/src/pmove/fly.ts +197 -0
  175. package/src/pmove/index.ts +18 -0
  176. package/src/pmove/jump.ts +92 -0
  177. package/src/pmove/move.ts +527 -0
  178. package/src/pmove/pmove.ts +446 -0
  179. package/src/pmove/slide.ts +267 -0
  180. package/src/pmove/snap.ts +89 -0
  181. package/src/pmove/special.ts +207 -0
  182. package/src/pmove/stuck.ts +258 -0
  183. package/src/pmove/types.ts +82 -0
  184. package/src/pmove/view.ts +57 -0
  185. package/src/pmove/water.ts +56 -0
  186. package/src/protocol/bitpack.ts +139 -0
  187. package/src/protocol/configstrings.ts +104 -0
  188. package/src/protocol/constants.ts +40 -0
  189. package/src/protocol/contracts.ts +149 -0
  190. package/src/protocol/crc.ts +32 -0
  191. package/src/protocol/cvar.ts +15 -0
  192. package/src/protocol/effects.ts +33 -0
  193. package/src/protocol/entity.ts +304 -0
  194. package/src/protocol/entityEvent.ts +14 -0
  195. package/src/protocol/entityState.ts +28 -0
  196. package/src/protocol/index.ts +19 -0
  197. package/src/protocol/layout.ts +9 -0
  198. package/src/protocol/ops.ts +49 -0
  199. package/src/protocol/player-state.ts +51 -0
  200. package/src/protocol/player.ts +165 -0
  201. package/src/protocol/renderFx.ts +22 -0
  202. package/src/protocol/stats.ts +161 -0
  203. package/src/protocol/tempEntity.ts +69 -0
  204. package/src/protocol/usercmd.ts +63 -0
  205. package/src/protocol/writeUserCmd.ts +30 -0
  206. package/src/replay/index.ts +2 -0
  207. package/src/replay/io.ts +37 -0
  208. package/src/replay/schema.ts +42 -0
  209. package/src/testing.ts +200 -0
@@ -0,0 +1,154 @@
1
+ import { MASK_SOLID, MASK_WATER, type ContentsFlag } from '../bsp/contents.js';
2
+ import type { Vec3 } from '../math/vec3.js';
3
+ import {
4
+ PlayerButton,
5
+ PmFlag,
6
+ type PmFlags,
7
+ PmType,
8
+ WaterLevel,
9
+ addPmFlag,
10
+ hasPmFlag,
11
+ removePmFlag,
12
+ } from './constants.js';
13
+ import type { PmoveTraceResult } from './types.js';
14
+ import { computePlayerDimensions, type PlayerDimensions } from './dimensions.js';
15
+
16
+ const CROUCH_MAX_Z = 4;
17
+ const STAND_MAX_Z = 32;
18
+ const ABOVE_WATER_OFFSET = 8;
19
+
20
+ export interface DuckTraceParams {
21
+ readonly start: Vec3;
22
+ readonly end: Vec3;
23
+ readonly mins: Vec3;
24
+ readonly maxs: Vec3;
25
+ readonly mask: ContentsFlag;
26
+ }
27
+
28
+ export type DuckTraceFn = (params: DuckTraceParams) => PmoveTraceResult;
29
+
30
+ export interface CheckDuckParams {
31
+ readonly pmType: PmType;
32
+ readonly pmFlags: PmFlags;
33
+ readonly buttons: number;
34
+ readonly waterlevel: WaterLevel;
35
+ readonly hasGroundEntity: boolean;
36
+ readonly onLadder: boolean;
37
+ readonly n64Physics: boolean;
38
+ readonly origin: Vec3;
39
+ readonly mins: Vec3;
40
+ readonly maxs: Vec3;
41
+ readonly trace: DuckTraceFn;
42
+ }
43
+
44
+ export interface CheckDuckResult extends PlayerDimensions {
45
+ readonly pmFlags: PmFlags;
46
+ readonly ducked: boolean;
47
+ readonly changed: boolean;
48
+ }
49
+
50
+ /**
51
+ * Pure port of PM_CheckDuck from rerelease `p_move.cpp`. Updates the PMF_DUCKED flag
52
+ * based on crouch input, obstruction traces, and special cases (dead bodies) without
53
+ * mutating the provided mins/maxs. Returns the updated flag word plus the dimensions
54
+ * computed from PM_SetDimensions so callers can update collision bounds atomically.
55
+ */
56
+ export function checkDuckState(params: CheckDuckParams): CheckDuckResult {
57
+ const { pmType } = params;
58
+
59
+ if (pmType === PmType.Gib) {
60
+ const dims = computePlayerDimensions(pmType, params.pmFlags);
61
+ return { pmFlags: params.pmFlags, ducked: hasPmFlag(params.pmFlags, PmFlag.Ducked), changed: false, ...dims };
62
+ }
63
+
64
+ let flags = params.pmFlags;
65
+ let changed = false;
66
+
67
+ if (pmType === PmType.Dead) {
68
+ if (!hasPmFlag(flags, PmFlag.Ducked)) {
69
+ flags = addPmFlag(flags, PmFlag.Ducked);
70
+ changed = true;
71
+ }
72
+ } else if (shouldDuck(params)) {
73
+ if (!hasPmFlag(flags, PmFlag.Ducked) && !isDuckBlocked(params)) {
74
+ flags = addPmFlag(flags, PmFlag.Ducked);
75
+ changed = true;
76
+ }
77
+ } else if (hasPmFlag(flags, PmFlag.Ducked) && !isStandBlocked(params)) {
78
+ flags = removePmFlag(flags, PmFlag.Ducked);
79
+ changed = true;
80
+ }
81
+
82
+ const dims = computePlayerDimensions(pmType, flags);
83
+ const ducked = pmType === PmType.Dead || hasPmFlag(flags, PmFlag.Ducked);
84
+
85
+ return { pmFlags: flags, ducked, changed, ...dims };
86
+ }
87
+
88
+ function shouldDuck(params: CheckDuckParams): boolean {
89
+ if ((params.buttons & PlayerButton.Crouch) === 0) {
90
+ return false;
91
+ }
92
+ if (params.onLadder || params.n64Physics) {
93
+ return false;
94
+ }
95
+ if (params.hasGroundEntity) {
96
+ return true;
97
+ }
98
+ if (params.waterlevel <= WaterLevel.Feet && !isAboveWater(params)) {
99
+ return true;
100
+ }
101
+ return false;
102
+ }
103
+
104
+ function isDuckBlocked(params: CheckDuckParams): boolean {
105
+ const trace = params.trace({
106
+ start: params.origin,
107
+ end: params.origin,
108
+ mins: params.mins,
109
+ maxs: withZ(params.maxs, CROUCH_MAX_Z),
110
+ mask: MASK_SOLID,
111
+ });
112
+ return trace.allsolid;
113
+ }
114
+
115
+ function isStandBlocked(params: CheckDuckParams): boolean {
116
+ const trace = params.trace({
117
+ start: params.origin,
118
+ end: params.origin,
119
+ mins: params.mins,
120
+ maxs: withZ(params.maxs, STAND_MAX_Z),
121
+ mask: MASK_SOLID,
122
+ });
123
+ return trace.allsolid;
124
+ }
125
+
126
+ function isAboveWater(params: CheckDuckParams): boolean {
127
+ const below: Vec3 = { x: params.origin.x, y: params.origin.y, z: params.origin.z - ABOVE_WATER_OFFSET };
128
+
129
+ const solidTrace = params.trace({
130
+ start: params.origin,
131
+ end: below,
132
+ mins: params.mins,
133
+ maxs: params.maxs,
134
+ mask: MASK_SOLID,
135
+ });
136
+
137
+ if (solidTrace.fraction < 1) {
138
+ return false;
139
+ }
140
+
141
+ const waterTrace = params.trace({
142
+ start: params.origin,
143
+ end: below,
144
+ mins: params.mins,
145
+ maxs: params.maxs,
146
+ mask: MASK_WATER,
147
+ });
148
+
149
+ return waterTrace.fraction < 1;
150
+ }
151
+
152
+ function withZ(vec: Vec3, z: number): Vec3 {
153
+ return { x: vec.x, y: vec.y, z };
154
+ }
@@ -0,0 +1,197 @@
1
+ import { addVec3, lengthVec3, normalizeVec3, scaleVec3, type Vec3 } from '../math/vec3.js';
2
+ import { PlayerButton } from './constants.js';
3
+ import { applyPmoveAccelerate } from './pmove.js';
4
+ import { stepSlideMove, type StepSlideMoveOutcome } from './slide.js';
5
+ import type { PmoveCmd, PmoveTraceFn } from './types.js';
6
+
7
+ const FLY_FRICTION_MULTIPLIER = 1.5;
8
+ const BUTTON_VERTICAL_SCALE = 0.5;
9
+ const DEFAULT_OVERBOUNCE = 1.01;
10
+
11
+ export interface FlyMoveParams {
12
+ readonly origin: Vec3;
13
+ readonly velocity: Vec3;
14
+ readonly cmd: PmoveCmd;
15
+ readonly forward: Vec3;
16
+ readonly right: Vec3;
17
+ readonly frametime: number;
18
+ readonly pmFriction: number;
19
+ readonly pmStopSpeed: number;
20
+ readonly pmMaxSpeed: number;
21
+ readonly pmAccelerate: number;
22
+ readonly pmWaterSpeed: number;
23
+ readonly doclip: boolean;
24
+ readonly mins?: Vec3;
25
+ readonly maxs?: Vec3;
26
+ readonly trace?: PmoveTraceFn;
27
+ readonly overbounce?: number;
28
+ readonly stepSize?: number;
29
+ readonly maxBumps?: number;
30
+ readonly maxClipPlanes?: number;
31
+ }
32
+
33
+ export type FlyMoveResult = StepSlideMoveOutcome;
34
+
35
+ /**
36
+ * Pure translation of PM_FlyMove from rerelease `p_move.cpp`: applies the
37
+ * spectator/noclip friction and acceleration rules, then either advances the
38
+ * origin freely or resolves movement through `stepSlideMove` when clipping is
39
+ * requested. This keeps the spectator and noclip physics deterministic between
40
+ * the client and server.
41
+ */
42
+ export function applyPmoveFlyMove(params: FlyMoveParams): FlyMoveResult {
43
+ const {
44
+ origin,
45
+ cmd,
46
+ frametime,
47
+ pmFriction,
48
+ pmStopSpeed,
49
+ pmMaxSpeed,
50
+ pmAccelerate,
51
+ pmWaterSpeed,
52
+ doclip,
53
+ forward,
54
+ right,
55
+ mins,
56
+ maxs,
57
+ trace,
58
+ overbounce = DEFAULT_OVERBOUNCE,
59
+ stepSize,
60
+ maxBumps,
61
+ maxClipPlanes,
62
+ } = params;
63
+
64
+ let velocity = applyFlyFriction({ velocity: params.velocity, pmFriction, pmStopSpeed, frametime });
65
+
66
+ const wishdirVelocity = buildFlyWishVelocity({
67
+ cmd,
68
+ forward,
69
+ right,
70
+ pmMaxSpeed,
71
+ pmWaterSpeed,
72
+ });
73
+
74
+ if (wishdirVelocity.wishspeed > 0) {
75
+ velocity = applyPmoveAccelerate({
76
+ velocity,
77
+ wishdir: wishdirVelocity.wishdir,
78
+ wishspeed: wishdirVelocity.accelSpeed,
79
+ accel: pmAccelerate,
80
+ frametime,
81
+ });
82
+ }
83
+
84
+ if (!doclip) {
85
+ const originDelta = scaleVec3(velocity, frametime);
86
+ const nextOrigin = addVec3(origin, originDelta);
87
+ return {
88
+ origin: nextOrigin,
89
+ velocity,
90
+ planes: [],
91
+ blocked: 0,
92
+ stopped: velocity.x === 0 && velocity.y === 0 && velocity.z === 0,
93
+ stepped: false,
94
+ stepHeight: 0,
95
+ };
96
+ }
97
+
98
+ if (!trace || !mins || !maxs) {
99
+ throw new Error('applyPmoveFlyMove: doclip=true requires trace/mins/maxs');
100
+ }
101
+
102
+ return stepSlideMove({
103
+ origin,
104
+ velocity,
105
+ frametime,
106
+ overbounce,
107
+ trace,
108
+ mins,
109
+ maxs,
110
+ stepSize,
111
+ maxBumps,
112
+ maxClipPlanes,
113
+ });
114
+ }
115
+
116
+ interface FlyFrictionParams {
117
+ readonly velocity: Vec3;
118
+ readonly pmFriction: number;
119
+ readonly pmStopSpeed: number;
120
+ readonly frametime: number;
121
+ }
122
+
123
+ function applyFlyFriction(params: FlyFrictionParams): Vec3 {
124
+ const { velocity, pmFriction, pmStopSpeed, frametime } = params;
125
+ const speed = lengthVec3(velocity);
126
+
127
+ if (speed < 1) {
128
+ return { x: 0, y: 0, z: 0 };
129
+ }
130
+
131
+ const friction = pmFriction * FLY_FRICTION_MULTIPLIER;
132
+ const control = speed < pmStopSpeed ? pmStopSpeed : speed;
133
+ const drop = control * friction * frametime;
134
+
135
+ let newspeed = speed - drop;
136
+ if (newspeed < 0) {
137
+ newspeed = 0;
138
+ }
139
+
140
+ if (newspeed === speed) {
141
+ return velocity;
142
+ }
143
+
144
+ return scaleVec3(velocity, newspeed / speed);
145
+ }
146
+
147
+ interface FlyWishVelocityParams {
148
+ readonly cmd: PmoveCmd;
149
+ readonly forward: Vec3;
150
+ readonly right: Vec3;
151
+ readonly pmMaxSpeed: number;
152
+ readonly pmWaterSpeed: number;
153
+ }
154
+
155
+ interface FlyWishVelocityResult {
156
+ readonly wishdir: Vec3;
157
+ readonly wishspeed: number;
158
+ readonly accelSpeed: number;
159
+ }
160
+
161
+ function buildFlyWishVelocity(params: FlyWishVelocityParams): FlyWishVelocityResult {
162
+ const { cmd, forward, right, pmMaxSpeed, pmWaterSpeed } = params;
163
+
164
+ const forwardNorm = normalizeVec3(forward);
165
+ const rightNorm = normalizeVec3(right);
166
+
167
+ const wishvel = {
168
+ x: forwardNorm.x * cmd.forwardmove + rightNorm.x * cmd.sidemove,
169
+ y: forwardNorm.y * cmd.forwardmove + rightNorm.y * cmd.sidemove,
170
+ z: forwardNorm.z * cmd.forwardmove + rightNorm.z * cmd.sidemove,
171
+ } satisfies Vec3;
172
+
173
+ let adjusted = wishvel;
174
+ const buttons = cmd.buttons ?? 0;
175
+
176
+ if (buttons & PlayerButton.Jump) {
177
+ adjusted = addVec3(adjusted, { x: 0, y: 0, z: pmWaterSpeed * BUTTON_VERTICAL_SCALE });
178
+ }
179
+
180
+ if (buttons & PlayerButton.Crouch) {
181
+ adjusted = addVec3(adjusted, { x: 0, y: 0, z: -pmWaterSpeed * BUTTON_VERTICAL_SCALE });
182
+ }
183
+
184
+ let wishspeed = lengthVec3(adjusted);
185
+ let wishdir = wishspeed === 0 ? { x: 0, y: 0, z: 0 } : normalizeVec3(adjusted);
186
+
187
+ if (wishspeed > pmMaxSpeed) {
188
+ const scale = pmMaxSpeed / wishspeed;
189
+ adjusted = scaleVec3(adjusted, scale);
190
+ wishspeed = pmMaxSpeed;
191
+ wishdir = wishspeed === 0 ? { x: 0, y: 0, z: 0 } : normalizeVec3(adjusted);
192
+ }
193
+
194
+ const accelSpeed = wishspeed * 2;
195
+
196
+ return { wishdir, wishspeed, accelSpeed };
197
+ }
@@ -0,0 +1,18 @@
1
+
2
+ export * from './apply.js';
3
+ export * from './categorize.js';
4
+ export * from './constants.js';
5
+ export * from './currents.js';
6
+ export * from './dimensions.js';
7
+ export * from './duck.js';
8
+ export * from './fly.js';
9
+ export * from './jump.js';
10
+ export * from './move.js';
11
+ export * from './pmove.js';
12
+ export * from './slide.js';
13
+ export * from './snap.js';
14
+ export * from './special.js';
15
+ export * from './stuck.js';
16
+ export * from './types.js';
17
+ export * from './view.js';
18
+ export * from './water.js';
@@ -0,0 +1,92 @@
1
+ import type { Vec3 } from '../math/vec3.js';
2
+ import { WaterLevel, type PmFlags, PmFlag, PmType, PlayerButton, addPmFlag, removePmFlag } from './constants.js';
3
+
4
+ const DEFAULT_JUMP_HEIGHT = 270;
5
+
6
+ export interface CheckJumpParams {
7
+ readonly pmFlags: PmFlags;
8
+ readonly pmType: PmType;
9
+ readonly buttons: number;
10
+ readonly waterlevel: WaterLevel;
11
+ readonly onGround: boolean;
12
+ readonly velocity: Vec3;
13
+ readonly origin: Vec3;
14
+ readonly jumpHeight?: number;
15
+ }
16
+
17
+ export interface CheckJumpResult {
18
+ readonly pmFlags: PmFlags;
19
+ readonly onGround: boolean;
20
+ readonly velocity: Vec3;
21
+ readonly origin: Vec3;
22
+ readonly jumpSound: boolean;
23
+ readonly jumped: boolean;
24
+ }
25
+
26
+ function hasButton(buttons: number, button: PlayerButton): boolean {
27
+ return (buttons & button) !== 0;
28
+ }
29
+
30
+ /**
31
+ * Pure translation of the rerelease `PM_CheckJump` helper from `p_move.cpp`.
32
+ * The function takes in the minimal pmove state that the original C++ logic
33
+ * touches and returns the updated flag/origin/velocity tuple so callers can
34
+ * apply the same semantics on both the server and client.
35
+ */
36
+ export function checkJump(params: CheckJumpParams): CheckJumpResult {
37
+ const { pmFlags, pmType, buttons, waterlevel, onGround, velocity, origin, jumpHeight = DEFAULT_JUMP_HEIGHT } = params;
38
+
39
+ // PM_CheckJump immediately bails while the landing timer is active.
40
+ if (pmFlags & PmFlag.TimeLand) {
41
+ return { pmFlags, onGround, velocity, origin, jumpSound: false, jumped: false };
42
+ }
43
+
44
+ const holdingJump = hasButton(buttons, PlayerButton.Jump);
45
+ let nextFlags = pmFlags;
46
+ let nextOnGround = onGround;
47
+ let jumpSound = false;
48
+ let jumped = false;
49
+ let nextVelocity = velocity;
50
+ let nextOrigin = origin;
51
+
52
+ if (!holdingJump) {
53
+ nextFlags = removePmFlag(nextFlags, PmFlag.JumpHeld);
54
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
55
+ }
56
+
57
+ if (hasPmJumpHold(nextFlags)) {
58
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
59
+ }
60
+
61
+ if (pmType === PmType.Dead) {
62
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
63
+ }
64
+
65
+ if (waterlevel >= WaterLevel.Waist) {
66
+ nextOnGround = false;
67
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
68
+ }
69
+
70
+ if (!nextOnGround) {
71
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
72
+ }
73
+
74
+ nextFlags = addPmFlag(nextFlags, PmFlag.JumpHeld);
75
+ nextFlags = removePmFlag(nextFlags, PmFlag.OnGround);
76
+ nextOnGround = false;
77
+ jumpSound = true;
78
+ jumped = true;
79
+
80
+ const z = velocity.z + jumpHeight;
81
+ const finalZ = z < jumpHeight ? jumpHeight : z;
82
+ nextVelocity = { ...velocity, z: finalZ };
83
+
84
+ // Unstuck from ground: pm->s.origin[2] += 1;
85
+ nextOrigin = { ...origin, z: origin.z + 1 };
86
+
87
+ return { pmFlags: nextFlags, onGround: nextOnGround, velocity: nextVelocity, origin: nextOrigin, jumpSound, jumped };
88
+ }
89
+
90
+ function hasPmJumpHold(flags: PmFlags): boolean {
91
+ return (flags & PmFlag.JumpHeld) !== 0;
92
+ }