Skip to content

Commit 86eafc1

Browse files
authored
Show bot ghoster callouts in compass (#1785)
1 parent c2a54bc commit 86eafc1

4 files changed

Lines changed: 208 additions & 0 deletions

File tree

game/neo/resource/NeoModEvents.res

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@
9191
"ghosterping" "bool" // the player is carrying the ghost
9292
}
9393

94+
"ghost_enemy_callout"
95+
{
96+
"userid" "short" // user ID of ghoster sending callout
97+
"team" "short" // team of ghoster
98+
"targetid" "short" // ID of target being called out
99+
"targetx" "short" // target x position
100+
"targety" "short" // target y position
101+
"targetz" "short" // target z position
102+
}
103+
94104
// inherited from NT
95105
"game_round_start"
96106
{

src/game/client/neo/ui/neo_hud_compass.cpp

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ ConVar cl_neo_hud_rangefinder_pos_frac_y("cl_neo_hud_rangefinder_pos_frac_y", "0
3131
"In fractional to the total screen height, the y-axis position of the rangefinder.",
3232
true, 0.0f, true, 1.0f);
3333

34+
ConVar cl_neo_ghost_callout_compass_time("cl_neo_ghost_callout_compass_time", "10.0", FCVAR_CHEAT,
35+
"Time in seconds that ghost callouts remain on the compass.", true, 0.0f, false, 0.0f);
36+
3437
DECLARE_NAMED_HUDELEMENT(CNEOHud_Compass, NHudCompass);
3538

3639
NEO_HUD_ELEMENT_DECLARE_FREQ_CVAR(Compass, 0.00695)
@@ -58,6 +61,61 @@ CNEOHud_Compass::CNEOHud_Compass(const char *pElementName, vgui::Panel *parent)
5861
SetVisible(true);
5962
}
6063

64+
void CNEOHud_Compass::Init()
65+
{
66+
ListenForGameEvent("ghost_enemy_callout");
67+
ListenForGameEvent("round_start");
68+
ListenForGameEvent("player_team");
69+
}
70+
71+
void CNEOHud_Compass::LevelShutdown()
72+
{
73+
HideAllGhostCallouts();
74+
}
75+
76+
void CNEOHud_Compass::HideAllGhostCallouts()
77+
{
78+
for (int i = 0; i < V_ARRAYSIZE(m_GhostCallouts); ++i)
79+
{
80+
m_GhostCallouts[i].timer.Invalidate();
81+
}
82+
}
83+
84+
void CNEOHud_Compass::FireGameEvent(IGameEvent* event)
85+
{
86+
auto eventName = event->GetName();
87+
if (!Q_stricmp(eventName, "ghost_enemy_callout"))
88+
{
89+
const int localTeam = GetLocalPlayerTeam();
90+
const int playerTeam = event->GetInt("team");
91+
92+
if (localTeam == TEAM_SPECTATOR || playerTeam != localTeam)
93+
{
94+
return;
95+
}
96+
97+
int targetId = event->GetInt("targetid");
98+
if (targetId > 0 && targetId < V_ARRAYSIZE(m_GhostCallouts))
99+
{
100+
GhostCallout &callout = m_GhostCallouts[targetId];
101+
callout.worldPos = Vector(event->GetInt("targetx"), event->GetInt("targety"), event->GetInt("targetz"));
102+
callout.timer.Start(cl_neo_ghost_callout_compass_time.GetFloat());
103+
}
104+
}
105+
else if (!Q_stricmp(eventName, "round_start"))
106+
{
107+
HideAllGhostCallouts();
108+
}
109+
else if (!Q_stricmp(eventName, "player_team"))
110+
{
111+
auto player = UTIL_PlayerByUserId(event->GetInt("userid"));
112+
if (player && player->IsLocalPlayer())
113+
{
114+
HideAllGhostCallouts();
115+
}
116+
}
117+
}
118+
61119
void CNEOHud_Compass::Paint()
62120
{
63121
PaintNeoElement();
@@ -159,6 +217,7 @@ void CNEOHud_Compass::ApplySchemeSettings(vgui::IScheme *pScheme)
159217
LoadControlSettings("scripts/HudLayout.res");
160218

161219
m_hFont = pScheme->GetFont("NHudOCRSmall");
220+
m_hFontSmall = pScheme->GetFont("NHudOCRSmallerNoAdditive");
162221

163222
surface()->GetScreenSize(m_resX, m_resY);
164223
SetBounds(0, 0, m_resX, m_resY);
@@ -232,4 +291,111 @@ void CNEOHud_Compass::DrawCompass() const
232291
surface()->DrawPrintText(arrowUnicode, Q_UnicodeLength(arrowUnicode));
233292
}
234293
}
294+
295+
DrawCallouts();
296+
}
297+
298+
void CNEOHud_Compass::DrawCallouts() const
299+
{
300+
struct CalloutDrawInfo
301+
{
302+
int calloutIdx; // Index into m_GhostCallouts
303+
float age;
304+
float renderX;
305+
float renderY;
306+
};
307+
308+
CUtlVectorFixed<CalloutDrawInfo, MAX_PLAYERS_ARRAY_SAFE> visibleCallouts;
309+
310+
int latestIdx = -1;
311+
float newestAge = 9999.0f;
312+
313+
const wchar_t arrowUnicode[] = L"";
314+
int labelWidth, labelHeight;
315+
surface()->GetTextSize(m_hFont, arrowUnicode, labelWidth, labelHeight);
316+
const float padding = (float)labelHeight;
317+
318+
// Cache ConVars and View parameters
319+
const float maxAge = cl_neo_ghost_callout_compass_time.GetFloat();
320+
const Vector viewOrigin = MainViewOrigin();
321+
const float viewYaw = MainViewAngles()[YAW];
322+
323+
// Collect visible callouts
324+
for (int i = 0; i < V_ARRAYSIZE(m_GhostCallouts); ++i)
325+
{
326+
const GhostCallout& callout = m_GhostCallouts[i];
327+
328+
if (!callout.timer.HasStarted() || callout.timer.IsElapsed())
329+
continue;
330+
331+
float age = callout.timer.GetElapsedTime();
332+
333+
const Vector objVec = callout.worldPos - viewOrigin;
334+
const float objYaw = RAD2DEG(atan2f(objVec.y, objVec.x));
335+
float drawObjAngle = AngleNormalize(-objYaw + viewYaw);
336+
float clampedAngle = Clamp(drawObjAngle, -(float)m_fov / 2, (float)m_fov / 2);
337+
338+
const float proportion = clampedAngle / m_fov + 0.5;
339+
340+
float renderX = m_xPos + padding + (m_width - padding * 2) * proportion - (float)labelWidth / 2;
341+
float renderY = m_yPos - labelHeight;
342+
343+
CalloutDrawInfo info;
344+
info.calloutIdx = i;
345+
info.age = age;
346+
info.renderX = renderX;
347+
info.renderY = renderY;
348+
349+
int currentIdx = visibleCallouts.AddToTail(info);
350+
351+
// Determine latest
352+
if (age < newestAge)
353+
{
354+
newestAge = age;
355+
latestIdx = currentIdx;
356+
}
357+
}
358+
359+
auto DrawSingleCallout = [&](int idx, bool bDrawText)
360+
{
361+
CalloutDrawInfo& info = visibleCallouts[idx];
362+
363+
float alphaScale = (maxAge > 0.f) ? (1.0f - Clamp(info.age / maxAge, 0.0f, 1.0f)) : 0.0f;
364+
int alpha = Clamp((int)(255.0f * alphaScale), 0, 255);
365+
Color calloutColor = Color(255, 0, 0, alpha);
366+
367+
surface()->DrawSetTextColor(calloutColor);
368+
surface()->DrawSetTextPos(info.renderX, info.renderY);
369+
surface()->DrawPrintText(arrowUnicode, 1);
370+
371+
if (bDrawText)
372+
{
373+
float dist = METERS_PER_INCH * viewOrigin.DistTo(m_GhostCallouts[info.calloutIdx].worldPos);
374+
375+
wchar_t wszDist[16];
376+
V_swprintf_safe(wszDist, L"%im", (int)dist);
377+
378+
int distWidth, distHeight;
379+
surface()->GetTextSize(m_hFontSmall, wszDist, distWidth, distHeight);
380+
381+
surface()->DrawSetTextFont(m_hFontSmall);
382+
surface()->DrawSetTextPos(info.renderX + (labelWidth / 2) - (distWidth / 2), info.renderY - distHeight);
383+
surface()->DrawPrintText(wszDist, Q_UnicodeLength(wszDist));
384+
}
385+
};
386+
387+
// Draw older ones first
388+
for (int i = 0; i < visibleCallouts.Count(); ++i)
389+
{
390+
if (i == latestIdx)
391+
continue;
392+
393+
DrawSingleCallout(i, false);
394+
}
395+
396+
// Draw the latest one on top with distance text
397+
if (latestIdx != -1)
398+
{
399+
DrawSingleCallout(latestIdx, true);
400+
}
235401
}

src/game/client/neo/ui/neo_hud_compass.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,50 @@
66

77
#include "neo_hud_childelement.h"
88
#include "hudelement.h"
9+
#include "shareddefs.h"
10+
#include "util_shared.h"
911
#include <vgui_controls/EditablePanel.h>
1012

13+
struct GhostCallout
14+
{
15+
Vector worldPos;
16+
CountdownTimer timer;
17+
};
18+
1119
class CNEOHud_Compass : public CNEOHud_ChildElement, public CHudElement, public vgui::EditablePanel
1220
{
1321
DECLARE_CLASS_SIMPLE(CNEOHud_Compass, EditablePanel);
1422

1523
public:
1624
CNEOHud_Compass(const char *pElementName, vgui::Panel *parent = nullptr);
1725

26+
virtual void Init() override;
27+
virtual void LevelShutdown() override;
1828
virtual void ApplySchemeSettings(vgui::IScheme *pScheme);
1929
virtual void Paint();
2030

2131
protected:
2232
virtual void UpdateStateForNeoHudElementDraw();
2333
virtual void DrawNeoHudElement();
2434
virtual ConVar* GetUpdateFrequencyConVar() const;
35+
virtual void FireGameEvent(IGameEvent* event) override;
2536

2637
private:
2738
void DrawCompass() const;
39+
void DrawCallouts() const;
40+
void HideAllGhostCallouts();
2841

2942
private:
3043
vgui::HFont m_hFont;
44+
vgui::HFont m_hFontSmall;
3145

3246
int m_resX, m_resY;
3347
float m_objAngle;
3448

3549
wchar_t m_wszRangeFinder[11];
3650

51+
GhostCallout m_GhostCallouts[MAX_PLAYERS_ARRAY_SAFE];
52+
3753
CPanelAnimationVarAliasType(bool, m_showCompass, "visible", "1", "bool");
3854
CPanelAnimationVarAliasType(int, m_xPos, "xpos", "c-50", "proportional_xpos");
3955
CPanelAnimationVarAliasType(int, m_yPos, "ypos", "469", "proportional_ypos");

src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ void CNEOBotGhostEquipmentHandler::Update( CNEOBot *me )
9191
CBaseEntity *pFocus = m_hCurrentFocusEnemy.Get();
9292
if ( pFocus && pFocus->IsAlive() )
9393
{
94+
if ( bUpdateCallout )
95+
{
96+
IGameEvent *event = gameeventmanager->CreateEvent( "ghost_enemy_callout" );
97+
if ( event )
98+
{
99+
event->SetInt( "userid", me->GetUserID() );
100+
event->SetInt( "team", me->GetTeamNumber() );
101+
event->SetInt( "targetid", pFocus->entindex() );
102+
Vector pos = pFocus->GetAbsOrigin();
103+
event->SetInt( "targetx", pos.x );
104+
event->SetInt( "targety", pos.y );
105+
event->SetInt( "targetz", pos.z );
106+
gameeventmanager->FireEvent( event );
107+
}
108+
}
109+
94110
// Notify teammates to look at the enemy
95111
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
96112
{

0 commit comments

Comments
 (0)