diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a0a38bc2..9ecbfaaf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1914,6 +1914,8 @@ if(CLIENT) components/emoticon.h components/flow.cpp components/flow.h + components/freezebars.cpp + components/freezebars.h components/ghost.cpp components/ghost.h components/hud.cpp diff --git a/src/game/client/components/freezebars.cpp b/src/game/client/components/freezebars.cpp new file mode 100644 index 000000000..f9386c5f6 --- /dev/null +++ b/src/game/client/components/freezebars.cpp @@ -0,0 +1,76 @@ +#include + +#include "freezebars.h" + + +void CFreezeBars::RenderFreezeBar(const int ClientID) +{ + const float FreezeBarWidth = 64.0f; + const float FreezeBarHalfWidth = 32.0f; + const float FreezeBarHight = 16.0f; + + if(m_pClient->m_aClients[ClientID].m_Predicted.m_FreezeEnd <= 0.0f) + { + return; + } + const int Max = m_pClient->m_aClients[ClientID].m_Predicted.m_FreezeEnd - m_pClient->m_aClients[ClientID].m_Predicted.m_FreezeTick; + float FreezeProgress = clamp(Max - (m_pClient->m_GameWorld.GameTick() - m_pClient->m_aClients[ClientID].m_Predicted.m_FreezeTick), 0, Max) / (float)Max; + if(FreezeProgress <= 0.0f) + { + return; + } + + vec2 Position; + if(ClientID >= 0 && ClientID < MAX_CLIENTS) + Position = m_pClient->m_aClients[ClientID].m_RenderPos; + else + Position = mix(vec2(m_pClient->m_aClients[ClientID].m_RenderPrev.m_X, m_pClient->m_aClients[ClientID].m_RenderPrev.m_Y), vec2(m_pClient->m_aClients[ClientID].m_RenderCur.m_X, m_pClient->m_aClients[ClientID].m_RenderCur.m_Y), Client()->IntraGameTick(g_Config.m_ClDummy)); + Position.x -= FreezeBarHalfWidth; + Position.y += 32; + + float Alpha = m_pClient->IsOtherTeam(ClientID) ? g_Config.m_ClShowOthersAlpha / 100.0f : 1.0f; + + m_pClient->m_Hud.RenderProgressBar(Position.x, Position.y, FreezeBarWidth, FreezeBarHight, FreezeProgress, Alpha); +} + +void CFreezeBars::OnRender() +{ + // 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 || !m_pClient->m_Players.IsPlayerInfoAvailable(ClientID)) + { + 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 && m_pClient->m_Players.IsPlayerInfoAvailable(LocalClientID)) + { + RenderFreezeBar(LocalClientID); + } +} \ No newline at end of file diff --git a/src/game/client/components/freezebars.h b/src/game/client/components/freezebars.h new file mode 100644 index 000000000..3f8e0ce1f --- /dev/null +++ b/src/game/client/components/freezebars.h @@ -0,0 +1,14 @@ +#ifndef GAME_CLIENT_COMPONENTS_FREEZEBARS_H +#define GAME_CLIENT_COMPONENTS_FREEZEBARS_H +#include + +class CFreezeBars : public CComponent +{ + void RenderFreezeBar(const int ClientID); + +public: + virtual int Sizeof() const override { return sizeof(*this); } + virtual void OnRender() override; +}; + +#endif diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index f62c8a6dd..774427d27 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -824,10 +824,6 @@ void CHud::PreparePlayerStateQuads() m_LiveFrozenOffset = RenderTools()->QuadContainerAddSprite(m_HudQuadContainerIndex, 0.f, 0.f, 12.f, 12.f); m_DummyHammerOffset = RenderTools()->QuadContainerAddSprite(m_HudQuadContainerIndex, 0.f, 0.f, 12.f, 12.f); m_DummyCopyOffset = RenderTools()->QuadContainerAddSprite(m_HudQuadContainerIndex, 0.f, 0.f, 12.f, 12.f); - - // Quads for displaying ninja bar - m_NinjaBarFullLeftOffset = RenderTools()->QuadContainerAddSprite(m_HudQuadContainerIndex, 0.f, 0.f, 6.f, 12.f); - m_NinjaBarEmptyRightOffset = RenderTools()->QuadContainerAddSprite(m_HudQuadContainerIndex, 0.f, 0.f, 6.f, 12.f); } void CHud::RenderPlayerState(const int ClientID) @@ -970,7 +966,7 @@ void CHud::RenderPlayerState(const int ClientID) { x = 5; y += 12; - RenderNinjaBar(x, y, NinjaProgress); + RenderProgressBar(x, y, 90.0f, 12.0f, NinjaProgress); } } @@ -1107,13 +1103,13 @@ void CHud::RenderPlayerState(const int ClientID) } } -void CHud::RenderNinjaBar(float x, float y, float Progress) +void CHud::RenderProgressBar(float x, const float y, const float width, const float height, float Progress, const float Alpha) { Progress = clamp(Progress, 0.0f, 1.0f); // half of the ends are also used for the progress display const float EndWidth = 6.0f; - const float BarHeight = 12.0f; - const float WholeBarWidth = 90.f; + const float BarHeight = height; + const float WholeBarWidth = width; const float MiddleBarWidth = WholeBarWidth - (EndWidth * 2.0f); const float EndProgressWidth = EndWidth / 2.0f; const float ProgressBarWidth = WholeBarWidth - (EndProgressWidth * 2.0f); @@ -1121,7 +1117,7 @@ void CHud::RenderNinjaBar(float x, float y, float Progress) const float MiddleProgressProportion = MiddleBarWidth / ProgressBarWidth; // we cut 10% of both sides (right and left) of all sprites so we don't get edge bleeding - + // beginning piece float BeginningPieceProgress = 1; if(Progress <= EndProgressProportion) @@ -1132,6 +1128,7 @@ void CHud::RenderNinjaBar(float x, float y, float Progress) // full Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFullLeft); Graphics()->QuadsBegin(); + Graphics()->SetColor(1.f, 1.f, 1.f, Alpha); Graphics()->QuadsSetSubset(0.1f, 0, 0.1f + 0.8f * BeginningPiecePercentVisible, 1); IGraphics::CQuadItem QuadFullBeginning(x, y, EndWidth * BeginningPiecePercentVisible, BarHeight); Graphics()->QuadsDrawTL(&QuadFullBeginning, 1); @@ -1141,6 +1138,7 @@ void CHud::RenderNinjaBar(float x, float y, float Progress) // empty Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmptyRight); Graphics()->QuadsBegin(); + Graphics()->SetColor(1.f, 1.f, 1.f, Alpha); Graphics()->QuadsSetSubset(0.1f, 1, 0.1f + 0.8f * (1.0f - BeginningPiecePercentVisible), 0); Graphics()->QuadsSetRotation(pi); IGraphics::CQuadItem QuadEmptyBeginning(x + (EndWidth * BeginningPiecePercentVisible), y, EndWidth * (1.0f - BeginningPiecePercentVisible), BarHeight); @@ -1172,6 +1170,7 @@ void CHud::RenderNinjaBar(float x, float y, float Progress) // full ninja bar 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(MiddlePieceProgress * MiddleBarWidth <= EndWidth * 0.8f) { @@ -1189,6 +1188,7 @@ void CHud::RenderNinjaBar(float x, float y, float Progress) // empty ninja bar 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((1.0f - MiddlePieceProgress) * MiddleBarWidth <= EndWidth * 0.8f) { @@ -1224,6 +1224,7 @@ void CHud::RenderNinjaBar(float x, float y, float Progress) { Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarFullLeft); Graphics()->QuadsBegin(); + Graphics()->SetColor(1.f, 1.f, 1.f, Alpha); Graphics()->QuadsSetSubset(0.5f + 0.4f * (1.0f - EndingPieceProgress), 1, 0.90f, 0); Graphics()->QuadsSetRotation(pi); IGraphics::CQuadItem QuadFullEnding(x, y, (EndWidth / 2) * EndingPieceProgress, BarHeight); @@ -1234,11 +1235,13 @@ void CHud::RenderNinjaBar(float x, float y, float Progress) // empty Graphics()->TextureSet(m_pClient->m_HudSkin.m_SpriteHudFreezeBarEmptyRight); Graphics()->QuadsBegin(); + Graphics()->SetColor(1.f, 1.f, 1.f, Alpha); Graphics()->QuadsSetSubset(0.5f - 0.4f * (1.0f - EndingPieceProgress), 0, 0.9f, 1); IGraphics::CQuadItem QuadEmptyEnding(x + ((EndWidth / 2) * EndingPieceProgress), y, (EndWidth / 2) * (1.0f - EndingPieceProgress) + (EndWidth / 2), BarHeight); Graphics()->QuadsDrawTL(&QuadEmptyEnding, 1); Graphics()->QuadsEnd(); Graphics()->QuadsSetSubset(0, 0, 1, 1); + Graphics()->SetColor(1.f, 1.f, 1.f, 1.f); } void CHud::RenderSpectatorHud() diff --git a/src/game/client/components/hud.h b/src/game/client/components/hud.h index d89968a3b..f5ef2fd81 100644 --- a/src/game/client/components/hud.h +++ b/src/game/client/components/hud.h @@ -54,7 +54,6 @@ class CHud : public CComponent void PreparePlayerStateQuads(); void RenderPlayerState(const int ClientID); - void RenderNinjaBar(float x, float y, float Progress); void RenderGameTimer(); void RenderPauseNotification(); @@ -79,6 +78,7 @@ public: // DDRace virtual void OnMessage(int MsgType, void *pRawMsg) override; + void RenderProgressBar(float x, const float y, const float width, const float height, float Progress, float Alpha = 1.0f); private: void RenderRecord(); @@ -125,8 +125,6 @@ private: int m_LiveFrozenOffset; int m_DummyHammerOffset; int m_DummyCopyOffset; - int m_NinjaBarFullLeftOffset; - int m_NinjaBarEmptyRightOffset; }; #endif diff --git a/src/game/client/components/players.h b/src/game/client/components/players.h index e8ce8491f..20e4fb7fc 100644 --- a/src/game/client/components/players.h +++ b/src/game/client/components/players.h @@ -37,10 +37,11 @@ class CPlayers : public CComponent int m_WeaponSpriteMuzzleQuadContainerIndex[NUM_WEAPONS]; public: - vec2 m_CurPredictedPos[MAX_CLIENTS]; virtual int Sizeof() const override { return sizeof(*this); } virtual void OnInit() override; virtual void OnRender() override; + + bool IsPlayerInfoAvailable(int ClientID) const; }; #endif diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 3a5046295..792fa789b 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -45,6 +45,7 @@ #include "components/effects.h" #include "components/emoticon.h" #include "components/flow.h" +#include "components/freezebars.h" #include "components/hud.h" #include "components/items.h" #include "components/killmessages.h" @@ -125,6 +126,7 @@ void CGameClient::OnConsoleInit() m_All.Add(&m_Particles.m_RenderExplosions); m_All.Add(&m_NamePlates); m_All.Add(&m_Particles.m_RenderGeneral); + m_All.Add(&m_FreezeBars); m_All.Add(&m_DamageInd); m_All.Add(&m_Hud); m_All.Add(&m_Spectator); diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 3e9f332f5..9d82a93db 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -34,6 +34,7 @@ #include "components/effects.h" #include "components/emoticon.h" #include "components/flow.h" +#include "components/freezebars.h" #include "components/ghost.h" #include "components/hud.h" #include "components/items.h" @@ -131,6 +132,7 @@ public: CPlayers m_Players; CNamePlates m_NamePlates; + CFreezeBars m_FreezeBars; CItems m_Items; CMapImages m_MapImages; diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index fb2fd8034..67683ec5a 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -982,10 +982,10 @@ bool CCharacter::Freeze(int Seconds) return false; if((Seconds <= 0 || m_Super || m_FreezeTime == -1 || m_FreezeTime > Seconds * GameWorld()->GameTickSpeed()) && Seconds != -1) return false; - if(m_FreezeTick < GameWorld()->GameTick() - GameWorld()->GameTickSpeed() || Seconds == -1) + if(m_Core.m_FreezeTick < GameWorld()->GameTick() - GameWorld()->GameTickSpeed() || Seconds == -1) { m_FreezeTime = Seconds == -1 ? Seconds : Seconds * GameWorld()->GameTickSpeed(); - m_FreezeTick = GameWorld()->GameTick(); + m_Core.m_FreezeTick = GameWorld()->GameTick(); return true; } return false; @@ -1003,7 +1003,7 @@ bool CCharacter::UnFreeze() if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Got) m_Core.m_ActiveWeapon = WEAPON_GUN; m_FreezeTime = 0; - m_FreezeTick = 0; + m_Core.m_FreezeTick = 0; m_FrozenLastTick = true; return true; } @@ -1100,7 +1100,7 @@ void CCharacter::ResetPrediction() m_Core.m_Collision = true; m_NumInputs = 0; m_FreezeTime = 0; - m_FreezeTick = 0; + m_Core.m_FreezeTick = 0; m_DeepFreeze = false; m_LiveFreeze = false; m_FrozenLastTick = false; @@ -1241,7 +1241,7 @@ void CCharacter::Read(CNetObj_Character *pChar, CNetObj_DDNetCharacter *pExtende // detect unfreeze (in case the player was frozen in the tile prediction and not correctly unfrozen) if(pChar->m_Emote != EMOTE_PAIN && pChar->m_Emote != EMOTE_NORMAL) m_DeepFreeze = false; - if(pChar->m_Weapon != WEAPON_NINJA || pChar->m_AttackTick > m_FreezeTick || absolute(pChar->m_VelX) == 256 * 10 || !GameWorld()->m_WorldConfig.m_PredictFreeze) + if(pChar->m_Weapon != WEAPON_NINJA || pChar->m_AttackTick > m_Core.m_FreezeTick || absolute(pChar->m_VelX) == 256 * 10 || !GameWorld()->m_WorldConfig.m_PredictFreeze) { m_DeepFreeze = false; UnFreeze(); diff --git a/src/game/client/prediction/entities/character.h b/src/game/client/prediction/entities/character.h index 700034005..89bf2f8e0 100644 --- a/src/game/client/prediction/entities/character.h +++ b/src/game/client/prediction/entities/character.h @@ -79,7 +79,6 @@ public: bool m_Jetpack; bool m_NinjaJetpack; int m_FreezeTime; - int m_FreezeTick; bool m_FrozenLastTick; bool m_DeepFreeze; bool m_LiveFreeze; diff --git a/src/game/gamecore.h b/src/game/gamecore.h index 8ac870aed..4fd0d5f15 100644 --- a/src/game/gamecore.h +++ b/src/game/gamecore.h @@ -290,6 +290,7 @@ public: bool m_HasTelegunGun; bool m_HasTelegunGrenade; bool m_HasTelegunLaser; + int m_FreezeTick; int m_FreezeEnd; bool m_DeepFrozen; bool m_LiveFrozen; diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index d49c936b7..fdc9b1847 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1145,7 +1145,7 @@ void CCharacter::SnapCharacter(int SnappingClient, int ID) pCharacter->m_AmmoCount = AmmoCount; if(m_FreezeTime > 0 || m_FreezeTime == -1 || m_DeepFreeze) - pCharacter->m_AmmoCount = m_FreezeTick + g_Config.m_SvFreezeDelay * Server()->TickSpeed(); + pCharacter->m_AmmoCount = m_Core.m_FreezeTick + g_Config.m_SvFreezeDelay * Server()->TickSpeed(); else if(Weapon == WEAPON_NINJA) pCharacter->m_AmmoCount = m_Core.m_Ninja.m_ActivationTick + g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000; @@ -2181,11 +2181,11 @@ bool CCharacter::Freeze(int Seconds) { if((Seconds <= 0 || m_Super || m_FreezeTime == -1 || m_FreezeTime > Seconds * Server()->TickSpeed()) && Seconds != -1) return false; - if(m_FreezeTick < Server()->Tick() - Server()->TickSpeed() || Seconds == -1) + if(m_Core.m_FreezeTick < Server()->Tick() - Server()->TickSpeed() || Seconds == -1) { m_Armor = 0; m_FreezeTime = Seconds == -1 ? Seconds : Seconds * Server()->TickSpeed(); - m_FreezeTick = Server()->Tick(); + m_Core.m_FreezeTick = Server()->Tick(); return true; } return false; @@ -2204,7 +2204,7 @@ bool CCharacter::UnFreeze() if(!m_Core.m_aWeapons[m_Core.m_ActiveWeapon].m_Got) m_Core.m_ActiveWeapon = WEAPON_GUN; m_FreezeTime = 0; - m_FreezeTick = 0; + m_Core.m_FreezeTick = 0; m_FrozenLastTick = true; return true; } diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 8d9f15f4c..a531eb6d3 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -183,7 +183,6 @@ public: bool m_NinjaJetpack; int m_TeamBeforeSuper; int m_FreezeTime; - int m_FreezeTick; bool m_FrozenLastTick; bool m_DeepFreeze; bool m_LiveFreeze; diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index 64edc34b0..f5fe7ec4c 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -39,7 +39,7 @@ void CSaveTee::Save(CCharacter *pChr) m_Jetpack = pChr->m_Jetpack; m_NinjaJetpack = pChr->m_NinjaJetpack; m_FreezeTime = pChr->m_FreezeTime; - m_FreezeTick = pChr->Server()->Tick() - pChr->m_FreezeTick; + m_FreezeTick = pChr->Server()->Tick() - pChr->m_Core.m_FreezeTick; m_DeepFreeze = pChr->m_DeepFreeze; m_LiveFreeze = pChr->m_LiveFreeze; @@ -134,7 +134,7 @@ void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap) pChr->m_Jetpack = m_Jetpack; pChr->m_NinjaJetpack = m_NinjaJetpack; pChr->m_FreezeTime = m_FreezeTime; - pChr->m_FreezeTick = pChr->Server()->Tick() - m_FreezeTick; + pChr->m_Core.m_FreezeTick = pChr->Server()->Tick() - m_FreezeTick; pChr->m_DeepFreeze = m_DeepFreeze; pChr->m_LiveFreeze = m_LiveFreeze;