]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Survival: show hunters to other hunters in the scoreboard. It fixes #2844
authorterencehill <piuntn@gmail.com>
Sun, 11 Jun 2023 12:16:52 +0000 (12:16 +0000)
committerLegendaryGuard <rootuser999@gmail.com>
Sun, 11 Jun 2023 12:16:52 +0000 (12:16 +0000)
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/common/gamemodes/gamemode/survival/cl_survival.qc
qcsrc/common/gamemodes/gamemode/survival/sv_survival.qc

index b04600dcfaefcf6a51085997bf7f83458269592d..adcadb00df337aff2d15a23c9de42e855c1014a6 100644 (file)
@@ -973,7 +973,13 @@ string Scoreboard_GetName(entity pl)
        }
        else if(!teamplay)
        {
-               int f = entcs_GetClientColors(pl.sv_entnum);
+               int f;
+               // NOTE: always adding 1024 allows saving the colormap 0 as a value != 0
+               if (playerslots[pl.sv_entnum].colormap >= 1024)
+                       f = playerslots[pl.sv_entnum].colormap - 1024; // override server-side player colors
+               else
+                       f = entcs_GetClientColors(pl.sv_entnum);
+
                {
                        sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
                        sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
index 72f4b83e0c05094d69d2d1a903217349b26079c4..01e6a60068decec87730d3f522e74c2f97e19d8d 100644 (file)
@@ -8,13 +8,13 @@ NET_HANDLE(ENT_CLIENT_SURVIVALSTATUSES, bool isnew)
        make_pure(this);
        int sf = 0;
        serialize(byte, 0, sf);
-       if (sf & BIT(1))
+       if (sf & BIT(1)) // make all players survivors
        {
                for (int j = 0; j < maxclients; ++j)
                        if (playerslots[j])
                                playerslots[j].survival_status = SURV_STATUS_PREY;
        }
-       if (sf & BIT(0))
+       if (sf & BIT(0)) // reveal hunters (to hunters)
        {
                for (int i = 1; i <= maxclients; i += 8)
                {
@@ -29,6 +29,24 @@ NET_HANDLE(ENT_CLIENT_SURVIVALSTATUSES, bool isnew)
                        }
                }
        }
+
+       // we could check STAT(GAME_STOPPED) instead of this dedicated flag,
+       // unfortunately STAT(GAME_STOPPED) is still false right when we receive this
+       bool reveal_hunters_to_survivors = (sf & BIT(2)); // when a round ends
+
+       int mystatus = playerslots[player_localnum].survival_status;
+       for (int i = 1; i <= maxclients; ++i)
+       {
+               entity e = playerslots[i - 1];
+               if (!e) continue;
+
+               int plcolor = SURV_COLOR_PREY;
+               if(e.survival_status == SURV_STATUS_HUNTER && (mystatus == SURV_STATUS_HUNTER || reveal_hunters_to_survivors))
+                       plcolor = SURV_COLOR_HUNTER;
+
+               e.colormap = 1024 + plcolor; // override scoreboard and player model colors
+       }
+
        return true;
 }
 
@@ -69,14 +87,10 @@ MUTATOR_HOOKFUNCTION(cl_surv, ForcePlayercolors_Skip, CBC_ORDER_LAST)
 
        entity player = M_ARGV(0, entity);
        entity e = playerslots[player.entnum - 1];
-       int surv_status = ((e) ? e.survival_status : SURV_COLOR_PREY);
-       int mystatus = playerslots[player_localnum].survival_status;
-
-       int plcolor = SURV_COLOR_PREY; // default to survivor
-       if((mystatus == SURV_STATUS_HUNTER || intermission || STAT(GAME_STOPPED)) && surv_status == SURV_STATUS_HUNTER)
-               plcolor = SURV_COLOR_HUNTER;
-
-       player.colormap = 1024 + plcolor;
+       if (e && e.colormap)
+               player.colormap = e.colormap;
+       else
+               player.colormap = 1024 + SURV_COLOR_PREY;
        return true;
 }
 
index 83817ac8ed3b7c5ff4ed10aac939ac4fb3cd85f1..dea924772d8d1b0e0ec330e9b86d82c070876000 100644 (file)
@@ -15,6 +15,13 @@ void SurvivalStatuses_Send()
 {
        // SendFlags can be set to anything != 0, SurvivalStatuses_SendEntity won't use its value
        survivalStatuses.SendFlags = 1;
+       survivalStatuses.nextthink = 0; // clear delayed send
+}
+
+void SurvivalStatuses_Send_Delayed()
+{
+       survivalStatuses.think = SurvivalStatuses_Send;
+       survivalStatuses.nextthink = time + 0.2;
 }
 
 bool SurvivalStatuses_SendEntity(entity this, entity dest, float sendflags)
@@ -22,12 +29,13 @@ bool SurvivalStatuses_SendEntity(entity this, entity dest, float sendflags)
        Stream out = MSG_ENTITY;
        WriteHeader(out, ENT_CLIENT_SURVIVALSTATUSES);
 
-       sendflags = BIT(0) | BIT(1);
+       sendflags |= BIT(1); // make all players survivors
 
-       // send hunter statuses only to hunters
-       bool send_hunter_statuses = (dest.survival_status == SURV_STATUS_HUNTER || STAT(GAME_STOPPED));
-       if (!send_hunter_statuses)
-               sendflags &= !BIT(0);
+       if (dest.survival_status == SURV_STATUS_HUNTER);
+               sendflags |= BIT(0); // send hunter statuses
+
+       if (round_handler_AwaitingNextRound())
+               sendflags |= (BIT(0) | BIT(2)); // send hunter statuses and reveal hunters to survivors
 
        serialize(byte, out, sendflags);
        if (sendflags & BIT(0)) {
@@ -35,7 +43,7 @@ bool SurvivalStatuses_SendEntity(entity this, entity dest, float sendflags)
                        int f = 0;
                        entity e = edict_num(i);
                        for (int b = 0; b < 8; ++b, e = nextent(e)) {
-                               bool is_hunter = (IS_PLAYER(e) && e.survival_status == SURV_STATUS_HUNTER);
+                               bool is_hunter = (INGAME(e) && e.survival_status == SURV_STATUS_HUNTER);
                                if (is_hunter)
                                        f |= BIT(b);
                        }
@@ -95,6 +103,7 @@ float Surv_CheckWinner()
                allowed_to_spawn = false;
                game_stopped = true;
                round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
+               SurvivalStatuses_Send();
                return 1;
        }
 
@@ -132,6 +141,7 @@ float Surv_CheckWinner()
        allowed_to_spawn = false;
        game_stopped = true;
        round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
+       SurvivalStatuses_Send();
 
        FOREACH_CLIENT(true,
        {
@@ -256,21 +266,6 @@ MUTATOR_HOOKFUNCTION(surv, ClientObituary)
                M_ARGV(5, bool) = true; // anonymous attacker
 }
 
-MUTATOR_HOOKFUNCTION(surv, PlayerPreThink)
-{
-       entity player = M_ARGV(0, entity);
-
-       if(IS_PLAYER(player) || INGAME(player))
-       {
-               // update the scoreboard colour display to out the real killer at the end of the round
-               // running this every frame to avoid cheats
-               int plcolor = SURV_COLOR_PREY;
-               if(player.survival_status == SURV_STATUS_HUNTER && game_stopped)
-                       plcolor = SURV_COLOR_HUNTER;
-               setcolor(player, plcolor);
-       }
-}
-
 MUTATOR_HOOKFUNCTION(surv, PlayerSpawn)
 {
        entity player = M_ARGV(0, entity);
@@ -310,6 +305,10 @@ MUTATOR_HOOKFUNCTION(surv, PutClientInServer)
 
        if (!warmup_stage)
                eliminatedPlayers.SendFlags |= 1;
+       // send statuses with a small delay to make sure a playerslot exists, otherwise
+       // personal colors for this player won't be overriden
+       // it also reduces network traffic when multiple clients join the server at once (at map start)
+       SurvivalStatuses_Send_Delayed();
 }
 
 MUTATOR_HOOKFUNCTION(surv, reset_map_players)
@@ -325,6 +324,7 @@ MUTATOR_HOOKFUNCTION(surv, reset_map_players)
                }
        });
        bot_relinkplayerlist();
+       // this will also clear scheduled SurvivalStatuses_Send set by PutClientInServer
        SurvivalStatuses_Send();
        return true;
 }
@@ -383,7 +383,7 @@ MUTATOR_HOOKFUNCTION(surv, PlayerDies)
 
        // killed an ally! punishment is death
        if(autocvar_g_survival_punish_teamkill && frag_attacker != frag_target && IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target) && frag_attacker.survival_status == frag_target.survival_status && !ITEM_DAMAGE_NEEDKILL(frag_deathtype))
-       if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) // don't autokill if the round hasn't
+       if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) // don't autokill if the round hasn't started yet
                Damage(frag_attacker, frag_attacker, frag_attacker, 100000, DEATH_MIRRORDAMAGE.m_id, DMG_NOWEP, frag_attacker.origin, '0 0 0');
        return true;
 }