ddnet/src/game/client/components/freezebars.cpp

239 lines
9 KiB
C++
Raw Normal View History

2022-03-25 11:54:11 +00:00
#include <game/client/gameclient.h>
#include "freezebars.h"
void CFreezeBars::RenderFreezeBar(const int ClientID)
{
const float FreezeBarWidth = 64.0f;
const float FreezeBarHalfWidth = 32.0f;
2022-03-29 21:24:39 +00:00
const float FreezeBarHight = 16.0f;
2022-03-25 11:54:11 +00:00
// pCharacter contains the predicted character for local players or the last snap for players who are spectated
CCharacterCore *pCharacter = &m_pClient->m_aClients[ClientID].m_Predicted;
if(pCharacter->m_FreezeEnd <= 0.0f || pCharacter->m_FreezeTick == 0 || !m_pClient->m_Snap.m_aCharacters[ClientID].m_HasExtendedDisplayInfo || pCharacter->m_IsInFreeze)
2022-03-25 11:54:11 +00:00
{
return;
}
2022-03-29 21:24:39 +00:00
const int Max = pCharacter->m_FreezeEnd - pCharacter->m_FreezeTick;
float FreezeProgress = clamp(Max - (Client()->GameTick(g_Config.m_ClDummy) - pCharacter->m_FreezeTick), 0, Max) / (float)Max;
2022-03-25 11:54:11 +00:00
if(FreezeProgress <= 0.0f)
{
return;
}
vec2 Position = m_pClient->m_aClients[ClientID].m_RenderPos;
2022-03-25 11:54:11 +00:00
Position.x -= FreezeBarHalfWidth;
Position.y += 32;
float Alpha = m_pClient->IsOtherTeam(ClientID) ? g_Config.m_ClShowOthersAlpha / 100.0f : 1.0f;
2022-03-29 21:24:39 +00:00
RenderFreezeBarPos(Position.x, Position.y, FreezeBarWidth, FreezeBarHight, FreezeProgress, Alpha);
}
void CFreezeBars::RenderFreezeBarPos(float x, const float y, const float width, const float height, float Progress, const float Alpha)
{
Progress = clamp(Progress, 0.0f, 1.0f);
// what percentage of the end pieces is used for the progress indicator and how much is the rest
// half of the ends are used for the progress display
const float RestPct = 0.5f;
const float ProgPct = 0.5f;
2022-03-29 21:24:39 +00:00
const float EndWidth = height; // to keep the correct scale - the height of the sprite is as long as the width
const float BarHeight = height;
const float WholeBarWidth = width;
2022-03-29 21:24:39 +00:00
const float MiddleBarWidth = WholeBarWidth - (EndWidth * 2.0f);
const float EndProgressWidth = EndWidth * ProgPct;
const float EndRestWidth = EndWidth * RestPct;
2022-03-29 21:24:39 +00:00
const float ProgressBarWidth = WholeBarWidth - (EndProgressWidth * 2.0f);
const float EndProgressProportion = EndProgressWidth / ProgressBarWidth;
const float MiddleProgressProportion = MiddleBarWidth / ProgressBarWidth;
// we cut 2% of all sides of all sprites so we don't get edge bleeding
const float CutL = 0.02f;
const float CutR = 0.98f;
const float CutT = 0.02f;
const float CutB = 0.98f;
const float Slice = 0.02f;
2022-03-29 21:24:39 +00:00
// beginning piece
float BeginningPieceProgress = 1;
if(Progress <= EndProgressProportion)
{
BeginningPieceProgress = Progress / EndProgressProportion;
}
2022-03-29 21:24:39 +00:00
// full
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFullLeft);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
// Subset: top_l, top_m, btm_m, btm_l
Graphics()->QuadsSetSubsetFree(CutL, CutT, RestPct + (ProgPct - CutL) * BeginningPieceProgress, CutT, RestPct + (ProgPct - CutL) * BeginningPieceProgress, CutB, CutL, CutB);
IGraphics::CQuadItem QuadFullBeginning(x, y, EndRestWidth + EndProgressWidth * BeginningPieceProgress, BarHeight);
2022-03-29 21:24:39 +00:00
Graphics()->QuadsDrawTL(&QuadFullBeginning, 1);
Graphics()->QuadsEnd();
// empty
if(BeginningPieceProgress < 1.0f)
2022-03-29 21:24:39 +00:00
{
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmptyRight);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
// Subset: top_m, top_l, btm_l, btm_m | it is mirrored on the horizontal axe and rotated 180 degrees
Graphics()->QuadsSetSubsetFree(ProgPct - (ProgPct - CutL) * BeginningPieceProgress, CutT, CutL, CutT, CutL, CutB, ProgPct - (ProgPct - CutL) * BeginningPieceProgress, CutB);
IGraphics::CQuadItem QuadEmptyBeginning(x + EndRestWidth + EndProgressWidth * BeginningPieceProgress, y, EndProgressWidth * (1.0f - BeginningPieceProgress), BarHeight);
2022-03-29 21:24:39 +00:00
Graphics()->QuadsDrawTL(&QuadEmptyBeginning, 1);
Graphics()->QuadsEnd();
}
// middle piece
x += EndWidth;
float MiddlePieceProgress = 1;
if(Progress <= EndProgressProportion + MiddleProgressProportion)
{
if(Progress <= EndProgressProportion)
{
MiddlePieceProgress = 0;
}
else
{
MiddlePieceProgress = (Progress - EndProgressProportion) / MiddleProgressProportion;
}
}
const float FullMiddleBarWidth = MiddleBarWidth * MiddlePieceProgress;
const float EmptyMiddleBarWidth = MiddleBarWidth - FullMiddleBarWidth;
// full freeze bar
2022-03-29 21:24:39 +00:00
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFull);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
// select the middle portion of the sprite so we don't get edge bleeding
if(FullMiddleBarWidth <= EndWidth)
2022-03-29 21:24:39 +00:00
{
// prevent pixel puree, select only a small slice
// Subset: top_l, top_m, btm_m, btm_l
Graphics()->QuadsSetSubsetFree(CutL, CutT, CutL + Slice, CutT, CutL + Slice, CutB, CutL, CutB);
2022-03-29 21:24:39 +00:00
}
else
{
// Subset: top_l, top_r, btm_r, btm_l
Graphics()->QuadsSetSubsetFree(CutL, CutT, CutR, CutT, CutR, CutB, CutL, CutB);
2022-03-29 21:24:39 +00:00
}
IGraphics::CQuadItem QuadFull(x, y, FullMiddleBarWidth, BarHeight);
Graphics()->QuadsDrawTL(&QuadFull, 1);
Graphics()->QuadsEnd();
// empty freeze bar
2022-03-29 21:24:39 +00:00
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmpty);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
// select the middle portion of the sprite so we don't get edge bleeding
if(EmptyMiddleBarWidth <= EndWidth)
2022-03-29 21:24:39 +00:00
{
// prevent pixel puree, select only a small slice
// Subset: top_m, top_l, btm_l, btm_m | it is mirrored on the horizontal axe and rotated 180 degrees
Graphics()->QuadsSetSubsetFree(CutL + Slice, CutT, CutL, CutT, CutL, CutB, CutL + Slice, CutB);
2022-03-29 21:24:39 +00:00
}
else
{
// Subset: top_r, top_l, btm_l, btm_r | it is mirrored on the horizontal axe and rotated 180 degrees
Graphics()->QuadsSetSubsetFree(CutR, CutT, CutL, CutT, CutL, CutB, CutR, CutB);
2022-03-29 21:24:39 +00:00
}
IGraphics::CQuadItem QuadEmpty(x + FullMiddleBarWidth, y, EmptyMiddleBarWidth, BarHeight);
Graphics()->QuadsDrawTL(&QuadEmpty, 1);
Graphics()->QuadsEnd();
// end piece
x += MiddleBarWidth;
float EndingPieceProgress = 1;
if(Progress <= 1)
{
if(Progress <= (EndProgressProportion + MiddleProgressProportion))
{
EndingPieceProgress = 0;
}
else
{
EndingPieceProgress = (Progress - EndProgressProportion - MiddleProgressProportion) / EndProgressProportion;
}
}
if(EndingPieceProgress > 0.0f)
{
// full
2022-03-29 21:24:39 +00:00
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFullLeft);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
// Subset: top_r, top_m, btm_m, btm_r | it is mirrored on the horizontal axe and rotated 180 degrees
Graphics()->QuadsSetSubsetFree(CutR, CutT, CutR - (ProgPct - CutL) * EndingPieceProgress, CutT, CutR - (ProgPct - CutL) * EndingPieceProgress, CutB, CutR, CutB);
IGraphics::CQuadItem QuadFullEnding(x, y, EndProgressWidth * EndingPieceProgress, BarHeight);
2022-03-29 21:24:39 +00:00
Graphics()->QuadsDrawTL(&QuadFullEnding, 1);
Graphics()->QuadsEnd();
}
// empty
Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmptyRight);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.f, 1.f, 1.f, Alpha);
// Subset: top_m, top_r, btm_r, btm_m
Graphics()->QuadsSetSubsetFree(ProgPct - (ProgPct - CutL) * (1.0f - EndingPieceProgress), CutT, CutR, CutT, CutR, CutB, ProgPct - (ProgPct - CutL) * (1.0f - EndingPieceProgress), CutB);
IGraphics::CQuadItem QuadEmptyEnding(x + EndProgressWidth * EndingPieceProgress, y, EndProgressWidth * (1.0f - EndingPieceProgress) + EndRestWidth, BarHeight);
2022-03-29 21:24:39 +00:00
Graphics()->QuadsDrawTL(&QuadEmptyEnding, 1);
Graphics()->QuadsEnd();
2022-03-29 21:24:39 +00:00
Graphics()->QuadsSetSubset(0, 0, 1, 1);
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
2022-03-25 11:54:11 +00:00
}
inline bool CFreezeBars::IsPlayerInfoAvailable(int ClientID) const
{
const void *pPrevInfo = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_PLAYERINFO, ClientID);
const void *pInfo = Client()->SnapFindItem(IClient::SNAP_CURRENT, NETOBJTYPE_PLAYERINFO, ClientID);
return pPrevInfo && pInfo;
}
2022-03-25 11:54:11 +00:00
void CFreezeBars::OnRender()
{
if(!g_Config.m_ClShowFreezeBars)
{
return;
}
2022-03-25 11:54:11 +00:00
// get screen edges to avoid rendering offscreen
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
// expand the edges to prevent popping in/out onscreen
//
// it is assumed that the tee with the freeze bar fit into a 240x240 box centered on the tee
// this may need to be changed or calculated differently in the future
float BorderBuffer = 120;
ScreenX0 -= BorderBuffer;
ScreenX1 += BorderBuffer;
ScreenY0 -= BorderBuffer;
ScreenY1 += BorderBuffer;
int LocalClientID = m_pClient->m_Snap.m_LocalClientID;
// render everyone else's freeze bar, then our own
for(int ClientID = 0; ClientID < MAX_CLIENTS; ClientID++)
{
if(ClientID == LocalClientID || !m_pClient->m_Snap.m_aCharacters[ClientID].m_Active || !IsPlayerInfoAvailable(ClientID))
2022-03-25 11:54:11 +00:00
{
continue;
}
//don't render if the tee is offscreen
vec2 *pRenderPos = &m_pClient->m_aClients[ClientID].m_RenderPos;
if(pRenderPos->x < ScreenX0 || pRenderPos->x > ScreenX1 || pRenderPos->y < ScreenY0 || pRenderPos->y > ScreenY1)
{
continue;
}
RenderFreezeBar(ClientID);
}
if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID))
2022-03-25 11:54:11 +00:00
{
RenderFreezeBar(LocalClientID);
}
}