Merge branch 'master' into Mario/showspecs
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cl_client.qc
index 3bc5cc7..fe12ffc 100644 (file)
@@ -30,7 +30,38 @@ void send_CSQC_teamnagger() {
        WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
 }
 
-float ClientData_Send(entity to, int sf)
+int CountSpectators(entity player, entity to)
+{
+       if(!player) { return 0; } // not sure how, but best to be safe
+
+       entity head;
+       float spec_count = 0;
+       FOR_EACH_REALCLIENT(head)
+       {
+               if(IS_SPEC(head))
+               if(head != to)
+               if(head.enemy == player)
+                       spec_count += 1;
+       }
+
+       return spec_count;
+}
+
+void WriteSpectators(entity player, entity to)
+{
+       if(!player) { return; } // not sure how, but best to be safe
+
+       entity head;
+       FOR_EACH_REALCLIENT(head)
+       {
+               if(IS_SPEC(head))
+               if(head != to)
+               if(head.enemy == player)
+                       WriteByte(MSG_ENTITY, num_for_edict(head));
+       }
+}
+
+bool ClientData_Send(entity to, int sf)
 {
        if(to != self.owner)
        {
@@ -54,6 +85,8 @@ float ClientData_Send(entity to, int sf)
                sf |= 4; // zoomed
        if(e.porto_v_angle_held)
                sf |= 8; // angles held
+       // always check spectators
+       sf |= 16; // spectator handling?
 
        WriteByte(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
        WriteByte(MSG_ENTITY, sf);
@@ -67,6 +100,13 @@ float ClientData_Send(entity to, int sf)
                WriteAngle(MSG_ENTITY, e.v_angle.y);
        }
 
+       if(sf & 16)
+       {
+               float specs = CountSpectators(e, to);
+               WriteByte(MSG_ENTITY, specs);
+               WriteSpectators(e, to);
+       }
+
        return true;
 }
 
@@ -164,7 +204,10 @@ putting a client as observer in the server
 void FixPlayermodel();
 void PutObserverInServer (void)
 {
-       entity  spot;
+       entity spot;
+
+       SetSpectator(self, world);
+
     self.hud = HUD_NORMAL;
 
        if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
@@ -1280,6 +1323,8 @@ void ClientDisconnect (void)
 
        PlayerStats_GameReport_FinalizePlayer(self);
 
+       SetSpectator(self, world);
+
        if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
 
        CheatShutdownClient();
@@ -1812,31 +1857,21 @@ float SpectateUpdate()
 
 float SpectateSet()
 {
-       if(self.enemy.classname != "player")
+       if(!IS_PLAYER(self.enemy))
                return false;
-       /*if(self.enemy.vehicle)
-       {
 
-               msg_entity = self;
-               WriteByte(MSG_ONE, SVC_SETVIEW);
-               WriteEntity(MSG_ONE, self.enemy);
-               //stuffcmd(self, "set viewsize $tmpviewsize \n");
+       ClientData_Touch(self.enemy);
 
-               self.movetype = MOVETYPE_NONE;
-               accuracy_resend(self);
-       }
-       else
-       {*/
-               msg_entity = self;
-               WriteByte(MSG_ONE, SVC_SETVIEW);
-               WriteEntity(MSG_ONE, self.enemy);
-               //stuffcmd(self, "set viewsize $tmpviewsize \n");
-               self.movetype = MOVETYPE_NONE;
-               accuracy_resend(self);
+       msg_entity = self;
+       WriteByte(MSG_ONE, SVC_SETVIEW);
+       WriteEntity(MSG_ONE, self.enemy);
+       //stuffcmd(self, "set viewsize $tmpviewsize \n");
+       self.movetype = MOVETYPE_NONE;
+       accuracy_resend(self);
+
+       if(!SpectateUpdate())
+               PutObserverInServer();
 
-               if(!SpectateUpdate())
-                       PutObserverInServer();
-       //}
        return true;
 }
 
@@ -1850,12 +1885,15 @@ void SetSpectator(entity player, entity spectatee)
        // 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; }
+
+       // needed to update spectator list
+       if(old_spectatee) { ClientData_Touch(old_spectatee); }
 }
 
 float Spectate(entity pl)
 {
        if(g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
-       if(pl.team != self.team)
+       if(DIFF_TEAM(pl, self))
                return 0;
 
        SetSpectator(self, pl);
@@ -1863,23 +1901,21 @@ float Spectate(entity pl)
 }
 
 // Returns next available player to spectate if g_ca_spectate_enemies == 0
-entity CA_SpectateNext(entity start) {
-       if (start.team == self.team) {
-               return start;
-       }
+entity CA_SpectateNext(entity start)
+{
+       if(SAME_TEAM(start, self)) { return start; }
 
        other = start;
        // continue from current player
-       while(other && other.team != self.team) {
+       while(other && DIFF_TEAM(other, self))
                other = find(other, classname, "player");
-       }
 
-       if (!other) {
+       if (!other)
+       {
                // restart from begining
                other = find(other, classname, "player");
-               while(other && other.team != self.team) {
+               while(other && DIFF_TEAM(other, self))
                        other = find(other, classname, "player");
-               }
        }
 
        return other;
@@ -1889,10 +1925,13 @@ float SpectateNext()
 {
        other = find(self.enemy, classname, "player");
 
-       if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) {
+       if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
+       {
                // CA and ca players when spectating enemies is forbidden
                other = CA_SpectateNext(other);
-       } else {
+       }
+       else
+       {
                // other modes and ca spectators or spectating enemies is allowed
                if (!other)
                        other = find(other, classname, "player");
@@ -1978,6 +2017,8 @@ void LeaveSpectatorMode()
                        self.classname = "player";
                        nades_RemoveBonus(self);
 
+                       SetSpectator(self, world);
+
                        if(autocvar_g_campaign || autocvar_g_balance_teams)
                                { JoinBestTeam(self, false, true); }
 
@@ -2258,7 +2299,7 @@ void PlayerPreThink (void)
 
        // Savage: Check for nameless players
        if (isInvisibleString(self.netname)) {
-               string new_name = strzone(strcat("Player@", self.netaddress));
+               string new_name = strzone(strcat("Player@", ftos(self.playerid)));
                if(autocvar_sv_eventlog)
                        GameLogEcho(strcat(":name:", ftos(self.playerid), ":", new_name));
                if(self.netname_previous)