Skip to content

Commit fe8909e

Browse files
authored
Compass revamp (#1410)
* Proper text fade, objective direction and customizability for the compass.
1 parent deec1ae commit fe8909e

3 files changed

Lines changed: 83 additions & 239 deletions

File tree

game/neo/scripts/HudLayout.res

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,10 +819,15 @@
819819
{
820820
"fieldName" "NHudCompass"
821821
"visible" "1"
822-
"y_bottom_pos" "3"
823-
"needle_visible" "0"
824-
"needle_colored" "0"
822+
"xpos" "c-100"
823+
"ypos" "469"
824+
"wide" "200"
825+
"tall" "8"
826+
"fov" "90"
827+
"separators" "1"
825828
"objective_visible" "1"
829+
"text_fade_exp" "1"
830+
"text_color" "255 255 255 150"
826831
"box_color" "200 200 200 40"
827832
}
828833
NHudWeapon

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

Lines changed: 66 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,8 @@
1919
// memdbgon must be the last include file in a .cpp file!!!
2020
#include "tier0/memdbgon.h"
2121

22-
#define UNICODE_NEO_COMPASS_SIZE_BYTES (UNICODE_NEO_COMPASS_STR_LENGTH * sizeof(wchar_t))
23-
2422
using vgui::surface;
2523

26-
ConVar cl_neo_hud_debug_compass_enabled("cl_neo_hud_debug_compass_enabled", "0", FCVAR_USERINFO | FCVAR_CHEAT,
27-
"Whether the Debug HUD compass is enabled or not.", true, 0.0f, true, 1.0f);
28-
ConVar cl_neo_hud_debug_compass_pos_x("cl_neo_hud_debug_compass_pos_x", "0.45", FCVAR_USERINFO | FCVAR_CHEAT,
29-
"Horizontal position of the Debug compass, in range 0 to 1.", true, 0.0f, true, 1.0f);
30-
ConVar cl_neo_hud_debug_compass_pos_y("cl_neo_hud_debug_compass_pos_y", "0.925", FCVAR_USERINFO | FCVAR_CHEAT,
31-
"Vertical position of the Debug compass, in range 0 to 1.", true, 0.0f, true, 1.0f);
32-
ConVar cl_neo_hud_debug_compass_color_r("cl_neo_hud_debug_compass_color_r", "190", FCVAR_USERINFO | FCVAR_CHEAT,
33-
"Red color value of the Debug compass, in range 0 - 255.", true, 0.0f, true, 255.0f);
34-
ConVar cl_neo_hud_debug_compass_color_g("cl_neo_hud_debug_compass_color_g", "185", FCVAR_USERINFO | FCVAR_CHEAT,
35-
"Green color value of the Debug compass, in range 0 - 255.", true, 0.0f, true, 255.0f);
36-
ConVar cl_neo_hud_debug_compass_color_b("cl_neo_hud_debug_compass_color_b", "205", FCVAR_USERINFO | FCVAR_CHEAT,
37-
"Blue value of the Debug compass, in range 0 - 255.", true, 0.0f, true, 255.0f);
38-
ConVar cl_neo_hud_debug_compass_color_a("cl_neo_hud_debug_compass_color_a", "255", FCVAR_USERINFO | FCVAR_CHEAT,
39-
"Alpha color value of the Debug compass, in range 0 - 255.", true, 0.0f, true, 255.0f);
40-
4124
ConVar cl_neo_hud_rangefinder_enabled("cl_neo_hud_rangefinder_enabled", "1", FCVAR_ARCHIVE,
4225
"Whether the rangefinder HUD is enabled or not.", true, 0.0f, true, 1.0f);
4326
ConVar cl_neo_hud_rangefinder_pos_frac_x("cl_neo_hud_rangefinder_pos_frac_x", "0.55", FCVAR_ARCHIVE,
@@ -72,10 +55,6 @@ CNEOHud_Compass::CNEOHud_Compass(const char *pElementName, vgui::Panel *parent)
7255
m_hFont = 0;
7356

7457
SetVisible(true);
75-
76-
COMPILE_TIME_ASSERT(sizeof(m_wszCompassUnicode) == UNICODE_NEO_COMPASS_SIZE_BYTES);
77-
Assert(g_pVGuiLocalize);
78-
g_pVGuiLocalize->ConvertANSIToUnicode("\0", m_wszCompassUnicode, UNICODE_NEO_COMPASS_SIZE_BYTES);
7958
}
8059

8160
void CNEOHud_Compass::Paint()
@@ -88,44 +67,6 @@ void CNEOHud_Compass::Paint()
8867
BaseClass::Paint();
8968
}
9069

91-
void CNEOHud_Compass::GetCompassUnicodeString(const float angle, wchar_t* outUnicodeStr) const
92-
{
93-
// Char representation of the compass strip
94-
static constexpr char ROSE[] =
95-
"N | NE |\
96-
E | SE |\
97-
S | SW |\
98-
W | NW |\
99-
";
100-
101-
// One compass tick represents this many degrees of rotation
102-
const float unitAccuracy = 360.0f / sizeof(ROSE);
103-
104-
// Get index offset for this angle's compass position
105-
int offset = RoundFloatToInt(angle / unitAccuracy) - UNICODE_NEO_COMPASS_VIS_AROUND;
106-
if (offset < 0)
107-
{
108-
offset += sizeof(ROSE);
109-
}
110-
111-
// Both sides + center + terminator
112-
char compass[UNICODE_NEO_COMPASS_STR_LENGTH];
113-
int i;
114-
for (i = 0; i < UNICODE_NEO_COMPASS_STR_LENGTH - 1; i++)
115-
{
116-
// Get our index by circling around the compass strip.
117-
// We do modulo -1, because sizeof would land us on NULL
118-
// and terminate the string early.
119-
const int wrappedIndex = (offset + i) % (sizeof(ROSE) - 1);
120-
121-
compass[i] = ROSE[wrappedIndex];
122-
}
123-
// Finally, make sure we have a null terminator
124-
compass[i] = '\0';
125-
126-
g_pVGuiLocalize->ConvertANSIToUnicode(compass, outUnicodeStr, UNICODE_NEO_COMPASS_SIZE_BYTES);
127-
}
128-
12970
static C_NEO_Player *GetFirstPersonPlayer()
13071
{
13172
auto pLocalPlayer = C_NEO_Player::GetLocalNEOPlayer();
@@ -141,29 +82,30 @@ static C_NEO_Player *GetFirstPersonPlayer()
14182
return pFPPlayer;
14283
}
14384

85+
static float safeAngle(float angle) {
86+
while (angle < -180) {
87+
angle += 360;
88+
}
89+
while (angle >= 180) {
90+
angle -= 360;
91+
}
92+
return angle;
93+
}
94+
14495
void CNEOHud_Compass::UpdateStateForNeoHudElementDraw()
14596
{
14697
auto pLocalPlayer = C_NEO_Player::GetLocalNEOPlayer();
14798
Assert(pLocalPlayer);
14899

149-
static auto safeAngle = [](const float angle) -> float {
150-
if (angle > 180.0f)
151-
{
152-
return angle - 360.0f;
153-
}
154-
else if (angle < -180.0f)
155-
{
156-
return angle + 360.0f;
157-
}
158-
return angle;
159-
};
160-
161-
// Direction in -180 to 180
162-
float angle = pLocalPlayer->EyeAngles()[YAW] * -1;
163-
angle = safeAngle(angle);
164-
angle += 180.0f; // NEO NOTE (nullsystem): Adjust it again to match OG:NT's compass angle
165-
angle = safeAngle(angle);
166-
GetCompassUnicodeString(angle, m_wszCompassUnicode);
100+
// Point the objective arrow to the ghost, if it exists
101+
if (NEORules()->GhostExists() || NEORules()->JuggernautItemExists())
102+
{
103+
const Vector objPos = NEORules()->GetGameType() == NEO_GAME_TYPE_JGR ? NEORules()->GetJuggernautMarkerPos() : NEORules()->GetGhostPos();
104+
const Vector objVec = objPos - pLocalPlayer->EyePosition();
105+
const float objYaw = RAD2DEG(atan2f(objVec.y, objVec.x));
106+
float objAngle = safeAngle(- objYaw + pLocalPlayer->EyeAngles()[YAW]);
107+
m_objAngle = Clamp(objAngle, -(float)m_fov / 2, (float)m_fov / 2);
108+
}
167109

168110
if (cl_neo_hud_rangefinder_enabled.GetBool())
169111
{
@@ -201,11 +143,6 @@ void CNEOHud_Compass::DrawNeoHudElement(void)
201143
DrawCompass();
202144
}
203145

204-
if (cl_neo_hud_debug_compass_enabled.GetBool())
205-
{
206-
DrawDebugCompass();
207-
}
208-
209146
if (cl_neo_hud_rangefinder_enabled.GetBool() && GetFirstPersonPlayer()->IsInAim())
210147
{
211148
surface()->DrawSetTextColor(COLOR_NEO_WHITE);
@@ -223,174 +160,77 @@ void CNEOHud_Compass::ApplySchemeSettings(vgui::IScheme *pScheme)
223160
LoadControlSettings("scripts/HudLayout.res");
224161

225162
m_hFont = pScheme->GetFont("NHudOCRSmall");
226-
m_savedXBoxWidth = 0;
227163

228164
surface()->GetScreenSize(m_resX, m_resY);
229165
SetBounds(0, 0, m_resX, m_resY);
230166
SetZPos(90);
167+
m_fov = Clamp(m_fov, 45, 360);
168+
m_fadeExp = Clamp(m_fadeExp, 0, 10);
231169
}
232170

233171
void CNEOHud_Compass::DrawCompass() const
234172
{
173+
static const wchar_t* ROSE[] = {
174+
L"S", L"|", L"SW", L"|", L"W", L"|", L"NW", L"|", L"N", L"|", L"NE", L"|", L"E", L"|", L"SE", L"|",
175+
};
176+
235177
auto player = C_NEO_Player::GetLocalNEOPlayer();
236178
Assert(player);
237179

238180
surface()->DrawSetTextFont(m_hFont);
239181

240-
int fontWidth, fontHeight;
241-
surface()->GetTextSize(m_hFont, m_wszCompassUnicode, fontWidth, fontHeight);
242-
243-
if (m_savedXBoxWidth == 0)
244-
{
245-
// Using the fontHeight for padding works out
246-
m_savedXBoxWidth = fontWidth + fontHeight;
247-
}
248-
// These are the compass box dimension, just based on the font's dimensions
249-
const int xBoxWidth = m_savedXBoxWidth;
250-
const int yBoxHeight = fontHeight;
251-
252-
const int resXHalf = m_resX / 2;
253-
const int xBoxWidthHalf = xBoxWidth / 2;
254-
const int margin = m_yFromBottomPos;
255-
256182
DrawNeoHudRoundedBox(
257-
resXHalf - xBoxWidthHalf, m_resY - yBoxHeight - margin,
258-
resXHalf + xBoxWidthHalf, m_resY - margin,
183+
m_xPos, m_yPos,
184+
m_xPos + m_width, m_yPos + m_height,
259185
m_boxColor);
260186

261-
// Draw the compass "needle"
262-
if (m_needleVisible)
263-
{
264-
static const Color COMPASS_NEEDLE_COLOR_GREEN{25, 255, 25, 150};
265-
static const Color COMPASS_NEEDLE_COLOR_BLUE{25, 25, 255, 150};
266-
static const Color COMPASS_NEEDLE_COLOR_WHITE{255, 255, 255, 150};
267-
Color needleColor = COMPASS_NEEDLE_COLOR_WHITE;
268-
if (m_needleColored)
269-
{
270-
const int playerTeam = player->GetTeamNumber();
271-
switch (playerTeam)
272-
{
273-
break; case TEAM_JINRAI: needleColor = COMPASS_NEEDLE_COLOR_GREEN;
274-
break; case TEAM_NSF: needleColor = COMPASS_NEEDLE_COLOR_BLUE;
275-
break; default: break;
276-
}
187+
const float angle = -player->EyeAngles()[YAW];
188+
const int steps = ARRAYSIZE(ROSE);
189+
for (int i = 0; i < steps; i += m_separators ? 1 : 2) {
190+
const float stepAngle = (float)(i * 360) / steps;
191+
float drawAngle = safeAngle(stepAngle - angle + (float)m_fov / 2);
192+
if (drawAngle < 0) {
193+
drawAngle += 360;
194+
}
195+
if (drawAngle >= m_fov) {
196+
continue;
277197
}
278-
surface()->DrawSetColor(needleColor);
279-
surface()->DrawFilledRect(resXHalf - 1, m_resY - yBoxHeight - margin, resXHalf + 1, m_resY - margin);
198+
const float proportion = drawAngle / m_fov;
199+
const float alpha = !m_fadeExp ? 1 : 1 - pow(abs(2 * (proportion - 0.5)), m_fadeExp);
200+
surface()->DrawSetTextColor(m_textColor.r(), m_textColor.g(), m_textColor.b(), alpha * m_textColor.a());
201+
int labelWidth, labelHeight;
202+
surface()->GetTextSize(m_hFont, ROSE[i], labelWidth, labelHeight);
203+
const float padding = (float)labelHeight;
204+
surface()->DrawSetTextPos(m_xPos + padding + (m_width - padding * 2) * proportion - (float)labelWidth / 2, m_yPos + (float)m_height / 2 - (float)labelHeight / 2);
205+
surface()->DrawPrintText(ROSE[i], Q_UnicodeLength(ROSE[i]));
280206
}
281207

282-
surface()->DrawSetTextColor(COLOR_WHITE);
283-
surface()->DrawSetTextPos(resXHalf - fontWidth / 2, m_resY - fontHeight - margin);
284-
surface()->DrawPrintText(m_wszCompassUnicode, UNICODE_NEO_COMPASS_STR_LENGTH);
285-
286208
// Print compass objective arrow
287-
if (m_objectiveVisible && !player->IsCarryingGhost())
209+
if (m_objectiveVisible && !player->IsObjective())
288210
{
289211
// Point the objective arrow to the ghost, if it exists
290212
if (NEORules()->GhostExists() || NEORules()->JuggernautItemExists())
291213
{
292-
int ghostMarkerX, ghostMarkerY;
293-
bool ghostIsInView = false;
294-
if (NEORules()->GetGameType() != NEO_GAME_TYPE_JGR)
295-
{
296-
ghostIsInView = GetVectorInScreenSpace(NEORules()->GetGhostPos(), ghostMarkerX, ghostMarkerY);
297-
}
298-
else
299-
{
300-
ghostIsInView = GetVectorInScreenSpace(NEORules()->GetJuggernautMarkerPos(), ghostMarkerX, ghostMarkerY);
301-
}
302-
if (ghostIsInView) {
303-
// Print a unicode arrow to signify compass needle
304-
const wchar_t arrowUnicode[] = L"";
305-
306-
ghostMarkerX = clamp(ghostMarkerX, resXHalf - xBoxWidthHalf, resXHalf + xBoxWidthHalf);
307-
308-
const int ghosterTeam = NEORules()->GetGhosterTeam();
309-
const int ownTeam = player->GetTeam()->GetTeamNumber();
310-
311-
const auto teamClr32 = player->GetTeam()->GetRenderColor();
312-
const Color teamColor = Color(teamClr32.r, teamClr32.g, teamClr32.b, teamClr32.a);
313-
314-
const bool ghostIsBeingCarried = (ghosterTeam == TEAM_JINRAI || ghosterTeam == TEAM_NSF);
315-
const bool ghostIsCarriedByEnemyTeam = (ghosterTeam != ownTeam);
316-
317-
surface()->DrawSetTextColor(ghostIsBeingCarried ? ghostIsCarriedByEnemyTeam ? COLOR_RED : teamColor : COLOR_WHITE);
318-
surface()->DrawSetTextPos(ghostMarkerX, m_resY - fontHeight - margin * 2.25f);
319-
surface()->DrawPrintText(arrowUnicode, Q_UnicodeLength(arrowUnicode));
320-
}
214+
const float proportion = m_objAngle / m_fov + 0.5;
215+
216+
// Print a unicode arrow to signify objective
217+
const wchar_t arrowUnicode[] = L"";
218+
219+
const int ghosterTeam = NEORules()->GetGhosterTeam();
220+
const int ownTeam = player->GetTeam()->GetTeamNumber();
221+
222+
const auto teamClr32 = player->GetTeam()->GetRenderColor();
223+
const Color teamColor = Color(teamClr32.r, teamClr32.g, teamClr32.b, teamClr32.a);
224+
225+
const bool ghostIsBeingCarried = (ghosterTeam == TEAM_JINRAI || ghosterTeam == TEAM_NSF);
226+
const bool ghostIsCarriedByEnemyTeam = (ghosterTeam != ownTeam);
227+
228+
surface()->DrawSetTextColor(ghostIsBeingCarried ? ghostIsCarriedByEnemyTeam ? COLOR_RED : teamColor : COLOR_WHITE);
229+
int labelWidth, labelHeight;
230+
surface()->GetTextSize(m_hFont, arrowUnicode, labelWidth, labelHeight);
231+
const float padding = (float)labelHeight;
232+
surface()->DrawSetTextPos(m_xPos + padding + (m_width - padding * 2) * proportion - (float)labelWidth / 2, m_yPos - labelHeight);
233+
surface()->DrawPrintText(arrowUnicode, Q_UnicodeLength(arrowUnicode));
321234
}
322235
}
323-
324-
static const Color FADE_END_COLOR(116, 116, 116, 255);
325-
DrawNeoHudRoundedBoxFaded(
326-
resXHalf - xBoxWidthHalf, m_resY - yBoxHeight - margin,
327-
resXHalf, m_resY - margin,
328-
FADE_END_COLOR, 255, 0, true,
329-
true, false, true, false);
330-
DrawNeoHudRoundedBoxFaded(
331-
resXHalf, m_resY - yBoxHeight - margin,
332-
resXHalf + xBoxWidthHalf, m_resY - margin,
333-
FADE_END_COLOR, 0, 255, true,
334-
false, true, false, true);
335-
}
336-
337-
void CNEOHud_Compass::DrawDebugCompass() const
338-
{
339-
auto player = C_NEO_Player::GetLocalNEOPlayer();
340-
Assert(player);
341-
342-
// Direction in -180 to 180
343-
float angle = -(player->EyeAngles()[YAW]);
344-
345-
// Clamp in 180 turn range.
346-
if (angle > 180)
347-
{
348-
angle -= 360;
349-
}
350-
else if (angle < -180)
351-
{
352-
angle += 360;
353-
}
354-
355-
// Char representation of the compass strip
356-
const char rose[] = "N -- ne -- E -- se -- S -- sw -- W -- nw -- ";
357-
358-
// One compass tick represents this many degrees of rotation
359-
const float unitAccuracy = 360.0f / sizeof(rose);
360-
361-
// How many characters should be visible around each side of the needle position
362-
const int numCharsVisibleAroundNeedle = 6;
363-
364-
// Get index offset for this angle's compass position
365-
int offset = RoundFloatToInt((angle / unitAccuracy)) - numCharsVisibleAroundNeedle;
366-
if (offset < 0)
367-
{
368-
offset += sizeof(rose);
369-
}
370-
371-
// Both sides + center + terminator
372-
char compass[numCharsVisibleAroundNeedle * 2 + 2];
373-
int i;
374-
for (i = 0; i < sizeof(compass) - 1; i++)
375-
{
376-
// Get our index by circling around the compass strip.
377-
// We do modulo -1, because sizeof would land us on NULL
378-
// and terminate the string early.
379-
const int wrappedIndex = (offset + i) % (sizeof(rose) - 1);
380-
381-
compass[i] = rose[wrappedIndex];
382-
}
383-
// Finally, make sure we have a null terminator
384-
compass[i] = '\0';
385-
386-
// Draw the compass for this frame
387-
debugoverlay->AddScreenTextOverlay(
388-
cl_neo_hud_debug_compass_pos_x.GetFloat(),
389-
cl_neo_hud_debug_compass_pos_y.GetFloat(),
390-
gpGlobals->frametime,
391-
cl_neo_hud_debug_compass_color_r.GetInt(),
392-
cl_neo_hud_debug_compass_color_g.GetInt(),
393-
cl_neo_hud_debug_compass_color_b.GetInt(),
394-
cl_neo_hud_debug_compass_color_a.GetInt(),
395-
compass);
396236
}

0 commit comments

Comments
 (0)