quake2ts 0.0.403 → 0.0.407
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 +1 -1
- package/packages/client/dist/browser/index.global.js +16 -16
- package/packages/client/dist/browser/index.global.js.map +1 -1
- package/packages/client/dist/cjs/index.cjs +405 -13
- package/packages/client/dist/cjs/index.cjs.map +1 -1
- package/packages/client/dist/esm/index.js +404 -13
- package/packages/client/dist/esm/index.js.map +1 -1
- package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/client/dist/types/demo/camera.d.ts +16 -0
- package/packages/client/dist/types/demo/camera.d.ts.map +1 -0
- package/packages/client/dist/types/index.d.ts +7 -1
- package/packages/client/dist/types/index.d.ts.map +1 -1
- package/packages/client/dist/types/ui/demo-controls.d.ts +2 -1
- package/packages/client/dist/types/ui/demo-controls.d.ts.map +1 -1
- package/packages/engine/dist/browser/index.global.js +16 -16
- package/packages/engine/dist/browser/index.global.js.map +1 -1
- package/packages/engine/dist/cjs/index.cjs +362 -4
- package/packages/engine/dist/cjs/index.cjs.map +1 -1
- package/packages/engine/dist/esm/index.js +361 -4
- package/packages/engine/dist/esm/index.js.map +1 -1
- package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/engine/dist/types/assets/mapStatistics.d.ts +20 -0
- package/packages/engine/dist/types/assets/mapStatistics.d.ts.map +1 -0
- package/packages/engine/dist/types/audio/api.d.ts +1 -0
- package/packages/engine/dist/types/audio/api.d.ts.map +1 -1
- package/packages/engine/dist/types/audio/context.d.ts +1 -0
- package/packages/engine/dist/types/audio/context.d.ts.map +1 -1
- package/packages/engine/dist/types/audio/system.d.ts +3 -0
- package/packages/engine/dist/types/audio/system.d.ts.map +1 -1
- package/packages/engine/dist/types/commands.d.ts +3 -0
- package/packages/engine/dist/types/commands.d.ts.map +1 -1
- package/packages/engine/dist/types/cvars.d.ts +11 -0
- package/packages/engine/dist/types/cvars.d.ts.map +1 -1
- package/packages/engine/dist/types/demo/analysis.d.ts +33 -0
- package/packages/engine/dist/types/demo/analysis.d.ts.map +1 -1
- package/packages/engine/dist/types/demo/analyzer.d.ts +28 -0
- package/packages/engine/dist/types/demo/analyzer.d.ts.map +1 -0
- package/packages/engine/dist/types/demo/playback.d.ts +16 -1
- package/packages/engine/dist/types/demo/playback.d.ts.map +1 -1
- package/packages/engine/dist/types/index.d.ts +1 -0
- package/packages/engine/dist/types/index.d.ts.map +1 -1
- package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
|
@@ -62,6 +62,7 @@ __export(index_exports, {
|
|
|
62
62
|
MD2_VERTEX_SHADER: () => MD2_VERTEX_SHADER,
|
|
63
63
|
MD3_FRAGMENT_SHADER: () => MD3_FRAGMENT_SHADER,
|
|
64
64
|
MD3_VERTEX_SHADER: () => MD3_VERTEX_SHADER,
|
|
65
|
+
MapAnalyzer: () => MapAnalyzer,
|
|
65
66
|
Md2Loader: () => Md2Loader,
|
|
66
67
|
Md2MeshBuffers: () => Md2MeshBuffers,
|
|
67
68
|
Md2ParseError: () => Md2ParseError,
|
|
@@ -295,6 +296,9 @@ var CommandRegistry = class {
|
|
|
295
296
|
this.commands.set(name, command);
|
|
296
297
|
return command;
|
|
297
298
|
}
|
|
299
|
+
registerCommand(name, callback) {
|
|
300
|
+
this.register(name, callback);
|
|
301
|
+
}
|
|
298
302
|
get(name) {
|
|
299
303
|
return this.commands.get(name);
|
|
300
304
|
}
|
|
@@ -310,10 +314,33 @@ var CommandRegistry = class {
|
|
|
310
314
|
command.execute(args);
|
|
311
315
|
return true;
|
|
312
316
|
}
|
|
317
|
+
this.onConsoleOutput?.(`Unknown command "${name}"`);
|
|
313
318
|
return false;
|
|
314
319
|
}
|
|
320
|
+
executeCommand(cmd) {
|
|
321
|
+
this.execute(cmd);
|
|
322
|
+
}
|
|
315
323
|
tokenize(text) {
|
|
316
|
-
|
|
324
|
+
const args = [];
|
|
325
|
+
let currentArg = "";
|
|
326
|
+
let inQuote = false;
|
|
327
|
+
for (let i = 0; i < text.length; i++) {
|
|
328
|
+
const char = text[i];
|
|
329
|
+
if (char === '"') {
|
|
330
|
+
inQuote = !inQuote;
|
|
331
|
+
} else if (char === " " && !inQuote) {
|
|
332
|
+
if (currentArg.length > 0) {
|
|
333
|
+
args.push(currentArg);
|
|
334
|
+
currentArg = "";
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
currentArg += char;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (currentArg.length > 0) {
|
|
341
|
+
args.push(currentArg);
|
|
342
|
+
}
|
|
343
|
+
return args;
|
|
317
344
|
}
|
|
318
345
|
list() {
|
|
319
346
|
return [...this.commands.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -1508,13 +1535,21 @@ var CvarRegistry = class {
|
|
|
1508
1535
|
if (existing) {
|
|
1509
1536
|
return existing;
|
|
1510
1537
|
}
|
|
1511
|
-
const
|
|
1538
|
+
const originalOnChange = def.onChange;
|
|
1539
|
+
const wrappedOnChange = (cvar2, prev) => {
|
|
1540
|
+
originalOnChange?.(cvar2, prev);
|
|
1541
|
+
this.onCvarChange?.(cvar2.name, cvar2.string);
|
|
1542
|
+
};
|
|
1543
|
+
const cvar = new Cvar({ ...def, onChange: wrappedOnChange });
|
|
1512
1544
|
this.cvars.set(def.name, cvar);
|
|
1513
1545
|
return cvar;
|
|
1514
1546
|
}
|
|
1515
1547
|
get(name) {
|
|
1516
1548
|
return this.cvars.get(name);
|
|
1517
1549
|
}
|
|
1550
|
+
getCvar(name) {
|
|
1551
|
+
return this.get(name);
|
|
1552
|
+
}
|
|
1518
1553
|
setValue(name, value) {
|
|
1519
1554
|
const cvar = this.get(name);
|
|
1520
1555
|
if (!cvar) {
|
|
@@ -1523,6 +1558,9 @@ var CvarRegistry = class {
|
|
|
1523
1558
|
cvar.set(value);
|
|
1524
1559
|
return cvar;
|
|
1525
1560
|
}
|
|
1561
|
+
setCvar(name, value) {
|
|
1562
|
+
this.setValue(name, value);
|
|
1563
|
+
}
|
|
1526
1564
|
resetAll() {
|
|
1527
1565
|
for (const cvar of this.cvars.values()) {
|
|
1528
1566
|
cvar.reset();
|
|
@@ -1538,6 +1576,15 @@ var CvarRegistry = class {
|
|
|
1538
1576
|
list() {
|
|
1539
1577
|
return [...this.cvars.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
1540
1578
|
}
|
|
1579
|
+
listCvars() {
|
|
1580
|
+
return this.list().map((cvar) => ({
|
|
1581
|
+
name: cvar.name,
|
|
1582
|
+
value: cvar.string,
|
|
1583
|
+
defaultValue: cvar.defaultValue,
|
|
1584
|
+
flags: cvar.flags,
|
|
1585
|
+
description: cvar.description
|
|
1586
|
+
}));
|
|
1587
|
+
}
|
|
1541
1588
|
};
|
|
1542
1589
|
|
|
1543
1590
|
// src/host.ts
|
|
@@ -1738,6 +1785,9 @@ var AudioApi = class {
|
|
|
1738
1785
|
stop_entity_sounds(entnum) {
|
|
1739
1786
|
this.system.stopEntitySounds(entnum);
|
|
1740
1787
|
}
|
|
1788
|
+
setPlaybackRate(rate) {
|
|
1789
|
+
this.system.setPlaybackRate(rate);
|
|
1790
|
+
}
|
|
1741
1791
|
set_listener(listener) {
|
|
1742
1792
|
this.system.setListener(listener);
|
|
1743
1793
|
}
|
|
@@ -4560,6 +4610,7 @@ function spatializeOrigin(origin, listener, masterVolume, attenuation, isListene
|
|
|
4560
4610
|
var AudioSystem = class {
|
|
4561
4611
|
constructor(options) {
|
|
4562
4612
|
this.activeSources = /* @__PURE__ */ new Map();
|
|
4613
|
+
this.playbackRate = 1;
|
|
4563
4614
|
this.contextController = options.context;
|
|
4564
4615
|
this.registry = options.registry;
|
|
4565
4616
|
this.playerEntity = options.playerEntity;
|
|
@@ -4581,6 +4632,15 @@ var AudioSystem = class {
|
|
|
4581
4632
|
setSfxVolume(volume) {
|
|
4582
4633
|
this.sfxVolume = volume;
|
|
4583
4634
|
}
|
|
4635
|
+
setPlaybackRate(rate) {
|
|
4636
|
+
this.playbackRate = rate;
|
|
4637
|
+
for (const active of this.activeSources.values()) {
|
|
4638
|
+
if (active.source.playbackRate) {
|
|
4639
|
+
active.source.playbackRate.value = rate;
|
|
4640
|
+
}
|
|
4641
|
+
this.updateSourceGain(active);
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4584
4644
|
async ensureRunning() {
|
|
4585
4645
|
await this.contextController.resume();
|
|
4586
4646
|
}
|
|
@@ -4603,6 +4663,9 @@ var AudioSystem = class {
|
|
|
4603
4663
|
const source = ctx.createBufferSource();
|
|
4604
4664
|
source.buffer = buffer;
|
|
4605
4665
|
source.loop = request.looping ?? false;
|
|
4666
|
+
if (source.playbackRate) {
|
|
4667
|
+
source.playbackRate.value = this.playbackRate;
|
|
4668
|
+
}
|
|
4606
4669
|
const origin = request.origin ?? this.listener.origin;
|
|
4607
4670
|
const gain = ctx.createGain();
|
|
4608
4671
|
const panner = this.createPanner(ctx, request.attenuation);
|
|
@@ -4614,7 +4677,8 @@ var AudioSystem = class {
|
|
|
4614
4677
|
const spatial = spatializeOrigin(origin, this.listener, request.volume, request.attenuation, isListenerSound);
|
|
4615
4678
|
const attenuationScale = request.volume === 0 ? 0 : Math.max(spatial.left, spatial.right) / Math.max(1, request.volume);
|
|
4616
4679
|
const gainValue = attenuationScale * (request.volume / 255) * this.masterVolume * this.sfxVolume;
|
|
4617
|
-
|
|
4680
|
+
const playbackRateMute = Math.abs(this.playbackRate - 1) < 1e-3 ? 1 : 0;
|
|
4681
|
+
gain.gain.value = gainValue * occlusionScale * playbackRateMute;
|
|
4618
4682
|
const startTimeSec = ctx.currentTime + (request.timeOffsetMs ?? 0) / 1e3;
|
|
4619
4683
|
const endTimeMs = (request.looping ? Number.POSITIVE_INFINITY : buffer.duration * 1e3) + startTimeSec * 1e3;
|
|
4620
4684
|
source.connect(panner);
|
|
@@ -4762,9 +4826,15 @@ var AudioSystem = class {
|
|
|
4762
4826
|
filter.frequency.value = clamp(cutoffHz, 10, 2e4);
|
|
4763
4827
|
return filter;
|
|
4764
4828
|
}
|
|
4829
|
+
updateSourceGain(active) {
|
|
4830
|
+
const occlusionScale = active.occlusion?.scale ?? 1;
|
|
4831
|
+
const playbackRateMute = Math.abs(this.playbackRate - 1) < 1e-3 ? 1 : 0;
|
|
4832
|
+
active.gain.gain.value = active.baseGain * occlusionScale * playbackRateMute;
|
|
4833
|
+
}
|
|
4765
4834
|
applyOcclusion(active, occlusion) {
|
|
4766
4835
|
const scale = clamp01(occlusion?.gainScale ?? 1);
|
|
4767
|
-
|
|
4836
|
+
const playbackRateMute = Math.abs(this.playbackRate - 1) < 1e-3 ? 1 : 0;
|
|
4837
|
+
active.gain.gain.value = active.baseGain * scale * playbackRateMute;
|
|
4768
4838
|
if (active.occlusion?.filter) {
|
|
4769
4839
|
const cutoff = occlusion?.lowpassHz ?? 2e4;
|
|
4770
4840
|
active.occlusion.filter.frequency.value = clamp(cutoff, 10, 2e4);
|
|
@@ -12627,6 +12697,165 @@ var NetworkMessageParser = class _NetworkMessageParser {
|
|
|
12627
12697
|
}
|
|
12628
12698
|
};
|
|
12629
12699
|
|
|
12700
|
+
// src/demo/analyzer.ts
|
|
12701
|
+
var DemoAnalyzer = class {
|
|
12702
|
+
constructor(buffer) {
|
|
12703
|
+
this.events = [];
|
|
12704
|
+
this.summary = {
|
|
12705
|
+
totalKills: 0,
|
|
12706
|
+
totalDeaths: 0,
|
|
12707
|
+
damageDealt: 0,
|
|
12708
|
+
damageReceived: 0,
|
|
12709
|
+
weaponUsage: /* @__PURE__ */ new Map()
|
|
12710
|
+
};
|
|
12711
|
+
this.header = null;
|
|
12712
|
+
this.configStrings = /* @__PURE__ */ new Map();
|
|
12713
|
+
this.serverInfo = {};
|
|
12714
|
+
this.statistics = null;
|
|
12715
|
+
this.playerStats = /* @__PURE__ */ new Map();
|
|
12716
|
+
// By playerNum
|
|
12717
|
+
this.weaponStats = /* @__PURE__ */ new Map();
|
|
12718
|
+
this.buffer = buffer;
|
|
12719
|
+
}
|
|
12720
|
+
analyze() {
|
|
12721
|
+
const reader = new DemoReader(this.buffer);
|
|
12722
|
+
let currentFrameIndex = -1;
|
|
12723
|
+
let currentTime = 0;
|
|
12724
|
+
let frameDuration = 0.1;
|
|
12725
|
+
let protocolVersion = 0;
|
|
12726
|
+
const handler = {
|
|
12727
|
+
onServerData: (protocol, serverCount, attractLoop, gameDir, playerNum, levelName, tickRate, demoType) => {
|
|
12728
|
+
protocolVersion = protocol;
|
|
12729
|
+
this.header = {
|
|
12730
|
+
protocolVersion: protocol,
|
|
12731
|
+
gameDir,
|
|
12732
|
+
levelName,
|
|
12733
|
+
playerNum,
|
|
12734
|
+
serverCount,
|
|
12735
|
+
spawnCount: serverCount,
|
|
12736
|
+
// Mapping generic arg
|
|
12737
|
+
tickRate,
|
|
12738
|
+
demoType
|
|
12739
|
+
};
|
|
12740
|
+
if (tickRate && tickRate > 0) {
|
|
12741
|
+
frameDuration = 1 / tickRate;
|
|
12742
|
+
}
|
|
12743
|
+
},
|
|
12744
|
+
onConfigString: (index, str) => {
|
|
12745
|
+
this.configStrings.set(index, str);
|
|
12746
|
+
if (index === 0) {
|
|
12747
|
+
this.parseServerInfo(str);
|
|
12748
|
+
}
|
|
12749
|
+
},
|
|
12750
|
+
onSpawnBaseline: (entity) => {
|
|
12751
|
+
},
|
|
12752
|
+
onFrame: (frame) => {
|
|
12753
|
+
},
|
|
12754
|
+
onPrint: (level, msg) => {
|
|
12755
|
+
if (msg.includes("died") || msg.includes("killed")) {
|
|
12756
|
+
this.summary.totalDeaths++;
|
|
12757
|
+
this.recordEvent({
|
|
12758
|
+
type: 4 /* Death */,
|
|
12759
|
+
frame: currentFrameIndex,
|
|
12760
|
+
time: currentTime,
|
|
12761
|
+
description: msg.trim()
|
|
12762
|
+
});
|
|
12763
|
+
}
|
|
12764
|
+
},
|
|
12765
|
+
onCenterPrint: () => {
|
|
12766
|
+
},
|
|
12767
|
+
onStuffText: () => {
|
|
12768
|
+
},
|
|
12769
|
+
onSound: () => {
|
|
12770
|
+
},
|
|
12771
|
+
onTempEntity: () => {
|
|
12772
|
+
},
|
|
12773
|
+
onLayout: () => {
|
|
12774
|
+
},
|
|
12775
|
+
onInventory: () => {
|
|
12776
|
+
},
|
|
12777
|
+
onMuzzleFlash: (ent, weapon) => {
|
|
12778
|
+
this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
|
|
12779
|
+
},
|
|
12780
|
+
onMuzzleFlash2: (ent, weapon) => {
|
|
12781
|
+
this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
|
|
12782
|
+
},
|
|
12783
|
+
onMuzzleFlash3: (ent, weapon) => {
|
|
12784
|
+
this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
|
|
12785
|
+
},
|
|
12786
|
+
onDisconnect: () => {
|
|
12787
|
+
},
|
|
12788
|
+
onReconnect: () => {
|
|
12789
|
+
},
|
|
12790
|
+
onDownload: () => {
|
|
12791
|
+
},
|
|
12792
|
+
// Rerelease specific
|
|
12793
|
+
onDamage: (indicators) => {
|
|
12794
|
+
for (const ind of indicators) {
|
|
12795
|
+
this.recordEvent({
|
|
12796
|
+
type: 2 /* DamageReceived */,
|
|
12797
|
+
frame: currentFrameIndex,
|
|
12798
|
+
time: currentTime,
|
|
12799
|
+
value: ind.damage,
|
|
12800
|
+
position: ind.dir,
|
|
12801
|
+
description: `Took ${ind.damage} damage`
|
|
12802
|
+
});
|
|
12803
|
+
this.summary.damageReceived += ind.damage;
|
|
12804
|
+
}
|
|
12805
|
+
}
|
|
12806
|
+
};
|
|
12807
|
+
while (reader.hasMore()) {
|
|
12808
|
+
const block = reader.readNextBlock();
|
|
12809
|
+
if (!block) break;
|
|
12810
|
+
currentFrameIndex++;
|
|
12811
|
+
currentTime = currentFrameIndex * frameDuration;
|
|
12812
|
+
const parser = new NetworkMessageParser(block.data, handler);
|
|
12813
|
+
parser.setProtocolVersion(protocolVersion);
|
|
12814
|
+
parser.parseMessage();
|
|
12815
|
+
protocolVersion = parser.getProtocolVersion();
|
|
12816
|
+
}
|
|
12817
|
+
this.statistics = {
|
|
12818
|
+
duration: currentTime,
|
|
12819
|
+
frameCount: currentFrameIndex + 1,
|
|
12820
|
+
averageFps: (currentFrameIndex + 1) / (currentTime || 1),
|
|
12821
|
+
mapName: this.header?.levelName || "unknown",
|
|
12822
|
+
playerCount: 1
|
|
12823
|
+
// Default to 1 for SP/client demo
|
|
12824
|
+
};
|
|
12825
|
+
return {
|
|
12826
|
+
events: this.events,
|
|
12827
|
+
summary: this.summary,
|
|
12828
|
+
header: this.header,
|
|
12829
|
+
configStrings: this.configStrings,
|
|
12830
|
+
serverInfo: this.serverInfo,
|
|
12831
|
+
statistics: this.statistics
|
|
12832
|
+
};
|
|
12833
|
+
}
|
|
12834
|
+
handleWeaponFire(ent, weapon, frame, time) {
|
|
12835
|
+
this.recordEvent({
|
|
12836
|
+
type: 0 /* WeaponFire */,
|
|
12837
|
+
frame,
|
|
12838
|
+
time,
|
|
12839
|
+
entityId: ent,
|
|
12840
|
+
value: weapon,
|
|
12841
|
+
description: `Weapon ${weapon} fired by ${ent}`
|
|
12842
|
+
});
|
|
12843
|
+
const count = this.summary.weaponUsage.get(weapon) || 0;
|
|
12844
|
+
this.summary.weaponUsage.set(weapon, count + 1);
|
|
12845
|
+
}
|
|
12846
|
+
recordEvent(event) {
|
|
12847
|
+
this.events.push(event);
|
|
12848
|
+
}
|
|
12849
|
+
parseServerInfo(str) {
|
|
12850
|
+
const parts = str.split("\\");
|
|
12851
|
+
for (let i = 1; i < parts.length; i += 2) {
|
|
12852
|
+
if (i + 1 < parts.length) {
|
|
12853
|
+
this.serverInfo[parts[i]] = parts[i + 1];
|
|
12854
|
+
}
|
|
12855
|
+
}
|
|
12856
|
+
}
|
|
12857
|
+
};
|
|
12858
|
+
|
|
12630
12859
|
// src/demo/playback.ts
|
|
12631
12860
|
var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
|
|
12632
12861
|
PlaybackState2[PlaybackState2["Stopped"] = 0] = "Stopped";
|
|
@@ -12638,6 +12867,8 @@ var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
|
|
|
12638
12867
|
var DemoPlaybackController = class {
|
|
12639
12868
|
constructor() {
|
|
12640
12869
|
this.reader = null;
|
|
12870
|
+
this.buffer = null;
|
|
12871
|
+
// Keep reference for analysis
|
|
12641
12872
|
this.state = 0 /* Stopped */;
|
|
12642
12873
|
this.playbackSpeed = 1;
|
|
12643
12874
|
this.currentProtocolVersion = 0;
|
|
@@ -12653,6 +12884,13 @@ var DemoPlaybackController = class {
|
|
|
12653
12884
|
this.snapshotInterval = 100;
|
|
12654
12885
|
// frames
|
|
12655
12886
|
this.snapshots = /* @__PURE__ */ new Map();
|
|
12887
|
+
// Analysis Cache
|
|
12888
|
+
this.cachedEvents = null;
|
|
12889
|
+
this.cachedSummary = null;
|
|
12890
|
+
this.cachedHeader = null;
|
|
12891
|
+
this.cachedConfigStrings = null;
|
|
12892
|
+
this.cachedServerInfo = null;
|
|
12893
|
+
this.cachedStatistics = null;
|
|
12656
12894
|
}
|
|
12657
12895
|
setHandler(handler) {
|
|
12658
12896
|
this.handler = handler;
|
|
@@ -12661,6 +12899,7 @@ var DemoPlaybackController = class {
|
|
|
12661
12899
|
this.callbacks = callbacks;
|
|
12662
12900
|
}
|
|
12663
12901
|
loadDemo(buffer) {
|
|
12902
|
+
this.buffer = buffer;
|
|
12664
12903
|
this.reader = new DemoReader(buffer);
|
|
12665
12904
|
this.transitionState(0 /* Stopped */);
|
|
12666
12905
|
this.accumulatedTime = 0;
|
|
@@ -12668,6 +12907,12 @@ var DemoPlaybackController = class {
|
|
|
12668
12907
|
this.currentFrameIndex = -1;
|
|
12669
12908
|
this.snapshots.clear();
|
|
12670
12909
|
this.lastFrameData = null;
|
|
12910
|
+
this.cachedEvents = null;
|
|
12911
|
+
this.cachedSummary = null;
|
|
12912
|
+
this.cachedHeader = null;
|
|
12913
|
+
this.cachedConfigStrings = null;
|
|
12914
|
+
this.cachedServerInfo = null;
|
|
12915
|
+
this.cachedStatistics = null;
|
|
12671
12916
|
}
|
|
12672
12917
|
play() {
|
|
12673
12918
|
if (this.reader && this.state !== 1 /* Playing */) {
|
|
@@ -12980,6 +13225,57 @@ var DemoPlaybackController = class {
|
|
|
12980
13225
|
this.seek(originalFrame);
|
|
12981
13226
|
return trajectory;
|
|
12982
13227
|
}
|
|
13228
|
+
// 3.2.3 Event Log Extraction & 3.3 Metadata
|
|
13229
|
+
getDemoEvents() {
|
|
13230
|
+
this.ensureAnalysis();
|
|
13231
|
+
return this.cachedEvents || [];
|
|
13232
|
+
}
|
|
13233
|
+
filterEvents(type, entityId) {
|
|
13234
|
+
const events = this.getDemoEvents();
|
|
13235
|
+
return events.filter((e) => {
|
|
13236
|
+
if (e.type !== type) return false;
|
|
13237
|
+
if (entityId !== void 0 && e.entityId !== entityId) return false;
|
|
13238
|
+
return true;
|
|
13239
|
+
});
|
|
13240
|
+
}
|
|
13241
|
+
getEventSummary() {
|
|
13242
|
+
this.ensureAnalysis();
|
|
13243
|
+
return this.cachedSummary || {
|
|
13244
|
+
totalKills: 0,
|
|
13245
|
+
totalDeaths: 0,
|
|
13246
|
+
damageDealt: 0,
|
|
13247
|
+
damageReceived: 0,
|
|
13248
|
+
weaponUsage: /* @__PURE__ */ new Map()
|
|
13249
|
+
};
|
|
13250
|
+
}
|
|
13251
|
+
getDemoHeader() {
|
|
13252
|
+
this.ensureAnalysis();
|
|
13253
|
+
return this.cachedHeader;
|
|
13254
|
+
}
|
|
13255
|
+
getDemoConfigStrings() {
|
|
13256
|
+
this.ensureAnalysis();
|
|
13257
|
+
return this.cachedConfigStrings || /* @__PURE__ */ new Map();
|
|
13258
|
+
}
|
|
13259
|
+
getDemoServerInfo() {
|
|
13260
|
+
this.ensureAnalysis();
|
|
13261
|
+
return this.cachedServerInfo || {};
|
|
13262
|
+
}
|
|
13263
|
+
getDemoStatistics() {
|
|
13264
|
+
this.ensureAnalysis();
|
|
13265
|
+
return this.cachedStatistics;
|
|
13266
|
+
}
|
|
13267
|
+
ensureAnalysis() {
|
|
13268
|
+
if (!this.cachedEvents && this.buffer) {
|
|
13269
|
+
const analyzer = new DemoAnalyzer(this.buffer);
|
|
13270
|
+
const result = analyzer.analyze();
|
|
13271
|
+
this.cachedEvents = result.events;
|
|
13272
|
+
this.cachedSummary = result.summary;
|
|
13273
|
+
this.cachedHeader = result.header;
|
|
13274
|
+
this.cachedConfigStrings = result.configStrings;
|
|
13275
|
+
this.cachedServerInfo = result.serverInfo;
|
|
13276
|
+
this.cachedStatistics = result.statistics;
|
|
13277
|
+
}
|
|
13278
|
+
}
|
|
12983
13279
|
};
|
|
12984
13280
|
|
|
12985
13281
|
// src/demo/recorder.ts
|
|
@@ -13328,6 +13624,67 @@ var AssetPreviewGenerator = class {
|
|
|
13328
13624
|
}
|
|
13329
13625
|
};
|
|
13330
13626
|
|
|
13627
|
+
// src/assets/mapStatistics.ts
|
|
13628
|
+
var MapAnalyzer = class {
|
|
13629
|
+
constructor(loader) {
|
|
13630
|
+
this.loader = loader;
|
|
13631
|
+
}
|
|
13632
|
+
async getMapStatistics(mapName) {
|
|
13633
|
+
const map = await this.loader.load(mapName);
|
|
13634
|
+
const lightmapCount = map.faces.filter((f) => f.lightOffset !== -1).length;
|
|
13635
|
+
const worldModel = map.models[0];
|
|
13636
|
+
const bounds = worldModel ? {
|
|
13637
|
+
mins: worldModel.mins,
|
|
13638
|
+
maxs: worldModel.maxs
|
|
13639
|
+
} : {
|
|
13640
|
+
mins: [0, 0, 0],
|
|
13641
|
+
maxs: [0, 0, 0]
|
|
13642
|
+
};
|
|
13643
|
+
return {
|
|
13644
|
+
entityCount: map.entities.entities.length,
|
|
13645
|
+
surfaceCount: map.faces.length,
|
|
13646
|
+
lightmapCount,
|
|
13647
|
+
vertexCount: map.vertices.length,
|
|
13648
|
+
bounds
|
|
13649
|
+
};
|
|
13650
|
+
}
|
|
13651
|
+
async getUsedTextures(mapName) {
|
|
13652
|
+
const map = await this.loader.load(mapName);
|
|
13653
|
+
const textures = /* @__PURE__ */ new Set();
|
|
13654
|
+
for (const info of map.texInfo) {
|
|
13655
|
+
if (info.texture) {
|
|
13656
|
+
textures.add(info.texture);
|
|
13657
|
+
}
|
|
13658
|
+
}
|
|
13659
|
+
return Array.from(textures).sort();
|
|
13660
|
+
}
|
|
13661
|
+
async getUsedModels(mapName) {
|
|
13662
|
+
const map = await this.loader.load(mapName);
|
|
13663
|
+
const models = /* @__PURE__ */ new Set();
|
|
13664
|
+
for (const ent of map.entities.entities) {
|
|
13665
|
+
if (ent.properties["model"] && !ent.properties["model"].startsWith("*")) {
|
|
13666
|
+
models.add(ent.properties["model"]);
|
|
13667
|
+
}
|
|
13668
|
+
}
|
|
13669
|
+
return Array.from(models).sort();
|
|
13670
|
+
}
|
|
13671
|
+
async getUsedSounds(mapName) {
|
|
13672
|
+
const map = await this.loader.load(mapName);
|
|
13673
|
+
const sounds = /* @__PURE__ */ new Set();
|
|
13674
|
+
for (const ent of map.entities.entities) {
|
|
13675
|
+
if (ent.properties["noise"]) {
|
|
13676
|
+
sounds.add(ent.properties["noise"]);
|
|
13677
|
+
}
|
|
13678
|
+
for (const [key, value] of Object.entries(ent.properties)) {
|
|
13679
|
+
if ((key === "noise" || key.endsWith("_sound") || key === "sound") && typeof value === "string") {
|
|
13680
|
+
sounds.add(value);
|
|
13681
|
+
}
|
|
13682
|
+
}
|
|
13683
|
+
}
|
|
13684
|
+
return Array.from(sounds).sort();
|
|
13685
|
+
}
|
|
13686
|
+
};
|
|
13687
|
+
|
|
13331
13688
|
// src/index.ts
|
|
13332
13689
|
function createEngine(imports) {
|
|
13333
13690
|
return {
|
|
@@ -13385,6 +13742,7 @@ function createEngine(imports) {
|
|
|
13385
13742
|
MD2_VERTEX_SHADER,
|
|
13386
13743
|
MD3_FRAGMENT_SHADER,
|
|
13387
13744
|
MD3_VERTEX_SHADER,
|
|
13745
|
+
MapAnalyzer,
|
|
13388
13746
|
Md2Loader,
|
|
13389
13747
|
Md2MeshBuffers,
|
|
13390
13748
|
Md2ParseError,
|