]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/showspecs
authorMario <mario@smbclan.net>
Thu, 21 Jul 2016 16:21:06 +0000 (02:21 +1000)
committerMario <mario@smbclan.net>
Thu, 21 Jul 2016 16:21:06 +0000 (02:21 +1000)
1  2 
defaultXonotic.cfg
qcsrc/client/autocvars.qh
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/server/cl_client.qc

diff --combined defaultXonotic.cfg
index c26d0c8ca0ad72f39f97c89d930feb9717188bf5,30ed7c988fdf9dee1293ff8887a039b52447ecb8..1be91c72f2034c603985cf49b3ba2ab594020bdd
@@@ -774,7 -774,7 +774,7 @@@ seta g_waypointsprite_turrets 1 "disabl
  seta g_waypointsprite_turrets_maxdist 5000 "max distace for turret sprites"
  seta g_waypointsprite_tactical 1 "tactical overlay on turrets when in a vehicle"
  
- seta cl_damagetext "0" "Draw damage dealt where you hit the enemy"
+ seta cl_damagetext "1" "Draw damage dealt where you hit the enemy"
  seta cl_damagetext_format "-{total}" "How to format the damage text. {health}, {armor}, {total}"
  seta cl_damagetext_color "1 1 0" "Damage text color"
  seta cl_damagetext_color_per_weapon "0" "Damage text uses weapon color"
@@@ -784,6 -784,8 +784,8 @@@ seta cl_damagetext_alpha_lifetime "3" "
  seta cl_damagetext_velocity "0 0 20" "Damage text move direction"
  seta cl_damagetext_offset "0 -40 0" "Damage text offset"
  seta cl_damagetext_accumulate_range "30" "Damage text spawned within this range is accumulated"
+ seta cl_damagetext_friendlyfire "1" "Show damage text for friendlyfire too"
+ seta cl_damagetext_friendlyfire_color "1 0 0" "Damage text color for friendlyfire"
  
  set sv_itemstime 1 "enable networking of left time until respawn for items such as mega health and large armor"
  
@@@ -978,6 -980,8 +980,8 @@@ gl_texturecompression_lightcubemaps 
  gl_texturecompression_q3bsplightmaps 0
  gl_texturecompression_sky 1
  
+ cl_maxfps 200
  seta menu_mouse_absolute 1 "use the OS mouse pointer motion for menu"
  seta menu_mouse_speed 1 "speed multiplier for the mouse in the menu (does not affect in-game aiming)"
  set menu_use_default_hostname 1
@@@ -1005,6 -1009,8 +1009,8 @@@ seta menu_cdtrack "rising-of-the-phoeni
  
  set sv_maxidle 0 "kick players idle for more than this amount of time in seconds"
  set sv_maxidle_spectatorsareidle 0 "when sv_maxidle is not 0, assume spectators are idle too"
+ set sv_maxidle_slots 0 "when not 0, only kick idlers when this many or less player slots are available"
+ set sv_maxidle_slots_countbots 1 "count bots as player slots"
  
  // these entities are not referenced by anything directly, they just represent
  // teams and are found by find() when needed
@@@ -1193,6 -1199,7 +1199,7 @@@ seta cl_forcemyplayermodel "" "set to t
  seta cl_forcemyplayerskin 0 "set to the skin number you want to show yourself as (does not affect how enemies look with cl_forceplayermodels)"
  seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_color) for your own player model (ignored in teamplay; does not affect how enemies look with cl_forceplayermodels)"
  seta cl_movement_errorcompensation 1 "try to compensate for prediction errors and reduce preceived lag"
+ seta cl_movement_intermissionrunning 0 "keep velocity after the match ends, players may appear to continue running while stationary"
  
  // campaign internal, set when loading a campaign map1G
  set _campaign_index ""
@@@ -1290,6 -1297,8 +1297,8 @@@ r_fakelight 
  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
  // strength sound settings
  set sv_strengthsound_antispam_time 0.1 "minimum distance of strength sounds"
  set sv_strengthsound_antispam_refire_threshold 0.04 "apply minimum distance only if refire of the gun is smaller than this"
@@@ -1445,12 -1454,7 +1454,12 @@@ set cl_fullbright_items 0 "enable fullb
  set cl_weapon_stay_color "2 0.5 0.5" "Color of picked up weapons when g_weapon_stay > 0"
  set cl_weapon_stay_alpha 0.75 "Alpha of picked up weapons when g_weapon_stay > 0"
  
 +<<<<<<< HEAD
 +set sv_showspectators 0
 +set cl_showspectators 1
 +=======
  // Facility for config.cfg use ONLY.
  // Interpreted in post-config.cfg.
  seta menu_forced_saved_cvars "" "These cvars will always be saved, despite engine/Xonotic cvar saving status"
  set menu_reverted_nonsaved_cvars "" "These cvars are currently marked as saved in the flags, but have been reverted and won't stay saved. INTERNAL USE ONLY."
 +>>>>>>> master
index 18b3b78e6b50cee29c02cc50710b1414693c9348,d159cd3170e27426c5d40653f8f6141c463e6ccb..ec9ac10ae622f580953e8da23b52f165f127d4ab
@@@ -418,7 -418,7 +418,7 @@@ float autocvar_scoreboard_offset_left
  float autocvar_scoreboard_offset_right;
  float autocvar_scoreboard_offset_vertical;
  float autocvar_scoreboard_respawntime_decimals;
float autocvar_scoreboard_dynamichud = 1;
bool autocvar_scoreboard_dynamichud = false;
  bool autocvar_v_flipped;
  float autocvar_vid_conheight;
  float autocvar_vid_conwidth;
@@@ -464,7 -464,6 +464,7 @@@ string autocvar__cl_playermodel
  float autocvar_cl_deathglow;
  bool autocvar_developer_csqcentities;
  float autocvar_g_jetpack_attenuation;
 +bool autocvar_cl_showspectators;
  string autocvar_crosshair_hmg = "";
  vector autocvar_crosshair_hmg_color = '0.2 1.0 0.2';
  float autocvar_crosshair_hmg_alpha = 1;
diff --combined qcsrc/client/main.qc
index 57c08d7cd697b98f58d9652534c0bba40b77ed7e,c96b3c8bdd6e17dc7678b949021f805ce6230584..9637c98c55542fd7b173019550e2714885e3a7ac
@@@ -516,22 -516,6 +516,22 @@@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool 
        else
                angles_held_status = 0;
  
 +      if(f & 16)
 +      {
 +              num_spectators = ReadByte();
 +
 +              float i, slot;
 +
 +              for(i = 0; i < MAX_SPECTATORS; ++i)
 +                      spectatorlist[i] = 0; // reset list first
 +
 +              for(i = 0; i < num_spectators; ++i)
 +              {
 +                      slot = ReadByte();
 +                      spectatorlist[i] = slot - 1;
 +              }
 +      }
 +
        return = true;
  
        if(newspectatee_status != spectatee_status)
@@@ -700,6 -684,8 +700,8 @@@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool 
        spn_origin.y = ReadCoord();
        spn_origin.z = ReadCoord();
  
+       this.team = (teamnum + 1);
        //if(is_new)
        //{
                this.origin = spn_origin;
@@@ -796,8 -782,8 +798,8 @@@ NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool 
  
  // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.
  // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.
- void CSQC_Ent_Update(bool isnew)
- {ENGINE_EVENT();
+ void CSQC_Ent_Update(entity this, bool isnew)
+ {
        this.sourceLoc = __FILE__ ":" STR(__LINE__);
        int t = ReadByte();
  
                if (autocvar_developer_csqcentities)
              LOG_INFOF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t);
                done = it.m_read(this, NULL, isnew);
+               MUTATOR_CALLHOOK(Ent_Update, this, isnew);
                break;
        });
        time = savetime;
@@@ -876,8 -863,8 +879,8 @@@ void Ent_Remove(entity this
        // TODO possibly set more stuff to defaults
  }
  // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(this) as well.
- void CSQC_Ent_Remove()
- {ENGINE_EVENT();
+ void CSQC_Ent_Remove(entity this)
+ {
        if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype);
        if (wasfreed(this))
        {
@@@ -999,6 -986,42 +1002,42 @@@ NET_HANDLE(ENT_CLIENT_INIT, bool isnew
        if (!postinit) PostInit();
  }
  
+ float GetSpeedUnitFactor(int speed_unit)
+ {
+       switch(speed_unit)
+       {
+               default:
+               case 1:
+                       return 1.0;
+               case 2:
+                       return 0.0254;
+               case 3:
+                       return 0.0254 * 3.6;
+               case 4:
+                       return 0.0254 * 3.6 * 0.6213711922;
+               case 5:
+                       return 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
+       }
+ }
+ string GetSpeedUnit(int speed_unit)
+ {
+       switch(speed_unit)
+       {
+               default:
+               case 1:
+                       return _(" qu/s");
+               case 2:
+                       return _(" m/s");
+               case 3:
+                       return _(" km/h");
+               case 4:
+                       return _(" mph");
+               case 5:
+                       return _(" knots");
+       }
+ }
  NET_HANDLE(TE_CSQC_RACE, bool isNew)
  {
        int b = ReadByte();
                        race_server_record = ReadInt24_t();
                        break;
                case RACE_NET_SPEED_AWARD:
-                       race_speedaward = ReadInt24_t();
+                       race_speedaward = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit);
                        if(race_speedaward_holder)
                                strunzone(race_speedaward_holder);
                        race_speedaward_holder = strzone(ReadString());
+                       if(race_speedaward_unit)
+                               strunzone(race_speedaward_unit);
+                       race_speedaward_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit));
                        break;
                case RACE_NET_SPEED_AWARD_BEST:
-                       race_speedaward_alltimebest = ReadInt24_t();
+                       race_speedaward_alltimebest = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit);
                        if(race_speedaward_alltimebest_holder)
                                strunzone(race_speedaward_alltimebest_holder);
                        race_speedaward_alltimebest_holder = strzone(ReadString());
+                       if(race_speedaward_alltimebest_unit)
+                               strunzone(race_speedaward_alltimebest_unit);
+                       race_speedaward_alltimebest_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit));
                        break;
                case RACE_NET_SERVER_RANKINGS:
                        float prevpos, del;
diff --combined qcsrc/client/main.qh
index 65aad3e9f64357a290a707eb8c4f32b629c38765,15362aad339c875a4e9f15f3fd333e34396b580f..bc6b5afec0d4059f446ba60df7443b035d7d4163
@@@ -140,10 -140,8 +140,13 @@@ float g_trueaim_minrange
  
  float hud;
  float view_quality;
 +
 +int num_spectators;
 +const int MAX_SPECTATORS = 7;
 +int spectatorlist[MAX_SPECTATORS];
 +
  int framecount;
  .float health;
+ float GetSpeedUnitFactor(int speed_unit);
+ string GetSpeedUnit(int speed_unit);
index c2a5877761c419c2aaa6adba704a371387198ae0,e0b7e116eae253a3b6f85f3dc43e352b69774f99..f375e71f0a428ae3516ed1c7e99e214c1a77ad07
  
  STATIC_METHOD(Client, Add, void(Client this, int _team))
  {
-     WITHSELF(this, ClientConnect());
+     ClientConnect(this);
      TRANSMUTE(Player, this);
      this.frame = 12; // 7
      this.team = _team;
-     WITHSELF(this, PutClientInServer());
+     PutClientInServer(this);
  }
  
  void PutObserverInServer(entity this);
- void ClientDisconnect();
  
  STATIC_METHOD(Client, Remove, void(Client this))
  {
      TRANSMUTE(Observer, this);
-     WITHSELF(this, PutClientInServer());
-     WITHSELF(this, ClientDisconnect());
+     PutClientInServer(this);
+     ClientDisconnect(this);
  }
  
  void send_CSQC_teamnagger() {
        WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
  }
  
 +int CountSpectators(entity player, entity to)
 +{
 +      if(!player) { return 0; } // not sure how, but best to be safe
 +
 +      int spec_count = 0;
 +
 +      FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
 +      {
 +              spec_count++;
 +      });
 +
 +      return spec_count;
 +}
 +
 +void WriteSpectators(entity player, entity to)
 +{
 +      if(!player) { return; } // not sure how, but best to be safe
 +
 +      FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
 +      {
 +              WriteByte(MSG_ENTITY, num_for_edict(it));
 +      });
 +}
 +
  bool ClientData_Send(entity this, entity to, int sf)
  {
        assert(to == this.owner, return false);
        if (to.spectatee_status)    sf |= 2; // spectator ent number follows
        if (e.zoomstate)            sf |= 4; // zoomed
        if (e.porto_v_angle_held)   sf |= 8; // angles held
 +      sf |= 16; // always check spectators
  
        WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
        WriteByte(MSG_ENTITY, sf);
                WriteAngle(MSG_ENTITY, e.v_angle.x);
                WriteAngle(MSG_ENTITY, e.v_angle.y);
        }
 +
 +      if(sf & 16)
 +      {
 +              float specs = CountSpectators(e, to);
 +              WriteByte(MSG_ENTITY, specs);
 +              WriteSpectators(e, to);
 +      }
 +
        return true;
  }
  
@@@ -258,7 -224,6 +257,7 @@@ void PutObserverInServer(entity this
      RemoveGrapplingHook(this);
        Portal_ClearAll(this);
        Unfreeze(this);
 +      SetSpectatee(this, world);
  
        if (this.alivetime)
        {
@@@ -474,8 -439,8 +473,8 @@@ void FixPlayermodel(entity player
  
  
  /** Called when a client spawns in the server */
- void PutClientInServer()
- {ENGINE_EVENT();
+ void PutClientInServer(entity this)
+ {
        if (IS_BOT_CLIENT(this)) {
                TRANSMUTE(Player, this);
        } else if (IS_REAL_CLIENT(this)) {
        if (IS_OBSERVER(this)) {
                PutObserverInServer(this);
        } else if (IS_PLAYER(this)) {
+               if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+               
                PlayerState_attach(this);
                accuracy_resend(this);
  
@@@ -765,8 -732,8 +766,8 @@@ void SetNewParms (
  SetChangeParms
  =============
  */
- void SetChangeParms ()
- {ENGINE_EVENT();
+ void SetChangeParms (entity this)
+ {
        // save parms for level change
        parm1 = this.parm_idlesince - time;
  
@@@ -813,7 -780,7 +814,7 @@@ void ClientKill_Now_TeamChange(entity t
                PutObserverInServer(this);
        }
        else
-               WITHSELF(this, SV_ChangeTeam(this.killindicator_teamchange - 1));
+               SV_ChangeTeam(this, this.killindicator_teamchange - 1);
        this.killindicator_teamchange = 0;
  }
  
@@@ -837,7 -804,7 +838,7 @@@ void ClientKill_Now(entity this
        if(this.killindicator_teamchange)
                ClientKill_Now_TeamChange(this);
  
-       if(IS_PLAYER(this))
+       if(!IS_SPEC(this) && !IS_OBSERVER(this))
                Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0');
  
        // now I am sure the player IS dead
@@@ -887,7 -854,6 +888,6 @@@ void ClientKill_TeamChange (entity this
  {
        float killtime;
        float starttime;
-       entity e;
  
        if (gameover)
                return;
                        this.killindicator.count = bound(0, ceil(killtime), 10);
                        //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n"));
  
-                       for(e = NULL; (e = find(e, classname, "body")) != NULL; )
+                       FOREACH_ENTITY_ENT(enemy, this,
                        {
-                               if(e.enemy != this)
+                               if(it.classname != "body")
                                        continue;
-                               e.killindicator = spawn();
-                               e.killindicator.owner = e;
-                               e.killindicator.scale = 0.5;
-                               setattachment(e.killindicator, e, "");
-                               setorigin(e.killindicator, '0 0 52');
-                               setthink(e.killindicator, KillIndicator_Think);
-                               e.killindicator.nextthink = starttime + (e.lip) * 0.05;
-                               clientkilltime = max(clientkilltime, e.killindicator.nextthink + 0.05);
-                               e.killindicator.cnt = ceil(killtime);
-                       }
+                               it.killindicator = spawn();
+                               it.killindicator.owner = it;
+                               it.killindicator.scale = 0.5;
+                               setattachment(it.killindicator, it, "");
+                               setorigin(it.killindicator, '0 0 52');
+                               setthink(it.killindicator, KillIndicator_Think);
+                               it.killindicator.nextthink = starttime + (it.lip) * 0.05;
+                               //clientkilltime = max(clientkilltime, it.killindicator.nextthink + 0.05);
+                               it.killindicator.cnt = ceil(killtime);
+                       });
                        this.lip = 0;
                }
        }
  
  }
  
- void ClientKill ()
- {ENGINE_EVENT();
+ void ClientKill (entity this)
+ {
        if(gameover) return;
        if(this.player_blocked) return;
        if(STAT(FROZEN, this)) return;
@@@ -1051,8 -1017,8 +1051,8 @@@ ClientConnec
  Called when a client connects to the server
  =============
  */
- void ClientConnect()
- {ENGINE_EVENT();
+ void ClientConnect(entity this)
+ {
        if (Ban_MaybeEnforceBanOnce(this)) return;
        assert(!IS_CLIENT(this), return);
        this.flags |= FL_CLIENT;
@@@ -1207,8 -1173,8 +1207,8 @@@ Called when a client disconnects from t
  */
  .entity chatbubbleentity;
  void ReadyCount();
- void ClientDisconnect()
- {ENGINE_EVENT();
+ void ClientDisconnect(entity this)
+ {
        assert(IS_CLIENT(this), return);
  
        PlayerStats_GameReport_FinalizePlayer(this);
  
        Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
  
 +      SetSpectatee(this, NULL);
 +
      MUTATOR_CALLHOOK(ClientDisconnect, this);
  
        ClientState_detach(this);
@@@ -1337,7 -1301,7 +1337,7 @@@ void respawn(entity this
        CopyBody(this, 1);
  
        this.effects |= EF_NODRAW; // prevent another CopyBody
-       WITHSELF(this, PutClientInServer());
+       PutClientInServer(this);
  }
  
  void play_countdown(entity this, float finished, Sound samp)
@@@ -1661,7 -1625,9 +1661,9 @@@ void SpectateCopy(entity this, entity s
        this.hud = spectatee.hud;
        if(spectatee.vehicle)
      {
-         this.fixangle = false;
+       this.angles = spectatee.v_angle;
+         //this.fixangle = false;
          //this.velocity = spectatee.vehicle.velocity;
          this.vehicle_health = spectatee.vehicle_health;
          this.vehicle_shield = spectatee.vehicle_shield;
          this.vehicle_reload1 = spectatee.vehicle_reload1;
          this.vehicle_reload2 = spectatee.vehicle_reload2;
  
-         msg_entity = this;
+         //msg_entity = this;
  
-         WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
-             WriteAngle(MSG_ONE,  spectatee.v_angle.x);
-             WriteAngle(MSG_ONE,  spectatee.v_angle.y);
-             WriteAngle(MSG_ONE,  spectatee.v_angle.z);
+        // WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
+             //WriteAngle(MSG_ONE,  spectatee.v_angle.x);
+            // WriteAngle(MSG_ONE,  spectatee.v_angle.y);
+            // WriteAngle(MSG_ONE,  spectatee.v_angle.z);
  
          //WriteByte (MSG_ONE, SVC_SETVIEW);
          //    WriteEntity(MSG_ONE, this);
@@@ -1706,8 -1672,6 +1708,8 @@@ bool SpectateSet(entity this
        if(!IS_PLAYER(this.enemy))
                return false;
  
 +      ClientData_Touch(this.enemy);
 +
        msg_entity = this;
        WriteByte(MSG_ONE, SVC_SETVIEW);
        WriteEntity(MSG_ONE, this.enemy);
@@@ -1730,9 -1694,6 +1732,9 @@@ void SetSpectatee(entity this, entity s
        // these are required to fix the spectator bug with arc
        if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
        if(this.enemy && this.enemy.arc_beam) { this.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
 +
 +      // needed to update spectator list
 +      if(old_spectatee) { ClientData_Touch(old_spectatee); }
  }
  
  bool Spectate(entity this, entity pl)
  
  bool SpectateNext(entity this)
  {
-       other = find(this.enemy, classname, STR_PLAYER);
+       entity ent = find(this.enemy, classname, STR_PLAYER);
  
-       if (MUTATOR_CALLHOOK(SpectateNext, this, other))
-               other = M_ARGV(1, entity);
-       else if (!other)
-               other = find(other, classname, STR_PLAYER);
+       if (MUTATOR_CALLHOOK(SpectateNext, this, ent))
+               ent = M_ARGV(1, entity);
+       else if (!ent)
+               ent = find(ent, classname, STR_PLAYER);
  
-       if(other) { SetSpectatee(this, other); }
+       if(ent) { SetSpectatee(this, ent); }
  
        return SpectateSet(this);
  }
  bool SpectatePrev(entity this)
  {
        // NOTE: chain order is from the highest to the lower entnum (unlike find)
-       other = findchain(classname, STR_PLAYER);
-       if (!other) // no player
+       entity ent = findchain(classname, STR_PLAYER);
+       if (!ent) // no player
                return false;
  
-       entity first = other;
+       entity first = ent;
        // skip players until current spectated player
        if(this.enemy)
-       while(other && other != this.enemy)
-               other = other.chain;
+       while(ent && ent != this.enemy)
+               ent = ent.chain;
  
-       switch (MUTATOR_CALLHOOK(SpectatePrev, this, other, first))
+       switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first))
        {
                case MUT_SPECPREV_FOUND:
-                   other = M_ARGV(1, entity);
+                   ent = M_ARGV(1, entity);
                    break;
                case MUT_SPECPREV_RETURN:
-                   other = M_ARGV(1, entity);
                    return true;
                case MUT_SPECPREV_CONTINUE:
                default:
                {
-                       if(other.chain)
-                               other = other.chain;
+                       if(ent.chain)
+                               ent = ent.chain;
                        else
-                               other = first;
+                               ent = first;
                        break;
                }
        }
  
-       SetSpectatee(this, other);
+       SetSpectatee(this, ent);
        return SpectateSet(this);
  }
  
@@@ -1831,8 -1791,6 +1832,8 @@@ void LeaveSpectatorMode(entity this
                {
                        TRANSMUTE(Player, this);
  
 +                      SetSpectatee(self, world);
 +
                        if(autocvar_g_campaign || autocvar_g_balance_teams)
                                { JoinBestTeam(this, false, true); }
  
  
                        Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
  
-                       WITHSELF(this, PutClientInServer());
+                       PutClientInServer(this);
  
                        if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); }
                }
@@@ -2012,7 -1970,7 +2013,7 @@@ void SpectatorThink(entity this
                                TRANSMUTE(Spectator, this);
                        } else {
                                TRANSMUTE(Observer, this);
-                               WITHSELF(this, PutClientInServer());
+                               PutClientInServer(this);
                        }
                        this.impulse = 0;
                } else if(this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) {
                                TRANSMUTE(Spectator, this);
                        } else {
                                TRANSMUTE(Observer, this);
-                               WITHSELF(this, PutClientInServer());
+                               PutClientInServer(this);
                        }
                        this.impulse = 0;
                } else if (PHYS_INPUT_BUTTON_ATCK2(this)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        TRANSMUTE(Observer, this);
-                       WITHSELF(this, PutClientInServer());
+                       PutClientInServer(this);
                } else {
                        if(!SpectateUpdate(this))
                                PutObserverInServer(this);
@@@ -2074,7 -2032,7 +2075,7 @@@ void PlayerUseKey(entity this
  
                        while(head) // find the closest acceptable target to enter
                        {
-                               if(head.vehicle_flags & VHF_ISVEHICLE)
+                               if(IS_VEHICLE(head))
                                if(!IS_DEAD(head))
                                if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this)))
                                if(head.takedamage != DAMAGE_NO)
@@@ -2109,8 -2067,8 +2110,8 @@@ Called every frame for each client befo
  .float usekeypressed;
  .float last_vehiclecheck;
  .int items_added;
- void PlayerPreThink ()
- {ENGINE_EVENT();
+ void PlayerPreThink (entity this)
+ {
        WarpZone_PlayerPhysics_FixVAngle(this);
  
      STAT(GAMESTARTTIME, this) = game_starttime;
  
        MUTATOR_CALLHOOK(PlayerPreThink, this);
  
-       if(autocvar_g_vehicles_enter)
-       if(time > this.last_vehiclecheck)
-       if(IS_PLAYER(this))
-       if(!gameover)
-       if(!STAT(FROZEN, this))
-       if(!this.vehicle)
-       if(!IS_DEAD(this))
-       {
-               entity veh;
-               for(veh = NULL; (veh = findflags(veh, vehicle_flags, VHF_ISVEHICLE)); )
-               if(vdist(veh.origin - this.origin, <, autocvar_g_vehicles_enter_radius))
-               if(!IS_DEAD(veh))
-               if(veh.takedamage != DAMAGE_NO)
-               if((veh.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(veh.owner, this))
-                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
-               else if(!veh.owner)
-               if(!veh.team || SAME_TEAM(this, veh))
-                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
-               else if(autocvar_g_vehicles_steal)
-                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
+       if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !gameover && !this.vehicle)
+       if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this))
+       {
+               FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it),
+               {
+                       if(!IS_DEAD(it) && it.takedamage != DAMAGE_NO)
+                       if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this))
+                       {
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
+                       }
+                       else if(!it.owner)
+                       {
+                               if(!it.team || SAME_TEAM(this, it))
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
+                               else if(autocvar_g_vehicles_steal)
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
+                       }
+               });
  
                this.last_vehiclecheck = time + 1;
        }
                                if (frametime) player_anim(this);
                                bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this));
  
-                               if (this.deadflag == DEAD_DYING) {
-                                       if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max)) {
-                                               this.deadflag = DEAD_RESPAWNING;
-                                       } else if (!button_pressed) {
-                                               this.deadflag = DEAD_DEAD;
-                     }
-                               } else if (this.deadflag == DEAD_DEAD) {
-                                       if (button_pressed) {
-                                               this.deadflag = DEAD_RESPAWNABLE;
-                                       } else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)) {
-                                               this.deadflag = DEAD_RESPAWNING;
-                     }
-                               } else if (this.deadflag == DEAD_RESPAWNABLE) {
-                                       if (!button_pressed) {
-                                               this.deadflag = DEAD_RESPAWNING;
-                     }
-                               } else if (this.deadflag == DEAD_RESPAWNING) {
-                                       if (time > this.respawn_time) {
-                                               this.respawn_time = time + 1; // only retry once a second
-                                               this.respawn_time_max = this.respawn_time;
-                                               respawn(this);
+                               switch(this.deadflag)
+                               {
+                                       case DEAD_DYING:
+                                       {
+                                               if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
+                                                       this.deadflag = DEAD_RESPAWNING;
+                                               else if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
+                                                       this.deadflag = DEAD_DEAD;
+                                               break;
+                                       }
+                                       case DEAD_DEAD:
+                                       {
+                                               if (button_pressed)
+                                                       this.deadflag = DEAD_RESPAWNABLE;
+                                               else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))
+                                                       this.deadflag = DEAD_RESPAWNING;
+                                               break;
+                                       }
+                                       case DEAD_RESPAWNABLE:
+                                       {
+                                               if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
+                                                       this.deadflag = DEAD_RESPAWNING;
+                                               break;
+                                       }
+                                       case DEAD_RESPAWNING:
+                                       {
+                                               if (time > this.respawn_time)
+                                               {
+                                                       this.respawn_time = time + 1; // only retry once a second
+                                                       this.respawn_time_max = this.respawn_time;
+                                                       respawn(this);
+                                               }
+                                               break;
                                        }
                                }
  
                entity e = this.teamkill_soundsource;
                entity oldpusher = e.pusher;
                e.pusher = this;
-               PlayerSound(e, playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY);
+               PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY);
                e.pusher = oldpusher;
        }
  
        if (this.taunt_soundtime && time > this.taunt_soundtime) {
                this.taunt_soundtime = 0;
-               PlayerSound(this, playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT);
+               PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT);
        }
  
        target_voicescript_next(this);
@@@ -2433,10 -2401,10 +2444,10 @@@ void DrownPlayer(entity this
        if(IS_DEAD(this))
                return;
  
-       if (this.waterlevel != WATERLEVEL_SUBMERGED)
+       if (this.waterlevel != WATERLEVEL_SUBMERGED || this.vehicle)
        {
                if(this.air_finished < time)
-                       PlayerSound(this, playersound_gasp, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+                       PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
                this.air_finished = time + autocvar_g_balance_contents_drowndelay;
                this.dmg = 2;
        }
@@@ -2458,14 -2426,25 +2469,25 @@@ Called every frame for each client afte
  =============
  */
  .float idlekick_lasttimeleft;
- void PlayerPostThink ()
- {ENGINE_EVENT();
+ void PlayerPostThink (entity this)
+ {
        if (sv_maxidle > 0)
        if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
        if (IS_REAL_CLIENT(this))
        if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle)
        {
-               if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
+               int totalClients = 0;
+               if(sv_maxidle_slots > 0)
+               {
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) || sv_maxidle_slots_countbots,
+                       {
+                               ++totalClients;
+                       });
+               }
+               if (sv_maxidle_slots > 0 && (maxclients - totalClients) > sv_maxidle_slots)
+               { /* do nothing */ }
+               else if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
                {
                        if (this.idlekick_lasttimeleft)
                        {