quake2ts 0.0.528 → 0.0.529

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quake2ts",
3
- "version": "0.0.528",
3
+ "version": "0.0.529",
4
4
  "description": "Quake II re-release port to TypeScript with WebGL renderer - A complete game engine with physics, networking, and BSP rendering",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -0,0 +1,440 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createBinaryStreamMock: () => createBinaryStreamMock,
24
+ createBinaryWriterMock: () => createBinaryWriterMock,
25
+ createEntity: () => createEntity,
26
+ createEntityStateFactory: () => createEntityStateFactory,
27
+ createGameStateSnapshotFactory: () => createGameStateSnapshotFactory,
28
+ createNetChanMock: () => createNetChanMock,
29
+ createPlayerStateFactory: () => createPlayerStateFactory,
30
+ createSpawnContext: () => createSpawnContext,
31
+ createTestContext: () => createTestContext,
32
+ intersects: () => intersects,
33
+ ladderTrace: () => ladderTrace,
34
+ stairTrace: () => stairTrace
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/mocks.ts
39
+ var import_vitest = require("vitest");
40
+ var createBinaryWriterMock = () => ({
41
+ writeByte: import_vitest.vi.fn(),
42
+ writeShort: import_vitest.vi.fn(),
43
+ writeLong: import_vitest.vi.fn(),
44
+ writeString: import_vitest.vi.fn(),
45
+ writeBytes: import_vitest.vi.fn(),
46
+ getBuffer: import_vitest.vi.fn(() => new Uint8Array(0)),
47
+ reset: import_vitest.vi.fn(),
48
+ // Legacy methods (if any)
49
+ writeInt8: import_vitest.vi.fn(),
50
+ writeUint8: import_vitest.vi.fn(),
51
+ writeInt16: import_vitest.vi.fn(),
52
+ writeUint16: import_vitest.vi.fn(),
53
+ writeInt32: import_vitest.vi.fn(),
54
+ writeUint32: import_vitest.vi.fn(),
55
+ writeFloat: import_vitest.vi.fn(),
56
+ getData: import_vitest.vi.fn(() => new Uint8Array(0))
57
+ });
58
+ var createNetChanMock = () => ({
59
+ qport: 1234,
60
+ // Sequencing
61
+ incomingSequence: 0,
62
+ outgoingSequence: 0,
63
+ incomingAcknowledged: 0,
64
+ // Reliable messaging
65
+ incomingReliableAcknowledged: false,
66
+ incomingReliableSequence: 0,
67
+ outgoingReliableSequence: 0,
68
+ reliableMessage: createBinaryWriterMock(),
69
+ reliableLength: 0,
70
+ // Fragmentation
71
+ fragmentSendOffset: 0,
72
+ fragmentBuffer: null,
73
+ fragmentLength: 0,
74
+ fragmentReceived: 0,
75
+ // Timing
76
+ lastReceived: 0,
77
+ lastSent: 0,
78
+ remoteAddress: { type: "IP", port: 1234 },
79
+ // Methods
80
+ setup: import_vitest.vi.fn(),
81
+ reset: import_vitest.vi.fn(),
82
+ transmit: import_vitest.vi.fn(),
83
+ process: import_vitest.vi.fn(),
84
+ canSendReliable: import_vitest.vi.fn(() => true),
85
+ writeReliableByte: import_vitest.vi.fn(),
86
+ writeReliableShort: import_vitest.vi.fn(),
87
+ writeReliableLong: import_vitest.vi.fn(),
88
+ writeReliableString: import_vitest.vi.fn(),
89
+ getReliableData: import_vitest.vi.fn(() => new Uint8Array(0)),
90
+ needsKeepalive: import_vitest.vi.fn(() => false),
91
+ isTimedOut: import_vitest.vi.fn(() => false)
92
+ });
93
+ var createBinaryStreamMock = () => ({
94
+ getPosition: import_vitest.vi.fn(() => 0),
95
+ getReadPosition: import_vitest.vi.fn(() => 0),
96
+ getLength: import_vitest.vi.fn(() => 0),
97
+ getRemaining: import_vitest.vi.fn(() => 0),
98
+ seek: import_vitest.vi.fn(),
99
+ setReadPosition: import_vitest.vi.fn(),
100
+ hasMore: import_vitest.vi.fn(() => true),
101
+ hasBytes: import_vitest.vi.fn(() => true),
102
+ readChar: import_vitest.vi.fn(() => 0),
103
+ readByte: import_vitest.vi.fn(() => 0),
104
+ readShort: import_vitest.vi.fn(() => 0),
105
+ readUShort: import_vitest.vi.fn(() => 0),
106
+ readLong: import_vitest.vi.fn(() => 0),
107
+ readULong: import_vitest.vi.fn(() => 0),
108
+ readFloat: import_vitest.vi.fn(() => 0),
109
+ readString: import_vitest.vi.fn(() => ""),
110
+ readStringLine: import_vitest.vi.fn(() => ""),
111
+ readCoord: import_vitest.vi.fn(() => 0),
112
+ readAngle: import_vitest.vi.fn(() => 0),
113
+ readAngle16: import_vitest.vi.fn(() => 0),
114
+ readData: import_vitest.vi.fn(() => new Uint8Array(0)),
115
+ readPos: import_vitest.vi.fn(),
116
+ readDir: import_vitest.vi.fn()
117
+ });
118
+
119
+ // src/factories.ts
120
+ var createPlayerStateFactory = (overrides) => ({
121
+ pm_type: 0,
122
+ pm_time: 0,
123
+ pm_flags: 0,
124
+ origin: { x: 0, y: 0, z: 0 },
125
+ velocity: { x: 0, y: 0, z: 0 },
126
+ viewAngles: { x: 0, y: 0, z: 0 },
127
+ onGround: false,
128
+ waterLevel: 0,
129
+ mins: { x: 0, y: 0, z: 0 },
130
+ maxs: { x: 0, y: 0, z: 0 },
131
+ damageAlpha: 0,
132
+ damageIndicators: [],
133
+ blend: [0, 0, 0, 0],
134
+ stats: [],
135
+ kick_angles: { x: 0, y: 0, z: 0 },
136
+ kick_origin: { x: 0, y: 0, z: 0 },
137
+ gunoffset: { x: 0, y: 0, z: 0 },
138
+ gunangles: { x: 0, y: 0, z: 0 },
139
+ gunindex: 0,
140
+ gun_frame: 0,
141
+ rdflags: 0,
142
+ fov: 90,
143
+ renderfx: 0,
144
+ ...overrides
145
+ });
146
+ var createEntityStateFactory = (overrides) => ({
147
+ number: 0,
148
+ origin: { x: 0, y: 0, z: 0 },
149
+ angles: { x: 0, y: 0, z: 0 },
150
+ oldOrigin: { x: 0, y: 0, z: 0 },
151
+ modelIndex: 0,
152
+ modelIndex2: 0,
153
+ modelIndex3: 0,
154
+ modelIndex4: 0,
155
+ frame: 0,
156
+ skinNum: 0,
157
+ effects: 0,
158
+ renderfx: 0,
159
+ solid: 0,
160
+ sound: 0,
161
+ event: 0,
162
+ ...overrides
163
+ });
164
+ var createGameStateSnapshotFactory = (overrides) => ({
165
+ entities: [],
166
+ playerState: createPlayerStateFactory(),
167
+ timestamp: Date.now(),
168
+ ...overrides
169
+ });
170
+
171
+ // src/helpers.ts
172
+ var import_vitest2 = require("vitest");
173
+ var import_game = require("@quake2ts/game");
174
+ var import_shared = require("@quake2ts/shared");
175
+ var intersects = (end, maxs, mins, boxMins, boxMaxs) => {
176
+ return end.x + maxs.x > boxMins.x && end.x + mins.x < boxMaxs.x && end.y + maxs.y > boxMins.y && end.y + mins.y < boxMaxs.y && end.z + maxs.z > boxMins.z && end.z + mins.z < boxMaxs.z;
177
+ };
178
+ var stairTrace = (start, end, mins, maxs) => {
179
+ const useMins = mins ?? { x: -16, y: -16, z: -24 };
180
+ const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };
181
+ const STEP_HEIGHT = 8;
182
+ const STEP_X_START = 0;
183
+ const isHorizontal = Math.abs(end.z - start.z) < 1;
184
+ const isMovingDown = end.z < start.z;
185
+ const endMinZ = end.z + useMins.z;
186
+ const startMinZ = start.z + useMins.z;
187
+ const endMaxX = end.x + useMaxs.x;
188
+ if (isHorizontal && end.z < STEP_HEIGHT && endMaxX > STEP_X_START) {
189
+ const startMaxX = start.x + useMaxs.x;
190
+ if (startMaxX <= STEP_X_START) {
191
+ return {
192
+ allsolid: false,
193
+ startsolid: false,
194
+ fraction: 0,
195
+ endpos: start,
196
+ planeNormal: { x: -1, y: 0, z: 0 },
197
+ contents: 1
198
+ };
199
+ }
200
+ }
201
+ if (isMovingDown && end.x >= STEP_X_START) {
202
+ const landZ = STEP_HEIGHT - useMins.z;
203
+ if (startMinZ > STEP_HEIGHT && endMinZ < STEP_HEIGHT) {
204
+ const fraction = (STEP_HEIGHT - startMinZ) / (endMinZ - startMinZ);
205
+ const clampedFraction = Math.max(0, Math.min(1, fraction));
206
+ const finalX = start.x + clampedFraction * (end.x - start.x);
207
+ const finalY = start.y + clampedFraction * (end.y - start.y);
208
+ const finalZ = start.z + clampedFraction * (end.z - start.z);
209
+ return {
210
+ allsolid: false,
211
+ startsolid: false,
212
+ fraction: clampedFraction,
213
+ endpos: { x: finalX, y: finalY, z: finalZ },
214
+ planeNormal: { x: 0, y: 0, z: 1 },
215
+ contents: 1
216
+ };
217
+ }
218
+ }
219
+ if (isMovingDown && endMinZ < 0) {
220
+ const landZ = -useMins.z;
221
+ if (startMinZ >= 0) {
222
+ const fraction = (0 - startMinZ) / (endMinZ - startMinZ);
223
+ const clampedFraction = Math.max(0, Math.min(1, fraction));
224
+ const finalX = start.x + clampedFraction * (end.x - start.x);
225
+ const finalY = start.y + clampedFraction * (end.y - start.y);
226
+ const finalZ = start.z + clampedFraction * (end.z - start.z);
227
+ return {
228
+ allsolid: false,
229
+ startsolid: false,
230
+ fraction: clampedFraction,
231
+ endpos: { x: finalX, y: finalY, z: finalZ },
232
+ planeNormal: { x: 0, y: 0, z: 1 },
233
+ contents: 1
234
+ };
235
+ }
236
+ return {
237
+ allsolid: false,
238
+ startsolid: false,
239
+ fraction: 0,
240
+ endpos: start,
241
+ planeNormal: { x: 0, y: 0, z: 1 },
242
+ contents: 1
243
+ };
244
+ }
245
+ return {
246
+ allsolid: false,
247
+ startsolid: false,
248
+ fraction: 1,
249
+ endpos: end,
250
+ contents: 0
251
+ };
252
+ };
253
+ var ladderTrace = (start, end, mins, maxs) => {
254
+ const useMins = mins ?? { x: -16, y: -16, z: -24 };
255
+ const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };
256
+ const LADDER_X_MIN = 0;
257
+ const LADDER_X_MAX = 8;
258
+ const LADDER_Y_MIN = -16;
259
+ const LADDER_Y_MAX = 16;
260
+ const LADDER_Z_MIN = 0;
261
+ const LADDER_Z_MAX = 100;
262
+ const endInLadder = end.x + useMins.x < LADDER_X_MAX && end.x + useMaxs.x > LADDER_X_MIN && end.y + useMins.y < LADDER_Y_MAX && end.y + useMaxs.y > LADDER_Y_MIN && end.z + useMins.z < LADDER_Z_MAX && end.z + useMaxs.z > LADDER_Z_MIN;
263
+ const movingIntoLadder = start.x < LADDER_X_MIN && end.x >= LADDER_X_MIN;
264
+ if (movingIntoLadder && Math.abs(end.z - start.z) < 0.1) {
265
+ return {
266
+ allsolid: false,
267
+ startsolid: false,
268
+ fraction: 0,
269
+ endpos: start,
270
+ planeNormal: { x: -1, y: 0, z: 0 },
271
+ contents: import_shared.CONTENTS_LADDER
272
+ };
273
+ }
274
+ if (endInLadder) {
275
+ return {
276
+ allsolid: false,
277
+ startsolid: false,
278
+ fraction: 1,
279
+ endpos: end,
280
+ contents: import_shared.CONTENTS_LADDER
281
+ };
282
+ }
283
+ if (end.z + useMins.z <= 0) {
284
+ return {
285
+ allsolid: false,
286
+ startsolid: false,
287
+ fraction: 0,
288
+ endpos: start,
289
+ planeNormal: { x: 0, y: 0, z: 1 },
290
+ contents: 1
291
+ };
292
+ }
293
+ return {
294
+ allsolid: false,
295
+ startsolid: false,
296
+ fraction: 1,
297
+ endpos: end,
298
+ contents: 0
299
+ };
300
+ };
301
+ function createTestContext(options) {
302
+ const engine = {
303
+ sound: import_vitest2.vi.fn(),
304
+ soundIndex: import_vitest2.vi.fn((sound) => 0),
305
+ modelIndex: import_vitest2.vi.fn((model) => 0),
306
+ centerprintf: import_vitest2.vi.fn()
307
+ };
308
+ const seed = options?.seed ?? 12345;
309
+ const traceFn = import_vitest2.vi.fn((start, end, mins, maxs) => ({
310
+ fraction: 1,
311
+ ent: null,
312
+ allsolid: false,
313
+ startsolid: false,
314
+ endpos: end,
315
+ // Use end argument
316
+ plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0 },
317
+ surfaceFlags: 0,
318
+ contents: 0
319
+ }));
320
+ const spawnRegistry = new import_game.SpawnRegistry();
321
+ const game = {
322
+ random: (0, import_shared.createRandomGenerator)({ seed }),
323
+ registerEntitySpawn: import_vitest2.vi.fn((classname, spawnFunc) => {
324
+ spawnRegistry.register(classname, (entity) => spawnFunc(entity));
325
+ }),
326
+ unregisterEntitySpawn: import_vitest2.vi.fn((classname) => {
327
+ spawnRegistry.unregister(classname);
328
+ }),
329
+ getCustomEntities: import_vitest2.vi.fn(() => Array.from(spawnRegistry.keys()))
330
+ };
331
+ const entityList = options?.initialEntities ? [...options.initialEntities] : [];
332
+ const entities = {
333
+ spawn: import_vitest2.vi.fn(() => {
334
+ const ent = new import_game.Entity(entityList.length + 1);
335
+ entityList.push(ent);
336
+ return ent;
337
+ }),
338
+ free: import_vitest2.vi.fn((ent) => {
339
+ const idx = entityList.indexOf(ent);
340
+ if (idx !== -1) {
341
+ entityList.splice(idx, 1);
342
+ }
343
+ }),
344
+ finalizeSpawn: import_vitest2.vi.fn(),
345
+ freeImmediate: import_vitest2.vi.fn((ent) => {
346
+ const idx = entityList.indexOf(ent);
347
+ if (idx !== -1) {
348
+ entityList.splice(idx, 1);
349
+ }
350
+ }),
351
+ setSpawnRegistry: import_vitest2.vi.fn(),
352
+ timeSeconds: 10,
353
+ deltaSeconds: 0.1,
354
+ // Added deltaSeconds
355
+ modelIndex: import_vitest2.vi.fn(() => 0),
356
+ scheduleThink: import_vitest2.vi.fn((entity, time) => {
357
+ entity.nextthink = time;
358
+ }),
359
+ linkentity: import_vitest2.vi.fn(),
360
+ trace: traceFn,
361
+ // Directly provide the mock function property
362
+ pointcontents: import_vitest2.vi.fn(() => 0),
363
+ multicast: import_vitest2.vi.fn(),
364
+ unicast: import_vitest2.vi.fn(),
365
+ engine,
366
+ // Attach mocked engine
367
+ game,
368
+ sound: import_vitest2.vi.fn((ent, chan, sound, vol, attn, timeofs) => {
369
+ engine.sound(ent, chan, sound, vol, attn, timeofs);
370
+ }),
371
+ soundIndex: import_vitest2.vi.fn((sound) => engine.soundIndex(sound)),
372
+ useTargets: import_vitest2.vi.fn((entity, activator) => {
373
+ }),
374
+ findByTargetName: import_vitest2.vi.fn(() => []),
375
+ pickTarget: import_vitest2.vi.fn(() => null),
376
+ killBox: import_vitest2.vi.fn(),
377
+ rng: (0, import_shared.createRandomGenerator)({ seed }),
378
+ // Use real RNG for determinism or easy mocking if we replace it
379
+ imports: {
380
+ configstring: import_vitest2.vi.fn(),
381
+ trace: traceFn,
382
+ // Also in imports for good measure
383
+ pointcontents: import_vitest2.vi.fn(() => 0)
384
+ },
385
+ level: {
386
+ intermission_angle: { x: 0, y: 0, z: 0 },
387
+ intermission_origin: { x: 0, y: 0, z: 0 }
388
+ },
389
+ targetNameIndex: /* @__PURE__ */ new Map(),
390
+ forEachEntity: import_vitest2.vi.fn((callback) => {
391
+ entityList.forEach(callback);
392
+ }),
393
+ find: import_vitest2.vi.fn((predicate) => {
394
+ return entityList.find(predicate);
395
+ }),
396
+ beginFrame: import_vitest2.vi.fn((timeSeconds) => {
397
+ entities.timeSeconds = timeSeconds;
398
+ }),
399
+ targetAwareness: {
400
+ timeSeconds: 10,
401
+ frameNumber: 1,
402
+ sightEntity: null,
403
+ soundEntity: null
404
+ }
405
+ };
406
+ return {
407
+ keyValues: {},
408
+ entities,
409
+ game,
410
+ health_multiplier: 1,
411
+ warn: import_vitest2.vi.fn(),
412
+ free: import_vitest2.vi.fn(),
413
+ // Legacy support for tests that might check precache
414
+ precacheModel: import_vitest2.vi.fn(),
415
+ precacheSound: import_vitest2.vi.fn(),
416
+ precacheImage: import_vitest2.vi.fn()
417
+ };
418
+ }
419
+ function createSpawnContext() {
420
+ return createTestContext();
421
+ }
422
+ function createEntity() {
423
+ return new import_game.Entity(1);
424
+ }
425
+ // Annotate the CommonJS export names for ESM import in node:
426
+ 0 && (module.exports = {
427
+ createBinaryStreamMock,
428
+ createBinaryWriterMock,
429
+ createEntity,
430
+ createEntityStateFactory,
431
+ createGameStateSnapshotFactory,
432
+ createNetChanMock,
433
+ createPlayerStateFactory,
434
+ createSpawnContext,
435
+ createTestContext,
436
+ intersects,
437
+ ladderTrace,
438
+ stairTrace
439
+ });
440
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/mocks.ts","../src/factories.ts","../src/helpers.ts"],"sourcesContent":["// Export all test utilities\nexport * from './mocks.js';\nexport * from './factories.js';\nexport * from './helpers.js';\n","import { vi } from 'vitest';\n\nexport const createBinaryWriterMock = () => ({\n writeByte: vi.fn(),\n writeShort: vi.fn(),\n writeLong: vi.fn(),\n writeString: vi.fn(),\n writeBytes: vi.fn(),\n getBuffer: vi.fn(() => new Uint8Array(0)),\n reset: vi.fn(),\n // Legacy methods (if any)\n writeInt8: vi.fn(),\n writeUint8: vi.fn(),\n writeInt16: vi.fn(),\n writeUint16: vi.fn(),\n writeInt32: vi.fn(),\n writeUint32: vi.fn(),\n writeFloat: vi.fn(),\n getData: vi.fn(() => new Uint8Array(0)),\n});\n\nexport const createNetChanMock = () => ({\n qport: 1234,\n\n // Sequencing\n incomingSequence: 0,\n outgoingSequence: 0,\n incomingAcknowledged: 0,\n\n // Reliable messaging\n incomingReliableAcknowledged: false,\n incomingReliableSequence: 0,\n outgoingReliableSequence: 0,\n reliableMessage: createBinaryWriterMock(),\n reliableLength: 0,\n\n // Fragmentation\n fragmentSendOffset: 0,\n fragmentBuffer: null,\n fragmentLength: 0,\n fragmentReceived: 0,\n\n // Timing\n lastReceived: 0,\n lastSent: 0,\n\n remoteAddress: { type: 'IP', port: 1234 },\n\n // Methods\n setup: vi.fn(),\n reset: vi.fn(),\n transmit: vi.fn(),\n process: vi.fn(),\n canSendReliable: vi.fn(() => true),\n writeReliableByte: vi.fn(),\n writeReliableShort: vi.fn(),\n writeReliableLong: vi.fn(),\n writeReliableString: vi.fn(),\n getReliableData: vi.fn(() => new Uint8Array(0)),\n needsKeepalive: vi.fn(() => false),\n isTimedOut: vi.fn(() => false),\n});\n\nexport const createBinaryStreamMock = () => ({\n getPosition: vi.fn(() => 0),\n getReadPosition: vi.fn(() => 0),\n getLength: vi.fn(() => 0),\n getRemaining: vi.fn(() => 0),\n seek: vi.fn(),\n setReadPosition: vi.fn(),\n hasMore: vi.fn(() => true),\n hasBytes: vi.fn(() => true),\n\n readChar: vi.fn(() => 0),\n readByte: vi.fn(() => 0),\n readShort: vi.fn(() => 0),\n readUShort: vi.fn(() => 0),\n readLong: vi.fn(() => 0),\n readULong: vi.fn(() => 0),\n readFloat: vi.fn(() => 0),\n\n readString: vi.fn(() => ''),\n readStringLine: vi.fn(() => ''),\n\n readCoord: vi.fn(() => 0),\n readAngle: vi.fn(() => 0),\n readAngle16: vi.fn(() => 0),\n\n readData: vi.fn(() => new Uint8Array(0)),\n\n readPos: vi.fn(),\n readDir: vi.fn(),\n});\n","import type { PlayerState, EntityState } from '@quake2ts/shared';\n\n// Define GameStateSnapshot locally if it's not exported or if we need a specific mock version\n// Assuming it matches the structure expected by the game/client\nexport interface GameStateSnapshot {\n entities: EntityState[];\n playerState: PlayerState;\n timestamp: number;\n}\n\nexport const createPlayerStateFactory = (overrides?: Partial<PlayerState>): PlayerState => ({\n pm_type: 0,\n pm_time: 0,\n pm_flags: 0,\n origin: { x: 0, y: 0, z: 0 },\n velocity: { x: 0, y: 0, z: 0 },\n viewAngles: { x: 0, y: 0, z: 0 },\n onGround: false,\n waterLevel: 0,\n mins: { x: 0, y: 0, z: 0 },\n maxs: { x: 0, y: 0, z: 0 },\n damageAlpha: 0,\n damageIndicators: [],\n blend: [0, 0, 0, 0],\n stats: [],\n kick_angles: { x: 0, y: 0, z: 0 },\n kick_origin: { x: 0, y: 0, z: 0 },\n gunoffset: { x: 0, y: 0, z: 0 },\n gunangles: { x: 0, y: 0, z: 0 },\n gunindex: 0,\n gun_frame: 0,\n rdflags: 0,\n fov: 90,\n renderfx: 0,\n ...overrides,\n});\n\nexport const createEntityStateFactory = (overrides?: Partial<EntityState>): EntityState => ({\n number: 0,\n origin: { x: 0, y: 0, z: 0 },\n angles: { x: 0, y: 0, z: 0 },\n oldOrigin: { x: 0, y: 0, z: 0 },\n modelIndex: 0,\n modelIndex2: 0,\n modelIndex3: 0,\n modelIndex4: 0,\n frame: 0,\n skinNum: 0,\n effects: 0,\n renderfx: 0,\n solid: 0,\n sound: 0,\n event: 0,\n ...overrides,\n});\n\nexport const createGameStateSnapshotFactory = (overrides?: Partial<GameStateSnapshot>): GameStateSnapshot => ({\n entities: [],\n playerState: createPlayerStateFactory(),\n timestamp: Date.now(),\n ...overrides,\n});\n","import { vi } from 'vitest';\nimport { Entity, SpawnRegistry, type SpawnContext, type EntitySystem } from '@quake2ts/game';\nimport { createRandomGenerator, type PmoveTraceFn, type PmoveTraceResult, type Vec3, CONTENTS_LADDER } from '@quake2ts/shared';\n\n// -- Shared Helpers --\n\nexport const intersects = (end: Vec3, maxs: Vec3, mins: Vec3, boxMins: Vec3, boxMaxs: Vec3): boolean => {\n return (\n end.x + maxs.x > boxMins.x &&\n end.x + mins.x < boxMaxs.x &&\n end.y + maxs.y > boxMins.y &&\n end.y + mins.y < boxMaxs.y &&\n end.z + maxs.z > boxMins.z &&\n end.z + mins.z < boxMaxs.z\n );\n};\n\nexport const stairTrace: PmoveTraceFn = (start: Vec3, end: Vec3, mins?: Vec3, maxs?: Vec3): PmoveTraceResult => {\n // Default bbox if not provided\n const useMins = mins ?? { x: -16, y: -16, z: -24 };\n const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };\n\n // Step: x from 0 forward, z from 0 to 8\n const STEP_HEIGHT = 8;\n const STEP_X_START = 0;\n\n const isHorizontal = Math.abs(end.z - start.z) < 1;\n const isMovingDown = end.z < start.z;\n\n // Check if trying to go below the floor\n const endMinZ = end.z + useMins.z;\n const startMinZ = start.z + useMins.z;\n const endMaxX = end.x + useMaxs.x;\n\n // If moving horizontally, check if we'd hit the vertical face of the step\n // The step only blocks if the player's origin is below the step height\n if (isHorizontal && end.z < STEP_HEIGHT && endMaxX > STEP_X_START) {\n // Check if we're crossing into the step area\n const startMaxX = start.x + useMaxs.x;\n if (startMaxX <= STEP_X_START) {\n // We're moving from before the step to past it, block\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: -1, y: 0, z: 0 },\n contents: 1,\n };\n }\n }\n\n // If moving down and over the step area, land on the step surface\n if (isMovingDown && end.x >= STEP_X_START) {\n // The step surface is at z=STEP_HEIGHT in world space\n // The player's bbox bottom reaches this plane when origin.z + mins.z = STEP_HEIGHT\n // So the player's origin should be at z = STEP_HEIGHT - mins.z\n const landZ = STEP_HEIGHT - useMins.z;\n\n // Check if we'd pass through the step surface\n // We cross the plane if start is above it and end would be below it\n if (startMinZ > STEP_HEIGHT && endMinZ < STEP_HEIGHT) {\n // Calculate the fraction along the ray where we intersect the plane\n // The bbox bottom is at: start.z + useMins.z + t * (end.z - start.z + 0) = STEP_HEIGHT\n // Solving for t: t = (STEP_HEIGHT - (start.z + useMins.z)) / ((end.z + useMins.z) - (start.z + useMins.z))\n const fraction = (STEP_HEIGHT - startMinZ) / (endMinZ - startMinZ);\n\n // Clamp to valid range [0, 1]\n const clampedFraction = Math.max(0, Math.min(1, fraction));\n\n // Calculate the endpos along the ray at this fraction\n const finalX = start.x + clampedFraction * (end.x - start.x);\n const finalY = start.y + clampedFraction * (end.y - start.y);\n const finalZ = start.z + clampedFraction * (end.z - start.z);\n\n return {\n allsolid: false,\n startsolid: false,\n fraction: clampedFraction,\n endpos: { x: finalX, y: finalY, z: finalZ },\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n }\n\n // If moving down and would go below floor level, block at floor\n if (isMovingDown && endMinZ < 0) {\n // Floor is at z=0, so player origin should be at z = -mins.z when landing\n const landZ = -useMins.z;\n\n // Only apply if we're crossing the floor plane\n if (startMinZ >= 0) {\n // Calculate fraction where bbox bottom hits z=0\n const fraction = (0 - startMinZ) / (endMinZ - startMinZ);\n const clampedFraction = Math.max(0, Math.min(1, fraction));\n\n const finalX = start.x + clampedFraction * (end.x - start.x);\n const finalY = start.y + clampedFraction * (end.y - start.y);\n const finalZ = start.z + clampedFraction * (end.z - start.z);\n\n return {\n allsolid: false,\n startsolid: false,\n fraction: clampedFraction,\n endpos: { x: finalX, y: finalY, z: finalZ },\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n\n // Already below floor, block immediately\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n\n // Free movement\n return {\n allsolid: false,\n startsolid: false,\n fraction: 1.0,\n endpos: end,\n contents: 0,\n };\n};\n\nexport const ladderTrace: PmoveTraceFn = (start: Vec3, end: Vec3, mins?: Vec3, maxs?: Vec3): PmoveTraceResult => {\n // Default bbox if not provided\n const useMins = mins ?? { x: -16, y: -16, z: -24 };\n const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };\n\n // Define the ladder volume (x=0 to x=8, y=-16 to y=16, z=0 to z=100)\n const LADDER_X_MIN = 0;\n const LADDER_X_MAX = 8;\n const LADDER_Y_MIN = -16;\n const LADDER_Y_MAX = 16;\n const LADDER_Z_MIN = 0;\n const LADDER_Z_MAX = 100;\n\n // Check if end position is within the ladder volume\n const endInLadder =\n end.x + useMins.x < LADDER_X_MAX &&\n end.x + useMaxs.x > LADDER_X_MIN &&\n end.y + useMins.y < LADDER_Y_MAX &&\n end.y + useMaxs.y > LADDER_Y_MIN &&\n end.z + useMins.z < LADDER_Z_MAX &&\n end.z + useMaxs.z > LADDER_Z_MIN;\n\n // If moving into the ladder from outside (moving forward into it)\n const movingIntoLadder = start.x < LADDER_X_MIN && end.x >= LADDER_X_MIN;\n\n // If moving horizontally into the ladder front face, block with ladder surface\n if (movingIntoLadder && Math.abs(end.z - start.z) < 0.1) {\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: -1, y: 0, z: 0 },\n contents: CONTENTS_LADDER,\n };\n }\n\n // If we're in the ladder volume, return success but with CONTENTS_LADDER\n // This allows the player to detect they're on a ladder without blocking movement\n if (endInLadder) {\n return {\n allsolid: false,\n startsolid: false,\n fraction: 1.0,\n endpos: end,\n contents: CONTENTS_LADDER,\n };\n }\n\n // Floor at z=0\n if (end.z + useMins.z <= 0) {\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n\n // No collision - free movement\n return {\n allsolid: false,\n startsolid: false,\n fraction: 1.0,\n endpos: end,\n contents: 0,\n };\n};\n\n// -- Game Helpers --\n\nexport function createTestContext(options?: { seed?: number, initialEntities?: Entity[] }): { entities: EntitySystem, game: any } & SpawnContext {\n const engine = {\n sound: vi.fn(),\n soundIndex: vi.fn((sound: string) => 0),\n modelIndex: vi.fn((model: string) => 0),\n centerprintf: vi.fn(),\n };\n\n const seed = options?.seed ?? 12345;\n const traceFn = vi.fn((start: Vec3, end: Vec3, mins?: Vec3, maxs?: Vec3) => ({\n fraction: 1.0,\n ent: null,\n allsolid: false,\n startsolid: false,\n endpos: end, // Use end argument\n plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0 },\n surfaceFlags: 0,\n contents: 0\n }));\n\n const spawnRegistry = new SpawnRegistry();\n\n const game = {\n random: createRandomGenerator({ seed }),\n registerEntitySpawn: vi.fn((classname: string, spawnFunc: (entity: Entity) => void) => {\n spawnRegistry.register(classname, (entity) => spawnFunc(entity));\n }),\n unregisterEntitySpawn: vi.fn((classname: string) => {\n spawnRegistry.unregister(classname);\n }),\n getCustomEntities: vi.fn(() => Array.from(spawnRegistry.keys()))\n };\n\n const entityList: Entity[] = options?.initialEntities ? [...options.initialEntities] : [];\n\n const entities = {\n spawn: vi.fn(() => {\n const ent = new Entity(entityList.length + 1);\n entityList.push(ent);\n return ent;\n }),\n free: vi.fn((ent: Entity) => {\n const idx = entityList.indexOf(ent);\n if (idx !== -1) {\n entityList.splice(idx, 1);\n }\n }),\n finalizeSpawn: vi.fn(),\n freeImmediate: vi.fn((ent: Entity) => {\n const idx = entityList.indexOf(ent);\n if (idx !== -1) {\n entityList.splice(idx, 1);\n }\n }),\n setSpawnRegistry: vi.fn(),\n timeSeconds: 10,\n deltaSeconds: 0.1, // Added deltaSeconds\n modelIndex: vi.fn(() => 0),\n scheduleThink: vi.fn((entity: Entity, time: number) => {\n entity.nextthink = time;\n }),\n linkentity: vi.fn(),\n trace: traceFn, // Directly provide the mock function property\n pointcontents: vi.fn(() => 0),\n multicast: vi.fn(),\n unicast: vi.fn(),\n engine, // Attach mocked engine\n game,\n sound: vi.fn((ent: Entity, chan: number, sound: string, vol: number, attn: number, timeofs: number) => {\n engine.sound(ent, chan, sound, vol, attn, timeofs);\n }),\n soundIndex: vi.fn((sound: string) => engine.soundIndex(sound)),\n useTargets: vi.fn((entity: Entity, activator: Entity | null) => {\n }),\n findByTargetName: vi.fn(() => []),\n pickTarget: vi.fn(() => null),\n killBox: vi.fn(),\n rng: createRandomGenerator({ seed }), // Use real RNG for determinism or easy mocking if we replace it\n imports: {\n configstring: vi.fn(),\n trace: traceFn, // Also in imports for good measure\n pointcontents: vi.fn(() => 0),\n },\n level: {\n intermission_angle: { x: 0, y: 0, z: 0 },\n intermission_origin: { x: 0, y: 0, z: 0 },\n },\n targetNameIndex: new Map(),\n forEachEntity: vi.fn((callback: (ent: Entity) => void) => {\n // Iterate over managed list\n entityList.forEach(callback);\n }),\n find: vi.fn((predicate: (ent: Entity) => boolean) => {\n // Find in managed list\n return entityList.find(predicate);\n }),\n beginFrame: vi.fn((timeSeconds: number) => {\n (entities as any).timeSeconds = timeSeconds;\n }),\n targetAwareness: {\n timeSeconds: 10,\n frameNumber: 1,\n sightEntity: null,\n soundEntity: null,\n }\n } as unknown as EntitySystem;\n\n return {\n keyValues: {},\n entities,\n game: game,\n health_multiplier: 1,\n warn: vi.fn(),\n free: vi.fn(),\n // Legacy support for tests that might check precache\n precacheModel: vi.fn(),\n precacheSound: vi.fn(),\n precacheImage: vi.fn(),\n } as unknown as SpawnContext & { entities: EntitySystem, game: any };\n}\n\nexport function createSpawnContext(): SpawnContext {\n return createTestContext();\n}\n\nexport function createEntity(): Entity {\n return new Entity(1);\n}\n"],"mappings":"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","names":["import_vitest"]}
@@ -0,0 +1,119 @@
1
+ import * as vitest from 'vitest';
2
+ import { EntityState, PlayerState, Vec3, PmoveTraceFn } from '@quake2ts/shared';
3
+ import { Entity, EntitySystem, SpawnContext } from '@quake2ts/game';
4
+
5
+ declare const createBinaryWriterMock: () => {
6
+ writeByte: vitest.Mock<any, any>;
7
+ writeShort: vitest.Mock<any, any>;
8
+ writeLong: vitest.Mock<any, any>;
9
+ writeString: vitest.Mock<any, any>;
10
+ writeBytes: vitest.Mock<any, any>;
11
+ getBuffer: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
12
+ reset: vitest.Mock<any, any>;
13
+ writeInt8: vitest.Mock<any, any>;
14
+ writeUint8: vitest.Mock<any, any>;
15
+ writeInt16: vitest.Mock<any, any>;
16
+ writeUint16: vitest.Mock<any, any>;
17
+ writeInt32: vitest.Mock<any, any>;
18
+ writeUint32: vitest.Mock<any, any>;
19
+ writeFloat: vitest.Mock<any, any>;
20
+ getData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
21
+ };
22
+ declare const createNetChanMock: () => {
23
+ qport: number;
24
+ incomingSequence: number;
25
+ outgoingSequence: number;
26
+ incomingAcknowledged: number;
27
+ incomingReliableAcknowledged: boolean;
28
+ incomingReliableSequence: number;
29
+ outgoingReliableSequence: number;
30
+ reliableMessage: {
31
+ writeByte: vitest.Mock<any, any>;
32
+ writeShort: vitest.Mock<any, any>;
33
+ writeLong: vitest.Mock<any, any>;
34
+ writeString: vitest.Mock<any, any>;
35
+ writeBytes: vitest.Mock<any, any>;
36
+ getBuffer: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
37
+ reset: vitest.Mock<any, any>;
38
+ writeInt8: vitest.Mock<any, any>;
39
+ writeUint8: vitest.Mock<any, any>;
40
+ writeInt16: vitest.Mock<any, any>;
41
+ writeUint16: vitest.Mock<any, any>;
42
+ writeInt32: vitest.Mock<any, any>;
43
+ writeUint32: vitest.Mock<any, any>;
44
+ writeFloat: vitest.Mock<any, any>;
45
+ getData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
46
+ };
47
+ reliableLength: number;
48
+ fragmentSendOffset: number;
49
+ fragmentBuffer: null;
50
+ fragmentLength: number;
51
+ fragmentReceived: number;
52
+ lastReceived: number;
53
+ lastSent: number;
54
+ remoteAddress: {
55
+ type: string;
56
+ port: number;
57
+ };
58
+ setup: vitest.Mock<any, any>;
59
+ reset: vitest.Mock<any, any>;
60
+ transmit: vitest.Mock<any, any>;
61
+ process: vitest.Mock<any, any>;
62
+ canSendReliable: vitest.Mock<[], boolean>;
63
+ writeReliableByte: vitest.Mock<any, any>;
64
+ writeReliableShort: vitest.Mock<any, any>;
65
+ writeReliableLong: vitest.Mock<any, any>;
66
+ writeReliableString: vitest.Mock<any, any>;
67
+ getReliableData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
68
+ needsKeepalive: vitest.Mock<[], boolean>;
69
+ isTimedOut: vitest.Mock<[], boolean>;
70
+ };
71
+ declare const createBinaryStreamMock: () => {
72
+ getPosition: vitest.Mock<[], number>;
73
+ getReadPosition: vitest.Mock<[], number>;
74
+ getLength: vitest.Mock<[], number>;
75
+ getRemaining: vitest.Mock<[], number>;
76
+ seek: vitest.Mock<any, any>;
77
+ setReadPosition: vitest.Mock<any, any>;
78
+ hasMore: vitest.Mock<[], boolean>;
79
+ hasBytes: vitest.Mock<[], boolean>;
80
+ readChar: vitest.Mock<[], number>;
81
+ readByte: vitest.Mock<[], number>;
82
+ readShort: vitest.Mock<[], number>;
83
+ readUShort: vitest.Mock<[], number>;
84
+ readLong: vitest.Mock<[], number>;
85
+ readULong: vitest.Mock<[], number>;
86
+ readFloat: vitest.Mock<[], number>;
87
+ readString: vitest.Mock<[], string>;
88
+ readStringLine: vitest.Mock<[], string>;
89
+ readCoord: vitest.Mock<[], number>;
90
+ readAngle: vitest.Mock<[], number>;
91
+ readAngle16: vitest.Mock<[], number>;
92
+ readData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
93
+ readPos: vitest.Mock<any, any>;
94
+ readDir: vitest.Mock<any, any>;
95
+ };
96
+
97
+ interface GameStateSnapshot {
98
+ entities: EntityState[];
99
+ playerState: PlayerState;
100
+ timestamp: number;
101
+ }
102
+ declare const createPlayerStateFactory: (overrides?: Partial<PlayerState>) => PlayerState;
103
+ declare const createEntityStateFactory: (overrides?: Partial<EntityState>) => EntityState;
104
+ declare const createGameStateSnapshotFactory: (overrides?: Partial<GameStateSnapshot>) => GameStateSnapshot;
105
+
106
+ declare const intersects: (end: Vec3, maxs: Vec3, mins: Vec3, boxMins: Vec3, boxMaxs: Vec3) => boolean;
107
+ declare const stairTrace: PmoveTraceFn;
108
+ declare const ladderTrace: PmoveTraceFn;
109
+ declare function createTestContext(options?: {
110
+ seed?: number;
111
+ initialEntities?: Entity[];
112
+ }): {
113
+ entities: EntitySystem;
114
+ game: any;
115
+ } & SpawnContext;
116
+ declare function createSpawnContext(): SpawnContext;
117
+ declare function createEntity(): Entity;
118
+
119
+ export { type GameStateSnapshot, createBinaryStreamMock, createBinaryWriterMock, createEntity, createEntityStateFactory, createGameStateSnapshotFactory, createNetChanMock, createPlayerStateFactory, createSpawnContext, createTestContext, intersects, ladderTrace, stairTrace };
@@ -0,0 +1,119 @@
1
+ import * as vitest from 'vitest';
2
+ import { EntityState, PlayerState, Vec3, PmoveTraceFn } from '@quake2ts/shared';
3
+ import { Entity, EntitySystem, SpawnContext } from '@quake2ts/game';
4
+
5
+ declare const createBinaryWriterMock: () => {
6
+ writeByte: vitest.Mock<any, any>;
7
+ writeShort: vitest.Mock<any, any>;
8
+ writeLong: vitest.Mock<any, any>;
9
+ writeString: vitest.Mock<any, any>;
10
+ writeBytes: vitest.Mock<any, any>;
11
+ getBuffer: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
12
+ reset: vitest.Mock<any, any>;
13
+ writeInt8: vitest.Mock<any, any>;
14
+ writeUint8: vitest.Mock<any, any>;
15
+ writeInt16: vitest.Mock<any, any>;
16
+ writeUint16: vitest.Mock<any, any>;
17
+ writeInt32: vitest.Mock<any, any>;
18
+ writeUint32: vitest.Mock<any, any>;
19
+ writeFloat: vitest.Mock<any, any>;
20
+ getData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
21
+ };
22
+ declare const createNetChanMock: () => {
23
+ qport: number;
24
+ incomingSequence: number;
25
+ outgoingSequence: number;
26
+ incomingAcknowledged: number;
27
+ incomingReliableAcknowledged: boolean;
28
+ incomingReliableSequence: number;
29
+ outgoingReliableSequence: number;
30
+ reliableMessage: {
31
+ writeByte: vitest.Mock<any, any>;
32
+ writeShort: vitest.Mock<any, any>;
33
+ writeLong: vitest.Mock<any, any>;
34
+ writeString: vitest.Mock<any, any>;
35
+ writeBytes: vitest.Mock<any, any>;
36
+ getBuffer: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
37
+ reset: vitest.Mock<any, any>;
38
+ writeInt8: vitest.Mock<any, any>;
39
+ writeUint8: vitest.Mock<any, any>;
40
+ writeInt16: vitest.Mock<any, any>;
41
+ writeUint16: vitest.Mock<any, any>;
42
+ writeInt32: vitest.Mock<any, any>;
43
+ writeUint32: vitest.Mock<any, any>;
44
+ writeFloat: vitest.Mock<any, any>;
45
+ getData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
46
+ };
47
+ reliableLength: number;
48
+ fragmentSendOffset: number;
49
+ fragmentBuffer: null;
50
+ fragmentLength: number;
51
+ fragmentReceived: number;
52
+ lastReceived: number;
53
+ lastSent: number;
54
+ remoteAddress: {
55
+ type: string;
56
+ port: number;
57
+ };
58
+ setup: vitest.Mock<any, any>;
59
+ reset: vitest.Mock<any, any>;
60
+ transmit: vitest.Mock<any, any>;
61
+ process: vitest.Mock<any, any>;
62
+ canSendReliable: vitest.Mock<[], boolean>;
63
+ writeReliableByte: vitest.Mock<any, any>;
64
+ writeReliableShort: vitest.Mock<any, any>;
65
+ writeReliableLong: vitest.Mock<any, any>;
66
+ writeReliableString: vitest.Mock<any, any>;
67
+ getReliableData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
68
+ needsKeepalive: vitest.Mock<[], boolean>;
69
+ isTimedOut: vitest.Mock<[], boolean>;
70
+ };
71
+ declare const createBinaryStreamMock: () => {
72
+ getPosition: vitest.Mock<[], number>;
73
+ getReadPosition: vitest.Mock<[], number>;
74
+ getLength: vitest.Mock<[], number>;
75
+ getRemaining: vitest.Mock<[], number>;
76
+ seek: vitest.Mock<any, any>;
77
+ setReadPosition: vitest.Mock<any, any>;
78
+ hasMore: vitest.Mock<[], boolean>;
79
+ hasBytes: vitest.Mock<[], boolean>;
80
+ readChar: vitest.Mock<[], number>;
81
+ readByte: vitest.Mock<[], number>;
82
+ readShort: vitest.Mock<[], number>;
83
+ readUShort: vitest.Mock<[], number>;
84
+ readLong: vitest.Mock<[], number>;
85
+ readULong: vitest.Mock<[], number>;
86
+ readFloat: vitest.Mock<[], number>;
87
+ readString: vitest.Mock<[], string>;
88
+ readStringLine: vitest.Mock<[], string>;
89
+ readCoord: vitest.Mock<[], number>;
90
+ readAngle: vitest.Mock<[], number>;
91
+ readAngle16: vitest.Mock<[], number>;
92
+ readData: vitest.Mock<[], Uint8Array<ArrayBuffer>>;
93
+ readPos: vitest.Mock<any, any>;
94
+ readDir: vitest.Mock<any, any>;
95
+ };
96
+
97
+ interface GameStateSnapshot {
98
+ entities: EntityState[];
99
+ playerState: PlayerState;
100
+ timestamp: number;
101
+ }
102
+ declare const createPlayerStateFactory: (overrides?: Partial<PlayerState>) => PlayerState;
103
+ declare const createEntityStateFactory: (overrides?: Partial<EntityState>) => EntityState;
104
+ declare const createGameStateSnapshotFactory: (overrides?: Partial<GameStateSnapshot>) => GameStateSnapshot;
105
+
106
+ declare const intersects: (end: Vec3, maxs: Vec3, mins: Vec3, boxMins: Vec3, boxMaxs: Vec3) => boolean;
107
+ declare const stairTrace: PmoveTraceFn;
108
+ declare const ladderTrace: PmoveTraceFn;
109
+ declare function createTestContext(options?: {
110
+ seed?: number;
111
+ initialEntities?: Entity[];
112
+ }): {
113
+ entities: EntitySystem;
114
+ game: any;
115
+ } & SpawnContext;
116
+ declare function createSpawnContext(): SpawnContext;
117
+ declare function createEntity(): Entity;
118
+
119
+ export { type GameStateSnapshot, createBinaryStreamMock, createBinaryWriterMock, createEntity, createEntityStateFactory, createGameStateSnapshotFactory, createNetChanMock, createPlayerStateFactory, createSpawnContext, createTestContext, intersects, ladderTrace, stairTrace };
@@ -0,0 +1,402 @@
1
+ // src/mocks.ts
2
+ import { vi } from "vitest";
3
+ var createBinaryWriterMock = () => ({
4
+ writeByte: vi.fn(),
5
+ writeShort: vi.fn(),
6
+ writeLong: vi.fn(),
7
+ writeString: vi.fn(),
8
+ writeBytes: vi.fn(),
9
+ getBuffer: vi.fn(() => new Uint8Array(0)),
10
+ reset: vi.fn(),
11
+ // Legacy methods (if any)
12
+ writeInt8: vi.fn(),
13
+ writeUint8: vi.fn(),
14
+ writeInt16: vi.fn(),
15
+ writeUint16: vi.fn(),
16
+ writeInt32: vi.fn(),
17
+ writeUint32: vi.fn(),
18
+ writeFloat: vi.fn(),
19
+ getData: vi.fn(() => new Uint8Array(0))
20
+ });
21
+ var createNetChanMock = () => ({
22
+ qport: 1234,
23
+ // Sequencing
24
+ incomingSequence: 0,
25
+ outgoingSequence: 0,
26
+ incomingAcknowledged: 0,
27
+ // Reliable messaging
28
+ incomingReliableAcknowledged: false,
29
+ incomingReliableSequence: 0,
30
+ outgoingReliableSequence: 0,
31
+ reliableMessage: createBinaryWriterMock(),
32
+ reliableLength: 0,
33
+ // Fragmentation
34
+ fragmentSendOffset: 0,
35
+ fragmentBuffer: null,
36
+ fragmentLength: 0,
37
+ fragmentReceived: 0,
38
+ // Timing
39
+ lastReceived: 0,
40
+ lastSent: 0,
41
+ remoteAddress: { type: "IP", port: 1234 },
42
+ // Methods
43
+ setup: vi.fn(),
44
+ reset: vi.fn(),
45
+ transmit: vi.fn(),
46
+ process: vi.fn(),
47
+ canSendReliable: vi.fn(() => true),
48
+ writeReliableByte: vi.fn(),
49
+ writeReliableShort: vi.fn(),
50
+ writeReliableLong: vi.fn(),
51
+ writeReliableString: vi.fn(),
52
+ getReliableData: vi.fn(() => new Uint8Array(0)),
53
+ needsKeepalive: vi.fn(() => false),
54
+ isTimedOut: vi.fn(() => false)
55
+ });
56
+ var createBinaryStreamMock = () => ({
57
+ getPosition: vi.fn(() => 0),
58
+ getReadPosition: vi.fn(() => 0),
59
+ getLength: vi.fn(() => 0),
60
+ getRemaining: vi.fn(() => 0),
61
+ seek: vi.fn(),
62
+ setReadPosition: vi.fn(),
63
+ hasMore: vi.fn(() => true),
64
+ hasBytes: vi.fn(() => true),
65
+ readChar: vi.fn(() => 0),
66
+ readByte: vi.fn(() => 0),
67
+ readShort: vi.fn(() => 0),
68
+ readUShort: vi.fn(() => 0),
69
+ readLong: vi.fn(() => 0),
70
+ readULong: vi.fn(() => 0),
71
+ readFloat: vi.fn(() => 0),
72
+ readString: vi.fn(() => ""),
73
+ readStringLine: vi.fn(() => ""),
74
+ readCoord: vi.fn(() => 0),
75
+ readAngle: vi.fn(() => 0),
76
+ readAngle16: vi.fn(() => 0),
77
+ readData: vi.fn(() => new Uint8Array(0)),
78
+ readPos: vi.fn(),
79
+ readDir: vi.fn()
80
+ });
81
+
82
+ // src/factories.ts
83
+ var createPlayerStateFactory = (overrides) => ({
84
+ pm_type: 0,
85
+ pm_time: 0,
86
+ pm_flags: 0,
87
+ origin: { x: 0, y: 0, z: 0 },
88
+ velocity: { x: 0, y: 0, z: 0 },
89
+ viewAngles: { x: 0, y: 0, z: 0 },
90
+ onGround: false,
91
+ waterLevel: 0,
92
+ mins: { x: 0, y: 0, z: 0 },
93
+ maxs: { x: 0, y: 0, z: 0 },
94
+ damageAlpha: 0,
95
+ damageIndicators: [],
96
+ blend: [0, 0, 0, 0],
97
+ stats: [],
98
+ kick_angles: { x: 0, y: 0, z: 0 },
99
+ kick_origin: { x: 0, y: 0, z: 0 },
100
+ gunoffset: { x: 0, y: 0, z: 0 },
101
+ gunangles: { x: 0, y: 0, z: 0 },
102
+ gunindex: 0,
103
+ gun_frame: 0,
104
+ rdflags: 0,
105
+ fov: 90,
106
+ renderfx: 0,
107
+ ...overrides
108
+ });
109
+ var createEntityStateFactory = (overrides) => ({
110
+ number: 0,
111
+ origin: { x: 0, y: 0, z: 0 },
112
+ angles: { x: 0, y: 0, z: 0 },
113
+ oldOrigin: { x: 0, y: 0, z: 0 },
114
+ modelIndex: 0,
115
+ modelIndex2: 0,
116
+ modelIndex3: 0,
117
+ modelIndex4: 0,
118
+ frame: 0,
119
+ skinNum: 0,
120
+ effects: 0,
121
+ renderfx: 0,
122
+ solid: 0,
123
+ sound: 0,
124
+ event: 0,
125
+ ...overrides
126
+ });
127
+ var createGameStateSnapshotFactory = (overrides) => ({
128
+ entities: [],
129
+ playerState: createPlayerStateFactory(),
130
+ timestamp: Date.now(),
131
+ ...overrides
132
+ });
133
+
134
+ // src/helpers.ts
135
+ import { vi as vi2 } from "vitest";
136
+ import { Entity, SpawnRegistry } from "@quake2ts/game";
137
+ import { createRandomGenerator, CONTENTS_LADDER } from "@quake2ts/shared";
138
+ var intersects = (end, maxs, mins, boxMins, boxMaxs) => {
139
+ return end.x + maxs.x > boxMins.x && end.x + mins.x < boxMaxs.x && end.y + maxs.y > boxMins.y && end.y + mins.y < boxMaxs.y && end.z + maxs.z > boxMins.z && end.z + mins.z < boxMaxs.z;
140
+ };
141
+ var stairTrace = (start, end, mins, maxs) => {
142
+ const useMins = mins ?? { x: -16, y: -16, z: -24 };
143
+ const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };
144
+ const STEP_HEIGHT = 8;
145
+ const STEP_X_START = 0;
146
+ const isHorizontal = Math.abs(end.z - start.z) < 1;
147
+ const isMovingDown = end.z < start.z;
148
+ const endMinZ = end.z + useMins.z;
149
+ const startMinZ = start.z + useMins.z;
150
+ const endMaxX = end.x + useMaxs.x;
151
+ if (isHorizontal && end.z < STEP_HEIGHT && endMaxX > STEP_X_START) {
152
+ const startMaxX = start.x + useMaxs.x;
153
+ if (startMaxX <= STEP_X_START) {
154
+ return {
155
+ allsolid: false,
156
+ startsolid: false,
157
+ fraction: 0,
158
+ endpos: start,
159
+ planeNormal: { x: -1, y: 0, z: 0 },
160
+ contents: 1
161
+ };
162
+ }
163
+ }
164
+ if (isMovingDown && end.x >= STEP_X_START) {
165
+ const landZ = STEP_HEIGHT - useMins.z;
166
+ if (startMinZ > STEP_HEIGHT && endMinZ < STEP_HEIGHT) {
167
+ const fraction = (STEP_HEIGHT - startMinZ) / (endMinZ - startMinZ);
168
+ const clampedFraction = Math.max(0, Math.min(1, fraction));
169
+ const finalX = start.x + clampedFraction * (end.x - start.x);
170
+ const finalY = start.y + clampedFraction * (end.y - start.y);
171
+ const finalZ = start.z + clampedFraction * (end.z - start.z);
172
+ return {
173
+ allsolid: false,
174
+ startsolid: false,
175
+ fraction: clampedFraction,
176
+ endpos: { x: finalX, y: finalY, z: finalZ },
177
+ planeNormal: { x: 0, y: 0, z: 1 },
178
+ contents: 1
179
+ };
180
+ }
181
+ }
182
+ if (isMovingDown && endMinZ < 0) {
183
+ const landZ = -useMins.z;
184
+ if (startMinZ >= 0) {
185
+ const fraction = (0 - startMinZ) / (endMinZ - startMinZ);
186
+ const clampedFraction = Math.max(0, Math.min(1, fraction));
187
+ const finalX = start.x + clampedFraction * (end.x - start.x);
188
+ const finalY = start.y + clampedFraction * (end.y - start.y);
189
+ const finalZ = start.z + clampedFraction * (end.z - start.z);
190
+ return {
191
+ allsolid: false,
192
+ startsolid: false,
193
+ fraction: clampedFraction,
194
+ endpos: { x: finalX, y: finalY, z: finalZ },
195
+ planeNormal: { x: 0, y: 0, z: 1 },
196
+ contents: 1
197
+ };
198
+ }
199
+ return {
200
+ allsolid: false,
201
+ startsolid: false,
202
+ fraction: 0,
203
+ endpos: start,
204
+ planeNormal: { x: 0, y: 0, z: 1 },
205
+ contents: 1
206
+ };
207
+ }
208
+ return {
209
+ allsolid: false,
210
+ startsolid: false,
211
+ fraction: 1,
212
+ endpos: end,
213
+ contents: 0
214
+ };
215
+ };
216
+ var ladderTrace = (start, end, mins, maxs) => {
217
+ const useMins = mins ?? { x: -16, y: -16, z: -24 };
218
+ const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };
219
+ const LADDER_X_MIN = 0;
220
+ const LADDER_X_MAX = 8;
221
+ const LADDER_Y_MIN = -16;
222
+ const LADDER_Y_MAX = 16;
223
+ const LADDER_Z_MIN = 0;
224
+ const LADDER_Z_MAX = 100;
225
+ const endInLadder = end.x + useMins.x < LADDER_X_MAX && end.x + useMaxs.x > LADDER_X_MIN && end.y + useMins.y < LADDER_Y_MAX && end.y + useMaxs.y > LADDER_Y_MIN && end.z + useMins.z < LADDER_Z_MAX && end.z + useMaxs.z > LADDER_Z_MIN;
226
+ const movingIntoLadder = start.x < LADDER_X_MIN && end.x >= LADDER_X_MIN;
227
+ if (movingIntoLadder && Math.abs(end.z - start.z) < 0.1) {
228
+ return {
229
+ allsolid: false,
230
+ startsolid: false,
231
+ fraction: 0,
232
+ endpos: start,
233
+ planeNormal: { x: -1, y: 0, z: 0 },
234
+ contents: CONTENTS_LADDER
235
+ };
236
+ }
237
+ if (endInLadder) {
238
+ return {
239
+ allsolid: false,
240
+ startsolid: false,
241
+ fraction: 1,
242
+ endpos: end,
243
+ contents: CONTENTS_LADDER
244
+ };
245
+ }
246
+ if (end.z + useMins.z <= 0) {
247
+ return {
248
+ allsolid: false,
249
+ startsolid: false,
250
+ fraction: 0,
251
+ endpos: start,
252
+ planeNormal: { x: 0, y: 0, z: 1 },
253
+ contents: 1
254
+ };
255
+ }
256
+ return {
257
+ allsolid: false,
258
+ startsolid: false,
259
+ fraction: 1,
260
+ endpos: end,
261
+ contents: 0
262
+ };
263
+ };
264
+ function createTestContext(options) {
265
+ const engine = {
266
+ sound: vi2.fn(),
267
+ soundIndex: vi2.fn((sound) => 0),
268
+ modelIndex: vi2.fn((model) => 0),
269
+ centerprintf: vi2.fn()
270
+ };
271
+ const seed = options?.seed ?? 12345;
272
+ const traceFn = vi2.fn((start, end, mins, maxs) => ({
273
+ fraction: 1,
274
+ ent: null,
275
+ allsolid: false,
276
+ startsolid: false,
277
+ endpos: end,
278
+ // Use end argument
279
+ plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0 },
280
+ surfaceFlags: 0,
281
+ contents: 0
282
+ }));
283
+ const spawnRegistry = new SpawnRegistry();
284
+ const game = {
285
+ random: createRandomGenerator({ seed }),
286
+ registerEntitySpawn: vi2.fn((classname, spawnFunc) => {
287
+ spawnRegistry.register(classname, (entity) => spawnFunc(entity));
288
+ }),
289
+ unregisterEntitySpawn: vi2.fn((classname) => {
290
+ spawnRegistry.unregister(classname);
291
+ }),
292
+ getCustomEntities: vi2.fn(() => Array.from(spawnRegistry.keys()))
293
+ };
294
+ const entityList = options?.initialEntities ? [...options.initialEntities] : [];
295
+ const entities = {
296
+ spawn: vi2.fn(() => {
297
+ const ent = new Entity(entityList.length + 1);
298
+ entityList.push(ent);
299
+ return ent;
300
+ }),
301
+ free: vi2.fn((ent) => {
302
+ const idx = entityList.indexOf(ent);
303
+ if (idx !== -1) {
304
+ entityList.splice(idx, 1);
305
+ }
306
+ }),
307
+ finalizeSpawn: vi2.fn(),
308
+ freeImmediate: vi2.fn((ent) => {
309
+ const idx = entityList.indexOf(ent);
310
+ if (idx !== -1) {
311
+ entityList.splice(idx, 1);
312
+ }
313
+ }),
314
+ setSpawnRegistry: vi2.fn(),
315
+ timeSeconds: 10,
316
+ deltaSeconds: 0.1,
317
+ // Added deltaSeconds
318
+ modelIndex: vi2.fn(() => 0),
319
+ scheduleThink: vi2.fn((entity, time) => {
320
+ entity.nextthink = time;
321
+ }),
322
+ linkentity: vi2.fn(),
323
+ trace: traceFn,
324
+ // Directly provide the mock function property
325
+ pointcontents: vi2.fn(() => 0),
326
+ multicast: vi2.fn(),
327
+ unicast: vi2.fn(),
328
+ engine,
329
+ // Attach mocked engine
330
+ game,
331
+ sound: vi2.fn((ent, chan, sound, vol, attn, timeofs) => {
332
+ engine.sound(ent, chan, sound, vol, attn, timeofs);
333
+ }),
334
+ soundIndex: vi2.fn((sound) => engine.soundIndex(sound)),
335
+ useTargets: vi2.fn((entity, activator) => {
336
+ }),
337
+ findByTargetName: vi2.fn(() => []),
338
+ pickTarget: vi2.fn(() => null),
339
+ killBox: vi2.fn(),
340
+ rng: createRandomGenerator({ seed }),
341
+ // Use real RNG for determinism or easy mocking if we replace it
342
+ imports: {
343
+ configstring: vi2.fn(),
344
+ trace: traceFn,
345
+ // Also in imports for good measure
346
+ pointcontents: vi2.fn(() => 0)
347
+ },
348
+ level: {
349
+ intermission_angle: { x: 0, y: 0, z: 0 },
350
+ intermission_origin: { x: 0, y: 0, z: 0 }
351
+ },
352
+ targetNameIndex: /* @__PURE__ */ new Map(),
353
+ forEachEntity: vi2.fn((callback) => {
354
+ entityList.forEach(callback);
355
+ }),
356
+ find: vi2.fn((predicate) => {
357
+ return entityList.find(predicate);
358
+ }),
359
+ beginFrame: vi2.fn((timeSeconds) => {
360
+ entities.timeSeconds = timeSeconds;
361
+ }),
362
+ targetAwareness: {
363
+ timeSeconds: 10,
364
+ frameNumber: 1,
365
+ sightEntity: null,
366
+ soundEntity: null
367
+ }
368
+ };
369
+ return {
370
+ keyValues: {},
371
+ entities,
372
+ game,
373
+ health_multiplier: 1,
374
+ warn: vi2.fn(),
375
+ free: vi2.fn(),
376
+ // Legacy support for tests that might check precache
377
+ precacheModel: vi2.fn(),
378
+ precacheSound: vi2.fn(),
379
+ precacheImage: vi2.fn()
380
+ };
381
+ }
382
+ function createSpawnContext() {
383
+ return createTestContext();
384
+ }
385
+ function createEntity() {
386
+ return new Entity(1);
387
+ }
388
+ export {
389
+ createBinaryStreamMock,
390
+ createBinaryWriterMock,
391
+ createEntity,
392
+ createEntityStateFactory,
393
+ createGameStateSnapshotFactory,
394
+ createNetChanMock,
395
+ createPlayerStateFactory,
396
+ createSpawnContext,
397
+ createTestContext,
398
+ intersects,
399
+ ladderTrace,
400
+ stairTrace
401
+ };
402
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mocks.ts","../src/factories.ts","../src/helpers.ts"],"sourcesContent":["import { vi } from 'vitest';\n\nexport const createBinaryWriterMock = () => ({\n writeByte: vi.fn(),\n writeShort: vi.fn(),\n writeLong: vi.fn(),\n writeString: vi.fn(),\n writeBytes: vi.fn(),\n getBuffer: vi.fn(() => new Uint8Array(0)),\n reset: vi.fn(),\n // Legacy methods (if any)\n writeInt8: vi.fn(),\n writeUint8: vi.fn(),\n writeInt16: vi.fn(),\n writeUint16: vi.fn(),\n writeInt32: vi.fn(),\n writeUint32: vi.fn(),\n writeFloat: vi.fn(),\n getData: vi.fn(() => new Uint8Array(0)),\n});\n\nexport const createNetChanMock = () => ({\n qport: 1234,\n\n // Sequencing\n incomingSequence: 0,\n outgoingSequence: 0,\n incomingAcknowledged: 0,\n\n // Reliable messaging\n incomingReliableAcknowledged: false,\n incomingReliableSequence: 0,\n outgoingReliableSequence: 0,\n reliableMessage: createBinaryWriterMock(),\n reliableLength: 0,\n\n // Fragmentation\n fragmentSendOffset: 0,\n fragmentBuffer: null,\n fragmentLength: 0,\n fragmentReceived: 0,\n\n // Timing\n lastReceived: 0,\n lastSent: 0,\n\n remoteAddress: { type: 'IP', port: 1234 },\n\n // Methods\n setup: vi.fn(),\n reset: vi.fn(),\n transmit: vi.fn(),\n process: vi.fn(),\n canSendReliable: vi.fn(() => true),\n writeReliableByte: vi.fn(),\n writeReliableShort: vi.fn(),\n writeReliableLong: vi.fn(),\n writeReliableString: vi.fn(),\n getReliableData: vi.fn(() => new Uint8Array(0)),\n needsKeepalive: vi.fn(() => false),\n isTimedOut: vi.fn(() => false),\n});\n\nexport const createBinaryStreamMock = () => ({\n getPosition: vi.fn(() => 0),\n getReadPosition: vi.fn(() => 0),\n getLength: vi.fn(() => 0),\n getRemaining: vi.fn(() => 0),\n seek: vi.fn(),\n setReadPosition: vi.fn(),\n hasMore: vi.fn(() => true),\n hasBytes: vi.fn(() => true),\n\n readChar: vi.fn(() => 0),\n readByte: vi.fn(() => 0),\n readShort: vi.fn(() => 0),\n readUShort: vi.fn(() => 0),\n readLong: vi.fn(() => 0),\n readULong: vi.fn(() => 0),\n readFloat: vi.fn(() => 0),\n\n readString: vi.fn(() => ''),\n readStringLine: vi.fn(() => ''),\n\n readCoord: vi.fn(() => 0),\n readAngle: vi.fn(() => 0),\n readAngle16: vi.fn(() => 0),\n\n readData: vi.fn(() => new Uint8Array(0)),\n\n readPos: vi.fn(),\n readDir: vi.fn(),\n});\n","import type { PlayerState, EntityState } from '@quake2ts/shared';\n\n// Define GameStateSnapshot locally if it's not exported or if we need a specific mock version\n// Assuming it matches the structure expected by the game/client\nexport interface GameStateSnapshot {\n entities: EntityState[];\n playerState: PlayerState;\n timestamp: number;\n}\n\nexport const createPlayerStateFactory = (overrides?: Partial<PlayerState>): PlayerState => ({\n pm_type: 0,\n pm_time: 0,\n pm_flags: 0,\n origin: { x: 0, y: 0, z: 0 },\n velocity: { x: 0, y: 0, z: 0 },\n viewAngles: { x: 0, y: 0, z: 0 },\n onGround: false,\n waterLevel: 0,\n mins: { x: 0, y: 0, z: 0 },\n maxs: { x: 0, y: 0, z: 0 },\n damageAlpha: 0,\n damageIndicators: [],\n blend: [0, 0, 0, 0],\n stats: [],\n kick_angles: { x: 0, y: 0, z: 0 },\n kick_origin: { x: 0, y: 0, z: 0 },\n gunoffset: { x: 0, y: 0, z: 0 },\n gunangles: { x: 0, y: 0, z: 0 },\n gunindex: 0,\n gun_frame: 0,\n rdflags: 0,\n fov: 90,\n renderfx: 0,\n ...overrides,\n});\n\nexport const createEntityStateFactory = (overrides?: Partial<EntityState>): EntityState => ({\n number: 0,\n origin: { x: 0, y: 0, z: 0 },\n angles: { x: 0, y: 0, z: 0 },\n oldOrigin: { x: 0, y: 0, z: 0 },\n modelIndex: 0,\n modelIndex2: 0,\n modelIndex3: 0,\n modelIndex4: 0,\n frame: 0,\n skinNum: 0,\n effects: 0,\n renderfx: 0,\n solid: 0,\n sound: 0,\n event: 0,\n ...overrides,\n});\n\nexport const createGameStateSnapshotFactory = (overrides?: Partial<GameStateSnapshot>): GameStateSnapshot => ({\n entities: [],\n playerState: createPlayerStateFactory(),\n timestamp: Date.now(),\n ...overrides,\n});\n","import { vi } from 'vitest';\nimport { Entity, SpawnRegistry, type SpawnContext, type EntitySystem } from '@quake2ts/game';\nimport { createRandomGenerator, type PmoveTraceFn, type PmoveTraceResult, type Vec3, CONTENTS_LADDER } from '@quake2ts/shared';\n\n// -- Shared Helpers --\n\nexport const intersects = (end: Vec3, maxs: Vec3, mins: Vec3, boxMins: Vec3, boxMaxs: Vec3): boolean => {\n return (\n end.x + maxs.x > boxMins.x &&\n end.x + mins.x < boxMaxs.x &&\n end.y + maxs.y > boxMins.y &&\n end.y + mins.y < boxMaxs.y &&\n end.z + maxs.z > boxMins.z &&\n end.z + mins.z < boxMaxs.z\n );\n};\n\nexport const stairTrace: PmoveTraceFn = (start: Vec3, end: Vec3, mins?: Vec3, maxs?: Vec3): PmoveTraceResult => {\n // Default bbox if not provided\n const useMins = mins ?? { x: -16, y: -16, z: -24 };\n const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };\n\n // Step: x from 0 forward, z from 0 to 8\n const STEP_HEIGHT = 8;\n const STEP_X_START = 0;\n\n const isHorizontal = Math.abs(end.z - start.z) < 1;\n const isMovingDown = end.z < start.z;\n\n // Check if trying to go below the floor\n const endMinZ = end.z + useMins.z;\n const startMinZ = start.z + useMins.z;\n const endMaxX = end.x + useMaxs.x;\n\n // If moving horizontally, check if we'd hit the vertical face of the step\n // The step only blocks if the player's origin is below the step height\n if (isHorizontal && end.z < STEP_HEIGHT && endMaxX > STEP_X_START) {\n // Check if we're crossing into the step area\n const startMaxX = start.x + useMaxs.x;\n if (startMaxX <= STEP_X_START) {\n // We're moving from before the step to past it, block\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: -1, y: 0, z: 0 },\n contents: 1,\n };\n }\n }\n\n // If moving down and over the step area, land on the step surface\n if (isMovingDown && end.x >= STEP_X_START) {\n // The step surface is at z=STEP_HEIGHT in world space\n // The player's bbox bottom reaches this plane when origin.z + mins.z = STEP_HEIGHT\n // So the player's origin should be at z = STEP_HEIGHT - mins.z\n const landZ = STEP_HEIGHT - useMins.z;\n\n // Check if we'd pass through the step surface\n // We cross the plane if start is above it and end would be below it\n if (startMinZ > STEP_HEIGHT && endMinZ < STEP_HEIGHT) {\n // Calculate the fraction along the ray where we intersect the plane\n // The bbox bottom is at: start.z + useMins.z + t * (end.z - start.z + 0) = STEP_HEIGHT\n // Solving for t: t = (STEP_HEIGHT - (start.z + useMins.z)) / ((end.z + useMins.z) - (start.z + useMins.z))\n const fraction = (STEP_HEIGHT - startMinZ) / (endMinZ - startMinZ);\n\n // Clamp to valid range [0, 1]\n const clampedFraction = Math.max(0, Math.min(1, fraction));\n\n // Calculate the endpos along the ray at this fraction\n const finalX = start.x + clampedFraction * (end.x - start.x);\n const finalY = start.y + clampedFraction * (end.y - start.y);\n const finalZ = start.z + clampedFraction * (end.z - start.z);\n\n return {\n allsolid: false,\n startsolid: false,\n fraction: clampedFraction,\n endpos: { x: finalX, y: finalY, z: finalZ },\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n }\n\n // If moving down and would go below floor level, block at floor\n if (isMovingDown && endMinZ < 0) {\n // Floor is at z=0, so player origin should be at z = -mins.z when landing\n const landZ = -useMins.z;\n\n // Only apply if we're crossing the floor plane\n if (startMinZ >= 0) {\n // Calculate fraction where bbox bottom hits z=0\n const fraction = (0 - startMinZ) / (endMinZ - startMinZ);\n const clampedFraction = Math.max(0, Math.min(1, fraction));\n\n const finalX = start.x + clampedFraction * (end.x - start.x);\n const finalY = start.y + clampedFraction * (end.y - start.y);\n const finalZ = start.z + clampedFraction * (end.z - start.z);\n\n return {\n allsolid: false,\n startsolid: false,\n fraction: clampedFraction,\n endpos: { x: finalX, y: finalY, z: finalZ },\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n\n // Already below floor, block immediately\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n\n // Free movement\n return {\n allsolid: false,\n startsolid: false,\n fraction: 1.0,\n endpos: end,\n contents: 0,\n };\n};\n\nexport const ladderTrace: PmoveTraceFn = (start: Vec3, end: Vec3, mins?: Vec3, maxs?: Vec3): PmoveTraceResult => {\n // Default bbox if not provided\n const useMins = mins ?? { x: -16, y: -16, z: -24 };\n const useMaxs = maxs ?? { x: 16, y: 16, z: 32 };\n\n // Define the ladder volume (x=0 to x=8, y=-16 to y=16, z=0 to z=100)\n const LADDER_X_MIN = 0;\n const LADDER_X_MAX = 8;\n const LADDER_Y_MIN = -16;\n const LADDER_Y_MAX = 16;\n const LADDER_Z_MIN = 0;\n const LADDER_Z_MAX = 100;\n\n // Check if end position is within the ladder volume\n const endInLadder =\n end.x + useMins.x < LADDER_X_MAX &&\n end.x + useMaxs.x > LADDER_X_MIN &&\n end.y + useMins.y < LADDER_Y_MAX &&\n end.y + useMaxs.y > LADDER_Y_MIN &&\n end.z + useMins.z < LADDER_Z_MAX &&\n end.z + useMaxs.z > LADDER_Z_MIN;\n\n // If moving into the ladder from outside (moving forward into it)\n const movingIntoLadder = start.x < LADDER_X_MIN && end.x >= LADDER_X_MIN;\n\n // If moving horizontally into the ladder front face, block with ladder surface\n if (movingIntoLadder && Math.abs(end.z - start.z) < 0.1) {\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: -1, y: 0, z: 0 },\n contents: CONTENTS_LADDER,\n };\n }\n\n // If we're in the ladder volume, return success but with CONTENTS_LADDER\n // This allows the player to detect they're on a ladder without blocking movement\n if (endInLadder) {\n return {\n allsolid: false,\n startsolid: false,\n fraction: 1.0,\n endpos: end,\n contents: CONTENTS_LADDER,\n };\n }\n\n // Floor at z=0\n if (end.z + useMins.z <= 0) {\n return {\n allsolid: false,\n startsolid: false,\n fraction: 0,\n endpos: start,\n planeNormal: { x: 0, y: 0, z: 1 },\n contents: 1,\n };\n }\n\n // No collision - free movement\n return {\n allsolid: false,\n startsolid: false,\n fraction: 1.0,\n endpos: end,\n contents: 0,\n };\n};\n\n// -- Game Helpers --\n\nexport function createTestContext(options?: { seed?: number, initialEntities?: Entity[] }): { entities: EntitySystem, game: any } & SpawnContext {\n const engine = {\n sound: vi.fn(),\n soundIndex: vi.fn((sound: string) => 0),\n modelIndex: vi.fn((model: string) => 0),\n centerprintf: vi.fn(),\n };\n\n const seed = options?.seed ?? 12345;\n const traceFn = vi.fn((start: Vec3, end: Vec3, mins?: Vec3, maxs?: Vec3) => ({\n fraction: 1.0,\n ent: null,\n allsolid: false,\n startsolid: false,\n endpos: end, // Use end argument\n plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0 },\n surfaceFlags: 0,\n contents: 0\n }));\n\n const spawnRegistry = new SpawnRegistry();\n\n const game = {\n random: createRandomGenerator({ seed }),\n registerEntitySpawn: vi.fn((classname: string, spawnFunc: (entity: Entity) => void) => {\n spawnRegistry.register(classname, (entity) => spawnFunc(entity));\n }),\n unregisterEntitySpawn: vi.fn((classname: string) => {\n spawnRegistry.unregister(classname);\n }),\n getCustomEntities: vi.fn(() => Array.from(spawnRegistry.keys()))\n };\n\n const entityList: Entity[] = options?.initialEntities ? [...options.initialEntities] : [];\n\n const entities = {\n spawn: vi.fn(() => {\n const ent = new Entity(entityList.length + 1);\n entityList.push(ent);\n return ent;\n }),\n free: vi.fn((ent: Entity) => {\n const idx = entityList.indexOf(ent);\n if (idx !== -1) {\n entityList.splice(idx, 1);\n }\n }),\n finalizeSpawn: vi.fn(),\n freeImmediate: vi.fn((ent: Entity) => {\n const idx = entityList.indexOf(ent);\n if (idx !== -1) {\n entityList.splice(idx, 1);\n }\n }),\n setSpawnRegistry: vi.fn(),\n timeSeconds: 10,\n deltaSeconds: 0.1, // Added deltaSeconds\n modelIndex: vi.fn(() => 0),\n scheduleThink: vi.fn((entity: Entity, time: number) => {\n entity.nextthink = time;\n }),\n linkentity: vi.fn(),\n trace: traceFn, // Directly provide the mock function property\n pointcontents: vi.fn(() => 0),\n multicast: vi.fn(),\n unicast: vi.fn(),\n engine, // Attach mocked engine\n game,\n sound: vi.fn((ent: Entity, chan: number, sound: string, vol: number, attn: number, timeofs: number) => {\n engine.sound(ent, chan, sound, vol, attn, timeofs);\n }),\n soundIndex: vi.fn((sound: string) => engine.soundIndex(sound)),\n useTargets: vi.fn((entity: Entity, activator: Entity | null) => {\n }),\n findByTargetName: vi.fn(() => []),\n pickTarget: vi.fn(() => null),\n killBox: vi.fn(),\n rng: createRandomGenerator({ seed }), // Use real RNG for determinism or easy mocking if we replace it\n imports: {\n configstring: vi.fn(),\n trace: traceFn, // Also in imports for good measure\n pointcontents: vi.fn(() => 0),\n },\n level: {\n intermission_angle: { x: 0, y: 0, z: 0 },\n intermission_origin: { x: 0, y: 0, z: 0 },\n },\n targetNameIndex: new Map(),\n forEachEntity: vi.fn((callback: (ent: Entity) => void) => {\n // Iterate over managed list\n entityList.forEach(callback);\n }),\n find: vi.fn((predicate: (ent: Entity) => boolean) => {\n // Find in managed list\n return entityList.find(predicate);\n }),\n beginFrame: vi.fn((timeSeconds: number) => {\n (entities as any).timeSeconds = timeSeconds;\n }),\n targetAwareness: {\n timeSeconds: 10,\n frameNumber: 1,\n sightEntity: null,\n soundEntity: null,\n }\n } as unknown as EntitySystem;\n\n return {\n keyValues: {},\n entities,\n game: game,\n health_multiplier: 1,\n warn: vi.fn(),\n free: vi.fn(),\n // Legacy support for tests that might check precache\n precacheModel: vi.fn(),\n precacheSound: vi.fn(),\n precacheImage: vi.fn(),\n } as unknown as SpawnContext & { entities: EntitySystem, game: any };\n}\n\nexport function createSpawnContext(): SpawnContext {\n return createTestContext();\n}\n\nexport function createEntity(): Entity {\n return new Entity(1);\n}\n"],"mappings":"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","names":["vi"]}