Skip to content

Commit

Permalink
Implement Looper and Engineer wall HUD
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaffeine committed Apr 6, 2023
1 parent 6c1f27f commit 011cef4
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 7 deletions.
220 changes: 214 additions & 6 deletions src/game/client/components/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,12 +490,12 @@ void CHud::RenderWarmupTimer()
}
}

void CHud::FormatTimerText(char *pDest, int DestSize, int Ticks)
float CHud::FormatTimerText(char *pDest, int DestSize, int Ticks, bool ForceLongFormat, float ShowFractionIfLessThan)
{
if(Ticks < 0)
{
str_copy(pDest, "N/A", DestSize);
return;
return -1;
}

int RemainingTicks = Ticks - Client()->GameTick(g_Config.m_ClDummy);
Expand All @@ -507,15 +507,92 @@ void CHud::FormatTimerText(char *pDest, int DestSize, int Ticks)
const float ResultTime = CentiSeconds / 10.0f;
int Seconds = ResultTime;

if(CentiSeconds % 10)
bool ShowFraction = ResultTime <= ShowFractionIfLessThan;

if(!ShowFraction)
{
Seconds += 1;
if(CentiSeconds % 10)
{
Seconds += 1;
}
}

const int Minutes = Seconds / 60;
Seconds = Seconds % 60;

str_format(pDest, DestSize, "%02d:%02d", Minutes, Seconds);
if(Minutes || ForceLongFormat)
{
str_format(pDest, DestSize, "%02d:%02d", Minutes, Seconds);
}
else
{
if(ShowFraction)
{
str_format(pDest, DestSize, "%d.%d", Seconds, CentiSeconds % 10);
}
else
{
str_format(pDest, DestSize, "%d", Seconds);
}
}

return ResultTime;
}

void CHud::RenderLaser(vec2 From, vec2 To, const ColorRGBA OuterColor, const ColorRGBA InnerColor)
{
float Len = distance(To, From);

vec2 Dir;
if(Len > 0)
{
Dir = normalize_pre_length(To - From, Len);
}

Graphics()->TextureClear();
Graphics()->QuadsBegin();

Graphics()->SetColor(OuterColor);

constexpr double LaserHeadScale = 0.25;

vec2 Out;

// do outline
Out = vec2(Dir.y, -Dir.x) * 5.0f * LaserHeadScale;
IGraphics::CFreeformItem Freeform(From.x - Out.x, From.y - Out.y, From.x + Out.x, From.y + Out.y, To.x - Out.x, To.y - Out.y, To.x + Out.x, To.y + Out.y);
Graphics()->QuadsDrawFreeform(&Freeform, 1);

// do inner
Graphics()->SetColor(InnerColor);
Out = vec2(Dir.y, -Dir.x) * 4.0f * LaserHeadScale;

Freeform = IGraphics::CFreeformItem(From.x - Out.x, From.y - Out.y, From.x + Out.x, From.y + Out.y, To.x - Out.x, To.y - Out.y, To.x + Out.x, To.y + Out.y);
Graphics()->QuadsDrawFreeform(&Freeform, 1);
Graphics()->QuadsEnd();

int SpriteIndex = time_get() % 3;
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_aSpriteParticleSplat[SpriteIndex]);
Graphics()->QuadsBegin();
Graphics()->QuadsSetRotation(time_get());
{
Graphics()->SetColor(OuterColor.r, OuterColor.g, OuterColor.b, 1.0f);
IGraphics::CQuadItem QuadItem[2] = {
{From.x, From.y, 24 * LaserHeadScale, 24 * LaserHeadScale},
{To.x, To.y, 24 * LaserHeadScale, 24 * LaserHeadScale},
};
Graphics()->QuadsDraw(&QuadItem[0], 2);
}
{
Graphics()->SetColor(InnerColor.r, InnerColor.g, InnerColor.b, 1.0f);
IGraphics::CQuadItem QuadItem[2] = {
{From.x, From.y, 20 * LaserHeadScale, 20 * LaserHeadScale},
{To.x, To.y, 20 * LaserHeadScale, 20 * LaserHeadScale},
};
Graphics()->QuadsDraw(&QuadItem[0], 2);
}
Graphics()->QuadsSetRotation(0);
Graphics()->QuadsEnd();
}

void CHud::RenderTextInfo()
Expand Down Expand Up @@ -734,6 +811,8 @@ void CHud::PrepareInfclassHudQuads()
Array[i] = IGraphics::CQuadItem(x + i * 24, y, g_Config.m_ClInfStatusSize, g_Config.m_ClInfStatusSize);
}
m_IcStatusIconOffset = Graphics()->QuadContainerAddQuads(m_HudQuadContainerIndex, Array, MaxStatusIcons);
Graphics()->QuadsSetSubset(0, 0, 1, 1);
m_IcParticleIconOffset = RenderTools()->QuadContainerAddSprite(m_HudQuadContainerIndex, 1.f);
}

void CHud::RenderAmmoHealthAndArmor(const CNetObj_Character *pCharacter)
Expand Down Expand Up @@ -1571,15 +1650,27 @@ void CHud::RenderClassExtraHud(int ClientID)
}

int Lines = 0;
int WallType = 0;
switch(PlayerClass)
{
case PLAYERCLASS_HERO:
Lines = 2;
break;
case PLAYERCLASS_ENGINEER:
case PLAYERCLASS_LOOPER:
Lines = 1;
WallType = PlayerClass;
break;
default:
return;
}

if(WallType && (pClientData->m_InfClassClassData1 == 0))
{
// The wall is not placed
return;
}

bool DrawBox = false;
float HudW = 48;
float HudH = 40;
Expand Down Expand Up @@ -1623,22 +1714,139 @@ void CHud::RenderClassExtraHud(int ClientID)
{
TextXOffset += 12;
}
else if(WallType)
{
if(WallType == PLAYERCLASS_LOOPER)
{
TextXOffset += 12;
}
else // PLAYERCLASS_ENGINEER
{
TextXOffset += 7;
}

constexpr int LaserXOffset = 5;
constexpr int LaserYOffset = 4;
constexpr float LaserVerticalSize = 22;

ColorHSLA OutlineColor = g_Config.m_ClLaserRifleOutlineColor;
ColorHSLA InnerColor = g_Config.m_ClLaserRifleInnerColor;

ColorRGBA OutlineColorRGB = color_cast<ColorRGBA, ColorHSLA>(OutlineColor);
ColorRGBA InnerColorRGB = color_cast<ColorRGBA, ColorHSLA>(InnerColor);

OutlineColorRGB.a = 1.0f;
InnerColorRGB.a = 1.0f;

vec2 From = vec2(X + LaserXOffset, Y + LaserYOffset);
vec2 To = vec2(X + LaserXOffset, Y + LaserYOffset + LaserVerticalSize);

RenderLaser(From, To, OutlineColorRGB, InnerColorRGB);

if(WallType == PLAYERCLASS_LOOPER)
{
IGraphics::CTextureHandle *aParticles = GameClient()->m_ParticlesSkin.m_aSpriteParticles;
Graphics()->TextureSet(aParticles[SPRITE_PART_BALL]);
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);

static int64_t LastTime = 0;
int64_t t = time();

float TimePassed = 0;

if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
if(!pInfo->m_Paused)
TimePassed = (float)((t - LastTime) / (double)time_freq()) * pInfo->m_Speed;
}
else
{
if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags & GAMESTATEFLAG_PAUSED))
TimePassed = (float)((t - LastTime) / (double)time_freq());
}
LastTime = t;

constexpr float ScaleFrom = 3.f;
constexpr float Duration = 0.75f;

float HammerAreaWidth = 5;
float HammerAreHeight = LaserVerticalSize;

static IGraphics::SRenderSpriteInfo aHammerDots[] = {
{vec2(From.x + 0.52f * HammerAreaWidth, From.y + 0.1f * HammerAreHeight), ScaleFrom * 0.78f, 0.0f},
{vec2(From.x + 0.8f * HammerAreaWidth, From.y + 0.11f * HammerAreHeight), ScaleFrom * 0.10f, 0.0f},
{vec2(From.x + 0.24f * HammerAreaWidth, From.y + 0.15f * HammerAreHeight), ScaleFrom * 0.71f, 0.0f},
{vec2(From.x + 0.66f * HammerAreaWidth, From.y + 0.18f * HammerAreHeight), ScaleFrom * 0.25f, 0.0f},
{vec2(From.x + 0.0f * HammerAreaWidth, From.y + 0.4f * HammerAreHeight), ScaleFrom * 0.5f, 0.0f},
{vec2(From.x + 0.55f * HammerAreaWidth, From.y + 0.45f * HammerAreHeight), ScaleFrom * 0.1f, 0.0f},
{vec2(From.x + 0.12f * HammerAreaWidth, From.y + 0.51f * HammerAreHeight), ScaleFrom * 0.9f, 0.0f},
{vec2(From.x + 0.38f * HammerAreaWidth, From.y + 0.50f * HammerAreHeight), ScaleFrom * 0.48f, 0.0f},
{vec2(From.x + 0.93f * HammerAreaWidth, From.y + 0.62f * HammerAreHeight), ScaleFrom * 0.5f, 0.0f},
{vec2(From.x + 0.44f * HammerAreaWidth, From.y + 0.68f * HammerAreHeight), ScaleFrom * 0.1f, 0.0f},
{vec2(From.x + 0.31f * HammerAreaWidth, From.y + 0.83f * HammerAreHeight), ScaleFrom * 0.9f, 0.0f},
{vec2(From.x + 0.79f * HammerAreaWidth, From.y + 0.91f * HammerAreHeight), ScaleFrom * 0.48f, 0.0f},
};

constexpr bool UseRandom = true;

for(auto &SpriteInfo : aHammerDots)
{
float ProgressRemaining = SpriteInfo.m_Scale / ScaleFrom;
float PassedProgress = TimePassed / Duration;
float NewProgressRemaining = ProgressRemaining - PassedProgress;
if(NewProgressRemaining < 0)
{
NewProgressRemaining = 1 + (NewProgressRemaining - static_cast<int>(NewProgressRemaining));
if(UseRandom)
{
SpriteInfo.m_Pos.x = From.x + random_float() * HammerAreaWidth;
SpriteInfo.m_Pos.y = From.y + random_float() * HammerAreHeight;
}
}
SpriteInfo.m_Scale = ScaleFrom * NewProgressRemaining;
}

Graphics()->RenderQuadContainerAsSpriteMultiple(m_HudQuadContainerIndex, m_IcParticleIconOffset, std::size(aHammerDots), aHammerDots);

RenderLaser(From, To, OutlineColorRGB, InnerColorRGB);
From.x += HammerAreaWidth;
To.x += HammerAreaWidth;
RenderLaser(From, To, OutlineColorRGB, InnerColorRGB);
}
else // PLAYERCLASS_ENGINEER
{
RenderLaser(From, To, OutlineColorRGB, InnerColorRGB);
}
}

{
char aBuffer[32];
const int PlayerTimerEndTick = pClientData->m_InfClassClassData1;
FormatTimerText(aBuffer, sizeof(aBuffer), PlayerTimerEndTick);
const bool ForceMinutes = WallType == 0; // Prefer short format for walls and force minutes for everything else (Flags)
const float FractionPartIfLessThan = WallType ? 5.0f : 0;
const float RemainingSeconds = FormatTimerText(aBuffer, sizeof(aBuffer), PlayerTimerEndTick, ForceMinutes, FractionPartIfLessThan);
ColorRGBA TextColor(1, 1, 1, 1);

// Draw the text
switch(PlayerClass)
{
case PLAYERCLASS_ENGINEER:
case PLAYERCLASS_LOOPER:
if(RemainingSeconds < FractionPartIfLessThan)
{
TextColor = ColorRGBA(1.0f, 0.5f, 0.5f, 1.0f);
}
break;
case PLAYERCLASS_HERO:
break;
default:
break;
}

TextRender()->TextColor(TextColor);
TextRender()->Text(X + TextXOffset, Y + TextYOffset, FontSize, aBuffer, -1.0f);
TextRender()->TextColor(1, 1, 1, 1);
}

if(PlayerClass == PLAYERCLASS_HERO)
Expand Down
4 changes: 3 additions & 1 deletion src/game/client/components/hud.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class CHud : public CComponent
void RenderWarmupTimer();
void RenderLocalTime(float x);

void FormatTimerText(char *pDest, int DestSize, int Ticks);
float FormatTimerText(char *pDest, int DestSize, int Ticks, bool ForceLongFormat = true, float ShowFractionIfLessThan = 0);
void RenderLaser(vec2 From, vec2 To, const ColorRGBA LaserOutlineColor, const ColorRGBA LaserInnerColor);

static constexpr float MOVEMENT_INFORMATION_LINE_HEIGHT = 8.0f;

Expand Down Expand Up @@ -140,6 +141,7 @@ class CHud : public CComponent
int m_PracticeModeOffset;

int m_IcStatusIconOffset;
int m_IcParticleIconOffset;
};

#endif

0 comments on commit 011cef4

Please sign in to comment.