quake2ts 0.0.402 → 0.0.405

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 (30) hide show
  1. package/package.json +1 -1
  2. package/packages/client/dist/browser/index.global.js +16 -16
  3. package/packages/client/dist/browser/index.global.js.map +1 -1
  4. package/packages/client/dist/cjs/index.cjs +383 -11
  5. package/packages/client/dist/cjs/index.cjs.map +1 -1
  6. package/packages/client/dist/esm/index.js +382 -11
  7. package/packages/client/dist/esm/index.js.map +1 -1
  8. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  9. package/packages/client/dist/types/demo/camera.d.ts +16 -0
  10. package/packages/client/dist/types/demo/camera.d.ts.map +1 -0
  11. package/packages/client/dist/types/index.d.ts +7 -1
  12. package/packages/client/dist/types/index.d.ts.map +1 -1
  13. package/packages/engine/dist/browser/index.global.js +16 -16
  14. package/packages/engine/dist/browser/index.global.js.map +1 -1
  15. package/packages/engine/dist/cjs/index.cjs +274 -2
  16. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  17. package/packages/engine/dist/esm/index.js +274 -2
  18. package/packages/engine/dist/esm/index.js.map +1 -1
  19. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  20. package/packages/engine/dist/types/commands.d.ts +3 -0
  21. package/packages/engine/dist/types/commands.d.ts.map +1 -1
  22. package/packages/engine/dist/types/cvars.d.ts +11 -0
  23. package/packages/engine/dist/types/cvars.d.ts.map +1 -1
  24. package/packages/engine/dist/types/demo/analysis.d.ts +33 -0
  25. package/packages/engine/dist/types/demo/analysis.d.ts.map +1 -1
  26. package/packages/engine/dist/types/demo/analyzer.d.ts +28 -0
  27. package/packages/engine/dist/types/demo/analyzer.d.ts.map +1 -0
  28. package/packages/engine/dist/types/demo/playback.d.ts +16 -1
  29. package/packages/engine/dist/types/demo/playback.d.ts.map +1 -1
  30. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
@@ -1821,6 +1821,9 @@ var CommandRegistry = class {
1821
1821
  this.commands.set(name, command);
1822
1822
  return command;
1823
1823
  }
1824
+ registerCommand(name, callback) {
1825
+ this.register(name, callback);
1826
+ }
1824
1827
  get(name) {
1825
1828
  return this.commands.get(name);
1826
1829
  }
@@ -1836,10 +1839,33 @@ var CommandRegistry = class {
1836
1839
  command.execute(args);
1837
1840
  return true;
1838
1841
  }
1842
+ this.onConsoleOutput?.(`Unknown command "${name}"`);
1839
1843
  return false;
1840
1844
  }
1845
+ executeCommand(cmd) {
1846
+ this.execute(cmd);
1847
+ }
1841
1848
  tokenize(text) {
1842
- return text.trim().split(/\s+/);
1849
+ const args = [];
1850
+ let currentArg = "";
1851
+ let inQuote = false;
1852
+ for (let i = 0; i < text.length; i++) {
1853
+ const char = text[i];
1854
+ if (char === '"') {
1855
+ inQuote = !inQuote;
1856
+ } else if (char === " " && !inQuote) {
1857
+ if (currentArg.length > 0) {
1858
+ args.push(currentArg);
1859
+ currentArg = "";
1860
+ }
1861
+ } else {
1862
+ currentArg += char;
1863
+ }
1864
+ }
1865
+ if (currentArg.length > 0) {
1866
+ args.push(currentArg);
1867
+ }
1868
+ return args;
1843
1869
  }
1844
1870
  list() {
1845
1871
  return [...this.commands.values()].sort((a, b) => a.name.localeCompare(b.name));
@@ -2924,13 +2950,21 @@ var CvarRegistry = class {
2924
2950
  if (existing) {
2925
2951
  return existing;
2926
2952
  }
2927
- const cvar = new Cvar(def);
2953
+ const originalOnChange = def.onChange;
2954
+ const wrappedOnChange = (cvar2, prev) => {
2955
+ originalOnChange?.(cvar2, prev);
2956
+ this.onCvarChange?.(cvar2.name, cvar2.string);
2957
+ };
2958
+ const cvar = new Cvar({ ...def, onChange: wrappedOnChange });
2928
2959
  this.cvars.set(def.name, cvar);
2929
2960
  return cvar;
2930
2961
  }
2931
2962
  get(name) {
2932
2963
  return this.cvars.get(name);
2933
2964
  }
2965
+ getCvar(name) {
2966
+ return this.get(name);
2967
+ }
2934
2968
  setValue(name, value) {
2935
2969
  const cvar = this.get(name);
2936
2970
  if (!cvar) {
@@ -2939,6 +2973,9 @@ var CvarRegistry = class {
2939
2973
  cvar.set(value);
2940
2974
  return cvar;
2941
2975
  }
2976
+ setCvar(name, value) {
2977
+ this.setValue(name, value);
2978
+ }
2942
2979
  resetAll() {
2943
2980
  for (const cvar of this.cvars.values()) {
2944
2981
  cvar.reset();
@@ -2954,6 +2991,15 @@ var CvarRegistry = class {
2954
2991
  list() {
2955
2992
  return [...this.cvars.values()].sort((a, b) => a.name.localeCompare(b.name));
2956
2993
  }
2994
+ listCvars() {
2995
+ return this.list().map((cvar) => ({
2996
+ name: cvar.name,
2997
+ value: cvar.string,
2998
+ defaultValue: cvar.defaultValue,
2999
+ flags: cvar.flags,
3000
+ description: cvar.description
3001
+ }));
3002
+ }
2957
3003
  };
2958
3004
  var EngineHost = class {
2959
3005
  constructor(game, client, options = {}) {
@@ -8762,6 +8808,162 @@ var NetworkMessageParser = class _NetworkMessageParser {
8762
8808
  }
8763
8809
  }
8764
8810
  };
8811
+ var DemoAnalyzer = class {
8812
+ constructor(buffer) {
8813
+ this.events = [];
8814
+ this.summary = {
8815
+ totalKills: 0,
8816
+ totalDeaths: 0,
8817
+ damageDealt: 0,
8818
+ damageReceived: 0,
8819
+ weaponUsage: /* @__PURE__ */ new Map()
8820
+ };
8821
+ this.header = null;
8822
+ this.configStrings = /* @__PURE__ */ new Map();
8823
+ this.serverInfo = {};
8824
+ this.statistics = null;
8825
+ this.playerStats = /* @__PURE__ */ new Map();
8826
+ this.weaponStats = /* @__PURE__ */ new Map();
8827
+ this.buffer = buffer;
8828
+ }
8829
+ analyze() {
8830
+ const reader = new DemoReader(this.buffer);
8831
+ let currentFrameIndex = -1;
8832
+ let currentTime = 0;
8833
+ let frameDuration = 0.1;
8834
+ let protocolVersion = 0;
8835
+ const handler = {
8836
+ onServerData: (protocol, serverCount, attractLoop, gameDir, playerNum, levelName, tickRate, demoType) => {
8837
+ protocolVersion = protocol;
8838
+ this.header = {
8839
+ protocolVersion: protocol,
8840
+ gameDir,
8841
+ levelName,
8842
+ playerNum,
8843
+ serverCount,
8844
+ spawnCount: serverCount,
8845
+ // Mapping generic arg
8846
+ tickRate,
8847
+ demoType
8848
+ };
8849
+ if (tickRate && tickRate > 0) {
8850
+ frameDuration = 1 / tickRate;
8851
+ }
8852
+ },
8853
+ onConfigString: (index, str3) => {
8854
+ this.configStrings.set(index, str3);
8855
+ if (index === 0) {
8856
+ this.parseServerInfo(str3);
8857
+ }
8858
+ },
8859
+ onSpawnBaseline: (entity) => {
8860
+ },
8861
+ onFrame: (frame) => {
8862
+ },
8863
+ onPrint: (level, msg) => {
8864
+ if (msg.includes("died") || msg.includes("killed")) {
8865
+ this.summary.totalDeaths++;
8866
+ this.recordEvent({
8867
+ type: 4,
8868
+ frame: currentFrameIndex,
8869
+ time: currentTime,
8870
+ description: msg.trim()
8871
+ });
8872
+ }
8873
+ },
8874
+ onCenterPrint: () => {
8875
+ },
8876
+ onStuffText: () => {
8877
+ },
8878
+ onSound: () => {
8879
+ },
8880
+ onTempEntity: () => {
8881
+ },
8882
+ onLayout: () => {
8883
+ },
8884
+ onInventory: () => {
8885
+ },
8886
+ onMuzzleFlash: (ent, weapon) => {
8887
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
8888
+ },
8889
+ onMuzzleFlash2: (ent, weapon) => {
8890
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
8891
+ },
8892
+ onMuzzleFlash3: (ent, weapon) => {
8893
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
8894
+ },
8895
+ onDisconnect: () => {
8896
+ },
8897
+ onReconnect: () => {
8898
+ },
8899
+ onDownload: () => {
8900
+ },
8901
+ // Rerelease specific
8902
+ onDamage: (indicators) => {
8903
+ for (const ind of indicators) {
8904
+ this.recordEvent({
8905
+ type: 2,
8906
+ frame: currentFrameIndex,
8907
+ time: currentTime,
8908
+ value: ind.damage,
8909
+ position: ind.dir,
8910
+ description: `Took ${ind.damage} damage`
8911
+ });
8912
+ this.summary.damageReceived += ind.damage;
8913
+ }
8914
+ }
8915
+ };
8916
+ while (reader.hasMore()) {
8917
+ const block = reader.readNextBlock();
8918
+ if (!block) break;
8919
+ currentFrameIndex++;
8920
+ currentTime = currentFrameIndex * frameDuration;
8921
+ const parser = new NetworkMessageParser(block.data, handler);
8922
+ parser.setProtocolVersion(protocolVersion);
8923
+ parser.parseMessage();
8924
+ protocolVersion = parser.getProtocolVersion();
8925
+ }
8926
+ this.statistics = {
8927
+ duration: currentTime,
8928
+ frameCount: currentFrameIndex + 1,
8929
+ averageFps: (currentFrameIndex + 1) / (currentTime || 1),
8930
+ mapName: this.header?.levelName || "unknown",
8931
+ playerCount: 1
8932
+ // Default to 1 for SP/client demo
8933
+ };
8934
+ return {
8935
+ events: this.events,
8936
+ summary: this.summary,
8937
+ header: this.header,
8938
+ configStrings: this.configStrings,
8939
+ serverInfo: this.serverInfo,
8940
+ statistics: this.statistics
8941
+ };
8942
+ }
8943
+ handleWeaponFire(ent, weapon, frame, time) {
8944
+ this.recordEvent({
8945
+ type: 0,
8946
+ frame,
8947
+ time,
8948
+ entityId: ent,
8949
+ value: weapon,
8950
+ description: `Weapon ${weapon} fired by ${ent}`
8951
+ });
8952
+ const count = this.summary.weaponUsage.get(weapon) || 0;
8953
+ this.summary.weaponUsage.set(weapon, count + 1);
8954
+ }
8955
+ recordEvent(event) {
8956
+ this.events.push(event);
8957
+ }
8958
+ parseServerInfo(str3) {
8959
+ const parts = str3.split("\\");
8960
+ for (let i = 1; i < parts.length; i += 2) {
8961
+ if (i + 1 < parts.length) {
8962
+ this.serverInfo[parts[i]] = parts[i + 1];
8963
+ }
8964
+ }
8965
+ }
8966
+ };
8765
8967
  var PlaybackState = /* @__PURE__ */ ((PlaybackState22) => {
8766
8968
  PlaybackState22[PlaybackState22["Stopped"] = 0] = "Stopped";
8767
8969
  PlaybackState22[PlaybackState22["Playing"] = 1] = "Playing";
@@ -8772,6 +8974,7 @@ var PlaybackState = /* @__PURE__ */ ((PlaybackState22) => {
8772
8974
  var DemoPlaybackController = class {
8773
8975
  constructor() {
8774
8976
  this.reader = null;
8977
+ this.buffer = null;
8775
8978
  this.state = 0;
8776
8979
  this.playbackSpeed = 1;
8777
8980
  this.currentProtocolVersion = 0;
@@ -8781,6 +8984,12 @@ var DemoPlaybackController = class {
8781
8984
  this.frameDuration = 100;
8782
8985
  this.snapshotInterval = 100;
8783
8986
  this.snapshots = /* @__PURE__ */ new Map();
8987
+ this.cachedEvents = null;
8988
+ this.cachedSummary = null;
8989
+ this.cachedHeader = null;
8990
+ this.cachedConfigStrings = null;
8991
+ this.cachedServerInfo = null;
8992
+ this.cachedStatistics = null;
8784
8993
  }
8785
8994
  setHandler(handler) {
8786
8995
  this.handler = handler;
@@ -8789,6 +8998,7 @@ var DemoPlaybackController = class {
8789
8998
  this.callbacks = callbacks;
8790
8999
  }
8791
9000
  loadDemo(buffer) {
9001
+ this.buffer = buffer;
8792
9002
  this.reader = new DemoReader(buffer);
8793
9003
  this.transitionState(
8794
9004
  0
@@ -8799,6 +9009,12 @@ var DemoPlaybackController = class {
8799
9009
  this.currentFrameIndex = -1;
8800
9010
  this.snapshots.clear();
8801
9011
  this.lastFrameData = null;
9012
+ this.cachedEvents = null;
9013
+ this.cachedSummary = null;
9014
+ this.cachedHeader = null;
9015
+ this.cachedConfigStrings = null;
9016
+ this.cachedServerInfo = null;
9017
+ this.cachedStatistics = null;
8802
9018
  }
8803
9019
  play() {
8804
9020
  if (this.reader && this.state !== 1) {
@@ -9126,6 +9342,57 @@ var DemoPlaybackController = class {
9126
9342
  this.seek(originalFrame);
9127
9343
  return trajectory;
9128
9344
  }
9345
+ // 3.2.3 Event Log Extraction & 3.3 Metadata
9346
+ getDemoEvents() {
9347
+ this.ensureAnalysis();
9348
+ return this.cachedEvents || [];
9349
+ }
9350
+ filterEvents(type, entityId) {
9351
+ const events = this.getDemoEvents();
9352
+ return events.filter((e) => {
9353
+ if (e.type !== type) return false;
9354
+ if (entityId !== void 0 && e.entityId !== entityId) return false;
9355
+ return true;
9356
+ });
9357
+ }
9358
+ getEventSummary() {
9359
+ this.ensureAnalysis();
9360
+ return this.cachedSummary || {
9361
+ totalKills: 0,
9362
+ totalDeaths: 0,
9363
+ damageDealt: 0,
9364
+ damageReceived: 0,
9365
+ weaponUsage: /* @__PURE__ */ new Map()
9366
+ };
9367
+ }
9368
+ getDemoHeader() {
9369
+ this.ensureAnalysis();
9370
+ return this.cachedHeader;
9371
+ }
9372
+ getDemoConfigStrings() {
9373
+ this.ensureAnalysis();
9374
+ return this.cachedConfigStrings || /* @__PURE__ */ new Map();
9375
+ }
9376
+ getDemoServerInfo() {
9377
+ this.ensureAnalysis();
9378
+ return this.cachedServerInfo || {};
9379
+ }
9380
+ getDemoStatistics() {
9381
+ this.ensureAnalysis();
9382
+ return this.cachedStatistics;
9383
+ }
9384
+ ensureAnalysis() {
9385
+ if (!this.cachedEvents && this.buffer) {
9386
+ const analyzer = new DemoAnalyzer(this.buffer);
9387
+ const result = analyzer.analyze();
9388
+ this.cachedEvents = result.events;
9389
+ this.cachedSummary = result.summary;
9390
+ this.cachedHeader = result.header;
9391
+ this.cachedConfigStrings = result.configStrings;
9392
+ this.cachedServerInfo = result.serverInfo;
9393
+ this.cachedStatistics = result.statistics;
9394
+ }
9395
+ }
9129
9396
  };
9130
9397
  var DemoRecorder = class {
9131
9398
  // -1 means start of demo
@@ -9205,12 +9472,56 @@ var __export3 = (target, all) => {
9205
9472
  };
9206
9473
  var ZERO_VEC3 = { x: 0, y: 0, z: 0 };
9207
9474
  var DEG_TO_RAD2 = Math.PI / 180;
9475
+ var PITCH = 0;
9476
+ var YAW = 1;
9477
+ var ROLL = 2;
9208
9478
  var DEG2RAD_FACTOR2 = Math.PI / 180;
9209
9479
  var RAD2DEG_FACTOR2 = 180 / Math.PI;
9480
+ function axisComponent(vec, axis) {
9481
+ switch (axis) {
9482
+ case PITCH:
9483
+ return vec.x;
9484
+ case YAW:
9485
+ return vec.y;
9486
+ case ROLL:
9487
+ default:
9488
+ return vec.z;
9489
+ }
9490
+ }
9491
+ function degToRad(degrees) {
9492
+ return degrees * DEG2RAD_FACTOR2;
9493
+ }
9210
9494
  function angleMod(angle2) {
9211
9495
  const value = angle2 % 360;
9212
9496
  return value < 0 ? 360 + value : value;
9213
9497
  }
9498
+ function angleVectors(angles) {
9499
+ const yaw = degToRad(axisComponent(angles, YAW));
9500
+ const pitch = degToRad(axisComponent(angles, PITCH));
9501
+ const roll = degToRad(axisComponent(angles, ROLL));
9502
+ const sy = Math.sin(yaw);
9503
+ const cy = Math.cos(yaw);
9504
+ const sp = Math.sin(pitch);
9505
+ const cp = Math.cos(pitch);
9506
+ const sr = Math.sin(roll);
9507
+ const cr = Math.cos(roll);
9508
+ const forward = {
9509
+ x: cp * cy,
9510
+ y: cp * sy,
9511
+ z: -sp
9512
+ };
9513
+ const right = {
9514
+ x: -sr * sp * cy - cr * -sy,
9515
+ y: -sr * sp * sy - cr * cy,
9516
+ z: -sr * cp
9517
+ };
9518
+ const up = {
9519
+ x: cr * sp * cy - sr * -sy,
9520
+ y: cr * sp * sy - sr * cy,
9521
+ z: cr * cp
9522
+ };
9523
+ return { forward, right, up };
9524
+ }
9214
9525
  var ANORMS2 = [
9215
9526
  [-0.525731, 0, 0.850651],
9216
9527
  [-0.442863, 0.238856, 0.864188],
@@ -13196,6 +13507,15 @@ var DemoControls = class {
13196
13507
  }
13197
13508
  };
13198
13509
 
13510
+ // src/demo/camera.ts
13511
+ var DemoCameraMode = /* @__PURE__ */ ((DemoCameraMode2) => {
13512
+ DemoCameraMode2[DemoCameraMode2["FirstPerson"] = 0] = "FirstPerson";
13513
+ DemoCameraMode2[DemoCameraMode2["ThirdPerson"] = 1] = "ThirdPerson";
13514
+ DemoCameraMode2[DemoCameraMode2["Free"] = 2] = "Free";
13515
+ DemoCameraMode2[DemoCameraMode2["Follow"] = 3] = "Follow";
13516
+ return DemoCameraMode2;
13517
+ })(DemoCameraMode || {});
13518
+
13199
13519
  // src/effects.ts
13200
13520
  function addDLight(dlights, origin, color, intensity, minLight = 0, die = 0) {
13201
13521
  dlights.push({
@@ -13893,6 +14213,14 @@ function createClient(imports) {
13893
14213
  let isDemoPlaying = false;
13894
14214
  let currentDemoName = null;
13895
14215
  let clientMode = 0 /* Normal */;
14216
+ const demoCameraState = {
14217
+ mode: 0 /* FirstPerson */,
14218
+ thirdPersonDistance: 80,
14219
+ thirdPersonOffset: { x: 0, y: 0, z: 0 },
14220
+ freeCameraOrigin: { x: 0, y: 0, z: 0 },
14221
+ freeCameraAngles: { x: 0, y: 0, z: 0 },
14222
+ followEntityId: -1
14223
+ };
13896
14224
  const menuSystem = new MenuSystem();
13897
14225
  const loadingScreen = new LoadingScreen();
13898
14226
  const errorDialog = new ErrorDialog();
@@ -14107,6 +14435,8 @@ function createClient(imports) {
14107
14435
  },
14108
14436
  handleInput(key, down) {
14109
14437
  if (isDemoPlaying) {
14438
+ if (demoCameraState.mode === 2 /* Free */) {
14439
+ }
14110
14440
  if (demoControls.handleInput(key, down)) {
14111
14441
  return true;
14112
14442
  }
@@ -14192,18 +14522,42 @@ function createClient(imports) {
14192
14522
  lastRenderTime = now;
14193
14523
  demoPlayback.update(dt);
14194
14524
  lastRendered = demoHandler.getPredictionState(demoPlayback.getCurrentTime());
14195
- const frameDuration = 100;
14196
14525
  renderEntities = demoHandler.getRenderableEntities(1, configStrings);
14197
14526
  if (demoHandler.latestFrame && demoHandler.latestFrame.packetEntities) {
14198
14527
  currentPacketEntities = demoHandler.latestFrame.packetEntities.entities;
14199
14528
  }
14200
14529
  if (lastRendered) {
14201
14530
  const demoCamera = demoHandler.getDemoCamera(1);
14202
- if (demoCamera) {
14203
- lastRendered.origin = demoCamera.origin;
14204
- lastRendered.viewAngles = demoCamera.angles;
14205
- if (demoCamera.fov) {
14206
- lastRendered.fov = demoCamera.fov;
14531
+ if (demoCameraState.mode === 0 /* FirstPerson */) {
14532
+ if (demoCamera) {
14533
+ lastRendered.origin = demoCamera.origin;
14534
+ lastRendered.viewAngles = demoCamera.angles;
14535
+ if (demoCamera.fov) {
14536
+ lastRendered.fov = demoCamera.fov;
14537
+ }
14538
+ }
14539
+ } else if (demoCameraState.mode === 1 /* ThirdPerson */) {
14540
+ if (demoCamera) {
14541
+ const vectors = angleVectors(demoCamera.angles);
14542
+ const forward = vectors.forward;
14543
+ const dist2 = demoCameraState.thirdPersonDistance;
14544
+ const camOrigin = { ...demoCamera.origin };
14545
+ camOrigin.x -= forward.x * dist2;
14546
+ camOrigin.y -= forward.y * dist2;
14547
+ camOrigin.z -= forward.z * dist2;
14548
+ lastRendered.origin = camOrigin;
14549
+ lastRendered.viewAngles = demoCamera.angles;
14550
+ }
14551
+ } else if (demoCameraState.mode === 2 /* Free */) {
14552
+ lastRendered.origin = demoCameraState.freeCameraOrigin;
14553
+ lastRendered.viewAngles = demoCameraState.freeCameraAngles;
14554
+ } else if (demoCameraState.mode === 3 /* Follow */) {
14555
+ if (demoCameraState.followEntityId !== -1) {
14556
+ const ent = renderEntities.find((e) => e.id === demoCameraState.followEntityId);
14557
+ if (ent) {
14558
+ const mat = ent.transform;
14559
+ lastRendered.origin = { x: mat[12], y: mat[13], z: mat[14] };
14560
+ }
14207
14561
  }
14208
14562
  }
14209
14563
  }
@@ -14243,8 +14597,10 @@ function createClient(imports) {
14243
14597
  const { origin, viewAngles } = lastRendered;
14244
14598
  camera = new Camera();
14245
14599
  camera.position = vec3_exports.fromValues(origin.x, origin.y, origin.z);
14246
- const viewOffset = lastView?.offset ?? { x: 0, y: 0, z: 0 };
14247
- vec3_exports.add(camera.position, camera.position, [viewOffset.x, viewOffset.y, viewOffset.z]);
14600
+ if (!isDemoPlaying || demoCameraState.mode === 0 /* FirstPerson */) {
14601
+ const viewOffset = lastView?.offset ?? { x: 0, y: 0, z: 0 };
14602
+ vec3_exports.add(camera.position, camera.position, [viewOffset.x, viewOffset.y, viewOffset.z]);
14603
+ }
14248
14604
  const effectAngles = lastView?.angles ?? { x: 0, y: 0, z: 0 };
14249
14605
  camera.angles = vec3_exports.fromValues(viewAngles.x + effectAngles.x, viewAngles.y + effectAngles.y, viewAngles.z + effectAngles.z);
14250
14606
  if (isDemoPlaying && lastRendered.fov) {
@@ -14477,7 +14833,21 @@ function createClient(imports) {
14477
14833
  },
14478
14834
  demoHandler,
14479
14835
  multiplayer,
14480
- configStrings
14836
+ configStrings,
14837
+ // Demo Camera API
14838
+ setDemoCameraMode(mode) {
14839
+ demoCameraState.mode = mode;
14840
+ },
14841
+ setDemoThirdPersonDistance(dist2) {
14842
+ demoCameraState.thirdPersonDistance = dist2;
14843
+ },
14844
+ setDemoThirdPersonOffset(offset) {
14845
+ demoCameraState.thirdPersonOffset = offset;
14846
+ },
14847
+ setDemoFreeCamera(origin, angles) {
14848
+ demoCameraState.freeCameraOrigin = origin;
14849
+ demoCameraState.freeCameraAngles = angles;
14850
+ }
14481
14851
  };
14482
14852
  return clientExports;
14483
14853
  }
@@ -14485,6 +14855,7 @@ export {
14485
14855
  ClientConfigStrings,
14486
14856
  ClientMode,
14487
14857
  ClientPrediction3 as ClientPrediction,
14858
+ DemoCameraMode,
14488
14859
  GameSession,
14489
14860
  InputAction,
14490
14861
  InputBindings,