quake2ts 0.0.80 → 0.0.82

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 (63) hide show
  1. package/package.json +1 -1
  2. package/packages/client/dist/browser/index.global.js +1 -1
  3. package/packages/client/dist/browser/index.global.js.map +1 -1
  4. package/packages/client/dist/cjs/index.cjs +9 -1
  5. package/packages/client/dist/cjs/index.cjs.map +1 -1
  6. package/packages/client/dist/esm/index.js +9 -1
  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/hud.d.ts.map +1 -1
  10. package/packages/client/dist/types/prediction.d.ts +2 -0
  11. package/packages/client/dist/types/prediction.d.ts.map +1 -1
  12. package/packages/engine/dist/browser/index.global.js +4 -2
  13. package/packages/engine/dist/browser/index.global.js.map +1 -1
  14. package/packages/engine/dist/cjs/index.cjs +6 -2
  15. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  16. package/packages/engine/dist/esm/index.js +6 -2
  17. package/packages/engine/dist/esm/index.js.map +1 -1
  18. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  19. package/packages/engine/dist/types/render/light.d.ts +10 -0
  20. package/packages/engine/dist/types/render/light.d.ts.map +1 -0
  21. package/packages/engine/dist/types/render/md2Pipeline.d.ts +3 -1
  22. package/packages/engine/dist/types/render/md2Pipeline.d.ts.map +1 -1
  23. package/packages/engine/dist/types/render/renderer.d.ts +1 -0
  24. package/packages/engine/dist/types/render/renderer.d.ts.map +1 -1
  25. package/packages/engine/dist/types/render/scene.d.ts +1 -0
  26. package/packages/engine/dist/types/render/scene.d.ts.map +1 -1
  27. package/packages/game/dist/browser/index.global.js +1 -1
  28. package/packages/game/dist/browser/index.global.js.map +1 -1
  29. package/packages/game/dist/cjs/index.cjs +724 -440
  30. package/packages/game/dist/cjs/index.cjs.map +1 -1
  31. package/packages/game/dist/esm/index.js +719 -440
  32. package/packages/game/dist/esm/index.js.map +1 -1
  33. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
  34. package/packages/game/dist/types/ai/monster.d.ts +3 -2
  35. package/packages/game/dist/types/ai/monster.d.ts.map +1 -1
  36. package/packages/game/dist/types/ai/movement.d.ts +2 -1
  37. package/packages/game/dist/types/ai/movement.d.ts.map +1 -1
  38. package/packages/game/dist/types/ai/targeting.d.ts +4 -3
  39. package/packages/game/dist/types/ai/targeting.d.ts.map +1 -1
  40. package/packages/game/dist/types/entities/entity.d.ts +3 -2
  41. package/packages/game/dist/types/entities/entity.d.ts.map +1 -1
  42. package/packages/game/dist/types/entities/funcs.d.ts.map +1 -1
  43. package/packages/game/dist/types/entities/items/ammo.d.ts +5 -0
  44. package/packages/game/dist/types/entities/items/ammo.d.ts.map +1 -0
  45. package/packages/game/dist/types/entities/items/index.d.ts +5 -0
  46. package/packages/game/dist/types/entities/items/index.d.ts.map +1 -1
  47. package/packages/game/dist/types/entities/items.d.ts.map +1 -1
  48. package/packages/game/dist/types/entities/monsters/attack.d.ts +8 -0
  49. package/packages/game/dist/types/entities/monsters/attack.d.ts.map +1 -0
  50. package/packages/game/dist/types/entities/monsters/gunner.d.ts +3 -0
  51. package/packages/game/dist/types/entities/monsters/gunner.d.ts.map +1 -0
  52. package/packages/game/dist/types/entities/monsters/soldier.d.ts.map +1 -1
  53. package/packages/game/dist/types/entities/projectiles.d.ts +6 -5
  54. package/packages/game/dist/types/entities/projectiles.d.ts.map +1 -1
  55. package/packages/game/dist/types/entities/spawn.d.ts.map +1 -1
  56. package/packages/game/dist/types/entities/system.d.ts +3 -1
  57. package/packages/game/dist/types/entities/system.d.ts.map +1 -1
  58. package/packages/game/dist/types/imports.d.ts +4 -2
  59. package/packages/game/dist/types/imports.d.ts.map +1 -1
  60. package/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
  61. package/packages/shared/dist/types/protocol/player-state.d.ts +2 -0
  62. package/packages/shared/dist/types/protocol/player-state.d.ts.map +1 -1
  63. package/packages/tools/dist/tsconfig.tsbuildinfo +1 -1
@@ -89,12 +89,17 @@ __export(index_exports, {
89
89
  convertRereleaseLevelToGameSave: () => convertRereleaseLevelToGameSave,
90
90
  convertRereleaseSaveToGameSave: () => convertRereleaseSaveToGameSave,
91
91
  createAmmoInventory: () => createAmmoInventory,
92
+ createAmmoPickupEntity: () => createAmmoPickupEntity,
93
+ createArmorPickupEntity: () => createArmorPickupEntity,
92
94
  createBaseAmmoCaps: () => createBaseAmmoCaps,
93
95
  createCallbackRegistry: () => createCallbackRegistry,
94
96
  createDefaultSpawnRegistry: () => createDefaultSpawnRegistry,
95
97
  createGame: () => createGame,
98
+ createHealthPickupEntity: () => createHealthPickupEntity,
99
+ createKeyPickupEntity: () => createKeyPickupEntity,
96
100
  createPlayerInventory: () => createPlayerInventory,
97
101
  createPlayerWeaponStates: () => createPlayerWeaponStates,
102
+ createPowerupPickupEntity: () => createPowerupPickupEntity,
98
103
  createSaveFile: () => createSaveFile,
99
104
  createWeaponPickupEntity: () => createWeaponPickupEntity,
100
105
  damageModName: () => damageModName,
@@ -1528,6 +1533,12 @@ var EntitySystem = class {
1528
1533
  }
1529
1534
  }
1530
1535
  }
1536
+ get trace() {
1537
+ return this.imports.trace;
1538
+ }
1539
+ get pointcontents() {
1540
+ return this.imports.pointcontents;
1541
+ }
1531
1542
  get world() {
1532
1543
  return this.pool.world;
1533
1544
  }
@@ -2479,14 +2490,14 @@ var AmmoType = /* @__PURE__ */ ((AmmoType3) => {
2479
2490
  return AmmoType3;
2480
2491
  })(AmmoType || {});
2481
2492
  var AMMO_TYPE_COUNT = Object.keys(AmmoType).length / 2;
2482
- var AmmoItemId = /* @__PURE__ */ ((AmmoItemId3) => {
2483
- AmmoItemId3["Shells"] = "ammo_shells";
2484
- AmmoItemId3["Bullets"] = "ammo_bullets";
2485
- AmmoItemId3["Rockets"] = "ammo_rockets";
2486
- AmmoItemId3["Grenades"] = "ammo_grenades";
2487
- AmmoItemId3["Cells"] = "ammo_cells";
2488
- AmmoItemId3["Slugs"] = "ammo_slugs";
2489
- return AmmoItemId3;
2493
+ var AmmoItemId = /* @__PURE__ */ ((AmmoItemId4) => {
2494
+ AmmoItemId4["Shells"] = "ammo_shells";
2495
+ AmmoItemId4["Bullets"] = "ammo_bullets";
2496
+ AmmoItemId4["Rockets"] = "ammo_rockets";
2497
+ AmmoItemId4["Grenades"] = "ammo_grenades";
2498
+ AmmoItemId4["Cells"] = "ammo_cells";
2499
+ AmmoItemId4["Slugs"] = "ammo_slugs";
2500
+ return AmmoItemId4;
2490
2501
  })(AmmoItemId || {});
2491
2502
  var AMMO_ITEM_DEFINITIONS = {
2492
2503
  ["ammo_shells" /* Shells */]: { id: "ammo_shells" /* Shells */, ammoType: 1 /* Shells */, quantity: 10, weaponAmmo: false },
@@ -3221,6 +3232,32 @@ function createKeyPickupEntity(game, keyItem) {
3221
3232
  };
3222
3233
  }
3223
3234
 
3235
+ // src/entities/items/ammo.ts
3236
+ function createAmmoPickupEntity(game, itemId) {
3237
+ const def = getAmmoItemDefinition(itemId);
3238
+ const respawn = (self) => {
3239
+ self.solid = 1 /* Trigger */;
3240
+ };
3241
+ return {
3242
+ classname: itemId,
3243
+ solid: 1 /* Trigger */,
3244
+ touch: (self, other) => {
3245
+ if (!other || !other.client) {
3246
+ return;
3247
+ }
3248
+ const result = pickupAmmo(other.client.inventory.ammo, itemId);
3249
+ if (result.pickedUp) {
3250
+ game.sound?.(other, 0, "items/pkup.wav", 1, 1, 0);
3251
+ game.centerprintf?.(other, `You got ${def.quantity} ${itemId.replace("ammo_", "")}`);
3252
+ self.solid = 0 /* Not */;
3253
+ self.nextthink = game.time + 30;
3254
+ game.entities.scheduleThink(self, self.nextthink);
3255
+ }
3256
+ },
3257
+ think: respawn
3258
+ };
3259
+ }
3260
+
3224
3261
  // src/entities/items.ts
3225
3262
  function registerItemSpawns(game, registry) {
3226
3263
  for (const weaponItem of Object.values(WEAPON_ITEMS)) {
@@ -3248,6 +3285,11 @@ function registerItemSpawns(game, registry) {
3248
3285
  Object.assign(entity, createKeyPickupEntity(game, keyItem));
3249
3286
  });
3250
3287
  }
3288
+ for (const ammoId of Object.values(AmmoItemId)) {
3289
+ registry.register(ammoId, (entity) => {
3290
+ Object.assign(entity, createAmmoPickupEntity(game, ammoId));
3291
+ });
3292
+ }
3251
3293
  }
3252
3294
 
3253
3295
  // src/entities/funcs.ts
@@ -3312,6 +3354,7 @@ var func_door = (entity, context) => {
3312
3354
  self.state = 1 /* Opening */;
3313
3355
  self.think = door_go_up;
3314
3356
  context.entities.scheduleThink(self, context.entities.timeSeconds + 0.1);
3357
+ context.entities.sound(self, 0, "doors/dr1_strt.wav", 1, 1, 0);
3315
3358
  };
3316
3359
  };
3317
3360
  var func_button = (entity, context) => {
@@ -3319,6 +3362,7 @@ var func_button = (entity, context) => {
3319
3362
  entity.movetype = 2 /* Push */;
3320
3363
  entity.use = (self) => {
3321
3364
  context.entities.useTargets(self, self);
3365
+ context.entities.sound(self, 0, "switches/butn2.wav", 1, 1, 0);
3322
3366
  };
3323
3367
  };
3324
3368
  var TRAIN_START_ON = 1;
@@ -3596,6 +3640,66 @@ var TraceMask = /* @__PURE__ */ ((TraceMask2) => {
3596
3640
  return TraceMask2;
3597
3641
  })(TraceMask || {});
3598
3642
 
3643
+ // src/ai/perception.ts
3644
+ var RangeCategory = /* @__PURE__ */ ((RangeCategory2) => {
3645
+ RangeCategory2["Melee"] = "melee";
3646
+ RangeCategory2["Near"] = "near";
3647
+ RangeCategory2["Mid"] = "mid";
3648
+ RangeCategory2["Far"] = "far";
3649
+ return RangeCategory2;
3650
+ })(RangeCategory || {});
3651
+ function absBounds(entity) {
3652
+ return {
3653
+ mins: {
3654
+ x: entity.origin.x + entity.mins.x,
3655
+ y: entity.origin.y + entity.mins.y,
3656
+ z: entity.origin.z + entity.mins.z
3657
+ },
3658
+ maxs: {
3659
+ x: entity.origin.x + entity.maxs.x,
3660
+ y: entity.origin.y + entity.maxs.y,
3661
+ z: entity.origin.z + entity.maxs.z
3662
+ }
3663
+ };
3664
+ }
3665
+ function rangeTo(self, other) {
3666
+ const a = absBounds(self);
3667
+ const b = absBounds(other);
3668
+ const distanceSquared = distanceBetweenBoxesSquared(a.mins, a.maxs, b.mins, b.maxs);
3669
+ return Math.sqrt(distanceSquared);
3670
+ }
3671
+ function classifyRange(distance2) {
3672
+ if (distance2 <= RANGE_MELEE) {
3673
+ return "melee" /* Melee */;
3674
+ }
3675
+ if (distance2 <= RANGE_NEAR) {
3676
+ return "near" /* Near */;
3677
+ }
3678
+ if (distance2 <= RANGE_MID) {
3679
+ return "mid" /* Mid */;
3680
+ }
3681
+ return "far" /* Far */;
3682
+ }
3683
+ function infront(self, other) {
3684
+ const { forward } = angleVectors(self.angles);
3685
+ const direction = normalizeVec3(subtractVec3(other.origin, self.origin));
3686
+ const dot = dotVec3(direction, forward);
3687
+ if ((self.spawnflags & SPAWNFLAG_MONSTER_AMBUSH) !== 0 && self.trail_time === 0 && self.enemy === null) {
3688
+ return dot > 0.15;
3689
+ }
3690
+ return dot > -0.3;
3691
+ }
3692
+ function visible(self, other, trace, options) {
3693
+ if ((other.flags & FL_NOVISIBLE) !== 0) {
3694
+ return false;
3695
+ }
3696
+ const start = { x: self.origin.x, y: self.origin.y, z: self.origin.z + self.viewheight };
3697
+ const end = { x: other.origin.x, y: other.origin.y, z: other.origin.z + other.viewheight };
3698
+ const mask = options?.throughGlass ? 1 /* Opaque */ : 1 /* Opaque */ | 2 /* Window */;
3699
+ const result = trace(start, end, self, mask);
3700
+ return result.fraction === 1 || result.entity === other;
3701
+ }
3702
+
3599
3703
  // src/ai/movement.ts
3600
3704
  function yawVector(yawDegrees, distance2) {
3601
3705
  if (distance2 === 0) {
@@ -3657,12 +3761,24 @@ function setIdealYawTowards(self, target) {
3657
3761
  function ai_stand(self, deltaSeconds) {
3658
3762
  changeYaw(self, deltaSeconds);
3659
3763
  }
3660
- function ai_walk(self, distance2, deltaSeconds) {
3764
+ function ai_walk(self, distance2, deltaSeconds, context) {
3661
3765
  setIdealYawTowards(self, self.goalentity);
3662
3766
  changeYaw(self, deltaSeconds);
3663
3767
  if (distance2 !== 0) {
3664
3768
  walkMove(self, self.angles.y, distance2);
3665
3769
  }
3770
+ if (self.goalentity && self.goalentity.classname === "path_corner") {
3771
+ const dist = rangeTo(self, self.goalentity);
3772
+ if (dist < 64) {
3773
+ if (self.goalentity.target) {
3774
+ const next = context.pickTarget(self.goalentity.target);
3775
+ if (next) {
3776
+ self.goalentity = next;
3777
+ self.ideal_yaw = self.angles.y;
3778
+ }
3779
+ }
3780
+ }
3781
+ }
3666
3782
  }
3667
3783
  function ai_turn(self, distance2, deltaSeconds) {
3668
3784
  if (distance2 !== 0) {
@@ -3694,66 +3810,6 @@ function ai_charge(self, distance2, deltaSeconds) {
3694
3810
  }
3695
3811
  }
3696
3812
 
3697
- // src/ai/perception.ts
3698
- var RangeCategory = /* @__PURE__ */ ((RangeCategory2) => {
3699
- RangeCategory2["Melee"] = "melee";
3700
- RangeCategory2["Near"] = "near";
3701
- RangeCategory2["Mid"] = "mid";
3702
- RangeCategory2["Far"] = "far";
3703
- return RangeCategory2;
3704
- })(RangeCategory || {});
3705
- function absBounds(entity) {
3706
- return {
3707
- mins: {
3708
- x: entity.origin.x + entity.mins.x,
3709
- y: entity.origin.y + entity.mins.y,
3710
- z: entity.origin.z + entity.mins.z
3711
- },
3712
- maxs: {
3713
- x: entity.origin.x + entity.maxs.x,
3714
- y: entity.origin.y + entity.maxs.y,
3715
- z: entity.origin.z + entity.maxs.z
3716
- }
3717
- };
3718
- }
3719
- function rangeTo(self, other) {
3720
- const a = absBounds(self);
3721
- const b = absBounds(other);
3722
- const distanceSquared = distanceBetweenBoxesSquared(a.mins, a.maxs, b.mins, b.maxs);
3723
- return Math.sqrt(distanceSquared);
3724
- }
3725
- function classifyRange(distance2) {
3726
- if (distance2 <= RANGE_MELEE) {
3727
- return "melee" /* Melee */;
3728
- }
3729
- if (distance2 <= RANGE_NEAR) {
3730
- return "near" /* Near */;
3731
- }
3732
- if (distance2 <= RANGE_MID) {
3733
- return "mid" /* Mid */;
3734
- }
3735
- return "far" /* Far */;
3736
- }
3737
- function infront(self, other) {
3738
- const { forward } = angleVectors(self.angles);
3739
- const direction = normalizeVec3(subtractVec3(other.origin, self.origin));
3740
- const dot = dotVec3(direction, forward);
3741
- if ((self.spawnflags & SPAWNFLAG_MONSTER_AMBUSH) !== 0 && self.trail_time === 0 && self.enemy === null) {
3742
- return dot > 0.15;
3743
- }
3744
- return dot > -0.3;
3745
- }
3746
- function visible(self, other, trace, options) {
3747
- if ((other.flags & FL_NOVISIBLE) !== 0) {
3748
- return false;
3749
- }
3750
- const start = { x: self.origin.x, y: self.origin.y, z: self.origin.z + self.viewheight };
3751
- const end = { x: other.origin.x, y: other.origin.y, z: other.origin.z + other.viewheight };
3752
- const mask = options?.throughGlass ? 1 /* Opaque */ : 1 /* Opaque */ | 2 /* Window */;
3753
- const result = trace(start, end, self, mask);
3754
- return result.fraction === 1 || result.entity === other;
3755
- }
3756
-
3757
3813
  // src/ai/targeting.ts
3758
3814
  function setIdealYawTowards2(self, other) {
3759
3815
  const delta = {
@@ -3766,19 +3822,19 @@ function setIdealYawTowards2(self, other) {
3766
3822
  function faceYawInstantly(self) {
3767
3823
  self.angles.y = angleMod(self.ideal_yaw);
3768
3824
  }
3769
- function huntTarget(self, level) {
3825
+ function huntTarget(self, level, context) {
3770
3826
  if (!self.enemy) return;
3771
3827
  self.goalentity = self.enemy;
3772
3828
  setIdealYawTowards2(self, self.enemy);
3773
3829
  faceYawInstantly(self);
3774
3830
  if ((self.monsterinfo.aiflags & 1 /* StandGround */) !== 0) {
3775
- self.monsterinfo.stand?.(self);
3831
+ self.monsterinfo.stand?.(self, context);
3776
3832
  } else {
3777
- self.monsterinfo.run?.(self);
3833
+ self.monsterinfo.run?.(self, context);
3778
3834
  self.attack_finished_time = level.timeSeconds + 1;
3779
3835
  }
3780
3836
  }
3781
- function foundTarget(self, level, options) {
3837
+ function foundTarget(self, level, context, options) {
3782
3838
  if (!self.enemy) return;
3783
3839
  if ((self.enemy.svflags & 8 /* Player */) !== 0) {
3784
3840
  level.sightEntity = self;
@@ -3793,7 +3849,7 @@ function foundTarget(self, level, options) {
3793
3849
  self.trail_time = level.timeSeconds;
3794
3850
  self.monsterinfo.trail_time = level.timeSeconds;
3795
3851
  if (!self.combattarget) {
3796
- huntTarget(self, level);
3852
+ huntTarget(self, level, context);
3797
3853
  return;
3798
3854
  }
3799
3855
  const pickTarget = options?.pickTarget;
@@ -3806,7 +3862,7 @@ function foundTarget(self, level, options) {
3806
3862
  self.movetarget.targetname = void 0;
3807
3863
  }
3808
3864
  self.monsterinfo.pausetime = 0;
3809
- self.monsterinfo.run?.(self);
3865
+ self.monsterinfo.run?.(self, context);
3810
3866
  }
3811
3867
  function classifyClientVisibility(self, other, level, trace) {
3812
3868
  const distance2 = rangeTo(self, other);
@@ -3863,7 +3919,7 @@ function rejectNotargetEntity(client) {
3863
3919
  if (client.enemy && (client.enemy.flags & FL_NOTARGET) !== 0) return true;
3864
3920
  return false;
3865
3921
  }
3866
- function findTarget(self, level, trace, hearability = {}) {
3922
+ function findTarget(self, level, context, trace, hearability = {}) {
3867
3923
  if ((self.monsterinfo.aiflags & 256 /* GoodGuy */) !== 0) {
3868
3924
  if (self.goalentity?.classname === "target_actor") {
3869
3925
  return false;
@@ -3884,7 +3940,7 @@ function findTarget(self, level, trace, hearability = {}) {
3884
3940
  } else if (!updateSoundChase(self, candidate, level, hearability, trace)) {
3885
3941
  return false;
3886
3942
  }
3887
- foundTarget(self, level);
3943
+ foundTarget(self, level, context);
3888
3944
  if ((self.monsterinfo.aiflags & 4 /* SoundTarget */) === 0) {
3889
3945
  self.monsterinfo.sight?.(self, self.enemy);
3890
3946
  }
@@ -3892,7 +3948,7 @@ function findTarget(self, level, trace, hearability = {}) {
3892
3948
  }
3893
3949
 
3894
3950
  // src/ai/monster.ts
3895
- function M_MoveFrame(self) {
3951
+ function M_MoveFrame(self, context) {
3896
3952
  const move = self.monsterinfo.current_move;
3897
3953
  if (!move) {
3898
3954
  return;
@@ -3907,10 +3963,10 @@ function M_MoveFrame(self) {
3907
3963
  const index = self.frame - move.firstframe;
3908
3964
  const frame = move.frames[index];
3909
3965
  if (frame.ai) {
3910
- frame.ai(self, frame.dist);
3966
+ frame.ai(self, frame.dist, context);
3911
3967
  }
3912
3968
  if (frame.think) {
3913
- frame.think(self);
3969
+ frame.think(self, context);
3914
3970
  }
3915
3971
  if (!self.inUse) {
3916
3972
  return;
@@ -3918,7 +3974,7 @@ function M_MoveFrame(self) {
3918
3974
  self.frame++;
3919
3975
  if (self.frame > move.lastframe) {
3920
3976
  if (move.endfunc) {
3921
- move.endfunc(self);
3977
+ move.endfunc(self, context);
3922
3978
  if (self.monsterinfo.current_move !== move) {
3923
3979
  return;
3924
3980
  }
@@ -3926,125 +3982,589 @@ function M_MoveFrame(self) {
3926
3982
  }
3927
3983
  }
3928
3984
  function monster_think(self, context) {
3929
- M_MoveFrame(self);
3930
- const time = context && typeof context.timeSeconds === "number" ? context.timeSeconds : self.nextthink;
3931
- self.nextthink = time + 0.1;
3932
- }
3933
-
3934
- // src/entities/monsters/soldier.ts
3935
- var MONSTER_TICK = 0.1;
3936
- function monster_ai_stand(self, dist) {
3937
- ai_stand(self, MONSTER_TICK);
3938
- }
3939
- function monster_ai_walk(self, dist) {
3940
- ai_walk(self, dist, MONSTER_TICK);
3941
- }
3942
- function monster_ai_run(self, dist) {
3943
- ai_run(self, dist, MONSTER_TICK);
3944
- }
3945
- function monster_ai_charge(self, dist) {
3946
- ai_charge(self, dist, MONSTER_TICK);
3947
- }
3948
- var stand_move;
3949
- var walk_move;
3950
- var run_move;
3951
- var attack_move;
3952
- function soldier_stand(self) {
3953
- self.monsterinfo.current_move = stand_move;
3954
- }
3955
- function soldier_walk(self) {
3956
- self.monsterinfo.current_move = walk_move;
3957
- }
3958
- function soldier_run(self) {
3959
- if (self.enemy && self.enemy.health > 0) {
3960
- self.monsterinfo.current_move = run_move;
3961
- } else {
3962
- self.monsterinfo.current_move = stand_move;
3963
- }
3964
- }
3965
- function soldier_attack(self) {
3966
- self.monsterinfo.current_move = attack_move;
3967
- }
3968
- function soldier_fire(self) {
3969
- }
3970
- var stand_frames = Array.from({ length: 30 }, () => ({
3971
- ai: monster_ai_stand,
3972
- dist: 0
3973
- }));
3974
- stand_move = {
3975
- firstframe: 0,
3976
- lastframe: 29,
3977
- frames: stand_frames,
3978
- endfunc: soldier_stand
3979
- };
3980
- var walk_frames = Array.from({ length: 40 }, () => ({
3981
- ai: monster_ai_walk,
3982
- dist: 2
3983
- }));
3984
- walk_move = {
3985
- firstframe: 30,
3986
- lastframe: 69,
3987
- frames: walk_frames,
3988
- endfunc: soldier_walk
3989
- };
3990
- var run_frames = Array.from({ length: 20 }, () => ({
3991
- ai: monster_ai_run,
3992
- dist: 10
3993
- }));
3994
- run_move = {
3995
- firstframe: 70,
3996
- lastframe: 89,
3997
- frames: run_frames,
3998
- endfunc: soldier_run
3999
- };
4000
- var attack_frames = Array.from({ length: 10 }, (_, i) => ({
4001
- ai: monster_ai_charge,
4002
- dist: 0,
4003
- think: i === 5 ? soldier_fire : null
4004
- }));
4005
- attack_move = {
4006
- firstframe: 90,
4007
- lastframe: 99,
4008
- frames: attack_frames,
4009
- endfunc: soldier_run
4010
- };
4011
- function SP_monster_soldier(self, context) {
4012
- self.model = "models/monsters/soldier/tris.md2";
4013
- self.mins = { x: -16, y: -16, z: -24 };
4014
- self.maxs = { x: 16, y: 16, z: 32 };
4015
- self.movetype = 5 /* Step */;
4016
- self.solid = 2 /* BoundingBox */;
4017
- self.health = 20;
4018
- self.max_health = 20;
4019
- self.mass = 100;
4020
- self.pain = (self2, other, kick, damage) => {
4021
- };
4022
- self.die = (self2, inflictor, attacker, damage, point) => {
4023
- self2.deadflag = 2 /* Dead */;
4024
- self2.solid = 0 /* Not */;
4025
- };
4026
- self.monsterinfo.stand = soldier_stand;
4027
- self.monsterinfo.walk = soldier_walk;
4028
- self.monsterinfo.run = soldier_run;
4029
- self.monsterinfo.attack = soldier_attack;
4030
- self.think = monster_think;
4031
- soldier_stand(self);
4032
- self.nextthink = self.timestamp + MONSTER_TICK;
4033
- }
4034
- function registerMonsterSpawns(registry) {
4035
- registry.register("monster_soldier", SP_monster_soldier);
3985
+ M_MoveFrame(self, context);
3986
+ self.nextthink = context.timeSeconds + 0.1;
4036
3987
  }
4037
3988
 
4038
- // src/entities/spawn.ts
4039
- var FIELD_LOOKUP = new Map(
4040
- ENTITY_FIELD_METADATA.map((field) => [field.name, field])
4041
- );
4042
- function parseVec3(text) {
4043
- const parts = text.trim().split(/\s+/);
4044
- const [x = 0, y = 0, z = 0] = parts.map((part) => Number.parseFloat(part)).map((value) => Number.isNaN(value) ? 0 : value);
4045
- return { x, y, z };
4046
- }
4047
- function parseBoolean(text) {
3989
+ // src/combat/damageMods.ts
3990
+ var DamageMod = /* @__PURE__ */ ((DamageMod2) => {
3991
+ DamageMod2[DamageMod2["UNKNOWN"] = 0] = "UNKNOWN";
3992
+ DamageMod2[DamageMod2["BLASTER"] = 1] = "BLASTER";
3993
+ DamageMod2[DamageMod2["SHOTGUN"] = 2] = "SHOTGUN";
3994
+ DamageMod2[DamageMod2["SSHOTGUN"] = 3] = "SSHOTGUN";
3995
+ DamageMod2[DamageMod2["MACHINEGUN"] = 4] = "MACHINEGUN";
3996
+ DamageMod2[DamageMod2["CHAINGUN"] = 5] = "CHAINGUN";
3997
+ DamageMod2[DamageMod2["GRENADE"] = 6] = "GRENADE";
3998
+ DamageMod2[DamageMod2["G_SPLASH"] = 7] = "G_SPLASH";
3999
+ DamageMod2[DamageMod2["ROCKET"] = 8] = "ROCKET";
4000
+ DamageMod2[DamageMod2["R_SPLASH"] = 9] = "R_SPLASH";
4001
+ DamageMod2[DamageMod2["HYPERBLASTER"] = 10] = "HYPERBLASTER";
4002
+ DamageMod2[DamageMod2["RAILGUN"] = 11] = "RAILGUN";
4003
+ DamageMod2[DamageMod2["BFG_LASER"] = 12] = "BFG_LASER";
4004
+ DamageMod2[DamageMod2["BFG_BLAST"] = 13] = "BFG_BLAST";
4005
+ DamageMod2[DamageMod2["BFG_EFFECT"] = 14] = "BFG_EFFECT";
4006
+ DamageMod2[DamageMod2["HANDGRENADE"] = 15] = "HANDGRENADE";
4007
+ DamageMod2[DamageMod2["HG_SPLASH"] = 16] = "HG_SPLASH";
4008
+ DamageMod2[DamageMod2["WATER"] = 17] = "WATER";
4009
+ DamageMod2[DamageMod2["SLIME"] = 18] = "SLIME";
4010
+ DamageMod2[DamageMod2["LAVA"] = 19] = "LAVA";
4011
+ DamageMod2[DamageMod2["CRUSH"] = 20] = "CRUSH";
4012
+ DamageMod2[DamageMod2["TELEFRAG"] = 21] = "TELEFRAG";
4013
+ DamageMod2[DamageMod2["TELEFRAG_SPAWN"] = 22] = "TELEFRAG_SPAWN";
4014
+ DamageMod2[DamageMod2["FALLING"] = 23] = "FALLING";
4015
+ DamageMod2[DamageMod2["SUICIDE"] = 24] = "SUICIDE";
4016
+ DamageMod2[DamageMod2["HELD_GRENADE"] = 25] = "HELD_GRENADE";
4017
+ DamageMod2[DamageMod2["EXPLOSIVE"] = 26] = "EXPLOSIVE";
4018
+ DamageMod2[DamageMod2["BARREL"] = 27] = "BARREL";
4019
+ DamageMod2[DamageMod2["BOMB"] = 28] = "BOMB";
4020
+ DamageMod2[DamageMod2["EXIT"] = 29] = "EXIT";
4021
+ DamageMod2[DamageMod2["SPLASH"] = 30] = "SPLASH";
4022
+ DamageMod2[DamageMod2["TARGET_LASER"] = 31] = "TARGET_LASER";
4023
+ DamageMod2[DamageMod2["TRIGGER_HURT"] = 32] = "TRIGGER_HURT";
4024
+ DamageMod2[DamageMod2["HIT"] = 33] = "HIT";
4025
+ DamageMod2[DamageMod2["TARGET_BLASTER"] = 34] = "TARGET_BLASTER";
4026
+ DamageMod2[DamageMod2["RIPPER"] = 35] = "RIPPER";
4027
+ DamageMod2[DamageMod2["PHALANX"] = 36] = "PHALANX";
4028
+ DamageMod2[DamageMod2["BRAINTENTACLE"] = 37] = "BRAINTENTACLE";
4029
+ DamageMod2[DamageMod2["BLASTOFF"] = 38] = "BLASTOFF";
4030
+ DamageMod2[DamageMod2["GEKK"] = 39] = "GEKK";
4031
+ DamageMod2[DamageMod2["TRAP"] = 40] = "TRAP";
4032
+ DamageMod2[DamageMod2["CHAINFIST"] = 41] = "CHAINFIST";
4033
+ DamageMod2[DamageMod2["DISINTEGRATOR"] = 42] = "DISINTEGRATOR";
4034
+ DamageMod2[DamageMod2["ETF_RIFLE"] = 43] = "ETF_RIFLE";
4035
+ DamageMod2[DamageMod2["BLASTER2"] = 44] = "BLASTER2";
4036
+ DamageMod2[DamageMod2["HEATBEAM"] = 45] = "HEATBEAM";
4037
+ DamageMod2[DamageMod2["TESLA"] = 46] = "TESLA";
4038
+ DamageMod2[DamageMod2["PROX"] = 47] = "PROX";
4039
+ DamageMod2[DamageMod2["NUKE"] = 48] = "NUKE";
4040
+ DamageMod2[DamageMod2["VENGEANCE_SPHERE"] = 49] = "VENGEANCE_SPHERE";
4041
+ DamageMod2[DamageMod2["HUNTER_SPHERE"] = 50] = "HUNTER_SPHERE";
4042
+ DamageMod2[DamageMod2["DEFENDER_SPHERE"] = 51] = "DEFENDER_SPHERE";
4043
+ DamageMod2[DamageMod2["TRACKER"] = 52] = "TRACKER";
4044
+ DamageMod2[DamageMod2["DBALL_CRUSH"] = 53] = "DBALL_CRUSH";
4045
+ DamageMod2[DamageMod2["DOPPLE_EXPLODE"] = 54] = "DOPPLE_EXPLODE";
4046
+ DamageMod2[DamageMod2["DOPPLE_VENGEANCE"] = 55] = "DOPPLE_VENGEANCE";
4047
+ DamageMod2[DamageMod2["DOPPLE_HUNTER"] = 56] = "DOPPLE_HUNTER";
4048
+ DamageMod2[DamageMod2["GRAPPLE"] = 57] = "GRAPPLE";
4049
+ DamageMod2[DamageMod2["BLUEBLASTER"] = 58] = "BLUEBLASTER";
4050
+ return DamageMod2;
4051
+ })(DamageMod || {});
4052
+ var ORDERED_DAMAGE_MODS = [
4053
+ 0 /* UNKNOWN */,
4054
+ 1 /* BLASTER */,
4055
+ 2 /* SHOTGUN */,
4056
+ 3 /* SSHOTGUN */,
4057
+ 4 /* MACHINEGUN */,
4058
+ 5 /* CHAINGUN */,
4059
+ 6 /* GRENADE */,
4060
+ 7 /* G_SPLASH */,
4061
+ 8 /* ROCKET */,
4062
+ 9 /* R_SPLASH */,
4063
+ 10 /* HYPERBLASTER */,
4064
+ 11 /* RAILGUN */,
4065
+ 12 /* BFG_LASER */,
4066
+ 13 /* BFG_BLAST */,
4067
+ 14 /* BFG_EFFECT */,
4068
+ 15 /* HANDGRENADE */,
4069
+ 16 /* HG_SPLASH */,
4070
+ 17 /* WATER */,
4071
+ 18 /* SLIME */,
4072
+ 19 /* LAVA */,
4073
+ 20 /* CRUSH */,
4074
+ 21 /* TELEFRAG */,
4075
+ 22 /* TELEFRAG_SPAWN */,
4076
+ 23 /* FALLING */,
4077
+ 24 /* SUICIDE */,
4078
+ 25 /* HELD_GRENADE */,
4079
+ 26 /* EXPLOSIVE */,
4080
+ 27 /* BARREL */,
4081
+ 28 /* BOMB */,
4082
+ 29 /* EXIT */,
4083
+ 30 /* SPLASH */,
4084
+ 31 /* TARGET_LASER */,
4085
+ 32 /* TRIGGER_HURT */,
4086
+ 33 /* HIT */,
4087
+ 34 /* TARGET_BLASTER */,
4088
+ 35 /* RIPPER */,
4089
+ 36 /* PHALANX */,
4090
+ 37 /* BRAINTENTACLE */,
4091
+ 38 /* BLASTOFF */,
4092
+ 39 /* GEKK */,
4093
+ 40 /* TRAP */,
4094
+ 41 /* CHAINFIST */,
4095
+ 42 /* DISINTEGRATOR */,
4096
+ 43 /* ETF_RIFLE */,
4097
+ 44 /* BLASTER2 */,
4098
+ 45 /* HEATBEAM */,
4099
+ 46 /* TESLA */,
4100
+ 47 /* PROX */,
4101
+ 48 /* NUKE */,
4102
+ 49 /* VENGEANCE_SPHERE */,
4103
+ 50 /* HUNTER_SPHERE */,
4104
+ 51 /* DEFENDER_SPHERE */,
4105
+ 52 /* TRACKER */,
4106
+ 53 /* DBALL_CRUSH */,
4107
+ 54 /* DOPPLE_EXPLODE */,
4108
+ 55 /* DOPPLE_VENGEANCE */,
4109
+ 56 /* DOPPLE_HUNTER */,
4110
+ 57 /* GRAPPLE */,
4111
+ 58 /* BLUEBLASTER */
4112
+ ];
4113
+ function damageModName(mod) {
4114
+ return `MOD_${DamageMod[mod]}`;
4115
+ }
4116
+
4117
+ // src/combat/damage.ts
4118
+ var EntityDamageFlags = /* @__PURE__ */ ((EntityDamageFlags2) => {
4119
+ EntityDamageFlags2[EntityDamageFlags2["GODMODE"] = 1] = "GODMODE";
4120
+ EntityDamageFlags2[EntityDamageFlags2["IMMORTAL"] = 2] = "IMMORTAL";
4121
+ EntityDamageFlags2[EntityDamageFlags2["NO_KNOCKBACK"] = 4] = "NO_KNOCKBACK";
4122
+ EntityDamageFlags2[EntityDamageFlags2["NO_DAMAGE_EFFECTS"] = 8] = "NO_DAMAGE_EFFECTS";
4123
+ return EntityDamageFlags2;
4124
+ })(EntityDamageFlags || {});
4125
+ function applyKnockback(targ, attacker, dir, knockback, dflags) {
4126
+ const hasNoKnockback = hasAnyDamageFlag(dflags, 8 /* NO_KNOCKBACK */) || ((targ.flags ?? 0) & 4 /* NO_KNOCKBACK */) !== 0;
4127
+ if (hasNoKnockback || knockback === 0) {
4128
+ return { x: 0, y: 0, z: 0 };
4129
+ }
4130
+ const mass = Math.max(50, targ.mass ?? 200);
4131
+ const normalized = normalizeVec3(dir);
4132
+ const scale = attacker === targ ? 1600 : 500;
4133
+ const delta = scaleVec3(normalized, scale * knockback / mass);
4134
+ targ.velocity = addVec3(targ.velocity, delta);
4135
+ return delta;
4136
+ }
4137
+ function applyProtection(targ, point, normal, damage, dflags) {
4138
+ let take = damage;
4139
+ let psave = 0;
4140
+ let asave = 0;
4141
+ let remainingCells;
4142
+ let remainingArmor;
4143
+ if (targ.powerArmor) {
4144
+ const result = applyPowerArmor(damage, dflags, point, normal, targ.powerArmor);
4145
+ psave = result.saved;
4146
+ remainingCells = result.remainingCells;
4147
+ take -= psave;
4148
+ }
4149
+ if (targ.regularArmor) {
4150
+ const result = applyRegularArmor(take, dflags, targ.regularArmor);
4151
+ asave = result.saved;
4152
+ remainingArmor = result.remainingArmor;
4153
+ take -= asave;
4154
+ }
4155
+ return [Math.max(0, take), psave, asave, remainingCells, remainingArmor];
4156
+ }
4157
+ function targetCenter(ent) {
4158
+ if (ent.mins && ent.maxs) {
4159
+ return {
4160
+ x: ent.origin.x + (ent.mins.x + ent.maxs.x) * 0.5,
4161
+ y: ent.origin.y + (ent.mins.y + ent.maxs.y) * 0.5,
4162
+ z: ent.origin.z + (ent.mins.z + ent.maxs.z) * 0.5
4163
+ };
4164
+ }
4165
+ return ent.origin;
4166
+ }
4167
+ function T_Damage(targ, inflictor, attacker, dir, point, normal, damage, knockback, dflags, mod) {
4168
+ if (!targ.takedamage || damage <= 0) {
4169
+ return null;
4170
+ }
4171
+ const protectedByGod = !hasAnyDamageFlag(dflags, 32 /* NO_PROTECTION */) && ((targ.flags ?? 0) & 1 /* GODMODE */) !== 0;
4172
+ if (protectedByGod) {
4173
+ return {
4174
+ take: 0,
4175
+ psave: 0,
4176
+ asave: damage,
4177
+ knocked: { x: 0, y: 0, z: 0 },
4178
+ killed: false
4179
+ };
4180
+ }
4181
+ const knocked = applyKnockback(targ, attacker, dir, knockback, dflags);
4182
+ const [take, psave, asave, remainingCells, remainingArmor] = applyProtection(targ, point, normal, damage, dflags);
4183
+ if (targ.powerArmor && remainingCells !== void 0) {
4184
+ targ.powerArmor.cellCount = remainingCells;
4185
+ }
4186
+ if (targ.regularArmor) {
4187
+ targ.regularArmor.armorCount = remainingArmor ?? targ.regularArmor.armorCount;
4188
+ }
4189
+ let actualTake = take;
4190
+ if (actualTake > 0) {
4191
+ targ.health -= actualTake;
4192
+ }
4193
+ const killed = targ.health <= 0;
4194
+ if (killed) {
4195
+ if (targ.flags && targ.flags & 2 /* IMMORTAL */) {
4196
+ targ.health = Math.max(1, targ.health);
4197
+ } else if (targ.die) {
4198
+ targ.die(targ, inflictor, attacker, actualTake, point, mod);
4199
+ }
4200
+ } else if (actualTake > 0 && targ.pain) {
4201
+ targ.pain(targ, attacker, knockback, actualTake, mod);
4202
+ }
4203
+ return { take: actualTake, psave, asave, knocked, killed, remainingCells, remainingArmor };
4204
+ }
4205
+ function T_RadiusDamage(entities, inflictor, attacker, damage, ignore, radius, dflags, mod, options = {}) {
4206
+ const hits = [];
4207
+ const inflictorCenter = targetCenter(inflictor);
4208
+ const canDamage = options.canDamage ?? (() => true);
4209
+ for (const ent of entities) {
4210
+ if (ent === ignore || !ent.takedamage || !canDamage(ent, inflictor)) {
4211
+ continue;
4212
+ }
4213
+ const entCenter = ent.mins && ent.maxs ? closestPointToBox(inflictorCenter, addVec3(ent.origin, ent.mins), addVec3(ent.origin, ent.maxs)) : targetCenter(ent);
4214
+ const toTarget = subtractVec3(inflictorCenter, entCenter);
4215
+ const distance2 = lengthVec3(toTarget);
4216
+ if (radius > 0 && distance2 > radius) {
4217
+ continue;
4218
+ }
4219
+ const points = damage - 0.5 * distance2;
4220
+ if (points <= 0) {
4221
+ continue;
4222
+ }
4223
+ const adjustedDamage = ent === attacker ? points * 0.5 : points;
4224
+ const dir = normalizeVec3(subtractVec3(ent.origin, inflictorCenter));
4225
+ const result = T_Damage(ent, inflictor, attacker, dir, entCenter, dir, adjustedDamage, adjustedDamage, dflags | 1 /* RADIUS */, mod);
4226
+ hits.push({ target: ent, result, appliedDamage: adjustedDamage });
4227
+ }
4228
+ return hits;
4229
+ }
4230
+
4231
+ // src/entities/monsters/attack.ts
4232
+ function monster_fire_bullet(self, start, dir, damage, kick, hspread, vspread, flashtype, context, mod = 0 /* UNKNOWN */) {
4233
+ const end = addVec3(start, scaleVec3(dir, 8192));
4234
+ const tr = context.trace(start, null, null, end, self, 1 | 536870912);
4235
+ if (!tr.ent || tr.fraction === 1) {
4236
+ return;
4237
+ }
4238
+ T_Damage(
4239
+ tr.ent,
4240
+ self,
4241
+ self,
4242
+ dir,
4243
+ tr.endpos,
4244
+ tr.plane?.normal || ZERO_VEC3,
4245
+ damage,
4246
+ kick,
4247
+ 16 /* BULLET */ | 2 /* NO_ARMOR */,
4248
+ // Usually monsters ignore armor? Or maybe not.
4249
+ mod
4250
+ );
4251
+ }
4252
+
4253
+ // src/entities/projectiles.ts
4254
+ function createGrenade(sys, owner, start, dir, damage, speed) {
4255
+ const grenade = sys.spawn();
4256
+ grenade.classname = "grenade";
4257
+ grenade.owner = owner;
4258
+ grenade.origin = { ...start };
4259
+ grenade.velocity = { x: dir.x * speed, y: dir.y * speed, z: dir.z * speed };
4260
+ grenade.movetype = 9 /* Bounce */;
4261
+ grenade.clipmask = 268566530;
4262
+ grenade.solid = 2 /* BoundingBox */;
4263
+ grenade.modelindex = sys.modelIndex("models/objects/grenade/tris.md2");
4264
+ grenade.mins = { x: -4, y: -4, z: -4 };
4265
+ grenade.maxs = { x: 4, y: 4, z: 4 };
4266
+ grenade.touch = (self, other, plane, surf) => {
4267
+ if (other === self.owner) {
4268
+ return;
4269
+ }
4270
+ if (other && other.takedamage) {
4271
+ T_Damage(
4272
+ other,
4273
+ self,
4274
+ self.owner,
4275
+ self.velocity,
4276
+ self.origin,
4277
+ plane ? plane.normal : ZERO_VEC3,
4278
+ damage,
4279
+ // Direct impact damage? Usually separate but let's assume it's part of it or handled by radius.
4280
+ // Actually G_Weapon.c Grenade_Touch calls Grenade_Explode which calls T_RadiusDamage.
4281
+ // It doesn't seem to do T_Damage separately?
4282
+ // Wait, if it hits a monster, it stops and explodes.
4283
+ 0,
4284
+ 0 /* NONE */,
4285
+ 6 /* GRENADE */
4286
+ );
4287
+ const entities = sys.findByRadius(self.origin, 120);
4288
+ T_RadiusDamage(entities, self, self.owner, damage, self.owner, 120, 0 /* NONE */, 6 /* GRENADE */);
4289
+ sys.free(self);
4290
+ return;
4291
+ }
4292
+ };
4293
+ grenade.think = (self) => {
4294
+ const entities = sys.findByRadius(self.origin, 120);
4295
+ T_RadiusDamage(entities, self, self.owner, damage, self.owner, 120, 0 /* NONE */, 6 /* GRENADE */);
4296
+ sys.free(self);
4297
+ };
4298
+ sys.scheduleThink(grenade, sys.timeSeconds + 2.5);
4299
+ sys.finalizeSpawn(grenade);
4300
+ }
4301
+
4302
+ // src/entities/monsters/gunner.ts
4303
+ var MONSTER_TICK = 0.1;
4304
+ function monster_ai_stand(self, dist, context) {
4305
+ ai_stand(self, MONSTER_TICK);
4306
+ }
4307
+ function monster_ai_walk(self, dist, context) {
4308
+ ai_walk(self, dist, MONSTER_TICK, context);
4309
+ }
4310
+ function monster_ai_run(self, dist, context) {
4311
+ ai_run(self, dist, MONSTER_TICK);
4312
+ }
4313
+ function monster_ai_charge(self, dist, context) {
4314
+ ai_charge(self, dist, MONSTER_TICK);
4315
+ }
4316
+ var stand_move;
4317
+ var walk_move;
4318
+ var run_move;
4319
+ var attack_chain_move;
4320
+ var attack_grenade_move;
4321
+ function gunner_stand(self) {
4322
+ self.monsterinfo.current_move = stand_move;
4323
+ }
4324
+ function gunner_walk(self) {
4325
+ self.monsterinfo.current_move = walk_move;
4326
+ }
4327
+ function gunner_run(self) {
4328
+ if (self.enemy && self.enemy.health > 0) {
4329
+ self.monsterinfo.current_move = run_move;
4330
+ } else {
4331
+ self.monsterinfo.current_move = stand_move;
4332
+ }
4333
+ }
4334
+ function gunner_attack(self) {
4335
+ if (Math.random() > 0.5) {
4336
+ self.monsterinfo.current_move = attack_chain_move;
4337
+ } else {
4338
+ self.monsterinfo.current_move = attack_grenade_move;
4339
+ }
4340
+ }
4341
+ function gunner_fire_chain(self, context) {
4342
+ if (!self.enemy) return;
4343
+ const start = {
4344
+ x: self.origin.x,
4345
+ y: self.origin.y,
4346
+ z: self.origin.z + self.viewheight - 8
4347
+ };
4348
+ const forward = normalizeVec3(subtractVec3(self.enemy.origin, start));
4349
+ const damage = 10;
4350
+ const kick = 2;
4351
+ monster_fire_bullet(self, start, forward, damage, kick, 0, 0, 0, context, 5 /* CHAINGUN */);
4352
+ }
4353
+ function gunner_fire_grenade(self, context) {
4354
+ if (!self.enemy) return;
4355
+ const start = {
4356
+ x: self.origin.x,
4357
+ y: self.origin.y,
4358
+ z: self.origin.z + self.viewheight
4359
+ };
4360
+ const forward = normalizeVec3(subtractVec3(self.enemy.origin, start));
4361
+ const damage = 50;
4362
+ const speed = 600;
4363
+ createGrenade(context, self, start, forward, damage, speed);
4364
+ }
4365
+ var stand_frames = Array.from({ length: 30 }, () => ({
4366
+ ai: monster_ai_stand,
4367
+ dist: 0
4368
+ }));
4369
+ stand_move = {
4370
+ firstframe: 0,
4371
+ lastframe: 29,
4372
+ frames: stand_frames,
4373
+ endfunc: gunner_stand
4374
+ };
4375
+ var walk_frames = Array.from({ length: 40 }, () => ({
4376
+ ai: monster_ai_walk,
4377
+ dist: 2
4378
+ }));
4379
+ walk_move = {
4380
+ firstframe: 30,
4381
+ lastframe: 69,
4382
+ frames: walk_frames,
4383
+ endfunc: gunner_walk
4384
+ };
4385
+ var run_frames = Array.from({ length: 20 }, () => ({
4386
+ ai: monster_ai_run,
4387
+ dist: 10
4388
+ }));
4389
+ run_move = {
4390
+ firstframe: 70,
4391
+ lastframe: 89,
4392
+ frames: run_frames,
4393
+ endfunc: gunner_run
4394
+ };
4395
+ var attack_chain_frames = Array.from({ length: 10 }, (_, i) => ({
4396
+ ai: monster_ai_charge,
4397
+ dist: 0,
4398
+ think: i >= 2 && i <= 8 ? gunner_fire_chain : null
4399
+ }));
4400
+ attack_chain_move = {
4401
+ firstframe: 90,
4402
+ lastframe: 99,
4403
+ frames: attack_chain_frames,
4404
+ endfunc: gunner_run
4405
+ };
4406
+ var attack_grenade_frames = Array.from({ length: 10 }, (_, i) => ({
4407
+ ai: monster_ai_charge,
4408
+ dist: 0,
4409
+ think: i === 5 ? gunner_fire_grenade : null
4410
+ }));
4411
+ attack_grenade_move = {
4412
+ firstframe: 100,
4413
+ lastframe: 109,
4414
+ frames: attack_grenade_frames,
4415
+ endfunc: gunner_run
4416
+ };
4417
+ function SP_monster_gunner(self, context) {
4418
+ self.model = "models/monsters/gunner/tris.md2";
4419
+ self.mins = { x: -16, y: -16, z: -24 };
4420
+ self.maxs = { x: 16, y: 16, z: 32 };
4421
+ self.movetype = 5 /* Step */;
4422
+ self.solid = 2 /* BoundingBox */;
4423
+ self.health = 175;
4424
+ self.max_health = 175;
4425
+ self.mass = 200;
4426
+ self.pain = (self2, other, kick, damage) => {
4427
+ };
4428
+ self.die = (self2, inflictor, attacker, damage, point) => {
4429
+ self2.deadflag = 2 /* Dead */;
4430
+ self2.solid = 0 /* Not */;
4431
+ };
4432
+ self.monsterinfo.stand = gunner_stand;
4433
+ self.monsterinfo.walk = gunner_walk;
4434
+ self.monsterinfo.run = gunner_run;
4435
+ self.monsterinfo.attack = gunner_attack;
4436
+ self.think = monster_think;
4437
+ gunner_stand(self);
4438
+ self.nextthink = self.timestamp + MONSTER_TICK;
4439
+ }
4440
+ function registerGunnerSpawns(registry) {
4441
+ registry.register("monster_gunner", SP_monster_gunner);
4442
+ }
4443
+
4444
+ // src/entities/monsters/soldier.ts
4445
+ var MONSTER_TICK2 = 0.1;
4446
+ function monster_ai_stand2(self, dist, context) {
4447
+ ai_stand(self, MONSTER_TICK2);
4448
+ }
4449
+ function monster_ai_walk2(self, dist, context) {
4450
+ ai_walk(self, dist, MONSTER_TICK2, context);
4451
+ }
4452
+ function monster_ai_run2(self, dist, context) {
4453
+ ai_run(self, dist, MONSTER_TICK2);
4454
+ }
4455
+ function monster_ai_charge2(self, dist, context) {
4456
+ ai_charge(self, dist, MONSTER_TICK2);
4457
+ }
4458
+ var stand_move2;
4459
+ var walk_move2;
4460
+ var run_move2;
4461
+ var attack_move;
4462
+ function soldier_stand(self) {
4463
+ self.monsterinfo.current_move = stand_move2;
4464
+ }
4465
+ function soldier_walk(self) {
4466
+ self.monsterinfo.current_move = walk_move2;
4467
+ }
4468
+ function soldier_run(self) {
4469
+ if (self.enemy && self.enemy.health > 0) {
4470
+ self.monsterinfo.current_move = run_move2;
4471
+ } else {
4472
+ self.monsterinfo.current_move = stand_move2;
4473
+ }
4474
+ }
4475
+ function soldier_attack(self) {
4476
+ self.monsterinfo.current_move = attack_move;
4477
+ }
4478
+ function soldier_fire(self, context) {
4479
+ if (!self.enemy) return;
4480
+ const start = {
4481
+ x: self.origin.x,
4482
+ y: self.origin.y,
4483
+ z: self.origin.z + self.viewheight
4484
+ };
4485
+ const forward = normalizeVec3(subtractVec3(self.enemy.origin, start));
4486
+ const damage = 4;
4487
+ const kick = 4;
4488
+ monster_fire_bullet(self, start, forward, damage, kick, 0, 0, 0, context, 4 /* MACHINEGUN */);
4489
+ }
4490
+ var stand_frames2 = Array.from({ length: 30 }, () => ({
4491
+ ai: monster_ai_stand2,
4492
+ dist: 0
4493
+ }));
4494
+ stand_move2 = {
4495
+ firstframe: 0,
4496
+ lastframe: 29,
4497
+ frames: stand_frames2,
4498
+ endfunc: soldier_stand
4499
+ };
4500
+ var walk_frames2 = Array.from({ length: 40 }, () => ({
4501
+ ai: monster_ai_walk2,
4502
+ dist: 2
4503
+ }));
4504
+ walk_move2 = {
4505
+ firstframe: 30,
4506
+ lastframe: 69,
4507
+ frames: walk_frames2,
4508
+ endfunc: soldier_walk
4509
+ };
4510
+ var run_frames2 = Array.from({ length: 20 }, () => ({
4511
+ ai: monster_ai_run2,
4512
+ dist: 10
4513
+ }));
4514
+ run_move2 = {
4515
+ firstframe: 70,
4516
+ lastframe: 89,
4517
+ frames: run_frames2,
4518
+ endfunc: soldier_run
4519
+ };
4520
+ var attack_frames = Array.from({ length: 10 }, (_, i) => ({
4521
+ ai: monster_ai_charge2,
4522
+ dist: 0,
4523
+ think: i === 5 ? soldier_fire : null
4524
+ }));
4525
+ attack_move = {
4526
+ firstframe: 90,
4527
+ lastframe: 99,
4528
+ frames: attack_frames,
4529
+ endfunc: soldier_run
4530
+ };
4531
+ function SP_monster_soldier(self, context) {
4532
+ self.model = "models/monsters/soldier/tris.md2";
4533
+ self.mins = { x: -16, y: -16, z: -24 };
4534
+ self.maxs = { x: 16, y: 16, z: 32 };
4535
+ self.movetype = 5 /* Step */;
4536
+ self.solid = 2 /* BoundingBox */;
4537
+ self.health = 20;
4538
+ self.max_health = 20;
4539
+ self.mass = 100;
4540
+ self.pain = (self2, other, kick, damage) => {
4541
+ };
4542
+ self.die = (self2, inflictor, attacker, damage, point) => {
4543
+ self2.deadflag = 2 /* Dead */;
4544
+ self2.solid = 0 /* Not */;
4545
+ };
4546
+ self.monsterinfo.stand = soldier_stand;
4547
+ self.monsterinfo.walk = soldier_walk;
4548
+ self.monsterinfo.run = soldier_run;
4549
+ self.monsterinfo.attack = soldier_attack;
4550
+ self.think = monster_think;
4551
+ soldier_stand(self);
4552
+ self.nextthink = self.timestamp + MONSTER_TICK2;
4553
+ }
4554
+ function registerMonsterSpawns(registry) {
4555
+ registry.register("monster_soldier", SP_monster_soldier);
4556
+ }
4557
+
4558
+ // src/entities/spawn.ts
4559
+ var FIELD_LOOKUP = new Map(
4560
+ ENTITY_FIELD_METADATA.map((field) => [field.name, field])
4561
+ );
4562
+ function parseVec3(text) {
4563
+ const parts = text.trim().split(/\s+/);
4564
+ const [x = 0, y = 0, z = 0] = parts.map((part) => Number.parseFloat(part)).map((value) => Number.isNaN(value) ? 0 : value);
4565
+ return { x, y, z };
4566
+ }
4567
+ function parseBoolean(text) {
4048
4568
  const normalized = text.trim().toLowerCase();
4049
4569
  return normalized === "1" || normalized === "true" || normalized === "yes";
4050
4570
  }
@@ -4250,6 +4770,7 @@ function registerDefaultSpawns(game, registry) {
4250
4770
  registerPathSpawns(registry);
4251
4771
  registerLightSpawns(registry);
4252
4772
  registerMonsterSpawns(registry);
4773
+ registerGunnerSpawns(registry);
4253
4774
  }
4254
4775
  function createDefaultSpawnRegistry(game) {
4255
4776
  const registry = new SpawnRegistry();
@@ -5116,248 +5637,6 @@ _SaveStorage.DEFAULT_STORE = "saves";
5116
5637
  _SaveStorage.QUICK_SLOT = "quicksave";
5117
5638
  var SaveStorage = _SaveStorage;
5118
5639
 
5119
- // src/combat/damage.ts
5120
- var EntityDamageFlags = /* @__PURE__ */ ((EntityDamageFlags2) => {
5121
- EntityDamageFlags2[EntityDamageFlags2["GODMODE"] = 1] = "GODMODE";
5122
- EntityDamageFlags2[EntityDamageFlags2["IMMORTAL"] = 2] = "IMMORTAL";
5123
- EntityDamageFlags2[EntityDamageFlags2["NO_KNOCKBACK"] = 4] = "NO_KNOCKBACK";
5124
- EntityDamageFlags2[EntityDamageFlags2["NO_DAMAGE_EFFECTS"] = 8] = "NO_DAMAGE_EFFECTS";
5125
- return EntityDamageFlags2;
5126
- })(EntityDamageFlags || {});
5127
- function applyKnockback(targ, attacker, dir, knockback, dflags) {
5128
- const hasNoKnockback = hasAnyDamageFlag(dflags, 8 /* NO_KNOCKBACK */) || ((targ.flags ?? 0) & 4 /* NO_KNOCKBACK */) !== 0;
5129
- if (hasNoKnockback || knockback === 0) {
5130
- return { x: 0, y: 0, z: 0 };
5131
- }
5132
- const mass = Math.max(50, targ.mass ?? 200);
5133
- const normalized = normalizeVec3(dir);
5134
- const scale = attacker === targ ? 1600 : 500;
5135
- const delta = scaleVec3(normalized, scale * knockback / mass);
5136
- targ.velocity = addVec3(targ.velocity, delta);
5137
- return delta;
5138
- }
5139
- function applyProtection(targ, point, normal, damage, dflags) {
5140
- let take = damage;
5141
- let psave = 0;
5142
- let asave = 0;
5143
- let remainingCells;
5144
- let remainingArmor;
5145
- if (targ.powerArmor) {
5146
- const result = applyPowerArmor(damage, dflags, point, normal, targ.powerArmor);
5147
- psave = result.saved;
5148
- remainingCells = result.remainingCells;
5149
- take -= psave;
5150
- }
5151
- if (targ.regularArmor) {
5152
- const result = applyRegularArmor(take, dflags, targ.regularArmor);
5153
- asave = result.saved;
5154
- remainingArmor = result.remainingArmor;
5155
- take -= asave;
5156
- }
5157
- return [Math.max(0, take), psave, asave, remainingCells, remainingArmor];
5158
- }
5159
- function targetCenter(ent) {
5160
- if (ent.mins && ent.maxs) {
5161
- return {
5162
- x: ent.origin.x + (ent.mins.x + ent.maxs.x) * 0.5,
5163
- y: ent.origin.y + (ent.mins.y + ent.maxs.y) * 0.5,
5164
- z: ent.origin.z + (ent.mins.z + ent.maxs.z) * 0.5
5165
- };
5166
- }
5167
- return ent.origin;
5168
- }
5169
- function T_Damage(targ, inflictor, attacker, dir, point, normal, damage, knockback, dflags, mod) {
5170
- if (!targ.takedamage || damage <= 0) {
5171
- return null;
5172
- }
5173
- const protectedByGod = !hasAnyDamageFlag(dflags, 32 /* NO_PROTECTION */) && ((targ.flags ?? 0) & 1 /* GODMODE */) !== 0;
5174
- if (protectedByGod) {
5175
- return {
5176
- take: 0,
5177
- psave: 0,
5178
- asave: damage,
5179
- knocked: { x: 0, y: 0, z: 0 },
5180
- killed: false
5181
- };
5182
- }
5183
- const knocked = applyKnockback(targ, attacker, dir, knockback, dflags);
5184
- const [take, psave, asave, remainingCells, remainingArmor] = applyProtection(targ, point, normal, damage, dflags);
5185
- if (targ.powerArmor && remainingCells !== void 0) {
5186
- targ.powerArmor.cellCount = remainingCells;
5187
- }
5188
- if (targ.regularArmor) {
5189
- targ.regularArmor.armorCount = remainingArmor ?? targ.regularArmor.armorCount;
5190
- }
5191
- let actualTake = take;
5192
- if (actualTake > 0) {
5193
- targ.health -= actualTake;
5194
- }
5195
- const killed = targ.health <= 0;
5196
- if (killed) {
5197
- if (targ.flags && targ.flags & 2 /* IMMORTAL */) {
5198
- targ.health = Math.max(1, targ.health);
5199
- } else if (targ.die) {
5200
- targ.die(targ, inflictor, attacker, actualTake, point, mod);
5201
- }
5202
- } else if (actualTake > 0 && targ.pain) {
5203
- targ.pain(targ, attacker, knockback, actualTake, mod);
5204
- }
5205
- return { take: actualTake, psave, asave, knocked, killed, remainingCells, remainingArmor };
5206
- }
5207
- function T_RadiusDamage(entities, inflictor, attacker, damage, ignore, radius, dflags, mod, options = {}) {
5208
- const hits = [];
5209
- const inflictorCenter = targetCenter(inflictor);
5210
- const canDamage = options.canDamage ?? (() => true);
5211
- for (const ent of entities) {
5212
- if (ent === ignore || !ent.takedamage || !canDamage(ent, inflictor)) {
5213
- continue;
5214
- }
5215
- const entCenter = ent.mins && ent.maxs ? closestPointToBox(inflictorCenter, addVec3(ent.origin, ent.mins), addVec3(ent.origin, ent.maxs)) : targetCenter(ent);
5216
- const toTarget = subtractVec3(inflictorCenter, entCenter);
5217
- const distance2 = lengthVec3(toTarget);
5218
- if (radius > 0 && distance2 > radius) {
5219
- continue;
5220
- }
5221
- const points = damage - 0.5 * distance2;
5222
- if (points <= 0) {
5223
- continue;
5224
- }
5225
- const adjustedDamage = ent === attacker ? points * 0.5 : points;
5226
- const dir = normalizeVec3(subtractVec3(ent.origin, inflictorCenter));
5227
- const result = T_Damage(ent, inflictor, attacker, dir, entCenter, dir, adjustedDamage, adjustedDamage, dflags | 1 /* RADIUS */, mod);
5228
- hits.push({ target: ent, result, appliedDamage: adjustedDamage });
5229
- }
5230
- return hits;
5231
- }
5232
-
5233
- // src/combat/damageMods.ts
5234
- var DamageMod = /* @__PURE__ */ ((DamageMod2) => {
5235
- DamageMod2[DamageMod2["UNKNOWN"] = 0] = "UNKNOWN";
5236
- DamageMod2[DamageMod2["BLASTER"] = 1] = "BLASTER";
5237
- DamageMod2[DamageMod2["SHOTGUN"] = 2] = "SHOTGUN";
5238
- DamageMod2[DamageMod2["SSHOTGUN"] = 3] = "SSHOTGUN";
5239
- DamageMod2[DamageMod2["MACHINEGUN"] = 4] = "MACHINEGUN";
5240
- DamageMod2[DamageMod2["CHAINGUN"] = 5] = "CHAINGUN";
5241
- DamageMod2[DamageMod2["GRENADE"] = 6] = "GRENADE";
5242
- DamageMod2[DamageMod2["G_SPLASH"] = 7] = "G_SPLASH";
5243
- DamageMod2[DamageMod2["ROCKET"] = 8] = "ROCKET";
5244
- DamageMod2[DamageMod2["R_SPLASH"] = 9] = "R_SPLASH";
5245
- DamageMod2[DamageMod2["HYPERBLASTER"] = 10] = "HYPERBLASTER";
5246
- DamageMod2[DamageMod2["RAILGUN"] = 11] = "RAILGUN";
5247
- DamageMod2[DamageMod2["BFG_LASER"] = 12] = "BFG_LASER";
5248
- DamageMod2[DamageMod2["BFG_BLAST"] = 13] = "BFG_BLAST";
5249
- DamageMod2[DamageMod2["BFG_EFFECT"] = 14] = "BFG_EFFECT";
5250
- DamageMod2[DamageMod2["HANDGRENADE"] = 15] = "HANDGRENADE";
5251
- DamageMod2[DamageMod2["HG_SPLASH"] = 16] = "HG_SPLASH";
5252
- DamageMod2[DamageMod2["WATER"] = 17] = "WATER";
5253
- DamageMod2[DamageMod2["SLIME"] = 18] = "SLIME";
5254
- DamageMod2[DamageMod2["LAVA"] = 19] = "LAVA";
5255
- DamageMod2[DamageMod2["CRUSH"] = 20] = "CRUSH";
5256
- DamageMod2[DamageMod2["TELEFRAG"] = 21] = "TELEFRAG";
5257
- DamageMod2[DamageMod2["TELEFRAG_SPAWN"] = 22] = "TELEFRAG_SPAWN";
5258
- DamageMod2[DamageMod2["FALLING"] = 23] = "FALLING";
5259
- DamageMod2[DamageMod2["SUICIDE"] = 24] = "SUICIDE";
5260
- DamageMod2[DamageMod2["HELD_GRENADE"] = 25] = "HELD_GRENADE";
5261
- DamageMod2[DamageMod2["EXPLOSIVE"] = 26] = "EXPLOSIVE";
5262
- DamageMod2[DamageMod2["BARREL"] = 27] = "BARREL";
5263
- DamageMod2[DamageMod2["BOMB"] = 28] = "BOMB";
5264
- DamageMod2[DamageMod2["EXIT"] = 29] = "EXIT";
5265
- DamageMod2[DamageMod2["SPLASH"] = 30] = "SPLASH";
5266
- DamageMod2[DamageMod2["TARGET_LASER"] = 31] = "TARGET_LASER";
5267
- DamageMod2[DamageMod2["TRIGGER_HURT"] = 32] = "TRIGGER_HURT";
5268
- DamageMod2[DamageMod2["HIT"] = 33] = "HIT";
5269
- DamageMod2[DamageMod2["TARGET_BLASTER"] = 34] = "TARGET_BLASTER";
5270
- DamageMod2[DamageMod2["RIPPER"] = 35] = "RIPPER";
5271
- DamageMod2[DamageMod2["PHALANX"] = 36] = "PHALANX";
5272
- DamageMod2[DamageMod2["BRAINTENTACLE"] = 37] = "BRAINTENTACLE";
5273
- DamageMod2[DamageMod2["BLASTOFF"] = 38] = "BLASTOFF";
5274
- DamageMod2[DamageMod2["GEKK"] = 39] = "GEKK";
5275
- DamageMod2[DamageMod2["TRAP"] = 40] = "TRAP";
5276
- DamageMod2[DamageMod2["CHAINFIST"] = 41] = "CHAINFIST";
5277
- DamageMod2[DamageMod2["DISINTEGRATOR"] = 42] = "DISINTEGRATOR";
5278
- DamageMod2[DamageMod2["ETF_RIFLE"] = 43] = "ETF_RIFLE";
5279
- DamageMod2[DamageMod2["BLASTER2"] = 44] = "BLASTER2";
5280
- DamageMod2[DamageMod2["HEATBEAM"] = 45] = "HEATBEAM";
5281
- DamageMod2[DamageMod2["TESLA"] = 46] = "TESLA";
5282
- DamageMod2[DamageMod2["PROX"] = 47] = "PROX";
5283
- DamageMod2[DamageMod2["NUKE"] = 48] = "NUKE";
5284
- DamageMod2[DamageMod2["VENGEANCE_SPHERE"] = 49] = "VENGEANCE_SPHERE";
5285
- DamageMod2[DamageMod2["HUNTER_SPHERE"] = 50] = "HUNTER_SPHERE";
5286
- DamageMod2[DamageMod2["DEFENDER_SPHERE"] = 51] = "DEFENDER_SPHERE";
5287
- DamageMod2[DamageMod2["TRACKER"] = 52] = "TRACKER";
5288
- DamageMod2[DamageMod2["DBALL_CRUSH"] = 53] = "DBALL_CRUSH";
5289
- DamageMod2[DamageMod2["DOPPLE_EXPLODE"] = 54] = "DOPPLE_EXPLODE";
5290
- DamageMod2[DamageMod2["DOPPLE_VENGEANCE"] = 55] = "DOPPLE_VENGEANCE";
5291
- DamageMod2[DamageMod2["DOPPLE_HUNTER"] = 56] = "DOPPLE_HUNTER";
5292
- DamageMod2[DamageMod2["GRAPPLE"] = 57] = "GRAPPLE";
5293
- DamageMod2[DamageMod2["BLUEBLASTER"] = 58] = "BLUEBLASTER";
5294
- return DamageMod2;
5295
- })(DamageMod || {});
5296
- var ORDERED_DAMAGE_MODS = [
5297
- 0 /* UNKNOWN */,
5298
- 1 /* BLASTER */,
5299
- 2 /* SHOTGUN */,
5300
- 3 /* SSHOTGUN */,
5301
- 4 /* MACHINEGUN */,
5302
- 5 /* CHAINGUN */,
5303
- 6 /* GRENADE */,
5304
- 7 /* G_SPLASH */,
5305
- 8 /* ROCKET */,
5306
- 9 /* R_SPLASH */,
5307
- 10 /* HYPERBLASTER */,
5308
- 11 /* RAILGUN */,
5309
- 12 /* BFG_LASER */,
5310
- 13 /* BFG_BLAST */,
5311
- 14 /* BFG_EFFECT */,
5312
- 15 /* HANDGRENADE */,
5313
- 16 /* HG_SPLASH */,
5314
- 17 /* WATER */,
5315
- 18 /* SLIME */,
5316
- 19 /* LAVA */,
5317
- 20 /* CRUSH */,
5318
- 21 /* TELEFRAG */,
5319
- 22 /* TELEFRAG_SPAWN */,
5320
- 23 /* FALLING */,
5321
- 24 /* SUICIDE */,
5322
- 25 /* HELD_GRENADE */,
5323
- 26 /* EXPLOSIVE */,
5324
- 27 /* BARREL */,
5325
- 28 /* BOMB */,
5326
- 29 /* EXIT */,
5327
- 30 /* SPLASH */,
5328
- 31 /* TARGET_LASER */,
5329
- 32 /* TRIGGER_HURT */,
5330
- 33 /* HIT */,
5331
- 34 /* TARGET_BLASTER */,
5332
- 35 /* RIPPER */,
5333
- 36 /* PHALANX */,
5334
- 37 /* BRAINTENTACLE */,
5335
- 38 /* BLASTOFF */,
5336
- 39 /* GEKK */,
5337
- 40 /* TRAP */,
5338
- 41 /* CHAINFIST */,
5339
- 42 /* DISINTEGRATOR */,
5340
- 43 /* ETF_RIFLE */,
5341
- 44 /* BLASTER2 */,
5342
- 45 /* HEATBEAM */,
5343
- 46 /* TESLA */,
5344
- 47 /* PROX */,
5345
- 48 /* NUKE */,
5346
- 49 /* VENGEANCE_SPHERE */,
5347
- 50 /* HUNTER_SPHERE */,
5348
- 51 /* DEFENDER_SPHERE */,
5349
- 52 /* TRACKER */,
5350
- 53 /* DBALL_CRUSH */,
5351
- 54 /* DOPPLE_EXPLODE */,
5352
- 55 /* DOPPLE_VENGEANCE */,
5353
- 56 /* DOPPLE_HUNTER */,
5354
- 57 /* GRAPPLE */,
5355
- 58 /* BLUEBLASTER */
5356
- ];
5357
- function damageModName(mod) {
5358
- return `MOD_${DamageMod[mod]}`;
5359
- }
5360
-
5361
5640
  // src/combat/specialDamage.ts
5362
5641
  var ZERO2 = { x: 0, y: 0, z: 0 };
5363
5642
  var EnvironmentalFlags = /* @__PURE__ */ ((EnvironmentalFlags2) => {
@@ -5836,12 +6115,17 @@ function createGame({ trace, pointcontents }, engine, options) {
5836
6115
  convertRereleaseLevelToGameSave,
5837
6116
  convertRereleaseSaveToGameSave,
5838
6117
  createAmmoInventory,
6118
+ createAmmoPickupEntity,
6119
+ createArmorPickupEntity,
5839
6120
  createBaseAmmoCaps,
5840
6121
  createCallbackRegistry,
5841
6122
  createDefaultSpawnRegistry,
5842
6123
  createGame,
6124
+ createHealthPickupEntity,
6125
+ createKeyPickupEntity,
5843
6126
  createPlayerInventory,
5844
6127
  createPlayerWeaponStates,
6128
+ createPowerupPickupEntity,
5845
6129
  createSaveFile,
5846
6130
  createWeaponPickupEntity,
5847
6131
  damageModName,