]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/cl_client.qc
Merge branch 'Mario/teams_bitflag' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cl_client.qc
index ffbb55087bef4ac48a98e36c8d9402fc9c7eb64c..fe307fc01ac5872538b0ffd5aada4cdd964cab24 100644 (file)
 
 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();
-void ClientDisconnect();
+void PutObserverInServer(entity this);
 
 STATIC_METHOD(Client, Remove, void(Client this))
 {
     TRANSMUTE(Observer, this);
-    WITHSELF(this, PutClientInServer());
-    WITHSELF(this, ClientDisconnect());
+    PutClientInServer(this);
+    ClientDisconnect(this);
 }
 
 void send_CSQC_teamnagger() {
@@ -177,17 +176,16 @@ void setplayermodel(entity e, string modelname)
 {
        precache_model(modelname);
        _setmodel(e, modelname);
-       player_setupanimsformodel();
+       player_setupanimsformodel(e);
        if(!autocvar_g_debug_globalsounds)
                UpdatePlayerSounds(e);
 }
 
 void FixPlayermodel(entity player);
 /** putting a client as observer in the server */
-void PutObserverInServer()
+void PutObserverInServer(entity this)
 {
-       SELFPARAM();
-    bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver);
+    bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this);
        PlayerState_detach(this);
 
        if (IS_PLAYER(this) && this.health >= 1) {
@@ -196,7 +194,7 @@ void PutObserverInServer()
     }
 
     {
-        entity spot = SelectSpawnPoint(true);
+        entity spot = SelectSpawnPoint(this, true);
         if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
         this.angles = spot.angles;
         this.angles_z = 0;
@@ -234,7 +232,7 @@ void PutObserverInServer()
                this.alivetime = 0;
        }
 
-       if (this.vehicle) vehicles_exit(VHEF_RELEASE);
+       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
 
        WaypointSprite_PlayerDead(this);
 
@@ -248,7 +246,7 @@ void PutObserverInServer()
 
        if (this.killcount != FRAGS_SPECTATOR)
        {
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
                if(!intermission_running)
                if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
@@ -271,7 +269,7 @@ void PutObserverInServer()
        this.health = FRAGS_SPECTATOR;
        this.takedamage = DAMAGE_NO;
        this.solid = SOLID_NOT;
-       this.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink
+       set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink
        this.flags = FL_CLIENT | FL_NOTARGET;
        this.armorvalue = 666;
        this.effects = 0;
@@ -294,7 +292,7 @@ void PutObserverInServer()
        this.superweapons_finished = 0;
        this.pushltime = 0;
        this.istypefrag = 0;
-       this.think = func_null;
+       setthink(this, func_null);
        this.nextthink = 0;
        this.hook_time = 0;
        this.deadflag = DEAD_NO;
@@ -384,9 +382,9 @@ void FixPlayermodel(entity player)
                        defaultskin = autocvar_sv_defaultplayerskin;
        }
 
-       MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin);
-       defaultmodel = ret_string;
-       defaultskin = ret_int;
+       MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player);
+       defaultmodel = M_ARGV(0, string);
+       defaultskin = M_ARGV(1, int);
 
        bool chmdl = false;
        int oldskin;
@@ -441,9 +439,8 @@ void FixPlayermodel(entity player)
 
 
 /** Called when a client spawns in the server */
-void PutClientInServer()
+void PutClientInServer(entity this)
 {
-       SELFPARAM();
        if (IS_BOT_CLIENT(this)) {
                TRANSMUTE(Player, this);
        } else if (IS_REAL_CLIENT(this)) {
@@ -463,15 +460,17 @@ void PutClientInServer()
        MUTATOR_CALLHOOK(PutClientInServer, this);
 
        if (IS_OBSERVER(this)) {
-               PutObserverInServer();
+               PutObserverInServer(this);
        } else if (IS_PLAYER(this)) {
+               if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+               
                PlayerState_attach(this);
                accuracy_resend(this);
 
                if (this.team < 0)
                        JoinBestTeam(this, false, true);
 
-               entity spot = SelectSpawnPoint(false);
+               entity spot = SelectSpawnPoint(this, false);
                if (!spot) {
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
                        return; // spawn failed
@@ -482,7 +481,7 @@ void PutClientInServer()
                this.iscreature = true;
                this.teleportable = TELEPORT_NORMAL;
                this.damagedbycontents = true;
-               this.movetype = MOVETYPE_WALK;
+               set_movetype(this, MOVETYPE_WALK);
                this.solid = SOLID_SLIDEBOX;
                this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
                if (autocvar_g_playerclip_collisions)
@@ -547,7 +546,7 @@ void PutClientInServer()
                this.pain_frame = 0;
                this.pain_finished = 0;
                this.pushltime = 0;
-               this.think = func_null; // players have no think function
+               setthink(this, func_null); // players have no think function
                this.nextthink = 0;
                this.dmg_team = 0;
                this.ballistics_density = autocvar_g_ballistics_density_player;
@@ -619,7 +618,7 @@ void PutClientInServer()
 
                // reset fields the weapons may use
                FOREACH(Weapons, true, LAMBDA(
-                       it.wr_resetplayer(it);
+                       it.wr_resetplayer(it, this);
                        // reload all reloadable weapons
                        if (it.spawnflags & WEP_FLAG_RELOADABLE) {
                                this.weapon_load[it.m_id] = it.reloading_ammo;
@@ -629,15 +628,13 @@ void PutClientInServer()
                {
                        string s = spot.target;
                        spot.target = string_null;
-                       WITH(entity, activator, this, LAMBDA(
-                               WITHSELF(spot, SUB_UseTargets())
-                       ));
+                       SUB_UseTargets(spot, this, NULL);
                        spot.target = s;
                }
 
                Unfreeze(this);
 
-               MUTATOR_CALLHOOK(PlayerSpawn, spot);
+               MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
 
                if (autocvar_spawn_debug)
                {
@@ -658,7 +655,7 @@ void PutClientInServer()
        }
 }
 
-void ClientInit_misc();
+void ClientInit_misc(entity this);
 
 .float ebouncefactor, ebouncestop; // electro's values
 // TODO do we need all these fields, or should we stop autodetecting runtime
@@ -672,12 +669,11 @@ bool ClientInit_SendEntity(entity this, entity to, int sf)
        // TODO: make easier to use
        Registry_send_all();
        W_PROP_reload(MSG_ONE, to);
-       ClientInit_misc();
+       ClientInit_misc(this);
        MUTATOR_CALLHOOK(Ent_Init);
 }
-void ClientInit_misc()
+void ClientInit_misc(entity this)
 {
-    SELFPARAM();
        int channel = MSG_ONE;
        WriteHeader(channel, ENT_CLIENT_INIT);
        WriteByte(channel, g_nexball_meter_period * 32);
@@ -699,8 +695,8 @@ void ClientInit_misc()
        WriteCoord(channel, autocvar_g_trueaim_minrange);
 }
 
-void ClientInit_CheckUpdate()
-{SELFPARAM();
+void ClientInit_CheckUpdate(entity this)
+{
        this.nextthink = time;
        if(this.count != autocvar_g_balance_armor_blockpercent)
        {
@@ -710,13 +706,12 @@ void ClientInit_CheckUpdate()
 }
 
 void ClientInit_Spawn()
-{SELFPARAM();
-
+{
        entity e = new_pure(clientinit);
-       e.think = ClientInit_CheckUpdate;
+       setthink(e, ClientInit_CheckUpdate);
        Net_LinkEntity(e, false, 0, ClientInit_SendEntity);
 
-       WITHSELF(e, ClientInit_CheckUpdate());
+       ClientInit_CheckUpdate(e);
 }
 
 /*
@@ -737,8 +732,8 @@ void SetNewParms ()
 SetChangeParms
 =============
 */
-void SetChangeParms ()
-{SELFPARAM();
+void SetChangeParms (entity this)
+{
        // save parms for level change
        parm1 = this.parm_idlesince - time;
 
@@ -782,18 +777,18 @@ void ClientKill_Now_TeamChange(entity this)
        {
                if(blockSpectators)
                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
-               WITHSELF(this, PutObserverInServer());
+               PutObserverInServer(this);
        }
        else
-               WITHSELF(this, SV_ChangeTeam(this.killindicator_teamchange - 1));
+               SV_ChangeTeam(this, this.killindicator_teamchange - 1);
        this.killindicator_teamchange = 0;
 }
 
-void ClientKill_Now()
-{SELFPARAM();
+void ClientKill_Now(entity this)
+{
        if(this.vehicle)
        {
-           vehicles_exit(VHEF_RELEASE);
+           vehicles_exit(this.vehicle, VHEF_RELEASE);
            if(!this.killindicator_teamchange)
            {
             this.vehicle_health = -1;
@@ -804,35 +799,35 @@ void ClientKill_Now()
        if(this.killindicator && !wasfreed(this.killindicator))
                remove(this.killindicator);
 
-       this.killindicator = world;
+       this.killindicator = NULL;
 
        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
 }
-void KillIndicator_Think()
-{SELFPARAM();
+void KillIndicator_Think(entity this)
+{
        if (gameover)
        {
-               this.owner.killindicator = world;
+               this.owner.killindicator = NULL;
                remove(this);
                return;
        }
 
        if (this.owner.alpha < 0 && !this.owner.vehicle)
        {
-               this.owner.killindicator = world;
+               this.owner.killindicator = NULL;
                remove(this);
                return;
        }
 
        if(this.cnt <= 0)
        {
-               WITHSELF(this.owner, ClientKill_Now());
+               ClientKill_Now(this.owner);
                return;
        }
     else if(g_cts && this.health == 1) // health == 1 means that it's silent
@@ -855,11 +850,10 @@ void KillIndicator_Think()
 }
 
 float clientkilltime;
-void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2 = spec
-{SELFPARAM();
+void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec
+{
        float killtime;
        float starttime;
-       entity e;
 
        if (gameover)
                return;
@@ -884,7 +878,7 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
 
                if(killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this))
                {
-                       ClientKill_Now();
+                       ClientKill_Now(this);
                }
                else
                {
@@ -895,27 +889,27 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
                        this.killindicator.scale = 0.5;
                        setattachment(this.killindicator, this, "");
                        setorigin(this.killindicator, '0 0 52');
-                       this.killindicator.think = KillIndicator_Think;
+                       setthink(this.killindicator, KillIndicator_Think);
                        this.killindicator.nextthink = starttime + (this.lip) * 0.05;
                        clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05);
                        this.killindicator.cnt = ceil(killtime);
                        this.killindicator.count = bound(0, ceil(killtime), 10);
                        //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n"));
 
-                       for(e = world; (e = find(e, classname, "body")) != world; )
+                       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');
-                               e.killindicator.think = 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;
                }
        }
@@ -953,13 +947,13 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
 
 }
 
-void ClientKill ()
-{SELFPARAM();
+void ClientKill (entity this)
+{
        if(gameover) return;
        if(this.player_blocked) return;
        if(STAT(FROZEN, this)) return;
 
-       ClientKill_TeamChange(0);
+       ClientKill_TeamChange(this, 0);
 }
 
 void FixClientCvars(entity e)
@@ -1004,7 +998,7 @@ Called once (not at each match start) when a client begins a connection to the s
 =============
 */
 void ClientPreConnect ()
-{SELFPARAM();
+{ENGINE_EVENT();
        if(autocvar_sv_eventlog)
        {
                GameLogEcho(sprintf(":connect:%d:%d:%s",
@@ -1023,9 +1017,8 @@ ClientConnect
 Called when a client connects to the server
 =============
 */
-void ClientConnect()
+void ClientConnect(entity this)
 {
-       SELFPARAM();
        if (Ban_MaybeEnforceBanOnce(this)) return;
        assert(!IS_CLIENT(this), return);
        this.flags |= FL_CLIENT;
@@ -1144,7 +1137,7 @@ void ClientConnect()
                if (!autocvar_g_campaign)
                {
                        this.motd_actived_time = -1;
-                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage());
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
                }
 
                if (g_weaponarena_weapons == WEPSET(TUBA))
@@ -1154,7 +1147,7 @@ void ClientConnect()
        if (!sv_foginterval && world.fog != "")
                stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
 
-       if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)))
+       if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AvailableTeams() == 2))
                if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
                        send_CSQC_teamnagger();
 
@@ -1166,7 +1159,7 @@ void ClientConnect()
                sv_notice_join(this);
 
        FOREACH_ENTITY_FLOAT(init_for_player_needed, true, {
-               WITHSELF(it, it.init_for_player(it));
+               it.init_for_player(it, this);
        });
 
        MUTATOR_CALLHOOK(ClientConnect, this);
@@ -1180,22 +1173,21 @@ Called when a client disconnects from the server
 */
 .entity chatbubbleentity;
 void ReadyCount();
-void ClientDisconnect()
+void ClientDisconnect(entity this)
 {
-       SELFPARAM();
        assert(IS_CLIENT(this), return);
 
        PlayerStats_GameReport_FinalizePlayer(this);
-       if (this.vehicle) vehicles_exit(VHEF_RELEASE);
+       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
        if (this.active_minigame) part_minigame(this);
        if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
 
        if (autocvar_sv_eventlog)
                GameLogEcho(strcat(":part:", ftos(this.playerid)));
 
-       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
 
-    MUTATOR_CALLHOOK(ClientDisconnect);
+    MUTATOR_CALLHOOK(ClientDisconnect, this);
 
        ClientState_detach(this);
 
@@ -1212,7 +1204,7 @@ void ClientDisconnect()
        if (this.chatbubbleentity) remove(this.chatbubbleentity);
        if (this.killindicator) remove(this.killindicator);
 
-       WaypointSprite_PlayerGone();
+       WaypointSprite_PlayerGone(this);
 
        bot_relinkplayerlist();
 
@@ -1226,13 +1218,13 @@ void ClientDisconnect()
        if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
 }
 
-void ChatBubbleThink()
-{SELFPARAM();
+void ChatBubbleThink(entity this)
+{
        this.nextthink = time;
        if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this)
        {
-               if(this.owner) // but why can that ever be world?
-                       this.owner.chatbubbleentity = world;
+               if(this.owner) // but why can that ever be NULL?
+                       this.owner.chatbubbleentity = NULL;
                remove(this);
                return;
        }
@@ -1252,8 +1244,8 @@ void ChatBubbleThink()
 
 }
 
-void UpdateChatBubble()
-{SELFPARAM();
+void UpdateChatBubble(entity this)
+{
        if (this.alpha < 0)
                return;
        // spawn a chatbubble entity if needed
@@ -1262,7 +1254,7 @@ void UpdateChatBubble()
                this.chatbubbleentity = new(chatbubbleentity);
                this.chatbubbleentity.owner = this;
                this.chatbubbleentity.exteriormodeltoclient = this;
-               this.chatbubbleentity.think = ChatBubbleThink;
+               setthink(this.chatbubbleentity, ChatBubbleThink);
                this.chatbubbleentity.nextthink = time;
                setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below
                //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1');
@@ -1291,13 +1283,13 @@ void UpdateChatBubble()
        else this.colormod = '1 1 1';
 }*/
 
-void respawn()
-{SELFPARAM();
+void respawn(entity this)
+{
        if(this.alpha >= 0 && autocvar_g_respawn_ghosts)
        {
                this.solid = SOLID_NOT;
                this.takedamage = DAMAGE_NO;
-               this.movetype = MOVETYPE_FLY;
+               set_movetype(this, MOVETYPE_FLY);
                this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
                this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
                this.effects |= CSQCMODEL_EF_RESPAWNGHOST;
@@ -1309,11 +1301,11 @@ void respawn()
        CopyBody(this, 1);
 
        this.effects |= EF_NODRAW; // prevent another CopyBody
-       PutClientInServer();
+       PutClientInServer(this);
 }
 
-void play_countdown(float finished, Sound samp)
-{SELFPARAM();
+void play_countdown(entity this, float finished, Sound samp)
+{
     TC(Sound, samp);
        if(IS_REAL_CLIENT(this))
                if(floor(finished - time - frametime) != floor(finished - time))
@@ -1321,8 +1313,8 @@ void play_countdown(float finished, Sound samp)
                                sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
 }
 
-void player_powerups ()
-{SELFPARAM();
+void player_powerups(entity this)
+{
        // add a way to see what the items were BEFORE all of these checks for the mutator hook
        int items_prev = this.items;
 
@@ -1343,12 +1335,12 @@ void player_powerups ()
        {
                if (this.items & ITEM_Strength.m_itemid)
                {
-                       play_countdown(this.strength_finished, SND_POWEROFF);
+                       play_countdown(this, this.strength_finished, SND_POWEROFF);
                        this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
                        if (time > this.strength_finished)
                        {
                                this.items = this.items - (this.items & ITEM_Strength.m_itemid);
-                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname);
+                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname);
                                Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
                        }
                }
@@ -1357,18 +1349,18 @@ void player_powerups ()
                        if (time < this.strength_finished)
                        {
                                this.items = this.items | ITEM_Strength.m_itemid;
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname);
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname);
                                Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH);
                        }
                }
                if (this.items & ITEM_Shield.m_itemid)
                {
-                       play_countdown(this.invincible_finished, SND_POWEROFF);
+                       play_countdown(this, this.invincible_finished, SND_POWEROFF);
                        this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
                        if (time > this.invincible_finished)
                        {
                                this.items = this.items - (this.items & ITEM_Shield.m_itemid);
-                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname);
+                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname);
                                Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
                        }
                }
@@ -1377,7 +1369,7 @@ void player_powerups ()
                        if (time < this.invincible_finished)
                        {
                                this.items = this.items | ITEM_Shield.m_itemid;
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_SHIELD, this.netname);
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname);
                                Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD);
                        }
                }
@@ -1387,7 +1379,7 @@ void player_powerups ()
                        {
                                this.superweapons_finished = 0;
                                this.items = this.items - (this.items & IT_SUPERWEAPON);
-                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
+                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
                                Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
                        }
                        else if (this.items & IT_UNLIMITED_SUPERWEAPONS)
@@ -1396,12 +1388,12 @@ void player_powerups ()
                        }
                        else
                        {
-                               play_countdown(this.superweapons_finished, SND_POWEROFF);
+                               play_countdown(this, this.superweapons_finished, SND_POWEROFF);
                                if (time > this.superweapons_finished)
                                {
                                        this.items = this.items - (this.items & IT_SUPERWEAPON);
                                        this.weapons &= ~WEPSET_SUPERWEAPONS;
-                                       //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
+                                       //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
                                        Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
                                }
                        }
@@ -1411,7 +1403,7 @@ void player_powerups ()
                        if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS))
                        {
                                this.items = this.items | IT_SUPERWEAPON;
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
                                Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
                        }
                        else
@@ -1484,22 +1476,32 @@ float CalcRotRegen(float current, float regenstable, float regenfactor, float re
        return current;
 }
 
-void player_regen ()
-{SELFPARAM();
+void player_regen(entity this)
+{
        float max_mod, regen_mod, rot_mod, limit_mod;
        max_mod = regen_mod = rot_mod = limit_mod = 1;
-       regen_mod_max = max_mod;
-       regen_mod_regen = regen_mod;
-       regen_mod_rot = rot_mod;
-       regen_mod_limit = limit_mod;
-
-       regen_health = autocvar_g_balance_health_regen;
-       regen_health_linear = autocvar_g_balance_health_regenlinear;
-       regen_health_rot = autocvar_g_balance_health_rot;
-       regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
-       regen_health_stable = autocvar_g_balance_health_regenstable;
-       regen_health_rotstable = autocvar_g_balance_health_rotstable;
-       if(!MUTATOR_CALLHOOK(PlayerRegen))
+
+       float regen_health = autocvar_g_balance_health_regen;
+       float regen_health_linear = autocvar_g_balance_health_regenlinear;
+       float regen_health_rot = autocvar_g_balance_health_rot;
+       float regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
+       float regen_health_stable = autocvar_g_balance_health_regenstable;
+       float regen_health_rotstable = autocvar_g_balance_health_rotstable;
+       bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot,
+               regen_health_rotlinear, regen_health_stable, regen_health_rotstable);
+       max_mod = M_ARGV(1, float);
+       regen_mod = M_ARGV(2, float);
+       rot_mod = M_ARGV(3, float);
+       limit_mod = M_ARGV(4, float);
+       regen_health = M_ARGV(5, float);
+       regen_health_linear = M_ARGV(6, float);
+       regen_health_rot = M_ARGV(7, float);
+       regen_health_rotlinear = M_ARGV(8, float);
+       regen_health_stable = M_ARGV(9, float);
+       regen_health_rotstable = M_ARGV(10, float);
+
+
+       if(!mutator_returnvalue)
        if(!STAT(FROZEN, this))
        {
                float mina, maxa, limith, limita;
@@ -1508,11 +1510,6 @@ void player_regen ()
                limith = autocvar_g_balance_health_limit;
                limita = autocvar_g_balance_armor_limit;
 
-               max_mod = regen_mod_max;
-               regen_mod = regen_mod_regen;
-               rot_mod = regen_mod_rot;
-               limit_mod = regen_mod_limit;
-
                regen_health_rotstable = regen_health_rotstable * max_mod;
                regen_health_stable = regen_health_stable * max_mod;
                limith = limith * limit_mod;
@@ -1527,7 +1524,7 @@ void player_regen ()
        if(this.health < 1)
        {
                if(this.vehicle)
-                       vehicles_exit(VHEF_RELEASE);
+                       vehicles_exit(this.vehicle, VHEF_RELEASE);
                if(this.event_damage)
                        this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0');
        }
@@ -1545,8 +1542,8 @@ void player_regen ()
 }
 
 bool zoomstate_set;
-void SetZoomState(float z)
-{SELFPARAM();
+void SetZoomState(entity this, float z)
+{
        if(z != this.zoomstate)
        {
                this.zoomstate = z;
@@ -1555,10 +1552,9 @@ void SetZoomState(float z)
        zoomstate_set = true;
 }
 
-void GetPressedKeys()
+void GetPressedKeys(entity this)
 {
-       SELFPARAM();
-       MUTATOR_CALLHOOK(GetPressedKeys);
+       MUTATOR_CALLHOOK(GetPressedKeys, this);
        int keys = this.pressedkeys;
        keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
        keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
@@ -1619,17 +1615,19 @@ void SpectateCopy(entity this, entity spectatee)
        this.angles = spectatee.v_angle;
        STAT(FROZEN, this) = STAT(FROZEN, spectatee);
        this.revive_progress = spectatee.revive_progress;
-       if(!PHYS_INPUT_BUTTON_USE(this))
+       if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
                this.fixangle = true;
        setorigin(this, spectatee.origin);
        setsize(this, spectatee.mins, spectatee.maxs);
-       SetZoomState(spectatee.zoomstate);
+       SetZoomState(this, spectatee.zoomstate);
 
     anticheat_spectatecopy(this, spectatee);
        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;
@@ -1639,12 +1637,12 @@ void SpectateCopy(entity this, entity spectatee)
         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);
@@ -1653,8 +1651,8 @@ void SpectateCopy(entity this, entity spectatee)
     }
 }
 
-bool SpectateUpdate()
-{SELFPARAM();
+bool SpectateUpdate(entity this)
+{
        if(!this.enemy)
            return false;
 
@@ -1669,93 +1667,92 @@ bool SpectateUpdate()
        return true;
 }
 
-bool SpectateSet()
-{SELFPARAM();
+bool SpectateSet(entity this)
+{
        if(!IS_PLAYER(this.enemy))
                return false;
 
        msg_entity = this;
        WriteByte(MSG_ONE, SVC_SETVIEW);
        WriteEntity(MSG_ONE, this.enemy);
-       this.movetype = MOVETYPE_NONE;
+       set_movetype(this, MOVETYPE_NONE);
        accuracy_resend(this);
 
-       if(!SpectateUpdate())
-               PutObserverInServer();
+       if(!SpectateUpdate(this))
+               PutObserverInServer(this);
 
        return true;
 }
 
-void SetSpectatee(entity player, entity spectatee)
+void SetSpectatee(entity this, entity spectatee)
 {
-       entity old_spectatee = player.enemy;
+       entity old_spectatee = this.enemy;
 
-       player.enemy = spectatee;
+       this.enemy = spectatee;
 
        // WEAPONTODO
        // 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(player.enemy && player.enemy.arc_beam) { player.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
+       if(this.enemy && this.enemy.arc_beam) { this.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
 }
 
-bool Spectate(entity pl)
-{SELFPARAM();
+bool Spectate(entity this, entity pl)
+{
        if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
                return false;
-       pl = spec_player;
+       pl = M_ARGV(1, entity);
 
        SetSpectatee(this, pl);
-       return SpectateSet();
+       return SpectateSet(this);
 }
 
-bool SpectateNext()
-{SELFPARAM();
-       other = find(this.enemy, classname, STR_PLAYER);
+bool SpectateNext(entity this)
+{
+       entity ent = find(this.enemy, classname, STR_PLAYER);
 
-       if (MUTATOR_CALLHOOK(SpectateNext, this, other))
-               other = spec_player;
-       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();
+       return SpectateSet(this);
 }
 
-bool SpectatePrev()
-{SELFPARAM();
+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 = spec_player;
+                   ent = M_ARGV(1, entity);
                    break;
                case MUT_SPECPREV_RETURN:
-                   other = spec_player;
                    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);
-       return SpectateSet();
+       SetSpectatee(this, ent);
+       return SpectateSet(this);
 }
 
 /*
@@ -1765,8 +1762,8 @@ ShowRespawnCountdown()
 Update a respawn countdown display.
 =============
 */
-void ShowRespawnCountdown()
-{SELFPARAM();
+void ShowRespawnCountdown(entity this)
+{
        float number;
        if(!IS_DEAD(this)) // just respawned?
                return;
@@ -1784,8 +1781,8 @@ void ShowRespawnCountdown()
        }
 }
 
-void LeaveSpectatorMode()
-{SELFPARAM();
+void LeaveSpectatorMode(entity this)
+{
        if(this.caplayer)
                return;
        if(nJoinAllowed(this, this))
@@ -1802,9 +1799,9 @@ void LeaveSpectatorMode()
 
                        Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
 
-                       PutClientInServer();
+                       PutClientInServer(this);
 
-                       if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); }
+                       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); }
                }
                else
                        stuffcmd(this, "menu_showteamselect\n");
@@ -1861,8 +1858,8 @@ bool nJoinAllowed(entity this, entity ignore)
  * Checks whether the client is an observer or spectator, if so, he will get kicked after
  * g_maxplayers_spectator_blocktime seconds
  */
-void checkSpectatorBlock()
-{SELFPARAM();
+void checkSpectatorBlock(entity this)
+{
        if(IS_SPEC(this) || IS_OBSERVER(this))
        if(!this.caplayer)
        if(IS_REAL_CLIENT(this))
@@ -1886,7 +1883,7 @@ void PrintWelcomeMessage(entity this)
                } else {
                        if (PHYS_INPUT_BUTTON_INFO(this)) {
                                this.motd_actived_time = time;
-                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage());
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
                        }
                }
        }
@@ -1921,27 +1918,25 @@ void PrintWelcomeMessage(entity this)
        }
 }
 
-void ObserverThink()
-{SELFPARAM();
+void ObserverThink(entity this)
+{
        if ( this.impulse )
        {
                MinigameImpulse(this, this.impulse);
                this.impulse = 0;
        }
-       float prefered_movetype;
        if (this.flags & FL_JUMPRELEASED) {
                if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
                        this.flags &= ~FL_JUMPRELEASED;
                        this.flags |= FL_SPAWNING;
                } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) {
                        this.flags &= ~FL_JUMPRELEASED;
-                       if(SpectateNext()) {
+                       if(SpectateNext(this)) {
                                TRANSMUTE(Spectator, this);
                        }
                } else {
-                       prefered_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
-                       if (this.movetype != prefered_movetype)
-                               this.movetype = prefered_movetype;
+                       int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+                       set_movetype(this, preferred_movetype);
                }
        } else {
                if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) {
@@ -1949,19 +1944,26 @@ void ObserverThink()
                        if(this.flags & FL_SPAWNING)
                        {
                                this.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode();
+                               LeaveSpectatorMode(this);
                                return;
                        }
                }
        }
 }
 
-void SpectatorThink()
-{SELFPARAM();
+void SpectatorThink(entity this)
+{
        if ( this.impulse )
        {
                if(MinigameImpulse(this, this.impulse))
                        this.impulse = 0;
+
+               if (this.impulse == IMP_weapon_drop.impulse)
+               {
+                       STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3;
+                       this.impulse = 0;
+                       return;
+               }
        }
        if (this.flags & FL_JUMPRELEASED) {
                if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
@@ -1969,29 +1971,29 @@ void SpectatorThink()
                        this.flags |= FL_SPAWNING;
                } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) {
                        this.flags &= ~FL_JUMPRELEASED;
-                       if(SpectateNext()) {
+                       if(SpectateNext(this)) {
                                TRANSMUTE(Spectator, this);
                        } else {
                                TRANSMUTE(Observer, this);
-                               PutClientInServer();
+                               PutClientInServer(this);
                        }
                        this.impulse = 0;
                } else if(this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) {
                        this.flags &= ~FL_JUMPRELEASED;
-                       if(SpectatePrev()) {
+                       if(SpectatePrev(this)) {
                                TRANSMUTE(Spectator, this);
                        } else {
                                TRANSMUTE(Observer, this);
-                               PutClientInServer();
+                               PutClientInServer(this);
                        }
                        this.impulse = 0;
                } else if (PHYS_INPUT_BUTTON_ATCK2(this)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        TRANSMUTE(Observer, this);
-                       PutClientInServer();
+                       PutClientInServer(this);
                } else {
-                       if(!SpectateUpdate())
-                               PutObserverInServer();
+                       if(!SpectateUpdate(this))
+                               PutObserverInServer(this);
                }
        } else {
                if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) {
@@ -1999,20 +2001,20 @@ void SpectatorThink()
                        if(this.flags & FL_SPAWNING)
                        {
                                this.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode();
+                               LeaveSpectatorMode(this);
                                return;
                        }
                }
-               if(!SpectateUpdate())
-                       PutObserverInServer();
+               if(!SpectateUpdate(this))
+                       PutObserverInServer(this);
        }
 
        this.flags |= FL_CLIENT | FL_NOTARGET;
 }
 
 void vehicles_enter (entity pl, entity veh);
-void PlayerUseKey()
-{SELFPARAM();
+void PlayerUseKey(entity this)
+{
        if (!IS_PLAYER(this))
                return;
 
@@ -2020,7 +2022,7 @@ void PlayerUseKey()
        {
                if(!gameover)
                {
-                       vehicles_exit(VHEF_NORMAL);
+                       vehicles_exit(this.vehicle, VHEF_NORMAL);
                        return;
                }
        }
@@ -2030,19 +2032,19 @@ void PlayerUseKey()
                if(!IS_DEAD(this))
                if(!gameover)
                {
-                       entity head, closest_target = world;
+                       entity head, closest_target = NULL;
                        head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true);
 
                        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)
                                {
                                        if(closest_target)
                                        {
-                                               if(vlen(this.origin - head.origin) < vlen(this.origin - closest_target.origin))
+                                               if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin))
                                                { closest_target = head; }
                                        }
                                        else { closest_target = head; }
@@ -2056,7 +2058,7 @@ void PlayerUseKey()
        }
 
        // a use key was pressed; call handlers
-       MUTATOR_CALLHOOK(PlayerUseKey);
+       MUTATOR_CALLHOOK(PlayerUseKey, this);
 }
 
 
@@ -2068,12 +2070,10 @@ Called every frame for each client before the physics are run
 =============
 */
 .float usekeypressed;
-void() nexball_setstatus;
 .float last_vehiclecheck;
 .int items_added;
-void PlayerPreThink ()
+void PlayerPreThink (entity this)
 {
-    SELFPARAM();
        WarpZone_PlayerPhysics_FixVAngle(this);
 
     STAT(GAMESTARTTIME, this) = game_starttime;
@@ -2091,7 +2091,7 @@ void PlayerPreThink ()
        if (blockSpectators && frametime) {
                // WORKAROUND: only use dropclient in server frames (frametime set).
                // Never use it in cl_movement frames (frametime zero).
-               checkSpectatorBlock();
+               checkSpectatorBlock(this);
     }
 
        zoomstate_set = false;
@@ -2151,35 +2151,34 @@ void PlayerPreThink ()
                if (this.health < 1)
                {
                        if (this.vehicle)
-                               vehicles_exit(VHEF_RELEASE);
-                       this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
+                               vehicles_exit(this.vehicle, VHEF_RELEASE);
+                       if(this.event_damage)
+                               this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
                }
                else if (this.revive_progress <= 0)
                        Unfreeze(this);
        }
 
-       MUTATOR_CALLHOOK(PlayerPreThink);
+       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))
+       if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !gameover && !this.vehicle)
+       if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this))
        {
-               entity veh;
-               for(veh = world; (veh = findflags(veh, vehicle_flags, VHF_ISVEHICLE)); )
-               if(vlen(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);
+               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;
        }
@@ -2187,7 +2186,7 @@ void PlayerPreThink ()
        if(!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) && !this.usekeypressed)
-                       PlayerUseKey();
+                       PlayerUseKey(this);
                this.usekeypressed = PHYS_INPUT_BUTTON_USE(this);
        }
 
@@ -2195,10 +2194,10 @@ void PlayerPreThink ()
                PrintWelcomeMessage(this);
 
        if (IS_PLAYER(this)) {
-               CheckRules_Player();
+               CheckRules_Player(this);
 
                if (intermission_running) {
-                       IntermissionThink();
+                       IntermissionThink(this);
                        return;
                }
 
@@ -2210,49 +2209,66 @@ void PlayerPreThink ()
                        this.fixangle = true;
                }
 
-               if (frametime) player_powerups();
+               if (frametime) player_powerups(this);
 
                if (IS_DEAD(this)) {
                        if (this.personal && g_race_qualifying) {
                                if (time > this.respawn_time) {
                                        STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
-                                       respawn();
+                                       respawn(this);
                                        this.impulse = CHIMPULSE_SPEEDRUN.impulse;
                                }
                        } else {
-                               if (frametime) player_anim();
+                               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) && !autocvar_g_respawn_delay_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();
+                               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;
                                        }
                                }
 
-                               ShowRespawnCountdown();
+                               ShowRespawnCountdown(this);
 
                                if (this.respawn_flags & RESPAWN_SILENT)
                                        STAT(RESPAWN_TIME, this) = 0;
-                               else if ((this.respawn_flags & RESPAWN_FORCE) && autocvar_g_respawn_delay_max)
-                                       STAT(RESPAWN_TIME, this) = this.respawn_time_max;
+                               else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
+                               {
+                                       if (time < this.respawn_time)
+                                               STAT(RESPAWN_TIME, this) = this.respawn_time;
+                                       else if (this.deadflag != DEAD_RESPAWNING)
+                                               STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
+                               }
                                else
                                        STAT(RESPAWN_TIME, this) = this.respawn_time;
                        }
@@ -2270,12 +2286,15 @@ void PlayerPreThink ()
         .entity weaponentity = weaponentities[0]; // TODO: unhardcode
                if (this.hook.state) {
                        do_crouch = false;
+               } else if (this.waterlevel >= WATERLEVEL_SWIMMING) {
+                       do_crouch = false;
                } else if (this.vehicle) {
                        do_crouch = false;
                } else if (STAT(FROZEN, this)) {
                        do_crouch = false;
-        } else if ((PS(this).m_weapon == WEP_SHOTGUN || PS(this).m_weapon == WEP_SHOCKWAVE) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) {
-                   // WEAPONTODO: predict
+        } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI) && this.(weaponentity).wframe == WFRAME_FIRE1 && time < this.(weaponentity).weapon_nextthink) {
+                       do_crouch = false;
+        } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) {
                        do_crouch = false;
         }
 
@@ -2311,17 +2330,17 @@ void PlayerPreThink ()
                        this.items |= this.items_added;
                }
 
-               player_regen();
+               player_regen(this);
 
                // WEAPONTODO: Add a weapon request for this
                // rot vortex charge to the charge limit
                if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time)
                        this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
 
-               if (frametime) player_anim();
+               if (frametime) player_anim(this);
 
                // secret status
-               secrets_setstatus();
+               secrets_setstatus(this);
 
                // monsters status
                monsters_setstatus(this);
@@ -2329,19 +2348,19 @@ void PlayerPreThink ()
                this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
        }
        else if (gameover) {
-               if (intermission_running) IntermissionThink();
+               if (intermission_running) IntermissionThink(this);
                return;
        }
        else if (IS_OBSERVER(this)) {
-               ObserverThink();
+               ObserverThink(this);
        }
        else if (IS_SPEC(this)) {
-               SpectatorThink();
+               SpectatorThink(this);
        }
 
        // WEAPONTODO: Add weapon request for this
        if (!zoomstate_set) {
-               SetZoomState(
+               SetZoomState(this,
                        PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this)
                        || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX)
                        || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)
@@ -2368,13 +2387,13 @@ void PlayerPreThink ()
                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);
@@ -2390,10 +2409,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;
        }
@@ -2401,12 +2420,36 @@ void DrownPlayer(entity this)
        {       // drown!
                if (this.pain_finished < time)
                {
-                       Damage (this, world, world, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0');
+                       Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0');
                        this.pain_finished = time + 0.5;
                }
        }
 }
 
+void Player_Physics(entity this)
+{
+       this.movetype = ((this.move_qcphysics) ? MOVETYPE_NONE : this.move_movetype);
+
+       if(!this.move_qcphysics)
+               return;
+
+       int mt = this.move_movetype;
+
+       if(mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH || mt == MOVETYPE_PHYSICS)
+       {
+               this.move_qcphysics = false;
+               this.movetype = mt;
+               return;
+       }
+
+       if(!frametime && !this.pm_frametime)
+               return;
+
+       Movetype_Physics_NoMatchTicrate(this, this.pm_frametime, true);
+
+       this.pm_frametime = 0;
+}
+
 /*
 =============
 PlayerPostThink
@@ -2415,15 +2458,27 @@ Called every frame for each client after the physics are run
 =============
 */
 .float idlekick_lasttimeleft;
-void PlayerPostThink ()
+void PlayerPostThink (entity this)
 {
-    SELFPARAM();
+       Player_Physics(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)
                        {
@@ -2439,7 +2494,7 @@ void PlayerPostThink ()
                                        Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
                        }
                        if (timeleft <= 0) {
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname);
+                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname);
                                dropclient(this);
                                return;
                        }
@@ -2452,17 +2507,17 @@ void PlayerPostThink ()
                }
        }
 
-       CheatFrame();
+       CheatFrame(this);
 
        //CheckPlayerJump();
 
        if (IS_PLAYER(this)) {
                DrownPlayer(this);
-               CheckRules_Player();
-               UpdateChatBubble();
+               CheckRules_Player(this);
+               UpdateChatBubble(this);
                if (this.impulse) ImpulseCommands(this);
                if (intermission_running) return; // intermission or finale
-               GetPressedKeys();
+               GetPressedKeys(this);
        }
 
        if (this.waypointsprite_attachedforcarrier) {
@@ -2470,7 +2525,7 @@ void PlayerPostThink ()
                WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v);
     }
 
-       playerdemo_write();
+       playerdemo_write(this);
 
        CSQCMODEL_AUTOUPDATE(this);
 }