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
@@ -811,36 +811,36 @@ var MAX_ITEMS = 256;
811
811
  var MAX_GENERAL = MAX_CLIENTS * 2;
812
812
  var MAX_SHADOW_LIGHTS = 256;
813
813
  var MAX_WHEEL_ITEMS = 32;
814
- var ConfigStringIndex = ((ConfigStringIndex2) => {
815
- ConfigStringIndex2[ConfigStringIndex2["Name"] = 0] = "Name";
816
- ConfigStringIndex2[ConfigStringIndex2["CdTrack"] = 1] = "CdTrack";
817
- ConfigStringIndex2[ConfigStringIndex2["Sky"] = 2] = "Sky";
818
- ConfigStringIndex2[ConfigStringIndex2["SkyAxis"] = 3] = "SkyAxis";
819
- ConfigStringIndex2[ConfigStringIndex2["SkyRotate"] = 4] = "SkyRotate";
820
- ConfigStringIndex2[ConfigStringIndex2["StatusBar"] = 5] = "StatusBar";
821
- ConfigStringIndex2[ConfigStringIndex2["HealthBarName"] = 55] = "HealthBarName";
822
- ConfigStringIndex2[ConfigStringIndex2["CONFIG_N64_PHYSICS"] = 56] = "CONFIG_N64_PHYSICS";
823
- ConfigStringIndex2[ConfigStringIndex2["CONFIG_CTF_TEAMS"] = 57] = "CONFIG_CTF_TEAMS";
824
- ConfigStringIndex2[ConfigStringIndex2["CONFIG_COOP_RESPAWN_STRING"] = 58] = "CONFIG_COOP_RESPAWN_STRING";
825
- ConfigStringIndex2[ConfigStringIndex2["Story"] = 54] = "Story";
826
- ConfigStringIndex2[ConfigStringIndex2["AirAccel"] = 59] = "AirAccel";
827
- ConfigStringIndex2[ConfigStringIndex2["MaxClients"] = 60] = "MaxClients";
828
- ConfigStringIndex2[ConfigStringIndex2["MapChecksum"] = 61] = "MapChecksum";
829
- ConfigStringIndex2[ConfigStringIndex2["Models"] = 62] = "Models";
830
- ConfigStringIndex2[ConfigStringIndex2["Sounds"] = 62 + MAX_MODELS] = "Sounds";
831
- ConfigStringIndex2[ConfigStringIndex2["Images"] = ConfigStringIndex2.Sounds + MAX_SOUNDS] = "Images";
832
- ConfigStringIndex2[ConfigStringIndex2["Lights"] = ConfigStringIndex2.Images + MAX_IMAGES] = "Lights";
833
- ConfigStringIndex2[ConfigStringIndex2["ShadowLights"] = ConfigStringIndex2.Lights + MAX_LIGHTSTYLES] = "ShadowLights";
834
- ConfigStringIndex2[ConfigStringIndex2["Items"] = ConfigStringIndex2.ShadowLights + MAX_SHADOW_LIGHTS] = "Items";
835
- ConfigStringIndex2[ConfigStringIndex2["PlayerSkins"] = ConfigStringIndex2.Items + MAX_ITEMS] = "PlayerSkins";
836
- ConfigStringIndex2[ConfigStringIndex2["General"] = ConfigStringIndex2.PlayerSkins + MAX_CLIENTS] = "General";
837
- ConfigStringIndex2[ConfigStringIndex2["WheelWeapons"] = ConfigStringIndex2.General + MAX_GENERAL] = "WheelWeapons";
838
- ConfigStringIndex2[ConfigStringIndex2["WheelAmmo"] = ConfigStringIndex2.WheelWeapons + MAX_WHEEL_ITEMS] = "WheelAmmo";
839
- ConfigStringIndex2[ConfigStringIndex2["WheelPowerups"] = ConfigStringIndex2.WheelAmmo + MAX_WHEEL_ITEMS] = "WheelPowerups";
840
- ConfigStringIndex2[ConfigStringIndex2["CdLoopCount"] = ConfigStringIndex2.WheelPowerups + MAX_WHEEL_ITEMS] = "CdLoopCount";
841
- ConfigStringIndex2[ConfigStringIndex2["GameStyle"] = ConfigStringIndex2.CdLoopCount + 1] = "GameStyle";
842
- ConfigStringIndex2[ConfigStringIndex2["MaxConfigStrings"] = ConfigStringIndex2.GameStyle + 1] = "MaxConfigStrings";
843
- return ConfigStringIndex2;
814
+ var ConfigStringIndex = ((ConfigStringIndex22) => {
815
+ ConfigStringIndex22[ConfigStringIndex22["Name"] = 0] = "Name";
816
+ ConfigStringIndex22[ConfigStringIndex22["CdTrack"] = 1] = "CdTrack";
817
+ ConfigStringIndex22[ConfigStringIndex22["Sky"] = 2] = "Sky";
818
+ ConfigStringIndex22[ConfigStringIndex22["SkyAxis"] = 3] = "SkyAxis";
819
+ ConfigStringIndex22[ConfigStringIndex22["SkyRotate"] = 4] = "SkyRotate";
820
+ ConfigStringIndex22[ConfigStringIndex22["StatusBar"] = 5] = "StatusBar";
821
+ ConfigStringIndex22[ConfigStringIndex22["HealthBarName"] = 55] = "HealthBarName";
822
+ ConfigStringIndex22[ConfigStringIndex22["CONFIG_N64_PHYSICS"] = 56] = "CONFIG_N64_PHYSICS";
823
+ ConfigStringIndex22[ConfigStringIndex22["CONFIG_CTF_TEAMS"] = 57] = "CONFIG_CTF_TEAMS";
824
+ ConfigStringIndex22[ConfigStringIndex22["CONFIG_COOP_RESPAWN_STRING"] = 58] = "CONFIG_COOP_RESPAWN_STRING";
825
+ ConfigStringIndex22[ConfigStringIndex22["Story"] = 54] = "Story";
826
+ ConfigStringIndex22[ConfigStringIndex22["AirAccel"] = 59] = "AirAccel";
827
+ ConfigStringIndex22[ConfigStringIndex22["MaxClients"] = 60] = "MaxClients";
828
+ ConfigStringIndex22[ConfigStringIndex22["MapChecksum"] = 61] = "MapChecksum";
829
+ ConfigStringIndex22[ConfigStringIndex22["Models"] = 62] = "Models";
830
+ ConfigStringIndex22[ConfigStringIndex22["Sounds"] = 62 + MAX_MODELS] = "Sounds";
831
+ ConfigStringIndex22[ConfigStringIndex22["Images"] = ConfigStringIndex22.Sounds + MAX_SOUNDS] = "Images";
832
+ ConfigStringIndex22[ConfigStringIndex22["Lights"] = ConfigStringIndex22.Images + MAX_IMAGES] = "Lights";
833
+ ConfigStringIndex22[ConfigStringIndex22["ShadowLights"] = ConfigStringIndex22.Lights + MAX_LIGHTSTYLES] = "ShadowLights";
834
+ ConfigStringIndex22[ConfigStringIndex22["Items"] = ConfigStringIndex22.ShadowLights + MAX_SHADOW_LIGHTS] = "Items";
835
+ ConfigStringIndex22[ConfigStringIndex22["PlayerSkins"] = ConfigStringIndex22.Items + MAX_ITEMS] = "PlayerSkins";
836
+ ConfigStringIndex22[ConfigStringIndex22["General"] = ConfigStringIndex22.PlayerSkins + MAX_CLIENTS] = "General";
837
+ ConfigStringIndex22[ConfigStringIndex22["WheelWeapons"] = ConfigStringIndex22.General + MAX_GENERAL] = "WheelWeapons";
838
+ ConfigStringIndex22[ConfigStringIndex22["WheelAmmo"] = ConfigStringIndex22.WheelWeapons + MAX_WHEEL_ITEMS] = "WheelAmmo";
839
+ ConfigStringIndex22[ConfigStringIndex22["WheelPowerups"] = ConfigStringIndex22.WheelAmmo + MAX_WHEEL_ITEMS] = "WheelPowerups";
840
+ ConfigStringIndex22[ConfigStringIndex22["CdLoopCount"] = ConfigStringIndex22.WheelPowerups + MAX_WHEEL_ITEMS] = "CdLoopCount";
841
+ ConfigStringIndex22[ConfigStringIndex22["GameStyle"] = ConfigStringIndex22.CdLoopCount + 1] = "GameStyle";
842
+ ConfigStringIndex22[ConfigStringIndex22["MaxConfigStrings"] = ConfigStringIndex22.GameStyle + 1] = "MaxConfigStrings";
843
+ return ConfigStringIndex22;
844
844
  })(ConfigStringIndex || {});
845
845
  var MAX_CONFIGSTRINGS = ConfigStringIndex.MaxConfigStrings;
846
846
  var replay_exports = {};
@@ -5327,6 +5327,9 @@ var EntitySystem = class {
5327
5327
  soundIndex(sound) {
5328
5328
  return this.engine.soundIndex?.(sound) || 0;
5329
5329
  }
5330
+ configStringIndex(str) {
5331
+ return this.engine.configStringIndex?.(str) || 0;
5332
+ }
5330
5333
  modelIndex(model) {
5331
5334
  return this.engine.modelIndex?.(model) || 0;
5332
5335
  }
@@ -5339,8 +5342,8 @@ var EntitySystem = class {
5339
5342
  unlink(ent) {
5340
5343
  this.spatialGrid.remove(ent);
5341
5344
  }
5342
- multicast(origin, type, ServerCommand6, ...args) {
5343
- this.imports.multicast(origin, type, ServerCommand6, ...args);
5345
+ multicast(origin, type, ServerCommand7, ...args) {
5346
+ this.imports.multicast(origin, type, ServerCommand7, ...args);
5344
5347
  }
5345
5348
  unicast(ent, reliable, event, ...args) {
5346
5349
  this.imports.unicast(ent, reliable, event, ...args);
@@ -6081,6 +6084,19 @@ function createTrap(context, owner, start, dir, speed) {
6081
6084
  // src/entities/projectiles.ts
6082
6085
  var BFG_LASER_RADIUS = 256;
6083
6086
  var BFG_LASER_RANGE = 2048;
6087
+ function createProjectile(sys, start, dir, speed, mod, damage, radiusDamage) {
6088
+ const proj = sys.spawn();
6089
+ proj.movetype = 8 /* FlyMissile */;
6090
+ proj.solid = 2 /* BoundingBox */;
6091
+ proj.origin = { ...start };
6092
+ proj.velocity = { x: dir.x * speed, y: dir.y * speed, z: dir.z * speed };
6093
+ proj.mins = { x: -4, y: -4, z: -4 };
6094
+ proj.maxs = { x: 4, y: 4, z: 4 };
6095
+ proj.angles = vectorToAngles(dir);
6096
+ proj.takedamage = false;
6097
+ sys.finalizeSpawn(proj);
6098
+ return proj;
6099
+ }
6084
6100
  function createRocket(sys, owner, start, dir, damage, radiusDamage, speed, flashtype = 0) {
6085
6101
  const rocket = sys.spawn();
6086
6102
  rocket.classname = "rocket";
@@ -7932,170 +7948,7 @@ function registerTargetSpawns(registry) {
7932
7948
  });
7933
7949
  }
7934
7950
 
7935
- // src/entities/triggers/fog.ts
7936
- function clamp2(value, min, max) {
7937
- return Math.min(Math.max(value, min), max);
7938
- }
7939
- function lerp(start, end, t) {
7940
- return start + (end - start) * t;
7941
- }
7942
- var SPAWNFLAG_FOG_AFFECT_FOG = 1;
7943
- var SPAWNFLAG_FOG_INSTANTANEOUS = 4;
7944
- var SPAWNFLAG_FOG_FORCE = 8;
7945
- var SPAWNFLAG_FOG_BLEND = 16;
7946
- function trigger_fog_touch(self, other, context) {
7947
- if (!other || !other.client) return;
7948
- if (self.timestamp > context.timeSeconds) return;
7949
- self.timestamp = context.timeSeconds + (self.wait || 0);
7950
- let fog_value_storage = self;
7951
- if (self.movetarget) {
7952
- fog_value_storage = self.movetarget;
7953
- }
7954
- if (self.spawnflags & SPAWNFLAG_FOG_INSTANTANEOUS) {
7955
- if (other.client.pers) other.client.pers.fog_transition_time = 0;
7956
- } else {
7957
- if (other.client.pers) other.client.pers.fog_transition_time = fog_value_storage.delay || 0.5;
7958
- }
7959
- if (self.spawnflags & SPAWNFLAG_FOG_BLEND) {
7960
- const center = scaleVec3(addVec3(self.absmin, self.absmax), 0.5);
7961
- const half_size = addVec3(scaleVec3(self.size, 0.5), scaleVec3(other.size, 0.5));
7962
- const start = {
7963
- x: -self.movedir.x * half_size.x,
7964
- y: -self.movedir.y * half_size.y,
7965
- z: -self.movedir.z * half_size.z
7966
- };
7967
- const end = {
7968
- x: self.movedir.x * half_size.x,
7969
- y: self.movedir.y * half_size.y,
7970
- z: self.movedir.z * half_size.z
7971
- };
7972
- const delta = subtractVec3(other.origin, center);
7973
- const absMovedir = {
7974
- x: Math.abs(self.movedir.x),
7975
- y: Math.abs(self.movedir.y),
7976
- z: Math.abs(self.movedir.z)
7977
- };
7978
- const player_dist = {
7979
- x: delta.x * absMovedir.x,
7980
- y: delta.y * absMovedir.y,
7981
- z: delta.z * absMovedir.z
7982
- };
7983
- let dist = lengthVec3(subtractVec3(player_dist, start));
7984
- dist /= lengthVec3(subtractVec3(start, end));
7985
- dist = clamp2(dist, 0, 1);
7986
- if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
7987
- const storage2 = fog_value_storage;
7988
- if (other.client.pers) {
7989
- other.client.pers.wanted_fog = {
7990
- density: lerp(storage2.fog_density_off || 0, storage2.fog_density || 0, dist),
7991
- r: lerp(storage2.fog_color_off?.[0] || 0, storage2.fog_color?.[0] || 0, dist),
7992
- g: lerp(storage2.fog_color_off?.[1] || 0, storage2.fog_color?.[1] || 0, dist),
7993
- b: lerp(storage2.fog_color_off?.[2] || 0, storage2.fog_color?.[2] || 0, dist),
7994
- sky_factor: lerp(storage2.fog_sky_factor_off || 0, storage2.fog_sky_factor || 0, dist)
7995
- };
7996
- }
7997
- }
7998
- return;
7999
- }
8000
- let use_on = true;
8001
- if (!(self.spawnflags & SPAWNFLAG_FOG_FORCE)) {
8002
- const len = lengthVec3(other.velocity);
8003
- if (len <= 1e-4) return;
8004
- const forward = normalizeVec3(other.velocity);
8005
- const dot = forward.x * self.movedir.x + forward.y * self.movedir.y + forward.z * self.movedir.z;
8006
- use_on = dot > 0;
8007
- }
8008
- const storage = fog_value_storage;
8009
- if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
8010
- if (other.client.pers) {
8011
- if (use_on) {
8012
- other.client.pers.wanted_fog = {
8013
- density: storage.fog_density || 0,
8014
- r: storage.fog_color?.[0] || 0,
8015
- g: storage.fog_color?.[1] || 0,
8016
- b: storage.fog_color?.[2] || 0,
8017
- sky_factor: storage.fog_sky_factor || 0
8018
- };
8019
- } else {
8020
- other.client.pers.wanted_fog = {
8021
- density: storage.fog_density_off || 0,
8022
- r: storage.fog_color_off?.[0] || 0,
8023
- g: storage.fog_color_off?.[1] || 0,
8024
- b: storage.fog_color_off?.[2] || 0,
8025
- sky_factor: storage.fog_sky_factor_off || 0
8026
- };
8027
- }
8028
- }
8029
- }
8030
- }
8031
- function registerTriggerFog(registry) {
8032
- registry.register("trigger_fog", (entity, context) => {
8033
- entity.solid = 1 /* Trigger */;
8034
- entity.movetype = 0 /* None */;
8035
- entity.svflags |= 1 /* NoClient */;
8036
- const entAny = entity;
8037
- const kv = context.keyValues;
8038
- entAny.fog_density = parseFloat(kv.fog_density) || 0;
8039
- entAny.fog_color = kv.fog_color ? kv.fog_color.split(" ").map(parseFloat) : [0, 0, 0];
8040
- entAny.fog_sky_factor = parseFloat(kv.fog_sky_factor) || 0;
8041
- entAny.fog_density_off = parseFloat(kv.fog_density_off) || 0;
8042
- entAny.fog_color_off = kv.fog_color_off ? kv.fog_color_off.split(" ").map(parseFloat) : [0, 0, 0];
8043
- entAny.fog_sky_factor_off = parseFloat(kv.fog_sky_factor_off) || 0;
8044
- if (entity.target) {
8045
- entity.movetype = 0 /* None */;
8046
- entity.movetarget = null;
8047
- }
8048
- if (!entity.delay) entity.delay = 0.5;
8049
- entity.touch = (self, other) => trigger_fog_touch(self, other, context.entities);
8050
- });
8051
- }
8052
-
8053
- // src/entities/triggers/flashlight.ts
8054
- var registerTriggerFlashlight = (registry) => {
8055
- registry.register("trigger_flashlight", (self, context) => {
8056
- self.solid = 1 /* Trigger */;
8057
- self.movetype = 0;
8058
- self.svflags |= 1 /* NoClient */;
8059
- self.touch = (self2, other, plane, surf) => {
8060
- if (!other || !other.client) return;
8061
- const style = self2.style || 0;
8062
- let sound = "";
8063
- if (style === 0) {
8064
- if (other.flags & 4194304 /* Flashlight */) {
8065
- other.flags &= ~4194304 /* Flashlight */;
8066
- sound = "items/flashlight_off.wav";
8067
- } else {
8068
- other.flags |= 4194304 /* Flashlight */;
8069
- sound = "items/flashlight_on.wav";
8070
- }
8071
- } else if (style === 1) {
8072
- if (!(other.flags & 4194304 /* Flashlight */)) {
8073
- other.flags |= 4194304 /* Flashlight */;
8074
- sound = "items/flashlight_on.wav";
8075
- }
8076
- } else if (style === 2) {
8077
- if (other.flags & 4194304 /* Flashlight */) {
8078
- other.flags &= ~4194304 /* Flashlight */;
8079
- sound = "items/flashlight_off.wav";
8080
- }
8081
- }
8082
- if (other.flags & 4194304 /* Flashlight */) {
8083
- other.renderfx |= RenderFx.Flashlight;
8084
- } else {
8085
- other.renderfx &= ~RenderFx.Flashlight;
8086
- }
8087
- if (sound) {
8088
- context.entities.sound(other, 0, sound, 1, 3, 0);
8089
- }
8090
- };
8091
- context.entities.linkentity(self);
8092
- });
8093
- };
8094
-
8095
- // src/entities/triggers.ts
8096
- var FRAME_TIME_SECONDS = 1 / 40;
8097
- var THINK_INTERVAL = 0.1;
8098
- var HURT_INTERVAL = 0.1;
7951
+ // src/entities/triggers/common.ts
8099
7952
  var TRIGGER_SPAWNFLAGS = {
8100
7953
  Monster: 1 << 0,
8101
7954
  NotPlayer: 1 << 1,
@@ -8104,50 +7957,7 @@ var TRIGGER_SPAWNFLAGS = {
8104
7957
  Latched: 1 << 4,
8105
7958
  Clip: 1 << 5
8106
7959
  };
8107
- var RELAY_SPAWNFLAGS = {
8108
- NoSound: 1 << 0
8109
- };
8110
- var COUNTER_SPAWNFLAGS = {
8111
- NoMessage: 1 << 0
8112
- };
8113
- var PUSH_SPAWNFLAGS = {
8114
- Once: 1 << 0,
8115
- Plus: 1 << 1,
8116
- Silent: 1 << 2,
8117
- StartOff: 1 << 3,
8118
- Clip: 1 << 4
8119
- };
8120
- var HURT_SPAWNFLAGS = {
8121
- StartOff: 1 << 0,
8122
- Toggle: 1 << 1,
8123
- Silent: 1 << 2,
8124
- NoProtection: 1 << 3,
8125
- Slow: 1 << 4,
8126
- NoPlayers: 1 << 5,
8127
- NoMonsters: 1 << 6,
8128
- Clip: 1 << 7
8129
- };
8130
- var TELEPORT_SPAWNFLAGS = {
8131
- StartOn: 1 << 3
8132
- };
8133
- var GRAVITY_SPAWNFLAGS = {
8134
- Toggle: 1 << 0,
8135
- StartOff: 1 << 1,
8136
- Clip: 1 << 2
8137
- };
8138
- var MONSTERJUMP_SPAWNFLAGS = {
8139
- Toggle: 1 << 0,
8140
- StartOff: 1 << 1,
8141
- Clip: 1 << 2
8142
- };
8143
- function trainResume(train, entities) {
8144
- if (!train.think) {
8145
- train.think = (self) => {
8146
- self.nextthink = 0;
8147
- };
8148
- }
8149
- entities.scheduleThink(train, entities.timeSeconds + FRAME_TIME_SECONDS);
8150
- }
7960
+ var FRAME_TIME_SECONDS = 1 / 40;
8151
7961
  function initTrigger(entity) {
8152
7962
  entity.movetype = 0 /* None */;
8153
7963
  entity.solid = 1 /* Trigger */;
@@ -8155,10 +7965,6 @@ function initTrigger(entity) {
8155
7965
  entity.movedir = setMovedir(entity.angles);
8156
7966
  entity.angles = { x: 0, y: 0, z: 0 };
8157
7967
  }
8158
- function multiWait(self) {
8159
- self.nextthink = 0;
8160
- self.think = void 0;
8161
- }
8162
7968
  function canActivate(trigger, other) {
8163
7969
  if (trigger.solid === 0 /* Not */) {
8164
7970
  return false;
@@ -8183,6 +7989,15 @@ function canActivate(trigger, other) {
8183
7989
  }
8184
7990
  return true;
8185
7991
  }
7992
+ function triggerEnable(self) {
7993
+ self.solid = 1 /* Trigger */;
7994
+ }
7995
+
7996
+ // src/entities/triggers/multiple.ts
7997
+ function multiWait(self) {
7998
+ self.nextthink = 0;
7999
+ self.think = void 0;
8000
+ }
8186
8001
  function multiTrigger(self, entities) {
8187
8002
  if (self.nextthink > entities.timeSeconds) {
8188
8003
  return;
@@ -8241,9 +8056,6 @@ function useMulti(self, _other, activator, entities) {
8241
8056
  self.activator = activator;
8242
8057
  multiTrigger(self, entities);
8243
8058
  }
8244
- function triggerEnable(self) {
8245
- self.solid = 1 /* Trigger */;
8246
- }
8247
8059
  function registerTriggerMultiple(registry) {
8248
8060
  registry.register("trigger_multiple", (entity, context) => {
8249
8061
  initTrigger(entity);
@@ -8269,12 +8081,23 @@ function registerTriggerMultiple(registry) {
8269
8081
  entity.touch = (self, other) => touchMulti(self, other, context.entities);
8270
8082
  });
8271
8083
  }
8084
+
8085
+ // src/entities/triggers/once.ts
8272
8086
  function registerTriggerOnce(registry) {
8273
8087
  registry.register("trigger_once", (entity, context) => {
8274
8088
  entity.wait = -1;
8275
- registry.get("trigger_multiple")?.(entity, context);
8089
+ const multipleSpawn = registry.get("trigger_multiple");
8090
+ if (multipleSpawn) {
8091
+ multipleSpawn(entity, context);
8092
+ } else {
8093
+ }
8276
8094
  });
8277
8095
  }
8096
+
8097
+ // src/entities/triggers/relay.ts
8098
+ var RELAY_SPAWNFLAGS = {
8099
+ NoSound: 1 << 0
8100
+ };
8278
8101
  function registerTriggerRelay(registry) {
8279
8102
  registry.register("trigger_relay", (entity, context) => {
8280
8103
  if (entity.spawnflags & RELAY_SPAWNFLAGS.NoSound) {
@@ -8291,6 +8114,8 @@ function registerTriggerRelay(registry) {
8291
8114
  };
8292
8115
  });
8293
8116
  }
8117
+
8118
+ // src/entities/triggers/always.ts
8294
8119
  function registerTriggerAlways(registry) {
8295
8120
  registry.register("trigger_always", (entity, context) => {
8296
8121
  if (entity.delay === 0) {
@@ -8299,14 +8124,27 @@ function registerTriggerAlways(registry) {
8299
8124
  context.entities.useTargets(entity, entity);
8300
8125
  });
8301
8126
  }
8127
+
8128
+ // src/entities/triggers/counter.ts
8129
+ var COUNTER_SPAWNFLAGS = {
8130
+ NoMessage: 1 << 0
8131
+ };
8302
8132
  function counterUse(self, _other, activator, entities) {
8303
8133
  if (self.count === 0) {
8304
8134
  return;
8305
8135
  }
8306
8136
  self.count -= 1;
8307
8137
  if (self.count > 0) {
8138
+ if (!(self.spawnflags & COUNTER_SPAWNFLAGS.NoMessage) && activator && activator.client) {
8139
+ entities.engine.centerprintf?.(activator, `${self.count} more to go...`);
8140
+ entities.sound(activator, 0, "misc/talk1.wav", 1, 1, 0);
8141
+ }
8308
8142
  return;
8309
8143
  }
8144
+ if (!(self.spawnflags & COUNTER_SPAWNFLAGS.NoMessage) && activator && activator.client) {
8145
+ entities.engine.centerprintf?.(activator, "Sequence completed!");
8146
+ entities.sound(activator, 0, "misc/talk1.wav", 1, 1, 0);
8147
+ }
8310
8148
  self.activator = activator;
8311
8149
  multiTrigger(self, entities);
8312
8150
  }
@@ -8322,6 +8160,8 @@ function registerTriggerCounter(registry) {
8322
8160
  }
8323
8161
  });
8324
8162
  }
8163
+
8164
+ // src/entities/triggers/key.ts
8325
8165
  function triggerKeyUse(self, activator, entities, warn) {
8326
8166
  if (!self.item || !activator) {
8327
8167
  return;
@@ -8332,13 +8172,17 @@ function triggerKeyUse(self, activator, entities, warn) {
8332
8172
  return;
8333
8173
  }
8334
8174
  self.timestamp = entities.timeSeconds + 5;
8335
- warn(`Missing required key item: ${self.item}`);
8175
+ if (activator.client) {
8176
+ entities.engine.centerprintf?.(activator, `You need the ${self.item}`);
8177
+ entities.sound(activator, 0, "misc/keytry.wav", 1, 1, 0);
8178
+ }
8336
8179
  return;
8337
8180
  }
8338
8181
  activator.inventory[self.item] = available - 1;
8339
8182
  if (activator.inventory[self.item] <= 0) {
8340
8183
  delete activator.inventory[self.item];
8341
8184
  }
8185
+ entities.sound(activator, 0, "misc/keyuse.wav", 1, 1, 0);
8342
8186
  entities.useTargets(self, activator);
8343
8187
  self.use = void 0;
8344
8188
  }
@@ -8354,6 +8198,16 @@ function registerTriggerKey(registry) {
8354
8198
  entity.use = (self, other, activator) => triggerKeyUse(self, activator ?? other, context.entities, context.warn);
8355
8199
  });
8356
8200
  }
8201
+
8202
+ // src/entities/triggers/push.ts
8203
+ var PUSH_SPAWNFLAGS = {
8204
+ Once: 1 << 0,
8205
+ Plus: 1 << 1,
8206
+ Silent: 1 << 2,
8207
+ StartOff: 1 << 3,
8208
+ Clip: 1 << 4
8209
+ };
8210
+ var THINK_INTERVAL = 0.1;
8357
8211
  function triggerPushTouch(self, other, entities) {
8358
8212
  if (!other) {
8359
8213
  return;
@@ -8365,6 +8219,12 @@ function triggerPushTouch(self, other, entities) {
8365
8219
  y: self.movedir.y * scale,
8366
8220
  z: self.movedir.z * scale
8367
8221
  };
8222
+ if (!(self.spawnflags & PUSH_SPAWNFLAGS.Silent)) {
8223
+ if (!other.fly_sound_debounce_time || other.fly_sound_debounce_time < entities.timeSeconds) {
8224
+ other.fly_sound_debounce_time = entities.timeSeconds + 1.5;
8225
+ entities.sound(other, 0, "misc/windfly.wav", 1, 1, 0);
8226
+ }
8227
+ }
8368
8228
  }
8369
8229
  if (self.spawnflags & PUSH_SPAWNFLAGS.Once) {
8370
8230
  entities.free(self);
@@ -8426,10 +8286,23 @@ function registerTriggerPush(registry) {
8426
8286
  }
8427
8287
  });
8428
8288
  }
8429
- function hurtTouch(self, other, entities) {
8430
- if (!other) {
8431
- return;
8432
- }
8289
+
8290
+ // src/entities/triggers/hurt.ts
8291
+ var HURT_SPAWNFLAGS = {
8292
+ StartOff: 1 << 0,
8293
+ Toggle: 1 << 1,
8294
+ Silent: 1 << 2,
8295
+ NoProtection: 1 << 3,
8296
+ Slow: 1 << 4,
8297
+ NoPlayers: 1 << 5,
8298
+ NoMonsters: 1 << 6,
8299
+ Clip: 1 << 7
8300
+ };
8301
+ var HURT_INTERVAL = 0.1;
8302
+ function hurtTouch(self, other, entities) {
8303
+ if (!other) {
8304
+ return;
8305
+ }
8433
8306
  if (!other.takedamage && other.classname !== "grenade") {
8434
8307
  return;
8435
8308
  }
@@ -8446,6 +8319,9 @@ function hurtTouch(self, other, entities) {
8446
8319
  const damage = self.dmg || 5;
8447
8320
  other.health -= damage;
8448
8321
  }
8322
+ function toggleSolid2(self) {
8323
+ self.solid = self.solid === 0 /* Not */ ? 1 /* Trigger */ : 0 /* Not */;
8324
+ }
8449
8325
  function registerTriggerHurt(registry) {
8450
8326
  registry.register("trigger_hurt", (entity, context) => {
8451
8327
  initTrigger(entity);
@@ -8458,178 +8334,475 @@ function registerTriggerHurt(registry) {
8458
8334
  }
8459
8335
  if (entity.spawnflags & HURT_SPAWNFLAGS.Toggle) {
8460
8336
  entity.use = (self) => {
8461
- toggleSolid(self);
8337
+ toggleSolid2(self);
8338
+ self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8339
+ };
8340
+ }
8341
+ });
8342
+ }
8343
+
8344
+ // src/entities/triggers/teleport.ts
8345
+ var TELEPORT_SPAWNFLAGS = {
8346
+ StartOn: 1 << 3
8347
+ };
8348
+ function teleportTouch(self, other, entities, warn) {
8349
+ if (!other) {
8350
+ return;
8351
+ }
8352
+ if (self.delay > 0) {
8353
+ return;
8354
+ }
8355
+ const destination = entities.pickTarget(self.target);
8356
+ if (!destination) {
8357
+ warn("trigger_teleport target not found");
8358
+ return;
8359
+ }
8360
+ const destOrigin = {
8361
+ x: destination.origin.x,
8362
+ y: destination.origin.y,
8363
+ z: destination.origin.z + 10
8364
+ };
8365
+ other.origin = { ...destOrigin };
8366
+ other.old_origin = { ...destOrigin };
8367
+ other.velocity = { x: 0, y: 0, z: 0 };
8368
+ other.groundentity = null;
8369
+ other.angles = { ...destination.angles };
8370
+ entities.killBox(other);
8371
+ }
8372
+ function registerTriggerTeleport(registry) {
8373
+ registry.register("trigger_teleport", (entity, context) => {
8374
+ if (!entity.wait) {
8375
+ entity.wait = 0.2;
8376
+ }
8377
+ initTrigger(entity);
8378
+ if (entity.targetname) {
8379
+ entity.use = (self) => {
8380
+ self.delay = self.delay > 0 ? 0 : 1;
8381
+ };
8382
+ if ((entity.spawnflags & TELEPORT_SPAWNFLAGS.StartOn) === 0) {
8383
+ entity.delay = 1;
8384
+ }
8385
+ }
8386
+ entity.touch = (self, other) => teleportTouch(self, other, context.entities, context.warn);
8387
+ });
8388
+ }
8389
+
8390
+ // src/entities/triggers/gravity.ts
8391
+ var GRAVITY_SPAWNFLAGS = {
8392
+ Toggle: 1 << 0,
8393
+ StartOff: 1 << 1,
8394
+ Clip: 1 << 2
8395
+ };
8396
+ function gravityTouch(self, other) {
8397
+ if (!other) {
8398
+ return;
8399
+ }
8400
+ if (self.spawnflags & GRAVITY_SPAWNFLAGS.Clip) {
8401
+ }
8402
+ other.gravity = self.gravity;
8403
+ }
8404
+ function toggleSolid3(self) {
8405
+ self.solid = self.solid === 0 /* Not */ ? 1 /* Trigger */ : 0 /* Not */;
8406
+ }
8407
+ function registerTriggerGravity(registry) {
8408
+ registry.register("trigger_gravity", (entity, context) => {
8409
+ const gravityText = context.keyValues.gravity;
8410
+ if (!gravityText) {
8411
+ context.warn("trigger_gravity requires a gravity value");
8412
+ context.free(entity);
8413
+ return;
8414
+ }
8415
+ initTrigger(entity);
8416
+ entity.gravity = Number.parseFloat(gravityText) || 0;
8417
+ const touchHandler = (self, other) => gravityTouch(self, other);
8418
+ entity.touch = touchHandler;
8419
+ if (entity.spawnflags & GRAVITY_SPAWNFLAGS.StartOff) {
8420
+ entity.solid = 0 /* Not */;
8421
+ entity.touch = void 0;
8422
+ }
8423
+ if (entity.spawnflags & GRAVITY_SPAWNFLAGS.Toggle) {
8424
+ entity.use = (self) => {
8425
+ toggleSolid3(self);
8462
8426
  self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8463
8427
  };
8464
8428
  }
8465
8429
  });
8466
- }
8467
- function teleportTouch(self, other, entities, warn) {
8468
- if (!other) {
8430
+ }
8431
+
8432
+ // src/entities/triggers/elevator.ts
8433
+ function trainResume(train, entities) {
8434
+ if (!train.think) {
8435
+ train.think = (self) => {
8436
+ self.nextthink = 0;
8437
+ };
8438
+ }
8439
+ entities.scheduleThink(train, entities.timeSeconds + FRAME_TIME_SECONDS);
8440
+ }
8441
+ function triggerElevatorUse(self, other, entities, warn) {
8442
+ if (!self.movetarget) {
8443
+ return;
8444
+ }
8445
+ if (self.movetarget.nextthink > 0) {
8446
+ return;
8447
+ }
8448
+ if (!other?.pathtarget) {
8449
+ warn("trigger_elevator used with no pathtarget");
8450
+ return;
8451
+ }
8452
+ const target = entities.pickTarget(other.pathtarget);
8453
+ if (!target) {
8454
+ warn(`trigger_elevator used with bad pathtarget: ${other.pathtarget}`);
8455
+ return;
8456
+ }
8457
+ self.movetarget.target_ent = target;
8458
+ trainResume(self.movetarget, entities);
8459
+ }
8460
+ function triggerElevatorInit(self, entities, warn) {
8461
+ if (!self.target) {
8462
+ warn("trigger_elevator has no target");
8463
+ return;
8464
+ }
8465
+ const target = entities.pickTarget(self.target);
8466
+ if (!target) {
8467
+ warn(`trigger_elevator unable to find target ${self.target}`);
8468
+ return;
8469
+ }
8470
+ self.movetarget = target;
8471
+ if (target.classname !== "func_train") {
8472
+ warn(`trigger_elevator target ${self.target} is not a train`);
8473
+ return;
8474
+ }
8475
+ self.use = (entity, other, activator) => triggerElevatorUse(entity, other ?? activator ?? null, entities, warn);
8476
+ self.svflags |= 1 /* NoClient */;
8477
+ }
8478
+ function registerTriggerElevator(registry) {
8479
+ registry.register("trigger_elevator", (entity, context) => {
8480
+ entity.think = (self) => triggerElevatorInit(self, context.entities, context.warn);
8481
+ context.entities.scheduleThink(entity, context.entities.timeSeconds + FRAME_TIME_SECONDS);
8482
+ });
8483
+ }
8484
+
8485
+ // src/entities/triggers/monsterjump.ts
8486
+ var MONSTERJUMP_SPAWNFLAGS = {
8487
+ Toggle: 1 << 0,
8488
+ StartOff: 1 << 1,
8489
+ Clip: 1 << 2
8490
+ };
8491
+ function monsterJumpTouch(self, other) {
8492
+ if (!other) {
8493
+ return;
8494
+ }
8495
+ if ((other.flags & (1 /* Fly */ | 2 /* Swim */)) !== 0) {
8496
+ return;
8497
+ }
8498
+ if (other.svflags & 2 /* DeadMonster */) {
8499
+ return;
8500
+ }
8501
+ if ((other.svflags & 4 /* Monster */) === 0) {
8502
+ return;
8503
+ }
8504
+ other.velocity = {
8505
+ x: self.movedir.x * self.speed,
8506
+ y: self.movedir.y * self.speed,
8507
+ z: other.velocity.z
8508
+ };
8509
+ if (!other.groundentity) {
8510
+ return;
8511
+ }
8512
+ other.groundentity = null;
8513
+ other.velocity = { x: other.velocity.x, y: other.velocity.y, z: self.movedir.z };
8514
+ }
8515
+ function toggleSolid4(self) {
8516
+ self.solid = self.solid === 0 /* Not */ ? 1 /* Trigger */ : 0 /* Not */;
8517
+ }
8518
+ function registerTriggerMonsterJump(registry) {
8519
+ registry.register("trigger_monsterjump", (entity, context) => {
8520
+ const heightText = context.keyValues.height;
8521
+ const height = heightText ? Number.parseFloat(heightText) || 0 : 200;
8522
+ if (entity.angles.y === 0) {
8523
+ entity.angles = { ...entity.angles, y: 360 };
8524
+ }
8525
+ if (!entity.speed) {
8526
+ entity.speed = 200;
8527
+ }
8528
+ initTrigger(entity);
8529
+ entity.movedir = { ...entity.movedir, z: height };
8530
+ const touchHandler = (self, other) => monsterJumpTouch(self, other);
8531
+ entity.touch = touchHandler;
8532
+ if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.StartOff) {
8533
+ entity.solid = 0 /* Not */;
8534
+ entity.touch = void 0;
8535
+ }
8536
+ if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.Toggle) {
8537
+ entity.use = (self) => {
8538
+ toggleSolid4(self);
8539
+ self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8540
+ };
8541
+ }
8542
+ });
8543
+ }
8544
+
8545
+ // src/entities/triggers/fog.ts
8546
+ function clamp2(value, min, max) {
8547
+ return Math.min(Math.max(value, min), max);
8548
+ }
8549
+ function lerp(start, end, t) {
8550
+ return start + (end - start) * t;
8551
+ }
8552
+ var SPAWNFLAG_FOG_AFFECT_FOG = 1;
8553
+ var SPAWNFLAG_FOG_INSTANTANEOUS = 4;
8554
+ var SPAWNFLAG_FOG_FORCE = 8;
8555
+ var SPAWNFLAG_FOG_BLEND = 16;
8556
+ function trigger_fog_touch(self, other, context) {
8557
+ if (!other || !other.client) return;
8558
+ if (self.timestamp > context.timeSeconds) return;
8559
+ self.timestamp = context.timeSeconds + (self.wait || 0);
8560
+ let fog_value_storage = self;
8561
+ if (self.movetarget) {
8562
+ fog_value_storage = self.movetarget;
8563
+ }
8564
+ if (self.spawnflags & SPAWNFLAG_FOG_INSTANTANEOUS) {
8565
+ if (other.client.pers) other.client.pers.fog_transition_time = 0;
8566
+ } else {
8567
+ if (other.client.pers) other.client.pers.fog_transition_time = fog_value_storage.delay || 0.5;
8568
+ }
8569
+ if (self.spawnflags & SPAWNFLAG_FOG_BLEND) {
8570
+ const center = scaleVec3(addVec3(self.absmin, self.absmax), 0.5);
8571
+ const half_size = addVec3(scaleVec3(self.size, 0.5), scaleVec3(other.size, 0.5));
8572
+ const start = {
8573
+ x: -self.movedir.x * half_size.x,
8574
+ y: -self.movedir.y * half_size.y,
8575
+ z: -self.movedir.z * half_size.z
8576
+ };
8577
+ const end = {
8578
+ x: self.movedir.x * half_size.x,
8579
+ y: self.movedir.y * half_size.y,
8580
+ z: self.movedir.z * half_size.z
8581
+ };
8582
+ const delta = subtractVec3(other.origin, center);
8583
+ const absMovedir = {
8584
+ x: Math.abs(self.movedir.x),
8585
+ y: Math.abs(self.movedir.y),
8586
+ z: Math.abs(self.movedir.z)
8587
+ };
8588
+ const player_dist = {
8589
+ x: delta.x * absMovedir.x,
8590
+ y: delta.y * absMovedir.y,
8591
+ z: delta.z * absMovedir.z
8592
+ };
8593
+ let dist = lengthVec3(subtractVec3(player_dist, start));
8594
+ dist /= lengthVec3(subtractVec3(start, end));
8595
+ dist = clamp2(dist, 0, 1);
8596
+ if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
8597
+ const storage2 = fog_value_storage;
8598
+ if (other.client.pers) {
8599
+ other.client.pers.wanted_fog = {
8600
+ density: lerp(storage2.fog_density_off || 0, storage2.fog_density || 0, dist),
8601
+ r: lerp(storage2.fog_color_off?.[0] || 0, storage2.fog_color?.[0] || 0, dist),
8602
+ g: lerp(storage2.fog_color_off?.[1] || 0, storage2.fog_color?.[1] || 0, dist),
8603
+ b: lerp(storage2.fog_color_off?.[2] || 0, storage2.fog_color?.[2] || 0, dist),
8604
+ sky_factor: lerp(storage2.fog_sky_factor_off || 0, storage2.fog_sky_factor || 0, dist)
8605
+ };
8606
+ }
8607
+ }
8608
+ return;
8609
+ }
8610
+ let use_on = true;
8611
+ if (!(self.spawnflags & SPAWNFLAG_FOG_FORCE)) {
8612
+ const len = lengthVec3(other.velocity);
8613
+ if (len <= 1e-4) return;
8614
+ const forward = normalizeVec3(other.velocity);
8615
+ const dot = forward.x * self.movedir.x + forward.y * self.movedir.y + forward.z * self.movedir.z;
8616
+ use_on = dot > 0;
8617
+ }
8618
+ const storage = fog_value_storage;
8619
+ if (self.spawnflags & SPAWNFLAG_FOG_AFFECT_FOG) {
8620
+ if (other.client.pers) {
8621
+ if (use_on) {
8622
+ other.client.pers.wanted_fog = {
8623
+ density: storage.fog_density || 0,
8624
+ r: storage.fog_color?.[0] || 0,
8625
+ g: storage.fog_color?.[1] || 0,
8626
+ b: storage.fog_color?.[2] || 0,
8627
+ sky_factor: storage.fog_sky_factor || 0
8628
+ };
8629
+ } else {
8630
+ other.client.pers.wanted_fog = {
8631
+ density: storage.fog_density_off || 0,
8632
+ r: storage.fog_color_off?.[0] || 0,
8633
+ g: storage.fog_color_off?.[1] || 0,
8634
+ b: storage.fog_color_off?.[2] || 0,
8635
+ sky_factor: storage.fog_sky_factor_off || 0
8636
+ };
8637
+ }
8638
+ }
8639
+ }
8640
+ }
8641
+ function registerTriggerFog(registry) {
8642
+ registry.register("trigger_fog", (entity, context) => {
8643
+ entity.solid = 1 /* Trigger */;
8644
+ entity.movetype = 0 /* None */;
8645
+ entity.svflags |= 1 /* NoClient */;
8646
+ const entAny = entity;
8647
+ const kv = context.keyValues;
8648
+ entAny.fog_density = parseFloat(kv.fog_density) || 0;
8649
+ entAny.fog_color = kv.fog_color ? kv.fog_color.split(" ").map(parseFloat) : [0, 0, 0];
8650
+ entAny.fog_sky_factor = parseFloat(kv.fog_sky_factor) || 0;
8651
+ entAny.fog_density_off = parseFloat(kv.fog_density_off) || 0;
8652
+ entAny.fog_color_off = kv.fog_color_off ? kv.fog_color_off.split(" ").map(parseFloat) : [0, 0, 0];
8653
+ entAny.fog_sky_factor_off = parseFloat(kv.fog_sky_factor_off) || 0;
8654
+ if (entity.target) {
8655
+ entity.movetype = 0 /* None */;
8656
+ entity.movetarget = null;
8657
+ }
8658
+ if (!entity.delay) entity.delay = 0.5;
8659
+ entity.touch = (self, other) => trigger_fog_touch(self, other, context.entities);
8660
+ });
8661
+ }
8662
+
8663
+ // src/entities/triggers/flashlight.ts
8664
+ var registerTriggerFlashlight = (registry) => {
8665
+ registry.register("trigger_flashlight", (self, context) => {
8666
+ self.solid = 1 /* Trigger */;
8667
+ self.movetype = 0;
8668
+ self.svflags |= 1 /* NoClient */;
8669
+ self.touch = (self2, other, plane, surf) => {
8670
+ if (!other || !other.client) return;
8671
+ const style = self2.style || 0;
8672
+ let sound = "";
8673
+ if (style === 0) {
8674
+ if (other.flags & 4194304 /* Flashlight */) {
8675
+ other.flags &= ~4194304 /* Flashlight */;
8676
+ sound = "items/flashlight_off.wav";
8677
+ } else {
8678
+ other.flags |= 4194304 /* Flashlight */;
8679
+ sound = "items/flashlight_on.wav";
8680
+ }
8681
+ } else if (style === 1) {
8682
+ if (!(other.flags & 4194304 /* Flashlight */)) {
8683
+ other.flags |= 4194304 /* Flashlight */;
8684
+ sound = "items/flashlight_on.wav";
8685
+ }
8686
+ } else if (style === 2) {
8687
+ if (other.flags & 4194304 /* Flashlight */) {
8688
+ other.flags &= ~4194304 /* Flashlight */;
8689
+ sound = "items/flashlight_off.wav";
8690
+ }
8691
+ }
8692
+ if (other.flags & 4194304 /* Flashlight */) {
8693
+ other.renderfx |= RenderFx.Flashlight;
8694
+ } else {
8695
+ other.renderfx &= ~RenderFx.Flashlight;
8696
+ }
8697
+ if (sound) {
8698
+ context.entities.sound(other, 0, sound, 1, 3, 0);
8699
+ }
8700
+ };
8701
+ context.entities.linkentity(self);
8702
+ });
8703
+ };
8704
+
8705
+ // src/entities/triggers/secret.ts
8706
+ var SECRET_SPAWNFLAGS = {
8707
+ Once: 1 << 0
8708
+ };
8709
+ function triggerSecretTouch(self, other, entities) {
8710
+ if (!other || !other.client) {
8469
8711
  return;
8470
8712
  }
8471
- if (self.delay > 0) {
8713
+ if (self.timestamp > entities.timeSeconds) {
8472
8714
  return;
8473
8715
  }
8474
- const destination = entities.pickTarget(self.target);
8475
- if (!destination) {
8476
- warn("trigger_teleport target not found");
8477
- return;
8716
+ self.timestamp = entities.timeSeconds + 1;
8717
+ if (!self.message) {
8718
+ self.message = "You found a secret area!";
8478
8719
  }
8479
- const destOrigin = {
8480
- x: destination.origin.x,
8481
- y: destination.origin.y,
8482
- z: destination.origin.z + 10
8483
- };
8484
- other.origin = { ...destOrigin };
8485
- other.old_origin = { ...destOrigin };
8486
- other.velocity = { x: 0, y: 0, z: 0 };
8487
- other.groundentity = null;
8488
- other.angles = { ...destination.angles };
8489
- entities.killBox(other);
8490
- }
8491
- function registerTriggerTeleport(registry) {
8492
- registry.register("trigger_teleport", (entity, context) => {
8493
- if (!entity.wait) {
8494
- entity.wait = 0.2;
8495
- }
8496
- initTrigger(entity);
8497
- if (entity.targetname) {
8498
- entity.use = (self) => {
8499
- self.delay = self.delay > 0 ? 0 : 1;
8500
- };
8501
- if ((entity.spawnflags & TELEPORT_SPAWNFLAGS.StartOn) === 0) {
8502
- entity.delay = 1;
8503
- }
8504
- }
8505
- entity.touch = (self, other) => teleportTouch(self, other, context.entities, context.warn);
8506
- });
8507
- }
8508
- function gravityTouch(self, other) {
8509
- if (!other) {
8510
- return;
8720
+ if (other.client) {
8721
+ entities.engine.centerprintf?.(other, self.message);
8722
+ entities.sound(other, 0, "misc/secret.wav", 1, 1, 0);
8511
8723
  }
8512
- if (self.spawnflags & GRAVITY_SPAWNFLAGS.Clip) {
8724
+ if (self.spawnflags & SECRET_SPAWNFLAGS.Once) {
8725
+ entities.free(self);
8513
8726
  }
8514
- other.gravity = self.gravity;
8515
8727
  }
8516
- function registerTriggerGravity(registry) {
8517
- registry.register("trigger_gravity", (entity, context) => {
8518
- const gravityText = context.keyValues.gravity;
8519
- if (!gravityText) {
8520
- context.warn("trigger_gravity requires a gravity value");
8521
- context.free(entity);
8522
- return;
8523
- }
8524
- initTrigger(entity);
8525
- entity.gravity = Number.parseFloat(gravityText) || 0;
8526
- const touchHandler = (self, other) => gravityTouch(self, other);
8527
- entity.touch = touchHandler;
8528
- if (entity.spawnflags & GRAVITY_SPAWNFLAGS.StartOff) {
8728
+ function registerTriggerSecret(registry) {
8729
+ registry.register("trigger_secret", (entity, context) => {
8730
+ entity.movetype = 0 /* None */;
8731
+ entity.solid = 1 /* Trigger */;
8732
+ entity.svflags |= 1 /* NoClient */;
8733
+ entity.movedir = setMovedir(entity.angles);
8734
+ entity.angles = { x: 0, y: 0, z: 0 };
8735
+ if (entity.targetname) {
8529
8736
  entity.solid = 0 /* Not */;
8530
- entity.touch = void 0;
8531
- }
8532
- if (entity.spawnflags & GRAVITY_SPAWNFLAGS.Toggle) {
8533
8737
  entity.use = (self) => {
8534
- toggleSolid(self);
8535
- self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8738
+ self.solid = 1 /* Trigger */;
8739
+ self.use = void 0;
8536
8740
  };
8537
8741
  }
8742
+ entity.touch = (self, other) => triggerSecretTouch(self, other, context.entities);
8538
8743
  });
8539
8744
  }
8540
- function monsterJumpTouch(self, other) {
8541
- if (!other) {
8542
- return;
8543
- }
8544
- if ((other.flags & (1 /* Fly */ | 2 /* Swim */)) !== 0) {
8545
- return;
8546
- }
8547
- if (other.svflags & 2 /* DeadMonster */) {
8548
- return;
8549
- }
8550
- if ((other.svflags & 4 /* Monster */) === 0) {
8551
- return;
8745
+
8746
+ // src/entities/triggers/look.ts
8747
+ var LOOK_SPAWNFLAGS = {
8748
+ LookAway: 1 << 0
8749
+ };
8750
+ function triggerLookThink(self, entities) {
8751
+ let fired = false;
8752
+ const players = entities.findByClassname("player");
8753
+ for (const player of players) {
8754
+ if (player.health <= 0) continue;
8755
+ const vec = subtractVec3(self.origin, player.origin);
8756
+ const dist = Math.sqrt(dotVec3(vec, vec));
8757
+ const dir = normalizeVec3(vec);
8758
+ const forward = angleVectors(player.angles).forward;
8759
+ const dot = dotVec3(forward, dir);
8760
+ const fov = self.fov || 0.9;
8761
+ if (dot >= fov) {
8762
+ fired = true;
8763
+ self.activator = player;
8764
+ entities.useTargets(self, player);
8765
+ break;
8766
+ }
8552
8767
  }
8553
- other.velocity = {
8554
- x: self.movedir.x * self.speed,
8555
- y: self.movedir.y * self.speed,
8556
- z: other.velocity.z
8557
- };
8558
- if (!other.groundentity) {
8559
- return;
8768
+ if (fired) {
8769
+ entities.free(self);
8770
+ } else {
8771
+ entities.scheduleThink(self, entities.timeSeconds + 0.1);
8560
8772
  }
8561
- other.groundentity = null;
8562
- other.velocity = { x: other.velocity.x, y: other.velocity.y, z: self.movedir.z };
8563
8773
  }
8564
- function triggerElevatorUse(self, other, entities, warn) {
8565
- if (!self.movetarget) {
8566
- return;
8567
- }
8568
- if (self.movetarget.nextthink > 0) {
8569
- return;
8570
- }
8571
- if (!other?.pathtarget) {
8572
- warn("trigger_elevator used with no pathtarget");
8573
- return;
8574
- }
8575
- const target = entities.pickTarget(other.pathtarget);
8576
- if (!target) {
8577
- warn(`trigger_elevator used with bad pathtarget: ${other.pathtarget}`);
8578
- return;
8579
- }
8580
- self.movetarget.target_ent = target;
8581
- trainResume(self.movetarget, entities);
8774
+ function registerTriggerLook(registry) {
8775
+ registry.register("trigger_look", (entity, context) => {
8776
+ entity.movetype = 0 /* None */;
8777
+ entity.solid = 0 /* Not */;
8778
+ entity.svflags |= 1 /* NoClient */;
8779
+ entity.movedir = setMovedir(entity.angles);
8780
+ if (!entity.fov) entity.fov = 0.9;
8781
+ entity.think = (self) => triggerLookThink(self, context.entities);
8782
+ context.entities.scheduleThink(entity, context.entities.timeSeconds + 0.1);
8783
+ });
8582
8784
  }
8583
- function triggerElevatorInit(self, entities, warn) {
8584
- if (!self.target) {
8585
- warn("trigger_elevator has no target");
8586
- return;
8587
- }
8588
- const target = entities.pickTarget(self.target);
8589
- if (!target) {
8590
- warn(`trigger_elevator unable to find target ${self.target}`);
8591
- return;
8592
- }
8593
- self.movetarget = target;
8594
- if (target.classname !== "func_train") {
8595
- warn(`trigger_elevator target ${self.target} is not a train`);
8596
- return;
8785
+
8786
+ // src/entities/triggers/bad_area.ts
8787
+ function badAreaTouch(self, other) {
8788
+ if (!other) return;
8789
+ if (other.takedamage) {
8597
8790
  }
8598
- self.use = (entity, other, activator) => triggerElevatorUse(entity, other ?? activator ?? null, entities, warn);
8599
- self.svflags |= 1 /* NoClient */;
8600
- }
8601
- function registerTriggerElevator(registry) {
8602
- registry.register("trigger_elevator", (entity, context) => {
8603
- entity.think = (self) => triggerElevatorInit(self, context.entities, context.warn);
8604
- context.entities.scheduleThink(entity, context.entities.timeSeconds + FRAME_TIME_SECONDS);
8605
- });
8606
8791
  }
8607
- function registerTriggerMonsterJump(registry) {
8608
- registry.register("trigger_monsterjump", (entity, context) => {
8609
- const heightText = context.keyValues.height;
8610
- const height = heightText ? Number.parseFloat(heightText) || 0 : 200;
8611
- if (entity.angles.y === 0) {
8612
- entity.angles = { ...entity.angles, y: 360 };
8613
- }
8614
- if (!entity.speed) {
8615
- entity.speed = 200;
8616
- }
8617
- initTrigger(entity);
8618
- entity.movedir = { ...entity.movedir, z: height };
8619
- const touchHandler = (self, other) => monsterJumpTouch(self, other);
8620
- entity.touch = touchHandler;
8621
- if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.StartOff) {
8622
- entity.solid = 0 /* Not */;
8623
- entity.touch = void 0;
8624
- }
8625
- if (entity.spawnflags & MONSTERJUMP_SPAWNFLAGS.Toggle) {
8626
- entity.use = (self) => {
8627
- toggleSolid(self);
8628
- self.touch = self.solid === 1 /* Trigger */ ? touchHandler : void 0;
8792
+ function registerBadArea(registry) {
8793
+ registry.register("bad_area", (entity, context) => {
8794
+ entity.movetype = 0 /* None */;
8795
+ entity.solid = 1 /* Trigger */;
8796
+ entity.touch = badAreaTouch;
8797
+ if (entity.nextthink) {
8798
+ entity.think = (self) => {
8799
+ context.entities.free(self);
8629
8800
  };
8630
8801
  }
8631
8802
  });
8632
8803
  }
8804
+
8805
+ // src/entities/triggers/index.ts
8633
8806
  function registerTriggerSpawns(registry) {
8634
8807
  registerTriggerMultiple(registry);
8635
8808
  registerTriggerOnce(registry);
@@ -8645,25 +8818,10 @@ function registerTriggerSpawns(registry) {
8645
8818
  registerTriggerMonsterJump(registry);
8646
8819
  registerTriggerFog(registry);
8647
8820
  registerTriggerFlashlight(registry);
8821
+ registerTriggerSecret(registry);
8822
+ registerTriggerLook(registry);
8648
8823
  registerBadArea(registry);
8649
8824
  }
8650
- function badAreaTouch(self, other) {
8651
- if (!other) return;
8652
- if (other.takedamage) {
8653
- }
8654
- }
8655
- function registerBadArea(registry) {
8656
- registry.register("bad_area", (entity, context) => {
8657
- entity.movetype = 0 /* None */;
8658
- entity.solid = 1 /* Trigger */;
8659
- entity.touch = badAreaTouch;
8660
- if (entity.nextthink) {
8661
- entity.think = (self) => {
8662
- context.entities.free(self);
8663
- };
8664
- }
8665
- });
8666
- }
8667
8825
 
8668
8826
  // src/combat/weapons/state.ts
8669
8827
  var WeaponStateEnum = /* @__PURE__ */ ((WeaponStateEnum2) => {
@@ -9978,6 +10136,95 @@ function Trap_Think(player, sys) {
9978
10136
  );
9979
10137
  }
9980
10138
 
10139
+ // src/modes/ctf/grapple.ts
10140
+ var GRAPPLE_SPEED = 1200;
10141
+ var GRAPPLE_PULL_SPEED = 800;
10142
+ var GRAPPLE_DAMAGE = 20;
10143
+ function Grapple_Think(player, sys) {
10144
+ if (player.client?.inventory.currentWeapon !== WeaponId.Grapple) {
10145
+ ResetGrapple(player, sys);
10146
+ }
10147
+ Weapon_Generic(
10148
+ player,
10149
+ 0,
10150
+ 10,
10151
+ // ready
10152
+ 11,
10153
+ 20,
10154
+ // fire
10155
+ [],
10156
+ // pause frames
10157
+ [11],
10158
+ // fire frames
10159
+ (ent) => fire_grapple(ent, sys),
10160
+ sys
10161
+ );
10162
+ if (player.client && !(player.client.buttons & 1)) {
10163
+ ResetGrapple(player, sys);
10164
+ }
10165
+ if (player.grapple && player.grapple.grappleState === "attached") {
10166
+ Grapple_Pull(player, player.grapple, sys);
10167
+ }
10168
+ }
10169
+ function fire_grapple(player, sys) {
10170
+ if (player.grapple) return;
10171
+ const angles = player.client.v_angle || player.angles;
10172
+ const vectors = angleVectors(angles);
10173
+ const forward = vectors.forward;
10174
+ const right = vectors.right;
10175
+ const up = vectors.up;
10176
+ const game = sys.game;
10177
+ const start = P_ProjectSource(game, player, { x: 8, y: 8, z: 8 }, forward, right, up);
10178
+ const dir = { ...forward };
10179
+ const grapple = createProjectile(sys, start, dir, GRAPPLE_SPEED, 57 /* GRAPPLE */, 0);
10180
+ grapple.classname = "grapple";
10181
+ grapple.owner = player;
10182
+ grapple.movetype = 8 /* FlyMissile */;
10183
+ grapple.solid = 2 /* BoundingBox */;
10184
+ grapple.mins = { x: -4, y: -4, z: -4 };
10185
+ grapple.maxs = { x: 4, y: 4, z: 4 };
10186
+ grapple.model = "models/weapons/grapple/hook/tris.md2";
10187
+ grapple.grappleState = "fly";
10188
+ grapple.touch = (self, other, plane, surface) => {
10189
+ if (!other) return;
10190
+ Grapple_Touch(self, other, plane, surface, sys);
10191
+ };
10192
+ player.grapple = grapple;
10193
+ sys.sound(player, 0, "weapons/grapple/throw.wav", 1, 1, 0);
10194
+ }
10195
+ function Grapple_Touch(self, other, plane, surface, sys) {
10196
+ if (other === self.owner) return;
10197
+ if (self.grappleState === "attached") return;
10198
+ if (other.takedamage) {
10199
+ T_Damage(other, self, self.owner, self.velocity, self.origin, ZERO_VEC3, GRAPPLE_DAMAGE, 0, 0, 57 /* GRAPPLE */, sys.timeSeconds, sys.multicast.bind(sys));
10200
+ sys.free(self);
10201
+ if (self.owner) self.owner.grapple = void 0;
10202
+ return;
10203
+ }
10204
+ if (other.solid === 3 /* Bsp */ || other.solid === 2 /* BoundingBox */) {
10205
+ self.velocity = { x: 0, y: 0, z: 0 };
10206
+ self.movetype = 0 /* None */;
10207
+ self.grappleState = "attached";
10208
+ sys.sound(self, 0, "weapons/grapple/hit.wav", 1, 1, 0);
10209
+ if (plane) {
10210
+ }
10211
+ }
10212
+ }
10213
+ function Grapple_Pull(player, grapple, sys) {
10214
+ const dir = subtractVec3(grapple.origin, player.origin);
10215
+ const dist = lengthVec3(dir);
10216
+ if (dist < 32) return;
10217
+ const normalizedDir = normalizeVec3(dir);
10218
+ const pull = scaleVec3(normalizedDir, GRAPPLE_PULL_SPEED);
10219
+ player.velocity = pull;
10220
+ }
10221
+ function ResetGrapple(player, sys) {
10222
+ if (player.grapple) {
10223
+ sys.free(player.grapple);
10224
+ player.grapple = void 0;
10225
+ }
10226
+ }
10227
+
9981
10228
  // src/inventory/items.ts
9982
10229
  var WEAPON_ITEMS = {
9983
10230
  "weapon_blaster": {
@@ -10175,6 +10422,18 @@ var WEAPON_ITEMS = {
10175
10422
  pickupAmmo: 5,
10176
10423
  fireRate: 1,
10177
10424
  think: Trap_Think
10425
+ },
10426
+ "weapon_grapple": {
10427
+ type: "weapon",
10428
+ id: "weapon_grapple",
10429
+ name: "Grapple",
10430
+ weaponId: WeaponId.Grapple,
10431
+ ammoType: null,
10432
+ // Grapple uses no ammo in standard Q2 CTF? Or maybe just internal timer.
10433
+ initialAmmo: 0,
10434
+ pickupAmmo: 0,
10435
+ fireRate: 0.5,
10436
+ think: Grapple_Think
10178
10437
  }
10179
10438
  };
10180
10439
  var HEALTH_ITEMS = {
@@ -10576,6 +10835,34 @@ function setFlagState(flag, newState, context) {
10576
10835
  flag.flagState = newState;
10577
10836
  }
10578
10837
 
10838
+ // src/modes/ctf/scoreboard.ts
10839
+ var teamScores = {
10840
+ [1 /* RED */]: 0,
10841
+ [2 /* BLUE */]: 0
10842
+ };
10843
+ function addTeamScore(team, points) {
10844
+ if (team === 1 /* RED */ || team === 2 /* BLUE */) {
10845
+ teamScores[team] += points;
10846
+ }
10847
+ }
10848
+ function updateCtfScoreboard(ent, sys) {
10849
+ if (!ent.client || !ent.client.stats) return;
10850
+ const stats = ent.client.stats;
10851
+ stats[PlayerStat.STAT_CTF_TEAM1_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_r.pcx") : 0;
10852
+ stats[PlayerStat.STAT_CTF_TEAM1_CAPS] = teamScores[1 /* RED */];
10853
+ stats[PlayerStat.STAT_CTF_TEAM2_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_b.pcx") : 0;
10854
+ stats[PlayerStat.STAT_CTF_TEAM2_CAPS] = teamScores[2 /* BLUE */];
10855
+ const clientTeam = ent.client.ctfTeam;
10856
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM1_PIC] = 0;
10857
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM2_PIC] = 0;
10858
+ if (clientTeam === 1 /* RED */) {
10859
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM1_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_r.pcx") : 0;
10860
+ } else if (clientTeam === 2 /* BLUE */) {
10861
+ stats[PlayerStat.STAT_CTF_JOINED_TEAM2_PIC] = sys.configStringIndex ? sys.configStringIndex("pics/ctf_b.pcx") : 0;
10862
+ }
10863
+ stats[PlayerStat.STAT_CTF_TEAMINFO] = 1;
10864
+ }
10865
+
10579
10866
  // src/modes/ctf/capture.ts
10580
10867
  function checkCapture(flag, player, game, context) {
10581
10868
  if (!player.client) return false;
@@ -10596,11 +10883,17 @@ function captureFlag(ownFlag, player, game, context) {
10596
10883
  if (!player.client) return false;
10597
10884
  const playerTeam = player.client.team || "red";
10598
10885
  const enemyTeam = playerTeam === "red" ? "blue" : "red";
10886
+ const ctfStats = player.client.ctfStats;
10887
+ if (ctfStats) {
10888
+ ctfStats.captures++;
10889
+ }
10599
10890
  if (player.client.score !== void 0) {
10600
10891
  player.client.score += 5;
10601
10892
  } else {
10602
10893
  player.client.score = 5;
10603
10894
  }
10895
+ const teamEnum = playerTeam === "red" ? 1 /* RED */ : 2 /* BLUE */;
10896
+ addTeamScore(teamEnum, 1);
10604
10897
  game.sound?.(player, 0, "ctf/flagcap.wav", 1, 1, 0);
10605
10898
  game.centerprintf?.(player, "You captured the flag!");
10606
10899
  const enemyFlagKey = playerTeam === "red" ? "key_blue_flag" /* BlueFlag */ : "key_red_flag" /* RedFlag */;
@@ -26230,6 +26523,9 @@ function player_think(self, sys) {
26230
26523
  }
26231
26524
  }
26232
26525
  P_PlayerThink(self, sys);
26526
+ if (sys.deathmatch && (sys.configStringIndex ? sys.configStringIndex("pics/ctf_r.pcx") > 0 : true)) {
26527
+ updateCtfScoreboard(self, sys);
26528
+ }
26233
26529
  self.nextthink = sys.timeSeconds + 0.1;
26234
26530
  sys.scheduleThink(self, self.nextthink);
26235
26531
  }
@@ -26297,6 +26593,16 @@ function populatePlayerStats(player, timeSeconds) {
26297
26593
  const remainingSeconds = Math.ceil((bestTime - nowMs) / 1e3);
26298
26594
  statArray[PlayerStat.STAT_TIMER] = remainingSeconds;
26299
26595
  }
26596
+ if (player.client.score !== void 0) {
26597
+ statArray[PlayerStat.STAT_FRAGS] = player.client.score;
26598
+ }
26599
+ if (player.client.stats) {
26600
+ for (let i = 0; i < player.client.stats.length; i++) {
26601
+ if (player.client.stats[i] !== 0) {
26602
+ statArray[i] = player.client.stats[i];
26603
+ }
26604
+ }
26605
+ }
26300
26606
  return statArray;
26301
26607
  }
26302
26608