]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/cl_client.qc
rename teamplay_default to teamplay_mode, stop using the "teamplay" cvar (more cleanu...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cl_client.qc
index 1438d317c76947d9fd04cec4477a49f1d5b15e15..3a416375b3874eb41a6593fb8933e9a5813bb4d6 100644 (file)
@@ -599,13 +599,20 @@ void PutObserverInServer (void)
        }
 
        DropAllRunes(self);
+       MUTATOR_CALLHOOK(MakePlayerObserver);
 
        Portal_ClearAll(self);
 
+       if(self.alivetime)
+       {
+               PlayerStats_Event(self, PLAYERSTATS_ALIVETIME, time - self.alivetime);
+               self.alivetime = 0;
+       }
+
        if(self.flagcarried)
                DropFlag(self.flagcarried, world, world);
 
-       if(self.ballcarried)
+       if(self.ballcarried && g_nexball)
                DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
 
        WaypointSprite_PlayerDead();
@@ -630,6 +637,8 @@ void PutObserverInServer (void)
 
        PlayerScore_Clear(self); // clear scores when needed
 
+       accuracy_resend(self);
+
        self.spectatortime = time;
 
        self.classname = "observer";
@@ -713,8 +722,6 @@ void PutObserverInServer (void)
        }
        else
                self.frags = FRAGS_SPECTATOR;
-
-       MUTATOR_CALLHOOK(MakePlayerObserver);
 }
 
 float RestrictSkin(float s)
@@ -795,8 +802,8 @@ void FixPlayermodel()
 
        if(!teams_matter)
                if(strlen(cvar_string("sv_defaultplayercolors")))
-                       if(self.clientcolors != cvar("sv_defaultplayercolors"))
-                               setcolor(self, cvar("sv_defaultplayercolors"));
+                       if(self.clientcolors != stof(cvar_string("sv_defaultplayercolors")))
+                               setcolor(self, stof(cvar_string("sv_defaultplayercolors")));
 }
 
 void PlayerTouchExplode(entity p1, entity p2)
@@ -854,6 +861,8 @@ void PutClientInServer (void)
                entity spot, oldself;
                float j;
 
+               accuracy_resend(self);
+
                if(self.team < 0)
                        JoinBestTeam(self, FALSE, TRUE);
 
@@ -890,7 +899,11 @@ void PutClientInServer (void)
                self.air_finished = time + 12;
                self.dmg = 2;
                if(cvar("g_balance_nex_charge"))
+               {
+                       if(cvar("g_balance_nex_secondary_chargepool"))
+                               self.nex_chargepool_ammo = 1;
                        self.nex_charge = cvar("g_balance_nex_charge_start");
+               }
 
                if(inWarmupStage)
                {
@@ -916,7 +929,13 @@ void PutClientInServer (void)
                }
 
                if(g_weaponarena_random)
+               {
+                       if(g_weaponarena_random_with_laser)
+                               self.weapons &~= WEPBIT_LASER;
                        self.weapons = randombits(self.weapons, g_weaponarena_random, FALSE);
+                       if(g_weaponarena_random_with_laser)
+                               self.weapons |= WEPBIT_LASER;
+               }
 
                self.items = start_items;
                self.jump_interval = time;
@@ -1061,6 +1080,9 @@ void PutClientInServer (void)
                self.switchweapon = w_getbestweapon(self);
                self.cnt = self.switchweapon;
                self.weapon = 0;
+
+               if(!self.alivetime)
+                       self.alivetime = time;
        } else if(self.classname == "observer" || (g_ca && !allowed_to_spawn)) {
                PutObserverInServer ();
        }
@@ -1069,6 +1091,9 @@ void PutClientInServer (void)
        //      ctf_playerchanged();
 }
 
+.float ebouncefactor, ebouncestop; // electro's values
+// TODO do we need all these fields, or should we stop autodetecting runtime
+// changes and just have a console command to update this?
 float ClientInit_SendEntity(entity to, float sf)
 {
        WriteByte(MSG_ENTITY, ENT_CLIENT_INIT);
@@ -1093,6 +1118,8 @@ float ClientInit_SendEntity(entity to, float sf)
        WriteByte(MSG_ENTITY, self.cnt * 255.0); // g_balance_weaponswitchdelay
        WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_grenadelauncher_bouncefactor
        WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_grenadelauncher_bouncestop
+       WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_grenadelauncher_bouncefactor
+       WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_grenadelauncher_bouncestop
        WriteByte(MSG_ENTITY, cvar("g_balance_nex_secondary")); // client has to know if it should zoom or not
        WriteByte(MSG_ENTITY, cvar("g_balance_campingrifle_secondary")); // client has to know if it should zoom or not
        WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not
@@ -1122,6 +1149,16 @@ void ClientInit_CheckUpdate()
                self.bouncestop = cvar("g_balance_grenadelauncher_bouncestop");
                self.SendFlags |= 1;
        }
+       if(self.ebouncefactor != cvar("g_balance_electro_secondary_bouncefactor"))
+       {
+               self.ebouncefactor = cvar("g_balance_electro_secondary_bouncefactor");
+               self.SendFlags |= 1;
+       }
+       if(self.ebouncestop != cvar("g_balance_electro_secondary_bouncestop"))
+       {
+               self.ebouncestop = cvar("g_balance_electro_secondary_bouncestop");
+               self.SendFlags |= 1;
+       }
 }
 
 void ClientInit_Spawn()
@@ -1310,6 +1347,10 @@ void ClientKill (void)
        {
                // do nothing
        }
+    else if(g_freezetag && self.freezetag_frozen == 1)
+    {
+        // do nothing
+    }
        else
                ClientKill_TeamChange(0);
 }
@@ -1408,6 +1449,27 @@ void FixClientCvars(entity e)
         */
 }
 
+float PlayerInIDList(entity p, string idlist)
+{
+       float n, i;
+       string s;
+
+       // NOTE: we do NOT check crypto_keyfp here, an unsigned ID is fine too for this
+       if not(p.crypto_idfp)
+               return 0;
+
+       // this function allows abbreviated player IDs too!
+       n = tokenize_console(idlist);
+       for(i = 0; i < n; ++i)
+       {
+               s = argv(i);
+               if(s == substring(p.crypto_idfp, 0, strlen(s)))
+                       return 1;
+       }
+
+       return 0;
+}
+
 /*
 =============
 ClientConnect
@@ -1451,6 +1513,7 @@ void ClientConnect (void)
 
        PlayerScore_Attach(self);
        ClientData_Attach();
+       accuracy_init(self);
 
        bot_clientconnect();
 
@@ -1463,9 +1526,37 @@ void ClientConnect (void)
        //if(g_domination)
        //      dom_player_join_team(self);
 
+       // identify the right forced team
+       if(PlayerInIDList(self, cvar_string("g_forced_team_red")))
+               self.team_forced = COLOR_TEAM1;
+       else if(PlayerInIDList(self, cvar_string("g_forced_team_blue")))
+               self.team_forced = COLOR_TEAM2;
+       else if(PlayerInIDList(self, cvar_string("g_forced_team_yellow")))
+               self.team_forced = COLOR_TEAM3;
+       else if(PlayerInIDList(self, cvar_string("g_forced_team_pink")))
+               self.team_forced = COLOR_TEAM4;
+       else if(cvar_string("g_forced_team_otherwise") == "red")
+               self.team_forced = COLOR_TEAM1;
+       else if(cvar_string("g_forced_team_otherwise") == "blue")
+               self.team_forced = COLOR_TEAM2;
+       else if(cvar_string("g_forced_team_otherwise") == "yellow")
+               self.team_forced = COLOR_TEAM3;
+       else if(cvar_string("g_forced_team_otherwise") == "pink")
+               self.team_forced = COLOR_TEAM4;
+       else if(cvar_string("g_forced_team_otherwise") == "spectate")
+               self.team_forced = -1;
+       else if(cvar_string("g_forced_team_otherwise") == "spectator")
+               self.team_forced = -1;
+       else
+               self.team_forced = 0;
+
+       if(!teams_matter)
+               if(self.team_forced > 0)
+                       self.team_forced = 0;
+
        JoinBestTeam(self, FALSE, FALSE); // if the team number is valid, keep it
 
-       if((cvar("sv_spectate") == 1 && !g_lms) || cvar("g_campaign")) {
+       if((cvar("sv_spectate") == 1 && !g_lms) || cvar("g_campaign") || self.team_forced < 0) {
                self.classname = "observer";
        } else {
                if(teams_matter)
@@ -1528,9 +1619,6 @@ void ClientConnect (void)
        // get other cvars from player
        GetCvars(0);
 
-       // set cvar for team scoreboard
-       stuffcmd(self, strcat("set teamplay ", ftos(teamplay), "\n"));
-
        // notify about available teams
        if(teams_matter)
        {
@@ -1623,6 +1711,8 @@ void ClientConnect (void)
        send_CSQC_cr_maxbullets(self);
 
        CheatInitClient();
+
+       PlayerStats_AddPlayer(self);
 }
 
 /*
@@ -1643,6 +1733,8 @@ void ClientDisconnect (void)
                return;
        }
 
+       PlayerStats_AddGlobalInfo(self);
+
        CheatShutdownClient();
 
        if(self.hitplotfh >= 0)
@@ -1675,7 +1767,7 @@ void ClientDisconnect (void)
 
        if(self.flagcarried)
                DropFlag(self.flagcarried, world, world);
-       if(self.ballcarried)
+       if(self.ballcarried && g_nexball)
                DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
 
        // Here, everything has been done that requires this player to be a client.
@@ -1695,17 +1787,13 @@ void ClientDisconnect (void)
 
        bot_relinkplayerlist();
 
-       // remove laserdot
-       if(self.weaponentity)
-               if(self.weaponentity.lasertarget)
-                       remove(self.weaponentity.lasertarget);
-
        if(g_arena)
        {
                Spawnqueue_Unmark(self);
                Spawnqueue_Remove(self);
        }
 
+       accuracy_free(self);
        ClientData_Detach();
        PlayerScore_Detach(self);
 
@@ -1913,6 +2001,9 @@ string getTimeoutText(float addOneSecond) {
 
 void player_powerups (void)
 {
+       // add a way to see what the items were BEFORE all of these checks for the mutator hook
+       olditems = self.items;
+       
        if((self.items & IT_USING_JETPACK) && !self.deadflag)
        {
                SoundEntity_StartSound(self, CHAN_PLAYER, "misc/jetpack_fly.wav", VOL_BASE, cvar("g_jetpack_attenuation"));
@@ -1975,64 +2066,67 @@ void player_powerups (void)
                                sprint(self, "^3You are on speed\n");
                        }
                }
-               return;
        }
-
-       if (self.items & IT_STRENGTH)
+       else // if we're not in minstagib, continue. I added this else to replace the "return" which was here that broke the callhook for this function -- This code is nasty.
        {
-               play_countdown(self.strength_finished, "misc/poweroff.wav");
-               self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
-               if (time > self.strength_finished && cvar("g_balance_powerup_timer"))
+               if (self.items & IT_STRENGTH)
                {
-                       self.items = self.items - (self.items & IT_STRENGTH);
-                       sprint(self, "^3Strength has worn off\n");
+                       play_countdown(self.strength_finished, "misc/poweroff.wav");
+                       self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > self.strength_finished && cvar("g_balance_powerup_timer"))
+                       {
+                               self.items = self.items - (self.items & IT_STRENGTH);
+                               sprint(self, "^3Strength has worn off\n");
+                       }
                }
-       }
-       else
-       {
-               if (time < self.strength_finished)
+               else
                {
-                       self.items = self.items | IT_STRENGTH;
-                       sprint(self, "^3Strength infuses your weapons with devastating power\n");
+                       if (time < self.strength_finished)
+                       {
+                               self.items = self.items | IT_STRENGTH;
+                               sprint(self, "^3Strength infuses your weapons with devastating power\n");
+                       }
                }
-       }
-       if (self.items & IT_INVINCIBLE)
-       {
-               play_countdown(self.invincible_finished, "misc/poweroff.wav");
-               self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
-               if (time > self.invincible_finished && cvar("g_balance_powerup_timer"))
+               if (self.items & IT_INVINCIBLE)
                {
-                       self.items = self.items - (self.items & IT_INVINCIBLE);
-                       sprint(self, "^3Shield has worn off\n");
+                       play_countdown(self.invincible_finished, "misc/poweroff.wav");
+                       self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > self.invincible_finished && cvar("g_balance_powerup_timer"))
+                       {
+                               self.items = self.items - (self.items & IT_INVINCIBLE);
+                               sprint(self, "^3Shield has worn off\n");
+                       }
                }
-       }
-       else
-       {
-               if (time < self.invincible_finished)
+               else
                {
-                       self.items = self.items | IT_INVINCIBLE;
-                       sprint(self, "^3Shield surrounds you\n");
+                       if (time < self.invincible_finished)
+                       {
+                               self.items = self.items | IT_INVINCIBLE;
+                               sprint(self, "^3Shield surrounds you\n");
+                       }
                }
-       }
 
-       if(cvar("g_nodepthtestplayers"))
-               self.effects = self.effects | EF_NODEPTHTEST;
+               if(cvar("g_nodepthtestplayers"))
+                       self.effects = self.effects | EF_NODEPTHTEST;
 
-       if(cvar("g_fullbrightplayers"))
-               self.effects = self.effects | EF_FULLBRIGHT;
+               if(cvar("g_fullbrightplayers"))
+                       self.effects = self.effects | EF_FULLBRIGHT;
 
-       // midair gamemode: damage only while in the air
-       // if in midair mode, being on ground grants temporary invulnerability
-       // (this is so that multishot weapon don't clear the ground flag on the
-       // first damage in the frame, leaving the player vulnerable to the
-       // remaining hits in the same frame)
-       if (self.flags & FL_ONGROUND)
-       if (g_midair)
-               self.spawnshieldtime = max(self.spawnshieldtime, time + cvar("g_midair_shieldtime"));
+               // midair gamemode: damage only while in the air
+               // if in midair mode, being on ground grants temporary invulnerability
+               // (this is so that multishot weapon don't clear the ground flag on the
+               // first damage in the frame, leaving the player vulnerable to the
+               // remaining hits in the same frame)
+               if (self.flags & FL_ONGROUND)
+               if (g_midair)
+                       self.spawnshieldtime = max(self.spawnshieldtime, time + cvar("g_midair_shieldtime"));
 
-       if (time >= game_starttime)
-       if (time < self.spawnshieldtime)
-               self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
+               if (time >= game_starttime)
+               if (time < self.spawnshieldtime)
+                       self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
+       }
+       
+       MUTATOR_CALLHOOK(PlayerPowerups);
 }
 
 float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
@@ -2203,33 +2297,13 @@ void GetPressedKeys(void) {
                self.pressedkeys &~= KEY_CROUCH;
 }
 
-void update_stats (float number, float hit, float fired) {
-// self.stat_hit   = number + ((number==0) ? 1 : 64) * hit   * sv_accuracy_data_share;
-// self.stat_fired = number + ((number==0) ? 1 : 64) * fired * sv_accuracy_data_share;
-
-       if(number) {
-               self.stat_hit = number + 64 * hit * sv_accuracy_data_share;
-               self.stat_fired = number + 64 * fired * sv_accuracy_data_share;
-       } else {
-               self.stat_hit = hit * sv_accuracy_data_share;
-               self.stat_fired = fired * sv_accuracy_data_share;
-       }
-}
-
 /*
 ======================
 spectate mode routines
 ======================
 */
 
-.float weapon_count;
 void SpectateCopy(entity spectatee) {
-       if(spectatee.weapon_count < WEP_LAST) {
-               update_stats (spectatee.weapon_count, spectatee.cvar_cl_accuracy_data_share * floor(spectatee.stats_hit[spectatee.weapon_count - 1]), spectatee.cvar_cl_accuracy_data_share * floor(spectatee.stats_fired[spectatee.weapon_count - 1]));
-               spectatee.weapon_count ++;
-       } else
-               update_stats (0, spectatee.cvar_cl_accuracy_data_share * spectatee.stat_hit, spectatee.cvar_cl_accuracy_data_share * spectatee.stat_fired);
-
        other = spectatee;
        MUTATOR_CALLHOOK(SpectateCopy);
        self.armortype = spectatee.armortype;
@@ -2297,8 +2371,7 @@ float SpectateNext() {
                WriteEntity(MSG_ONE, self.enemy);
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
                self.movetype = MOVETYPE_NONE;
-
-               self.enemy.weapon_count = 0;
+               accuracy_resend(self);
 
                if(!SpectateUpdate())
                        PutObserverInServer();
@@ -2338,7 +2411,7 @@ void ShowRespawnCountdown()
 void LeaveSpectatorMode()
 {
        if(isJoinAllowed()) {
-               if(!teams_matter || cvar("g_campaign") || cvar("g_balance_teams") || (self.wasplayer && cvar("g_changeteam_banned"))) {
+               if(!teams_matter || cvar("g_campaign") || cvar("g_balance_teams") || (self.wasplayer && cvar("g_changeteam_banned")) || self.team_forced > 0) {
                        self.classname = "player";
 
                        if(cvar("g_campaign") || cvar("g_balance_teams") || cvar("g_balance_teams_force"))
@@ -2347,8 +2420,6 @@ void LeaveSpectatorMode()
                        if(cvar("g_campaign"))
                                campaign_bots_may_start = 1;
 
-                       self.stat_count = WEP_LAST;
-
                        PutClientInServer();
 
                        if(self.classname == "player")
@@ -2379,6 +2450,9 @@ void LeaveSpectatorMode()
  * @return bool TRUE if the player is allowed to join, false otherwise
  */
 float isJoinAllowed() {
+       if(self.team_forced < 0)
+               return FALSE; // forced spectators can never join
+
        if (!cvar("g_maxplayers"))
                return TRUE;
 
@@ -2407,50 +2481,6 @@ void checkSpectatorBlock() {
        }
 }
 
-float vercmp_recursive(string v1, string v2)
-{
-       float dot1, dot2;
-       string s1, s2;
-       float r;
-
-       dot1 = strstrofs(v1, ".", 0);
-       dot2 = strstrofs(v2, ".", 0);
-       if(dot1 == -1)
-               s1 = v1;
-       else
-               s1 = substring(v1, 0, dot1);
-       if(dot2 == -1)
-               s2 = v2;
-       else
-               s2 = substring(v2, 0, dot2);
-
-       r = stof(s1) - stof(s2);
-       if(r != 0)
-               return r;
-
-       r = strcasecmp(s1, s2);
-       if(r != 0)
-               return r;
-
-       if(dot1 == -1)
-               if(dot2 == -1)
-                       return 0;
-               else
-                       return -1;
-       else
-               if(dot2 == -1)
-                       return 1;
-               else
-                       return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
-}
-
-float vercmp(string v1, string v2)
-{
-       if(strcasecmp(v1, v2) == 0) // early out check
-               return 0;
-       return vercmp_recursive(v1, v2);
-}
-
 void ObserverThink()
 {
        if (self.flags & FL_JUMPRELEASED) {
@@ -2493,14 +2523,12 @@ void SpectatorThink()
                                self.classname = "spectator";
                        } else {
                                self.classname = "observer";
-                               self.stat_count = WEP_LAST;
                                PutClientInServer();
                        }
                } else if (self.BUTTON_ATCK2) {
                        self.welcomemessage_time = 0;
                        self.flags &~= FL_JUMPRELEASED;
                        self.classname = "observer";
-                       self.stat_count = WEP_LAST;
                        PutClientInServer();
                } else {
                        if(!SpectateUpdate())
@@ -2811,7 +2839,7 @@ void PlayerPreThink (void)
                player_regen();
 
                // rot nex charge to the charge limit
-               if(cvar("g_balance_nex_charge_rot_rate") && self.nex_charge > cvar("g_balance_nex_charge_limit"))
+               if(cvar("g_balance_nex_charge_rot_rate") && self.nex_charge > cvar("g_balance_nex_charge_limit") && self.nex_charge_rottime < time)
                        self.nex_charge = bound(cvar("g_balance_nex_charge_limit"), self.nex_charge - cvar("g_balance_nex_charge_rot_rate") * frametime / W_TICSPERFRAME, 1);
 
                if(frametime)
@@ -2820,8 +2848,11 @@ void PlayerPreThink (void)
                if (g_minstagib)
                        minstagib_ammocheck();
 
-               ctf_setstatus();
-               nexball_setstatus();
+               if(g_ctf)
+                       ctf_setstatus();
+
+               if(g_nexball)
+                       nexball_setstatus();
 
                self.dmg_team = max(0, self.dmg_team - cvar("g_teamdamage_resetspeed") * frametime);
 
@@ -2935,15 +2966,6 @@ void PlayerPostThink (void)
                stuffcmd(self, strcat("name ", self.netname, substring(ftos(random()), 2, -1), "\n"));
        }
 
-       // send the clients accuracy stats to the client
-       if(self.stat_count > 0)
-       if(frametime)
-       {
-               self.stat_hit = self.stat_count + 64 * floor(self.(stats_hit[self.stat_count - 1]));
-               self.stat_fired = self.stat_count + 64 * floor(self.(stats_fired[self.stat_count - 1]));
-               self.stat_count -= 1;
-       }
-
        if(sv_maxidle && frametime)
        {
                // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
@@ -3040,7 +3062,7 @@ void PlayerPostThink (void)
 
        playerdemo_write();
 
-       if((g_cts || g_race) && self.cvar_cl_allow_uid2name)
+       if((g_cts || g_race) && self.cvar_cl_allow_uid2name == 1)
        {
                if(!self.stored_netname)
                        self.stored_netname = strzone(uid2name(self.crypto_idfp));