quake2ts 0.0.494 → 0.0.497

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 (58) hide show
  1. package/package.json +1 -1
  2. package/packages/client/dist/browser/index.global.js +16 -16
  3. package/packages/client/dist/browser/index.global.js.map +1 -1
  4. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  5. package/packages/game/dist/browser/index.global.js +4 -4
  6. package/packages/game/dist/browser/index.global.js.map +1 -1
  7. package/packages/game/dist/cjs/index.cjs +720 -414
  8. package/packages/game/dist/cjs/index.cjs.map +1 -1
  9. package/packages/game/dist/esm/index.js +720 -414
  10. package/packages/game/dist/esm/index.js.map +1 -1
  11. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
  12. package/packages/game/dist/types/entities/player.d.ts.map +1 -1
  13. package/packages/game/dist/types/entities/playerStats.d.ts.map +1 -1
  14. package/packages/game/dist/types/entities/projectiles.d.ts +5 -0
  15. package/packages/game/dist/types/entities/projectiles.d.ts.map +1 -1
  16. package/packages/game/dist/types/entities/system.d.ts +1 -0
  17. package/packages/game/dist/types/entities/system.d.ts.map +1 -1
  18. package/packages/game/dist/types/entities/triggers/always.d.ts +3 -0
  19. package/packages/game/dist/types/entities/triggers/always.d.ts.map +1 -0
  20. package/packages/game/dist/types/entities/triggers/bad_area.d.ts +3 -0
  21. package/packages/game/dist/types/entities/triggers/bad_area.d.ts.map +1 -0
  22. package/packages/game/dist/types/entities/triggers/common.d.ts +14 -0
  23. package/packages/game/dist/types/entities/triggers/common.d.ts.map +1 -0
  24. package/packages/game/dist/types/entities/triggers/counter.d.ts +3 -0
  25. package/packages/game/dist/types/entities/triggers/counter.d.ts.map +1 -0
  26. package/packages/game/dist/types/entities/triggers/elevator.d.ts +3 -0
  27. package/packages/game/dist/types/entities/triggers/elevator.d.ts.map +1 -0
  28. package/packages/game/dist/types/entities/triggers/gravity.d.ts +3 -0
  29. package/packages/game/dist/types/entities/triggers/gravity.d.ts.map +1 -0
  30. package/packages/game/dist/types/entities/triggers/hurt.d.ts +3 -0
  31. package/packages/game/dist/types/entities/triggers/hurt.d.ts.map +1 -0
  32. package/packages/game/dist/types/entities/triggers/index.d.ts +3 -0
  33. package/packages/game/dist/types/entities/triggers/index.d.ts.map +1 -0
  34. package/packages/game/dist/types/entities/triggers/key.d.ts +3 -0
  35. package/packages/game/dist/types/entities/triggers/key.d.ts.map +1 -0
  36. package/packages/game/dist/types/entities/triggers/monsterjump.d.ts +3 -0
  37. package/packages/game/dist/types/entities/triggers/monsterjump.d.ts.map +1 -0
  38. package/packages/game/dist/types/entities/triggers/multiple.d.ts +6 -0
  39. package/packages/game/dist/types/entities/triggers/multiple.d.ts.map +1 -0
  40. package/packages/game/dist/types/entities/triggers/once.d.ts +3 -0
  41. package/packages/game/dist/types/entities/triggers/once.d.ts.map +1 -0
  42. package/packages/game/dist/types/entities/triggers/push.d.ts +3 -0
  43. package/packages/game/dist/types/entities/triggers/push.d.ts.map +1 -0
  44. package/packages/game/dist/types/entities/triggers/relay.d.ts +3 -0
  45. package/packages/game/dist/types/entities/triggers/relay.d.ts.map +1 -0
  46. package/packages/game/dist/types/entities/triggers/teleport.d.ts +3 -0
  47. package/packages/game/dist/types/entities/triggers/teleport.d.ts.map +1 -0
  48. package/packages/game/dist/types/inventory/items.d.ts.map +1 -1
  49. package/packages/game/dist/types/inventory/playerInventory.d.ts +1 -0
  50. package/packages/game/dist/types/inventory/playerInventory.d.ts.map +1 -1
  51. package/packages/game/dist/types/modes/ctf/capture.d.ts.map +1 -1
  52. package/packages/game/dist/types/modes/ctf/grapple.d.ts +13 -0
  53. package/packages/game/dist/types/modes/ctf/grapple.d.ts.map +1 -0
  54. package/packages/game/dist/types/modes/ctf/pickup.d.ts.map +1 -1
  55. package/packages/game/dist/types/modes/ctf/scoreboard.d.ts +34 -0
  56. package/packages/game/dist/types/modes/ctf/scoreboard.d.ts.map +1 -0
  57. package/packages/game/dist/types/entities/triggers.d.ts +0 -3
  58. package/packages/game/dist/types/entities/triggers.d.ts.map +0 -1
@@ -611,36 +611,36 @@ var MAX_ITEMS = 256;
611
611
  var MAX_GENERAL = MAX_CLIENTS * 2;
612
612
  var MAX_SHADOW_LIGHTS = 256;
613
613
  var MAX_WHEEL_ITEMS = 32;
614
- var ConfigStringIndex = ((ConfigStringIndex2) => {
615
- ConfigStringIndex2[ConfigStringIndex2["Name"] = 0] = "Name";
616
- ConfigStringIndex2[ConfigStringIndex2["CdTrack"] = 1] = "CdTrack";
617
- ConfigStringIndex2[ConfigStringIndex2["Sky"] = 2] = "Sky";
618
- ConfigStringIndex2[ConfigStringIndex2["SkyAxis"] = 3] = "SkyAxis";
619
- ConfigStringIndex2[ConfigStringIndex2["SkyRotate"] = 4] = "SkyRotate";
620
- ConfigStringIndex2[ConfigStringIndex2["StatusBar"] = 5] = "StatusBar";
621
- ConfigStringIndex2[ConfigStringIndex2["HealthBarName"] = 55] = "HealthBarName";
622
- ConfigStringIndex2[ConfigStringIndex2["CONFIG_N64_PHYSICS"] = 56] = "CONFIG_N64_PHYSICS";
623
- ConfigStringIndex2[ConfigStringIndex2["CONFIG_CTF_TEAMS"] = 57] = "CONFIG_CTF_TEAMS";
624
- ConfigStringIndex2[ConfigStringIndex2["CONFIG_COOP_RESPAWN_STRING"] = 58] = "CONFIG_COOP_RESPAWN_STRING";
625
- ConfigStringIndex2[ConfigStringIndex2["Story"] = 54] = "Story";
626
- ConfigStringIndex2[ConfigStringIndex2["AirAccel"] = 59] = "AirAccel";
627
- ConfigStringIndex2[ConfigStringIndex2["MaxClients"] = 60] = "MaxClients";
628
- ConfigStringIndex2[ConfigStringIndex2["MapChecksum"] = 61] = "MapChecksum";
629
- ConfigStringIndex2[ConfigStringIndex2["Models"] = 62] = "Models";
630
- ConfigStringIndex2[ConfigStringIndex2["Sounds"] = 62 + MAX_MODELS] = "Sounds";
631
- ConfigStringIndex2[ConfigStringIndex2["Images"] = ConfigStringIndex2.Sounds + MAX_SOUNDS] = "Images";
632
- ConfigStringIndex2[ConfigStringIndex2["Lights"] = ConfigStringIndex2.Images + MAX_IMAGES] = "Lights";
633
- ConfigStringIndex2[ConfigStringIndex2["ShadowLights"] = ConfigStringIndex2.Lights + MAX_LIGHTSTYLES] = "ShadowLights";
634
- ConfigStringIndex2[ConfigStringIndex2["Items"] = ConfigStringIndex2.ShadowLights + MAX_SHADOW_LIGHTS] = "Items";
635
- ConfigStringIndex2[ConfigStringIndex2["PlayerSkins"] = ConfigStringIndex2.Items + MAX_ITEMS] = "PlayerSkins";
636
- ConfigStringIndex2[ConfigStringIndex2["General"] = ConfigStringIndex2.PlayerSkins + MAX_CLIENTS] = "General";
637
- ConfigStringIndex2[ConfigStringIndex2["WheelWeapons"] = ConfigStringIndex2.General + MAX_GENERAL] = "WheelWeapons";
638
- ConfigStringIndex2[ConfigStringIndex2["WheelAmmo"] = ConfigStringIndex2.WheelWeapons + MAX_WHEEL_ITEMS] = "WheelAmmo";
639
- ConfigStringIndex2[ConfigStringIndex2["WheelPowerups"] = ConfigStringIndex2.WheelAmmo + MAX_WHEEL_ITEMS] = "WheelPowerups";
640
- ConfigStringIndex2[ConfigStringIndex2["CdLoopCount"] = ConfigStringIndex2.WheelPowerups + MAX_WHEEL_ITEMS] = "CdLoopCount";
641
- ConfigStringIndex2[ConfigStringIndex2["GameStyle"] = ConfigStringIndex2.CdLoopCount + 1] = "GameStyle";
642
- ConfigStringIndex2[ConfigStringIndex2["MaxConfigStrings"] = ConfigStringIndex2.GameStyle + 1] = "MaxConfigStrings";
643
- return ConfigStringIndex2;
614
+ var ConfigStringIndex = ((ConfigStringIndex22) => {
615
+ ConfigStringIndex22[ConfigStringIndex22["Name"] = 0] = "Name";
616
+ ConfigStringIndex22[ConfigStringIndex22["CdTrack"] = 1] = "CdTrack";
617
+ ConfigStringIndex22[ConfigStringIndex22["Sky"] = 2] = "Sky";
618
+ ConfigStringIndex22[ConfigStringIndex22["SkyAxis"] = 3] = "SkyAxis";
619
+ ConfigStringIndex22[ConfigStringIndex22["SkyRotate"] = 4] = "SkyRotate";
620
+ ConfigStringIndex22[ConfigStringIndex22["StatusBar"] = 5] = "StatusBar";
621
+ ConfigStringIndex22[ConfigStringIndex22["HealthBarName"] = 55] = "HealthBarName";
622
+ ConfigStringIndex22[ConfigStringIndex22["CONFIG_N64_PHYSICS"] = 56] = "CONFIG_N64_PHYSICS";
623
+ ConfigStringIndex22[ConfigStringIndex22["CONFIG_CTF_TEAMS"] = 57] = "CONFIG_CTF_TEAMS";
624
+ ConfigStringIndex22[ConfigStringIndex22["CONFIG_COOP_RESPAWN_STRING"] = 58] = "CONFIG_COOP_RESPAWN_STRING";
625
+ ConfigStringIndex22[ConfigStringIndex22["Story"] = 54] = "Story";
626
+ ConfigStringIndex22[ConfigStringIndex22["AirAccel"] = 59] = "AirAccel";
627
+ ConfigStringIndex22[ConfigStringIndex22["MaxClients"] = 60] = "MaxClients";
628
+ ConfigStringIndex22[ConfigStringIndex22["MapChecksum"] = 61] = "MapChecksum";
629
+ ConfigStringIndex22[ConfigStringIndex22["Models"] = 62] = "Models";
630
+ ConfigStringIndex22[ConfigStringIndex22["Sounds"] = 62 + MAX_MODELS] = "Sounds";
631
+ ConfigStringIndex22[ConfigStringIndex22["Images"] = ConfigStringIndex22.Sounds + MAX_SOUNDS] = "Images";
632
+ ConfigStringIndex22[ConfigStringIndex22["Lights"] = ConfigStringIndex22.Images + MAX_IMAGES] = "Lights";
633
+ ConfigStringIndex22[ConfigStringIndex22["ShadowLights"] = ConfigStringIndex22.Lights + MAX_LIGHTSTYLES] = "ShadowLights";
634
+ ConfigStringIndex22[ConfigStringIndex22["Items"] = ConfigStringIndex22.ShadowLights + MAX_SHADOW_LIGHTS] = "Items";
635
+ ConfigStringIndex22[ConfigStringIndex22["PlayerSkins"] = ConfigStringIndex22.Items + MAX_ITEMS] = "PlayerSkins";
636
+ ConfigStringIndex22[ConfigStringIndex22["General"] = ConfigStringIndex22.PlayerSkins + MAX_CLIENTS] = "General";
637
+ ConfigStringIndex22[ConfigStringIndex22["WheelWeapons"] = ConfigStringIndex22.General + MAX_GENERAL] = "WheelWeapons";
638
+ ConfigStringIndex22[ConfigStringIndex22["WheelAmmo"] = ConfigStringIndex22.WheelWeapons + MAX_WHEEL_ITEMS] = "WheelAmmo";
639
+ ConfigStringIndex22[ConfigStringIndex22["WheelPowerups"] = ConfigStringIndex22.WheelAmmo + MAX_WHEEL_ITEMS] = "WheelPowerups";
640
+ ConfigStringIndex22[ConfigStringIndex22["CdLoopCount"] = ConfigStringIndex22.WheelPowerups + MAX_WHEEL_ITEMS] = "CdLoopCount";
641
+ ConfigStringIndex22[ConfigStringIndex22["GameStyle"] = ConfigStringIndex22.CdLoopCount + 1] = "GameStyle";
642
+ ConfigStringIndex22[ConfigStringIndex22["MaxConfigStrings"] = ConfigStringIndex22.GameStyle + 1] = "MaxConfigStrings";
643
+ return ConfigStringIndex22;
644
644
  })(ConfigStringIndex || {});
645
645
  var MAX_CONFIGSTRINGS = ConfigStringIndex.MaxConfigStrings;
646
646
  var replay_exports = {};
@@ -5127,6 +5127,9 @@ var EntitySystem = class {
5127
5127
  soundIndex(sound) {
5128
5128
  return this.engine.soundIndex?.(sound) || 0;
5129
5129
  }
5130
+ configStringIndex(str) {
5131
+ return this.engine.configStringIndex?.(str) || 0;
5132
+ }
5130
5133
  modelIndex(model) {
5131
5134
  return this.engine.modelIndex?.(model) || 0;
5132
5135
  }
@@ -5139,8 +5142,8 @@ var EntitySystem = class {
5139
5142
  unlink(ent) {
5140
5143
  this.spatialGrid.remove(ent);
5141
5144
  }
5142
- multicast(origin, type, ServerCommand6, ...args) {
5143
- this.imports.multicast(origin, type, ServerCommand6, ...args);
5145
+ multicast(origin, type, ServerCommand7, ...args) {
5146
+ this.imports.multicast(origin, type, ServerCommand7, ...args);
5144
5147
  }
5145
5148
  unicast(ent, reliable, event, ...args) {
5146
5149
  this.imports.unicast(ent, reliable, event, ...args);
@@ -5881,6 +5884,19 @@ function createTrap(context, owner, start, dir, speed) {
5881
5884
  // src/entities/projectiles.ts
5882
5885
  var BFG_LASER_RADIUS = 256;
5883
5886
  var BFG_LASER_RANGE = 2048;
5887
+ function createProjectile(sys, start, dir, speed, mod, damage, radiusDamage) {
5888
+ const proj = sys.spawn();
5889
+ proj.movetype = 8 /* FlyMissile */;
5890
+ proj.solid = 2 /* BoundingBox */;
5891
+ proj.origin = { ...start };
5892
+ proj.velocity = { x: dir.x * speed, y: dir.y * speed, z: dir.z * speed };
5893
+ proj.mins = { x: -4, y: -4, z: -4 };
5894
+ proj.maxs = { x: 4, y: 4, z: 4 };
5895
+ proj.angles = vectorToAngles(dir);
5896
+ proj.takedamage = false;
5897
+ sys.finalizeSpawn(proj);
5898
+ return proj;
5899
+ }
5884
5900
  function createRocket(sys, owner, start, dir, damage, radiusDamage, speed, flashtype = 0) {
5885
5901
  const rocket = sys.spawn();
5886
5902
  rocket.classname = "rocket";
@@ -7732,170 +7748,7 @@ function registerTargetSpawns(registry) {
7732
7748
  });
7733
7749
  }
7734
7750
 
7735
- // src/entities/triggers/fog.ts
7736
- function clamp2(value, min, max) {
7737
- return Math.min(Math.max(value, min), max);
7738
- }
7739
- function lerp(start, end, t) {
7740
- return start + (end - start) * t;
7741
- }
7742
- var SPAWNFLAG_FOG_AFFECT_FOG = 1;
7743
- var SPAWNFLAG_FOG_INSTANTANEOUS = 4;
7744
- var SPAWNFLAG_FOG_FORCE = 8;
7745
- var SPAWNFLAG_FOG_BLEND = 16;
7746
- function trigger_fog_touch(self, other, context) {
7747
- if (!other || !other.client) return;
7748
- if (self.timestamp > context.timeSeconds) return;
7749
- self.timestamp = context.timeSeconds + (self.wait || 0);
7750
- let fog_value_storage = self;
7751
- if (self.movetarget) {
7752
- fog_value_storage = self.movetarget;
7753
- }
7754
- if (self.spawnflags & SPAWNFLAG_FOG_INSTANTANEOUS) {
7755
- if (other.client.pers) other.client.pers.fog_transition_time = 0;
7756
- } else {
7757
- if (other.client.pers) other.client.pers.fog_transition_time = fog_value_storage.delay || 0.5;
7758
- }
7759
- if (self.spawnflags & SPAWNFLAG_FOG_BLEND) {
7760
- const center = scaleVec3(addVec3(self.absmin, self.absmax), 0.5);
7761
- const half_size = addVec3(scaleVec3(self.size, 0.5), scaleVec3(other.size, 0.5));
7762
- const start = {
7763
- x: -self.movedir.x * half_size.x,
7764
- y: -self.movedir.y * half_size.y,
7765
- z: -self.movedir.z * half_size.z
7766
- };
7767
- const end = {
7768
- x: self.movedir.x * half_size.x,
7769
- y: self.movedir.y * half_size.y,
7770
- z: self.movedir.z * half_size.z
7771
- };
7772
- const delta = subtractVec3(other.origin, center);
7773
- const absMovedir = {
7774
- x: Math.abs(self.movedir.x),
7775
- y: Math.abs(self.movedir.y),
7776
- z: Math.abs(self.movedir.z)
7777
- };
7778
- const player_dist = {
7779
- x: delta.x * absMovedir.x,
7780
- y: delta.y * absMovedir.y,
7781
- z: delta.z * absMovedir.z
7782
- };
7783
- let dist = lengthVec3(subtractVec3(player_dist, start));
7784
- dist /= lengthVec3(subtractVec3(start, end));
7785
- dist = clamp2(dist, 0, 1);
7786
- if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
7787
- const storage2 = fog_value_storage;
7788
- if (other.client.pers) {
7789
- other.client.pers.wanted_fog = {
7790
- density: lerp(storage2.fog_density_off || 0, storage2.fog_density || 0, dist),
7791
- r: lerp(storage2.fog_color_off?.[0] || 0, storage2.fog_color?.[0] || 0, dist),
7792
- g: lerp(storage2.fog_color_off?.[1] || 0, storage2.fog_color?.[1] || 0, dist),
7793
- b: lerp(storage2.fog_color_off?.[2] || 0, storage2.fog_color?.[2] || 0, dist),
7794
- sky_factor: lerp(storage2.fog_sky_factor_off || 0, storage2.fog_sky_factor || 0, dist)
7795
- };
7796
- }
7797
- }
7798
- return;
7799
- }
7800
- let use_on = true;
7801
- if (!(self.spawnflags & SPAWNFLAG_FOG_FORCE)) {
7802
- const len = lengthVec3(other.velocity);
7803
- if (len <= 1e-4) return;
7804
- const forward = normalizeVec3(other.velocity);
7805
- const dot = forward.x * self.movedir.x + forward.y * self.movedir.y + forward.z * self.movedir.z;
7806
- use_on = dot > 0;
7807
- }
7808
- const storage = fog_value_storage;
7809
- if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
7810
- if (other.client.pers) {
7811
- if (use_on) {
7812
- other.client.pers.wanted_fog = {
7813
- density: storage.fog_density || 0,
7814
- r: storage.fog_color?.[0] || 0,
7815
- g: storage.fog_color?.[1] || 0,
7816
- b: storage.fog_color?.[2] || 0,
7817
- sky_factor: storage.fog_sky_factor || 0
7818
- };
7819
- } else {
7820
- other.client.pers.wanted_fog = {
7821
- density: storage.fog_density_off || 0,
7822
- r: storage.fog_color_off?.[0] || 0,
7823
- g: storage.fog_color_off?.[1] || 0,
7824
- b: storage.fog_color_off?.[2] || 0,
7825
- sky_factor: storage.fog_sky_factor_off || 0
7826
- };
7827
- }
7828
- }
7829
- }
7830
- }
7831
- function registerTriggerFog(registry) {
7832
- registry.register("trigger_fog", (entity, context) => {
7833
- entity.solid = 1 /* Trigger */;
7834
- entity.movetype = 0 /* None */;
7835
- entity.svflags |= 1 /* NoClient */;
7836
- const entAny = entity;
7837
- const kv = context.keyValues;
7838
- entAny.fog_density = parseFloat(kv.fog_density) || 0;
7839
- entAny.fog_color = kv.fog_color ? kv.fog_color.split(" ").map(parseFloat) : [0, 0, 0];
7840
- entAny.fog_sky_factor = parseFloat(kv.fog_sky_factor) || 0;
7841
- entAny.fog_density_off = parseFloat(kv.fog_density_off) || 0;
7842
- entAny.fog_color_off = kv.fog_color_off ? kv.fog_color_off.split(" ").map(parseFloat) : [0, 0, 0];
7843
- entAny.fog_sky_factor_off = parseFloat(kv.fog_sky_factor_off) || 0;
7844
- if (entity.target) {
7845
- entity.movetype = 0 /* None */;
7846
- entity.movetarget = null;
7847
- }
7848
- if (!entity.delay) entity.delay = 0.5;
7849
- entity.touch = (self, other) => trigger_fog_touch(self, other, context.entities);
7850
- });
7851
- }
7852
-
7853
- // src/entities/triggers/flashlight.ts
7854
- var registerTriggerFlashlight = (registry) => {
7855
- registry.register("trigger_flashlight", (self, context) => {
7856
- self.solid = 1 /* Trigger */;
7857
- self.movetype = 0;
7858
- self.svflags |= 1 /* NoClient */;
7859
- self.touch = (self2, other, plane, surf) => {
7860
- if (!other || !other.client) return;
7861
- const style = self2.style || 0;
7862
- let sound = "";
7863
- if (style === 0) {
7864
- if (other.flags & 4194304 /* Flashlight */) {
7865
- other.flags &= ~4194304 /* Flashlight */;
7866
- sound = "items/flashlight_off.wav";
7867
- } else {
7868
- other.flags |= 4194304 /* Flashlight */;
7869
- sound = "items/flashlight_on.wav";
7870
- }
7871
- } else if (style === 1) {
7872
- if (!(other.flags & 4194304 /* Flashlight */)) {
7873
- other.flags |= 4194304 /* Flashlight */;
7874
- sound = "items/flashlight_on.wav";
7875
- }
7876
- } else if (style === 2) {
7877
- if (other.flags & 4194304 /* Flashlight */) {
7878
- other.flags &= ~4194304 /* Flashlight */;
7879
- sound = "items/flashlight_off.wav";
7880
- }
7881
- }
7882
- if (other.flags & 4194304 /* Flashlight */) {
7883
- other.renderfx |= RenderFx.Flashlight;
7884
- } else {
7885
- other.renderfx &= ~RenderFx.Flashlight;
7886
- }
7887
- if (sound) {
7888
- context.entities.sound(other, 0, sound, 1, 3, 0);
7889
- }
7890
- };
7891
- context.entities.linkentity(self);
7892
- });
7893
- };
7894
-
7895
- // src/entities/triggers.ts
7896
- var FRAME_TIME_SECONDS = 1 / 40;
7897
- var THINK_INTERVAL = 0.1;
7898
- var HURT_INTERVAL = 0.1;
7751
+ // src/entities/triggers/common.ts
7899
7752
  var TRIGGER_SPAWNFLAGS = {
7900
7753
  Monster: 1 << 0,
7901
7754
  NotPlayer: 1 << 1,
@@ -7904,50 +7757,7 @@ var TRIGGER_SPAWNFLAGS = {
7904
7757
  Latched: 1 << 4,
7905
7758
  Clip: 1 << 5
7906
7759
  };
7907
- var RELAY_SPAWNFLAGS = {
7908
- NoSound: 1 << 0
7909
- };
7910
- var COUNTER_SPAWNFLAGS = {
7911
- NoMessage: 1 << 0
7912
- };
7913
- var PUSH_SPAWNFLAGS = {
7914
- Once: 1 << 0,
7915
- Plus: 1 << 1,
7916
- Silent: 1 << 2,
7917
- StartOff: 1 << 3,
7918
- Clip: 1 << 4
7919
- };
7920
- var HURT_SPAWNFLAGS = {
7921
- StartOff: 1 << 0,
7922
- Toggle: 1 << 1,
7923
- Silent: 1 << 2,
7924
- NoProtection: 1 << 3,
7925
- Slow: 1 << 4,
7926
- NoPlayers: 1 << 5,
7927
- NoMonsters: 1 << 6,
7928
- Clip: 1 << 7
7929
- };
7930
- var TELEPORT_SPAWNFLAGS = {
7931
- StartOn: 1 << 3
7932
- };
7933
- var GRAVITY_SPAWNFLAGS = {
7934
- Toggle: 1 << 0,
7935
- StartOff: 1 << 1,
7936
- Clip: 1 << 2
7937
- };
7938
- var MONSTERJUMP_SPAWNFLAGS = {
7939
- Toggle: 1 << 0,
7940
- StartOff: 1 << 1,
7941
- Clip: 1 << 2
7942
- };
7943
- function trainResume(train, entities) {
7944
- if (!train.think) {
7945
- train.think = (self) => {
7946
- self.nextthink = 0;
7947
- };
7948
- }
7949
- entities.scheduleThink(train, entities.timeSeconds + FRAME_TIME_SECONDS);
7950
- }
7760
+ var FRAME_TIME_SECONDS = 1 / 40;
7951
7761
  function initTrigger(entity) {
7952
7762
  entity.movetype = 0 /* None */;
7953
7763
  entity.solid = 1 /* Trigger */;
@@ -7955,10 +7765,6 @@ function initTrigger(entity) {
7955
7765
  entity.movedir = setMovedir(entity.angles);
7956
7766
  entity.angles = { x: 0, y: 0, z: 0 };
7957
7767
  }
7958
- function multiWait(self) {
7959
- self.nextthink = 0;
7960
- self.think = void 0;
7961
- }
7962
7768
  function canActivate(trigger, other) {
7963
7769
  if (trigger.solid === 0 /* Not */) {
7964
7770
  return false;
@@ -7983,6 +7789,15 @@ function canActivate(trigger, other) {
7983
7789
  }
7984
7790
  return true;
7985
7791
  }
7792
+ function triggerEnable(self) {
7793
+ self.solid = 1 /* Trigger */;
7794
+ }
7795
+
7796
+ // src/entities/triggers/multiple.ts
7797
+ function multiWait(self) {
7798
+ self.nextthink = 0;
7799
+ self.think = void 0;
7800
+ }
7986
7801
  function multiTrigger(self, entities) {
7987
7802
  if (self.nextthink > entities.timeSeconds) {
7988
7803
  return;
@@ -8041,9 +7856,6 @@ function useMulti(self, _other, activator, entities) {
8041
7856
  self.activator = activator;
8042
7857
  multiTrigger(self, entities);
8043
7858
  }
8044
- function triggerEnable(self) {
8045
- self.solid = 1 /* Trigger */;
8046
- }
8047
7859
  function registerTriggerMultiple(registry) {
8048
7860
  registry.register("trigger_multiple", (entity, context) => {
8049
7861
  initTrigger(entity);
@@ -8069,12 +7881,23 @@ function registerTriggerMultiple(registry) {
8069
7881
  entity.touch = (self, other) => touchMulti(self, other, context.entities);
8070
7882
  });
8071
7883
  }
7884
+
7885
+ // src/entities/triggers/once.ts
8072
7886
  function registerTriggerOnce(registry) {
8073
7887
  registry.register("trigger_once", (entity, context) => {
8074
7888
  entity.wait = -1;
8075
- registry.get("trigger_multiple")?.(entity, context);
7889
+ const multipleSpawn = registry.get("trigger_multiple");
7890
+ if (multipleSpawn) {
7891
+ multipleSpawn(entity, context);
7892
+ } else {
7893
+ }
8076
7894
  });
8077
7895
  }
7896
+
7897
+ // src/entities/triggers/relay.ts
7898
+ var RELAY_SPAWNFLAGS = {
7899
+ NoSound: 1 << 0
7900
+ };
8078
7901
  function registerTriggerRelay(registry) {
8079
7902
  registry.register("trigger_relay", (entity, context) => {
8080
7903
  if (entity.spawnflags & RELAY_SPAWNFLAGS.NoSound) {
@@ -8091,6 +7914,8 @@ function registerTriggerRelay(registry) {
8091
7914
  };
8092
7915
  });
8093
7916
  }
7917
+
7918
+ // src/entities/triggers/always.ts
8094
7919
  function registerTriggerAlways(registry) {
8095
7920
  registry.register("trigger_always", (entity, context) => {
8096
7921
  if (entity.delay === 0) {
@@ -8099,14 +7924,27 @@ function registerTriggerAlways(registry) {
8099
7924
  context.entities.useTargets(entity, entity);
8100
7925
  });
8101
7926
  }
7927
+
7928
+ // src/entities/triggers/counter.ts
7929
+ var COUNTER_SPAWNFLAGS = {
7930
+ NoMessage: 1 << 0
7931
+ };
8102
7932
  function counterUse(self, _other, activator, entities) {
8103
7933
  if (self.count === 0) {
8104
7934
  return;
8105
7935
  }
8106
7936
  self.count -= 1;
8107
7937
  if (self.count > 0) {
7938
+ if (!(self.spawnflags & COUNTER_SPAWNFLAGS.NoMessage) && activator && activator.client) {
7939
+ entities.engine.centerprintf?.(activator, `${self.count} more to go...`);
7940
+ entities.sound(activator, 0, "misc/talk1.wav", 1, 1, 0);
7941
+ }
8108
7942
  return;
8109
7943
  }
7944
+ if (!(self.spawnflags & COUNTER_SPAWNFLAGS.NoMessage) && activator && activator.client) {
7945
+ entities.engine.centerprintf?.(activator, "Sequence completed!");
7946
+ entities.sound(activator, 0, "misc/talk1.wav", 1, 1, 0);
7947
+ }
8110
7948
  self.activator = activator;
8111
7949
  multiTrigger(self, entities);
8112
7950
  }
@@ -8122,6 +7960,8 @@ function registerTriggerCounter(registry) {
8122
7960
  }
8123
7961
  });
8124
7962
  }
7963
+
7964
+ // src/entities/triggers/key.ts
8125
7965
  function triggerKeyUse(self, activator, entities, warn) {
8126
7966
  if (!self.item || !activator) {
8127
7967
  return;
@@ -8132,13 +7972,17 @@ function triggerKeyUse(self, activator, entities, warn) {
8132
7972
  return;
8133
7973
  }
8134
7974
  self.timestamp = entities.timeSeconds + 5;
8135
- warn(`Missing required key item: ${self.item}`);
7975
+ if (activator.client) {
7976
+ entities.engine.centerprintf?.(activator, `You need the ${self.item}`);
7977
+ entities.sound(activator, 0, "misc/keytry.wav", 1, 1, 0);
7978
+ }
8136
7979
  return;
8137
7980
  }
8138
7981
  activator.inventory[self.item] = available - 1;
8139
7982
  if (activator.inventory[self.item] <= 0) {
8140
7983
  delete activator.inventory[self.item];
8141
7984
  }
7985
+ entities.sound(activator, 0, "misc/keyuse.wav", 1, 1, 0);
8142
7986
  entities.useTargets(self, activator);
8143
7987
  self.use = void 0;
8144
7988
  }
@@ -8154,6 +7998,16 @@ function registerTriggerKey(registry) {
8154
7998
  entity.use = (self, other, activator) => triggerKeyUse(self, activator ?? other, context.entities, context.warn);
8155
7999
  });
8156
8000
  }
8001
+
8002
+ // src/entities/triggers/push.ts
8003
+ var PUSH_SPAWNFLAGS = {
8004
+ Once: 1 << 0,
8005
+ Plus: 1 << 1,
8006
+ Silent: 1 << 2,
8007
+ StartOff: 1 << 3,
8008
+ Clip: 1 << 4
8009
+ };
8010
+ var THINK_INTERVAL = 0.1;
8157
8011
  function triggerPushTouch(self, other, entities) {
8158
8012
  if (!other) {
8159
8013
  return;
@@ -8165,6 +8019,12 @@ function triggerPushTouch(self, other, entities) {
8165
8019
  y: self.movedir.y * scale,
8166
8020
  z: self.movedir.z * scale
8167
8021
  };
8022
+ if (!(self.spawnflags & PUSH_SPAWNFLAGS.Silent)) {
8023
+ if (!other.fly_sound_debounce_time || other.fly_sound_debounce_time < entities.timeSeconds) {
8024
+ other.fly_sound_debounce_time = entities.timeSeconds + 1.5;
8025
+ entities.sound(other, 0, "misc/windfly.wav", 1, 1, 0);
8026
+ }
8027
+ }
8168
8028
  }
8169
8029
  if (self.spawnflags & PUSH_SPAWNFLAGS.Once) {
8170
8030
  entities.free(self);
@@ -8226,10 +8086,23 @@ function registerTriggerPush(registry) {
8226
8086
  }
8227
8087
  });
8228
8088
  }
8229
- function hurtTouch(self, other, entities) {
8230
- if (!other) {
8231
- return;
8232
- }
8089
+
8090
+ // src/entities/triggers/hurt.ts
8091
+ var HURT_SPAWNFLAGS = {
8092
+ StartOff: 1 << 0,
8093
+ Toggle: 1 << 1,
8094
+ Silent: 1 << 2,
8095
+ NoProtection: 1 << 3,
8096
+ Slow: 1 << 4,
8097
+ NoPlayers: 1 << 5,
8098
+ NoMonsters: 1 << 6,
8099
+ Clip: 1 << 7
8100
+ };
8101
+ var HURT_INTERVAL = 0.1;
8102
+ function hurtTouch(self, other, entities) {
8103
+ if (!other) {
8104
+ return;
8105
+ }
8233
8106
  if (!other.takedamage && other.classname !== "grenade") {
8234
8107
  return;
8235
8108
  }
@@ -8246,6 +8119,9 @@ function hurtTouch(self, other, entities) {
8246
8119
  const damage = self.dmg || 5;
8247
8120
  other.health -= damage;
8248
8121
  }
8122
+ function toggleSolid2(self) {
8123
+ self.solid = self.solid === 0 /* Not */ ? 1 /* Trigger */ : 0 /* Not */;
8124
+ }
8249
8125
  function registerTriggerHurt(registry) {
8250
8126
  registry.register("trigger_hurt", (entity, context) => {
8251
8127
  initTrigger(entity);
@@ -8258,178 +8134,475 @@ function registerTriggerHurt(registry) {
8258
8134
  }
8259
8135
  if (entity.spawnflags & HURT_SPAWNFLAGS.Toggle) {
8260
8136
  entity.use = (self) => {
8261
- toggleSolid(self);
8137
+ toggleSolid2(self);
8138
+ self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8139
+ };
8140
+ }
8141
+ });
8142
+ }
8143
+
8144
+ // src/entities/triggers/teleport.ts
8145
+ var TELEPORT_SPAWNFLAGS = {
8146
+ StartOn: 1 << 3
8147
+ };
8148
+ function teleportTouch(self, other, entities, warn) {
8149
+ if (!other) {
8150
+ return;
8151
+ }
8152
+ if (self.delay > 0) {
8153
+ return;
8154
+ }
8155
+ const destination = entities.pickTarget(self.target);
8156
+ if (!destination) {
8157
+ warn("trigger_teleport target not found");
8158
+ return;
8159
+ }
8160
+ const destOrigin = {
8161
+ x: destination.origin.x,
8162
+ y: destination.origin.y,
8163
+ z: destination.origin.z + 10
8164
+ };
8165
+ other.origin = { ...destOrigin };
8166
+ other.old_origin = { ...destOrigin };
8167
+ other.velocity = { x: 0, y: 0, z: 0 };
8168
+ other.groundentity = null;
8169
+ other.angles = { ...destination.angles };
8170
+ entities.killBox(other);
8171
+ }
8172
+ function registerTriggerTeleport(registry) {
8173
+ registry.register("trigger_teleport", (entity, context) => {
8174
+ if (!entity.wait) {
8175
+ entity.wait = 0.2;
8176
+ }
8177
+ initTrigger(entity);
8178
+ if (entity.targetname) {
8179
+ entity.use = (self) => {
8180
+ self.delay = self.delay > 0 ? 0 : 1;
8181
+ };
8182
+ if ((entity.spawnflags & TELEPORT_SPAWNFLAGS.StartOn) === 0) {
8183
+ entity.delay = 1;
8184
+ }
8185
+ }
8186
+ entity.touch = (self, other) => teleportTouch(self, other, context.entities, context.warn);
8187
+ });
8188
+ }
8189
+
8190
+ // src/entities/triggers/gravity.ts
8191
+ var GRAVITY_SPAWNFLAGS = {
8192
+ Toggle: 1 << 0,
8193
+ StartOff: 1 << 1,
8194
+ Clip: 1 << 2
8195
+ };
8196
+ function gravityTouch(self, other) {
8197
+ if (!other) {
8198
+ return;
8199
+ }
8200
+ if (self.spawnflags & GRAVITY_SPAWNFLAGS.Clip) {
8201
+ }
8202
+ other.gravity = self.gravity;
8203
+ }
8204
+ function toggleSolid3(self) {
8205
+ self.solid = self.solid === 0 /* Not */ ? 1 /* Trigger */ : 0 /* Not */;
8206
+ }
8207
+ function registerTriggerGravity(registry) {
8208
+ registry.register("trigger_gravity", (entity, context) => {
8209
+ const gravityText = context.keyValues.gravity;
8210
+ if (!gravityText) {
8211
+ context.warn("trigger_gravity requires a gravity value");
8212
+ context.free(entity);
8213
+ return;
8214
+ }
8215
+ initTrigger(entity);
8216
+ entity.gravity = Number.parseFloat(gravityText) || 0;
8217
+ const touchHandler = (self, other) => gravityTouch(self, other);
8218
+ entity.touch = touchHandler;
8219
+ if (entity.spawnflags & GRAVITY_SPAWNFLAGS.StartOff) {
8220
+ entity.solid = 0 /* Not */;
8221
+ entity.touch = void 0;
8222
+ }
8223
+ if (entity.spawnflags & GRAVITY_SPAWNFLAGS.Toggle) {
8224
+ entity.use = (self) => {
8225
+ toggleSolid3(self);
8262
8226
  self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8263
8227
  };
8264
8228
  }
8265
8229
  });
8266
- }
8267
- function teleportTouch(self, other, entities, warn) {
8268
- if (!other) {
8230
+ }
8231
+
8232
+ // src/entities/triggers/elevator.ts
8233
+ function trainResume(train, entities) {
8234
+ if (!train.think) {
8235
+ train.think = (self) => {
8236
+ self.nextthink = 0;
8237
+ };
8238
+ }
8239
+ entities.scheduleThink(train, entities.timeSeconds + FRAME_TIME_SECONDS);
8240
+ }
8241
+ function triggerElevatorUse(self, other, entities, warn) {
8242
+ if (!self.movetarget) {
8243
+ return;
8244
+ }
8245
+ if (self.movetarget.nextthink > 0) {
8246
+ return;
8247
+ }
8248
+ if (!other?.pathtarget) {
8249
+ warn("trigger_elevator used with no pathtarget");
8250
+ return;
8251
+ }
8252
+ const target = entities.pickTarget(other.pathtarget);
8253
+ if (!target) {
8254
+ warn(`trigger_elevator used with bad pathtarget: ${other.pathtarget}`);
8255
+ return;
8256
+ }
8257
+ self.movetarget.target_ent = target;
8258
+ trainResume(self.movetarget, entities);
8259
+ }
8260
+ function triggerElevatorInit(self, entities, warn) {
8261
+ if (!self.target) {
8262
+ warn("trigger_elevator has no target");
8263
+ return;
8264
+ }
8265
+ const target = entities.pickTarget(self.target);
8266
+ if (!target) {
8267
+ warn(`trigger_elevator unable to find target ${self.target}`);
8268
+ return;
8269
+ }
8270
+ self.movetarget = target;
8271
+ if (target.classname !== "func_train") {
8272
+ warn(`trigger_elevator target ${self.target} is not a train`);
8273
+ return;
8274
+ }
8275
+ self.use = (entity, other, activator) => triggerElevatorUse(entity, other ?? activator ?? null, entities, warn);
8276
+ self.svflags |= 1 /* NoClient */;
8277
+ }
8278
+ function registerTriggerElevator(registry) {
8279
+ registry.register("trigger_elevator", (entity, context) => {
8280
+ entity.think = (self) => triggerElevatorInit(self, context.entities, context.warn);
8281
+ context.entities.scheduleThink(entity, context.entities.timeSeconds + FRAME_TIME_SECONDS);
8282
+ });
8283
+ }
8284
+
8285
+ // src/entities/triggers/monsterjump.ts
8286
+ var MONSTERJUMP_SPAWNFLAGS = {
8287
+ Toggle: 1 << 0,
8288
+ StartOff: 1 << 1,
8289
+ Clip: 1 << 2
8290
+ };
8291
+ function monsterJumpTouch(self, other) {
8292
+ if (!other) {
8293
+ return;
8294
+ }
8295
+ if ((other.flags & (1 /* Fly */ | 2 /* Swim */)) !== 0) {
8296
+ return;
8297
+ }
8298
+ if (other.svflags & 2 /* DeadMonster */) {
8299
+ return;
8300
+ }
8301
+ if ((other.svflags & 4 /* Monster */) === 0) {
8302
+ return;
8303
+ }
8304
+ other.velocity = {
8305
+ x: self.movedir.x * self.speed,
8306
+ y: self.movedir.y * self.speed,
8307
+ z: other.velocity.z
8308
+ };
8309
+ if (!other.groundentity) {
8310
+ return;
8311
+ }
8312
+ other.groundentity = null;
8313
+ other.velocity = { x: other.velocity.x, y: other.velocity.y, z: self.movedir.z };
8314
+ }
8315
+ function toggleSolid4(self) {
8316
+ self.solid = self.solid === 0 /* Not */ ? 1 /* Trigger */ : 0 /* Not */;
8317
+ }
8318
+ function registerTriggerMonsterJump(registry) {
8319
+ registry.register("trigger_monsterjump", (entity, context) => {
8320
+ const heightText = context.keyValues.height;
8321
+ const height = heightText ? Number.parseFloat(heightText) || 0 : 200;
8322
+ if (entity.angles.y === 0) {
8323
+ entity.angles = { ...entity.angles, y: 360 };
8324
+ }
8325
+ if (!entity.speed) {
8326
+ entity.speed = 200;
8327
+ }
8328
+ initTrigger(entity);
8329
+ entity.movedir = { ...entity.movedir, z: height };
8330
+ const touchHandler = (self, other) => monsterJumpTouch(self, other);
8331
+ entity.touch = touchHandler;
8332
+ if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.StartOff) {
8333
+ entity.solid = 0 /* Not */;
8334
+ entity.touch = void 0;
8335
+ }
8336
+ if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.Toggle) {
8337
+ entity.use = (self) => {
8338
+ toggleSolid4(self);
8339
+ self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8340
+ };
8341
+ }
8342
+ });
8343
+ }
8344
+
8345
+ // src/entities/triggers/fog.ts
8346
+ function clamp2(value, min, max) {
8347
+ return Math.min(Math.max(value, min), max);
8348
+ }
8349
+ function lerp(start, end, t) {
8350
+ return start + (end - start) * t;
8351
+ }
8352
+ var SPAWNFLAG_FOG_AFFECT_FOG = 1;
8353
+ var SPAWNFLAG_FOG_INSTANTANEOUS = 4;
8354
+ var SPAWNFLAG_FOG_FORCE = 8;
8355
+ var SPAWNFLAG_FOG_BLEND = 16;
8356
+ function trigger_fog_touch(self, other, context) {
8357
+ if (!other || !other.client) return;
8358
+ if (self.timestamp > context.timeSeconds) return;
8359
+ self.timestamp = context.timeSeconds + (self.wait || 0);
8360
+ let fog_value_storage = self;
8361
+ if (self.movetarget) {
8362
+ fog_value_storage = self.movetarget;
8363
+ }
8364
+ if (self.spawnflags & SPAWNFLAG_FOG_INSTANTANEOUS) {
8365
+ if (other.client.pers) other.client.pers.fog_transition_time = 0;
8366
+ } else {
8367
+ if (other.client.pers) other.client.pers.fog_transition_time = fog_value_storage.delay || 0.5;
8368
+ }
8369
+ if (self.spawnflags & SPAWNFLAG_FOG_BLEND) {
8370
+ const center = scaleVec3(addVec3(self.absmin, self.absmax), 0.5);
8371
+ const half_size = addVec3(scaleVec3(self.size, 0.5), scaleVec3(other.size, 0.5));
8372
+ const start = {
8373
+ x: -self.movedir.x * half_size.x,
8374
+ y: -self.movedir.y * half_size.y,
8375
+ z: -self.movedir.z * half_size.z
8376
+ };
8377
+ const end = {
8378
+ x: self.movedir.x * half_size.x,
8379
+ y: self.movedir.y * half_size.y,
8380
+ z: self.movedir.z * half_size.z
8381
+ };
8382
+ const delta = subtractVec3(other.origin, center);
8383
+ const absMovedir = {
8384
+ x: Math.abs(self.movedir.x),
8385
+ y: Math.abs(self.movedir.y),
8386
+ z: Math.abs(self.movedir.z)
8387
+ };
8388
+ const player_dist = {
8389
+ x: delta.x * absMovedir.x,
8390
+ y: delta.y * absMovedir.y,
8391
+ z: delta.z * absMovedir.z
8392
+ };
8393
+ let dist = lengthVec3(subtractVec3(player_dist, start));
8394
+ dist /= lengthVec3(subtractVec3(start, end));
8395
+ dist = clamp2(dist, 0, 1);
8396
+ if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
8397
+ const storage2 = fog_value_storage;
8398
+ if (other.client.pers) {
8399
+ other.client.pers.wanted_fog = {
8400
+ density: lerp(storage2.fog_density_off || 0, storage2.fog_density || 0, dist),
8401
+ r: lerp(storage2.fog_color_off?.[0] || 0, storage2.fog_color?.[0] || 0, dist),
8402
+ g: lerp(storage2.fog_color_off?.[1] || 0, storage2.fog_color?.[1] || 0, dist),
8403
+ b: lerp(storage2.fog_color_off?.[2] || 0, storage2.fog_color?.[2] || 0, dist),
8404
+ sky_factor: lerp(storage2.fog_sky_factor_off || 0, storage2.fog_sky_factor || 0, dist)
8405
+ };
8406
+ }
8407
+ }
8408
+ return;
8409
+ }
8410
+ let use_on = true;
8411
+ if (!(self.spawnflags & SPAWNFLAG_FOG_FORCE)) {
8412
+ const len = lengthVec3(other.velocity);
8413
+ if (len <= 1e-4) return;
8414
+ const forward = normalizeVec3(other.velocity);
8415
+ const dot = forward.x * self.movedir.x + forward.y * self.movedir.y + forward.z * self.movedir.z;
8416
+ use_on = dot > 0;
8417
+ }
8418
+ const storage = fog_value_storage;
8419
+ if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
8420
+ if (other.client.pers) {
8421
+ if (use_on) {
8422
+ other.client.pers.wanted_fog = {
8423
+ density: storage.fog_density || 0,
8424
+ r: storage.fog_color?.[0] || 0,
8425
+ g: storage.fog_color?.[1] || 0,
8426
+ b: storage.fog_color?.[2] || 0,
8427
+ sky_factor: storage.fog_sky_factor || 0
8428
+ };
8429
+ } else {
8430
+ other.client.pers.wanted_fog = {
8431
+ density: storage.fog_density_off || 0,
8432
+ r: storage.fog_color_off?.[0] || 0,
8433
+ g: storage.fog_color_off?.[1] || 0,
8434
+ b: storage.fog_color_off?.[2] || 0,
8435
+ sky_factor: storage.fog_sky_factor_off || 0
8436
+ };
8437
+ }
8438
+ }
8439
+ }
8440
+ }
8441
+ function registerTriggerFog(registry) {
8442
+ registry.register("trigger_fog", (entity, context) => {
8443
+ entity.solid = 1 /* Trigger */;
8444
+ entity.movetype = 0 /* None */;
8445
+ entity.svflags |= 1 /* NoClient */;
8446
+ const entAny = entity;
8447
+ const kv = context.keyValues;
8448
+ entAny.fog_density = parseFloat(kv.fog_density) || 0;
8449
+ entAny.fog_color = kv.fog_color ? kv.fog_color.split(" ").map(parseFloat) : [0, 0, 0];
8450
+ entAny.fog_sky_factor = parseFloat(kv.fog_sky_factor) || 0;
8451
+ entAny.fog_density_off = parseFloat(kv.fog_density_off) || 0;
8452
+ entAny.fog_color_off = kv.fog_color_off ? kv.fog_color_off.split(" ").map(parseFloat) : [0, 0, 0];
8453
+ entAny.fog_sky_factor_off = parseFloat(kv.fog_sky_factor_off) || 0;
8454
+ if (entity.target) {
8455
+ entity.movetype = 0 /* None */;
8456
+ entity.movetarget = null;
8457
+ }
8458
+ if (!entity.delay) entity.delay = 0.5;
8459
+ entity.touch = (self, other) => trigger_fog_touch(self, other, context.entities);
8460
+ });
8461
+ }
8462
+
8463
+ // src/entities/triggers/flashlight.ts
8464
+ var registerTriggerFlashlight = (registry) => {
8465
+ registry.register("trigger_flashlight", (self, context) => {
8466
+ self.solid = 1 /* Trigger */;
8467
+ self.movetype = 0;
8468
+ self.svflags |= 1 /* NoClient */;
8469
+ self.touch = (self2, other, plane, surf) => {
8470
+ if (!other || !other.client) return;
8471
+ const style = self2.style || 0;
8472
+ let sound = "";
8473
+ if (style === 0) {
8474
+ if (other.flags & 4194304 /* Flashlight */) {
8475
+ other.flags &= ~4194304 /* Flashlight */;
8476
+ sound = "items/flashlight_off.wav";
8477
+ } else {
8478
+ other.flags |= 4194304 /* Flashlight */;
8479
+ sound = "items/flashlight_on.wav";
8480
+ }
8481
+ } else if (style === 1) {
8482
+ if (!(other.flags & 4194304 /* Flashlight */)) {
8483
+ other.flags |= 4194304 /* Flashlight */;
8484
+ sound = "items/flashlight_on.wav";
8485
+ }
8486
+ } else if (style === 2) {
8487
+ if (other.flags & 4194304 /* Flashlight */) {
8488
+ other.flags &= ~4194304 /* Flashlight */;
8489
+ sound = "items/flashlight_off.wav";
8490
+ }
8491
+ }
8492
+ if (other.flags & 4194304 /* Flashlight */) {
8493
+ other.renderfx |= RenderFx.Flashlight;
8494
+ } else {
8495
+ other.renderfx &= ~RenderFx.Flashlight;
8496
+ }
8497
+ if (sound) {
8498
+ context.entities.sound(other, 0, sound, 1, 3, 0);
8499
+ }
8500
+ };
8501
+ context.entities.linkentity(self);
8502
+ });
8503
+ };
8504
+
8505
+ // src/entities/triggers/secret.ts
8506
+ var SECRET_SPAWNFLAGS = {
8507
+ Once: 1 << 0
8508
+ };
8509
+ function triggerSecretTouch(self, other, entities) {
8510
+ if (!other || !other.client) {
8269
8511
  return;
8270
8512
  }
8271
- if (self.delay > 0) {
8513
+ if (self.timestamp > entities.timeSeconds) {
8272
8514
  return;
8273
8515
  }
8274
- const destination = entities.pickTarget(self.target);
8275
- if (!destination) {
8276
- warn("trigger_teleport target not found");
8277
- return;
8516
+ self.timestamp = entities.timeSeconds + 1;
8517
+ if (!self.message) {
8518
+ self.message = "You found a secret area!";
8278
8519
  }
8279
- const destOrigin = {
8280
- x: destination.origin.x,
8281
- y: destination.origin.y,
8282
- z: destination.origin.z + 10
8283
- };
8284
- other.origin = { ...destOrigin };
8285
- other.old_origin = { ...destOrigin };
8286
- other.velocity = { x: 0, y: 0, z: 0 };
8287
- other.groundentity = null;
8288
- other.angles = { ...destination.angles };
8289
- entities.killBox(other);
8290
- }
8291
- function registerTriggerTeleport(registry) {
8292
- registry.register("trigger_teleport", (entity, context) => {
8293
- if (!entity.wait) {
8294
- entity.wait = 0.2;
8295
- }
8296
- initTrigger(entity);
8297
- if (entity.targetname) {
8298
- entity.use = (self) => {
8299
- self.delay = self.delay > 0 ? 0 : 1;
8300
- };
8301
- if ((entity.spawnflags & TELEPORT_SPAWNFLAGS.StartOn) === 0) {
8302
- entity.delay = 1;
8303
- }
8304
- }
8305
- entity.touch = (self, other) => teleportTouch(self, other, context.entities, context.warn);
8306
- });
8307
- }
8308
- function gravityTouch(self, other) {
8309
- if (!other) {
8310
- return;
8520
+ if (other.client) {
8521
+ entities.engine.centerprintf?.(other, self.message);
8522
+ entities.sound(other, 0, "misc/secret.wav", 1, 1, 0);
8311
8523
  }
8312
- if (self.spawnflags & GRAVITY_SPAWNFLAGS.Clip) {
8524
+ if (self.spawnflags & SECRET_SPAWNFLAGS.Once) {
8525
+ entities.free(self);
8313
8526
  }
8314
- other.gravity = self.gravity;
8315
8527
  }
8316
- function registerTriggerGravity(registry) {
8317
- registry.register("trigger_gravity", (entity, context) => {
8318
- const gravityText = context.keyValues.gravity;
8319
- if (!gravityText) {
8320
- context.warn("trigger_gravity requires a gravity value");
8321
- context.free(entity);
8322
- return;
8323
- }
8324
- initTrigger(entity);
8325
- entity.gravity = Number.parseFloat(gravityText) || 0;
8326
- const touchHandler = (self, other) => gravityTouch(self, other);
8327
- entity.touch = touchHandler;
8328
- if (entity.spawnflags & GRAVITY_SPAWNFLAGS.StartOff) {
8528
+ function registerTriggerSecret(registry) {
8529
+ registry.register("trigger_secret", (entity, context) => {
8530
+ entity.movetype = 0 /* None */;
8531
+ entity.solid = 1 /* Trigger */;
8532
+ entity.svflags |= 1 /* NoClient */;
8533
+ entity.movedir = setMovedir(entity.angles);
8534
+ entity.angles = { x: 0, y: 0, z: 0 };
8535
+ if (entity.targetname) {
8329
8536
  entity.solid = 0 /* Not */;
8330
- entity.touch = void 0;
8331
- }
8332
- if (entity.spawnflags & GRAVITY_SPAWNFLAGS.Toggle) {
8333
8537
  entity.use = (self) => {
8334
- toggleSolid(self);
8335
- self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8538
+ self.solid = 1 /* Trigger */;
8539
+ self.use = void 0;
8336
8540
  };
8337
8541
  }
8542
+ entity.touch = (self, other) => triggerSecretTouch(self, other, context.entities);
8338
8543
  });
8339
8544
  }
8340
- function monsterJumpTouch(self, other) {
8341
- if (!other) {
8342
- return;
8343
- }
8344
- if ((other.flags & (1 /* Fly */ | 2 /* Swim */)) !== 0) {
8345
- return;
8346
- }
8347
- if (other.svflags & 2 /* DeadMonster */) {
8348
- return;
8349
- }
8350
- if ((other.svflags & 4 /* Monster */) === 0) {
8351
- return;
8545
+
8546
+ // src/entities/triggers/look.ts
8547
+ var LOOK_SPAWNFLAGS = {
8548
+ LookAway: 1 << 0
8549
+ };
8550
+ function triggerLookThink(self, entities) {
8551
+ let fired = false;
8552
+ const players = entities.findByClassname("player");
8553
+ for (const player of players) {
8554
+ if (player.health <= 0) continue;
8555
+ const vec = subtractVec3(self.origin, player.origin);
8556
+ const dist = Math.sqrt(dotVec3(vec, vec));
8557
+ const dir = normalizeVec3(vec);
8558
+ const forward = angleVectors(player.angles).forward;
8559
+ const dot = dotVec3(forward, dir);
8560
+ const fov = self.fov || 0.9;
8561
+ if (dot >= fov) {
8562
+ fired = true;
8563
+ self.activator = player;
8564
+ entities.useTargets(self, player);
8565
+ break;
8566
+ }
8352
8567
  }
8353
- other.velocity = {
8354
- x: self.movedir.x * self.speed,
8355
- y: self.movedir.y * self.speed,
8356
- z: other.velocity.z
8357
- };
8358
- if (!other.groundentity) {
8359
- return;
8568
+ if (fired) {
8569
+ entities.free(self);
8570
+ } else {
8571
+ entities.scheduleThink(self, entities.timeSeconds + 0.1);
8360
8572
  }
8361
- other.groundentity = null;
8362
- other.velocity = { x: other.velocity.x, y: other.velocity.y, z: self.movedir.z };
8363
8573
  }
8364
- function triggerElevatorUse(self, other, entities, warn) {
8365
- if (!self.movetarget) {
8366
- return;
8367
- }
8368
- if (self.movetarget.nextthink > 0) {
8369
- return;
8370
- }
8371
- if (!other?.pathtarget) {
8372
- warn("trigger_elevator used with no pathtarget");
8373
- return;
8374
- }
8375
- const target = entities.pickTarget(other.pathtarget);
8376
- if (!target) {
8377
- warn(`trigger_elevator used with bad pathtarget: ${other.pathtarget}`);
8378
- return;
8379
- }
8380
- self.movetarget.target_ent = target;
8381
- trainResume(self.movetarget, entities);
8574
+ function registerTriggerLook(registry) {
8575
+ registry.register("trigger_look", (entity, context) => {
8576
+ entity.movetype = 0 /* None */;
8577
+ entity.solid = 0 /* Not */;
8578
+ entity.svflags |= 1 /* NoClient */;
8579
+ entity.movedir = setMovedir(entity.angles);
8580
+ if (!entity.fov) entity.fov = 0.9;
8581
+ entity.think = (self) => triggerLookThink(self, context.entities);
8582
+ context.entities.scheduleThink(entity, context.entities.timeSeconds + 0.1);
8583
+ });
8382
8584
  }
8383
- function triggerElevatorInit(self, entities, warn) {
8384
- if (!self.target) {
8385
- warn("trigger_elevator has no target");
8386
- return;
8387
- }
8388
- const target = entities.pickTarget(self.target);
8389
- if (!target) {
8390
- warn(`trigger_elevator unable to find target ${self.target}`);
8391
- return;
8392
- }
8393
- self.movetarget = target;
8394
- if (target.classname !== "func_train") {
8395
- warn(`trigger_elevator target ${self.target} is not a train`);
8396
- return;
8585
+
8586
+ // src/entities/triggers/bad_area.ts
8587
+ function badAreaTouch(self, other) {
8588
+ if (!other) return;
8589
+ if (other.takedamage) {
8397
8590
  }
8398
- self.use = (entity, other, activator) => triggerElevatorUse(entity, other ?? activator ?? null, entities, warn);
8399
- self.svflags |= 1 /* NoClient */;
8400
- }
8401
- function registerTriggerElevator(registry) {
8402
- registry.register("trigger_elevator", (entity, context) => {
8403
- entity.think = (self) => triggerElevatorInit(self, context.entities, context.warn);
8404
- context.entities.scheduleThink(entity, context.entities.timeSeconds + FRAME_TIME_SECONDS);
8405
- });
8406
8591
  }
8407
- function registerTriggerMonsterJump(registry) {
8408
- registry.register("trigger_monsterjump", (entity, context) => {
8409
- const heightText = context.keyValues.height;
8410
- const height = heightText ? Number.parseFloat(heightText) || 0 : 200;
8411
- if (entity.angles.y === 0) {
8412
- entity.angles = { ...entity.angles, y: 360 };
8413
- }
8414
- if (!entity.speed) {
8415
- entity.speed = 200;
8416
- }
8417
- initTrigger(entity);
8418
- entity.movedir = { ...entity.movedir, z: height };
8419
- const touchHandler = (self, other) => monsterJumpTouch(self, other);
8420
- entity.touch = touchHandler;
8421
- if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.StartOff) {
8422
- entity.solid = 0 /* Not */;
8423
- entity.touch = void 0;
8424
- }
8425
- if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.Toggle) {
8426
- entity.use = (self) => {
8427
- toggleSolid(self);
8428
- self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8592
+ function registerBadArea(registry) {
8593
+ registry.register("bad_area", (entity, context) => {
8594
+ entity.movetype = 0 /* None */;
8595
+ entity.solid = 1 /* Trigger */;
8596
+ entity.touch = badAreaTouch;
8597
+ if (entity.nextthink) {
8598
+ entity.think = (self) => {
8599
+ context.entities.free(self);
8429
8600
  };
8430
8601
  }
8431
8602
  });
8432
8603
  }
8604
+
8605
+ // src/entities/triggers/index.ts
8433
8606
  function registerTriggerSpawns(registry) {
8434
8607
  registerTriggerMultiple(registry);
8435
8608
  registerTriggerOnce(registry);
@@ -8445,25 +8618,10 @@ function registerTriggerSpawns(registry) {
8445
8618
  registerTriggerMonsterJump(registry);
8446
8619
  registerTriggerFog(registry);
8447
8620
  registerTriggerFlashlight(registry);
8621
+ registerTriggerSecret(registry);
8622
+ registerTriggerLook(registry);
8448
8623
  registerBadArea(registry);
8449
8624
  }
8450
- function badAreaTouch(self, other) {
8451
- if (!other) return;
8452
- if (other.takedamage) {
8453
- }
8454
- }
8455
- function registerBadArea(registry) {
8456
- registry.register("bad_area", (entity, context) => {
8457
- entity.movetype = 0 /* None */;
8458
- entity.solid = 1 /* Trigger */;
8459
- entity.touch = badAreaTouch;
8460
- if (entity.nextthink) {
8461
- entity.think = (self) => {
8462
- context.entities.free(self);
8463
- };
8464
- }
8465
- });
8466
- }
8467
8625
 
8468
8626
  // src/combat/weapons/state.ts
8469
8627
  var WeaponStateEnum = /* @__PURE__ */ ((WeaponStateEnum2) => {
@@ -9778,6 +9936,95 @@ function Trap_Think(player, sys) {
9778
9936
  );
9779
9937
  }
9780
9938
 
9939
+ // src/modes/ctf/grapple.ts
9940
+ var GRAPPLE_SPEED = 1200;
9941
+ var GRAPPLE_PULL_SPEED = 800;
9942
+ var GRAPPLE_DAMAGE = 20;
9943
+ function Grapple_Think(player, sys) {
9944
+ if (player.client?.inventory.currentWeapon !== WeaponId.Grapple) {
9945
+ ResetGrapple(player, sys);
9946
+ }
9947
+ Weapon_Generic(
9948
+ player,
9949
+ 0,
9950
+ 10,
9951
+ // ready
9952
+ 11,
9953
+ 20,
9954
+ // fire
9955
+ [],
9956
+ // pause frames
9957
+ [11],
9958
+ // fire frames
9959
+ (ent) => fire_grapple(ent, sys),
9960
+ sys
9961
+ );
9962
+ if (player.client && !(player.client.buttons & 1)) {
9963
+ ResetGrapple(player, sys);
9964
+ }
9965
+ if (player.grapple && player.grapple.grappleState === "attached") {
9966
+ Grapple_Pull(player, player.grapple, sys);
9967
+ }
9968
+ }
9969
+ function fire_grapple(player, sys) {
9970
+ if (player.grapple) return;
9971
+ const angles = player.client.v_angle || player.angles;
9972
+ const vectors = angleVectors(angles);
9973
+ const forward = vectors.forward;
9974
+ const right = vectors.right;
9975
+ const up = vectors.up;
9976
+ const game = sys.game;
9977
+ const start = P_ProjectSource(game, player, { x: 8, y: 8, z: 8 }, forward, right, up);
9978
+ const dir = { ...forward };
9979
+ const grapple = createProjectile(sys, start, dir, GRAPPLE_SPEED, 57 /* GRAPPLE */, 0);
9980
+ grapple.classname = "grapple";
9981
+ grapple.owner = player;
9982
+ grapple.movetype = 8 /* FlyMissile */;
9983
+ grapple.solid = 2 /* BoundingBox */;
9984
+ grapple.mins = { x: -4, y: -4, z: -4 };
9985
+ grapple.maxs = { x: 4, y: 4, z: 4 };
9986
+ grapple.model = "models/weapons/grapple/hook/tris.md2";
9987
+ grapple.grappleState = "fly";
9988
+ grapple.touch = (self, other, plane, surface) => {
9989
+ if (!other) return;
9990
+ Grapple_Touch(self, other, plane, surface, sys);
9991
+ };
9992
+ player.grapple = grapple;
9993
+ sys.sound(player, 0, "weapons/grapple/throw.wav", 1, 1, 0);
9994
+ }
9995
+ function Grapple_Touch(self, other, plane, surface, sys) {
9996
+ if (other === self.owner) return;
9997
+ if (self.grappleState === "attached") return;
9998
+ if (other.takedamage) {
9999
+ T_Damage(other, self, self.owner, self.velocity, self.origin, ZERO_VEC3, GRAPPLE_DAMAGE, 0, 0, 57 /* GRAPPLE */, sys.timeSeconds, sys.multicast.bind(sys));
10000
+ sys.free(self);
10001
+ if (self.owner) self.owner.grapple = void 0;
10002
+ return;
10003
+ }
10004
+ if (other.solid === 3 /* Bsp */ || other.solid === 2 /* BoundingBox */) {
10005
+ self.velocity = { x: 0, y: 0, z: 0 };
10006
+ self.movetype = 0 /* None */;
10007
+ self.grappleState = "attached";
10008
+ sys.sound(self, 0, "weapons/grapple/hit.wav", 1, 1, 0);
10009
+ if (plane) {
10010
+ }
10011
+ }
10012
+ }
10013
+ function Grapple_Pull(player, grapple, sys) {
10014
+ const dir = subtractVec3(grapple.origin, player.origin);
10015
+ const dist = lengthVec3(dir);
10016
+ if (dist < 32) return;
10017
+ const normalizedDir = normalizeVec3(dir);
10018
+ const pull = scaleVec3(normalizedDir, GRAPPLE_PULL_SPEED);
10019
+ player.velocity = pull;
10020
+ }
10021
+ function ResetGrapple(player, sys) {
10022
+ if (player.grapple) {
10023
+ sys.free(player.grapple);
10024
+ player.grapple = void 0;
10025
+ }
10026
+ }
10027
+
9781
10028
  // src/inventory/items.ts
9782
10029
  var WEAPON_ITEMS = {
9783
10030
  "weapon_blaster": {
@@ -9975,6 +10222,18 @@ var WEAPON_ITEMS = {
9975
10222
  pickupAmmo: 5,
9976
10223
  fireRate: 1,
9977
10224
  think: Trap_Think
10225
+ },
10226
+ "weapon_grapple": {
10227
+ type: "weapon",
10228
+ id: "weapon_grapple",
10229
+ name: "Grapple",
10230
+ weaponId: WeaponId.Grapple,
10231
+ ammoType: null,
10232
+ // Grapple uses no ammo in standard Q2 CTF? Or maybe just internal timer.
10233
+ initialAmmo: 0,
10234
+ pickupAmmo: 0,
10235
+ fireRate: 0.5,
10236
+ think: Grapple_Think
9978
10237
  }
9979
10238
  };
9980
10239
  var HEALTH_ITEMS = {
@@ -10376,6 +10635,34 @@ function setFlagState(flag, newState, context) {
10376
10635
  flag.flagState = newState;
10377
10636
  }
10378
10637
 
10638
+ // src/modes/ctf/scoreboard.ts
10639
+ var teamScores = {
10640
+ [1 /* RED */]: 0,
10641
+ [2 /* BLUE */]: 0
10642
+ };
10643
+ function addTeamScore(team, points) {
10644
+ if (team === 1 /* RED */ || team === 2 /* BLUE */) {
10645
+ teamScores[team] += points;
10646
+ }
10647
+ }
10648
+ function updateCtfScoreboard(ent, sys) {
10649
+ if (!ent.client || !ent.client.stats) return;
10650
+ const stats = ent.client.stats;
10651
+ stats[PlayerStat.STAT_CTF_TEAM1_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_r.pcx") : 0;
10652
+ stats[PlayerStat.STAT_CTF_TEAM1_CAPS] = teamScores[1 /* RED */];
10653
+ stats[PlayerStat.STAT_CTF_TEAM2_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_b.pcx") : 0;
10654
+ stats[PlayerStat.STAT_CTF_TEAM2_CAPS] = teamScores[2 /* BLUE */];
10655
+ const clientTeam = ent.client.ctfTeam;
10656
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM1_PIC] = 0;
10657
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM2_PIC] = 0;
10658
+ if (clientTeam === 1 /* RED */) {
10659
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM1_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_r.pcx") : 0;
10660
+ } else if (clientTeam === 2 /* BLUE */) {
10661
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM2_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_b.pcx") : 0;
10662
+ }
10663
+ stats[PlayerStat.STAT_CTF_TEAMINFO] = 1;
10664
+ }
10665
+
10379
10666
  // src/modes/ctf/capture.ts
10380
10667
  function checkCapture(flag, player, game, context) {
10381
10668
  if (!player.client) return false;
@@ -10396,11 +10683,17 @@ function captureFlag(ownFlag, player, game, context) {
10396
10683
  if (!player.client) return false;
10397
10684
  const playerTeam = player.client.team || "red";
10398
10685
  const enemyTeam = playerTeam === "red" ? "blue" : "red";
10686
+ const ctfStats = player.client.ctfStats;
10687
+ if (ctfStats) {
10688
+ ctfStats.captures++;
10689
+ }
10399
10690
  if (player.client.score !== void 0) {
10400
10691
  player.client.score += 5;
10401
10692
  } else {
10402
10693
  player.client.score = 5;
10403
10694
  }
10695
+ const teamEnum = playerTeam === "red" ? 1 /* RED */ : 2 /* BLUE */;
10696
+ addTeamScore(teamEnum, 1);
10404
10697
  game.sound?.(player, 0, "ctf/flagcap.wav", 1, 1, 0);
10405
10698
  game.centerprintf?.(player, "You captured the flag!");
10406
10699
  const enemyFlagKey = playerTeam === "red" ? "key_blue_flag" /* BlueFlag */ : "key_red_flag" /* RedFlag */;
@@ -26030,6 +26323,9 @@ function player_think(self, sys) {
26030
26323
  }
26031
26324
  }
26032
26325
  P_PlayerThink(self, sys);
26326
+ if (sys.deathmatch && (sys.configStringIndex ? sys.configStringIndex("pics/ctf_r.pcx") > 0 : true)) {
26327
+ updateCtfScoreboard(self, sys);
26328
+ }
26033
26329
  self.nextthink = sys.timeSeconds + 0.1;
26034
26330
  sys.scheduleThink(self, self.nextthink);
26035
26331
  }
@@ -26097,6 +26393,16 @@ function populatePlayerStats(player, timeSeconds) {
26097
26393
  const remainingSeconds = Math.ceil((bestTime - nowMs) / 1e3);
26098
26394
  statArray[PlayerStat.STAT_TIMER] = remainingSeconds;
26099
26395
  }
26396
+ if (player.client.score !== void 0) {
26397
+ statArray[PlayerStat.STAT_FRAGS] = player.client.score;
26398
+ }
26399
+ if (player.client.stats) {
26400
+ for (let i = 0; i < player.client.stats.length; i++) {
26401
+ if (player.client.stats[i] !== 0) {
26402
+ statArray[i] = player.client.stats[i];
26403
+ }
26404
+ }
26405
+ }
26100
26406
  return statArray;
26101
26407
  }
26102
26408