]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/cl_client.qc
ClientKill: prevent multiple suicides in same frame
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cl_client.qc
index 144e849e1d7e712f134253cc145baeba8d1f3763..432fa7c503a517303d8af692235c1e335e4c5ecb 100644 (file)
@@ -659,13 +659,13 @@ void PutObserverInServer (void)
        accuracy_resend(self);
 
        self.spectatortime = time;
-
+       
        self.classname = "observer";
        self.iscreature = FALSE;
        self.health = -666;
        self.takedamage = DAMAGE_NO;
        self.solid = SOLID_NOT;
-       self.movetype = MOVETYPE_NOCLIP;
+       self.movetype = MOVETYPE_FLY_WORLDONLY; //(self.cvar_cl_clippedspectating ? MOVETYPE_NOCLIP : MOVETYPE_FLY); // it's too early for this anyway, lets just set it in playerprethink
        self.flags = FL_CLIENT | FL_NOTARGET;
        self.armorvalue = 666;
        self.effects = 0;
@@ -694,9 +694,9 @@ void PutObserverInServer (void)
        self.fixangle = TRUE;
        self.crouch = FALSE;
 
-       self.view_ofs = PL_VIEW_OFS;
+       self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
        setorigin (self, spot.origin);
-       setsize (self, '0 0 0', '0 0 0');
+       setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
        self.prevorigin = self.origin;
        self.items = 0;
        self.weapons = 0;
@@ -705,6 +705,7 @@ void PutObserverInServer (void)
        self.model = "";
        self.modelindex = 0;
        self.weapon = 0;
+       self.switchingweapon = 0;
        self.weaponmodel = "";
        self.weaponentity = world;
        self.exteriorweaponentity = world;
@@ -739,6 +740,13 @@ void PutObserverInServer (void)
                else
                        self.frags = FRAGS_LMS_LOSER;
        }
+       else if(g_ca)
+       {
+               if(self.caplayer)
+                       self.frags = FRAGS_LMS_LOSER;
+               else
+                       self.frags = FRAGS_SPECTATOR;
+       }
        else
                self.frags = FRAGS_SPECTATOR;
 }
@@ -1018,6 +1026,7 @@ void PutClientInServer (void)
                self.oldorigin = self.origin;
                self.prevorigin = self.origin;
                self.lastrocket = world; // stop rocket guiding, no revenge from the grave!
+               self.lastteleporttime = time; // prevent insane speeds due to changing origin
 
                if(g_arena)
                {
@@ -1063,7 +1072,7 @@ void PutClientInServer (void)
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
 
                if (autocvar_g_spawnsound)
-                       sound (self, CHAN_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTN_NORM);
+                       sound (self, CH_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTN_NORM);
 
                if(g_assault) {
                        if(self.team == assault_attacker_team)
@@ -1085,7 +1094,6 @@ void PutClientInServer (void)
                        if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
                                self.weapon_load[j] = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));
                }
-               self.weapon_forbidchange = FALSE;
 
                oldself = self;
                self = spot;
@@ -1103,6 +1111,7 @@ void PutClientInServer (void)
                self.switchweapon = w_getbestweapon(self);
                self.cnt = self.switchweapon;
                self.weapon = 0;
+               self.switchingweapon = 0;
 
                if(!self.alivetime)
                        self.alivetime = time;
@@ -1279,10 +1288,10 @@ void ClientKill_Now()
             Damage(self, self, self, 1 , DEATH_KILL, self.origin, '0 0 0');            
            }
        }
-       
+
        if(self.killindicator && !wasfreed(self.killindicator))
-        remove(self.killindicator);
-       
+               remove(self.killindicator);
+
        self.killindicator = world;
 
        if(self.killindicator_teamchange)
@@ -1295,6 +1304,13 @@ void ClientKill_Now()
 }
 void KillIndicator_Think()
 {
+       if (gameover)
+       {
+               self.owner.killindicator = world;
+               remove(self);
+               return;
+       }
+
        if (!self.owner.modelindex)
        {
                self.owner.killindicator = world;
@@ -1327,10 +1343,16 @@ void KillIndicator_Think()
        }
 }
 
+float clientkilltime;
 void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2 = spec
 {
        float killtime;
+       float starttime;
        entity e;
+
+       if (gameover)
+               return;
+
        killtime = autocvar_g_balance_kill_delay;
 
        if(g_race_qualifying || g_cts)
@@ -1361,13 +1383,16 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
                }
                else
                {
+                       starttime = max(time, clientkilltime);
+
                        self.killindicator = spawn();
                        self.killindicator.owner = self;
                        self.killindicator.scale = 0.5;
                        setattachment(self.killindicator, self, "");
                        setorigin(self.killindicator, '0 0 52');
                        self.killindicator.think = KillIndicator_Think;
-                       self.killindicator.nextthink = time + (self.lip) * 0.05;
+                       self.killindicator.nextthink = starttime + (self.lip) * 0.05;
+                       clientkilltime = max(clientkilltime, self.killindicator.nextthink + 0.05);
                        self.killindicator.cnt = ceil(killtime);
                        self.killindicator.count = bound(0, ceil(killtime), 10);
                        //sprint(self, strcat("^1You'll be dead in ", ftos(self.killindicator.cnt), " seconds\n"));
@@ -1382,7 +1407,8 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
                                setattachment(e.killindicator, e, "");
                                setorigin(e.killindicator, '0 0 52');
                                e.killindicator.think = KillIndicator_Think;
-                               e.killindicator.nextthink = time + (e.lip) * 0.05;
+                               e.killindicator.nextthink = starttime + (e.lip) * 0.05;
+                               clientkilltime = max(clientkilltime, e.killindicator.nextthink + 0.05);
                                e.killindicator.cnt = ceil(killtime);
                        }
                        self.lip = 0;
@@ -1424,6 +1450,9 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2
 
 void ClientKill (void)
 {
+       if (gameover)
+               return;
+
        if((g_arena || g_ca) && ((champion && champion.classname == "player" && player_count > 1) || player_count == 1)) // don't allow a kill in this case either
        {
                // do nothing
@@ -1447,55 +1476,6 @@ void CTS_ClientKill (entity e) // silent version of ClientKill, used when player
     e.lip = 0;
 }
 
-void DoTeamChange(float destteam)
-{
-       float t, c0;
-       if(!teamplay)
-       {
-               if(destteam >= 0)
-                       SetPlayerColors(self, destteam);
-               return;
-       }
-       if(self.classname == "player")
-       if(destteam == -1)
-       {
-               CheckAllowedTeams(self);
-               t = FindSmallestTeam(self, TRUE);
-               switch(self.team)
-               {
-                       case COLOR_TEAM1: c0 = c1; break;
-                       case COLOR_TEAM2: c0 = c2; break;
-                       case COLOR_TEAM3: c0 = c3; break;
-                       case COLOR_TEAM4: c0 = c4; break;
-                       default:          c0 = 999;
-               }
-               switch(t)
-               {
-                       case 1:
-                               if(c0 > c1)
-                                       destteam = COLOR_TEAM1;
-                               break;
-                       case 2:
-                               if(c0 > c2)
-                                       destteam = COLOR_TEAM2;
-                               break;
-                       case 3:
-                               if(c0 > c3)
-                                       destteam = COLOR_TEAM3;
-                               break;
-                       case 4:
-                               if(c0 > c4)
-                                       destteam = COLOR_TEAM4;
-                               break;
-               }
-               if(destteam == -1)
-                       return;
-       }
-       if(destteam == self.team && destteam >= 0 && !self.killindicator)
-               return;
-       ClientKill_TeamChange(destteam);
-}
-
 void FixClientCvars(entity e)
 {
        // send prediction settings to the client
@@ -1671,6 +1651,8 @@ void ClientConnect (void)
 
        self.playerid = (playerid_last = playerid_last + 1);
 
+       PlayerStats_AddEvent(sprintf("kills-%d", self.playerid));
+
     if(clienttype(self) == CLIENTTYPE_BOT)
         PlayerStats_AddPlayer(self);
 
@@ -1691,8 +1673,7 @@ void ClientConnect (void)
        bprint("\n");
 
        stuffcmd(self, strcat(clientstuff, "\n"));
-       stuffcmd(self, strcat("exec maps/", mapname, ".cfg\n"));
-       stuffcmd(self, "cl_particles_reloadeffects\n");
+       stuffcmd(self, "cl_particles_reloadeffects\n"); // TODO do we still need this?
 
        FixClientCvars(self);
 
@@ -2001,7 +1982,7 @@ void play_countdown(float finished, string samp)
        if(clienttype(self) == CLIENTTYPE_REAL)
                if(floor(finished - time - frametime) != floor(finished - time))
                        if(finished - time < 6)
-                               sound (self, CHAN_AUTO, samp, VOL_BASE, ATTN_NORM);
+                               sound (self, CH_INFO, samp, VOL_BASE, ATTN_NORM);
 }
 
 void player_powerups (void)
@@ -2011,12 +1992,12 @@ void player_powerups (void)
 
        if((self.items & IT_USING_JETPACK) && !self.deadflag)
        {
-               SoundEntity_StartSound(self, CHAN_PLAYER, "misc/jetpack_fly.wav", VOL_BASE, autocvar_g_jetpack_attenuation);
+               SoundEntity_StartSound(self, CH_TRIGGER_SINGLE, "misc/jetpack_fly.wav", VOL_BASE, autocvar_g_jetpack_attenuation);
                self.modelflags |= MF_ROCKET;
        }
        else
        {
-               SoundEntity_StopSound(self, CHAN_PLAYER);
+               SoundEntity_StopSound(self, CH_TRIGGER_SINGLE);
                self.modelflags &~= MF_ROCKET;
        }
 
@@ -2332,6 +2313,7 @@ void SpectateCopy(entity spectatee) {
        self.pressedkeys = spectatee.pressedkeys;
        self.weapons = spectatee.weapons;
        self.switchweapon = spectatee.switchweapon;
+       self.switchingweapon = spectatee.switchingweapon;
        self.weapon = spectatee.weapon;
        self.nex_charge = spectatee.nex_charge;
        self.nex_chargepool_ammo = spectatee.nex_chargepool_ammo;
@@ -2345,7 +2327,8 @@ void SpectateCopy(entity spectatee) {
        self.dmg_save = spectatee.dmg_save;
        self.dmg_inflictor = spectatee.dmg_inflictor;
        self.angles = spectatee.v_angle;
-       self.fixangle = TRUE;
+       if(!self.BUTTON_USE)
+               self.fixangle = TRUE;
        setorigin(self, spectatee.origin);
        setsize(self, spectatee.mins, spectatee.maxs);
        SetZoomState(spectatee.zoomstate);
@@ -2391,12 +2374,41 @@ float SpectateUpdate() {
        return 1;
 }
 
-float SpectateNext() {
-       other = find(self.enemy, classname, "player");
 
-       if (!other)
+// Returns next available player to spectate if g_ca_spectate_enemies == 0
+entity CA_SpectateNext(entity start) {
+       if (start.team == self.team) {
+               return start;
+       }
+       
+       other = start;
+       // continue from current player
+       while(other && other.team != self.team) {
+               other = find(other, classname, "player");
+       }
+       
+       if (!other) {
+               // restart from begining
                other = find(other, classname, "player");
+               while(other && other.team != self.team) {
+                       other = find(other, classname, "player");
+               }
+       }
+       
+       return other;
+}
 
+float SpectateNext() {
+       other = find(self.enemy, classname, "player");
+       if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) {
+               // CA and ca players when spectating enemies is forbidden
+               other = CA_SpectateNext(other);
+       } else {
+               // other modes and ca spectators or spectating enemies is allowed
+               if (!other)
+                       other = find(other, classname, "player");
+       }
+       
        if (other)
                self.enemy = other;
 
@@ -2454,6 +2466,7 @@ void ShowRespawnCountdown()
        }
 }
 
+.float prevent_join_msgtime;
 void LeaveSpectatorMode()
 {
        if(nJoinAllowed(1)) {
@@ -2475,6 +2488,12 @@ void LeaveSpectatorMode()
                        if (time < self.jointime + autocvar_welcome_message_time)
                                Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD); // clear MOTD
 
+                       if (self.prevent_join_msgtime)
+                       {
+                               Send_CSQC_Centerprint_Generic_Expire(self, CPID_PREVENT_JOIN);
+                               self.prevent_join_msgtime = 0;
+                       }
+
                        return;
                } else {
                        if (g_ca && self.caplayer) {
@@ -2486,7 +2505,11 @@ void LeaveSpectatorMode()
        }
        else {
                //player may not join because of g_maxplayers is set
-               centerprint(self, PREVENT_JOIN_TEXT);
+               if (time - self.prevent_join_msgtime > 2)
+               {
+                       Send_CSQC_Centerprint_Generic(self, CPID_PREVENT_JOIN, PREVENT_JOIN_TEXT, 0, 0);
+                       self.prevent_join_msgtime = time;
+               }
        }
 }
 
@@ -2533,8 +2556,45 @@ void checkSpectatorBlock() {
        }
 }
 
+.float motd_actived_time; // used for both motd and campaign_message
+void PrintWelcomeMessage()
+{
+       if (self.motd_actived_time == 0) { // is there already a message showing?
+               if (autocvar_g_campaign) {
+                       if ((self.classname == "player" && self.BUTTON_INFO) || (self.classname != "player")) {
+                               self.motd_actived_time = time;
+                               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, campaign_message, -1, 0);
+                       }
+               } else {
+                       if ((time - self.jointime > autocvar_welcome_message_time) && self.BUTTON_INFO) {
+                               self.motd_actived_time = time;
+                               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, getwelcomemessage(), -1, 0);
+                       }
+               }
+       } else { // showing MOTD or campaign message
+               if (autocvar_g_campaign) {
+                       if (self.BUTTON_INFO)
+                               self.motd_actived_time = time;
+                       else if ((time - self.motd_actived_time > 2) && self.classname == "player") { // hide it some seconds after BUTTON_INFO has been released
+                               self.motd_actived_time = 0;
+                               Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD);
+                       }
+               } else {
+                       if ((time - self.jointime) > autocvar_welcome_message_time) {
+                               if (self.BUTTON_INFO)
+                                       self.motd_actived_time = time;
+                               else if (time - self.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
+                                       self.motd_actived_time = 0;
+                                       Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD);
+                               }
+                       }
+               }
+       }
+}
+
 void ObserverThink()
 {
+       float prefered_movetype;
        if (self.flags & FL_JUMPRELEASED) {
                if (self.BUTTON_JUMP && !self.version_mismatch) {
                        self.flags &~= FL_JUMPRELEASED;
@@ -2544,6 +2604,10 @@ void ObserverThink()
                        if(SpectateNext() == 1) {
                                self.classname = "spectator";
                        }
+               } else {
+                       prefered_movetype = ((!self.BUTTON_USE ? self.cvar_cl_clippedspectating : !self.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+                       if (self.movetype != prefered_movetype)
+                               self.movetype = prefered_movetype;
                }
        } else {
                if (!(self.BUTTON_ATCK || self.BUTTON_JUMP)) {
@@ -2556,6 +2620,8 @@ void ObserverThink()
                        }
                }
        }
+
+       PrintWelcomeMessage();
 }
 
 void SpectatorThink()
@@ -2594,6 +2660,7 @@ void SpectatorThink()
                        PutObserverInServer();
        }
 
+       PrintWelcomeMessage();
        self.flags |= FL_CLIENT | FL_NOTARGET;
 }
 
@@ -2629,7 +2696,6 @@ Called every frame for each client before the physics are run
 void() ctf_setstatus;
 void() nexball_setstatus;
 .float items_added;
-.float motd_actived_time; // used for both motd and campaign_message
 void PlayerPreThink (void)
 {
        WarpZone_PlayerPhysics_FixVAngle();
@@ -2664,10 +2730,12 @@ void PlayerPreThink (void)
                if(self.cvar_g_xonoticversion)
                        if(time > self.version_nagtime)
                        {
-                               if(strstr(self.cvar_g_xonoticversion, "git", 0) < 0)
+                               // don't notify git users
+                               if(strstr(self.cvar_g_xonoticversion, "git", 0) < 0 && strstr(self.cvar_g_xonoticversion, "autobuild", 0) < 0)
                                {
-                                       if(strstr(autocvar_g_xonoticversion, "git", 0) >= 0)
+                                       if(strstr(autocvar_g_xonoticversion, "git", 0) >= 0 || strstr(autocvar_g_xonoticversion, "autobuild", 0) >= 0)
                                        {
+                                               // notify release users if connecting to git
                                                dprint("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, " (beta)^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
                                                sprint(self, strcat("\{1}^1NOTE: ^7the server is running ^3Xonotic ", autocvar_g_xonoticversion, " (beta)^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n"));
                                        }
@@ -2677,12 +2745,14 @@ void PlayerPreThink (void)
                                                r = vercmp(self.cvar_g_xonoticversion, autocvar_g_xonoticversion);
                                                if(r < 0)
                                                {
-                                                       dprint("^1NOTE^7 to ", self.netname, "^7 - ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.com/^1!\n");
-                                                       sprint(self, strcat("\{1}^1NOTE: ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.com/^1!\n"));
+                                                       // give users new version
+                                                       dprint("^1NOTE^7 to ", self.netname, "^7 - ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.org/^1!\n");
+                                                       sprint(self, strcat("\{1}^1NOTE: ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.org/^1!\n"));
                                                }
                                                else if(r > 0)
                                                {
-                                                       dprint("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, "^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
+                                                       // notify users about old server version
+                                                       print("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, "^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
                                                        sprint(self, strcat("\{1}^1NOTE: ^7the server is running ^3Xonotic ", autocvar_g_xonoticversion, "^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n"));
                                                }
                                        }
@@ -2708,39 +2778,7 @@ void PlayerPreThink (void)
                PlayerUseKey();
        self.usekeypressed = self.BUTTON_USE;
 
-       if (self.motd_actived_time == 0) {
-               if (autocvar_g_campaign) {
-                       if (self.classname == "player" && self.BUTTON_INFO) {
-                               self.motd_actived_time = time;
-                               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, campaign_message, -1, 0);
-                       }
-               } else {
-                       if ((self.classname == "player" || time - self.jointime > autocvar_welcome_message_time) && self.BUTTON_INFO) {
-                               self.motd_actived_time = time;
-                               Send_CSQC_Centerprint_Generic(self, CPID_MOTD, getwelcomemessage(), -1, 0);
-                       }
-               }
-       } else { // showing MOTD or campaign message
-               if (autocvar_g_campaign) {
-                       if (self.classname == "player") {
-                               if (self.BUTTON_INFO)
-                                       self.motd_actived_time = time;
-                               else if (time - self.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
-                                       self.motd_actived_time = 0;
-                                       Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD);
-                               }
-                       }
-               } else {
-                       if (self.classname == "player" || (time - self.jointime) > autocvar_welcome_message_time) {
-                               if (self.BUTTON_INFO)
-                                       self.motd_actived_time = time;
-                               else if (time - self.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
-                                       self.motd_actived_time = 0;
-                                       Send_CSQC_Centerprint_Generic_Expire(self, CPID_MOTD);
-                               }
-                       }
-               }
-       }
+       PrintWelcomeMessage();
 
        if(self.classname == "player") {
 //             if(self.netname == "Wazat")
@@ -3013,7 +3051,7 @@ void PlayerPreThink (void)
                oldself = self; self = self.teamkill_soundsource;
                oldpusher = self.pusher; self.pusher = oldself;
 
-               PlayerSound(playersound_teamshoot, CHAN_VOICE, VOICETYPE_LASTATTACKER_ONLY);
+               PlayerSound(playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY);
 
                self.pusher = oldpusher;
                self = oldself;
@@ -3023,7 +3061,7 @@ void PlayerPreThink (void)
        if(time > self.taunt_soundtime)
        {
                self.taunt_soundtime = 0;
-               PlayerSound(playersound_taunt, CHAN_VOICE, VOICETYPE_AUTOTAUNT);
+               PlayerSound(playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT);
        }
 
        target_voicescript_next(self);
@@ -3131,6 +3169,8 @@ void PlayerPostThink (void)
 
        CheatFrame();
 
+       //CheckPlayerJump();
+
        if(self.classname == "player") {
                CheckRules_Player();
                UpdateChatBubble();