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
@@ -1385,6 +1385,12 @@ var EntitySystem = class {
1385
1385
  }
1386
1386
  }
1387
1387
  }
1388
+ get trace() {
1389
+ return this.imports.trace;
1390
+ }
1391
+ get pointcontents() {
1392
+ return this.imports.pointcontents;
1393
+ }
1388
1394
  get world() {
1389
1395
  return this.pool.world;
1390
1396
  }
@@ -2336,14 +2342,14 @@ var AmmoType = /* @__PURE__ */ ((AmmoType3) => {
2336
2342
  return AmmoType3;
2337
2343
  })(AmmoType || {});
2338
2344
  var AMMO_TYPE_COUNT = Object.keys(AmmoType).length / 2;
2339
- var AmmoItemId = /* @__PURE__ */ ((AmmoItemId3) => {
2340
- AmmoItemId3["Shells"] = "ammo_shells";
2341
- AmmoItemId3["Bullets"] = "ammo_bullets";
2342
- AmmoItemId3["Rockets"] = "ammo_rockets";
2343
- AmmoItemId3["Grenades"] = "ammo_grenades";
2344
- AmmoItemId3["Cells"] = "ammo_cells";
2345
- AmmoItemId3["Slugs"] = "ammo_slugs";
2346
- return AmmoItemId3;
2345
+ var AmmoItemId = /* @__PURE__ */ ((AmmoItemId4) => {
2346
+ AmmoItemId4["Shells"] = "ammo_shells";
2347
+ AmmoItemId4["Bullets"] = "ammo_bullets";
2348
+ AmmoItemId4["Rockets"] = "ammo_rockets";
2349
+ AmmoItemId4["Grenades"] = "ammo_grenades";
2350
+ AmmoItemId4["Cells"] = "ammo_cells";
2351
+ AmmoItemId4["Slugs"] = "ammo_slugs";
2352
+ return AmmoItemId4;
2347
2353
  })(AmmoItemId || {});
2348
2354
  var AMMO_ITEM_DEFINITIONS = {
2349
2355
  ["ammo_shells" /* Shells */]: { id: "ammo_shells" /* Shells */, ammoType: 1 /* Shells */, quantity: 10, weaponAmmo: false },
@@ -3078,6 +3084,32 @@ function createKeyPickupEntity(game, keyItem) {
3078
3084
  };
3079
3085
  }
3080
3086
 
3087
+ // src/entities/items/ammo.ts
3088
+ function createAmmoPickupEntity(game, itemId) {
3089
+ const def = getAmmoItemDefinition(itemId);
3090
+ const respawn = (self) => {
3091
+ self.solid = 1 /* Trigger */;
3092
+ };
3093
+ return {
3094
+ classname: itemId,
3095
+ solid: 1 /* Trigger */,
3096
+ touch: (self, other) => {
3097
+ if (!other || !other.client) {
3098
+ return;
3099
+ }
3100
+ const result = pickupAmmo(other.client.inventory.ammo, itemId);
3101
+ if (result.pickedUp) {
3102
+ game.sound?.(other, 0, "items/pkup.wav", 1, 1, 0);
3103
+ game.centerprintf?.(other, `You got ${def.quantity} ${itemId.replace("ammo_", "")}`);
3104
+ self.solid = 0 /* Not */;
3105
+ self.nextthink = game.time + 30;
3106
+ game.entities.scheduleThink(self, self.nextthink);
3107
+ }
3108
+ },
3109
+ think: respawn
3110
+ };
3111
+ }
3112
+
3081
3113
  // src/entities/items.ts
3082
3114
  function registerItemSpawns(game, registry) {
3083
3115
  for (const weaponItem of Object.values(WEAPON_ITEMS)) {
@@ -3105,6 +3137,11 @@ function registerItemSpawns(game, registry) {
3105
3137
  Object.assign(entity, createKeyPickupEntity(game, keyItem));
3106
3138
  });
3107
3139
  }
3140
+ for (const ammoId of Object.values(AmmoItemId)) {
3141
+ registry.register(ammoId, (entity) => {
3142
+ Object.assign(entity, createAmmoPickupEntity(game, ammoId));
3143
+ });
3144
+ }
3108
3145
  }
3109
3146
 
3110
3147
  // src/entities/funcs.ts
@@ -3169,6 +3206,7 @@ var func_door = (entity, context) => {
3169
3206
  self.state = 1 /* Opening */;
3170
3207
  self.think = door_go_up;
3171
3208
  context.entities.scheduleThink(self, context.entities.timeSeconds + 0.1);
3209
+ context.entities.sound(self, 0, "doors/dr1_strt.wav", 1, 1, 0);
3172
3210
  };
3173
3211
  };
3174
3212
  var func_button = (entity, context) => {
@@ -3176,6 +3214,7 @@ var func_button = (entity, context) => {
3176
3214
  entity.movetype = 2 /* Push */;
3177
3215
  entity.use = (self) => {
3178
3216
  context.entities.useTargets(self, self);
3217
+ context.entities.sound(self, 0, "switches/butn2.wav", 1, 1, 0);
3179
3218
  };
3180
3219
  };
3181
3220
  var TRAIN_START_ON = 1;
@@ -3453,6 +3492,66 @@ var TraceMask = /* @__PURE__ */ ((TraceMask2) => {
3453
3492
  return TraceMask2;
3454
3493
  })(TraceMask || {});
3455
3494
 
3495
+ // src/ai/perception.ts
3496
+ var RangeCategory = /* @__PURE__ */ ((RangeCategory2) => {
3497
+ RangeCategory2["Melee"] = "melee";
3498
+ RangeCategory2["Near"] = "near";
3499
+ RangeCategory2["Mid"] = "mid";
3500
+ RangeCategory2["Far"] = "far";
3501
+ return RangeCategory2;
3502
+ })(RangeCategory || {});
3503
+ function absBounds(entity) {
3504
+ return {
3505
+ mins: {
3506
+ x: entity.origin.x + entity.mins.x,
3507
+ y: entity.origin.y + entity.mins.y,
3508
+ z: entity.origin.z + entity.mins.z
3509
+ },
3510
+ maxs: {
3511
+ x: entity.origin.x + entity.maxs.x,
3512
+ y: entity.origin.y + entity.maxs.y,
3513
+ z: entity.origin.z + entity.maxs.z
3514
+ }
3515
+ };
3516
+ }
3517
+ function rangeTo(self, other) {
3518
+ const a = absBounds(self);
3519
+ const b = absBounds(other);
3520
+ const distanceSquared = distanceBetweenBoxesSquared(a.mins, a.maxs, b.mins, b.maxs);
3521
+ return Math.sqrt(distanceSquared);
3522
+ }
3523
+ function classifyRange(distance2) {
3524
+ if (distance2 <= RANGE_MELEE) {
3525
+ return "melee" /* Melee */;
3526
+ }
3527
+ if (distance2 <= RANGE_NEAR) {
3528
+ return "near" /* Near */;
3529
+ }
3530
+ if (distance2 <= RANGE_MID) {
3531
+ return "mid" /* Mid */;
3532
+ }
3533
+ return "far" /* Far */;
3534
+ }
3535
+ function infront(self, other) {
3536
+ const { forward } = angleVectors(self.angles);
3537
+ const direction = normalizeVec3(subtractVec3(other.origin, self.origin));
3538
+ const dot = dotVec3(direction, forward);
3539
+ if ((self.spawnflags & SPAWNFLAG_MONSTER_AMBUSH) !== 0 && self.trail_time === 0 && self.enemy === null) {
3540
+ return dot > 0.15;
3541
+ }
3542
+ return dot > -0.3;
3543
+ }
3544
+ function visible(self, other, trace, options) {
3545
+ if ((other.flags & FL_NOVISIBLE) !== 0) {
3546
+ return false;
3547
+ }
3548
+ const start = { x: self.origin.x, y: self.origin.y, z: self.origin.z + self.viewheight };
3549
+ const end = { x: other.origin.x, y: other.origin.y, z: other.origin.z + other.viewheight };
3550
+ const mask = options?.throughGlass ? 1 /* Opaque */ : 1 /* Opaque */ | 2 /* Window */;
3551
+ const result = trace(start, end, self, mask);
3552
+ return result.fraction === 1 || result.entity === other;
3553
+ }
3554
+
3456
3555
  // src/ai/movement.ts
3457
3556
  function yawVector(yawDegrees, distance2) {
3458
3557
  if (distance2 === 0) {
@@ -3514,12 +3613,24 @@ function setIdealYawTowards(self, target) {
3514
3613
  function ai_stand(self, deltaSeconds) {
3515
3614
  changeYaw(self, deltaSeconds);
3516
3615
  }
3517
- function ai_walk(self, distance2, deltaSeconds) {
3616
+ function ai_walk(self, distance2, deltaSeconds, context) {
3518
3617
  setIdealYawTowards(self, self.goalentity);
3519
3618
  changeYaw(self, deltaSeconds);
3520
3619
  if (distance2 !== 0) {
3521
3620
  walkMove(self, self.angles.y, distance2);
3522
3621
  }
3622
+ if (self.goalentity && self.goalentity.classname === "path_corner") {
3623
+ const dist = rangeTo(self, self.goalentity);
3624
+ if (dist < 64) {
3625
+ if (self.goalentity.target) {
3626
+ const next = context.pickTarget(self.goalentity.target);
3627
+ if (next) {
3628
+ self.goalentity = next;
3629
+ self.ideal_yaw = self.angles.y;
3630
+ }
3631
+ }
3632
+ }
3633
+ }
3523
3634
  }
3524
3635
  function ai_turn(self, distance2, deltaSeconds) {
3525
3636
  if (distance2 !== 0) {
@@ -3551,66 +3662,6 @@ function ai_charge(self, distance2, deltaSeconds) {
3551
3662
  }
3552
3663
  }
3553
3664
 
3554
- // src/ai/perception.ts
3555
- var RangeCategory = /* @__PURE__ */ ((RangeCategory2) => {
3556
- RangeCategory2["Melee"] = "melee";
3557
- RangeCategory2["Near"] = "near";
3558
- RangeCategory2["Mid"] = "mid";
3559
- RangeCategory2["Far"] = "far";
3560
- return RangeCategory2;
3561
- })(RangeCategory || {});
3562
- function absBounds(entity) {
3563
- return {
3564
- mins: {
3565
- x: entity.origin.x + entity.mins.x,
3566
- y: entity.origin.y + entity.mins.y,
3567
- z: entity.origin.z + entity.mins.z
3568
- },
3569
- maxs: {
3570
- x: entity.origin.x + entity.maxs.x,
3571
- y: entity.origin.y + entity.maxs.y,
3572
- z: entity.origin.z + entity.maxs.z
3573
- }
3574
- };
3575
- }
3576
- function rangeTo(self, other) {
3577
- const a = absBounds(self);
3578
- const b = absBounds(other);
3579
- const distanceSquared = distanceBetweenBoxesSquared(a.mins, a.maxs, b.mins, b.maxs);
3580
- return Math.sqrt(distanceSquared);
3581
- }
3582
- function classifyRange(distance2) {
3583
- if (distance2 <= RANGE_MELEE) {
3584
- return "melee" /* Melee */;
3585
- }
3586
- if (distance2 <= RANGE_NEAR) {
3587
- return "near" /* Near */;
3588
- }
3589
- if (distance2 <= RANGE_MID) {
3590
- return "mid" /* Mid */;
3591
- }
3592
- return "far" /* Far */;
3593
- }
3594
- function infront(self, other) {
3595
- const { forward } = angleVectors(self.angles);
3596
- const direction = normalizeVec3(subtractVec3(other.origin, self.origin));
3597
- const dot = dotVec3(direction, forward);
3598
- if ((self.spawnflags & SPAWNFLAG_MONSTER_AMBUSH) !== 0 && self.trail_time === 0 && self.enemy === null) {
3599
- return dot > 0.15;
3600
- }
3601
- return dot > -0.3;
3602
- }
3603
- function visible(self, other, trace, options) {
3604
- if ((other.flags & FL_NOVISIBLE) !== 0) {
3605
- return false;
3606
- }
3607
- const start = { x: self.origin.x, y: self.origin.y, z: self.origin.z + self.viewheight };
3608
- const end = { x: other.origin.x, y: other.origin.y, z: other.origin.z + other.viewheight };
3609
- const mask = options?.throughGlass ? 1 /* Opaque */ : 1 /* Opaque */ | 2 /* Window */;
3610
- const result = trace(start, end, self, mask);
3611
- return result.fraction === 1 || result.entity === other;
3612
- }
3613
-
3614
3665
  // src/ai/targeting.ts
3615
3666
  function setIdealYawTowards2(self, other) {
3616
3667
  const delta = {
@@ -3623,19 +3674,19 @@ function setIdealYawTowards2(self, other) {
3623
3674
  function faceYawInstantly(self) {
3624
3675
  self.angles.y = angleMod(self.ideal_yaw);
3625
3676
  }
3626
- function huntTarget(self, level) {
3677
+ function huntTarget(self, level, context) {
3627
3678
  if (!self.enemy) return;
3628
3679
  self.goalentity = self.enemy;
3629
3680
  setIdealYawTowards2(self, self.enemy);
3630
3681
  faceYawInstantly(self);
3631
3682
  if ((self.monsterinfo.aiflags & 1 /* StandGround */) !== 0) {
3632
- self.monsterinfo.stand?.(self);
3683
+ self.monsterinfo.stand?.(self, context);
3633
3684
  } else {
3634
- self.monsterinfo.run?.(self);
3685
+ self.monsterinfo.run?.(self, context);
3635
3686
  self.attack_finished_time = level.timeSeconds + 1;
3636
3687
  }
3637
3688
  }
3638
- function foundTarget(self, level, options) {
3689
+ function foundTarget(self, level, context, options) {
3639
3690
  if (!self.enemy) return;
3640
3691
  if ((self.enemy.svflags & 8 /* Player */) !== 0) {
3641
3692
  level.sightEntity = self;
@@ -3650,7 +3701,7 @@ function foundTarget(self, level, options) {
3650
3701
  self.trail_time = level.timeSeconds;
3651
3702
  self.monsterinfo.trail_time = level.timeSeconds;
3652
3703
  if (!self.combattarget) {
3653
- huntTarget(self, level);
3704
+ huntTarget(self, level, context);
3654
3705
  return;
3655
3706
  }
3656
3707
  const pickTarget = options?.pickTarget;
@@ -3663,7 +3714,7 @@ function foundTarget(self, level, options) {
3663
3714
  self.movetarget.targetname = void 0;
3664
3715
  }
3665
3716
  self.monsterinfo.pausetime = 0;
3666
- self.monsterinfo.run?.(self);
3717
+ self.monsterinfo.run?.(self, context);
3667
3718
  }
3668
3719
  function classifyClientVisibility(self, other, level, trace) {
3669
3720
  const distance2 = rangeTo(self, other);
@@ -3720,7 +3771,7 @@ function rejectNotargetEntity(client) {
3720
3771
  if (client.enemy && (client.enemy.flags & FL_NOTARGET) !== 0) return true;
3721
3772
  return false;
3722
3773
  }
3723
- function findTarget(self, level, trace, hearability = {}) {
3774
+ function findTarget(self, level, context, trace, hearability = {}) {
3724
3775
  if ((self.monsterinfo.aiflags & 256 /* GoodGuy */) !== 0) {
3725
3776
  if (self.goalentity?.classname === "target_actor") {
3726
3777
  return false;
@@ -3741,7 +3792,7 @@ function findTarget(self, level, trace, hearability = {}) {
3741
3792
  } else if (!updateSoundChase(self, candidate, level, hearability, trace)) {
3742
3793
  return false;
3743
3794
  }
3744
- foundTarget(self, level);
3795
+ foundTarget(self, level, context);
3745
3796
  if ((self.monsterinfo.aiflags & 4 /* SoundTarget */) === 0) {
3746
3797
  self.monsterinfo.sight?.(self, self.enemy);
3747
3798
  }
@@ -3749,7 +3800,7 @@ function findTarget(self, level, trace, hearability = {}) {
3749
3800
  }
3750
3801
 
3751
3802
  // src/ai/monster.ts
3752
- function M_MoveFrame(self) {
3803
+ function M_MoveFrame(self, context) {
3753
3804
  const move = self.monsterinfo.current_move;
3754
3805
  if (!move) {
3755
3806
  return;
@@ -3764,10 +3815,10 @@ function M_MoveFrame(self) {
3764
3815
  const index = self.frame - move.firstframe;
3765
3816
  const frame = move.frames[index];
3766
3817
  if (frame.ai) {
3767
- frame.ai(self, frame.dist);
3818
+ frame.ai(self, frame.dist, context);
3768
3819
  }
3769
3820
  if (frame.think) {
3770
- frame.think(self);
3821
+ frame.think(self, context);
3771
3822
  }
3772
3823
  if (!self.inUse) {
3773
3824
  return;
@@ -3775,7 +3826,7 @@ function M_MoveFrame(self) {
3775
3826
  self.frame++;
3776
3827
  if (self.frame > move.lastframe) {
3777
3828
  if (move.endfunc) {
3778
- move.endfunc(self);
3829
+ move.endfunc(self, context);
3779
3830
  if (self.monsterinfo.current_move !== move) {
3780
3831
  return;
3781
3832
  }
@@ -3783,125 +3834,589 @@ function M_MoveFrame(self) {
3783
3834
  }
3784
3835
  }
3785
3836
  function monster_think(self, context) {
3786
- M_MoveFrame(self);
3787
- const time = context && typeof context.timeSeconds === "number" ? context.timeSeconds : self.nextthink;
3788
- self.nextthink = time + 0.1;
3789
- }
3790
-
3791
- // src/entities/monsters/soldier.ts
3792
- var MONSTER_TICK = 0.1;
3793
- function monster_ai_stand(self, dist) {
3794
- ai_stand(self, MONSTER_TICK);
3795
- }
3796
- function monster_ai_walk(self, dist) {
3797
- ai_walk(self, dist, MONSTER_TICK);
3798
- }
3799
- function monster_ai_run(self, dist) {
3800
- ai_run(self, dist, MONSTER_TICK);
3801
- }
3802
- function monster_ai_charge(self, dist) {
3803
- ai_charge(self, dist, MONSTER_TICK);
3804
- }
3805
- var stand_move;
3806
- var walk_move;
3807
- var run_move;
3808
- var attack_move;
3809
- function soldier_stand(self) {
3810
- self.monsterinfo.current_move = stand_move;
3811
- }
3812
- function soldier_walk(self) {
3813
- self.monsterinfo.current_move = walk_move;
3814
- }
3815
- function soldier_run(self) {
3816
- if (self.enemy && self.enemy.health > 0) {
3817
- self.monsterinfo.current_move = run_move;
3818
- } else {
3819
- self.monsterinfo.current_move = stand_move;
3820
- }
3821
- }
3822
- function soldier_attack(self) {
3823
- self.monsterinfo.current_move = attack_move;
3824
- }
3825
- function soldier_fire(self) {
3826
- }
3827
- var stand_frames = Array.from({ length: 30 }, () => ({
3828
- ai: monster_ai_stand,
3829
- dist: 0
3830
- }));
3831
- stand_move = {
3832
- firstframe: 0,
3833
- lastframe: 29,
3834
- frames: stand_frames,
3835
- endfunc: soldier_stand
3836
- };
3837
- var walk_frames = Array.from({ length: 40 }, () => ({
3838
- ai: monster_ai_walk,
3839
- dist: 2
3840
- }));
3841
- walk_move = {
3842
- firstframe: 30,
3843
- lastframe: 69,
3844
- frames: walk_frames,
3845
- endfunc: soldier_walk
3846
- };
3847
- var run_frames = Array.from({ length: 20 }, () => ({
3848
- ai: monster_ai_run,
3849
- dist: 10
3850
- }));
3851
- run_move = {
3852
- firstframe: 70,
3853
- lastframe: 89,
3854
- frames: run_frames,
3855
- endfunc: soldier_run
3856
- };
3857
- var attack_frames = Array.from({ length: 10 }, (_, i) => ({
3858
- ai: monster_ai_charge,
3859
- dist: 0,
3860
- think: i === 5 ? soldier_fire : null
3861
- }));
3862
- attack_move = {
3863
- firstframe: 90,
3864
- lastframe: 99,
3865
- frames: attack_frames,
3866
- endfunc: soldier_run
3867
- };
3868
- function SP_monster_soldier(self, context) {
3869
- self.model = "models/monsters/soldier/tris.md2";
3870
- self.mins = { x: -16, y: -16, z: -24 };
3871
- self.maxs = { x: 16, y: 16, z: 32 };
3872
- self.movetype = 5 /* Step */;
3873
- self.solid = 2 /* BoundingBox */;
3874
- self.health = 20;
3875
- self.max_health = 20;
3876
- self.mass = 100;
3877
- self.pain = (self2, other, kick, damage) => {
3878
- };
3879
- self.die = (self2, inflictor, attacker, damage, point) => {
3880
- self2.deadflag = 2 /* Dead */;
3881
- self2.solid = 0 /* Not */;
3882
- };
3883
- self.monsterinfo.stand = soldier_stand;
3884
- self.monsterinfo.walk = soldier_walk;
3885
- self.monsterinfo.run = soldier_run;
3886
- self.monsterinfo.attack = soldier_attack;
3887
- self.think = monster_think;
3888
- soldier_stand(self);
3889
- self.nextthink = self.timestamp + MONSTER_TICK;
3890
- }
3891
- function registerMonsterSpawns(registry) {
3892
- registry.register("monster_soldier", SP_monster_soldier);
3837
+ M_MoveFrame(self, context);
3838
+ self.nextthink = context.timeSeconds + 0.1;
3893
3839
  }
3894
3840
 
3895
- // src/entities/spawn.ts
3896
- var FIELD_LOOKUP = new Map(
3897
- ENTITY_FIELD_METADATA.map((field) => [field.name, field])
3898
- );
3899
- function parseVec3(text) {
3900
- const parts = text.trim().split(/\s+/);
3901
- const [x = 0, y = 0, z = 0] = parts.map((part) => Number.parseFloat(part)).map((value) => Number.isNaN(value) ? 0 : value);
3902
- return { x, y, z };
3903
- }
3904
- function parseBoolean(text) {
3841
+ // src/combat/damageMods.ts
3842
+ var DamageMod = /* @__PURE__ */ ((DamageMod2) => {
3843
+ DamageMod2[DamageMod2["UNKNOWN"] = 0] = "UNKNOWN";
3844
+ DamageMod2[DamageMod2["BLASTER"] = 1] = "BLASTER";
3845
+ DamageMod2[DamageMod2["SHOTGUN"] = 2] = "SHOTGUN";
3846
+ DamageMod2[DamageMod2["SSHOTGUN"] = 3] = "SSHOTGUN";
3847
+ DamageMod2[DamageMod2["MACHINEGUN"] = 4] = "MACHINEGUN";
3848
+ DamageMod2[DamageMod2["CHAINGUN"] = 5] = "CHAINGUN";
3849
+ DamageMod2[DamageMod2["GRENADE"] = 6] = "GRENADE";
3850
+ DamageMod2[DamageMod2["G_SPLASH"] = 7] = "G_SPLASH";
3851
+ DamageMod2[DamageMod2["ROCKET"] = 8] = "ROCKET";
3852
+ DamageMod2[DamageMod2["R_SPLASH"] = 9] = "R_SPLASH";
3853
+ DamageMod2[DamageMod2["HYPERBLASTER"] = 10] = "HYPERBLASTER";
3854
+ DamageMod2[DamageMod2["RAILGUN"] = 11] = "RAILGUN";
3855
+ DamageMod2[DamageMod2["BFG_LASER"] = 12] = "BFG_LASER";
3856
+ DamageMod2[DamageMod2["BFG_BLAST"] = 13] = "BFG_BLAST";
3857
+ DamageMod2[DamageMod2["BFG_EFFECT"] = 14] = "BFG_EFFECT";
3858
+ DamageMod2[DamageMod2["HANDGRENADE"] = 15] = "HANDGRENADE";
3859
+ DamageMod2[DamageMod2["HG_SPLASH"] = 16] = "HG_SPLASH";
3860
+ DamageMod2[DamageMod2["WATER"] = 17] = "WATER";
3861
+ DamageMod2[DamageMod2["SLIME"] = 18] = "SLIME";
3862
+ DamageMod2[DamageMod2["LAVA"] = 19] = "LAVA";
3863
+ DamageMod2[DamageMod2["CRUSH"] = 20] = "CRUSH";
3864
+ DamageMod2[DamageMod2["TELEFRAG"] = 21] = "TELEFRAG";
3865
+ DamageMod2[DamageMod2["TELEFRAG_SPAWN"] = 22] = "TELEFRAG_SPAWN";
3866
+ DamageMod2[DamageMod2["FALLING"] = 23] = "FALLING";
3867
+ DamageMod2[DamageMod2["SUICIDE"] = 24] = "SUICIDE";
3868
+ DamageMod2[DamageMod2["HELD_GRENADE"] = 25] = "HELD_GRENADE";
3869
+ DamageMod2[DamageMod2["EXPLOSIVE"] = 26] = "EXPLOSIVE";
3870
+ DamageMod2[DamageMod2["BARREL"] = 27] = "BARREL";
3871
+ DamageMod2[DamageMod2["BOMB"] = 28] = "BOMB";
3872
+ DamageMod2[DamageMod2["EXIT"] = 29] = "EXIT";
3873
+ DamageMod2[DamageMod2["SPLASH"] = 30] = "SPLASH";
3874
+ DamageMod2[DamageMod2["TARGET_LASER"] = 31] = "TARGET_LASER";
3875
+ DamageMod2[DamageMod2["TRIGGER_HURT"] = 32] = "TRIGGER_HURT";
3876
+ DamageMod2[DamageMod2["HIT"] = 33] = "HIT";
3877
+ DamageMod2[DamageMod2["TARGET_BLASTER"] = 34] = "TARGET_BLASTER";
3878
+ DamageMod2[DamageMod2["RIPPER"] = 35] = "RIPPER";
3879
+ DamageMod2[DamageMod2["PHALANX"] = 36] = "PHALANX";
3880
+ DamageMod2[DamageMod2["BRAINTENTACLE"] = 37] = "BRAINTENTACLE";
3881
+ DamageMod2[DamageMod2["BLASTOFF"] = 38] = "BLASTOFF";
3882
+ DamageMod2[DamageMod2["GEKK"] = 39] = "GEKK";
3883
+ DamageMod2[DamageMod2["TRAP"] = 40] = "TRAP";
3884
+ DamageMod2[DamageMod2["CHAINFIST"] = 41] = "CHAINFIST";
3885
+ DamageMod2[DamageMod2["DISINTEGRATOR"] = 42] = "DISINTEGRATOR";
3886
+ DamageMod2[DamageMod2["ETF_RIFLE"] = 43] = "ETF_RIFLE";
3887
+ DamageMod2[DamageMod2["BLASTER2"] = 44] = "BLASTER2";
3888
+ DamageMod2[DamageMod2["HEATBEAM"] = 45] = "HEATBEAM";
3889
+ DamageMod2[DamageMod2["TESLA"] = 46] = "TESLA";
3890
+ DamageMod2[DamageMod2["PROX"] = 47] = "PROX";
3891
+ DamageMod2[DamageMod2["NUKE"] = 48] = "NUKE";
3892
+ DamageMod2[DamageMod2["VENGEANCE_SPHERE"] = 49] = "VENGEANCE_SPHERE";
3893
+ DamageMod2[DamageMod2["HUNTER_SPHERE"] = 50] = "HUNTER_SPHERE";
3894
+ DamageMod2[DamageMod2["DEFENDER_SPHERE"] = 51] = "DEFENDER_SPHERE";
3895
+ DamageMod2[DamageMod2["TRACKER"] = 52] = "TRACKER";
3896
+ DamageMod2[DamageMod2["DBALL_CRUSH"] = 53] = "DBALL_CRUSH";
3897
+ DamageMod2[DamageMod2["DOPPLE_EXPLODE"] = 54] = "DOPPLE_EXPLODE";
3898
+ DamageMod2[DamageMod2["DOPPLE_VENGEANCE"] = 55] = "DOPPLE_VENGEANCE";
3899
+ DamageMod2[DamageMod2["DOPPLE_HUNTER"] = 56] = "DOPPLE_HUNTER";
3900
+ DamageMod2[DamageMod2["GRAPPLE"] = 57] = "GRAPPLE";
3901
+ DamageMod2[DamageMod2["BLUEBLASTER"] = 58] = "BLUEBLASTER";
3902
+ return DamageMod2;
3903
+ })(DamageMod || {});
3904
+ var ORDERED_DAMAGE_MODS = [
3905
+ 0 /* UNKNOWN */,
3906
+ 1 /* BLASTER */,
3907
+ 2 /* SHOTGUN */,
3908
+ 3 /* SSHOTGUN */,
3909
+ 4 /* MACHINEGUN */,
3910
+ 5 /* CHAINGUN */,
3911
+ 6 /* GRENADE */,
3912
+ 7 /* G_SPLASH */,
3913
+ 8 /* ROCKET */,
3914
+ 9 /* R_SPLASH */,
3915
+ 10 /* HYPERBLASTER */,
3916
+ 11 /* RAILGUN */,
3917
+ 12 /* BFG_LASER */,
3918
+ 13 /* BFG_BLAST */,
3919
+ 14 /* BFG_EFFECT */,
3920
+ 15 /* HANDGRENADE */,
3921
+ 16 /* HG_SPLASH */,
3922
+ 17 /* WATER */,
3923
+ 18 /* SLIME */,
3924
+ 19 /* LAVA */,
3925
+ 20 /* CRUSH */,
3926
+ 21 /* TELEFRAG */,
3927
+ 22 /* TELEFRAG_SPAWN */,
3928
+ 23 /* FALLING */,
3929
+ 24 /* SUICIDE */,
3930
+ 25 /* HELD_GRENADE */,
3931
+ 26 /* EXPLOSIVE */,
3932
+ 27 /* BARREL */,
3933
+ 28 /* BOMB */,
3934
+ 29 /* EXIT */,
3935
+ 30 /* SPLASH */,
3936
+ 31 /* TARGET_LASER */,
3937
+ 32 /* TRIGGER_HURT */,
3938
+ 33 /* HIT */,
3939
+ 34 /* TARGET_BLASTER */,
3940
+ 35 /* RIPPER */,
3941
+ 36 /* PHALANX */,
3942
+ 37 /* BRAINTENTACLE */,
3943
+ 38 /* BLASTOFF */,
3944
+ 39 /* GEKK */,
3945
+ 40 /* TRAP */,
3946
+ 41 /* CHAINFIST */,
3947
+ 42 /* DISINTEGRATOR */,
3948
+ 43 /* ETF_RIFLE */,
3949
+ 44 /* BLASTER2 */,
3950
+ 45 /* HEATBEAM */,
3951
+ 46 /* TESLA */,
3952
+ 47 /* PROX */,
3953
+ 48 /* NUKE */,
3954
+ 49 /* VENGEANCE_SPHERE */,
3955
+ 50 /* HUNTER_SPHERE */,
3956
+ 51 /* DEFENDER_SPHERE */,
3957
+ 52 /* TRACKER */,
3958
+ 53 /* DBALL_CRUSH */,
3959
+ 54 /* DOPPLE_EXPLODE */,
3960
+ 55 /* DOPPLE_VENGEANCE */,
3961
+ 56 /* DOPPLE_HUNTER */,
3962
+ 57 /* GRAPPLE */,
3963
+ 58 /* BLUEBLASTER */
3964
+ ];
3965
+ function damageModName(mod) {
3966
+ return `MOD_${DamageMod[mod]}`;
3967
+ }
3968
+
3969
+ // src/combat/damage.ts
3970
+ var EntityDamageFlags = /* @__PURE__ */ ((EntityDamageFlags2) => {
3971
+ EntityDamageFlags2[EntityDamageFlags2["GODMODE"] = 1] = "GODMODE";
3972
+ EntityDamageFlags2[EntityDamageFlags2["IMMORTAL"] = 2] = "IMMORTAL";
3973
+ EntityDamageFlags2[EntityDamageFlags2["NO_KNOCKBACK"] = 4] = "NO_KNOCKBACK";
3974
+ EntityDamageFlags2[EntityDamageFlags2["NO_DAMAGE_EFFECTS"] = 8] = "NO_DAMAGE_EFFECTS";
3975
+ return EntityDamageFlags2;
3976
+ })(EntityDamageFlags || {});
3977
+ function applyKnockback(targ, attacker, dir, knockback, dflags) {
3978
+ const hasNoKnockback = hasAnyDamageFlag(dflags, 8 /* NO_KNOCKBACK */) || ((targ.flags ?? 0) & 4 /* NO_KNOCKBACK */) !== 0;
3979
+ if (hasNoKnockback || knockback === 0) {
3980
+ return { x: 0, y: 0, z: 0 };
3981
+ }
3982
+ const mass = Math.max(50, targ.mass ?? 200);
3983
+ const normalized = normalizeVec3(dir);
3984
+ const scale = attacker === targ ? 1600 : 500;
3985
+ const delta = scaleVec3(normalized, scale * knockback / mass);
3986
+ targ.velocity = addVec3(targ.velocity, delta);
3987
+ return delta;
3988
+ }
3989
+ function applyProtection(targ, point, normal, damage, dflags) {
3990
+ let take = damage;
3991
+ let psave = 0;
3992
+ let asave = 0;
3993
+ let remainingCells;
3994
+ let remainingArmor;
3995
+ if (targ.powerArmor) {
3996
+ const result = applyPowerArmor(damage, dflags, point, normal, targ.powerArmor);
3997
+ psave = result.saved;
3998
+ remainingCells = result.remainingCells;
3999
+ take -= psave;
4000
+ }
4001
+ if (targ.regularArmor) {
4002
+ const result = applyRegularArmor(take, dflags, targ.regularArmor);
4003
+ asave = result.saved;
4004
+ remainingArmor = result.remainingArmor;
4005
+ take -= asave;
4006
+ }
4007
+ return [Math.max(0, take), psave, asave, remainingCells, remainingArmor];
4008
+ }
4009
+ function targetCenter(ent) {
4010
+ if (ent.mins && ent.maxs) {
4011
+ return {
4012
+ x: ent.origin.x + (ent.mins.x + ent.maxs.x) * 0.5,
4013
+ y: ent.origin.y + (ent.mins.y + ent.maxs.y) * 0.5,
4014
+ z: ent.origin.z + (ent.mins.z + ent.maxs.z) * 0.5
4015
+ };
4016
+ }
4017
+ return ent.origin;
4018
+ }
4019
+ function T_Damage(targ, inflictor, attacker, dir, point, normal, damage, knockback, dflags, mod) {
4020
+ if (!targ.takedamage || damage <= 0) {
4021
+ return null;
4022
+ }
4023
+ const protectedByGod = !hasAnyDamageFlag(dflags, 32 /* NO_PROTECTION */) && ((targ.flags ?? 0) & 1 /* GODMODE */) !== 0;
4024
+ if (protectedByGod) {
4025
+ return {
4026
+ take: 0,
4027
+ psave: 0,
4028
+ asave: damage,
4029
+ knocked: { x: 0, y: 0, z: 0 },
4030
+ killed: false
4031
+ };
4032
+ }
4033
+ const knocked = applyKnockback(targ, attacker, dir, knockback, dflags);
4034
+ const [take, psave, asave, remainingCells, remainingArmor] = applyProtection(targ, point, normal, damage, dflags);
4035
+ if (targ.powerArmor && remainingCells !== void 0) {
4036
+ targ.powerArmor.cellCount = remainingCells;
4037
+ }
4038
+ if (targ.regularArmor) {
4039
+ targ.regularArmor.armorCount = remainingArmor ?? targ.regularArmor.armorCount;
4040
+ }
4041
+ let actualTake = take;
4042
+ if (actualTake > 0) {
4043
+ targ.health -= actualTake;
4044
+ }
4045
+ const killed = targ.health <= 0;
4046
+ if (killed) {
4047
+ if (targ.flags && targ.flags & 2 /* IMMORTAL */) {
4048
+ targ.health = Math.max(1, targ.health);
4049
+ } else if (targ.die) {
4050
+ targ.die(targ, inflictor, attacker, actualTake, point, mod);
4051
+ }
4052
+ } else if (actualTake > 0 && targ.pain) {
4053
+ targ.pain(targ, attacker, knockback, actualTake, mod);
4054
+ }
4055
+ return { take: actualTake, psave, asave, knocked, killed, remainingCells, remainingArmor };
4056
+ }
4057
+ function T_RadiusDamage(entities, inflictor, attacker, damage, ignore, radius, dflags, mod, options = {}) {
4058
+ const hits = [];
4059
+ const inflictorCenter = targetCenter(inflictor);
4060
+ const canDamage = options.canDamage ?? (() => true);
4061
+ for (const ent of entities) {
4062
+ if (ent === ignore || !ent.takedamage || !canDamage(ent, inflictor)) {
4063
+ continue;
4064
+ }
4065
+ const entCenter = ent.mins && ent.maxs ? closestPointToBox(inflictorCenter, addVec3(ent.origin, ent.mins), addVec3(ent.origin, ent.maxs)) : targetCenter(ent);
4066
+ const toTarget = subtractVec3(inflictorCenter, entCenter);
4067
+ const distance2 = lengthVec3(toTarget);
4068
+ if (radius > 0 && distance2 > radius) {
4069
+ continue;
4070
+ }
4071
+ const points = damage - 0.5 * distance2;
4072
+ if (points <= 0) {
4073
+ continue;
4074
+ }
4075
+ const adjustedDamage = ent === attacker ? points * 0.5 : points;
4076
+ const dir = normalizeVec3(subtractVec3(ent.origin, inflictorCenter));
4077
+ const result = T_Damage(ent, inflictor, attacker, dir, entCenter, dir, adjustedDamage, adjustedDamage, dflags | 1 /* RADIUS */, mod);
4078
+ hits.push({ target: ent, result, appliedDamage: adjustedDamage });
4079
+ }
4080
+ return hits;
4081
+ }
4082
+
4083
+ // src/entities/monsters/attack.ts
4084
+ function monster_fire_bullet(self, start, dir, damage, kick, hspread, vspread, flashtype, context, mod = 0 /* UNKNOWN */) {
4085
+ const end = addVec3(start, scaleVec3(dir, 8192));
4086
+ const tr = context.trace(start, null, null, end, self, 1 | 536870912);
4087
+ if (!tr.ent || tr.fraction === 1) {
4088
+ return;
4089
+ }
4090
+ T_Damage(
4091
+ tr.ent,
4092
+ self,
4093
+ self,
4094
+ dir,
4095
+ tr.endpos,
4096
+ tr.plane?.normal || ZERO_VEC3,
4097
+ damage,
4098
+ kick,
4099
+ 16 /* BULLET */ | 2 /* NO_ARMOR */,
4100
+ // Usually monsters ignore armor? Or maybe not.
4101
+ mod
4102
+ );
4103
+ }
4104
+
4105
+ // src/entities/projectiles.ts
4106
+ function createGrenade(sys, owner, start, dir, damage, speed) {
4107
+ const grenade = sys.spawn();
4108
+ grenade.classname = "grenade";
4109
+ grenade.owner = owner;
4110
+ grenade.origin = { ...start };
4111
+ grenade.velocity = { x: dir.x * speed, y: dir.y * speed, z: dir.z * speed };
4112
+ grenade.movetype = 9 /* Bounce */;
4113
+ grenade.clipmask = 268566530;
4114
+ grenade.solid = 2 /* BoundingBox */;
4115
+ grenade.modelindex = sys.modelIndex("models/objects/grenade/tris.md2");
4116
+ grenade.mins = { x: -4, y: -4, z: -4 };
4117
+ grenade.maxs = { x: 4, y: 4, z: 4 };
4118
+ grenade.touch = (self, other, plane, surf) => {
4119
+ if (other === self.owner) {
4120
+ return;
4121
+ }
4122
+ if (other && other.takedamage) {
4123
+ T_Damage(
4124
+ other,
4125
+ self,
4126
+ self.owner,
4127
+ self.velocity,
4128
+ self.origin,
4129
+ plane ? plane.normal : ZERO_VEC3,
4130
+ damage,
4131
+ // Direct impact damage? Usually separate but let's assume it's part of it or handled by radius.
4132
+ // Actually G_Weapon.c Grenade_Touch calls Grenade_Explode which calls T_RadiusDamage.
4133
+ // It doesn't seem to do T_Damage separately?
4134
+ // Wait, if it hits a monster, it stops and explodes.
4135
+ 0,
4136
+ 0 /* NONE */,
4137
+ 6 /* GRENADE */
4138
+ );
4139
+ const entities = sys.findByRadius(self.origin, 120);
4140
+ T_RadiusDamage(entities, self, self.owner, damage, self.owner, 120, 0 /* NONE */, 6 /* GRENADE */);
4141
+ sys.free(self);
4142
+ return;
4143
+ }
4144
+ };
4145
+ grenade.think = (self) => {
4146
+ const entities = sys.findByRadius(self.origin, 120);
4147
+ T_RadiusDamage(entities, self, self.owner, damage, self.owner, 120, 0 /* NONE */, 6 /* GRENADE */);
4148
+ sys.free(self);
4149
+ };
4150
+ sys.scheduleThink(grenade, sys.timeSeconds + 2.5);
4151
+ sys.finalizeSpawn(grenade);
4152
+ }
4153
+
4154
+ // src/entities/monsters/gunner.ts
4155
+ var MONSTER_TICK = 0.1;
4156
+ function monster_ai_stand(self, dist, context) {
4157
+ ai_stand(self, MONSTER_TICK);
4158
+ }
4159
+ function monster_ai_walk(self, dist, context) {
4160
+ ai_walk(self, dist, MONSTER_TICK, context);
4161
+ }
4162
+ function monster_ai_run(self, dist, context) {
4163
+ ai_run(self, dist, MONSTER_TICK);
4164
+ }
4165
+ function monster_ai_charge(self, dist, context) {
4166
+ ai_charge(self, dist, MONSTER_TICK);
4167
+ }
4168
+ var stand_move;
4169
+ var walk_move;
4170
+ var run_move;
4171
+ var attack_chain_move;
4172
+ var attack_grenade_move;
4173
+ function gunner_stand(self) {
4174
+ self.monsterinfo.current_move = stand_move;
4175
+ }
4176
+ function gunner_walk(self) {
4177
+ self.monsterinfo.current_move = walk_move;
4178
+ }
4179
+ function gunner_run(self) {
4180
+ if (self.enemy && self.enemy.health > 0) {
4181
+ self.monsterinfo.current_move = run_move;
4182
+ } else {
4183
+ self.monsterinfo.current_move = stand_move;
4184
+ }
4185
+ }
4186
+ function gunner_attack(self) {
4187
+ if (Math.random() > 0.5) {
4188
+ self.monsterinfo.current_move = attack_chain_move;
4189
+ } else {
4190
+ self.monsterinfo.current_move = attack_grenade_move;
4191
+ }
4192
+ }
4193
+ function gunner_fire_chain(self, context) {
4194
+ if (!self.enemy) return;
4195
+ const start = {
4196
+ x: self.origin.x,
4197
+ y: self.origin.y,
4198
+ z: self.origin.z + self.viewheight - 8
4199
+ };
4200
+ const forward = normalizeVec3(subtractVec3(self.enemy.origin, start));
4201
+ const damage = 10;
4202
+ const kick = 2;
4203
+ monster_fire_bullet(self, start, forward, damage, kick, 0, 0, 0, context, 5 /* CHAINGUN */);
4204
+ }
4205
+ function gunner_fire_grenade(self, context) {
4206
+ if (!self.enemy) return;
4207
+ const start = {
4208
+ x: self.origin.x,
4209
+ y: self.origin.y,
4210
+ z: self.origin.z + self.viewheight
4211
+ };
4212
+ const forward = normalizeVec3(subtractVec3(self.enemy.origin, start));
4213
+ const damage = 50;
4214
+ const speed = 600;
4215
+ createGrenade(context, self, start, forward, damage, speed);
4216
+ }
4217
+ var stand_frames = Array.from({ length: 30 }, () => ({
4218
+ ai: monster_ai_stand,
4219
+ dist: 0
4220
+ }));
4221
+ stand_move = {
4222
+ firstframe: 0,
4223
+ lastframe: 29,
4224
+ frames: stand_frames,
4225
+ endfunc: gunner_stand
4226
+ };
4227
+ var walk_frames = Array.from({ length: 40 }, () => ({
4228
+ ai: monster_ai_walk,
4229
+ dist: 2
4230
+ }));
4231
+ walk_move = {
4232
+ firstframe: 30,
4233
+ lastframe: 69,
4234
+ frames: walk_frames,
4235
+ endfunc: gunner_walk
4236
+ };
4237
+ var run_frames = Array.from({ length: 20 }, () => ({
4238
+ ai: monster_ai_run,
4239
+ dist: 10
4240
+ }));
4241
+ run_move = {
4242
+ firstframe: 70,
4243
+ lastframe: 89,
4244
+ frames: run_frames,
4245
+ endfunc: gunner_run
4246
+ };
4247
+ var attack_chain_frames = Array.from({ length: 10 }, (_, i) => ({
4248
+ ai: monster_ai_charge,
4249
+ dist: 0,
4250
+ think: i >= 2 && i <= 8 ? gunner_fire_chain : null
4251
+ }));
4252
+ attack_chain_move = {
4253
+ firstframe: 90,
4254
+ lastframe: 99,
4255
+ frames: attack_chain_frames,
4256
+ endfunc: gunner_run
4257
+ };
4258
+ var attack_grenade_frames = Array.from({ length: 10 }, (_, i) => ({
4259
+ ai: monster_ai_charge,
4260
+ dist: 0,
4261
+ think: i === 5 ? gunner_fire_grenade : null
4262
+ }));
4263
+ attack_grenade_move = {
4264
+ firstframe: 100,
4265
+ lastframe: 109,
4266
+ frames: attack_grenade_frames,
4267
+ endfunc: gunner_run
4268
+ };
4269
+ function SP_monster_gunner(self, context) {
4270
+ self.model = "models/monsters/gunner/tris.md2";
4271
+ self.mins = { x: -16, y: -16, z: -24 };
4272
+ self.maxs = { x: 16, y: 16, z: 32 };
4273
+ self.movetype = 5 /* Step */;
4274
+ self.solid = 2 /* BoundingBox */;
4275
+ self.health = 175;
4276
+ self.max_health = 175;
4277
+ self.mass = 200;
4278
+ self.pain = (self2, other, kick, damage) => {
4279
+ };
4280
+ self.die = (self2, inflictor, attacker, damage, point) => {
4281
+ self2.deadflag = 2 /* Dead */;
4282
+ self2.solid = 0 /* Not */;
4283
+ };
4284
+ self.monsterinfo.stand = gunner_stand;
4285
+ self.monsterinfo.walk = gunner_walk;
4286
+ self.monsterinfo.run = gunner_run;
4287
+ self.monsterinfo.attack = gunner_attack;
4288
+ self.think = monster_think;
4289
+ gunner_stand(self);
4290
+ self.nextthink = self.timestamp + MONSTER_TICK;
4291
+ }
4292
+ function registerGunnerSpawns(registry) {
4293
+ registry.register("monster_gunner", SP_monster_gunner);
4294
+ }
4295
+
4296
+ // src/entities/monsters/soldier.ts
4297
+ var MONSTER_TICK2 = 0.1;
4298
+ function monster_ai_stand2(self, dist, context) {
4299
+ ai_stand(self, MONSTER_TICK2);
4300
+ }
4301
+ function monster_ai_walk2(self, dist, context) {
4302
+ ai_walk(self, dist, MONSTER_TICK2, context);
4303
+ }
4304
+ function monster_ai_run2(self, dist, context) {
4305
+ ai_run(self, dist, MONSTER_TICK2);
4306
+ }
4307
+ function monster_ai_charge2(self, dist, context) {
4308
+ ai_charge(self, dist, MONSTER_TICK2);
4309
+ }
4310
+ var stand_move2;
4311
+ var walk_move2;
4312
+ var run_move2;
4313
+ var attack_move;
4314
+ function soldier_stand(self) {
4315
+ self.monsterinfo.current_move = stand_move2;
4316
+ }
4317
+ function soldier_walk(self) {
4318
+ self.monsterinfo.current_move = walk_move2;
4319
+ }
4320
+ function soldier_run(self) {
4321
+ if (self.enemy && self.enemy.health > 0) {
4322
+ self.monsterinfo.current_move = run_move2;
4323
+ } else {
4324
+ self.monsterinfo.current_move = stand_move2;
4325
+ }
4326
+ }
4327
+ function soldier_attack(self) {
4328
+ self.monsterinfo.current_move = attack_move;
4329
+ }
4330
+ function soldier_fire(self, context) {
4331
+ if (!self.enemy) return;
4332
+ const start = {
4333
+ x: self.origin.x,
4334
+ y: self.origin.y,
4335
+ z: self.origin.z + self.viewheight
4336
+ };
4337
+ const forward = normalizeVec3(subtractVec3(self.enemy.origin, start));
4338
+ const damage = 4;
4339
+ const kick = 4;
4340
+ monster_fire_bullet(self, start, forward, damage, kick, 0, 0, 0, context, 4 /* MACHINEGUN */);
4341
+ }
4342
+ var stand_frames2 = Array.from({ length: 30 }, () => ({
4343
+ ai: monster_ai_stand2,
4344
+ dist: 0
4345
+ }));
4346
+ stand_move2 = {
4347
+ firstframe: 0,
4348
+ lastframe: 29,
4349
+ frames: stand_frames2,
4350
+ endfunc: soldier_stand
4351
+ };
4352
+ var walk_frames2 = Array.from({ length: 40 }, () => ({
4353
+ ai: monster_ai_walk2,
4354
+ dist: 2
4355
+ }));
4356
+ walk_move2 = {
4357
+ firstframe: 30,
4358
+ lastframe: 69,
4359
+ frames: walk_frames2,
4360
+ endfunc: soldier_walk
4361
+ };
4362
+ var run_frames2 = Array.from({ length: 20 }, () => ({
4363
+ ai: monster_ai_run2,
4364
+ dist: 10
4365
+ }));
4366
+ run_move2 = {
4367
+ firstframe: 70,
4368
+ lastframe: 89,
4369
+ frames: run_frames2,
4370
+ endfunc: soldier_run
4371
+ };
4372
+ var attack_frames = Array.from({ length: 10 }, (_, i) => ({
4373
+ ai: monster_ai_charge2,
4374
+ dist: 0,
4375
+ think: i === 5 ? soldier_fire : null
4376
+ }));
4377
+ attack_move = {
4378
+ firstframe: 90,
4379
+ lastframe: 99,
4380
+ frames: attack_frames,
4381
+ endfunc: soldier_run
4382
+ };
4383
+ function SP_monster_soldier(self, context) {
4384
+ self.model = "models/monsters/soldier/tris.md2";
4385
+ self.mins = { x: -16, y: -16, z: -24 };
4386
+ self.maxs = { x: 16, y: 16, z: 32 };
4387
+ self.movetype = 5 /* Step */;
4388
+ self.solid = 2 /* BoundingBox */;
4389
+ self.health = 20;
4390
+ self.max_health = 20;
4391
+ self.mass = 100;
4392
+ self.pain = (self2, other, kick, damage) => {
4393
+ };
4394
+ self.die = (self2, inflictor, attacker, damage, point) => {
4395
+ self2.deadflag = 2 /* Dead */;
4396
+ self2.solid = 0 /* Not */;
4397
+ };
4398
+ self.monsterinfo.stand = soldier_stand;
4399
+ self.monsterinfo.walk = soldier_walk;
4400
+ self.monsterinfo.run = soldier_run;
4401
+ self.monsterinfo.attack = soldier_attack;
4402
+ self.think = monster_think;
4403
+ soldier_stand(self);
4404
+ self.nextthink = self.timestamp + MONSTER_TICK2;
4405
+ }
4406
+ function registerMonsterSpawns(registry) {
4407
+ registry.register("monster_soldier", SP_monster_soldier);
4408
+ }
4409
+
4410
+ // src/entities/spawn.ts
4411
+ var FIELD_LOOKUP = new Map(
4412
+ ENTITY_FIELD_METADATA.map((field) => [field.name, field])
4413
+ );
4414
+ function parseVec3(text) {
4415
+ const parts = text.trim().split(/\s+/);
4416
+ const [x = 0, y = 0, z = 0] = parts.map((part) => Number.parseFloat(part)).map((value) => Number.isNaN(value) ? 0 : value);
4417
+ return { x, y, z };
4418
+ }
4419
+ function parseBoolean(text) {
3905
4420
  const normalized = text.trim().toLowerCase();
3906
4421
  return normalized === "1" || normalized === "true" || normalized === "yes";
3907
4422
  }
@@ -4107,6 +4622,7 @@ function registerDefaultSpawns(game, registry) {
4107
4622
  registerPathSpawns(registry);
4108
4623
  registerLightSpawns(registry);
4109
4624
  registerMonsterSpawns(registry);
4625
+ registerGunnerSpawns(registry);
4110
4626
  }
4111
4627
  function createDefaultSpawnRegistry(game) {
4112
4628
  const registry = new SpawnRegistry();
@@ -4973,248 +5489,6 @@ _SaveStorage.DEFAULT_STORE = "saves";
4973
5489
  _SaveStorage.QUICK_SLOT = "quicksave";
4974
5490
  var SaveStorage = _SaveStorage;
4975
5491
 
4976
- // src/combat/damage.ts
4977
- var EntityDamageFlags = /* @__PURE__ */ ((EntityDamageFlags2) => {
4978
- EntityDamageFlags2[EntityDamageFlags2["GODMODE"] = 1] = "GODMODE";
4979
- EntityDamageFlags2[EntityDamageFlags2["IMMORTAL"] = 2] = "IMMORTAL";
4980
- EntityDamageFlags2[EntityDamageFlags2["NO_KNOCKBACK"] = 4] = "NO_KNOCKBACK";
4981
- EntityDamageFlags2[EntityDamageFlags2["NO_DAMAGE_EFFECTS"] = 8] = "NO_DAMAGE_EFFECTS";
4982
- return EntityDamageFlags2;
4983
- })(EntityDamageFlags || {});
4984
- function applyKnockback(targ, attacker, dir, knockback, dflags) {
4985
- const hasNoKnockback = hasAnyDamageFlag(dflags, 8 /* NO_KNOCKBACK */) || ((targ.flags ?? 0) & 4 /* NO_KNOCKBACK */) !== 0;
4986
- if (hasNoKnockback || knockback === 0) {
4987
- return { x: 0, y: 0, z: 0 };
4988
- }
4989
- const mass = Math.max(50, targ.mass ?? 200);
4990
- const normalized = normalizeVec3(dir);
4991
- const scale = attacker === targ ? 1600 : 500;
4992
- const delta = scaleVec3(normalized, scale * knockback / mass);
4993
- targ.velocity = addVec3(targ.velocity, delta);
4994
- return delta;
4995
- }
4996
- function applyProtection(targ, point, normal, damage, dflags) {
4997
- let take = damage;
4998
- let psave = 0;
4999
- let asave = 0;
5000
- let remainingCells;
5001
- let remainingArmor;
5002
- if (targ.powerArmor) {
5003
- const result = applyPowerArmor(damage, dflags, point, normal, targ.powerArmor);
5004
- psave = result.saved;
5005
- remainingCells = result.remainingCells;
5006
- take -= psave;
5007
- }
5008
- if (targ.regularArmor) {
5009
- const result = applyRegularArmor(take, dflags, targ.regularArmor);
5010
- asave = result.saved;
5011
- remainingArmor = result.remainingArmor;
5012
- take -= asave;
5013
- }
5014
- return [Math.max(0, take), psave, asave, remainingCells, remainingArmor];
5015
- }
5016
- function targetCenter(ent) {
5017
- if (ent.mins && ent.maxs) {
5018
- return {
5019
- x: ent.origin.x + (ent.mins.x + ent.maxs.x) * 0.5,
5020
- y: ent.origin.y + (ent.mins.y + ent.maxs.y) * 0.5,
5021
- z: ent.origin.z + (ent.mins.z + ent.maxs.z) * 0.5
5022
- };
5023
- }
5024
- return ent.origin;
5025
- }
5026
- function T_Damage(targ, inflictor, attacker, dir, point, normal, damage, knockback, dflags, mod) {
5027
- if (!targ.takedamage || damage <= 0) {
5028
- return null;
5029
- }
5030
- const protectedByGod = !hasAnyDamageFlag(dflags, 32 /* NO_PROTECTION */) && ((targ.flags ?? 0) & 1 /* GODMODE */) !== 0;
5031
- if (protectedByGod) {
5032
- return {
5033
- take: 0,
5034
- psave: 0,
5035
- asave: damage,
5036
- knocked: { x: 0, y: 0, z: 0 },
5037
- killed: false
5038
- };
5039
- }
5040
- const knocked = applyKnockback(targ, attacker, dir, knockback, dflags);
5041
- const [take, psave, asave, remainingCells, remainingArmor] = applyProtection(targ, point, normal, damage, dflags);
5042
- if (targ.powerArmor && remainingCells !== void 0) {
5043
- targ.powerArmor.cellCount = remainingCells;
5044
- }
5045
- if (targ.regularArmor) {
5046
- targ.regularArmor.armorCount = remainingArmor ?? targ.regularArmor.armorCount;
5047
- }
5048
- let actualTake = take;
5049
- if (actualTake > 0) {
5050
- targ.health -= actualTake;
5051
- }
5052
- const killed = targ.health <= 0;
5053
- if (killed) {
5054
- if (targ.flags && targ.flags & 2 /* IMMORTAL */) {
5055
- targ.health = Math.max(1, targ.health);
5056
- } else if (targ.die) {
5057
- targ.die(targ, inflictor, attacker, actualTake, point, mod);
5058
- }
5059
- } else if (actualTake > 0 && targ.pain) {
5060
- targ.pain(targ, attacker, knockback, actualTake, mod);
5061
- }
5062
- return { take: actualTake, psave, asave, knocked, killed, remainingCells, remainingArmor };
5063
- }
5064
- function T_RadiusDamage(entities, inflictor, attacker, damage, ignore, radius, dflags, mod, options = {}) {
5065
- const hits = [];
5066
- const inflictorCenter = targetCenter(inflictor);
5067
- const canDamage = options.canDamage ?? (() => true);
5068
- for (const ent of entities) {
5069
- if (ent === ignore || !ent.takedamage || !canDamage(ent, inflictor)) {
5070
- continue;
5071
- }
5072
- const entCenter = ent.mins && ent.maxs ? closestPointToBox(inflictorCenter, addVec3(ent.origin, ent.mins), addVec3(ent.origin, ent.maxs)) : targetCenter(ent);
5073
- const toTarget = subtractVec3(inflictorCenter, entCenter);
5074
- const distance2 = lengthVec3(toTarget);
5075
- if (radius > 0 && distance2 > radius) {
5076
- continue;
5077
- }
5078
- const points = damage - 0.5 * distance2;
5079
- if (points <= 0) {
5080
- continue;
5081
- }
5082
- const adjustedDamage = ent === attacker ? points * 0.5 : points;
5083
- const dir = normalizeVec3(subtractVec3(ent.origin, inflictorCenter));
5084
- const result = T_Damage(ent, inflictor, attacker, dir, entCenter, dir, adjustedDamage, adjustedDamage, dflags | 1 /* RADIUS */, mod);
5085
- hits.push({ target: ent, result, appliedDamage: adjustedDamage });
5086
- }
5087
- return hits;
5088
- }
5089
-
5090
- // src/combat/damageMods.ts
5091
- var DamageMod = /* @__PURE__ */ ((DamageMod2) => {
5092
- DamageMod2[DamageMod2["UNKNOWN"] = 0] = "UNKNOWN";
5093
- DamageMod2[DamageMod2["BLASTER"] = 1] = "BLASTER";
5094
- DamageMod2[DamageMod2["SHOTGUN"] = 2] = "SHOTGUN";
5095
- DamageMod2[DamageMod2["SSHOTGUN"] = 3] = "SSHOTGUN";
5096
- DamageMod2[DamageMod2["MACHINEGUN"] = 4] = "MACHINEGUN";
5097
- DamageMod2[DamageMod2["CHAINGUN"] = 5] = "CHAINGUN";
5098
- DamageMod2[DamageMod2["GRENADE"] = 6] = "GRENADE";
5099
- DamageMod2[DamageMod2["G_SPLASH"] = 7] = "G_SPLASH";
5100
- DamageMod2[DamageMod2["ROCKET"] = 8] = "ROCKET";
5101
- DamageMod2[DamageMod2["R_SPLASH"] = 9] = "R_SPLASH";
5102
- DamageMod2[DamageMod2["HYPERBLASTER"] = 10] = "HYPERBLASTER";
5103
- DamageMod2[DamageMod2["RAILGUN"] = 11] = "RAILGUN";
5104
- DamageMod2[DamageMod2["BFG_LASER"] = 12] = "BFG_LASER";
5105
- DamageMod2[DamageMod2["BFG_BLAST"] = 13] = "BFG_BLAST";
5106
- DamageMod2[DamageMod2["BFG_EFFECT"] = 14] = "BFG_EFFECT";
5107
- DamageMod2[DamageMod2["HANDGRENADE"] = 15] = "HANDGRENADE";
5108
- DamageMod2[DamageMod2["HG_SPLASH"] = 16] = "HG_SPLASH";
5109
- DamageMod2[DamageMod2["WATER"] = 17] = "WATER";
5110
- DamageMod2[DamageMod2["SLIME"] = 18] = "SLIME";
5111
- DamageMod2[DamageMod2["LAVA"] = 19] = "LAVA";
5112
- DamageMod2[DamageMod2["CRUSH"] = 20] = "CRUSH";
5113
- DamageMod2[DamageMod2["TELEFRAG"] = 21] = "TELEFRAG";
5114
- DamageMod2[DamageMod2["TELEFRAG_SPAWN"] = 22] = "TELEFRAG_SPAWN";
5115
- DamageMod2[DamageMod2["FALLING"] = 23] = "FALLING";
5116
- DamageMod2[DamageMod2["SUICIDE"] = 24] = "SUICIDE";
5117
- DamageMod2[DamageMod2["HELD_GRENADE"] = 25] = "HELD_GRENADE";
5118
- DamageMod2[DamageMod2["EXPLOSIVE"] = 26] = "EXPLOSIVE";
5119
- DamageMod2[DamageMod2["BARREL"] = 27] = "BARREL";
5120
- DamageMod2[DamageMod2["BOMB"] = 28] = "BOMB";
5121
- DamageMod2[DamageMod2["EXIT"] = 29] = "EXIT";
5122
- DamageMod2[DamageMod2["SPLASH"] = 30] = "SPLASH";
5123
- DamageMod2[DamageMod2["TARGET_LASER"] = 31] = "TARGET_LASER";
5124
- DamageMod2[DamageMod2["TRIGGER_HURT"] = 32] = "TRIGGER_HURT";
5125
- DamageMod2[DamageMod2["HIT"] = 33] = "HIT";
5126
- DamageMod2[DamageMod2["TARGET_BLASTER"] = 34] = "TARGET_BLASTER";
5127
- DamageMod2[DamageMod2["RIPPER"] = 35] = "RIPPER";
5128
- DamageMod2[DamageMod2["PHALANX"] = 36] = "PHALANX";
5129
- DamageMod2[DamageMod2["BRAINTENTACLE"] = 37] = "BRAINTENTACLE";
5130
- DamageMod2[DamageMod2["BLASTOFF"] = 38] = "BLASTOFF";
5131
- DamageMod2[DamageMod2["GEKK"] = 39] = "GEKK";
5132
- DamageMod2[DamageMod2["TRAP"] = 40] = "TRAP";
5133
- DamageMod2[DamageMod2["CHAINFIST"] = 41] = "CHAINFIST";
5134
- DamageMod2[DamageMod2["DISINTEGRATOR"] = 42] = "DISINTEGRATOR";
5135
- DamageMod2[DamageMod2["ETF_RIFLE"] = 43] = "ETF_RIFLE";
5136
- DamageMod2[DamageMod2["BLASTER2"] = 44] = "BLASTER2";
5137
- DamageMod2[DamageMod2["HEATBEAM"] = 45] = "HEATBEAM";
5138
- DamageMod2[DamageMod2["TESLA"] = 46] = "TESLA";
5139
- DamageMod2[DamageMod2["PROX"] = 47] = "PROX";
5140
- DamageMod2[DamageMod2["NUKE"] = 48] = "NUKE";
5141
- DamageMod2[DamageMod2["VENGEANCE_SPHERE"] = 49] = "VENGEANCE_SPHERE";
5142
- DamageMod2[DamageMod2["HUNTER_SPHERE"] = 50] = "HUNTER_SPHERE";
5143
- DamageMod2[DamageMod2["DEFENDER_SPHERE"] = 51] = "DEFENDER_SPHERE";
5144
- DamageMod2[DamageMod2["TRACKER"] = 52] = "TRACKER";
5145
- DamageMod2[DamageMod2["DBALL_CRUSH"] = 53] = "DBALL_CRUSH";
5146
- DamageMod2[DamageMod2["DOPPLE_EXPLODE"] = 54] = "DOPPLE_EXPLODE";
5147
- DamageMod2[DamageMod2["DOPPLE_VENGEANCE"] = 55] = "DOPPLE_VENGEANCE";
5148
- DamageMod2[DamageMod2["DOPPLE_HUNTER"] = 56] = "DOPPLE_HUNTER";
5149
- DamageMod2[DamageMod2["GRAPPLE"] = 57] = "GRAPPLE";
5150
- DamageMod2[DamageMod2["BLUEBLASTER"] = 58] = "BLUEBLASTER";
5151
- return DamageMod2;
5152
- })(DamageMod || {});
5153
- var ORDERED_DAMAGE_MODS = [
5154
- 0 /* UNKNOWN */,
5155
- 1 /* BLASTER */,
5156
- 2 /* SHOTGUN */,
5157
- 3 /* SSHOTGUN */,
5158
- 4 /* MACHINEGUN */,
5159
- 5 /* CHAINGUN */,
5160
- 6 /* GRENADE */,
5161
- 7 /* G_SPLASH */,
5162
- 8 /* ROCKET */,
5163
- 9 /* R_SPLASH */,
5164
- 10 /* HYPERBLASTER */,
5165
- 11 /* RAILGUN */,
5166
- 12 /* BFG_LASER */,
5167
- 13 /* BFG_BLAST */,
5168
- 14 /* BFG_EFFECT */,
5169
- 15 /* HANDGRENADE */,
5170
- 16 /* HG_SPLASH */,
5171
- 17 /* WATER */,
5172
- 18 /* SLIME */,
5173
- 19 /* LAVA */,
5174
- 20 /* CRUSH */,
5175
- 21 /* TELEFRAG */,
5176
- 22 /* TELEFRAG_SPAWN */,
5177
- 23 /* FALLING */,
5178
- 24 /* SUICIDE */,
5179
- 25 /* HELD_GRENADE */,
5180
- 26 /* EXPLOSIVE */,
5181
- 27 /* BARREL */,
5182
- 28 /* BOMB */,
5183
- 29 /* EXIT */,
5184
- 30 /* SPLASH */,
5185
- 31 /* TARGET_LASER */,
5186
- 32 /* TRIGGER_HURT */,
5187
- 33 /* HIT */,
5188
- 34 /* TARGET_BLASTER */,
5189
- 35 /* RIPPER */,
5190
- 36 /* PHALANX */,
5191
- 37 /* BRAINTENTACLE */,
5192
- 38 /* BLASTOFF */,
5193
- 39 /* GEKK */,
5194
- 40 /* TRAP */,
5195
- 41 /* CHAINFIST */,
5196
- 42 /* DISINTEGRATOR */,
5197
- 43 /* ETF_RIFLE */,
5198
- 44 /* BLASTER2 */,
5199
- 45 /* HEATBEAM */,
5200
- 46 /* TESLA */,
5201
- 47 /* PROX */,
5202
- 48 /* NUKE */,
5203
- 49 /* VENGEANCE_SPHERE */,
5204
- 50 /* HUNTER_SPHERE */,
5205
- 51 /* DEFENDER_SPHERE */,
5206
- 52 /* TRACKER */,
5207
- 53 /* DBALL_CRUSH */,
5208
- 54 /* DOPPLE_EXPLODE */,
5209
- 55 /* DOPPLE_VENGEANCE */,
5210
- 56 /* DOPPLE_HUNTER */,
5211
- 57 /* GRAPPLE */,
5212
- 58 /* BLUEBLASTER */
5213
- ];
5214
- function damageModName(mod) {
5215
- return `MOD_${DamageMod[mod]}`;
5216
- }
5217
-
5218
5492
  // src/combat/specialDamage.ts
5219
5493
  var ZERO2 = { x: 0, y: 0, z: 0 };
5220
5494
  var EnvironmentalFlags = /* @__PURE__ */ ((EnvironmentalFlags2) => {
@@ -5692,12 +5966,17 @@ export {
5692
5966
  convertRereleaseLevelToGameSave,
5693
5967
  convertRereleaseSaveToGameSave,
5694
5968
  createAmmoInventory,
5969
+ createAmmoPickupEntity,
5970
+ createArmorPickupEntity,
5695
5971
  createBaseAmmoCaps,
5696
5972
  createCallbackRegistry,
5697
5973
  createDefaultSpawnRegistry,
5698
5974
  createGame,
5975
+ createHealthPickupEntity,
5976
+ createKeyPickupEntity,
5699
5977
  createPlayerInventory,
5700
5978
  createPlayerWeaponStates,
5979
+ createPowerupPickupEntity,
5701
5980
  createSaveFile,
5702
5981
  createWeaponPickupEntity,
5703
5982
  damageModName,