quake2ts 0.0.43 → 0.0.45
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/apps/viewer/dist/browser/index.global.js +1 -1
- package/apps/viewer/dist/browser/index.global.js.map +1 -1
- package/apps/viewer/dist/cjs/index.cjs +3 -0
- package/apps/viewer/dist/cjs/index.cjs.map +1 -1
- package/apps/viewer/dist/esm/index.js +3 -0
- package/apps/viewer/dist/esm/index.js.map +1 -1
- package/apps/viewer/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/packages/game/dist/browser/index.global.js +1 -1
- package/packages/game/dist/browser/index.global.js.map +1 -1
- package/packages/game/dist/cjs/index.cjs +152 -0
- package/packages/game/dist/cjs/index.cjs.map +1 -1
- package/packages/game/dist/esm/index.js +148 -0
- package/packages/game/dist/esm/index.js.map +1 -1
- package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/game/dist/types/save/rerelease.d.ts +25 -0
- package/packages/game/dist/types/save/rerelease.d.ts.map +1 -1
|
@@ -75,6 +75,9 @@ __export(index_exports, {
|
|
|
75
75
|
clampAmmoCounts: () => clampAmmoCounts,
|
|
76
76
|
classifyRange: () => classifyRange,
|
|
77
77
|
clearExpiredPowerups: () => clearExpiredPowerups,
|
|
78
|
+
convertGameSaveToRereleaseLevel: () => convertGameSaveToRereleaseLevel,
|
|
79
|
+
convertRereleaseLevelToGameSave: () => convertRereleaseLevelToGameSave,
|
|
80
|
+
convertRereleaseSaveToGameSave: () => convertRereleaseSaveToGameSave,
|
|
78
81
|
createAmmoInventory: () => createAmmoInventory,
|
|
79
82
|
createBaseAmmoCaps: () => createBaseAmmoCaps,
|
|
80
83
|
createDefaultSpawnRegistry: () => createDefaultSpawnRegistry,
|
|
@@ -103,6 +106,7 @@ __export(index_exports, {
|
|
|
103
106
|
rangeTo: () => rangeTo,
|
|
104
107
|
registerDefaultSpawns: () => registerDefaultSpawns,
|
|
105
108
|
selectWeapon: () => selectWeapon,
|
|
109
|
+
serializeRereleaseSave: () => serializeRereleaseSave,
|
|
106
110
|
setMovedir: () => setMovedir,
|
|
107
111
|
spawnEntitiesFromText: () => spawnEntitiesFromText,
|
|
108
112
|
spawnEntityFromDictionary: () => spawnEntityFromDictionary,
|
|
@@ -2739,6 +2743,150 @@ function summarizeRereleaseSave(raw) {
|
|
|
2739
2743
|
highestEntityIndex: highestEntityIndex >= 0 ? highestEntityIndex : void 0
|
|
2740
2744
|
};
|
|
2741
2745
|
}
|
|
2746
|
+
var SERIALIZABLE_FIELD_NAMES = new Set(
|
|
2747
|
+
ENTITY_FIELD_METADATA.filter((field) => field.save).map((field) => field.name)
|
|
2748
|
+
);
|
|
2749
|
+
function toVec3(value, label) {
|
|
2750
|
+
if (!Array.isArray(value) || value.length !== 3) {
|
|
2751
|
+
throw new Error(`${label} must be a vec3 array`);
|
|
2752
|
+
}
|
|
2753
|
+
const [x, y, z] = value;
|
|
2754
|
+
return [ensureNumber2(x, `${label}[0]`), ensureNumber2(y, `${label}[1]`), ensureNumber2(z, `${label}[2]`)];
|
|
2755
|
+
}
|
|
2756
|
+
function normalizeEntityFields(raw, label) {
|
|
2757
|
+
const fields = {};
|
|
2758
|
+
for (const [rawName, value] of Object.entries(raw)) {
|
|
2759
|
+
const name = rawName;
|
|
2760
|
+
if (!SERIALIZABLE_FIELD_NAMES.has(name)) {
|
|
2761
|
+
continue;
|
|
2762
|
+
}
|
|
2763
|
+
if (value === null) {
|
|
2764
|
+
fields[name] = null;
|
|
2765
|
+
continue;
|
|
2766
|
+
}
|
|
2767
|
+
if (Array.isArray(value)) {
|
|
2768
|
+
fields[name] = toVec3(value, `${label}.${name}`);
|
|
2769
|
+
continue;
|
|
2770
|
+
}
|
|
2771
|
+
switch (typeof value) {
|
|
2772
|
+
case "number":
|
|
2773
|
+
case "string":
|
|
2774
|
+
case "boolean":
|
|
2775
|
+
fields[name] = value;
|
|
2776
|
+
break;
|
|
2777
|
+
default:
|
|
2778
|
+
throw new Error(`Unsupported field type for ${label}.${name}`);
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
return fields;
|
|
2782
|
+
}
|
|
2783
|
+
function buildEntitySnapshot(entities, levelTimeSeconds, capacityHint) {
|
|
2784
|
+
let highestIndex = 0;
|
|
2785
|
+
const active = /* @__PURE__ */ new Set();
|
|
2786
|
+
const activeOrder = [];
|
|
2787
|
+
const serialized = [];
|
|
2788
|
+
for (const [index, entry] of entities.entries()) {
|
|
2789
|
+
if (index > highestIndex) {
|
|
2790
|
+
highestIndex = index;
|
|
2791
|
+
}
|
|
2792
|
+
const inUse = entry.inuse !== false;
|
|
2793
|
+
if (!inUse) {
|
|
2794
|
+
continue;
|
|
2795
|
+
}
|
|
2796
|
+
if (!active.has(index)) {
|
|
2797
|
+
activeOrder.push(index);
|
|
2798
|
+
}
|
|
2799
|
+
active.add(index);
|
|
2800
|
+
serialized.push({ index, fields: normalizeEntityFields(entry, `entities[${index}]`) });
|
|
2801
|
+
}
|
|
2802
|
+
if (!active.has(0)) {
|
|
2803
|
+
active.add(0);
|
|
2804
|
+
activeOrder.unshift(0);
|
|
2805
|
+
serialized.unshift({ index: 0, fields: { classname: "worldspawn" } });
|
|
2806
|
+
}
|
|
2807
|
+
const capacity = Math.max(capacityHint ?? 0, highestIndex + 1, Math.max(...active) + 1);
|
|
2808
|
+
const freeList = [];
|
|
2809
|
+
for (let i = 0; i < capacity; i += 1) {
|
|
2810
|
+
if (!active.has(i)) {
|
|
2811
|
+
freeList.push(i);
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
return {
|
|
2815
|
+
timeSeconds: levelTimeSeconds,
|
|
2816
|
+
pool: { capacity, activeOrder, freeList, pendingFree: [] },
|
|
2817
|
+
entities: serialized,
|
|
2818
|
+
thinks: []
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
function buildLevelState(level) {
|
|
2822
|
+
const timeSeconds = typeof level.time === "number" ? level.time : 0;
|
|
2823
|
+
const frameNumber = typeof level.framenum === "number" ? level.framenum : 0;
|
|
2824
|
+
const deltaSeconds = typeof level.frametime === "number" ? level.frametime : 0;
|
|
2825
|
+
const previousTimeSeconds = timeSeconds - deltaSeconds;
|
|
2826
|
+
return { frameNumber, timeSeconds, previousTimeSeconds, deltaSeconds };
|
|
2827
|
+
}
|
|
2828
|
+
function convertRereleaseLevelToGameSave(save, options = {}) {
|
|
2829
|
+
const { timestamp = Date.now(), defaultDifficulty = 1, defaultPlaytimeSeconds, rngState, configstrings = [] } = options;
|
|
2830
|
+
const levelName = save.level.mapname ?? "unknown";
|
|
2831
|
+
const level = buildLevelState(save.level);
|
|
2832
|
+
const playtimeSeconds = defaultPlaytimeSeconds ?? level.timeSeconds;
|
|
2833
|
+
return {
|
|
2834
|
+
version: SAVE_FORMAT_VERSION,
|
|
2835
|
+
timestamp,
|
|
2836
|
+
map: levelName,
|
|
2837
|
+
difficulty: defaultDifficulty,
|
|
2838
|
+
playtimeSeconds,
|
|
2839
|
+
gameState: { ...options.gameState ?? {}, rereleaseVersion: save.saveVersion },
|
|
2840
|
+
level,
|
|
2841
|
+
rng: rngState ? { mt: { index: rngState.mt.index, state: [...rngState.mt.state] } } : new RandomGenerator().getState(),
|
|
2842
|
+
entities: buildEntitySnapshot(save.entities, level.timeSeconds, save.level.maxentities ?? void 0),
|
|
2843
|
+
cvars: [],
|
|
2844
|
+
configstrings: [...configstrings]
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
function convertRereleaseSaveToGameSave(save, options = {}) {
|
|
2848
|
+
if ("game" in save) {
|
|
2849
|
+
throw new Error("Game-wide rerelease saves are not currently supported for conversion");
|
|
2850
|
+
}
|
|
2851
|
+
return convertRereleaseLevelToGameSave(save, options);
|
|
2852
|
+
}
|
|
2853
|
+
function convertGameSaveToRereleaseLevel(save) {
|
|
2854
|
+
const active = new Set(save.entities.pool.activeOrder);
|
|
2855
|
+
const entities = /* @__PURE__ */ new Map();
|
|
2856
|
+
for (const entity of save.entities.entities) {
|
|
2857
|
+
if (!active.has(entity.index)) {
|
|
2858
|
+
continue;
|
|
2859
|
+
}
|
|
2860
|
+
const fields = { inuse: true };
|
|
2861
|
+
for (const [name, value] of Object.entries(entity.fields)) {
|
|
2862
|
+
if (value === void 0) {
|
|
2863
|
+
continue;
|
|
2864
|
+
}
|
|
2865
|
+
fields[name] = value;
|
|
2866
|
+
}
|
|
2867
|
+
entities.set(entity.index, fields);
|
|
2868
|
+
}
|
|
2869
|
+
return {
|
|
2870
|
+
saveVersion: save.version,
|
|
2871
|
+
level: {
|
|
2872
|
+
mapname: save.map,
|
|
2873
|
+
time: save.level.timeSeconds,
|
|
2874
|
+
framenum: save.level.frameNumber,
|
|
2875
|
+
frametime: save.level.deltaSeconds
|
|
2876
|
+
},
|
|
2877
|
+
entities
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
function serializeRereleaseSave(save) {
|
|
2881
|
+
if ("game" in save) {
|
|
2882
|
+
return { save_version: save.saveVersion, game: save.game, clients: save.clients };
|
|
2883
|
+
}
|
|
2884
|
+
const entities = {};
|
|
2885
|
+
for (const [index, entity] of save.entities.entries()) {
|
|
2886
|
+
entities[index.toString(10)] = entity;
|
|
2887
|
+
}
|
|
2888
|
+
return { save_version: save.saveVersion, level: save.level, entities };
|
|
2889
|
+
}
|
|
2742
2890
|
|
|
2743
2891
|
// src/save/storage.ts
|
|
2744
2892
|
var TEXT_ENCODER_CTOR = globalThis.TextEncoder;
|
|
@@ -3709,6 +3857,9 @@ function createGame(engine, options) {
|
|
|
3709
3857
|
clampAmmoCounts,
|
|
3710
3858
|
classifyRange,
|
|
3711
3859
|
clearExpiredPowerups,
|
|
3860
|
+
convertGameSaveToRereleaseLevel,
|
|
3861
|
+
convertRereleaseLevelToGameSave,
|
|
3862
|
+
convertRereleaseSaveToGameSave,
|
|
3712
3863
|
createAmmoInventory,
|
|
3713
3864
|
createBaseAmmoCaps,
|
|
3714
3865
|
createDefaultSpawnRegistry,
|
|
@@ -3737,6 +3888,7 @@ function createGame(engine, options) {
|
|
|
3737
3888
|
rangeTo,
|
|
3738
3889
|
registerDefaultSpawns,
|
|
3739
3890
|
selectWeapon,
|
|
3891
|
+
serializeRereleaseSave,
|
|
3740
3892
|
setMovedir,
|
|
3741
3893
|
spawnEntitiesFromText,
|
|
3742
3894
|
spawnEntityFromDictionary,
|