]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Implement HUD info text, now team name and round time is visible
authorLegendaryGuard <rootuser999@gmail.com>
Mon, 29 Nov 2021 01:13:47 +0000 (02:13 +0100)
committerLegendaryGuard <rootuser999@gmail.com>
Mon, 29 Nov 2021 01:13:47 +0000 (02:13 +0100)
qcsrc/common/ent_cs.qc
qcsrc/common/gamemodes/gamemode/mh/cl_mh.qc
qcsrc/common/gamemodes/gamemode/mh/cl_mh.qh
qcsrc/common/gamemodes/gamemode/mh/mh.qh
qcsrc/common/gamemodes/gamemode/mh/sv_mh.qc
qcsrc/common/stats.qh

index a3691386be379296ae2b494025666e320a13de18..22201bfc63f62999057f5cfc4806097841681139 100644 (file)
@@ -157,6 +157,11 @@ ENTCS_PROP(SOLID, true, sv_solid, solid, ENTCS_SET_NORMAL,
        { WriteByte(chan, ent.sv_solid); },
        { ent.sv_solid = ReadByte(); })
 
+// gamemode specific player mh status (independent of score and frags)
+ENTCS_PROP(MH_STATUS, true, mh_status, mh_status, ENTCS_SET_NORMAL,
+       { WriteShort(chan, ent.mh_status); },
+       { ent.mh_status = ReadShort(); })
+
 #ifdef SVQC
 
        int ENTCS_PUBLICMASK = 0, ENTCS_PRIVATEMASK = 0;
index db54ce2d02cdca5de484d7c62a658144a6e9635b..3e60dce671cb8771d25d92759b16fcfbe383c0a1 100644 (file)
@@ -1,83 +1,56 @@
 #include "cl_mh.qh"
 
 #include <client/draw.qh>
+#include <client/hud/panel/modicons.qh>
 
-// TODO: change this?
-
-void HUD_Mod_MH_Export(int fh)
+void HUD_Mod_MH(vector pos, vector mySize)
 {
-       HUD_Write_Cvar("hud_panel_modicons_mh_layout");
-}
+       mod_active = 1; // required in each mod function that always shows something
 
-void DrawMHItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
-{
-       TC(int, layout); TC(int, i);
-       int stat = -1;
-       string pic = "";
-       vector color = '0 0 0';
-       switch(i)
+       int mystatus = entcs_receiver(player_localnum).mh_status;
+       string player_text = "";
+       vector player_color = '1 1 1';
+       //string player_icon = "";
+       if(mystatus == MH_STATUS_HUNTER)
        {
-               case 0: stat = STAT(REDALIVE); pic = "player_red"; color = '1 0 0'; break;
-               case 1: stat = STAT(BLUEALIVE); pic = "player_blue"; color = '0 0 1'; break;
-               case 2: stat = STAT(YELLOWALIVE); pic = "player_yellow"; color = '1 1 0'; break;
-               default:
-               case 3: stat = STAT(PINKALIVE); pic = "player_pink"; color = '1 0 1'; break;
+               player_text = _("Hunter");
+               player_color = '1 0 0';
+               //player_icon = "player_red";
        }
-
-       if(mySize.x/mySize.y > aspect_ratio)
+       else if(mystatus == MH_STATUS_RUNNER)
        {
-               i = aspect_ratio * mySize.y;
-               myPos.x = myPos.x + (mySize.x - i) / 2;
-               mySize.x = i;
+               player_text = _("Runner");
+               player_color = '0 0 1';
+               //player_icon = "player_blue";
        }
        else
        {
-               i = 1/aspect_ratio * mySize.x;
-               myPos.y = myPos.y + (mySize.y - i) / 2;
-               mySize.y = i;
+               // if the player has no valid status, don't draw anything
+               return;
        }
 
-       if(layout)
+       string time_text = string_null;
+       vector timer_color = '1 1 1';
+       if(!STAT(GAME_STOPPED) && !warmup_stage && STAT(MH_ROUNDTIMER) > 0)
        {
-               drawpic_aspect_skin(myPos, pic, vec2(0.5 * mySize.x, mySize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-               drawstring_aspect(myPos + eX * 0.5 * mySize.x, ftos(stat), vec2(0.5 * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL);
+               float timeleft = max(0, STAT(MH_ROUNDTIMER) - time);
+               timeleft = ceil(timeleft);
+               float minutesLeft = floor(timeleft / 60);
+               time_text = seconds_tostring(timeleft);
+               if(intermission_time || minutesLeft >= 5 || warmup_stage || STAT(MH_ROUNDTIMER) == 0)
+                       timer_color = '1 1 1'; //white
+               else if(minutesLeft >= 1)
+                       timer_color = '1 1 0'; //yellow
+               else
+                       timer_color = '1 0 0'; //red
        }
-       else
-               drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
-}
-
-void HUD_Mod_MH_Draw(vector myPos, vector mySize, int layout)
-{
-       int rows, columns;
-       float aspect_ratio;
-       aspect_ratio = (layout) ? 2 : 1;
-       rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
-       columns = ceil(team_count/rows);
 
-       int i;
-       float row = 0, column = 0;
-       vector pos = '0 0 0', itemSize;
-       itemSize = vec2(mySize.x / columns, mySize.y / rows);
-       for(i=0; i<team_count; ++i)
+       //drawpic_aspect_skin(pos, player_icon, vec2(0.5 * mySize.x, mySize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+       if(!time_text)
+               drawstring_aspect(pos, player_text, vec2(mySize.x, mySize.y), player_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+       else
        {
-               pos.x = myPos.x + column * itemSize.x;
-               pos.y = myPos.y + row * itemSize.y;
-
-               DrawMHItem(pos, itemSize, aspect_ratio, layout, i);
-
-               ++row;
-               if(row >= rows)
-               {
-                       row = 0;
-                       ++column;
-               }
+               drawstring_aspect(pos, player_text, vec2(0.5 * mySize.x, mySize.y), player_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+               drawstring_aspect(pos + eX * (0.5 * mySize.x), time_text, vec2(0.5 * mySize.x, mySize.y), timer_color, panel_fg_alpha, DRAWFLAG_NORMAL);
        }
 }
-
-// Clan Arena and Freeze Tag HUD modicons
-void HUD_Mod_MH(vector myPos, vector mySize)
-{
-       mod_active = 1; // required in each mod function that always shows something
-
-       HUD_Mod_MH_Draw(myPos, mySize, autocvar_hud_panel_modicons_mh_layout);
-}
index b198d4f0e52c4a7be77e4bb9ebd5fdce2b51f3cc..334db92f0be5d999371a01214ae767770263b21d 100644 (file)
@@ -1,7 +1,3 @@
 #pragma once
 
-int autocvar_hud_panel_modicons_mh_layout;
-
-void HUD_Mod_MH(vector myPos, vector mySize);
-void HUD_Mod_MH_Draw(vector myPos, vector mySize, int layout);
-void HUD_Mod_MH_Export(int fh);
+void HUD_Mod_MH(vector myPos, vector mySize);
\ No newline at end of file
index 090c410cc49de3a39f8dce37487c30efd3e1c992..eacfa51cf80eede7e3356ed1d1494eea028ad027 100644 (file)
@@ -2,12 +2,10 @@
 
 #include <common/mapinfo.qh>
 
-
 bool autocvar_g_mh_not_dm_maps;
 
 #ifdef CSQC
 void HUD_Mod_MH(vector pos, vector mySize);
-void HUD_Mod_MH_Export(int fh);
 #endif
 CLASS(Manhunt, Gametype)
        INIT(Manhunt)
@@ -16,7 +14,7 @@ CLASS(Manhunt, Gametype)
        }
        METHOD(Manhunt, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
        {
-               if(spawnpoints >= 4 && diameter > 4096)
+               if(spawnpoints >= 2 && diameter > 4096)
                        return true;
                return false;
        }
@@ -24,7 +22,7 @@ CLASS(Manhunt, Gametype)
        {
                if(!autocvar_g_mh_not_dm_maps)
                {
-                       // if this is unset, all DM maps support MMM too
+                       // if this is unset, all DM maps support MH too
                        if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags))
                                return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported)
                }
@@ -38,7 +36,17 @@ CLASS(Manhunt, Gametype)
                TC(Gametype, this);
                returns(menu, _("Point limit:"),     5,  100,  5, "g_mh_point_limit",         "g_mh_teams_override",         _("The amount of points needed before the match will end"));
        }
+#ifdef CSQC
+    ATTRIB(Manhunt, m_modicons, void(vector pos, vector mySize), HUD_Mod_MH);
+#endif
        ATTRIB(Manhunt, m_legacydefaults, string, "50 20 2 0");
 ENDCLASS(Manhunt)
 REGISTER_GAMETYPE(MANHUNT, NEW(Manhunt));
 #define g_mh IS_GAMETYPE(MANHUNT)
+
+#ifdef GAMEQC
+// shared state signalling the player's mh status
+.int mh_status;
+const int MH_STATUS_HUNTER = 1;
+const int MH_STATUS_RUNNER = 2;
+#endif
index a55c7bfdeadcc3d9286e3d650224a588720521df..95d224a38e9806c473dac6e3af7ec2b779252455 100644 (file)
@@ -5,6 +5,23 @@
 .vector taggedplayervelocity;
 .vector taggedplayerviewangles;
 
+void MH_FakeTimeLimit(entity e, float t)
+{
+       if(!IS_REAL_CLIENT(e))
+               return;
+#if 0
+       msg_entity = e;
+       WriteByte(MSG_ONE, 3); // svc_updatestat
+       WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
+       if(t < 0)
+               WriteCoord(MSG_ONE, autocvar_timelimit);
+       else
+               WriteCoord(MSG_ONE, (t + 1) / 60);
+#else
+       STAT(MH_ROUNDTIMER, e) = t;
+#endif
+}
+
 MUTATOR_HOOKFUNCTION(mh, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(1, string) = "mh_team";
@@ -47,6 +64,11 @@ MUTATOR_HOOKFUNCTION(mh, PlayerPreThink)
                        }
                });
        }
+
+       if(player.team == Team_IndexToTeam(1))
+               player.mh_status = MH_STATUS_HUNTER;
+       else if(player.team == Team_IndexToTeam(2))
+               player.mh_status = MH_STATUS_RUNNER;
 }
 
 MUTATOR_HOOKFUNCTION(mh, Damage_Calculate)
@@ -57,11 +79,15 @@ MUTATOR_HOOKFUNCTION(mh, Damage_Calculate)
        float frag_damage = M_ARGV(4, float);
        vector frag_force = M_ARGV(6, vector);
        
-       if(frag_deathtype==DEATH_CAMP.m_id)return;
+       if(frag_deathtype == DEATH_FALL.m_id){
+               M_ARGV(4, float) = 0; //no fall damage or splat damage
+               return;
+       }
        
-       if(IS_PLAYER(frag_target) && !IS_DEAD(frag_target)){ //check that the target is a player and not dead, allows anyone to knock around corpses for funsies
+       if(frag_deathtype==DEATH_CAMP.m_id || !IS_PLAYER(frag_attacker)) return;
 
-               if(frag_deathtype == DEATH_FALL.m_id)frag_damage = 0; //no fall damage or splat damage
+       
+       if(IS_PLAYER(frag_target) && !IS_DEAD(frag_target)){ //check that the target is a player and not dead, allows anyone to knock around corpses for funsies
 
                switch(autocvar_g_mh_weapons_damage)
                {
@@ -196,7 +222,7 @@ MUTATOR_HOOKFUNCTION(mh, GiveFragsForKill, CBC_ORDER_FIRST)
 // MH_CheckWinner                                      y                                       y
 // MH_RoundStart                                       y                                       y
 // MH_CheckTeams                                       y                                       y
-                       
+
 
 
 // general order which they are called in / when they are called:
@@ -289,6 +315,19 @@ MUTATOR_HOOKFUNCTION(mh, ClientDisconnect)
        return true;
 }
 
+// when players want to spec, clear team HUD
+MUTATOR_HOOKFUNCTION(mh, MakePlayerObserver)
+{
+       entity player = M_ARGV(0, entity);
+
+       if (IS_PLAYER(player) && !IS_DEAD(player))
+               mh_LastPlayerForTeam_Notify(player);
+               
+       player.mh_status = 0;
+       MH_FakeTimeLimit(player, -1); // restore original timelimit
+       return false;  // allow team reset
+}
+
 // Function: 
 // HideTeamNagger
 // Purpose:
@@ -426,7 +465,7 @@ float MH_CheckWinner()
                shuffleteams_on_reset_map = !allowed_to_spawn_untagged;
        ++round_counter_for_teamchanging;
        //FOREACH_CLIENT(IS_PLAYER(it), { CS(it).killcount = 0; nades_Clear(it); }); //hopefully "{ CS(it).killcount = 0; nades_Clear(it); }" works and doesn't cut off nades_Clear, untested
-       FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); }); //hopefully "{ CS(it).killcount = 0; nades_Clear(it); }" works and doesn't cut off nades_Clear, untested
+       FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); MH_FakeTimeLimit(it, -1); }); //hopefully "{ CS(it).killcount = 0; nades_Clear(it); }" works and doesn't cut off nades_Clear, untested
 
        return did_the_round_end;
 }
@@ -490,15 +529,30 @@ void MH_RoundStart()
 {
        allowed_to_spawn_untagged = boolean(warmup_stage);
        if(autocvar_g_mh_player_waypoints == 0){
+               FOREACH_CLIENT(IS_PLAYER(it), 
+               {
+                       if(it.team == Team_IndexToTeam(1))
+                               it.mh_status = MH_STATUS_HUNTER;
+                       else if(it.team == Team_IndexToTeam(2))
+                               it.mh_status = MH_STATUS_RUNNER;
+                       MH_FakeTimeLimit(it, round_handler_GetEndTime());
+               });
                return;
        } else if(autocvar_g_mh_player_waypoints == 1 || autocvar_g_mh_player_waypoints == 2){
-               FOREACH_CLIENT(IS_PLAYER(it) && it.team == Team_IndexToTeam(2)
+               FOREACH_CLIENT(IS_PLAYER(it), 
                {
-                       WaypointSprite_AttachCarrier(WP_Null, it, RADARICON_FLAGCARRIER);
-                       WaypointSprite_UpdateRule(it.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
-                       vector pl_color = colormapPaletteColor(it.clientcolors & 0x0F, false);
-                       WaypointSprite_UpdateTeamRadar(it.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, pl_color);
-                       WaypointSprite_Ping(it.waypointsprite_attachedforcarrier);
+                       if(it.team == Team_IndexToTeam(1))
+                               it.mh_status = MH_STATUS_HUNTER;
+                       else if(it.team == Team_IndexToTeam(2))
+                       {
+                               it.mh_status = MH_STATUS_RUNNER;
+                               WaypointSprite_AttachCarrier(WP_Null, it, RADARICON_FLAGCARRIER);
+                               WaypointSprite_UpdateRule(it.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+                               vector pl_color = colormapPaletteColor(it.clientcolors, false);
+                               WaypointSprite_UpdateTeamRadar(it.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, pl_color);
+                               WaypointSprite_Ping(it.waypointsprite_attachedforcarrier);
+                       }
+                       MH_FakeTimeLimit(it, round_handler_GetEndTime());
                });
        }
 }
@@ -521,6 +575,7 @@ MUTATOR_HOOKFUNCTION(mh, PutClientInServer)
        
        if (!allowed_to_spawn_untagged && IS_PLAYER(player) && round_handler_IsRoundStarted()){ // this can be true even when player is trying to join
                if (CS(player).jointime != time){ // not when connecting
+                       MH_FakeTimeLimit(player, round_handler_GetEndTime() - CS(player).jointime); // set HUD with current round time
                        Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_MH_JOIN_LATE);
                }
        }
@@ -601,11 +656,13 @@ MUTATOR_HOOKFUNCTION(mh, PlayerSpawn)
        player.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP;
        MH_count_players();
        if(player.team == Team_IndexToTeam(2) && !allowed_to_spawn_untagged && Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(2)) > 1 && round_handler_IsActive() && round_handler_IsRoundStarted()){
+               player.mh_status = MH_STATUS_RUNNER;
                player.deadflag = 1; // avoid a crash when a spectator joins runners mid-round and gets sent to hunters
                MoveToTeam(player, 1, 6); //index of 1 static is wrong way but it's working somehow with bubblegum and prayers
                player.deadflag = 0; // with bubblegum and prayers, there probably is probably a better check to use here
        }
        if(player.team == Team_IndexToTeam(1) && !IS_DEAD(player) && player.tagteleport == true && !allowed_to_spawn_untagged){
+               player.mh_status = MH_STATUS_HUNTER;
                player.tagteleport = false;
                player.origin = player.taggedplayerlocation;
                player.velocity = player.taggedplayervelocity;
@@ -615,6 +672,7 @@ MUTATOR_HOOKFUNCTION(mh, PlayerSpawn)
        if(autocvar_g_mh_player_waypoints == 2){
                if(player.team == Team_IndexToTeam(1))
                {
+                       player.mh_status = MH_STATUS_HUNTER;
                        WaypointSprite_AttachCarrier(WP_Null, player, RADARICON_FLAGCARRIER);
                        //player.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = mh_waypointsprite_visible_for_player;
                        WaypointSprite_UpdateRule(player.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
@@ -630,6 +688,11 @@ MUTATOR_HOOKFUNCTION(mh, reset_map_players)
 {
        FOREACH_CLIENT(true, {
                CS(it).killcount = 0;
+               MH_FakeTimeLimit(it, -1);
+               if(it.team == Team_IndexToTeam(1))
+                       it.mh_status = MH_STATUS_HUNTER;
+               else if(it.team == Team_IndexToTeam(2))
+                       it.mh_status = MH_STATUS_RUNNER;
                PutClientInServer(it);
                }
        );
index 463d98c850157f9c595dd4e57759ce75fb57c60c..7e260ec23aefe3ba5c855ae238935fbc74862a9c 100644 (file)
@@ -338,6 +338,9 @@ REGISTER_STAT(DOM_PPS_BLUE, float)
 REGISTER_STAT(DOM_PPS_YELLOW, float)
 REGISTER_STAT(DOM_PPS_PINK, float)
 
+// man hunt
+REGISTER_STAT(MH_ROUNDTIMER, float)
+
 #ifdef SVQC
 float autocvar_g_teleport_maxspeed;
 #endif