diff --git a/datasrc/network.py b/datasrc/network.py index 862dedf6e..09d5997e0 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -245,6 +245,13 @@ Objects = [ NetIntRange("m_StrongWeakID", 0, 'MAX_CLIENTS-1'), ]), + NetObjectEx("DDNetCharacterDisplayInfo", "character-display-info@netobj.ddnet.tw", [ + NetIntRange("m_JumpedTotal", -2, 255), + NetTick("m_NinjaActivationTick"), + NetTick("m_FreezeTick"), + NetBool("m_IsInFreeze"), + ]), + NetObjectEx("DDNetPlayer", "player@netobj.ddnet.tw", [ NetIntAny("m_Flags"), NetIntRange("m_AuthLevel", "AUTHED_NO", "AUTHED_ADMIN"), diff --git a/src/game/client/components/freezebars.cpp b/src/game/client/components/freezebars.cpp index f9386c5f6..bd91c992e 100644 --- a/src/game/client/components/freezebars.cpp +++ b/src/game/client/components/freezebars.cpp @@ -2,29 +2,34 @@ #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) + // 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; + + const float PhysSize = 28.0f; + bool Grounded = false; + if(Collision()->CheckPoint(pCharacter->m_Pos.x + PhysSize / 2, pCharacter->m_Pos.y + PhysSize / 2 + 5)) + Grounded = true; + if(Collision()->CheckPoint(pCharacter->m_Pos.x - PhysSize / 2, pCharacter->m_Pos.y + PhysSize / 2 + 5)) + Grounded = true; + + if(pCharacter->m_FreezeEnd <= 0.0f || !m_pClient->m_Snap.m_aCharacters[ClientID].m_HasExtendedDisplayInfo || (pCharacter->m_IsInFreeze && Grounded)) { 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; + 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; 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)); + vec2 Position = m_pClient->m_aClients[ClientID].m_RenderPos; Position.x -= FreezeBarHalfWidth; Position.y += 32; @@ -33,6 +38,13 @@ void CFreezeBars::RenderFreezeBar(const int ClientID) m_pClient->m_Hud.RenderProgressBar(Position.x, Position.y, FreezeBarWidth, FreezeBarHight, FreezeProgress, Alpha); } +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; +} + void CFreezeBars::OnRender() { // get screen edges to avoid rendering offscreen @@ -53,7 +65,7 @@ void CFreezeBars::OnRender() // 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)) + if(ClientID == LocalClientID || !m_pClient->m_Snap.m_aCharacters[ClientID].m_Active || !IsPlayerInfoAvailable(ClientID)) { continue; } @@ -66,10 +78,8 @@ void CFreezeBars::OnRender() } RenderFreezeBar(ClientID); - - } - if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && m_pClient->m_Players.IsPlayerInfoAvailable(LocalClientID)) + if(LocalClientID != -1 && m_pClient->m_Snap.m_aCharacters[LocalClientID].m_Active && IsPlayerInfoAvailable(LocalClientID)) { RenderFreezeBar(LocalClientID); } diff --git a/src/game/client/components/freezebars.h b/src/game/client/components/freezebars.h index 3f8e0ce1f..c2ea51f75 100644 --- a/src/game/client/components/freezebars.h +++ b/src/game/client/components/freezebars.h @@ -5,6 +5,7 @@ class CFreezeBars : public CComponent { void RenderFreezeBar(const int ClientID); + bool IsPlayerInfoAvailable(int ClientID) const; public: virtual int Sizeof() const override { return sizeof(*this); } diff --git a/src/game/client/components/hud.cpp b/src/game/client/components/hud.cpp index 774427d27..d6462d3ea 100644 --- a/src/game/client/components/hud.cpp +++ b/src/game/client/components/hud.cpp @@ -830,14 +830,14 @@ void CHud::RenderPlayerState(const int ClientID) { Graphics()->SetColor(1.f, 1.f, 1.f, 1.f); - // pCharacter contains the predicted character for local players and the last snap for spectated players. + // 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; int TotalJumpsToDisplay, AvailableJumpsToDisplay; // If the player is predicted in the Game World, we take the predicted jump values, otherwise the values from a snap - if(m_pClient->m_Snap.m_pLocalCharacter) + if(m_pClient->m_Snap.m_aCharacters[ClientID].m_HasExtendedDisplayInfo) { - float PhysSize = 28.0f; + const float PhysSize = 28.0f; bool Grounded = false; if(Collision()->CheckPoint(pCharacter->m_Pos.x + PhysSize / 2, pCharacter->m_Pos.y + PhysSize / 2 + 5)) Grounded = true; @@ -960,9 +960,9 @@ void CHud::RenderPlayerState(const int ClientID) Graphics()->QuadsSetRotation(0); Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); - const int Max = g_pData->m_Weapons.m_Ninja.m_Duration * m_pClient->m_GameWorld.GameTickSpeed() / 1000; - float NinjaProgress = clamp(pCharacter->m_Ninja.m_ActivationTick + g_pData->m_Weapons.m_Ninja.m_Duration * m_pClient->m_GameWorld.GameTickSpeed() / 1000 - m_pClient->m_GameWorld.GameTick(), 0, Max) / (float)Max; - if(NinjaProgress > 0.0f) + const int Max = g_pData->m_Weapons.m_Ninja.m_Duration * Client()->GameTickSpeed() / 1000; + float NinjaProgress = clamp(pCharacter->m_Ninja.m_ActivationTick + g_pData->m_Weapons.m_Ninja.m_Duration * Client()->GameTickSpeed() / 1000 - Client()->GameTick(g_Config.m_ClDummy), 0, Max) / (float)Max; + if(NinjaProgress > 0.0f && m_pClient->m_Snap.m_aCharacters[ClientID].m_HasExtendedDisplayInfo) { x = 5; y += 12; @@ -1117,7 +1117,7 @@ void CHud::RenderProgressBar(float x, const float y, const float width, const fl 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) diff --git a/src/game/client/components/players.h b/src/game/client/components/players.h index 20e4fb7fc..f23cc64ec 100644 --- a/src/game/client/components/players.h +++ b/src/game/client/components/players.h @@ -40,8 +40,6 @@ public: 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 792fa789b..42396baa0 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1346,6 +1346,19 @@ void CGameClient::OnNewSnapshot() pClient->m_Predicted.ReadDDNet(pCharacterData); } } + else if(Item.m_Type == NETOBJTYPE_DDNETCHARACTERDISPLAYINFO) + { + const CNetObj_DDNetCharacterDisplayInfo *pCharacterDisplayInfo = (const CNetObj_DDNetCharacterDisplayInfo *)pData; + + if(Item.m_ID < MAX_CLIENTS) + { + m_Snap.m_aCharacters[Item.m_ID].m_ExtendedDisplayInfo = *pCharacterDisplayInfo; + m_Snap.m_aCharacters[Item.m_ID].m_HasExtendedDisplayInfo = true; + + CClientData *pClient = &m_aClients[Item.m_ID]; + pClient->m_Predicted.ReadDDNetDisplayInfo(pCharacterDisplayInfo); + } + } else if(Item.m_Type == NETOBJTYPE_SPECCHAR) { const CNetObj_SpecChar *pSpecCharData = (const CNetObj_SpecChar *)pData; diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 9d82a93db..b8df8340d 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -318,6 +318,9 @@ public: CNetObj_DDNetCharacter m_ExtendedData; bool m_HasExtendedData; + CNetObj_DDNetCharacterDisplayInfo m_ExtendedDisplayInfo; + bool m_HasExtendedDisplayInfo; + // interpolated position vec2 m_Position; }; diff --git a/src/game/client/prediction/entities/character.cpp b/src/game/client/prediction/entities/character.cpp index 67683ec5a..6a705ec6e 100644 --- a/src/game/client/prediction/entities/character.cpp +++ b/src/game/client/prediction/entities/character.cpp @@ -697,7 +697,13 @@ void CCharacter::HandleTiles(int Index) if(Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER) { if(Collision()->GetSwitchNumber(MapIndex) == 0 || Collision()->m_pSwitchers[Collision()->GetSwitchNumber(MapIndex)].m_Status[Team()]) + { Freeze(Collision()->GetSwitchDelay(MapIndex)); + if(IsGrounded()) + { + m_Core.m_IsInFreeze = true; + } + } } else if(Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER) { @@ -781,6 +787,10 @@ void CCharacter::HandleTiles(int Index) if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Super && !m_DeepFreeze) { Freeze(); + if(IsGrounded()) + { + m_Core.m_IsInFreeze = true; + } } else if(((m_TileIndex == TILE_UNFREEZE) || (m_TileFIndex == TILE_UNFREEZE)) && !m_DeepFreeze) { @@ -946,6 +956,7 @@ void CCharacter::DDRacePostCoreTick() m_Core.m_HookTick = 0; m_FrozenLastTick = false; + m_Core.m_IsInFreeze = false; if(m_DeepFreeze && !m_Super) Freeze(); @@ -1101,6 +1112,7 @@ void CCharacter::ResetPrediction() m_NumInputs = 0; m_FreezeTime = 0; m_Core.m_FreezeTick = 0; + m_Core.m_IsInFreeze = false; m_DeepFreeze = false; m_LiveFreeze = false; m_FrozenLastTick = false; diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index 077a89f16..2ae1092d2 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -586,6 +586,14 @@ void CCharacterCore::ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet) m_Jumps = pObjDDNet->m_Jumps; } +void CCharacterCore::ReadDDNetDisplayInfo(const CNetObj_DDNetCharacterDisplayInfo *pObjDDNet) +{ + m_JumpedTotal = pObjDDNet->m_JumpedTotal; + m_Ninja.m_ActivationTick = pObjDDNet->m_NinjaActivationTick; + m_FreezeTick = pObjDDNet->m_FreezeTick; + m_IsInFreeze = pObjDDNet->m_IsInFreeze; +} + void CCharacterCore::Quantize() { CNetObj_CharacterCore Core; diff --git a/src/game/gamecore.h b/src/game/gamecore.h index 4fd0d5f15..c174e53a0 100644 --- a/src/game/gamecore.h +++ b/src/game/gamecore.h @@ -276,6 +276,7 @@ public: void SetTeamsCore(CTeamsCore *pTeams); void SetTeleOuts(std::map> *pTeleOuts); void ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet); + void ReadDDNetDisplayInfo(const CNetObj_DDNetCharacterDisplayInfo *pObjDDNet); bool m_Solo; bool m_Jetpack; bool m_NoCollision; @@ -292,6 +293,7 @@ public: bool m_HasTelegunLaser; int m_FreezeTick; int m_FreezeEnd; + bool m_IsInFreeze; bool m_DeepFrozen; bool m_LiveFrozen; CTuningParams m_Tuning; diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index fdc9b1847..309a793d9 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1242,6 +1242,14 @@ void CCharacter::Snap(int SnappingClient) pDDNetCharacter->m_Jumps = m_Core.m_Jumps; pDDNetCharacter->m_TeleCheckpoint = m_TeleCheckpoint; pDDNetCharacter->m_StrongWeakID = m_StrongWeakID; + + CNetObj_DDNetCharacterDisplayInfo *pDDNetCharacterDisplayInfo = static_cast(Server()->SnapNewItem(NETOBJTYPE_DDNETCHARACTERDISPLAYINFO, ID, sizeof(CNetObj_DDNetCharacterDisplayInfo))); + if(!pDDNetCharacterDisplayInfo) + return; + pDDNetCharacterDisplayInfo->m_JumpedTotal = m_Core.m_JumpedTotal; + pDDNetCharacterDisplayInfo->m_NinjaActivationTick = m_Core.m_Ninja.m_ActivationTick; + pDDNetCharacterDisplayInfo->m_FreezeTick = m_Core.m_FreezeTick; + pDDNetCharacterDisplayInfo->m_IsInFreeze = m_Core.m_IsInFreeze; } // DDRace @@ -1476,7 +1484,13 @@ void CCharacter::HandleTiles(int Index) // freeze if(((m_TileIndex == TILE_FREEZE) || (m_TileFIndex == TILE_FREEZE)) && !m_Super && !m_DeepFreeze) + { Freeze(); + if(IsGrounded()) + { + m_Core.m_IsInFreeze = true; + } + } else if(((m_TileIndex == TILE_UNFREEZE) || (m_TileFIndex == TILE_UNFREEZE)) && !m_DeepFreeze) UnFreeze(); @@ -1699,7 +1713,13 @@ void CCharacter::HandleTiles(int Index) else if(GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_FREEZE && Team() != TEAM_SUPER) { if(GameServer()->Collision()->GetSwitchNumber(MapIndex) == 0 || GameServer()->Collision()->m_pSwitchers[GameServer()->Collision()->GetSwitchNumber(MapIndex)].m_Status[Team()]) + { Freeze(GameServer()->Collision()->GetSwitchDelay(MapIndex)); + if(IsGrounded()) + { + m_Core.m_IsInFreeze = true; + } + } } else if(GameServer()->Collision()->GetSwitchType(MapIndex) == TILE_DFREEZE && Team() != TEAM_SUPER) { @@ -2095,16 +2115,16 @@ void CCharacter::DDRaceTick() GameServer()->Collision()->GetSwitchType(Index)}; if(IsGrounded() && !m_DeepFreeze) { - bool IsInFreeze = false; + bool IsInAnyFreeze = false; for(const int Tile : aTiles) { if(Tile == TILE_FREEZE || Tile == TILE_DFREEZE || Tile == TILE_LFREEZE) { - IsInFreeze = true; + IsInAnyFreeze = true; break; } } - if(!IsInFreeze) + if(!IsInAnyFreeze) { SetRescue(); } @@ -2122,6 +2142,7 @@ void CCharacter::DDRacePostCoreTick() m_Core.m_HookTick = 0; m_FrozenLastTick = false; + m_Core.m_IsInFreeze = false; if(m_DeepFreeze && !m_Super) Freeze();