Merge branch 'master' into TimePath/stats
authorTimePath <andrew.hardaker1995@gmail.com>
Sun, 29 Nov 2015 02:45:20 +0000 (13:45 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Sun, 29 Nov 2015 02:46:37 +0000 (13:46 +1100)
# Conflicts:
# qcsrc/client/hud/panel/ammo.qc
# qcsrc/client/hud/panel/powerups.qc
# qcsrc/client/view.qc
# qcsrc/common/mutators/mutator/nades/nades.qc
# qcsrc/common/physics.qc
# qcsrc/common/physics.qh
# qcsrc/common/weapons/weapon/vortex.qc
# qcsrc/lib/registry.qh
# qcsrc/lib/stats.qh

34 files changed:
1  2 
qcsrc/client/announcer.qc
qcsrc/client/hud/panel/ammo.qc
qcsrc/client/hud/panel/powerups.qc
qcsrc/client/main.qc
qcsrc/client/scoreboard.qc
qcsrc/client/view.qc
qcsrc/common/constants.qh
qcsrc/common/gamemodes/gamemode/nexball/nexball.qc
qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc
qcsrc/common/mutators/mutator/buffs/all.qh
qcsrc/common/mutators/mutator/buffs/buffs.qc
qcsrc/common/mutators/mutator/buffs/module.inc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/nades/nades.qh
qcsrc/common/mutators/mutator/overkill/overkill.qc
qcsrc/common/physics.qc
qcsrc/common/physics.qh
qcsrc/common/triggers/trigger/jumppads.qc
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/all.qh
qcsrc/common/weapons/weapon/vortex.qc
qcsrc/dpdefs/csprogsdefs.qh
qcsrc/lib/registry.qh
qcsrc/lib/stats.qh
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/mutator/gamemode_ca.qc
qcsrc/server/mutators/mutator/gamemode_ctf.qc
qcsrc/server/mutators/mutator/gamemode_domination.qc
qcsrc/server/sv_main.qc

Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -360,9 -360,9 +360,9 @@@ float TrueAimCheck(
                        break;
        }
  
-       vector traceorigin = getplayerorigin(player_localentnum-1) + (eZ * getstati(STAT_VIEWHEIGHT));
+       vector traceorigin = entcs_receiver(player_localentnum - 1).origin + (eZ * getstati(STAT_VIEWHEIGHT));
  
 -      vecs = decompressShotOrigin(getstati(STAT_SHOTORG));
 +      vecs = decompressShotOrigin(STAT(SHOTORG));
  
        traceline(traceorigin, traceorigin + view_forward * MAX_SHOT_DISTANCE, mv, ta);
        trueaimpoint = trace_endpos;
@@@ -1005,19 -1005,27 +1005,27 @@@ void HUD_Crosshair(
  
  void HUD_Draw()
  {
-       if(STAT(FROZEN))
-               drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, ((STAT(REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * STAT(REVIVE_PROGRESS)) + ('0 1 1' * STAT(REVIVE_PROGRESS) * -1)) : '0.25 0.90 1'), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
-       else if (STAT(HEALING_ORB)>time)
-               drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, NADE_TYPE_HEAL.m_color, autocvar_hud_colorflash_alpha*STAT(HEALING_ORB_ALPHA), DRAWFLAG_ADDITIVE);
+       vector rgb = '0 0 0';
+       float a = 1;
+       if (MUTATOR_CALLHOOK(HUD_Draw_overlay))
+       {
+               rgb = MUTATOR_ARGV(0, vector);
+               a = MUTATOR_ARGV(0, float);
+       }
 -      else if(getstati(STAT_FROZEN))
++      else if(STAT(FROZEN))
+       {
 -              rgb = ((getstatf(STAT_REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * getstatf(STAT_REVIVE_PROGRESS)) + ('0 1 1' * getstatf(STAT_REVIVE_PROGRESS) * -1)) : '0.25 0.90 1');
++              rgb = ((STAT(REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * STAT(REVIVE_PROGRESS)) + ('0 1 1' * STAT(REVIVE_PROGRESS) * -1)) : '0.25 0.90 1');
+       }
+       drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, rgb, autocvar_hud_colorflash_alpha * a, DRAWFLAG_ADDITIVE);
        if(!intermission)
 -      if(getstatf(STAT_NADE_TIMER) && autocvar_cl_nade_timer) // give nade top priority, as it's a matter of life and death
 +      if(STAT(NADE_TIMER) && autocvar_cl_nade_timer) // give nade top priority, as it's a matter of life and death
        {
 -              DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * getstatf(STAT_NADE_TIMER)) - ('0 1 1' * getstatf(STAT_NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
 +              DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * STAT(NADE_TIMER)) - ('0 1 1' * STAT(NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
                drawstring_aspect(eY * 0.64 * vid_conheight, ((autocvar_cl_nade_timer == 2) ? _("Nade timer") : ""), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
        }
 -      else if(getstatf(STAT_REVIVE_PROGRESS))
 +      else if(STAT(REVIVE_PROGRESS))
        {
 -              DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
 +              DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
                drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
        }
  
Simple merge
index 0000000,db22d31..76ecff8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,73 +1,73 @@@
 -      .int buffs;
+ #ifndef BUFFS_ALL_H
+ #define BUFFS_ALL_H
+ // Welcome to the stuff behind the scenes
+ // Below, you will find the list of buffs
+ // Add new buffs here!
+ // Note: Buffs also need spawnfuncs, which are set below
+ #include "../../../teams.qh"
+ #include "../../../util.qh"
+ REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1);
+ REGISTER_RADARICON(Buff, 1);
+ REGISTRY(Buffs, BITS(4))
+ #define Buffs_from(i) _Buffs_from(i, BUFF_Null)
+ REGISTER_REGISTRY(Buffs)
+ REGISTRY_CHECK(Buffs)
+ #define REGISTER_BUFF(id) \
+     REGISTER(Buffs, BUFF_##id, m_id, NEW(Buff)); \
+     REGISTER_INIT_POST(BUFF_##id) { \
+         this.netname = this.m_name; \
+         this.m_itemid = BIT(this.m_id - 1); \
+         this.m_sprite = strzone(strcat("buff-", this.m_name)); \
+     } \
+     REGISTER_INIT(BUFF_##id)
+ #include "../../../items/item/pickup.qh"
+ CLASS(Buff, Pickup)
+       /** bit index */
+       ATTRIB(Buff, m_itemid, int, 0)
+       ATTRIB(Buff, m_name, string, "buff")
+       ATTRIB(Buff, m_color, vector, '1 1 1')
+       ATTRIB(Buff, m_prettyName, string, "Buff")
+       ATTRIB(Buff, m_skin, int, 0)
+       ATTRIB(Buff, m_sprite, string, "")
+       METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) {
+               returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name));
+       }
+ #ifdef SVQC
+       METHOD(Buff, m_time, float(Buff this))
+       { return cvar(strcat("g_buffs_", this.netname, "_time")); }
+ #endif
+ ENDCLASS(Buff)
+ #ifdef SVQC
++      // .int buffs = _STAT(BUFFS);
+       void buff_Init(entity ent);
+       void buff_Init_Compat(entity ent, entity replacement);
+       #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \
+               self.buffs = b.m_itemid; \
+               self.team = t; \
+               buff_Init(self); \
+       }
+       #define BUFF_SPAWNFUNCS(e, b)                       \
+                       BUFF_SPAWNFUNC(e,           b,  0)          \
+                       BUFF_SPAWNFUNC(e##_team1,   b,  NUM_TEAM_1) \
+                       BUFF_SPAWNFUNC(e##_team2,   b,  NUM_TEAM_2) \
+                       BUFF_SPAWNFUNC(e##_team3,   b,  NUM_TEAM_3) \
+                       BUFF_SPAWNFUNC(e##_team4,   b,  NUM_TEAM_4)
+       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(self, r); }
+ #else
+       #define BUFF_SPAWNFUNC(e, b, t)
+       #define BUFF_SPAWNFUNCS(e, b)
+       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r)
+ #endif
+ REGISTER_BUFF(Null);
+ BUFF_SPAWNFUNCS(random, BUFF_Null)
+ #include "all.inc"
+ #endif
@@@ -75,9 -75,8 +75,8 @@@ const vector BUFF_MAX = ('16 16 20')
  
  #include "../../../triggers/target/music.qh"
  #include "../../../gamemodes/all.qh"
- #include "../../../buffs/all.qh"
  
 -.float buff_time;
 +.float buff_time = _STAT(BUFF_TIME);
  void buffs_DelayedInit();
  
  REGISTER_MUTATOR(buffs, cvar("g_buffs"))
@@@ -1,3 -2,45 +2,45 @@@
  #ifdef SVQC
  #include "buffs.qc"
  #endif
 -    int allBuffs = getstati(STAT_BUFFS, 0, 24);
+ #ifdef IMPLEMENTATION
+ string BUFF_NAME(int i)
+ {
+     Buff b = Buffs_from(i);
+     return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName);
+ }
+ #ifndef MENUQC
+ REGISTER_MUTATOR(buffs_flight, true);
+ MUTATOR_HOOKFUNCTION(buffs_flight, IsFlying)
+ {
+     noref entity e = MUTATOR_ARGV(0, entity);
+       return BUFFS_STAT(e) & BUFF_FLIGHT.m_itemid;
+ }
+ #endif
+ #ifdef CSQC
+ REGISTER_MUTATOR(cl_buffs, true);
+ MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add)
+ {
 -              addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, getstatf(STAT_BUFF_TIME) - time, 99), 60);
++    int allBuffs = STAT(BUFFS);
+     FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA(
++              addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60);
+       ));
+ }
+ MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format)
+ {
+     entity this = MUTATOR_ARGV(0, entity);
+     string s = MUTATOR_ARGV(0, string);
+     if (s == WP_Buff.netname || s == RADARICON_Buff.netname)
+     {
+         Buff b = Buffs_from(this.wp_extra);
+         MUTATOR_ARGV(0, vector) = b.m_color;
+         MUTATOR_ARGV(0, string) = b.m_prettyName;
+         return true;
+     }
+ }
+ #endif
+ #endif
- #ifndef MUTATOR_NADES_H
- #define MUTATOR_NADES_H
+ #include "nades.qh"
  
- #ifdef SVQC
- #include "../../../../server/mutators/mutator/gamemode_freezetag.qc"
+ #ifdef IMPLEMENTATION
+ #ifndef MENUQC
+ entity Nade_TrailEffect(int proj, int nade_team)
+ {
+     switch (proj)
+     {
+         case PROJECTILE_NADE:       return EFFECT_NADE_TRAIL(nade_team);
+         case PROJECTILE_NADE_BURN:  return EFFECT_NADE_TRAIL_BURN(nade_team);
+     }
+     FOREACH(Nades, true, LAMBDA(
+         for (int j = 0; j < 2; j++)
+         {
+             if (it.m_projectile[j] == proj)
+             {
+                 string trail = it.m_trail[j].eent_eff_name;
+                 if (trail) return it.m_trail[j];
+                 break;
+             }
+         }
+     ));
+     return EFFECT_Null;
+ }
  #endif
  
- .entity nade;
- .entity fake_nade;
- .float nade_timer = _STAT(NADE_TIMER);
- .float nade_refire;
- .float bonus_nades = _STAT(NADE_BONUS);
- .float nade_special_time;
- .float bonus_nade_score = _STAT(NADE_BONUS_SCORE);
- .int nade_type = _STAT(NADE_BONUS_TYPE);
- .string pokenade_type;
- .entity nade_damage_target;
- .float cvar_cl_nade_type;
- .string cvar_cl_pokenade_type;
- .float toss_time;
- .float stat_healing_orb = _STAT(HEALING_ORB);
- .float stat_healing_orb_alpha = _STAT(HEALING_ORB_ALPHA);
- .float nade_show_particles;
- // Remove nades that are being thrown
- void nades_Clear(entity player);
- // Give a bonus grenade to a player
- void(entity player, float score) nades_GiveBonus;
- /**
-  * called to adjust nade damage and force on hit
-  */
- #define EV_Nade_Damage(i, o) \
-       /** weapon */ i(entity, MUTATOR_ARGV_0_entity) \
-     /** force */  i(vector, MUTATOR_ARGV_0_vector) \
-     /**/          o(vector, MUTATOR_ARGV_0_vector) \
-       /** damage */ i(float,  MUTATOR_ARGV_0_float) \
-     /**/          o(float,  MUTATOR_ARGV_0_float) \
-     /**/
- MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage);
+ #ifdef CSQC
+ REGISTER_MUTATOR(cl_nades, true);
+ MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay)
+ {
 -      if (getstatf(STAT_HEALING_ORB) <= time) return false;
++      if (STAT(HEALING_ORB) <= time) return false;
+       MUTATOR_ARGV(0, vector) = NADE_TYPE_HEAL.m_color;
 -      MUTATOR_ARGV(0, float) = getstatf(STAT_HEALING_ORB_ALPHA);
++      MUTATOR_ARGV(0, float) = STAT(HEALING_ORB_ALPHA);
+       return true;
+ }
+ MUTATOR_HOOKFUNCTION(cl_nades, Ent_Projectile)
+ {
+       if (self.cnt == PROJECTILE_NAPALM_FOUNTAIN)
+       {
+               self.modelindex = 0;
+               self.traileffect = EFFECT_FIREBALL.m_id;
+               return true;
+       }
+       if (Nade_FromProjectile(self.cnt) != NADE_TYPE_Null)
+       {
+               setmodel(self, MDL_PROJECTILE_NADE);
+               entity trail = Nade_TrailEffect(self.cnt, self.team);
+               if (trail.eent_eff_name) self.traileffect = trail.m_id;
+               return true;
+       }
+ }
+ MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile)
+ {
+       if (self.cnt == PROJECTILE_NAPALM_FOUNTAIN)
+       {
+               loopsound(self, CH_SHOTS_SINGLE, SND(FIREBALL_FLY2), VOL_BASE, ATTEN_NORM);
+               self.mins = '-16 -16 -16';
+               self.maxs = '16 16 16';
+       }
+       entity nade_type = Nade_FromProjectile(self.cnt);
+       if (nade_type == NADE_TYPE_Null) return;
+       self.mins = '-16 -16 -16';
+       self.maxs = '16 16 16';
+       self.colormod = nade_type.m_color;
+       self.move_movetype = MOVETYPE_BOUNCE;
+       self.move_touch = func_null;
+       self.scale = 1.5;
+       self.avelocity = randomvec() * 720;
+       if (nade_type == NADE_TYPE_TRANSLOCATE || nade_type == NADE_TYPE_SPAWN)
+               self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+       else
+               self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+ }
+ bool Projectile_isnade(int p)
+ {
+       return Nade_FromProjectile(p) != NADE_TYPE_Null;
+ }
+ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time)
+ {
 -      float bonusNades    = getstatf(STAT_NADE_BONUS);
 -      float bonusProgress = getstatf(STAT_NADE_BONUS_SCORE);
 -      float bonusType     = getstati(STAT_NADE_BONUS_TYPE);
++      float bonusNades    = STAT(NADE_BONUS);
++      float bonusProgress = STAT(NADE_BONUS_SCORE);
++      float bonusType     = STAT(NADE_BONUS_TYPE);
+       Nade def = Nades_from(bonusType);
+       vector nadeColor    = def.m_color;
+       string nadeIcon     = def.m_icon;
+       vector iconPos, textPos;
+       if(autocvar_hud_panel_ammo_iconalign)
+       {
+               iconPos = myPos + eX * 2 * mySize.y;
+               textPos = myPos;
+       }
+       else
+       {
+               iconPos = myPos;
+               textPos = myPos + eX * mySize.y;
+       }
+       if(bonusNades > 0 || bonusProgress > 0)
+       {
+               DrawNadeProgressBar(myPos, mySize, bonusProgress, nadeColor);
+               if(autocvar_hud_panel_ammo_text)
+                       drawstring_aspect(textPos, ftos(bonusNades), eX * (2/3) * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+               if(draw_expanding)
+                       drawpic_aspect_skin_expanding(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, expand_time);
  
+               drawpic_aspect_skin(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       }
+ }
  #endif
  
- #ifdef IMPLEMENTATION
+ #ifdef SVQC
  
- #include "../../../nades/all.qh"
  #include "../../../gamemodes/all.qh"
  #include "../../../monsters/spawn.qh"
  #include "../../../monsters/sv_monsters.qh"
index 0000000,2e48293..312cf4a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,103 +1,103 @@@
 -.float nade_timer;
+ #ifndef NADES_ALL_H
+ #define NADES_ALL_H
+ #include "../../../teams.qh"
+ // use slots 70-100
+ const int PROJECTILE_NADE = 71;
+ const int PROJECTILE_NADE_BURN = 72;
+ const int PROJECTILE_NADE_NAPALM = 73;
+ const int PROJECTILE_NADE_NAPALM_BURN = 74;
+ const int PROJECTILE_NAPALM_FOUNTAIN = 75;
+ const int PROJECTILE_NADE_ICE = 76;
+ const int PROJECTILE_NADE_ICE_BURN = 77;
+ const int PROJECTILE_NADE_TRANSLOCATE = 78;
+ const int PROJECTILE_NADE_SPAWN = 79;
+ const int PROJECTILE_NADE_HEAL = 80;
+ const int PROJECTILE_NADE_HEAL_BURN = 81;
+ const int PROJECTILE_NADE_MONSTER = 82;
+ const int PROJECTILE_NADE_MONSTER_BURN = 83;
+ REGISTRY(Nades, BITS(4))
+ #define Nades_from(i) _Nades_from(i, NADE_TYPE_Null)
+ REGISTER_REGISTRY(Nades)
+ REGISTRY_CHECK(Nades)
+ #define REGISTER_NADE(id) REGISTER(Nades, NADE_TYPE, id, m_id, NEW(Nade))
+ CLASS(Nade, Object)
+     ATTRIB(Nade, m_id, int, 0)
+     ATTRIB(Nade, m_color, vector, '0 0 0')
+     ATTRIB(Nade, m_name, string, _("Grenade"))
+     ATTRIB(Nade, m_icon, string, "nade_normal")
+     ATTRIBARRAY(Nade, m_projectile, int, 2)
+     ATTRIBARRAY(Nade, m_trail, entity, 2)
+     METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) {
+         returns(this.m_name, sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon));
+     }
+ ENDCLASS(Nade)
+ REGISTER_NADE(Null);
+ Nade Nade_FromProjectile(int proj)
+ {
+     FOREACH(Nades, true, LAMBDA(
+         for (int j = 0; j < 2; j++)
+         {
+             if (it.m_projectile[j] == proj) return it;
+         }
+     ));
+     return NADE_TYPE_Null;
+ }
+ #ifndef MENUQC
+ #include "effects.inc"
+ #endif
+ #include "nades.inc"
+ .float healer_lifetime;
+ .float healer_radius;
+ #ifdef SVQC
+ .entity nade;
+ .entity fake_nade;
 -.float bonus_nades;
++.float nade_timer = _STAT(NADE_TIMER);
+ .float nade_refire;
 -.float bonus_nade_score;
 -.float nade_type;
++.float bonus_nades = _STAT(NADE_BONUS);
+ .float nade_special_time;
 -.float stat_healing_orb;
 -.float stat_healing_orb_alpha;
++.float bonus_nade_score = _STAT(NADE_BONUS_SCORE);
++.int nade_type = _STAT(NADE_BONUS_TYPE);
+ .string pokenade_type;
+ .entity nade_damage_target;
+ .float cvar_cl_nade_type;
+ .string cvar_cl_pokenade_type;
+ .float toss_time;
++.float stat_healing_orb = _STAT(HEALING_ORB);
++.float stat_healing_orb_alpha = _STAT(HEALING_ORB_ALPHA);
+ .float nade_show_particles;
+ bool healer_send(entity this, entity to, int sf);
+ // Remove nades that are being thrown
+ void nades_Clear(entity player);
+ // Give a bonus grenade to a player
+ void(entity player, float score) nades_GiveBonus;
+ /**
+  * called to adjust nade damage and force on hit
+  */
+ #define EV_Nade_Damage(i, o) \
+       /** weapon */ i(entity, MUTATOR_ARGV_0_entity) \
+     /** force */  i(vector, MUTATOR_ARGV_0_vector) \
+     /**/          o(vector, MUTATOR_ARGV_0_vector) \
+       /** damage */ i(float,  MUTATOR_ARGV_0_float) \
+     /**/          o(float,  MUTATOR_ARGV_0_float) \
+     /**/
+ MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage);
+ #endif
+ #endif
@@@ -1382,20 -1476,20 +1366,20 @@@ void PM_walk(entity this, float maxspd_
                        v >= PHYS_STOPSPEED * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION)
                 */
        }
-       float addspeed = wishspeed - self.velocity * wishdir;
+       const float addspeed = wishspeed - this.velocity * wishdir;
        if (addspeed > 0)
        {
-               float accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
-               self.velocity += accelspeed * wishdir;
+               const float accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed);
+               this.velocity += accelspeed * wishdir;
        }
-       float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH;
 -      const float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
++      const float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH;
        if (!(GAMEPLAYFIX_NOGRAVITYONGROUND))
-               self.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
-       if (self.velocity * self.velocity)
+               this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1);
+       if (vdist(this.velocity, >, 0))
                PM_ClientMovement_Move();
        if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE)
-               if (!IS_ONGROUND(self) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
-                       self.velocity_z -= g * 0.5;
+               if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND)
+                       this.velocity_z -= g * 0.5;
  }
  
  void PM_air(float buttons_prev, float maxspd_mod)
@@@ -1513,10 -1607,10 +1497,10 @@@ void PM_Main(entity this
        maxspeed_mod *= PHYS_HIGHSPEED;
  
  #ifdef SVQC
 -      Physics_UpdateStats(maxspeed_mod);
 +      Physics_UpdateStats(this, maxspeed_mod);
  
-       if (self.PlayerPhysplug)
-               if (self.PlayerPhysplug())
+       if (this.PlayerPhysplug)
+               if (this.PlayerPhysplug())
                        return;
  #endif
  
@@@ -159,11 -72,16 +159,11 @@@ bool IsFlying(entity a)
  
        #define PHYS_DEAD(s)                                            s.csqcmodel_isdead
  
 -      #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  boolean(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
 -      #define GAMEPLAYFIX_NOGRAVITYONGROUND                   cvar("sv_gameplayfix_nogravityonground")
 -      #define GAMEPLAYFIX_Q2AIRACCELERATE                             cvar("sv_gameplayfix_q2airaccelerate")
 -      #define GAMEPLAYFIX_EASIERWATERJUMP                     getstati(STAT_GAMEPLAYFIX_EASIERWATERJUMP)
 -      #define GAMEPLAYFIX_DOWNTRACEONGROUND                   getstati(STAT_GAMEPLAYFIX_DOWNTRACEONGROUND)
 -      #define GAMEPLAYFIX_STEPMULTIPLETIMES                   getstati(STAT_GAMEPLAYFIX_STEPMULTIPLETIMES)
 -      #define GAMEPLAYFIX_UNSTICKPLAYERS                              getstati(STAT_GAMEPLAYFIX_UNSTICKPLAYERS)
 -      #define GAMEPLAYFIX_STEPDOWN                                    getstati(STAT_GAMEPLAYFIX_STEPDOWN)
 +      #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE  (boolean(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE))
 +      #define GAMEPLAYFIX_NOGRAVITYONGROUND                   (boolean(moveflags & MOVEFLAG_NOGRAVITYONGROUND))
 +      #define GAMEPLAYFIX_Q2AIRACCELERATE                             (boolean(moveflags & MOVEFLAG_Q2AIRACCELERATE))
  
-       #define IS_DUCKED(s)                                            !!(s.flags & FL_DUCKED)
+       #define IS_DUCKED(s)                                            boolean(s.flags & FL_DUCKED)
        #define SET_DUCKED(s)                                           s.flags |= FL_DUCKED
        #define UNSET_DUCKED(s)                                         s.flags &= ~FL_DUCKED
  
        #define SET_ONGROUND(s)                                         s.flags |= FL_ONGROUND
        #define UNSET_ONGROUND(s)                                       s.flags &= ~FL_ONGROUND
  
-       #define WAS_ONGROUND(s)                                         !!(s.lastflags & FL_ONGROUND)
+       #define WAS_ONGROUND(s)                                         boolean(s.lastflags & FL_ONGROUND)
  
        #define ITEMS_STAT(s)                                           (s).items
 -      #define BUFFS_STAT(s)                                           getstati(STAT_BUFFS)
 -
 -      #define PHYS_AMMO_FUEL(s)                                       getstati(STAT_FUEL)
 -
 -      #define PHYS_FROZEN(s)                                          getstati(STAT_FROZEN)
 -
 -      #define PHYS_DOUBLEJUMP                                         getstati(STAT_DOUBLEJUMP)
 -
 -      #define PHYS_BUGRIGS                                            getstati(STAT_BUGRIGS)
 -      #define PHYS_BUGRIGS_ANGLE_SMOOTHING            getstati(STAT_BUGRIGS_ANGLE_SMOOTHING)
 -      #define PHYS_BUGRIGS_PLANAR_MOVEMENT            getstati(STAT_BUGRIGS_PLANAR_MOVEMENT)
 -      #define PHYS_BUGRIGS_REVERSE_SPEEDING           getstati(STAT_BUGRIGS_REVERSE_SPEEDING)
 -      #define PHYS_BUGRIGS_FRICTION_FLOOR             getstatf(STAT_BUGRIGS_FRICTION_FLOOR)
 -      #define PHYS_BUGRIGS_AIR_STEERING                       getstati(STAT_BUGRIGS_AIR_STEERING)
 -      #define PHYS_BUGRIGS_FRICTION_BRAKE             getstatf(STAT_BUGRIGS_FRICTION_BRAKE)
 -      #define PHYS_BUGRIGS_ACCEL                                      getstatf(STAT_BUGRIGS_ACCEL)
 -      #define PHYS_BUGRIGS_SPEED_REF                          getstatf(STAT_BUGRIGS_SPEED_REF)
 -      #define PHYS_BUGRIGS_SPEED_POW                          getstatf(STAT_BUGRIGS_SPEED_POW)
 -      #define PHYS_BUGRIGS_STEER                                      getstatf(STAT_BUGRIGS_STEER)
 -      #define PHYS_BUGRIGS_FRICTION_AIR                       getstatf(STAT_BUGRIGS_FRICTION_AIR)
 -      #define PHYS_BUGRIGS_CAR_JUMPING                        getstatf(STAT_BUGRIGS_CAR_JUMPING)
 -      #define PHYS_BUGRIGS_REVERSE_SPINNING           getstatf(STAT_BUGRIGS_REVERSE_SPINNING)
 -      #define PHYS_BUGRIGS_REVERSE_STOPPING           getstatf(STAT_BUGRIGS_REVERSE_STOPPING)
  
        #define PHYS_JUMPSPEEDCAP_MIN                           cvar_string("cl_jumpspeedcap_min")
        #define PHYS_JUMPSPEEDCAP_MAX                           cvar_string("cl_jumpspeedcap_max")
        #define SET_ONGROUND(s)                                         s.flags |= FL_ONGROUND
        #define UNSET_ONGROUND(s)                                       s.flags &= ~FL_ONGROUND
  
-       #define WAS_ONGROUND(s)                                         !!((s).lastflags & FL_ONGROUND)
+       #define WAS_ONGROUND(s)                                         boolean((s).lastflags & FL_ONGROUND)
  
        #define ITEMS_STAT(s)                                           s.items
 -      #define BUFFS_STAT(s)                                           (s).buffs
 -
 -      #define PHYS_AMMO_FUEL(s)                                       s.ammo_fuel
 -
 -      #define PHYS_FROZEN(s)                                          s.frozen
 -
 -      #define PHYS_DOUBLEJUMP                                         autocvar_sv_doublejump
 -
 -      #define PHYS_BUGRIGS                                            g_bugrigs
 -      #define PHYS_BUGRIGS_ANGLE_SMOOTHING            g_bugrigs_angle_smoothing
 -      #define PHYS_BUGRIGS_PLANAR_MOVEMENT            g_bugrigs_planar_movement
 -      #define PHYS_BUGRIGS_REVERSE_SPEEDING           g_bugrigs_reverse_speeding
 -      #define PHYS_BUGRIGS_FRICTION_FLOOR                     g_bugrigs_friction_floor
 -      #define PHYS_BUGRIGS_AIR_STEERING                       g_bugrigs_air_steering
 -      #define PHYS_BUGRIGS_FRICTION_BRAKE                     g_bugrigs_friction_brake
 -      #define PHYS_BUGRIGS_ACCEL                                      g_bugrigs_accel
 -      #define PHYS_BUGRIGS_SPEED_REF                          g_bugrigs_speed_ref
 -      #define PHYS_BUGRIGS_SPEED_POW                          g_bugrigs_speed_pow
 -      #define PHYS_BUGRIGS_STEER                                      g_bugrigs_steer
 -      #define PHYS_BUGRIGS_FRICTION_AIR                       g_bugrigs_friction_air
 -      #define PHYS_BUGRIGS_CAR_JUMPING                        g_bugrigs_planar_movement_car_jumping
 -      #define PHYS_BUGRIGS_REVERSE_SPINNING           g_bugrigs_reverse_spinning
 -      #define PHYS_BUGRIGS_REVERSE_STOPPING           g_bugrigs_reverse_stopping
  
        #define PHYS_JUMPSPEEDCAP_MIN                           autocvar_sv_jumpspeedcap_min
        #define PHYS_JUMPSPEEDCAP_MAX                           autocvar_sv_jumpspeedcap_max
Simple merge
@@@ -7,9 -7,9 +7,7 @@@
  
  // weapon sets
  typedef vector WepSet;
- #define WEPSET(id) WepSet_FromWeapon(WEP_##id.m_id)
- WepSet WepSet_FromWeapon(int a);
  #ifdef SVQC
 -void WepSet_AddStat();
 -void WepSet_AddStat_InMap();
  void WriteWepSet(float dest, WepSet w);
  #endif
  
@@@ -91,7 -91,8 +91,8 @@@ NET_HANDLE(TE_CSQC_VORTEXBEAMPARTICLE, 
        charge = sqrt(charge); // divide evenly among trail spacing and alpha
        particles_alphamin = particles_alphamax = particles_fade = charge;
  
-       if (autocvar_cl_particles_oldvortexbeam && (STAT(ALLOW_OLDVORTEXBEAM) || isdemo()))
+       if(!MUTATOR_CALLHOOK(Particles_VortexBeam, shotorg, endpos))
 -      if(autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo()))
++      if(autocvar_cl_particles_oldvortexbeam && (STAT(ALLOW_OLDVORTEXBEAM) || isdemo()))
                WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM_OLD), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
        else
                WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
  #undef pointparticles
  #undef setmodel
  
 +#undef STAT_FRAGLIMIT
 +#undef STAT_TIMELIMIT
 +#undef STAT_MOVEVARS_TICRATE
 +#undef STAT_MOVEVARS_TIMESCALE
 +#undef STAT_MOVEVARS_GRAVITY
 +
  #pragma noref 0
  
- #define ReadFloat() ReadCoord()
  #endif
@@@ -29,48 -43,39 +43,49 @@@ REGISTRY(Registries, BITS(8)
   *         } \
   *         REGISTER_INIT(FOO, id)
   *
-  * Don't forget to forward declare `initfunc` and call `REGISTER_REGISTRY`:
-  *     void RegisterFoos();
-  *     REGISTER_REGISTRY(RegisterFoos)
   *
-  * @param initfunc  The global constructor to accumulate into
+  * @param registry  The registry to add each entity to.
   * @param ns        Short for namespace, prefix for each global (ns##_##id)
-  * @param array     The array to add each entity to. Also requires `array##_first` and `array##_last` to be defined
   * @param id        The identifier of the current entity being registered
-  * @param fld       The field to store the current count into
+  * @param fld       The field to store the locally unique unique entity id
   * @param inst      An expression to create a new instance, invoked for every registration
   */
- #define REGISTER(initfunc, ns, array, id, fld, inst) \
-       entity ns##_##id; \
-       REGISTER_INIT(ns, id) {} \
-       REGISTER_INIT_POST(ns, id) {} \
-       void Register_##ns##_##id() \
+ #define REGISTER(...) EVAL(OVERLOAD(REGISTER, __VA_ARGS__))
+ #define REGISTER_5(registry, ns, id, fld, inst) REGISTER_4(registry, ns##_##id, fld, inst)
+ #define REGISTER_4(registry, id, fld, inst) \
+       entity id; \
+       REGISTER_INIT(id) {} \
+       REGISTER_INIT_POST(id) {} \
+       void Register_##id() \
        { \
-               if (array##_COUNT >= array##_MAX) LOG_FATALF("Registry capacity exceeded (%s)", ftos(array##_MAX)); \
-               entity this = inst; \
-               ns##_##id = this; \
+               if (registry##_COUNT >= registry##_MAX) LOG_FATALF("Registry capacity exceeded (%s)", ftos(registry##_MAX)); \
+               entity this = id = inst; \
                this.registered_id = #id; \
-               REGISTRY_PUSH(array, fld, this); \
-               Register_##ns##_##id##_init(this); \
-               Register_##ns##_##id##_init_post(this); \
 -              this.fld = registry##_COUNT; \
 -              _R_SET(_##registry, registry##_COUNT, this); \
 -              ++registry##_COUNT; \
 -              if (!registry##_first) registry##_first = this; \
 -              if (registry##_last)   registry##_last.REGISTRY_NEXT = this; \
 -              registry##_last = this; \
++              REGISTRY_PUSH(registry, fld, this); \
+               Register_##id##_init(this); \
+               Register_##id##_init_post(this); \
        } \
-       ACCUMULATE_FUNCTION(initfunc, Register_##ns##_##id) \
-       REGISTER_INIT(ns, id)
- #define REGISTRY_PUSH(array, fld, it) do { \
-       it.fld = array##_COUNT; \
-       _##array[array##_COUNT++] = it; \
-       if (!array##_first) array##_first = it; \
-       if (array##_last)   array##_last.REGISTRY_NEXT = it; \
-       array##_last = it; \
+       ACCUMULATE_FUNCTION(Register##registry, Register_##id) \
+       REGISTER_INIT(id)
++#define REGISTRY_PUSH(registry, fld, it) do { \
++      it.fld = registry##_COUNT; \
++      _R_SET(_##registry, registry##_COUNT, it); \
++      ++registry##_COUNT; \
++      if (!registry##_first) registry##_first = it; \
++      if (registry##_last)   registry##_last.REGISTRY_NEXT = it; \
++      registry##_last = it; \
 +} while (0)
 +
 +#define REGISTRY_RESERVE(registry, fld, id, suffix) do { \
 +      entity e = new(registry_reserved); \
-       e.registered_id = #id #suffix; \
++      e.registered_id = #id "/" #suffix; \
 +      REGISTRY_PUSH(registry, fld, e); \
 +} while (0)
 +
+ #define REGISTER_INIT(id) [[accumulate]] void Register_##id##_init(entity this)
+ #define REGISTER_INIT_POST(id) [[accumulate]] void Register_##id##_init_post(entity this)
  /** internal next pointer */
  #define REGISTRY_NEXT enemy
  .entity REGISTRY_NEXT;
@@@ -20,19 -16,13 +20,19 @@@ typedef vector vectori
        #define getstat_int(id) getstati(id, 0, 24)
        #define getstat_bool(id) boolean(getstati(id))
        #define getstat_float(id) getstatf(id)
 +      #define getstat_vector(id) vec3(getstat_float(id + 0), getstat_float(id + 1), getstat_float(id + 2))
 +      #define getstat_vectori(id) vec3(getstat_int(id + 0), getstat_int(id + 1), getstat_int(id + 2))
  
        #define _STAT(id) g_stat_##id
 -      #define REGISTER_STAT(id, type) \
 -              type _STAT(id); \
 -              REGISTER(Stats, STAT, id, m_id, new(stat)) \
 +      #define REGISTER_STAT_2(id, T) \
 +              T _STAT(id); \
-               REGISTER(RegisterStats, STAT, Stats, id, m_id, new(stat)) \
++              REGISTER(Stats, STAT_##id, m_id, new(stat)) \
                { \
                        make_pure(this); \
-                               REGISTRY_RESERVE(Stats, m_id, id, _y); \
-                               REGISTRY_RESERVE(Stats, m_id, id, _z); \
 +                      if (#T == "vector" || #T == "vectori") { \
++                              REGISTRY_RESERVE(Stats, m_id, STAT_##id, y); \
++                              REGISTRY_RESERVE(Stats, m_id, STAT_##id, z); \
 +                      } \
                } \
                [[accumulate]] void stats_get() \
                { \
        const int AS_INT = 2;
        const int AS_FLOAT = 8;
  
 +      .int __stat_null;
 +      /** Prevent engine stats being sent */
 +      STATIC_INIT(stats_clear)
 +      {
 +              int r = 32;
 +              for (int i = 0, n = 256 - r; i < n; ++i) {
 +                      addstat(r + i, AS_INT, __stat_null);
 +              }
 +      }
 +
        #define _STAT(id) stat_##id
 -      #define REGISTER_STAT(id, type) \
 -              .type _STAT(id); \
 -              REGISTER(Stats, STAT, id, m_id, new(stat)) \
 +      #define REGISTER_STAT_2(id, T) \
 +              .T _STAT(id); \
-               REGISTER(RegisterStats, STAT, Stats, id, m_id, new(stat)) \
++              REGISTER(Stats, STAT_##id, m_id, new(stat)) \
                { \
                        make_pure(this); \
-                               REGISTRY_RESERVE(Stats, m_id, id, _y); \
-                               REGISTRY_RESERVE(Stats, m_id, id, _z); \
 +                      if (#T == "vector" || #T == "vectori") { \
++                              REGISTRY_RESERVE(Stats, m_id, STAT_##id, y); \
++                              REGISTRY_RESERVE(Stats, m_id, STAT_##id, z); \
 +                      } \
                } \
                [[accumulate]] void stats_add() \
                { \
 -                      addstat_##type(STAT_##id.m_id, _STAT(id)); \
 +                      addstat_##T(STAT_##id.m_id, _STAT(id)); \
                }
 +      void GlobalStats_update(entity this) {}
 +    #define REGISTER_STAT_3(x, T, expr) \
 +      REGISTER_STAT(x, T); \
 +      [[accumulate]] void GlobalStats_update(entity this) { STAT(x, this) = (expr); } \
 +      STATIC_INIT(worldstat_##x) { entity this = world; STAT(x, this) = (expr); }
  #else
 -      #define REGISTER_STAT(id, type)
 +      #define REGISTER_STAT_2(id, type)
 +    #define REGISTER_STAT_3(x, T, expr)
  #endif
  
 -const int STATS_ENGINE_RESERVE = 32 + (8 * 3); // Not sure how to handle vector stats yet, reserve them too
 +const int STATS_ENGINE_RESERVE = 32;
  
 -REGISTRY(Stats, 220 - STATS_ENGINE_RESERVE)
 +REGISTRY(Stats, 256 - STATS_ENGINE_RESERVE)
- REGISTER_REGISTRY(RegisterStats)
- REGISTRY_SORT(Stats, 0)
+ REGISTER_REGISTRY(Stats)
+ REGISTRY_SORT(Stats)
  REGISTRY_CHECK(Stats)
  STATIC_INIT(RegisterStats_renumber)
  {
Simple merge
Simple merge
Simple merge
@@@ -262,6 -259,8 +259,8 @@@ float Obituary_WeaponDeath
        return false;
  }
  
 -.int buffs; // TODO: remove
++.int buffs = _STAT(BUFFS); // TODO: remove
  void Obituary(entity attacker, entity inflictor, entity targ, int deathtype)
  {
        // Sanity check
Simple merge
Simple merge
Simple merge