]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into bones_was_here/q3compat
authorbones_was_here <bones_was_here@xa.org.au>
Sat, 6 Mar 2021 10:15:39 +0000 (20:15 +1000)
committerbones_was_here <bones_was_here@xa.org.au>
Sat, 6 Mar 2021 10:15:39 +0000 (20:15 +1000)
18 files changed:
1  2 
qcsrc/common/items/item/powerup.qh
qcsrc/common/mapobjects/trigger/jumppads.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qh
qcsrc/common/physics/player.qc
qcsrc/common/stats.qh
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/lib/spawnfunc.qh
qcsrc/server/client.qc
qcsrc/server/compat/quake3.qh
qcsrc/server/items/items.qc
qcsrc/server/items/items.qh
qcsrc/server/race.qc
qcsrc/server/teamplay.qc
qcsrc/server/teamplay.qh
qcsrc/server/world.qc
xonotic-server.cfg

index 921cc0b2f2c23e85610ae4c0f14d1202f6ee16ba,76a17af25c632e3a6ac8d7a33ab3f7b89a24c7c1..29f503bb5a69efd7f60a23ee07511a180612f662
@@@ -14,7 -14,6 +14,7 @@@ CLASS(Powerup, Pickup
      ATTRIB(Powerup, m_itemflags, int, FL_POWERUP);
      ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup));
      ATTRIB(Powerup, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_powerup));
 +    ATTRIB(Powerup, count, float);
  #endif
  ENDCLASS(Powerup)
  
@@@ -24,11 -23,15 +24,15 @@@ SOUND(Strength, Item_Sound("powerup"))
  #endif
  
  #ifdef SVQC
+ float autocvar_g_balance_powerup_strength_damage;
+ float autocvar_g_balance_powerup_strength_force;
+ float autocvar_g_balance_powerup_strength_selfdamage;
+ float autocvar_g_balance_powerup_strength_selfforce;
  float autocvar_g_balance_powerup_strength_time;
  void powerup_strength_init(Pickup this, entity item)
  {
      if(!item.strength_finished)
 -        item.strength_finished = autocvar_g_balance_powerup_strength_time;
 +        item.strength_finished = (item.count) ? item.count : autocvar_g_balance_powerup_strength_time;
  }
  #endif
  REGISTER_ITEM(Strength, Powerup) {
@@@ -62,11 -65,13 +66,13 @@@ SOUND(Shield, Item_Sound("powerup_shiel
  #endif
  
  #ifdef SVQC
+ float autocvar_g_balance_powerup_invincible_takedamage;
+ float autocvar_g_balance_powerup_invincible_takeforce = 0.33;
  float autocvar_g_balance_powerup_invincible_time;
  void powerup_shield_init(Pickup this, entity item)
  {
      if(!item.invincible_finished)
 -        item.invincible_finished = autocvar_g_balance_powerup_invincible_time;
 +        item.invincible_finished = (item.count) ? item.count : autocvar_g_balance_powerup_invincible_time;
  }
  #endif
  REGISTER_ITEM(Shield, Powerup) {
index e7ed03768cdb58c3acf59eb1448c5a10b7aef2f2,06842395bdd81f361a90d137bdc4a9e1a2b5e1be..67bb65191cb2b8643189828433dc696f2ceec9d7
@@@ -134,8 -134,11 +134,8 @@@ bool jumppad_push(entity this, entity t
                return false;
  
        vector org = targ.origin;
 -#ifdef SVQC
 -      if(autocvar_sv_q3defragcompat)
 -#elif defined(CSQC)
 -      if(STAT(Q3DEFRAGCOMPAT))
 -#endif
 +
 +      if(STAT(Q3COMPAT))
        {
                org.z += targ.mins_z;
                org.z += 1; // off by 1!
                // reset tracking of oldvelocity for impact damage (sudden velocity changes)
                targ.oldvelocity = targ.velocity;
  
-               if(this.pushltime < time)  // prevent "snorring" sound when a player hits the jumppad more than once
+               // prevent sound spam when a player hits the jumppad more than once
+               // or when a dead player gets stuck in the jumppad for some reason
+               if(this.pushltime < time && !(IS_DEAD(targ) && targ.velocity == '0 0 0'))
                {
                        // flash when activated
                        Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1);
index dbe43ace3b7ab002bf2f957aab568e8b76c1fb46,6fa6e8d054c50785a5971e02e685d6e98bacabcc..e52ab17592646bb470146eb3bf1be7a04f11b63a
@@@ -1,22 -1,10 +1,10 @@@
  #include "sv_buffs.qh"
  
  #include <common/mapobjects/target/music.qh>
+ #include <common/mutators/mutator/instagib/_mod.qh>
  #include <common/gamemodes/_mod.qh>
  #include <server/items/items.qh>
  
- void buffs_DelayedInit(entity this);
- AUTOCVAR(g_buffs, int, -1, "Enable buffs, -1: enabled but no auto location or replacing powerups, 1: enabled and can replace them");
- REGISTER_MUTATOR(buffs, autocvar_g_buffs)
- {
-       MUTATOR_ONADD
-       {
-               if(autocvar_g_buffs > 0)
-                       InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
-       }
- }
  bool buffs_BuffModel_Customize(entity this, entity client)
  {
        entity player = WaypointSprite_getviewentity(client);
@@@ -65,7 -53,7 +53,7 @@@ void buffs_BuffModel_Remove(entity play
  
  vector buff_GlowColor(entity buff)
  {
 -      //if(buff.team) { return Team_ColorRGB(buff.team); }
 +      //if(buff.team_forced) { return Team_ColorRGB(buff.team_forced); }
        return buff.m_color;
  }
  
@@@ -88,7 -76,7 +76,7 @@@ bool buff_Waypoint_visible_for_player(e
  
        if (STAT(BUFFS, view))
        {
-               return CS(view).cvar_cl_buffs_autoreplace == false || STAT(BUFFS, view) != STAT(BUFFS, this.owner);
+               return CS_CVAR(view).cvar_cl_buffs_autoreplace == false || STAT(BUFFS, view) != STAT(BUFFS, this.owner);
        }
  
        return WaypointSprite_visible_for_player(this, player, view);
@@@ -99,7 -87,7 +87,7 @@@ void buff_Waypoint_Spawn(entity e
        if(autocvar_g_buffs_waypoint_distance <= 0) return;
  
        entity buff = buff_FirstFromFlags(STAT(BUFFS, e));
 -      entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff);
 +      entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team_forced, e, buff_waypoint, true, RADARICON_Buff);
        wp.wp_extra = buff.m_id;
        WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod);
        e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
@@@ -174,7 -162,7 +162,7 @@@ void buff_Touch(entity this, entity tou
        if(!IS_PLAYER(toucher))
                return; // incase mutator changed toucher
  
 -      if((this.team && DIFF_TEAM(toucher, this))
 +      if((this.team_forced && toucher.team != this.team_forced)
        || (STAT(FROZEN, toucher))
        || (toucher.vehicle)
        || (time < PS(toucher).buff_shield)
  
        if (STAT(BUFFS, toucher))
        {
-               if (CS(toucher).cvar_cl_buffs_autoreplace && STAT(BUFFS, toucher) != STAT(BUFFS, this))
+               if (CS_CVAR(toucher).cvar_cl_buffs_autoreplace && STAT(BUFFS, toucher) != STAT(BUFFS, this))
                {
                        // TODO: lost-gained notification for this case
                        int buffid = buff_FirstFromFlags(STAT(BUFFS, toucher)).m_id;
@@@ -307,7 -295,7 +295,7 @@@ void buff_Think(entity this
  
        if(this.buff_active)
        {
 -              if(this.team && !this.buff_waypoint)
 +              if(this.team_forced && !this.buff_waypoint)
                        buff_Waypoint_Spawn(this);
  
                if(this.lifetime && time >= this.lifetime)
@@@ -341,7 -329,7 +329,7 @@@ void buff_Reset(entity this
  bool buff_Customize(entity this, entity client)
  {
        entity player = WaypointSprite_getviewentity(client);
 -      if(!this.buff_active || (this.team && DIFF_TEAM(player, this)))
 +      if(!this.buff_active || (this.team_forced && player.team != this.team_forced))
        {
                this.alpha = 0.3;
                if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); }
@@@ -367,6 -355,8 +355,6 @@@ void buff_Init(entity this
  {
        if(!cvar("g_buffs")) { delete(this); return; }
  
 -      if(!teamplay && this.team) { this.team = 0; }
 -
        entity buff = buff_FirstFromFlags(STAT(BUFFS, this));
  
        if(!STAT(BUFFS, this) || !buff_Available(buff))
  
  void buff_Init_Compat(entity ent, entity replacement)
  {
 -      if (ent.spawnflags & 2)
 -              ent.team = NUM_TEAM_1;
 -      else if (ent.spawnflags & 4)
 -              ent.team = NUM_TEAM_2;
 +      if (teamplay)
 +      {
 +              if (ent.spawnflags & 2)
 +                      ent.team_forced = NUM_TEAM_1;
 +              else if (ent.spawnflags & 4)
 +                      ent.team_forced = NUM_TEAM_2;
 +      }
  
        STAT(BUFFS, ent) = replacement.m_itemid;
  
@@@ -444,25 -431,6 +432,6 @@@ void buff_Vengeance_DelayedDamage(entit
        return;
  }
  
- // note: only really useful in teamplay
- void buff_Medic_Heal(entity this)
- {
-       FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range),
-       {
-               if (DIFF_TEAM(it, this))
-               {
-                       continue;
-               }
-               float hp = GetResource(it, RES_HEALTH);
-               if(hp >= autocvar_g_balance_health_regenstable)
-               {
-                       continue;
-               }
-               Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
-               SetResource(it, RES_HEALTH, bound(0, hp + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable));
-       });
- }
  float buff_Inferno_CalculateTime(float damg, float offset_x, float offset_y, float intersect_x, float intersect_y, float base)
  {
        return offset_y + (intersect_y - offset_y) * logn(((damg - offset_x) * ((base - 1) / intersect_x)) + 1, base);
@@@ -505,7 -473,7 +474,7 @@@ MUTATOR_HOOKFUNCTION(buffs, Damage_Calc
        if(frag_attacker != frag_target)
        if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
        {
-               entity dmgent = new(dmgent);
+               entity dmgent = new_pure(dmgent);
  
                dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
                dmgent.enemy = frag_attacker;
                Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id);
        }
  
-       // this... is ridiculous (TODO: fix!)
-       if(STAT(BUFFS, frag_attacker) & BUFF_VAMPIRE.m_itemid)
-       if(!frag_target.vehicle)
-       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
-       if(!IS_DEAD(frag_target))
-       if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target))
-       if(frag_attacker != frag_target)
-       if(!STAT(FROZEN, frag_target))
-       if(frag_target.takedamage)
-       if(DIFF_TEAM(frag_attacker, frag_target))
-       {
-               float amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal,
-                       GetResource(frag_target, RES_HEALTH));
-               GiveResourceWithLimit(frag_attacker, RES_HEALTH, amount, g_pickup_healthsmall_max);
-               if (GetResource(frag_target, RES_ARMOR))
-               {
-                       amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal,
-                               GetResource(frag_target, RES_ARMOR));
-                       GiveResourceWithLimit(frag_attacker, RES_ARMOR, amount, g_pickup_armorsmall_max);
-               }
-       }
        M_ARGV(4, float) = frag_damage;
        M_ARGV(6, vector) = frag_force;
  }
  
+ MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
+ {
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       if(!(STAT(BUFFS, frag_attacker) & BUFF_VAMPIRE.m_itemid))
+               return;
+       float health_take = bound(0, M_ARGV(4, float), GetResource(frag_target, RES_HEALTH));
+       if(time >= frag_target.spawnshieldtime &&
+               frag_target != frag_attacker &&
+               IS_PLAYER(frag_attacker) &&
+               !IS_DEAD(frag_target) && !STAT(FROZEN, frag_target))
+       {
+               GiveResource(frag_attacker, RES_HEALTH,
+                       autocvar_g_buffs_vampire_damage_steal * health_take);
+       }
+ }
  MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn)
  {
        entity player = M_ARGV(0, entity);
@@@ -904,13 -868,6 +869,6 @@@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreTh
        if((STAT(BUFFS, player) & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid))
                player.alpha = ((autocvar_g_buffs_invisible_alpha) ? autocvar_g_buffs_invisible_alpha : -1); // powerups reset alpha, so we must enforce this (TODO)
  
-       if(STAT(BUFFS, player) & BUFF_MEDIC.m_itemid)
-       if(teamplay && time >= player.buff_medic_healtime)
-       {
-               buff_Medic_Heal(player);
-               player.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay;
-       }
  #define BUFF_ONADD(b) if ( (STAT(BUFFS, player) & (b).m_itemid) && !(player.oldbuffs & (b).m_itemid))
  #define BUFF_ONREM(b) if (!(STAT(BUFFS, player) & (b).m_itemid) &&  (player.oldbuffs & (b).m_itemid))
  
@@@ -1070,3 -1027,10 +1028,10 @@@ void buffs_DelayedInit(entity this
                }
        }
  }
+ void buffs_Initialize()
+ {
+       // if buffs are above 0, allow random spawning
+       if(autocvar_g_buffs > 0 && autocvar_g_buffs_spawn_count > 0)
+               InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
+ }
index b3a8ddc649f865d67ccd7d17bff9dbad42f28e47,0a39b35eb42a0a486d1fb7b2cf4e0a8372c5dd76..d198898bdceba58b6dcfad23da3b0ced6a1590e4
@@@ -1,8 -1,20 +1,20 @@@
  #pragma once
  
+ #include <common/mutators/base.qh>
  #include "buffs.qh"
  
- #include "../instagib/_mod.qh"
+ void buffs_Initialize();
+ AUTOCVAR(g_buffs, int, -1, "Enable buffs, -1: enabled but no auto location or replacing powerups, 1: enabled and can replace them");
+ REGISTER_MUTATOR(buffs, autocvar_g_buffs)
+ {
+       MUTATOR_ONADD
+       {
+               buffs_Initialize();
+       }
+ }
  
  bool  autocvar_g_buffs_effects;
  float autocvar_g_buffs_waypoint_distance;
@@@ -24,9 -36,6 +36,6 @@@ float autocvar_g_buffs_medic_survive_he
  float autocvar_g_buffs_medic_rot;
  float autocvar_g_buffs_medic_max;
  float autocvar_g_buffs_medic_regen;
- float autocvar_g_buffs_medic_heal_amount = 15;
- float autocvar_g_buffs_medic_heal_delay = 1;
- float autocvar_g_buffs_medic_heal_range = 400;
  float autocvar_g_buffs_vengeance_damage_multiplier;
  float autocvar_g_buffs_bash_force;
  float autocvar_g_buffs_bash_force_self;
@@@ -58,8 -67,6 +67,6 @@@ float autocvar_g_buffs_luck_damagemulti
  .int buff_ammo_prev_clipload;
  // invisible
  .float buff_invisible_prev_alpha;
- // medic
- .float buff_medic_healtime;
  // disability
  .float buff_disability_time;
  .float buff_disability_effect_time;
@@@ -69,9 -76,9 +76,9 @@@
  .float buff_effect_delay;
  
  // buff definitions
 -.float buff_active;
 +.bool buff_active;
  .float buff_activetime;
 -.float buff_activetime_updated;
 +.bool buff_activetime_updated;
  .entity buff_waypoint;
  .int oldbuffs; // for updating effects
  .float buff_shield; // delay for players to keep them from spamming buff pickups
index 93076b4634b439e373a38533a383021705c84f14,1c55d3d14b58e13a02369f0ba49b9f4e9e63d2ec..d1ceef8dced15a8fc803a5c56d877961450b6d14
@@@ -19,9 -19,9 +19,9 @@@ float Physics_ClientOption(entity this
        if(!autocvar_g_physics_clientselect)
                return defaultval;
  
-       if(IS_REAL_CLIENT(this) && Physics_Valid(CS(this).cvar_cl_physics))
+       if(IS_REAL_CLIENT(this) && Physics_Valid(CS_CVAR(this).cvar_cl_physics))
        {
-               string s = strcat("g_physics_", CS(this).cvar_cl_physics, "_", option);
+               string s = strcat("g_physics_", CS_CVAR(this).cvar_cl_physics, "_", option);
                if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
                        return cvar(s);
        }
@@@ -54,13 -54,13 +54,13 @@@ void Physics_UpdateStats(entity this
              : 0;
            STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw) * maxspd_mod;
          }
 -      bool q3dfcompat = autocvar_sv_q3defragcompat && autocvar_sv_q3defragcompat_changehitbox; // NOTE: these hitboxes are off by 1 due to engine differences
 -      STAT(PL_MIN, this) = (q3dfcompat) ? '-15 -15 -20' : autocvar_sv_player_mins;
 -      STAT(PL_MAX, this) = (q3dfcompat) ? '15 15 36' : autocvar_sv_player_maxs;
 -      STAT(PL_VIEW_OFS, this) = (q3dfcompat) ? '0 0 30' : autocvar_sv_player_viewoffset;
 -      STAT(PL_CROUCH_MIN, this) = (q3dfcompat) ? '-15 -15 -20' : autocvar_sv_player_crouch_mins;
 -      STAT(PL_CROUCH_MAX, this) = (q3dfcompat) ? '15 15 20' : autocvar_sv_player_crouch_maxs;
 -      STAT(PL_CROUCH_VIEW_OFS, this) = (q3dfcompat) ? '0 0 16' : autocvar_sv_player_crouch_viewoffset;
 +      bool q3hb = q3compat && autocvar_sv_q3compat_changehitbox;
 +      STAT(PL_MIN, this) = (q3hb) ? '-15 -15 -20' : autocvar_sv_player_mins;
 +      STAT(PL_MAX, this) = (q3hb) ? '15 15 36' : autocvar_sv_player_maxs;
 +      STAT(PL_VIEW_OFS, this) = (q3hb) ? '0 0 30' : autocvar_sv_player_viewoffset;
 +      STAT(PL_CROUCH_MIN, this) = (q3hb) ? '-15 -15 -20' : autocvar_sv_player_crouch_mins;
 +      STAT(PL_CROUCH_MAX, this) = (q3hb) ? '15 15 20' : autocvar_sv_player_crouch_maxs;
 +      STAT(PL_CROUCH_VIEW_OFS, this) = (q3hb) ? '0 0 16' : autocvar_sv_player_crouch_viewoffset;
  
        // old stats
        // fix some new settings
@@@ -457,7 -457,7 +457,7 @@@ void CheckWaterJump(entity this
  
  
  #ifdef SVQC
-       #define JETPACK_JUMP(s) CS(s).cvar_cl_jetpack_jump
+       #define JETPACK_JUMP(s) CS_CVAR(s).cvar_cl_jetpack_jump
  #elif defined(CSQC)
        float autocvar_cl_jetpack_jump;
        #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump
diff --combined qcsrc/common/stats.qh
index ffc0cfacfbf14acb31196cac9e9c3996c1d5e8d2,e5621c29ec97ecc80a2ef740ba4061aaec48aa77..c709da51a769d5564386fbca0d553085980a46fc
@@@ -1,10 -1,16 +1,17 @@@
  #pragma once
  
+ // you're next
  #ifdef SVQC
- #include <server/autocvars.qh>
  #include <server/client.qh>
 +#include <server/compat/quake3.qh>
+ #include <server/main.qh>
+ #include <common/gamemodes/sv_rules.qh>
+ #include <common/mapobjects/teleporters.qh>
  #include <common/mapobjects/trigger/secret.qh>
+ #include <common/mutators/mutator/doublejump/doublejump.qh>
+ #include <common/mutators/mutator/itemstime/itemstime.qh>
+ #include <common/physics/player.qh>
  #endif
  
  // Full list of all stat constants, included in a single location for easy reference
@@@ -69,6 -75,11 +76,11 @@@ float game_starttime; //point in time w
  float round_starttime; //point in time when the countdown to round start is over
  bool autocvar_g_allow_oldvortexbeam;
  int autocvar_leadlimit;
+ // TODO: world.qh can't be included here due to circular includes!
+ #define autocvar_fraglimit cvar("fraglimit")
+ #define autocvar_fraglimit_override cvar("fraglimit_override")
+ #define autocvar_timelimit cvar("timelimit")
+ #define autocvar_timelimit_override cvar("timelimit_override")
  #endif
  REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
  REGISTER_STAT(GAME_STOPPED, int, game_stopped)
@@@ -129,7 -140,7 +141,7 @@@ REGISTER_STAT(VEIL_ORB, float
  REGISTER_STAT(VEIL_ORB_ALPHA, float)
  
  #ifdef SVQC
- float autocvar_sv_showfps = 5;
+ float autocvar_sv_showfps = 0;
  #endif
  REGISTER_STAT(SHOWFPS, float, autocvar_sv_showfps)
  
@@@ -202,6 -213,7 +214,7 @@@ int autocvar_sv_gameplayfix_grenadeboun
  int autocvar_sv_gameplayfix_noairborncorpse = 1;
  int autocvar_sv_gameplayfix_noairborncorpse_allowsuspendeditems = 1;
  int autocvar_sv_gameplayfix_delayprojectiles = 0;
+ bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag = true;
  #endif
  REGISTER_STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, int, autocvar_sv_gameplayfix_downtracesupportsongroundflag)
  REGISTER_STAT(GAMEPLAYFIX_EASIERWATERJUMP, int, autocvar_sv_gameplayfix_easierwaterjump)
@@@ -220,6 -232,10 +233,10 @@@ REGISTER_STAT(GAMEPLAYFIX_DELAYPROJECTI
  REGISTER_STAT(MOVEVARS_JUMPSTEP, int, cvar("sv_jumpstep"))
  REGISTER_STAT(NOSTEP, int, cvar("sv_nostep"))
  
+ #ifdef SVQC
+ float autocvar_sv_friction_on_land;
+ var float autocvar_sv_friction_slick = 0.5;
+ #endif
  REGISTER_STAT(MOVEVARS_FRICTION, float)
  REGISTER_STAT(MOVEVARS_FRICTION_SLICK, float, autocvar_sv_friction_slick)
  REGISTER_STAT(MOVEVARS_FRICTION_ONLAND, float, autocvar_sv_friction_on_land)
@@@ -273,6 -289,15 +290,15 @@@ REGISTER_STAT(DODGING_CLIENTSELECT, boo
  REGISTER_STAT(DODGING_FROZEN, int, autocvar_sv_dodging_frozen)
  REGISTER_STAT(DODGING_TIMEOUT, float)
  
+ #ifdef SVQC
+ float autocvar_g_jetpack_acceleration_side;
+ float autocvar_g_jetpack_acceleration_up;
+ float autocvar_g_jetpack_antigravity;
+ int autocvar_g_jetpack_fuel;
+ float autocvar_g_jetpack_maxspeed_side;
+ float autocvar_g_jetpack_maxspeed_up;
+ float autocvar_g_jetpack_reverse_thrust;
+ #endif
  REGISTER_STAT(JETPACK_ACCEL_SIDE, float, autocvar_g_jetpack_acceleration_side)
  REGISTER_STAT(JETPACK_ACCEL_UP, float, autocvar_g_jetpack_acceleration_up)
  REGISTER_STAT(JETPACK_ANTIGRAVITY, float, autocvar_g_jetpack_antigravity)
@@@ -315,6 -340,9 +341,9 @@@ REGISTER_STAT(DOM_PPS_BLUE, float
  REGISTER_STAT(DOM_PPS_YELLOW, float)
  REGISTER_STAT(DOM_PPS_PINK, float)
  
+ #ifdef SVQC
+ float autocvar_g_teleport_maxspeed;
+ #endif
  REGISTER_STAT(TELEPORT_MAXSPEED, float, autocvar_g_teleport_maxspeed)
  REGISTER_STAT(TELEPORT_TELEFRAG_AVOID, int, autocvar_g_telefrags_avoid)
  
@@@ -327,7 -355,10 +356,7 @@@ bool autocvar_sv_slick_applygravity
  #endif
  REGISTER_STAT(SLICK_APPLYGRAVITY, bool, autocvar_sv_slick_applygravity)
  
 -#ifdef SVQC
 -bool autocvar_sv_q3defragcompat;
 -#endif
 -REGISTER_STAT(Q3DEFRAGCOMPAT, bool, autocvar_sv_q3defragcompat)
 +REGISTER_STAT(Q3COMPAT, int, q3compat)
  
  #ifdef SVQC
  #include "physics/movetypes/movetypes.qh"
@@@ -348,7 -379,11 +377,11 @@@ REGISTER_STAT(MOVEVARS_AIRSTRAFEACCEL_Q
  REGISTER_STAT(MOVEVARS_AIRCONTROL_POWER, float)
  REGISTER_STAT(MOVEVARS_AIRCONTROL_BACKWARDS, bool)
  REGISTER_STAT(MOVEVARS_AIRCONTROL_SIDEWARDS, bool)
- noref bool autocvar_sv_gameplayfix_nogravityonground = true;
+ #ifdef SVQC
+ float autocvar_sv_gameplayfix_q2airaccelerate = 1;
+ bool autocvar_sv_gameplayfix_nogravityonground = true;
+ bool autocvar_sv_gameplayfix_gravityunaffectedbyticrate = true;
+ #endif
  REGISTER_STAT(MOVEFLAGS, int, MOVEFLAG_VALID
                                | (autocvar_sv_gameplayfix_q2airaccelerate ? MOVEFLAG_Q2AIRACCELERATE : 0)
                                | (autocvar_sv_gameplayfix_nogravityonground ? MOVEFLAG_NOGRAVITYONGROUND : 0)
@@@ -369,6 -404,8 +402,8 @@@ REGISTER_STAT(TIMELIMIT, float, autocva
  REGISTER_STAT(WARMUP_TIMELIMIT, float, warmup_limit)
  #ifdef SVQC
  float autocvar_sv_wallfriction;
+ #define autocvar_sv_gravity cvar("sv_gravity")
+ float autocvar_sv_stepheight;
  #endif
  REGISTER_STAT(MOVEVARS_WALLFRICTION, int, autocvar_sv_wallfriction)
  REGISTER_STAT(MOVEVARS_TICRATE, float, autocvar_sys_ticrate)
index 36f555be3709cd9e021517ce2feef4628e3bd653,11ac0641a2c18be823715cd6c17df3d8f975bbb3..5d49ed0cf42503173e29e466ce3f2e2968157424
@@@ -3,7 -3,6 +3,6 @@@
  #define WEAPONS_ALL_C
  
  #if defined(CSQC)
-       #include <client/autocvars.qh>
        #include <client/main.qh>
        #include <common/constants.qh>
        #include <common/deathtypes/all.qh>
@@@ -38,7 -37,6 +37,6 @@@
        #include <lib/warpzone/common.qh>
        #include <lib/warpzone/server.qh>
        #include <lib/warpzone/util_server.qh>
-       #include <server/autocvars.qh>
        #include <server/command/_mod.qh>
        #include <server/hook.qh>
        #include <server/items/spawning.qh>
@@@ -232,23 -230,6 +230,23 @@@ string GetAmmoName(int ammotype
        }
  }
  
 +entity GetAmmoItem(int ammotype)
 +{
 +      switch (ammotype)
 +      {
 +              case RES_SHELLS:  return ITEM_Shells;
 +              case RES_BULLETS: return ITEM_Bullets;
 +              case RES_ROCKETS: return ITEM_Rockets;
 +              case RES_CELLS:   return ITEM_Cells;
 +              case RES_PLASMA:  return ITEM_Plasma;
 +              case RES_FUEL:    return ITEM_JetpackFuel;
 +      }
 +      LOG_WARNF("Invalid ammo type %d ", ammotype);
 +      return NULL;
 +      // WEAPONTODO: use this generic func to reduce duplication ?
 +      // GetAmmoPicture  GetAmmoName  notif_arg_item_wepammo  ammo_pickupevalfunc ?
 +}
 +
  #ifdef CSQC
  int GetAmmoTypeFromNum(int i)
  {
@@@ -673,8 -654,8 +671,8 @@@ CLIENT_COMMAND(weapon_find, "Show spaw
                        LOG_INFOF("Incorrect parameters for ^2%s^7", argv(0));
                case CMD_REQUEST_USAGE:
                {
-                       LOG_HELP("Usage:^3 cl_cmd weapon_find weapon");
-                       LOG_HELP("  Where 'weapon' is the lowercase weapon name, 'all' or 'unowned'.");
+                       LOG_HELP("Usage:^3 cl_cmd weapon_find <weapon>");
+                       LOG_HELP("  Where <weapon> is the lowercase weapon name, 'all' or 'unowned'.");
                        return;
                }
        }
index 6191cda8552c68a0895e62010c15dccf9de98e5e,5444980ca89ba2efc129dcfc25141a8503e681aa..6b357968a7c8ac993ac234b82b622a3d55314d39
@@@ -3,6 -3,18 +3,6 @@@
  REGISTER_NET_TEMP(TE_CSQC_SHOCKWAVEPARTICLE)
  
  #ifdef SVQC
 -// enable when shockwave replaces shotgun
 -#if 0
 -METHOD(Shockwave, m_spawnfunc_hookreplace, Weapon(Shockwave this, entity e))
 -{
 -      //if(autocvar_sv_q3acompat_machineshockwaveswap) // WEAPONTODO
 -      if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
 -      {
 -              return WEP_MACHINEGUN;
 -      }
 -      return this;
 -}
 -#endif
  
  const float MAX_SHOCKWAVE_HITS = 10;
  //#define DEBUG_SHOCKWAVE
@@@ -299,7 -311,7 +299,7 @@@ void W_Shockwave_Attack(Weapon thiswep
        );
  
        float lag = ((IS_REAL_CLIENT(actor)) ? ANTILAG_LATENCY(actor) : 0);
-       bool noantilag = ((IS_CLIENT(actor)) ? CS(actor).cvar_cl_noantilag : false);
+       bool noantilag = ((IS_CLIENT(actor)) ? CS_CVAR(actor).cvar_cl_noantilag : false);
        if(lag < 0.001)
                lag = 0;
        if(autocvar_g_antilag == 0 || noantilag)
diff --combined qcsrc/lib/spawnfunc.qh
index 54732766b4a3df0dad3024c210c8b6f141eb828b,d254364cefe2d38a4ecd228183d8d65a3043508d..679ddda67f427c586f401e60b0417dcf0e332ef9
@@@ -7,8 -7,7 +7,8 @@@
  noref bool require_spawnfunc_prefix;
  .bool spawnfunc_checked;
  /** Not for production use, provides access to a dump of the entity's fields when it is parsed from map data */
 -//noref string __fullspawndata;
 +noref string __fullspawndata;
 +.string fullspawndata;
  
  // Optional type checking; increases compile time too much to be enabled by default
  #if 0
                FIELD_SCALAR(fld, noise2) \
                FIELD_SCALAR(fld, noise3) \
                FIELD_SCALAR(fld, noise) \
 +              FIELD_SCALAR(fld, notcpm) \
 +              FIELD_SCALAR(fld, notfree) \
 +              FIELD_SCALAR(fld, notta) \
 +              FIELD_SCALAR(fld, notteam) \
 +              FIELD_SCALAR(fld, notvq3) \
                FIELD_SCALAR(fld, phase) \
                FIELD_SCALAR(fld, platmovetype) \
                FIELD_SCALAR(fld, race_place) \
@@@ -286,14 -280,12 +286,17 @@@ noref bool __spawnfunc_first
                this.classname = #id; \
                if (!this.spawnfunc_checked) { \
                        _checkWhitelisted(this, #id); \
 +                      if (__fullspawndata) { \
 +                              /* not supported in old DP */ \
 +                              /* must be read inside the real spawnfunc */ \
 +                              this.fullspawndata = __fullspawndata; \
 +                      } \
                        this.spawnfunc_checked = true; \
                        if (this) { \
                                /* not worldspawn, delay spawn */ \
+                               /* clear some dangerous fields (TODO: properly support these in the map!) */ \
+                               this.think = func_null; \
+                               this.nextthink = 0; \
                                __spawnfunc_defer(this, __spawnfunc_##id); \
                        } else { \
                                /* world might not be "worldspawn" */ \
diff --combined qcsrc/server/client.qc
index 391ca4a7f6895b6765430205679e4a3302a850c1,74592b62cb0972217162c135cc4d68b4fc3efa72..613f68fd07636bf2a9955c6b2a796547de4d53d2
@@@ -21,6 -21,7 +21,7 @@@
  #include <common/minigames/sv_minigames.qh>
  #include <common/monsters/sv_monsters.qh>
  #include <common/mutators/mutator/instagib/sv_instagib.qh>
+ #include <common/mutators/mutator/nades/nades.qh>
  #include <common/mutators/mutator/overkill/oknex.qh>
  #include <common/mutators/mutator/waypoints/all.qh>
  #include <common/net_linked.qh>
@@@ -42,6 -43,7 +43,7 @@@
  #include <server/anticheat.qh>
  #include <server/antilag.qh>
  #include <server/bot/api.qh>
+ #include <server/bot/default/cvars.qh>
  #include <server/campaign.qh>
  #include <server/chat.qh>
  #include <server/cheats.qh>
@@@ -71,6 -73,7 +73,7 @@@
  #include <server/weapons/common.qh>
  #include <server/weapons/hitplot.qh>
  #include <server/weapons/selection.qh>
+ #include <server/weapons/tracing.qh>
  #include <server/weapons/weaponsystem.qh>
  #include <server/world.qh>
  
@@@ -196,19 -199,19 +199,19 @@@ string CheckPlayerModel(string plyermod
                FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel"));
        }
        // only in right path
-       if( substring(plyermodel,0,14) != "models/player/")
+       if(substring(plyermodel, 0, 14) != "models/player/")
                return FallbackPlayerModel;
        // only good file extensions
-       if(substring(plyermodel,-4,4) != ".zym")
-       if(substring(plyermodel,-4,4) != ".dpm")
-       if(substring(plyermodel,-4,4) != ".iqm")
-       if(substring(plyermodel,-4,4) != ".md3")
-       if(substring(plyermodel,-4,4) != ".psk")
+       if(substring(plyermodel, -4, 4) != ".iqm"
+               && substring(plyermodel, -4, 4) != ".zym"
+               && substring(plyermodel, -4, 4) != ".dpm"
+               && substring(plyermodel, -4, 4) != ".md3"
+               && substring(plyermodel, -4, 4) != ".psk")
+       {
                return FallbackPlayerModel;
+       }
        // forbid the LOD models
-       if(substring(plyermodel, -9,5) == "_lod1")
-               return FallbackPlayerModel;
-       if(substring(plyermodel, -9,5) == "_lod2")
+       if(substring(plyermodel, -9, 5) == "_lod1" || substring(plyermodel, -9, 5) == "_lod2")
                return FallbackPlayerModel;
        if(plyermodel != strtolower(plyermodel))
                return FallbackPlayerModel;
@@@ -250,6 -253,7 +253,7 @@@ void PutObserverInServer(entity this
                        if (vote_called) { VoteCount(false); }
                        ReadyCount();
                }
+               entcs_update_players(this);
        }
  
        entity spot = SelectSpawnPoint(this, true);
@@@ -609,7 -613,8 +613,7 @@@ void PutPlayerInServer(entity this
        this.respawn_flags = 0;
        this.respawn_time = 0;
        STAT(RESPAWN_TIME, this) = 0;
 -      bool q3dfcompat = autocvar_sv_q3defragcompat && autocvar_sv_q3defragcompat_changehitbox;
 -      this.scale = ((q3dfcompat) ? 0.9 : autocvar_sv_player_scale);
 +      this.scale = ((q3compat && autocvar_sv_q3compat_changehitbox) ? 0.9 : autocvar_sv_player_scale);
        this.fade_time = 0;
        this.pain_finished = 0;
        this.pushltime = 0;
  
        if (CS(this).impulse) ImpulseCommands(this);
  
-       W_ResetGunAlign(this, CS(this).cvar_cl_gunalign);
+       W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
        {
                .entity weaponentity = weaponentities[slot];
@@@ -1196,6 -1201,8 +1200,8 @@@ Called when a client disconnects from t
  =============
  */
  .entity chatbubbleentity;
+ void player_powerups_remove_all(entity this);
  void ClientDisconnect(entity this)
  {
        assert(IS_CLIENT(this), return);
        MUTATOR_CALLHOOK(ClientDisconnect, this);
  
        strfree(CS(this).netname_previous); // needs to be before the CS entity is removed!
-       strfree(CS(this).weaponorder_byimpulse);
+       strfree(CS_CVAR(this).weaponorder_byimpulse);
        ClientState_detach(this);
  
        Portal_ClearAll(this);
        ReadyCount();
        if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
  
+       player_powerups_remove_all(this); // stop powerup sound
        ONREMOVE(this);
  }
  
@@@ -1440,6 -1449,20 +1448,20 @@@ void play_countdown(entity this, float 
                                sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
  }
  
+ void player_powerups_remove_all(entity this)
+ {
+       if (this.items & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON))
+       {
+               // don't play the poweroff sound when the game restarts or the player disconnects
+               if (time > game_starttime + 1 && IS_CLIENT(this))
+                       sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
+               stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+               this.items &= ~ITEM_Strength.m_itemid;
+               this.items &= ~ITEM_Shield.m_itemid;
+               this.items -= (this.items & IT_SUPERWEAPON);
+       }
+ }
  void player_powerups(entity this)
  {
        if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !game_stopped)
        this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
  
        if (IS_DEAD(this))
-       {
-               if (this.items & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON))
-               {
-                       sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
-                       stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
-                       this.items &= ~ITEM_Strength.m_itemid;
-                       this.items &= ~ITEM_Shield.m_itemid;
-                       this.items -= (this.items & IT_SUPERWEAPON);
-               }
-       }
+               player_powerups_remove_all(this);
  
        if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
                return;
@@@ -1699,6 -1713,14 +1712,14 @@@ void SetZoomState(entity this, float ne
  void GetPressedKeys(entity this)
  {
        MUTATOR_CALLHOOK(GetPressedKeys, this);
+       if (game_stopped)
+       {
+               CS(this).pressedkeys = 0;
+               STAT(PRESSED_KEYS, this) = 0;
+               return;
+       }
+       // NOTE: GetPressedKeys and PM_dodging_GetPressedKeys use similar code
        int keys = STAT(PRESSED_KEYS, this);
        keys = BITSET(keys, KEY_FORWARD,        CS(this).movement.x > 0);
        keys = BITSET(keys, KEY_BACKWARD,       CS(this).movement.x < 0);
@@@ -2256,7 -2278,7 +2277,7 @@@ bool PlayerThink(entity this
        bool dualwielding = W_DualWielding(this);
        if(this.dualwielding_prev != dualwielding)
        {
-               W_ResetGunAlign(this, CS(this).cvar_cl_gunalign);
+               W_ResetGunAlign(this, CS_CVAR(this).cvar_cl_gunalign);
                this.dualwielding_prev = dualwielding;
        }
  
  }
  
  .bool would_spectate;
+ // merged SpectatorThink and ObserverThink (old names are here so you can grep for them)
  void ObserverOrSpectatorThink(entity this)
  {
        bool is_spec = IS_SPEC(this);
                        }
                }
                else {
-                       int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? CS(this).cvar_cl_clippedspectating : !CS(this).cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+                       int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? CS_CVAR(this).cvar_cl_clippedspectating : !CS_CVAR(this).cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
                        set_movetype(this, preferred_movetype);
                }
-       } else {
+       } else { // jump pressed
                if ((is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)))
                        || (!is_spec && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this)))) {
                        this.flags |= FL_JUMPRELEASED;
@@@ -2426,8 -2449,8 +2448,8 @@@ Called every frame for each client befo
  .float last_vehiclecheck;
  void PlayerPreThink (entity this)
  {
-       STAT(GUNALIGN, this) = CS(this).cvar_cl_gunalign; // TODO
-       STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS(this).cvar_cl_movement_track_canjump;
+       STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO
+       STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump;
  
        WarpZone_PlayerPhysics_FixVAngle(this);
  
        }
  
        // version nagging
-       if (CS(this).version_nagtime && CS(this).cvar_g_xonoticversion && time > CS(this).version_nagtime) {
+       if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime) {
          CS(this).version_nagtime = 0;
-         if (strstrofs(CS(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS(this).cvar_g_xonoticversion, "autobuild", 0) >= 0) {
+         if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0) {
              // git client
          } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) {
              // git server
-             Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS(this).cvar_g_xonoticversion);
+             Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
          } else {
-             int r = vercmp(CS(this).cvar_g_xonoticversion, autocvar_g_xonoticversion);
+             int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion);
              if (r < 0) { // old client
-                 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS(this).cvar_g_xonoticversion);
+                 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
              } else if (r > 0) { // old server
-                 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS(this).cvar_g_xonoticversion);
+                 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion);
              }
          }
      }
                this.max_armorvalue = 0;
        }
  
-       if (frametime && IS_PLAYER(this))
+       if (frametime && IS_PLAYER(this) && time >= game_starttime)
        {
                if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
                {
                this.last_vehiclecheck = time + 1;
        }
  
-       if(!CS(this).cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
+       if(!CS_CVAR(this).cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
        {
                if(PHYS_INPUT_BUTTON_USE(this) && !CS(this).usekeypressed)
                        PlayerUseKey(this);
        }
  
        target_voicescript_next(this);
-       // WEAPONTODO: Move into weaponsystem somehow
-       // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
-       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-       {
-               .entity weaponentity = weaponentities[slot];
-               if(this.(weaponentity).m_weapon == WEP_Null)
-                       this.(weaponentity).clip_load = this.(weaponentity).clip_size = 0;
-       }
  }
  
  void DrownPlayer(entity this)
@@@ -2752,12 -2766,12 +2765,12 @@@ void PlayerPostThink (entity this
                DrownPlayer(this);
                UpdateChatBubble(this);
                if (CS(this).impulse) ImpulseCommands(this);
+               GetPressedKeys(this);
                if (game_stopped)
                {
                        CSQCMODEL_AUTOUPDATE(this);
                        return;
                }
-               GetPressedKeys(this);
        }
        else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this))
        {
index 029485ab03f969fecd2d45c033a309dc2b0f5092,a9b90abadc44b1ed9bf0ca7d8d0b0d27cc9f5888..d2c547b6f6e9d3bacce6388c7f5eed703c642be1
@@@ -1,38 -1,8 +1,40 @@@
  #pragma once
  
 -bool autocvar_sv_q3acompat_machineshotgunswap;
 -bool autocvar_sv_q3defragcompat_changehitbox = false;
 +int q3compat = 0;
 +#define Q3COMPAT_ARENA BIT(0)
 +#define Q3COMPAT_DEFI BIT(1)
 +
++bool autocvar_sv_q3compat_changehitbox;
  bool DoesQ3ARemoveThisEntity(entity this);
 +int GetAmmoConsumptionQ3(string netname);
  
  .int fragsfilter_cnt;
 +
 +/* We tell the ammo spawnfunc which weapon will use the ammo so it can
 + * calculate the amount required for the number of shots in the count field,
 + * and so the type can be looked up rather than specified in quake3.qc
 + */
 +// Ammo only, unconditional
 +#define SPAWNFUNC_Q3AMMO(ammo_classname, xonwep) \
 +      spawnfunc(ammo_classname) \
 +      { \
 +              if(this.count && xonwep.ammo_type) \
 +                      SetResource(this, xonwep.ammo_type, this.count * GetAmmoConsumptionQ3(xonwep.netname)); \
 +      SPAWNFUNC_BODY(GetAmmoItem(xonwep.ammo_type)) \
 +      }
 +
 +// Ammo only, conditional
 +#define SPAWNFUNC_Q3AMMO_COND(ammo_classname, cond, xonwep1, xonwep0) \
 +      SPAWNFUNC_Q3AMMO(ammo_classname, (cond ? xonwep1 : xonwep0))
 +
 +// Weapon & ammo, unconditional
 +#define SPAWNFUNC_Q3(weapon_classname, ammo_classname, xonwep) \
 +      SPAWNFUNC_WEAPON(weapon_classname, xonwep) \
 +      SPAWNFUNC_Q3AMMO(ammo_classname, xonwep)
 +
 +// Weapon & ammo, conditional
 +#define SPAWNFUNC_Q3_COND(weapon_classname, ammo_classname, cond, xonwep1, xonwep0) \
 +      SPAWNFUNC_WEAPON_COND(weapon_classname, cond, xonwep1, xonwep0) \
 +      SPAWNFUNC_Q3AMMO_COND(ammo_classname, cond, xonwep1, xonwep0)
 +
index 68d19f373c6c0d4bb9eff91a6d40f3378525c23b,403125eea51b7ef9c41bcbfdea6aa6c654405991..f5b15786f97dd35fe3e30d22d558cd98c0c8ae9a
@@@ -2,6 -2,7 +2,7 @@@
  
  #include <common/constants.qh>
  #include <common/deathtypes/all.qh>
+ #include <common/gamemodes/gamemode/cts/cts.qh>
  #include <common/items/_mod.qh>
  #include <common/mapobjects/subs.qh>
  #include <common/mapobjects/triggers.qh>
@@@ -480,7 -481,7 +481,7 @@@ bool Item_GiveTo(entity item, entity pl
        // if the player is using their best weapon before items are given, they
        // probably want to switch to an even better weapon after items are given
  
-       if(CS(player).autoswitch)
+       if(CS_CVAR(player).autoswitch)
        {
                for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
                return false;
  
        // crude hack to enforce switching weapons
-       if(g_cts && item.itemdef.instanceOfWeaponPickup && !CS(player).cvar_cl_cts_noautoswitch)
+       if(g_cts && item.itemdef.instanceOfWeaponPickup && !CS_CVAR(player).cvar_cl_cts_noautoswitch)
        {
                for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
@@@ -1017,19 -1018,19 +1018,19 @@@ void _StartItem(entity this, entity def
  
                if(autocvar_spawn_debug >= 2)
                {
 -            // why not flags & fl_item?
 -                  FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
 -                LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
 -                LOG_TRACE(" vs ", it.netname, vtos(it.origin));
 -                error("Mapper sucks.");
 -            });
 +                      // why not flags & fl_item?
 +                      FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
 +                              LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
 +                              LOG_TRACE(" vs ", it.netname, vtos(it.origin));
 +                              error("Mapper sucks.");
 +                      });
                        this.is_item = true;
                }
  
                weaponsInMap |= WepSet_FromWeapon(REGISTRY_GET(Weapons, weaponid));
  
 -              if (   def.instanceOfPowerup
 -                      || def.instanceOfWeaponPickup
 +              if (        def.instanceOfPowerup
 +                      ||  def.instanceOfWeaponPickup
                        || (def.instanceOfHealth && def != ITEM_HealthSmall)
                        || (def.instanceOfArmor && def != ITEM_ArmorSmall)
                        || (itemid & (IT_KEY1 | IT_KEY2))
        this.bot_pickupevalfunc = pickupevalfunc;
        this.bot_pickupbasevalue = pickupbasevalue;
        this.mdl = this.model ? this.model : strzone(this.item_model_ent.model_str());
 -      this.netname = itemname;
 +      this.netname = (def.m_weapon) ? def.m_weapon.netname : def.netname;
        settouch(this, Item_Touch);
        setmodel(this, MDL_Null); // precision set below
        //this.effects |= EF_LOWPRECISION;
  
  void StartItem(entity this, GameItem def)
  {
 -    def = def.m_spawnfunc_hookreplace(def, this);
 -    if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
 -    {
 -        delete(this);
 -        return;
 -    }
 -    this.classname = def.m_canonical_spawnfunc;
 -    _StartItem(
 -      this,
 -      this.itemdef = def,
 -      def.m_respawntime(), // defaultrespawntime
 -      def.m_respawntimejitter() // defaultrespawntimejitter
 +      def = def.m_spawnfunc_hookreplace(def, this);
 +
 +      if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
 +      {
 +              delete(this);
 +              return;
 +      }
 +
 +      this.classname = def.m_canonical_spawnfunc;
 +
 +      _StartItem(
 +              this,
 +              this.itemdef = def,
 +              def.m_respawntime(), // defaultrespawntime
 +              def.m_respawntimejitter() // defaultrespawntimejitter
        );
  }
  
@@@ -1431,7 -1429,7 +1432,7 @@@ float GiveItems(entity e, float beginar
  
        int _switchweapon = 0;
  
-       if(CS(e).autoswitch)
+       if(CS_CVAR(e).autoswitch)
        {
                for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
index 5bf040f506d60fb8538adf9845e21c7f811c3597,7fc9f0fa3154d384e449ef6d34decac85fb19ef9..eed40eca39fb9a7eeb32472542e886fd40a64a79
@@@ -2,6 -2,15 +2,15 @@@
  
  #include <common/sounds/sound.qh>
  
+ float autocvar_g_balance_superweapons_time;
+ bool autocvar_g_fullbrightitems;
+ int autocvar_g_powerups;
+ float autocvar_g_items_mindist;
+ float autocvar_g_items_maxdist;
+ int autocvar_g_pickup_items;
+ bool autocvar_g_nodepthtestitems;
+ #define autocvar_g_weapon_stay cvar("g_weapon_stay")
  void StartItem(entity this, entity a);
  .int item_group;
  .int item_group_count;
@@@ -20,7 -29,6 +29,7 @@@ const float ITEM_RESPAWN_TICKS = 10
  
  .float max_armorvalue;
  .float pickup_anyway;
 +.int count;
  
  .float scheduledrespawntime;
  .float respawntime;
diff --combined qcsrc/server/race.qc
index ebad6157d8750edb24e2f589f1eb4887b0beab2d,70a98083efb22a03171f78a0adae0fe623c189ac..f3f0ac907575f7e93e235c4b34e5d0fc6b5420d3
@@@ -789,35 -789,9 +789,35 @@@ bool race_waypointsprite_visible_for_pl
                return false;
  }
  
 +void defrag_waypointsprites(entity targeted, entity checkpoint)
 +{
 +      for(entity t = findchain(target, targeted.targetname); t; t = t.chain)
 +      {
 +              if(t.modelindex)
 +              {
 +                      entity s = WP_RaceStart;
 +
 +                      if(checkpoint.classname == "target_checkpoint")
 +                              s = WP_RaceCheckpoint;
 +                      else if(checkpoint.classname == "target_stopTimer")
 +                              s = WP_RaceFinish;
 +
 +                      vector o = (t.absmin + t.absmax) * 0.5;
 +
 +                      WaypointSprite_SpawnFixed(s, o, t, sprite, RADARICON_NONE);
 +
 +                      t.sprite.realowner = checkpoint;
 +                      t.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
 +              }
 +
 +              if(t.targetname)
 +                      defrag_waypointsprites(t, checkpoint);
 +      }
 +}
 +
  void trigger_race_checkpoint_verify(entity this)
  {
 -    static bool have_verified;
 +      static bool have_verified;
        if (have_verified) return;
        have_verified = true;
  
                        pl_race_place = 0;
                        if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for respawning in race) - bailing out"));
 -            }
 +                      }
  
                        if (i == 0) {
                                // qualifying only
                                pl_race_place = race_lowest_place_spawn;
                                if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                        error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
 -                }
 +                              }
  
                                // race only (initial spawn)
                                g_race_qualifying = 0;
                                        pl_race_place = p;
                                        if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                                error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for initially spawning in race) - bailing out"));
 -                    }
 +                                      }
                                }
                        }
                }
                pl_race_place = race_lowest_place_spawn;
                if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                        error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
 -        }
 +              }
        } else {
                pl_race_checkpoint = race_NextCheckpoint(0);
                g_race_qualifying = 1;
                                for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
                                        if (argv(0) == cp.targetname) {
                                                cp.race_checkpoint = stof(argv(1));
 -                    }
 -                }
 +                                      }
 +                              }
                        }
                        fclose(fh);
                }
  
        g_race_qualifying = qual;
  
 -      IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
 -      {
 -              if(it.targetname == "" || !it.targetname) // somehow this is a case...
 -                      continue;
 -              entity cpt = it;
 -              FOREACH_ENTITY_STRING(target, cpt.targetname,
 -              {
 -                      vector org = (it.absmin + it.absmax) * 0.5;
 -                      if(cpt.race_checkpoint == 0)
 -                              WaypointSprite_SpawnFixed(WP_RaceStart, org, it, sprite, RADARICON_NONE);
 -                      else
 -                              WaypointSprite_SpawnFixed(WP_RaceCheckpoint, org, it, sprite, RADARICON_NONE);
 -
 -                      it.sprite.realowner = cpt;
 -                      it.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
 -              });
 -      });
 -
        if (race_timed_checkpoint) {
                if (defrag_ents) {
                        IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
                        {
 -                              entity cpt = it;
 -                              if(it.classname == "target_startTimer" || it.classname == "target_stopTimer") {
 -                                      if(it.targetname == "" || !it.targetname) // somehow this is a case...
 -                                              continue;
 -                                      FOREACH_ENTITY_STRING(target, cpt.targetname, {
 -                                              if(it.sprite)
 -                                                      WaypointSprite_UpdateSprites(it.sprite, ((cpt.classname == "target_startTimer") ? WP_RaceStart : WP_RaceFinish), WP_Null, WP_Null);
 -                                      });
 -                              }
 +                              defrag_waypointsprites(it, it);
 +
                                if(it.classname == "target_checkpoint") {
                                        if(it.race_checkpoint == -2)
                                                defragcpexists = -1; // something's wrong with the defrag cp file or it has not been written yet, set defragcpexists to -1 so that it will be rewritten when someone finishes
                                for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
                                        if (cp.race_checkpoint > largest_cp_id) {
                                                largest_cp_id = cp.race_checkpoint;
 -                    }
 -                }
 +                                      }
 +                              }
                                for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
                                        cp.race_checkpoint = largest_cp_id + 1; // finish line
 -                }
 +                              }
                                race_highest_checkpoint = largest_cp_id + 1;
                                race_timed_checkpoint = largest_cp_id + 1;
                        } else {
                                for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
                                        cp.race_checkpoint = 255; // finish line
 -                }
 +                              }
                                race_highest_checkpoint = 255;
                                race_timed_checkpoint = 255;
                        }
                        {
                                if (it.race_checkpoint == 0) {
                                        WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null);
 -                } else if (it.race_checkpoint == race_timed_checkpoint) {
 +                              } else if (it.race_checkpoint == race_timed_checkpoint) {
                                        WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null);
                                }
 -            });
 +                      });
                }
        }
  
 -      if (defrag_ents) {
 +      if (defrag_ents) { /* The following hack shall be removed when per-player trigger_multiple.wait is implemented for cts */
                for (entity trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); ) {
                        for (entity targ = NULL; (targ = find(targ, targetname, trigger.target)); ) {
                                if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") {
@@@ -1042,7 -1041,7 +1042,7 @@@ spawnfunc(trigger_race_checkpoint
        InitializeEntity(this, trigger_race_checkpoint_verify, INITPRIO_FINDTARGET);
  }
  
- spawnfunc(target_checkpoint) // defrag entity
+ void target_checkpoint_setup(entity this)
  {
        if(!g_race && !g_cts) { delete(this); return; }
        defrag_ents = 1;
        InitializeEntity(this, trigger_race_checkpoint_verify, INITPRIO_FINDTARGET);
  }
  
- spawnfunc(target_startTimer) { spawnfunc_target_checkpoint(this); }
- spawnfunc(target_stopTimer) { spawnfunc_target_checkpoint(this); }
+ spawnfunc(target_checkpoint)
+ {
+       // xonotic defrag entity
+       target_checkpoint_setup(this);
+ }
+ // compatibility entity names
+ spawnfunc(target_startTimer) { target_checkpoint_setup(this); }
+ spawnfunc(target_stopTimer) { target_checkpoint_setup(this); }
  
  void race_AbandonRaceCheck(entity p)
  {
diff --combined qcsrc/server/teamplay.qc
index afaf00ed66460d968d0fba62d2e62026a46e9ec4,7b2027589cca326d0a13f53561678e0fba770e39..ff6de57bc465ef5dcc1a81c5f9ebdf5c0dde156c
@@@ -4,6 -4,8 +4,8 @@@
  #include <common/gamemodes/_mod.qh>
  #include <common/teams.qh>
  #include <server/bot/api.qh>
+ #include <server/bot/default/cvars.qh>
+ #include <server/campaign.qh>
  #include <server/client.qh>
  #include <server/command/vote.qh>
  #include <server/damage.qh>
@@@ -26,6 -28,8 +28,6 @@@ enu
  /// \brief Indicates that the player is not allowed to join a team.
  const int TEAM_NOT_ALLOWED = -1;
  
 -.float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
 -
  .int m_team_balance_state; ///< Holds the state of the team balance entity.
  .entity m_team_balance_team[NUM_TEAMS]; ///< ???
  
@@@ -46,7 -50,7 +48,7 @@@ STATIC_INIT(g_team_entities
  {
        for (int i = 0; i < NUM_TEAMS; ++i)
        {
-               g_team_entities[i] = spawn();
+               g_team_entities[i] = new_pure();
        }
  }
  
@@@ -1119,18 -1123,13 +1121,13 @@@ void SV_ChangeTeam(entity player, int n
        {
                SetPlayerColors(player, new_color);
        }
-       // TODO: Should we really bother with this?
        if(!IS_CLIENT(player))
        {
-               // since this is an engine function, and gamecode doesn't have any calls earlier than this, do the connecting message here
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CONNECTING,
-                       player.netname);
                return;
        }
        if (!teamplay)
        {
                return;
        }
-       Player_SetTeamIndexChecked(player, Team_TeamToIndex((new_color & 0x0F) +
-               1));
+       Player_SetTeamIndexChecked(player, Team_TeamToIndex((new_color & 0x0F) + 1));
  }
diff --combined qcsrc/server/teamplay.qh
index 9faaaa521353b22a8ab8ed7f104df46214a27657,279df06fc303a8e766fa182d6eb90f2313b81cc6..a8c48be15ddceff941391f350df3077ce62bbb6a
@@@ -8,10 -8,10 +8,12 @@@ bool autocvar_teamplay_lockonrestart
  bool autocvar_g_balance_teams;
  bool autocvar_g_balance_teams_prevent_imbalance;
  
+ string autocvar_g_forced_team_otherwise;
  bool lockteams;
  
 +.int team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
 +
  // ========================== Global teams API ================================
  
  /// \brief Returns the global team entity at the given index.
@@@ -321,8 -321,8 +323,8 @@@ int TeamBalanceTeam_GetNumberOfBots(ent
  int TeamBalance_CompareTeamsInternal(entity team_a, entity team_index_b,
        entity player, bool use_score);
  
- /// \brief Called when the player connects or when they change their color with
- /// the "color" command.
+ /// \brief Called when the player changes color with the "color" command.
+ /// Note that the "color" command is always called early on player connection
  /// \param[in,out] player Player that requested a new color.
  /// \param[in] new_color Requested color.
  void SV_ChangeTeam(entity player, int new_color);
diff --combined qcsrc/server/world.qc
index 16e86fb2ed9eb2469d2627e6805bba0c9b3943b7,b61f75ceb7068f00bf85b76a05a88f04a016dac9..d57a0af9eb29bf45b33c227f4ceab508d0821266
@@@ -37,6 -37,7 +37,7 @@@
  #include <server/hook.qh>
  #include <server/intermission.qh>
  #include <server/ipban.qh>
+ #include <server/items/items.qh>
  #include <server/main.qh>
  #include <server/mapvoting.qh>
  #include <server/mutators/_mod.qh>
@@@ -68,7 -69,7 +69,7 @@@ void PingPLReport_Think(entity this
        {
                WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
                WriteByte(MSG_BROADCAST, this.cnt);
-               WriteShort(MSG_BROADCAST, bound(1, CS(e).ping, 65535));
+               WriteShort(MSG_BROADCAST, bound(1, CS(e).ping, 32767));
                WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_packetloss * 255), 255));
                WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_movementloss * 255), 255));
  
@@@ -99,7 -100,6 +100,6 @@@ void PingPLReport_Spawn(
  }
  
  const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1;
- float world_initialized;
  
  void SetDefaultAlpha()
  {
@@@ -170,6 -170,7 +170,7 @@@ void cvar_changes_init(
  #define BADPREFIX(p) if(substring(k, 0, strlen(p)) == p) continue
  #define BADPRESUFFIX(p,s) if(substring(k, 0, strlen(p)) == p && substring(k, -strlen(s), -1) == s) continue
  #define BADCVAR(p) if(k == p) continue
+ #define BADVALUE(p, val) if (k == p && v == val) continue
  
                // general excludes and namespaces for server admin used cvars
                BADPREFIX("help_"); // PN's server has this listed as changed, let's not rat him out for THAT
                BADCVAR("sv_motd");
                BADCVAR("sv_public");
                BADCVAR("sv_ready_restart");
+               BADCVAR("sv_showfps");
                BADCVAR("sv_status_privacy");
                BADCVAR("sv_taunt");
                BADCVAR("sv_vote_call");
                BADCVAR("sv_vote_master_commands");
                BADCVAR("sv_vote_master_password");
                BADCVAR("sv_vote_simple_majority_factor");
+               BADVALUE("sys_ticrate", "0.0166667");
+               BADVALUE("sys_ticrate", "0.0333333");
                BADCVAR("teamplay_mode");
                BADCVAR("timelimit_override");
                BADPREFIX("g_warmup_");
                BADCVAR("g_lms_weaponarena");
                BADCVAR("g_ctf_stalemate_time");
  
-               if(cvar_string("g_mod_balance") == "Testing")
-               {
-                       // (temporary) while using the Testing balance, any weapon balance cvars are allowed to be changed
-                       BADPREFIX("g_balance_");
-               }
  #undef BADPRESUFFIX
  #undef BADPREFIX
  #undef BADCVAR
+ #undef BADVALUE
  
                if(pureadding)
                {
@@@ -887,8 -886,11 +886,8 @@@ spawnfunc(worldspawn
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
  
 -      if(fexists(strcat("scripts/", mapname, ".arena")))
 -              cvar_settemp("sv_q3acompat_machineshotgunswap", "1");
 -
 -      if(fexists(strcat("scripts/", mapname, ".defi")))
 -              cvar_settemp("sv_q3defragcompat", "1");
 +      q3compat = BITSET(q3compat, Q3COMPAT_ARENA, fexists(strcat("scripts/", mapname, ".arena")));
 +      q3compat = BITSET(q3compat, Q3COMPAT_DEFI, fexists(strcat("scripts/", mapname, ".defi")));
  
        if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
        {
        maplist_reply = strzone(getmaplist());
        lsmaps_reply = strzone(getlsmaps());
        monsterlist_reply = strzone(getmonsterlist());
+       bool records_available = false;
        for(int i = 0; i < 10; ++i)
        {
                string s = getrecords(i);
-               if (s)
+               if (s != "")
+               {
                        records_reply[i] = strzone(s);
+                       records_available = true;
+               }
        }
+       if (!records_available)
+               records_reply[0] = "No records available for the current game mode.\n";
        ladder_reply = strzone(getladder());
        rankings_reply = strzone(getrankings());
  
@@@ -1182,7 -1190,7 +1187,7 @@@ void DumpStats(float final
        s = strcat(s, GetGametype(), "_", GetMapname(), ":", ftos(rint(time)));
  
        if(to_console)
-               LOG_INFO(s);
+               LOG_HELP(s);
        if(to_eventlog)
                GameLogEcho(s);
  
  
        s = strcat(":labels:player:", GetPlayerScoreString(NULL, 0));
        if(to_console)
-               LOG_INFO(s);
+               LOG_HELP(s);
        if(to_eventlog)
                GameLogEcho(s);
        if(to_file)
                        s = strcat(s, "spectator:");
  
                if(to_console)
-                       LOG_INFO(s, playername(it.netname, it.team, false));
+                       LOG_HELP(s, playername(it.netname, it.team, false));
                if(to_eventlog)
                        GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it.netname, it.team, false)));
                if(to_file)
        {
                s = strcat(":labels:teamscores:", GetTeamScoreString(0, 0));
                if(to_console)
-                       LOG_INFO(s);
+                       LOG_HELP(s);
                if(to_eventlog)
                        GameLogEcho(s);
                if(to_file)
                        s = strcat(":teamscores:see-labels:", GetTeamScoreString(i, 0));
                        s = strcat(s, ":", ftos(i));
                        if(to_console)
-                               LOG_INFO(s);
+                               LOG_HELP(s);
                        if(to_eventlog)
                                GameLogEcho(s);
                        if(to_file)
        }
  
        if(to_console)
-               LOG_INFO(":end");
+               LOG_HELP(":end");
        if(to_eventlog)
                GameLogEcho(":end");
        if(to_file)
@@@ -1261,7 -1269,7 +1266,7 @@@ only called if a time or frag limit ha
  void NextLevel()
  {
        game_stopped = true;
-       intermission_running = 1; // game over
+       intermission_running = true; // game over
  
        // enforce a wait time before allowing changelevel
        if(player_count > 0)
@@@ -1825,7 -1833,7 +1830,7 @@@ void readplayerstartcvars(
        start_ammo_plasma = 0;
        if (random_start_ammo == NULL)
        {
-               random_start_ammo = new(random_start_ammo);
+               random_start_ammo = new_pure(random_start_ammo);
        }
        start_health = cvar("g_balance_health_start");
        start_armorvalue = cvar("g_balance_armor_start");
                random_start_weapons_count = cvar("g_random_start_weapons_count");
                SetResource(random_start_ammo, RES_SHELLS, cvar("g_random_start_shells"));
                SetResource(random_start_ammo, RES_BULLETS, cvar("g_random_start_bullets"));
-               SetResource(random_start_ammo, RES_ROCKETS,cvar("g_random_start_rockets"));
+               SetResource(random_start_ammo, RES_ROCKETS, cvar("g_random_start_rockets"));
                SetResource(random_start_ammo, RES_CELLS, cvar("g_random_start_cells"));
                SetResource(random_start_ammo, RES_PLASMA, cvar("g_random_start_plasma"));
        }
        start_ammo_cells = max(0, start_ammo_cells);
        start_ammo_plasma = max(0, start_ammo_plasma);
        start_ammo_fuel = max(0, start_ammo_fuel);
-       SetResource(random_start_ammo, RES_SHELLS,
-               max(0, GetResource(random_start_ammo, RES_SHELLS)));
-       SetResource(random_start_ammo, RES_BULLETS,
-               max(0, GetResource(random_start_ammo, RES_BULLETS)));
-       SetResource(random_start_ammo, RES_ROCKETS,
-               max(0, GetResource(random_start_ammo, RES_ROCKETS)));
-       SetResource(random_start_ammo, RES_CELLS,
-               max(0, GetResource(random_start_ammo, RES_CELLS)));
-       SetResource(random_start_ammo, RES_PLASMA,
-               max(0, GetResource(random_start_ammo, RES_PLASMA)));
+       SetResource(random_start_ammo, RES_SHELLS, max(0, GetResource(random_start_ammo, RES_SHELLS)));
+       SetResource(random_start_ammo, RES_BULLETS, max(0, GetResource(random_start_ammo, RES_BULLETS)));
+       SetResource(random_start_ammo, RES_ROCKETS, max(0, GetResource(random_start_ammo, RES_ROCKETS)));
+       SetResource(random_start_ammo, RES_CELLS, max(0, GetResource(random_start_ammo, RES_CELLS)));
+       SetResource(random_start_ammo, RES_PLASMA, max(0, GetResource(random_start_ammo, RES_PLASMA)));
  
        warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
        warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
diff --combined xonotic-server.cfg
index 39482b8518d14d6efbbfd9651f9a20bbf08e9a6a,ad32666c6d6d59989a137e1d852e3633dbe8cebc..e4db3b8f62d5f87be1754e03d356a5ac8f58136c
@@@ -231,11 -231,9 +231,9 @@@ set g_items_maxdist 4500 "maximum dista
  
  set g_grab_range 200 "distance at which dragable objects can be grabbed"
  
- set g_cloaked 0 "display all players mostly invisible"
  set g_player_alpha 1 "default opacity of players"
  set g_player_brightness 0 "set to 2 for brighter players"
  set g_player_damageforcescale 2 "push multiplier of attacks against players"
- set g_balance_cloaked_alpha 0.25 "opacity of cloaked players"
  
  set g_playerclip_collisions 1 "0 = disable collision testing against playerclips, might be useful on some defrag maps"
  set g_botclip_collisions 1 "0 = disable collision testing against botclips, might be useful on some defrag maps"
@@@ -496,7 -494,8 +494,7 @@@ sv_gameplayfix_consistentplayerprethin
  sv_gameplayfix_gravityunaffectedbyticrate 1
  sv_gameplayfix_nogravityonground 1
  
 -set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping machinegun and shotgun (for Q3A map compatibility in mapinfo files)"
 -set sv_q3defragcompat 0 "toggle for some compatibility hacks (for Q3DF map compatibility)"
 +set sv_q3compat_changehitbox 0 "use Q3 player hitbox dimensions and camera height on Q3 maps (maps with an entry in a .arena or .defi file)
  
  set g_movement_highspeed 1 "multiplier scale for movement speed (applies to sv_maxspeed and sv_maxairspeed, also applies to air acceleration when g_movement_highspeed_q3_compat is set to 0)"
  set g_movement_highspeed_q3_compat 0 "apply speed modifiers to air movement in a more Q3-compatible way (only apply speed buffs and g_movement_highspeed to max air speed, not to acceleration)"
@@@ -580,7 -579,7 +578,7 @@@ set sv_showspectators 1 "Show who's spe
  
  set sv_damagetext 2 "<= 0: disabled, >= 1: visible to spectators, >= 2: visible to attacker, >= 3: all players see everyone's damage"
  
- set sv_showfps 5 "Show player's FPS counters in the scoreboard. This setting acts as a delay in seconds between updates"
+ set sv_showfps 0 "Show player's FPS counters in the scoreboard. This setting acts as a delay in seconds between updates. NOTE: this feature gathers performance telemetry, it may require consent from players on the server depending on your legislation"
  set sv_doors_always_open 0 "If set to 1 don't close doors which after they were open"
  
  set sv_warpzone_allow_selftarget 0 "do not touch"