@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,108 @@
1
+ /**
2
+ * Bitflag constants mirroring the Quake II rerelease `contents_t` and
3
+ * `surfflags_t` enumerations from `game.h`. The helpers here operate purely on
4
+ * numeric bitmasks so both the authoritative game simulation and the client can
5
+ * share the same semantic checks.
6
+ */
7
+ export type ContentsFlag = number;
8
+ export type SurfaceFlag = number;
9
+
10
+ export const CONTENTS_NONE: ContentsFlag = 0;
11
+ export const CONTENTS_SOLID: ContentsFlag = 1 << 0;
12
+ export const CONTENTS_WINDOW: ContentsFlag = 1 << 1;
13
+ export const CONTENTS_AUX: ContentsFlag = 1 << 2;
14
+ export const CONTENTS_LAVA: ContentsFlag = 1 << 3;
15
+ export const CONTENTS_SLIME: ContentsFlag = 1 << 4;
16
+ export const CONTENTS_WATER: ContentsFlag = 1 << 5;
17
+ export const CONTENTS_MIST: ContentsFlag = 1 << 6;
18
+ export const CONTENTS_TRIGGER: ContentsFlag = 0x40000000;
19
+ export const CONTENTS_NO_WATERJUMP: ContentsFlag = 1 << 13;
20
+ export const CONTENTS_PROJECTILECLIP: ContentsFlag = 1 << 14;
21
+ export const CONTENTS_AREAPORTAL: ContentsFlag = 1 << 15;
22
+ export const CONTENTS_PLAYERCLIP: ContentsFlag = 1 << 16;
23
+ export const CONTENTS_MONSTERCLIP: ContentsFlag = 1 << 17;
24
+ export const CONTENTS_CURRENT_0: ContentsFlag = 1 << 18;
25
+ export const CONTENTS_CURRENT_90: ContentsFlag = 1 << 19;
26
+ export const CONTENTS_CURRENT_180: ContentsFlag = 1 << 20;
27
+ export const CONTENTS_CURRENT_270: ContentsFlag = 1 << 21;
28
+ export const CONTENTS_CURRENT_UP: ContentsFlag = 1 << 22;
29
+ export const CONTENTS_CURRENT_DOWN: ContentsFlag = 1 << 23;
30
+ export const CONTENTS_ORIGIN: ContentsFlag = 1 << 24;
31
+ export const CONTENTS_MONSTER: ContentsFlag = 1 << 25;
32
+ export const CONTENTS_DEADMONSTER: ContentsFlag = 1 << 26;
33
+ export const CONTENTS_DETAIL: ContentsFlag = 1 << 27;
34
+ export const CONTENTS_TRANSLUCENT: ContentsFlag = 1 << 28;
35
+ export const CONTENTS_LADDER: ContentsFlag = 1 << 29;
36
+ export const CONTENTS_PLAYER: ContentsFlag = 1 << 30;
37
+ export const CONTENTS_PROJECTILE: ContentsFlag = 1 << 31;
38
+
39
+ export const LAST_VISIBLE_CONTENTS: ContentsFlag = CONTENTS_MIST;
40
+
41
+ export const SURF_NONE: SurfaceFlag = 0;
42
+ export const SURF_LIGHT: SurfaceFlag = 1 << 0;
43
+ export const SURF_SLICK: SurfaceFlag = 1 << 1;
44
+ export const SURF_SKY: SurfaceFlag = 1 << 2;
45
+ export const SURF_WARP: SurfaceFlag = 1 << 3;
46
+ export const SURF_TRANS33: SurfaceFlag = 1 << 4;
47
+ export const SURF_TRANS66: SurfaceFlag = 1 << 5;
48
+ export const SURF_FLOWING: SurfaceFlag = 1 << 6;
49
+ export const SURF_NODRAW: SurfaceFlag = 1 << 7;
50
+ export const SURF_ALPHATEST: SurfaceFlag = 1 << 25;
51
+ export const SURF_N64_UV: SurfaceFlag = 1 << 28;
52
+ export const SURF_N64_SCROLL_X: SurfaceFlag = 1 << 29;
53
+ export const SURF_N64_SCROLL_Y: SurfaceFlag = 1 << 30;
54
+ export const SURF_N64_SCROLL_FLIP: SurfaceFlag = 1 << 31;
55
+
56
+ export const MASK_ALL: ContentsFlag = 0xffffffff;
57
+ export const MASK_SOLID: ContentsFlag = CONTENTS_SOLID | CONTENTS_WINDOW;
58
+ export const MASK_PLAYERSOLID: ContentsFlag =
59
+ CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_PLAYER;
60
+ export const MASK_DEADSOLID: ContentsFlag = CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW;
61
+ export const MASK_MONSTERSOLID: ContentsFlag =
62
+ CONTENTS_SOLID | CONTENTS_MONSTERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_PLAYER;
63
+ export const MASK_WATER: ContentsFlag = CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME;
64
+ export const MASK_OPAQUE: ContentsFlag = CONTENTS_SOLID | CONTENTS_SLIME | CONTENTS_LAVA;
65
+ export const MASK_SHOT: ContentsFlag =
66
+ CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_PLAYER | CONTENTS_WINDOW | CONTENTS_DEADMONSTER;
67
+ export const MASK_CURRENT: ContentsFlag =
68
+ CONTENTS_CURRENT_0 |
69
+ CONTENTS_CURRENT_90 |
70
+ CONTENTS_CURRENT_180 |
71
+ CONTENTS_CURRENT_270 |
72
+ CONTENTS_CURRENT_UP |
73
+ CONTENTS_CURRENT_DOWN;
74
+ export const MASK_BLOCK_SIGHT: ContentsFlag =
75
+ CONTENTS_SOLID | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_MONSTER | CONTENTS_PLAYER;
76
+ export const MASK_NAV_SOLID: ContentsFlag = CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW;
77
+ export const MASK_LADDER_NAV_SOLID: ContentsFlag = CONTENTS_SOLID | CONTENTS_WINDOW;
78
+ export const MASK_WALK_NAV_SOLID: ContentsFlag =
79
+ CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTERCLIP;
80
+ export const MASK_PROJECTILE: ContentsFlag = MASK_SHOT | CONTENTS_PROJECTILECLIP;
81
+
82
+ export function hasAllContents(mask: ContentsFlag, flags: ContentsFlag): boolean {
83
+ return (mask & flags) === flags;
84
+ }
85
+
86
+ export function hasAnyContents(mask: ContentsFlag, flags: ContentsFlag): boolean {
87
+ return (mask & flags) !== 0;
88
+ }
89
+
90
+ export function addContents(mask: ContentsFlag, flags: ContentsFlag): ContentsFlag {
91
+ return mask | flags;
92
+ }
93
+
94
+ export function removeContents(mask: ContentsFlag, flags: ContentsFlag): ContentsFlag {
95
+ return mask & ~flags;
96
+ }
97
+
98
+ export function hasSurfaceFlags(surface: SurfaceFlag, flags: SurfaceFlag): boolean {
99
+ return (surface & flags) === flags;
100
+ }
101
+
102
+ export function combineSurfaceFlags(...flags: SurfaceFlag[]): SurfaceFlag {
103
+ let mask = SURF_NONE;
104
+ for (const flag of flags) {
105
+ mask |= flag;
106
+ }
107
+ return mask;
108
+ }
@@ -0,0 +1,116 @@
1
+ import type { Vec3 } from '../math/vec3.js';
2
+
3
+ export const AREA_DEPTH = 4;
4
+ export const WORLD_SIZE = 8192; // Standard Q2 world extent
5
+
6
+ export interface SpatialNode {
7
+ axis: number; // 0=X, 1=Y, -1=Leaf
8
+ dist: number;
9
+ children: [SpatialNode, SpatialNode] | null;
10
+ items: Set<number>;
11
+ }
12
+
13
+ export function createSpatialTree(
14
+ depth = 0,
15
+ mins: Vec3 = { x: -WORLD_SIZE, y: -WORLD_SIZE, z: -WORLD_SIZE },
16
+ maxs: Vec3 = { x: WORLD_SIZE, y: WORLD_SIZE, z: WORLD_SIZE }
17
+ ): SpatialNode {
18
+ if (depth >= AREA_DEPTH) {
19
+ return {
20
+ axis: -1,
21
+ dist: 0,
22
+ children: null,
23
+ items: new Set(),
24
+ };
25
+ }
26
+
27
+ const axis = depth % 2; // Alternates X (0) and Y (1)
28
+ const dist = 0.5 * (axis === 0 ? mins.x + maxs.x : mins.y + maxs.y);
29
+
30
+ const mins1 = { ...mins };
31
+ const maxs1 = { ...maxs };
32
+ const mins2 = { ...mins };
33
+ const maxs2 = { ...maxs };
34
+
35
+ if (axis === 0) {
36
+ maxs1.x = dist;
37
+ mins2.x = dist;
38
+ } else {
39
+ maxs1.y = dist;
40
+ mins2.y = dist;
41
+ }
42
+
43
+ const child1 = createSpatialTree(depth + 1, mins1, maxs1);
44
+ const child2 = createSpatialTree(depth + 1, mins2, maxs2);
45
+
46
+ return {
47
+ axis,
48
+ dist,
49
+ children: [child1, child2],
50
+ items: new Set(),
51
+ };
52
+ }
53
+
54
+ export function linkEntityToSpatialTree(
55
+ node: SpatialNode,
56
+ id: number,
57
+ absmin: Vec3,
58
+ absmax: Vec3
59
+ ): SpatialNode {
60
+ let current = node;
61
+
62
+ while (current.axis !== -1 && current.children) {
63
+ const axis = current.axis;
64
+ const dist = current.dist;
65
+
66
+ const min = axis === 0 ? absmin.x : absmin.y;
67
+ const max = axis === 0 ? absmax.x : absmax.y;
68
+
69
+ if (min > dist) {
70
+ current = current.children[1];
71
+ } else if (max < dist) {
72
+ current = current.children[0];
73
+ } else {
74
+ break; // Straddles the plane, resides in this node
75
+ }
76
+ }
77
+
78
+ current.items.add(id);
79
+ return current;
80
+ }
81
+
82
+ export function querySpatialTree(
83
+ node: SpatialNode,
84
+ absmin: Vec3,
85
+ absmax: Vec3,
86
+ results: Set<number>
87
+ ): void {
88
+ // Add all items in the current node (because if we are here, we overlap this node's space
89
+ // and straddling items definitely overlap us or are at least in the parent region)
90
+ // Actually, strictly speaking, items in this node straddle the split plane.
91
+ // Since we are traversing down, we are within the node's volume.
92
+ // The items in this node are those that couldn't be pushed further down.
93
+ // So we must check them.
94
+
95
+ // NOTE: This collects candidates. Precise collision check still needed.
96
+ for (const id of node.items) {
97
+ results.add(id);
98
+ }
99
+
100
+ if (node.axis === -1 || !node.children) {
101
+ return;
102
+ }
103
+
104
+ const axis = node.axis;
105
+ const dist = node.dist;
106
+
107
+ const min = axis === 0 ? absmin.x : absmin.y;
108
+ const max = axis === 0 ? absmax.x : absmax.y;
109
+
110
+ if (max > dist) {
111
+ querySpatialTree(node.children[1], absmin, absmax, results);
112
+ }
113
+ if (min < dist) {
114
+ querySpatialTree(node.children[0], absmin, absmax, results);
115
+ }
116
+ }
package/src/index.ts ADDED
@@ -0,0 +1,37 @@
1
+ export * from './math/vec3.js';
2
+ export * from './math/angles.js';
3
+ export * from './math/anorms.js';
4
+ export * from './math/color.js';
5
+ export * from './math/random.js';
6
+ export * from './math/mat4.js';
7
+ export * from './bsp/contents.js';
8
+ export * from './bsp/collision.js';
9
+ export * from './protocol/cvar.js';
10
+ export * from './protocol/configstrings.js';
11
+ export * as Replay from './replay/index.js';
12
+ export * from './protocol/contracts.js';
13
+ export * from './pmove/types.js';
14
+ export * from './pmove/constants.js';
15
+ export * from './pmove/pmove.js';
16
+ export * from './pmove/slide.js';
17
+ export * from './pmove/stuck.js';
18
+ export * from './pmove/currents.js';
19
+ export * from './pmove/fly.js';
20
+ export * from './pmove/water.js';
21
+ export * from './pmove/jump.js';
22
+ export * from './pmove/dimensions.js';
23
+ export * from './pmove/duck.js';
24
+ export * from './pmove/categorize.js';
25
+ export * from './pmove/move.js';
26
+ export * from './pmove/special.js';
27
+ export * from './pmove/snap.js';
28
+ export * from './pmove/view.js';
29
+ export * from './protocol/index.js';
30
+ export * from './protocol/entityState.js';
31
+ export * from './pmove/apply.js';
32
+ export * from './io/index.js';
33
+ export * from './net/index.js';
34
+ export * from './items/index.js';
35
+ export * from './audio/constants.js';
36
+ export * from './inventory-helpers.js';
37
+ export * from './testing.js';
@@ -0,0 +1,81 @@
1
+ import { PlayerState } from './protocol/index.js';
2
+ import { AmmoItemId, AmmoType, WeaponId } from './items/index.js';
3
+ import { G_GetAmmoStat } from './protocol/stats.js';
4
+ import { WEAPON_AMMO_MAP } from './items/index.js';
5
+ import { ConfigStringIndex } from './protocol/configstrings.js';
6
+
7
+ // Blaster uses no ammo in standard Q2.
8
+ // We handle mapping `AmmoItemId` (string) to `AmmoType` (enum).
9
+ export const AMMO_ITEM_MAP: Record<AmmoItemId, AmmoType> = {
10
+ [AmmoItemId.Shells]: AmmoType.Shells,
11
+ [AmmoItemId.Bullets]: AmmoType.Bullets,
12
+ [AmmoItemId.Rockets]: AmmoType.Rockets,
13
+ [AmmoItemId.Grenades]: AmmoType.Grenades,
14
+ [AmmoItemId.Cells]: AmmoType.Cells,
15
+ [AmmoItemId.Slugs]: AmmoType.Slugs,
16
+ [AmmoItemId.MagSlugs]: AmmoType.MagSlugs,
17
+ [AmmoItemId.Flechettes]: AmmoType.Flechettes,
18
+ [AmmoItemId.Disruptor]: AmmoType.Disruptor,
19
+ [AmmoItemId.Tesla]: AmmoType.Tesla,
20
+ [AmmoItemId.Trap]: AmmoType.Trap,
21
+ [AmmoItemId.Prox]: AmmoType.Prox,
22
+ };
23
+
24
+ /**
25
+ * Retrieves the ammo count for a given item (Weapon or Ammo).
26
+ * @param playerState The current player state.
27
+ * @param item The item identifier (WeaponId or AmmoItemId).
28
+ * @returns The ammo count, or 0 if not found/applicable. Returns -1 for infinite ammo (e.g. Blaster).
29
+ */
30
+ export function getAmmoCount(playerState: PlayerState, item: WeaponId | AmmoItemId): number {
31
+ let ammoType: AmmoType | null | undefined;
32
+
33
+ // Check if it's an Ammo Item ID
34
+ if (Object.values(AmmoItemId).includes(item as AmmoItemId)) {
35
+ ammoType = AMMO_ITEM_MAP[item as AmmoItemId];
36
+ }
37
+ // Check if it's a Weapon ID
38
+ else if (Object.values(WeaponId).includes(item as WeaponId)) {
39
+ ammoType = WEAPON_AMMO_MAP[item as WeaponId];
40
+
41
+ // Existing map has null for Blaster, Grapple, etc.
42
+ if (ammoType === null) {
43
+ return -1;
44
+ }
45
+ }
46
+
47
+ if (ammoType === undefined || ammoType === null) {
48
+ return 0;
49
+ }
50
+
51
+ return G_GetAmmoStat(playerState.stats, ammoType);
52
+ }
53
+
54
+ /**
55
+ * Resolves the icon path for a given stat index (e.g. STAT_SELECTED_ICON).
56
+ * @param statIndex The index in the stats array to read (e.g. PlayerStat.STAT_SELECTED_ICON).
57
+ * @param playerState The player state containing the stats.
58
+ * @param configStrings The array of configuration strings (from client state).
59
+ * @returns The path to the icon image, or undefined if invalid.
60
+ */
61
+ export function getIconPath(
62
+ statIndex: number,
63
+ playerState: PlayerState,
64
+ configStrings: string[]
65
+ ): string | undefined {
66
+ const iconIndex = playerState.stats[statIndex];
67
+
68
+ // 0 usually means no icon or null
69
+ if (iconIndex === undefined || iconIndex <= 0) {
70
+ return undefined;
71
+ }
72
+
73
+ // The value in the stat is the index into the Config Strings relative to ConfigStringIndex.Images.
74
+ const configIndex = ConfigStringIndex.Images + iconIndex;
75
+
76
+ if (configIndex < 0 || configIndex >= configStrings.length) {
77
+ return undefined;
78
+ }
79
+
80
+ return configStrings[configIndex];
81
+ }
@@ -0,0 +1,159 @@
1
+ import { Vec3 } from '../math/vec3.js';
2
+ import { ANORMS } from '../math/anorms.js';
3
+
4
+ export class BinaryStream {
5
+ private view: DataView;
6
+ private offset: number;
7
+ private length: number;
8
+
9
+ constructor(buffer: ArrayBuffer | Uint8Array) {
10
+ if (buffer instanceof Uint8Array) {
11
+ this.view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
12
+ } else {
13
+ this.view = new DataView(buffer);
14
+ }
15
+ this.offset = 0;
16
+ this.length = this.view.byteLength;
17
+ }
18
+
19
+ public getPosition(): number {
20
+ return this.offset;
21
+ }
22
+
23
+ public getReadPosition(): number {
24
+ return this.offset;
25
+ }
26
+
27
+ public getLength(): number {
28
+ return this.length;
29
+ }
30
+
31
+ public getRemaining(): number {
32
+ return this.length - this.offset;
33
+ }
34
+
35
+ public seek(position: number): void {
36
+ if (position < 0 || position > this.length) {
37
+ throw new Error(`Seek out of bounds: ${position} (length: ${this.length})`);
38
+ }
39
+ this.offset = position;
40
+ }
41
+
42
+ public setReadPosition(position: number): void {
43
+ this.seek(position);
44
+ }
45
+
46
+ public hasMore(): boolean {
47
+ return this.offset < this.length;
48
+ }
49
+
50
+ public hasBytes(count: number): boolean {
51
+ return this.offset + count <= this.length;
52
+ }
53
+
54
+ public readChar(): number {
55
+ const value = this.view.getInt8(this.offset);
56
+ this.offset += 1;
57
+ return value;
58
+ }
59
+
60
+ public readByte(): number {
61
+ const value = this.view.getUint8(this.offset);
62
+ this.offset += 1;
63
+ return value;
64
+ }
65
+
66
+ public readShort(): number {
67
+ const value = this.view.getInt16(this.offset, true);
68
+ this.offset += 2;
69
+ return value;
70
+ }
71
+
72
+ public readUShort(): number {
73
+ const value = this.view.getUint16(this.offset, true);
74
+ this.offset += 2;
75
+ return value;
76
+ }
77
+
78
+ public readLong(): number {
79
+ const value = this.view.getInt32(this.offset, true);
80
+ this.offset += 4;
81
+ return value;
82
+ }
83
+
84
+ public readULong(): number {
85
+ const value = this.view.getUint32(this.offset, true);
86
+ this.offset += 4;
87
+ return value;
88
+ }
89
+
90
+ public readFloat(): number {
91
+ const value = this.view.getFloat32(this.offset, true);
92
+ this.offset += 4;
93
+ return value;
94
+ }
95
+
96
+ public readString(): string {
97
+ let str = '';
98
+ while (this.offset < this.length) {
99
+ const charCode = this.readChar();
100
+ if (charCode === -1 || charCode === 0) {
101
+ break;
102
+ }
103
+ str += String.fromCharCode(charCode);
104
+ }
105
+ return str;
106
+ }
107
+
108
+ public readStringLine(): string {
109
+ let str = '';
110
+ while (this.offset < this.length) {
111
+ const charCode = this.readChar();
112
+ if (charCode === -1 || charCode === 0 || charCode === 10) { // 10 is \n
113
+ break;
114
+ }
115
+ str += String.fromCharCode(charCode);
116
+ }
117
+ return str;
118
+ }
119
+
120
+ public readCoord(): number {
121
+ return this.readShort() * (1.0 / 8.0);
122
+ }
123
+
124
+ public readAngle(): number {
125
+ return this.readChar() * (360.0 / 256.0);
126
+ }
127
+
128
+ public readAngle16(): number {
129
+ return (this.readShort() * 360.0) / 65536.0;
130
+ }
131
+
132
+ public readData(length: number): Uint8Array {
133
+ if (this.offset + length > this.length) {
134
+ throw new Error(`Read out of bounds: ${this.offset + length} (length: ${this.length})`);
135
+ }
136
+ const data = new Uint8Array(this.view.buffer, this.view.byteOffset + this.offset, length);
137
+ this.offset += length;
138
+ // Return a copy to avoid side effects if the original buffer is modified or reused
139
+ return new Uint8Array(data);
140
+ }
141
+
142
+ public readPos(out: { x: number, y: number, z: number }): void {
143
+ out.x = this.readCoord();
144
+ out.y = this.readCoord();
145
+ out.z = this.readCoord();
146
+ }
147
+
148
+ public readDir(out: { x: number, y: number, z: number }): void {
149
+ const b = this.readByte();
150
+ if (b >= 162) { // NUMVERTEXNORMALS
151
+ out.x = 0; out.y = 0; out.z = 0;
152
+ return;
153
+ }
154
+ const norm = ANORMS[b];
155
+ out.x = norm[0];
156
+ out.y = norm[1];
157
+ out.z = norm[2];
158
+ }
159
+ }
@@ -0,0 +1,146 @@
1
+ import { ANORMS } from '../math/anorms.js';
2
+ import { Vec3 } from '../math/vec3.js';
3
+
4
+ export class BinaryWriter {
5
+ private buffer: Uint8Array;
6
+ private view: DataView;
7
+ private offset: number;
8
+ private readonly fixed: boolean;
9
+
10
+ constructor(sizeOrBuffer: number | Uint8Array = 1400) {
11
+ if (typeof sizeOrBuffer === 'number') {
12
+ this.buffer = new Uint8Array(sizeOrBuffer);
13
+ this.fixed = false;
14
+ } else {
15
+ this.buffer = sizeOrBuffer;
16
+ this.fixed = true;
17
+ }
18
+ this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
19
+ this.offset = 0;
20
+ }
21
+
22
+ private ensureSpace(bytes: number) {
23
+ if (this.offset + bytes > this.buffer.byteLength) {
24
+ if (this.fixed) {
25
+ throw new Error(`Buffer overflow: capacity ${this.buffer.byteLength}, needed ${this.offset + bytes}`);
26
+ }
27
+ // Expand buffer (double size)
28
+ const newSize = Math.max(this.buffer.byteLength * 2, this.offset + bytes);
29
+ const newBuffer = new Uint8Array(newSize);
30
+ newBuffer.set(this.buffer);
31
+ this.buffer = newBuffer;
32
+ this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
33
+ }
34
+ }
35
+
36
+ public writeByte(value: number): void {
37
+ this.ensureSpace(1);
38
+ this.view.setUint8(this.offset, value);
39
+ this.offset += 1;
40
+ }
41
+
42
+ public writeBytes(data: Uint8Array): void {
43
+ this.ensureSpace(data.byteLength);
44
+ this.buffer.set(data, this.offset);
45
+ this.offset += data.byteLength;
46
+ }
47
+
48
+ public writeChar(value: number): void {
49
+ this.ensureSpace(1);
50
+ this.view.setInt8(this.offset, value);
51
+ this.offset += 1;
52
+ }
53
+
54
+ public writeShort(value: number): void {
55
+ this.ensureSpace(2);
56
+ // Use setUint16 to allow writing 0xFFFF as a valid pattern even if it represents -1
57
+ // But value might be negative (-1). setUint16(-1) wraps to 65535.
58
+ // So setInt16 is fine if value is in range.
59
+ // If value is 65535 (from bit manipulation), setInt16 might throw?
60
+ // Let's safe cast.
61
+ this.view.setInt16(this.offset, value, true);
62
+ this.offset += 2;
63
+ }
64
+
65
+ public writeLong(value: number): void {
66
+ this.ensureSpace(4);
67
+ this.view.setInt32(this.offset, value, true);
68
+ this.offset += 4;
69
+ }
70
+
71
+ public writeFloat(value: number): void {
72
+ this.ensureSpace(4);
73
+ this.view.setFloat32(this.offset, value, true);
74
+ this.offset += 4;
75
+ }
76
+
77
+ public writeString(value: string): void {
78
+ // UTF-8 encoding of string + null terminator
79
+ // We iterate manually to match readString behavior (ASCII/Latin1 mostly)
80
+ // and avoid TextEncoder overhead if simple
81
+ const len = value.length;
82
+ this.ensureSpace(len + 1);
83
+ for (let i = 0; i < len; i++) {
84
+ this.view.setUint8(this.offset + i, value.charCodeAt(i));
85
+ }
86
+ this.view.setUint8(this.offset + len, 0);
87
+ this.offset += len + 1;
88
+ }
89
+
90
+ public writeCoord(value: number): void {
91
+ this.writeShort(Math.trunc(value * 8));
92
+ }
93
+
94
+ public writeAngle(value: number): void {
95
+ this.writeByte(Math.trunc(value * 256.0 / 360.0) & 255);
96
+ }
97
+
98
+ public writeAngle16(value: number): void {
99
+ this.writeShort(Math.trunc(value * 65536.0 / 360.0) & 65535);
100
+ }
101
+
102
+ public writePos(pos: Vec3): void {
103
+ this.writeCoord(pos.x);
104
+ this.writeCoord(pos.y);
105
+ this.writeCoord(pos.z);
106
+ }
107
+
108
+ public writeDir(dir: Vec3): void {
109
+ // Find closest normal
110
+ let maxDot = -1.0;
111
+ let bestIndex = 0;
112
+
113
+ // Check for zero vector
114
+ if (dir.x === 0 && dir.y === 0 && dir.z === 0) {
115
+ this.writeByte(0);
116
+ return;
117
+ }
118
+
119
+ for (let i = 0; i < ANORMS.length; i++) {
120
+ const norm = ANORMS[i];
121
+ const dot = dir.x * norm[0] + dir.y * norm[1] + dir.z * norm[2];
122
+ if (dot > maxDot) {
123
+ maxDot = dot;
124
+ bestIndex = i;
125
+ }
126
+ }
127
+
128
+ this.writeByte(bestIndex);
129
+ }
130
+
131
+ public getData(): Uint8Array {
132
+ return this.buffer.slice(0, this.offset);
133
+ }
134
+
135
+ public getBuffer(): Uint8Array {
136
+ return this.buffer;
137
+ }
138
+
139
+ public getOffset(): number {
140
+ return this.offset;
141
+ }
142
+
143
+ public reset(): void {
144
+ this.offset = 0;
145
+ }
146
+ }
@@ -0,0 +1,3 @@
1
+ export * from './binaryStream.js';
2
+ export * from './binaryWriter.js';
3
+ export * from './messageBuilder.js';