From 668f983a5406b5d1ebe36abc3c9c469f38ebb4bd Mon Sep 17 00:00:00 2001 From: LegendaryGuard Date: Mon, 29 Nov 2021 02:13:47 +0100 Subject: [PATCH] Implement HUD info text, now team name and round time is visible --- qcsrc/common/ent_cs.qc | 5 ++ qcsrc/common/gamemodes/gamemode/mh/cl_mh.qc | 99 ++++++++------------- qcsrc/common/gamemodes/gamemode/mh/cl_mh.qh | 6 +- qcsrc/common/gamemodes/gamemode/mh/mh.qh | 16 +++- qcsrc/common/gamemodes/gamemode/mh/sv_mh.qc | 85 +++++++++++++++--- qcsrc/common/stats.qh | 3 + 6 files changed, 131 insertions(+), 83 deletions(-) diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc index a3691386b..22201bfc6 100644 --- a/qcsrc/common/ent_cs.qc +++ b/qcsrc/common/ent_cs.qc @@ -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; diff --git a/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qc b/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qc index db54ce2d0..3e60dce67 100644 --- a/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qc +++ b/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qc @@ -1,83 +1,56 @@ #include "cl_mh.qh" #include +#include -// 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= 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); -} diff --git a/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qh b/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qh index b198d4f0e..334db92f0 100644 --- a/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qh +++ b/qcsrc/common/gamemodes/gamemode/mh/cl_mh.qh @@ -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 diff --git a/qcsrc/common/gamemodes/gamemode/mh/mh.qh b/qcsrc/common/gamemodes/gamemode/mh/mh.qh index 090c410cc..eacfa51cf 100644 --- a/qcsrc/common/gamemodes/gamemode/mh/mh.qh +++ b/qcsrc/common/gamemodes/gamemode/mh/mh.qh @@ -2,12 +2,10 @@ #include - 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 diff --git a/qcsrc/common/gamemodes/gamemode/mh/sv_mh.qc b/qcsrc/common/gamemodes/gamemode/mh/sv_mh.qc index a55c7bfde..95d224a38 100644 --- a/qcsrc/common/gamemodes/gamemode/mh/sv_mh.qc +++ b/qcsrc/common/gamemodes/gamemode/mh/sv_mh.qc @@ -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); } ); diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index 463d98c85..7e260ec23 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -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 -- 2.39.2