Merge branch 'master' into Mario/stats_eloranking
authorMario <mario@smbclan.net>
Thu, 29 Aug 2019 04:44:42 +0000 (14:44 +1000)
committerMario <mario@smbclan.net>
Thu, 29 Aug 2019 04:44:42 +0000 (14:44 +1000)
1  2 
qcsrc/common/playerstats.qc
qcsrc/server/defs.qh
qcsrc/server/miscfunctions.qc
xonotic-client.cfg

index 3599e84e617b53fcd40a4c78204f725ed3b501f0,2c9bbcde805eb9770015c385877971563b872731..cf272e5635a1eb66e72ec90810b864b10eff9f27
@@@ -55,7 -55,7 +55,7 @@@ void PlayerStats_GameReport_AddPlayer(e
        }
  }
  
- void PlayerStats_GameReport_AddTeam(float t)
+ void PlayerStats_GameReport_AddTeam(int t)
  {
        if(PS_GR_OUT_DB < 0) { return; }
  
@@@ -152,14 -152,12 +152,14 @@@ void PlayerStats_GameReport_FinalizePla
                        if(latency)
                                PlayerStats_GameReport_Event_Player(p, PLAYERSTATS_AVGLATENCY, latency);
                }
 +
 +              db_put(PS_GR_OUT_DB, sprintf("%s:_ranked", p.playerstats_id), ftos(CS(p).cvar_cl_allow_uidranking));
        }
  
        strfree(p.playerstats_id);
  }
  
- void PlayerStats_GameReport(float finished)
+ void PlayerStats_GameReport(bool finished)
  {
        if(PS_GR_OUT_DB < 0) { return; }
  
@@@ -260,6 -258,7 +260,7 @@@ void PlayerStats_GameReport_Init() // i
  }
  
  // this... is a hack, a temporary one until we get a proper duel gametype
+ // TODO: remove duel hack after servers have migrated to the proper duel gametype!
  string PlayerStats_GetGametype()
  {
        if(IS_GAMETYPE(DEATHMATCH) && autocvar_g_maxplayers == 2)
@@@ -377,10 -376,6 +378,10 @@@ void PlayerStats_GameReport_Handler(ent
                                        url_fputs(fh, sprintf("t %s\n", tt));
                                }
  
 +                              // elo ranking enabled
 +                              nn = db_get(PS_GR_OUT_DB, sprintf("%s:_ranked", p));
 +                              if(nn != "") { url_fputs(fh, sprintf("r %s\n", nn)); }
 +
                                // output player events
                                for(e = PS_GR_OUT_EVL; (en = db_get(PS_GR_OUT_DB, sprintf("*:%s", e))) != ""; e = en)
                                {
diff --combined qcsrc/server/defs.qh
index 217a74223a1d1e0b3e273ec8c66d623e2f85f018,b45e20986a76c0acba01ac6fdf4cea4137dd0c24..566d57b57deae341a0f86e9cb783d155491c980c
@@@ -25,9 -25,7 +25,7 @@@ float currentbots
  float bots_would_leave;
  
  void UpdateFrags(entity player, int f);
- .float totalfrags;
- float team1_score, team2_score, team3_score, team4_score;
+ .int totalfrags;
  
  // flag set on worldspawn so that the code knows if it is dedicated or not
  float server_is_dedicated;
  
  .void(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) event_damage;
  
+ .bool(entity targ, entity inflictor, float amount, float limit) event_heal;
  //.string     wad;
  //.string     map;
  
  //.float      worldtype;
  // Needed for dynamic clientwalls
- .float inactive; // Clientwall disappears when inactive
+ .bool inactive; // Clientwall disappears when inactive
  .float alpha_max, alpha_min;
  .float fade_start, fade_end, fade_vertical_offset;
  .float default_solid; // Variable to store default .solid for clientwalls
@@@ -50,7 -50,7 +50,7 @@@
  .float        pain_frame;                     //"
  .float  crouch;       // Crouching or not?
  
.float        superweapons_finished = _STAT(SUPERWEAPONS_FINISHED);
const .float superweapons_finished = _STAT(SUPERWEAPONS_FINISHED);
  
  .float cnt; // used in too many places
  .float count;
@@@ -168,6 -168,9 +168,9 @@@ float default_weapon_alpha
  .float cvar_cl_jetpack_jump;
  .float cvar_cl_movement_track_canjump;
  .float cvar_cl_newusekeysupported;
+ .float cvar_cl_cts_noautoswitch;
+ .bool cvar_cl_weapon_switch_reload;
+ .bool cvar_cl_weapon_switch_fallback_to_impulse;
  
  .string cvar_g_xonoticversion;
  .string cvar_cl_weaponpriority;
  
  .float cvar_cl_allow_uid2name;
  .float cvar_cl_allow_uidtracking;
 +.bool cvar_cl_allow_uidranking;
  .string stored_netname;
  
  string gamemode_name;
@@@ -188,9 -190,6 +191,6 @@@ string W_Apply_Weaponreplace(string in)
  void FixIntermissionClient(entity e);
  void FixClientCvars(entity e);
  
- // WEAPONTODO: remove this
- //WepSet weaponsInMap;
  .float respawn_countdown; // next number to count
  
  float bot_waypoints_for_items;
  #else
  #define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).attack_finished_single[slot])
  #endif
- #define ATTACK_FINISHED(ent, slot) ATTACK_FINISHED_FOR(ent, ent.(weaponentity).m_weapon.m_id, slot)
- // assault game mode: Which team is attacking in this round?
- float assault_attacker_team;
+ #define ATTACK_FINISHED(ent, w) ATTACK_FINISHED_FOR(ent, ent.(w).m_weapon.m_id, weaponslot(w))
  
  // speedrun: when 1, player auto teleports back when capture timeout happens
  .float speedrunning;
  float ServerProgsDB;
  float TemporaryDB;
  
- .float team_saved;
+ .int team_saved;
  
  bool some_spawn_has_been_used;
  int have_team_spawns; // 0 = no team spawns requested, -1 = team spawns requested but none found, 1 = team spawns requested and found
  int have_team_spawns_forteams; // if Xth bit is 1 then team X has spawns else it has no spawns; team 0 is the "no-team"
  
- // set when showing a kill countdown
- .entity killindicator;
  .bool canteamdamage;
  
  void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force);
  // WEAPONTODO
  #define DMG_NOWEP (weaponentities[0])
  
- float lockteams;
  float sv_maxidle;
  float sv_maxidle_spectatorsareidle;
  int sv_maxidle_slots;
@@@ -250,7 -241,7 +242,7 @@@ int autocvar__independent_players
  bool independent_players;
  #define INDEPENDENT_PLAYERS (autocvar__independent_players ? (autocvar__independent_players > 0) : independent_players)
  #define IS_INDEPENDENT_PLAYER(e) ((e).solid == SOLID_TRIGGER)
- #define MAKE_INDEPENDENT_PLAYER(e) (((e).solid = SOLID_TRIGGER), ((e).frags = FRAGS_PLAYER_NONSOLID))
+ #define MAKE_INDEPENDENT_PLAYER(e) (((e).solid = SOLID_TRIGGER), ((e).frags = FRAGS_PLAYER_OUT_OF_GAME))
  
  string clientstuff;
  .float phase;
@@@ -304,16 -295,11 +296,11 @@@ string matchid
  
  bool radar_showennemies;
  
- #ifdef PROFILING
- float client_cefc_accumulator;
- float client_cefc_accumulatortime;
- #endif
  .float weapon_load[Weapons_MAX];
  .int ammo_none; // used by the reloading system, must always be 0
- .float clip_load;
- .float old_clip_load;
- .float clip_size;
+ .int clip_load;
+ .int old_clip_load;
+ .int clip_size;
  
  .int minelayer_mines;
  .float vortex_charge;
  // when doing this, hagar can go through clones
  // #define PROJECTILE_MAKETRIGGER(e) (e).solid = SOLID_BBOX
  
- .float spectatee_status;
- .float zoomstate;
- .float restriction;
+ .int spectatee_status;
+ .bool zoomstate;
+ .int restriction;
  
  .entity clientdata;
  .entity personal;
@@@ -342,24 -328,27 +329,27 @@@ string deathmessage
  .bool just_joined;
  
  .float cvar_cl_weaponimpulsemode;
- .float selectweapon; // last selected weapon of the player
+ .int selectweapon; // last selected weapon of the player
  
  .float ballistics_density; // wall piercing factor, larger = bullet can pass through more
  
- const float ACTIVE_NOT                = 0;
- const float ACTIVE_ACTIVE     = 1;
- const float ACTIVE_IDLE       = 2;
- const float ACTIVE_BUSY       = 2;
- const float ACTIVE_TOGGLE     = 3;
- .float active;
+ //const int FROZEN_NOT                                = 0;
+ const int FROZEN_NORMAL                               = 1;
+ const int FROZEN_TEMP_REVIVING                = 2;
+ const int FROZEN_TEMP_DYING                   = 3;
+ const int ACTIVE_NOT          = 0;
+ const int ACTIVE_ACTIVE       = 1;
+ const int ACTIVE_IDLE         = 2;
+ const int ACTIVE_BUSY         = 2;
+ const int ACTIVE_TOGGLE       = 3;
+ .int active;
  .void (entity this, int act_state) setactive;
  .entity realowner;
  
  //float serverflags;
  
- .float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
- .float player_blocked;
+ .bool player_blocked;
  
  .float revival_time; // time at which player was last revived
  .float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
  .entity muzzle_flash;
  .float misc_bulletcounter;    // replaces uzi & hlac bullet counter.
  
- .int killindicator_teamchange;
  void PlayerUseKey(entity this);
  
  USING(spawn_evalfunc_t, vector(entity this, entity player, entity spot, vector current));
  
  string modname;
  
- .float missile_flags;
+ .int missile_flags;
  const int MIF_SPLASH = BIT(1);
  const int MIF_ARC = BIT(2);
  const int MIF_PROXY = BIT(3);
index 798d64d66b36585bda65acdad4beddeb216515cf,a59f4afa231380a58cc9f5e24fd6b7e76dd6e0b5..c68d1a679aaee61c210b496c3c0aa1cc7e980982
@@@ -16,6 -16,7 +16,7 @@@
  #include "../common/command/_mod.qh"
  #include "../common/constants.qh"
  #include <common/net_linked.qh>
+ #include <common/weapons/weapon/crylink.qh>
  #include "../common/deathtypes/all.qh"
  #include "../common/mapinfo.qh"
  #include "../common/notifications/all.qh"
@@@ -39,34 -40,47 +40,47 @@@ void crosshair_trace(entity pl
  {
        traceline_antilag(pl, CS(pl).cursor_trace_start, CS(pl).cursor_trace_start + normalize(CS(pl).cursor_trace_endpos - CS(pl).cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
  }
- .bool ctrace_solidchanged;
  void crosshair_trace_plusvisibletriggers(entity pl)
+ {
+       crosshair_trace_plusvisibletriggers__is_wz(pl, false);
+ }
+ void WarpZone_crosshair_trace_plusvisibletriggers(entity pl)
+ {
+       crosshair_trace_plusvisibletriggers__is_wz(pl, true);
+ }
+ void crosshair_trace_plusvisibletriggers__is_wz(entity pl, bool is_wz)
  {
        FOREACH_ENTITY_FLOAT(solid, SOLID_TRIGGER,
        {
                if(it.model != "")
                {
                        it.solid = SOLID_BSP;
-                       it.ctrace_solidchanged = true;
                        IL_PUSH(g_ctrace_changed, it);
                }
        });
  
-       crosshair_trace(pl);
+       if (is_wz)
+               WarpZone_crosshair_trace(pl);
+       else
+               crosshair_trace(pl);
  
-       IL_EACH(g_ctrace_changed, it.ctrace_solidchanged,
-       {
-               it.solid = SOLID_TRIGGER;
-               it.ctrace_solidchanged = false;
-       });
+       IL_EACH(g_ctrace_changed, true, { it.solid = SOLID_TRIGGER; });
  
        IL_CLEAR(g_ctrace_changed);
  }
  void WarpZone_crosshair_trace(entity pl)
  {
        WarpZone_traceline_antilag(pl, CS(pl).cursor_trace_start, CS(pl).cursor_trace_start + normalize(CS(pl).cursor_trace_endpos - CS(pl).cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
  }
  
+ void dedicated_print(string input)
+ {
+       if (server_is_dedicated) print(input);
+ }
  
  void GameLogEcho(string s)
  {
@@@ -204,12 -218,12 +218,12 @@@ string AmmoNameFromWeaponentity(Weapon 
        string ammoitems = "batteries";
        switch (wep.ammo_type)
        {
-               case RESOURCE_SHELLS:  ammoitems = ITEM_Shells.m_name;      break;
-               case RESOURCE_BULLETS: ammoitems = ITEM_Bullets.m_name;     break;
-               case RESOURCE_ROCKETS: ammoitems = ITEM_Rockets.m_name;     break;
-               case RESOURCE_CELLS:   ammoitems = ITEM_Cells.m_name;       break;
-               case RESOURCE_PLASMA:  ammoitems = ITEM_Plasma.m_name;      break;
-               case RESOURCE_FUEL:    ammoitems = ITEM_JetpackFuel.m_name; break;
+               case RES_SHELLS:  ammoitems = ITEM_Shells.m_name;      break;
+               case RES_BULLETS: ammoitems = ITEM_Bullets.m_name;     break;
+               case RES_ROCKETS: ammoitems = ITEM_Rockets.m_name;     break;
+               case RES_CELLS:   ammoitems = ITEM_Cells.m_name;       break;
+               case RES_PLASMA:  ammoitems = ITEM_Plasma.m_name;      break;
+               case RES_FUEL:    ammoitems = ITEM_JetpackFuel.m_name; break;
        }
        return ammoitems;
  }
@@@ -218,16 -232,13 +232,13 @@@ string formatmessage(entity this, strin
  {
        float p, p1, p2;
        float n;
-       vector cursor;
-       entity cursor_ent;
+       vector cursor = '0 0 0';
+       entity cursor_ent = NULL;
        string escape;
        string replacement;
        p = 0;
        n = 7;
-       WarpZone_crosshair_trace(this);
-       cursor = trace_endpos;
-       cursor_ent = trace_ent;
+       bool traced = false;
  
        MUTATOR_CALLHOOK(PreFormatMessage, this, msg);
        msg = M_ARGV(1, string);
                if (p < 0)
                        break;
  
+               if(!traced)
+               {
+                       WarpZone_crosshair_trace_plusvisibletriggers(this);
+                       cursor = trace_endpos;
+                       cursor_ent = trace_ent;
+                       traced = true;
+               }
                replacement = substring(msg, p, 2);
                escape = substring(msg, p + 1, 1);
  
                        case "%": replacement = "%"; break;
                        case "\\":replacement = "\\"; break;
                        case "n": replacement = "\n"; break;
-                       case "a": replacement = ftos(floor(this.armorvalue)); break;
-                       case "h": replacement = ftos(floor(this.health)); break;
+                       case "a": replacement = ftos(floor(GetResource(this, RES_ARMOR))); break;
+                       case "h": replacement = ftos(floor(GetResource(this, RES_HEALTH))); break;
                        case "l": replacement = NearestLocation(this.origin); break;
                        case "y": replacement = NearestLocation(cursor); break;
                        case "d": replacement = NearestLocation(this.death_origin); break;
@@@ -371,8 -390,6 +390,8 @@@ REPLICATE(autoswitch, bool, "cl_autoswi
  
  REPLICATE(cvar_cl_allow_uid2name, bool, "cl_allow_uid2name");
  
 +REPLICATE(cvar_cl_allow_uidranking, bool, "cl_allow_uidranking");
 +
  REPLICATE(cvar_cl_autoscreenshot, int, "cl_autoscreenshot");
  
  REPLICATE(cvar_cl_autotaunt, float, "cl_autotaunt");
@@@ -399,6 -416,12 +418,12 @@@ REPLICATE(cvar_cl_weaponimpulsemode, in
  
  REPLICATE(cvar_g_xonoticversion, string, "g_xonoticversion");
  
+ REPLICATE(cvar_cl_cts_noautoswitch, bool, "cl_cts_noautoswitch");
+ REPLICATE(cvar_cl_weapon_switch_reload, bool, "cl_weapon_switch_reload");
+ REPLICATE(cvar_cl_weapon_switch_fallback_to_impulse, bool, "cl_weapon_switch_fallback_to_impulse");
  /**
   * @param f -1: cleanup, 0: request, 1: receive
   */
@@@ -406,6 -429,9 +431,9 @@@ void GetCvars(entity this, entity store
  {
        string s = string_null;
  
+       if (f == 0)
+               LOG_INFO("Warning: requesting cvar values is deprecated. Client should send them automatically using REPLICATE.\n");
        if (f > 0)
                s = strcat1(argv(f));
  
@@@ -461,13 -487,12 +489,12 @@@ string playername(entity p, bool team_c
          return p.netname;
  }
  
- float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
+ float want_weapon(entity weaponinfo, float allguns)
  {
-       int i = weaponinfo.m_id;
        int d = 0;
        bool allow_mutatorblocked = false;
  
-       if(!i)
+       if(!weaponinfo.m_id)
                return 0;
  
        bool mutator_returnvalue = MUTATOR_CALLHOOK(WantWeapon, weaponinfo, d, allguns, allow_mutatorblocked);
        allow_mutatorblocked = M_ARGV(3, bool);
  
        if(allguns)
-       {
-               if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
-                       d = true;
-               else
-                       d = false;
-       }
+               d = boolean((weaponinfo.spawnflags & WEP_FLAG_NORMAL) && !(weaponinfo.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_SPECIALATTACK)));
        else if(!mutator_returnvalue)
                d = !(!weaponinfo.weaponstart);
  
  
        float t = weaponinfo.weaponstartoverride;
  
-       //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
+       //LOG_INFOF("want_weapon: %s - d: %d t: %d\n", weaponinfo.netname, d, t);
  
        // bit order in t:
        // 1: want or not
        return t;
  }
  
+ /// Weapons the player normally starts with outside weapon arena.
+ WepSet weapons_start()
+ {
+       WepSet ret = '0 0 0';
+       FOREACH(Weapons, it != WEP_Null, {
+               int w = want_weapon(it, false);
+               if (w & 1)
+                       ret |= it.m_wepset;
+       });
+       return ret;
+ }
+ WepSet weapons_all()
+ {
+       WepSet ret = '0 0 0';
+       FOREACH(Weapons, it != WEP_Null, {
+               if (!(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_SPECIALATTACK)))
+                       ret |= it.m_wepset;
+       });
+       return ret;
+ }
+ WepSet weapons_devall()
+ {
+       WepSet ret = '0 0 0';
+       FOREACH(Weapons, it != WEP_Null,
+       {
+               ret |= it.m_wepset;
+       });
+       return ret;
+ }
+ WepSet weapons_most()
+ {
+       WepSet ret = '0 0 0';
+       FOREACH(Weapons, it != WEP_Null, {
+               if ((it.spawnflags & WEP_FLAG_NORMAL) && !(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_SPECIALATTACK)))
+                       ret |= it.m_wepset;
+       });
+       return ret;
+ }
+ void weaponarena_available_all_update(entity this)
+ {
+       if (weaponsInMapAll)
+       {
+               start_weapons = warmup_start_weapons = g_weaponarena_weapons = weapons_start() | (weaponsInMapAll & weapons_all());
+       }
+       else
+       {
+               // if no weapons are available on the map, just fall back to all weapons arena
+               start_weapons = warmup_start_weapons = g_weaponarena_weapons = weapons_all();
+       }
+ }
+ void weaponarena_available_devall_update(entity this)
+ {
+       if (weaponsInMapAll)
+       {
+               start_weapons = warmup_start_weapons = g_weaponarena_weapons = weapons_start() | weaponsInMapAll;
+       }
+       else
+       {
+               // if no weapons are available on the map, just fall back to devall weapons arena
+               start_weapons = warmup_start_weapons = g_weaponarena_weapons = weapons_devall();
+       }
+ }
+ void weaponarena_available_most_update(entity this)
+ {
+       if (weaponsInMapAll)
+       {
+               start_weapons = warmup_start_weapons = g_weaponarena_weapons = weapons_start() | (weaponsInMapAll & weapons_most());
+       }
+       else
+       {
+               // if no weapons are available on the map, just fall back to most weapons arena
+               start_weapons = warmup_start_weapons = g_weaponarena_weapons = weapons_most();
+       }
+ }
  void readplayerstartcvars()
  {
        float i, t;
-       string s;
  
        // initialize starting values for players
        start_weapons = '0 0 0';
        g_weaponarena = 0;
        g_weaponarena_weapons = '0 0 0';
  
-       s = cvar_string("g_weaponarena");
+       string s = cvar_string("g_weaponarena");
  
        MUTATOR_CALLHOOK(SetWeaponArena, s);
        s = M_ARGV(0, string);
        {
                g_weaponarena = 1;
                g_weaponarena_list = "All Weapons";
-               FOREACH(Weapons, it != WEP_Null, {
-                       if(!(it.spawnflags & WEP_FLAG_MUTATORBLOCKED))
-                               g_weaponarena_weapons |= (it.m_wepset);
-               });
+               g_weaponarena_weapons = weapons_all();
        }
        else if (s == "devall")
        {
                g_weaponarena = 1;
-               g_weaponarena_list = "All Weapons"; // TODO: report as more than just all weapons?
-               FOREACH(Weapons, it != WEP_Null,
-               {
-                       g_weaponarena_weapons |= (it.m_wepset);
-               });
+               g_weaponarena_list = "Dev All Weapons";
+               g_weaponarena_weapons = weapons_devall();
        }
        else if (s == "most")
        {
                g_weaponarena = 1;
                g_weaponarena_list = "Most Weapons";
-               FOREACH(Weapons, it != WEP_Null, {
-                       if(!(it.spawnflags & WEP_FLAG_MUTATORBLOCKED))
-                               if(it.spawnflags & WEP_FLAG_NORMAL)
-                                       g_weaponarena_weapons |= (it.m_wepset);
-               });
+               g_weaponarena_weapons = weapons_most();
+       }
+       else if (s == "all_available")
+       {
+               g_weaponarena = 1;
+               g_weaponarena_list = "All Available Weapons";
+               // this needs to run after weaponsInMapAll is initialized
+               InitializeEntity(NULL, weaponarena_available_all_update, INITPRIO_FINDTARGET);
+       }
+       else if (s == "devall_available")
+       {
+               g_weaponarena = 1;
+               g_weaponarena_list = "Dev All Available Weapons";
+               // this needs to run after weaponsInMapAll is initialized
+               InitializeEntity(NULL, weaponarena_available_devall_update, INITPRIO_FINDTARGET);
+       }
+       else if (s == "most_available")
+       {
+               g_weaponarena = 1;
+               g_weaponarena_list = "Most Available Weapons";
+               // this needs to run after weaponsInMapAll is initialized
+               InitializeEntity(NULL, weaponarena_available_most_update, INITPRIO_FINDTARGET);
        }
        else if (s == "none")
        {
                for (i = 0; i < t; ++i)
                {
                        s = argv(i);
-                       FOREACH(Weapons, it != WEP_Null, {
-                               if(it.netname == s)
-                               {
-                                       g_weaponarena_weapons |= (it.m_wepset);
-                                       g_weaponarena_list = strcat(g_weaponarena_list, it.m_name, " & ");
-                                       break;
-                               }
-                       });
+                       Weapon wep = Weapons_fromstr(s);
+                       if(wep != WEP_Null)
+                       {
+                               g_weaponarena_weapons |= (wep.m_wepset);
+                               g_weaponarena_list = strcat(g_weaponarena_list, wep.m_name, " & ");
+                       }
                }
                g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
        }
                });
        }
  
+       if(cvar("g_balance_superweapons_time") < 0)
+               start_items |= IT_UNLIMITED_SUPERWEAPONS;
        if(!cvar("g_use_ammunition"))
                start_items |= IT_UNLIMITED_AMMO;
  
                start_ammo_plasma = cvar("g_start_ammo_plasma");
                start_ammo_fuel = cvar("g_start_ammo_fuel");
                random_start_weapons_count = cvar("g_random_start_weapons_count");
-               SetResourceAmount(random_start_ammo, RESOURCE_SHELLS, cvar(
-                       "g_random_start_shells"));
-               SetResourceAmount(random_start_ammo, RESOURCE_BULLETS, cvar(
-                       "g_random_start_bullets"));
-               SetResourceAmount(random_start_ammo, RESOURCE_ROCKETS,
-                       cvar("g_random_start_rockets"));
-               SetResourceAmount(random_start_ammo, RESOURCE_CELLS, cvar(
-                       "g_random_start_cells"));
-               SetResourceAmount(random_start_ammo, RESOURCE_PLASMA, cvar(
-                       "g_random_start_plasma"));
+               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_CELLS, cvar("g_random_start_cells"));
+               SetResource(random_start_ammo, RES_PLASMA, cvar("g_random_start_plasma"));
        }
  
-       if (warmup_stage)
+       warmup_start_ammo_shells = start_ammo_shells;
+       warmup_start_ammo_nails = start_ammo_nails;
+       warmup_start_ammo_rockets = start_ammo_rockets;
+       warmup_start_ammo_cells = start_ammo_cells;
+       warmup_start_ammo_plasma = start_ammo_plasma;
+       warmup_start_ammo_fuel = start_ammo_fuel;
+       warmup_start_health = start_health;
+       warmup_start_armorvalue = start_armorvalue;
+       warmup_start_weapons = start_weapons;
+       warmup_start_weapons_default = start_weapons_default;
+       warmup_start_weapons_defaultmask = start_weapons_defaultmask;
+       if (!g_weaponarena)
        {
-               warmup_start_ammo_shells = start_ammo_shells;
-               warmup_start_ammo_nails = start_ammo_nails;
-               warmup_start_ammo_rockets = start_ammo_rockets;
-               warmup_start_ammo_cells = start_ammo_cells;
-               warmup_start_ammo_plasma = start_ammo_plasma;
-               warmup_start_ammo_fuel = start_ammo_fuel;
-               warmup_start_health = start_health;
-               warmup_start_armorvalue = start_armorvalue;
-               warmup_start_weapons = start_weapons;
-               warmup_start_weapons_default = start_weapons_default;
-               warmup_start_weapons_defaultmask = start_weapons_defaultmask;
-               if (!g_weaponarena && !g_ca && !g_freezetag)
-               {
-                       warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
-                       warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
-                       warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
-                       warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
-                       warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
-                       warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
-                       warmup_start_health = cvar("g_warmup_start_health");
-                       warmup_start_armorvalue = cvar("g_warmup_start_armor");
-                       warmup_start_weapons = '0 0 0';
-                       warmup_start_weapons_default = '0 0 0';
-                       warmup_start_weapons_defaultmask = '0 0 0';
-                       FOREACH(Weapons, it != WEP_Null, {
-                               int w = want_weapon(it, g_warmup_allguns);
-                               WepSet s = (it.m_wepset);
-                               if(w & 1)
-                                       warmup_start_weapons |= s;
-                               if(w & 2)
-                                       warmup_start_weapons_default |= s;
-                               if(w & 4)
-                                       warmup_start_weapons_defaultmask |= s;
-                       });
-               }
+               warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
+               warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
+               warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
+               warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
+               warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
+               warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
+               warmup_start_health = cvar("g_warmup_start_health");
+               warmup_start_armorvalue = cvar("g_warmup_start_armor");
+               warmup_start_weapons = '0 0 0';
+               warmup_start_weapons_default = '0 0 0';
+               warmup_start_weapons_defaultmask = '0 0 0';
+               FOREACH(Weapons, it != WEP_Null, {
+                       int w = want_weapon(it, g_warmup_allguns);
+                       WepSet s = it.m_wepset;
+                       if(w & 1)
+                               warmup_start_weapons |= s;
+                       if(w & 2)
+                               warmup_start_weapons_default |= s;
+                       if(w & 4)
+                               warmup_start_weapons_defaultmask |= s;
+               });
        }
  
        if (g_jetpack)
                warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
        }
  
-       WepSet precache_weapons = start_weapons;
-       if (g_warmup_allguns != 1)
-               precache_weapons |= warmup_start_weapons;
-       FOREACH(Weapons, it != WEP_Null, {
-               if(precache_weapons & (it.m_wepset))
-                       it.wr_init(it);
-       });
        start_ammo_shells = max(0, start_ammo_shells);
        start_ammo_nails = max(0, start_ammo_nails);
        start_ammo_rockets = max(0, start_ammo_rockets);
        start_ammo_cells = max(0, start_ammo_cells);
        start_ammo_plasma = max(0, start_ammo_plasma);
        start_ammo_fuel = max(0, start_ammo_fuel);
-       SetResourceAmount(random_start_ammo, RESOURCE_SHELLS, max(0,
-               GetResourceAmount(random_start_ammo, RESOURCE_SHELLS)));
-       SetResourceAmount(random_start_ammo, RESOURCE_BULLETS, max(0,
-               GetResourceAmount(random_start_ammo, RESOURCE_BULLETS)));
-       SetResourceAmount(random_start_ammo, RESOURCE_ROCKETS, max(0,
-               GetResourceAmount(random_start_ammo, RESOURCE_ROCKETS)));
-       SetResourceAmount(random_start_ammo, RESOURCE_CELLS, max(0,
-               GetResourceAmount(random_start_ammo, RESOURCE_CELLS)));
-       SetResourceAmount(random_start_ammo, RESOURCE_PLASMA, max(0,
-               GetResourceAmount(random_start_ammo, RESOURCE_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);
@@@ -738,9 -837,9 +839,9 @@@ void precache_playermodel(string m
        float globhandle, i, n;
        string f;
  
-       if(substring(m, -9,5) == "_lod1")
+       if(substring(m, -9, 5) == "_lod1")
                return;
-       if(substring(m, -9,5) == "_lod2")
+       if(substring(m, -9, 5) == "_lod2")
                return;
        precache_model(m);
        f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
@@@ -872,7 -971,7 +973,7 @@@ void remove_safely(entity e
      builtin_remove(e);
  }
  
- void InitializeEntity(entity e, void(entity this) func, float order)
+ void InitializeEntity(entity e, void(entity this) func, int order)
  {
      entity prev, cur;
  
@@@ -1057,7 -1156,7 +1158,7 @@@ bool SUB_NoImpactCheck(entity this, ent
        if(trace_dphitcontents == 0)
        {
                LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin);
-               checkclient(this);
+               checkclient(this); // TODO: .health is checked in the engine with this, possibly replace with a QC function?
        }
      if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
          return true;
  
  #define SUB_OwnerCheck(ent,oth) ((oth) && ((oth) == (ent).owner))
  
- void W_Crylink_Dequeue(entity e);
  bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher)
  {
        if(SUB_OwnerCheck(this, toucher))
diff --combined xonotic-client.cfg
index 200af1e53ab860ccaacd747ba88c32b2eb10b505,0bb2d446d5a6c5083fc6f70924151cc718dab084..68ee079173d7e2cb6a8bc7a94a95254fff148b35
@@@ -2,8 -2,8 +2,8 @@@
  // if you want to reset your client to defaults, it's probably a better idea to delete (parts of) config.cfg and restart
  
  
- // changes a cvar and reports it to the server (for the menu to notify the
- // server about changes)
+ // changes a cvar and reports it to the server (for the client to notify the server about changes)
+ // DEPRECATED, cvars can be set in the client code to be sent automatically (using REPLICATE)
  alias setreport "set \"$1\" \"$2\" ; sendcvar \"$1\""
  
  seta cl_firststart "" "how many times the client has been run"
@@@ -60,6 -60,9 +60,9 @@@ seta cl_unpress_zoom_on_death 1 "automa
  seta cl_unpress_zoom_on_weapon_switch 1 "automatically unpress zoom when you switch a weapon"
  seta cl_unpress_attack_on_weapon_switch 0 "automatically unpress fire and fire1 attack buttons when you switch a weapon"
  
+ seta cl_weapon_switch_reload 1 "When trying to switch to the currently held weapon, reload it"
+ seta cl_weapon_switch_fallback_to_impulse 1 "When trying to switch to a weapon that is not available, switch to an alternative from the same impulse"
  seta cl_spawn_event_particles 1 "pointparticles effect whenever a player spawns"
  seta cl_spawn_event_sound 1 "sound effect whenever a player spawns"
  //seta cl_spawn_point_model 0 "place a model at all spawn points" // still needs a model
@@@ -72,7 -75,6 +75,6 @@@ sensitivity 
  v_gamma 1
  viewsize 100
  bgmvolume 1
- volume 0.5
  // fullscreen 1024x768x32bit
  vid_bitsperpixel 32
  vid_fullscreen 1
@@@ -197,6 -199,8 +199,8 @@@ seta cl_hitsound_min_pitch 0.75 "minimu
  seta cl_hitsound_max_pitch 1.5 "maximum pitch of hit sound"
  seta cl_hitsound_nom_damage 25 "damage amount at which hitsound bases pitch off"
  
+ seta cl_eventchase_spectated_change 1 "camera goes into 3rd person mode for a moment when changing spectated player"
+ seta cl_eventchase_spectated_change_time 1 "how much time the effect lasts when changing spectated player"
  seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead; set to 2 to active the effect only when the corpse doesn't move anymore"
  seta cl_eventchase_frozen 0 "camera goes into 3rd person mode when the player is frozen"
  seta cl_eventchase_nexball 1 "camera goes into 3rd person mode when in nexball game-mode"
@@@ -229,11 -233,19 +233,19 @@@ cl_movement 
  cl_movement_track_canjump 0
  cl_stairsmoothspeed 200
  
- alias g_waypointeditor_spawn "impulse 103"
- alias g_waypointeditor_remove "impulse 104"
- alias g_waypointeditor_relinkall "impulse 105"
- alias g_waypointeditor_saveall "impulse 106"
- alias g_waypointeditor_unreachable "impulse 107"
+ alias wpeditor_menu "quickmenu file \"\" wpeditor.txt"
+ alias g_waypointeditor_spawn         "wpeditor spawn"
+ alias g_waypointeditor_remove        "wpeditor remove"
+ alias g_waypointeditor_relinkall     "wpeditor relinkall"
+ alias g_waypointeditor_saveall       "wpeditor saveall"
+ alias g_waypointeditor_unreachable   "wpeditor unreachable"
+ alias navwaypoint_relink        g_waypointeditor_spawn
+ alias navwaypoint_remove        g_waypointeditor_remove
+ alias navwaypoint_save          g_waypointeditor_relinkall
+ alias navwaypoint_spawn         g_waypointeditor_saveall
+ alias navwaypoint_unreachable   g_waypointeditor_unreachable
  
  seta menu_sandbox_spawn_model ""
  seta menu_sandbox_attach_bone ""
@@@ -294,8 -306,6 +306,6 @@@ r_glsl_offsetmapping_lod 
  r_glsl_offsetmapping_reliefmapping 0
  r_glsl_offsetmapping_scale 0.02
  
- scr_conalpha 1
- scr_conbrightness 0.2
  scr_screenshot_jpeg 1
  scr_screenshot_jpeg_quality 0.9
  
@@@ -384,6 -394,8 +394,8 @@@ set g_waypointsprite_spam 0 "Debugging 
  set g_waypointsprite_timealphaexponent 1
  seta g_waypointsprite_turrets 1 "disable turret waypoints"
  seta g_waypointsprite_turrets_maxdist 5000 "max distance for turret waypoints"
+ seta g_waypointsprite_turrets_text 0 "show the turret's name in the waypoint"
+ seta g_waypointsprite_turrets_onlyhurt 0 "only show the turret waypoint for a short period after being hurt"
  seta g_waypointsprite_uppercase 1
  seta g_waypointsprite_text 0 "Always show text instead of icons, setting this to 0 will still use text if the icon is unavailable"
  seta g_waypointsprite_iconsize 32
@@@ -417,13 -429,16 +429,16 @@@ seta cl_damagetext_size_max 16 "Damage 
  seta cl_damagetext_size_max_damage 140 "How much damage is considered large"
  seta cl_damagetext_alpha_start "1" "Damage text initial alpha"
  seta cl_damagetext_alpha_lifetime "3" "Damage text lifetime in seconds"
- seta cl_damagetext_velocity "0 0 20" "Damage text move direction"
- seta cl_damagetext_offset "0 -40 0" "Damage text offset"
+ seta cl_damagetext_velocity_screen "0 0 0" "Damage text move direction (screen coordinates)"
+ seta cl_damagetext_velocity_world "0 0 20" "Damage text move direction (world coordinates relative to player's view)"
+ seta cl_damagetext_offset_screen "0 -45 0" "Damage text offset (screen coordinates)"
+ seta cl_damagetext_offset_world "0 0 0" "Damage text offset (world coordinates relative to player's view)"
  seta cl_damagetext_accumulate_range "30" "Damage text spawned within this range is accumulated"
  seta cl_damagetext_accumulate_alpha_rel "0.65" "Only update existing damage text when it's above this much percentage (0 to 1) of the starting alpha"
- seta cl_damagetext_friendlyfire "1" "Show damage text for friendlyfire too"
+ seta cl_damagetext_friendlyfire "1" "0: never show for friendly fire, 1: when more than 0 damage, 2: always"
  seta cl_damagetext_friendlyfire_color "1 0 0" "Damage text color for friendlyfire"
  
+ seta cl_damagetext_2d 1 "Show damagetext in 2D coordinates if the enemy's location is not known"
  seta cl_damagetext_2d_pos "0.47 0.53 0" "2D damage text initial position (X and Y between 0 and 1)"
  seta cl_damagetext_2d_alpha_start 1 "2D damage text initial alpha"
  seta cl_damagetext_2d_alpha_lifetime 1.3 "2D damage text lifetime (alpha fading) in seconds"
@@@ -431,7 -446,7 +446,7 @@@ seta cl_damagetext_2d_size_lifetime 3 "
  seta cl_damagetext_2d_velocity "-25 0 0" "2D damage text move direction (screen coordinates)"
  seta cl_damagetext_2d_overlap_offset "0 -15 0" "Offset 2D damage text by this much to prevent overlapping (screen coordinates)"
  seta cl_damagetext_2d_close_range 125 "Always use 2D damagetext for hits closer that this"
- seta cl_damagetext_2d_out_of_view 1 "Always use 2D damagetext for hits that occured off-screen"
+ seta cl_damagetext_2d_out_of_view 1 "Always use 2D damagetext for hits that occurred off-screen"
  
  seta cl_vehicles_alarm 1 "Play an alarm sound when the vehicle you are driving is heavily damaged"
  seta cl_vehicles_hud_tactical 1
@@@ -596,11 -611,12 +611,12 @@@ makesaved music_playlist_random
  
  cl_netfps 60 // should match or be a multiple of sys_ticrate
  
- seta gl_texturecompression 0
+ gl_texture_anisotropy 8
+ seta gl_texturecompression 0 // FIXME the description is wrong - when this is 0, e.g. gl_texturecompression_sky still takes effect
  gl_texturecompression_color 1
  gl_texturecompression_gloss 1
  gl_texturecompression_glow 1
- gl_texturecompression_lightcubemaps 1
+ gl_texturecompression_lightcubemaps 0
  gl_texturecompression_q3bsplightmaps 0
  gl_texturecompression_sky 1
  
@@@ -631,9 -647,9 +647,9 @@@ alias _gl_flashblend_update_01 "gl_flas
  alias _gl_flashblend_update_11 "gl_flashblend 0"
  alias gl_flashblend_update "_gl_flashblend_update_$r_shadow_realtime_dlight$r_showsurfaces"
  
- set cl_handicap 1     "multiplies damage received and divides damage dealt NOTE: reconnect or use 'sendcvar cl_handicap' to update the choice."
+ set cl_handicap 1     "multiplies damage received and divides damage dealt"
  
- seta cl_clippedspectating 1 "movement collision for spectators so that you can't pass through walls and such. (client setting) NOTE: reconnect or use sendcvar command to update the choice."
+ seta cl_clippedspectating 1 "movement collision for spectators so that you can't pass through walls and such"
  
  seta cl_autoscreenshot 1 "Take a screenshot upon the end of a match... 0 = Disable completely, 1 = Allow sv_autoscreenshot to take a screenshot when requested, 2 = Always take an autoscreenshot anyway."
  
@@@ -642,6 -658,8 +658,8 @@@ seta cl_jetpack_jump 1 "Activate jetpac
  seta cl_race_cptimes_showself 1 "Always show your own times as well as the current best on checkpoints in Race/CTS"
  seta cl_race_cptimes_onlyself 0 "Only show your own times on checkpoints in Race/CTS"
  
+ seta cl_cts_noautoswitch 0 "Prevent forced switching to new weapons in CTS"
  set cl_stripcolorcodes 0      "experimental feature (notes: strips ALL color codes from messages!)"
  
  // Demo camera
@@@ -677,8 -695,8 +695,8 @@@ set cl_effects_lightningarc_branchfacto
  
  set menu_updatecheck_getpacks 1 "get update packs from update server"
  
- set cl_loddistance1 1024
- set cl_loddistance2 3072
+ seta cl_loddistance1 1024
+ seta cl_loddistance2 3072
  seta cl_playerdetailreduction 4       "the higher, the less detailed player models are displayed (LOD)"
  seta cl_modeldetailreduction 1        "the higher, the less detailed certain map models are displayed (LOD)"
  
@@@ -715,6 -733,8 +733,8 @@@ seta cl_forcemyplayercolors 0 "set to t
  seta cl_movement_errorcompensation 1 "try to compensate for prediction errors and reduce perceived lag"
  seta cl_movement_intermissionrunning 0 "keep velocity after the match ends, players may appear to continue running while stationary"
  
+ seta cl_viewmodel_alpha 0 "Maximum transparency of the view model, set to 0 to disable"
  set debugdraw 0
  set debugdraw_filter ""
  set debugdraw_filterout ""
@@@ -744,7 -764,6 +764,6 @@@ scr_conalpha2factor 0.
  scr_conalpha3factor 1
  scr_conalphafactor 0.8
  scr_conbrightness 0.35
- scr_conforcewhiledisconnected 1
  scr_conscroll2_x 0.11
  scr_conscroll2_y 0.2
  scr_conscroll3_x 0
@@@ -771,17 -790,14 +790,14 @@@ r_cullentities_trace 
  r_shadow_glossexact 1
  r_shadow_glossintensity 1
  
- // use fake light if map has no lightmaps
- r_fakelight 1
+ // use slightly better lighting than r_fullbright if map has no lightmaps, and for fullbrightplayers
+ r_fullbright_directed 1
  
  r_water_hideplayer 1 // hide your own feet/player model in refraction views, this way you don't see half of your body under water
  r_water_refractdistort 0.019
  
  set cl_rainsnow_maxdrawdist 2048
  
- // equalize looks better than fullbright
- r_equalize_entities_fullbright 1
  // safe font defaults
  r_font_hinting 1
  r_font_disable_freetype 0
@@@ -793,7 -809,6 +809,7 @@@ set cl_db_saveasdump 0 "write client.d
  // uid2name
  seta cl_allow_uid2name -1 "-1 = ask if the player wants to disable/enable this feature, 0 = disable, 1 = enable uid2name (allows showing your name in race rankings for instance)"
  seta cl_allow_uidtracking 1 "-1 = ask if the player wants to disable/enable this feature, 0 = disable, 1 = enable uid tracking (allows associating your data with your player ID)"
 +seta cl_allow_uidranking 1 "0 = disable, 1 = enable uid ranking (allows statistics like elo to rank you in leaderboards)"
  // FIXME set to -1 before release, once we have a dialog for this!
  
  // polygonoffset for submodel SUCKS SUCKS SUCKS (only a hack for quake1, we don't need that)