diff --git a/datasrc/network.py b/datasrc/network.py index 66e064597..6da825613 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -513,4 +513,9 @@ Messages = [ NetIntAny("m_ServerTimeBest"), NetIntAny("m_PlayerTimeBest"), ]), + + NetMessageEx("Sv_KillMsgTeam", "killmsgteam@netmsg.ddnet.tw", [ + NetIntRange("m_Team", 0, 'MAX_CLIENTS-1'), + NetIntRange("m_First", -1, 'MAX_CLIENTS-1'), + ]), ] diff --git a/src/game/client/components/killmessages.cpp b/src/game/client/components/killmessages.cpp index 317725c77..2158f468b 100644 --- a/src/game/client/components/killmessages.cpp +++ b/src/game/client/components/killmessages.cpp @@ -6,10 +6,13 @@ #include #include #include +#include #include "killmessages.h" #include #include +#include +#include void CKillMessages::OnWindowResize() { @@ -23,6 +26,7 @@ void CKillMessages::OnWindowResize() void CKillMessages::OnReset() { m_KillmsgCurrent = 0; + for(auto &Killmsg : m_aKillmsgs) { Killmsg.m_Tick = -100000; @@ -103,6 +107,83 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) if(m_pClient->m_SuppressEvents) return; + if(MsgType == NETMSGTYPE_SV_KILLMSGTEAM) + { + CNetMsg_Sv_KillMsgTeam *pMsg = (CNetMsg_Sv_KillMsgTeam *)pRawMsg; + + // unpack messages + CKillMsg Kill; + Kill.m_aVictimName[0] = '\0'; + Kill.m_aKillerName[0] = '\0'; + + std::vector> vStrongWeakSorted; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_pClient->m_Teams.Team(i) == pMsg->m_Team) + { + CCharacter *pChr = m_pClient->m_GameWorld.GetCharacterByID(i); + vStrongWeakSorted.emplace_back(i, pMsg->m_First == i ? MAX_CLIENTS : pChr ? pChr->GetStrongWeakID() : 0); + } + } + + std::stable_sort(vStrongWeakSorted.begin(), vStrongWeakSorted.end(), [](auto &Left, auto &Right) { return Left.second > Right.second; }); + + Kill.m_TeamSize = vStrongWeakSorted.size(); + if(Kill.m_TeamSize > MAX_KILLMSG_TEAM_MEMBERS) + Kill.m_TeamSize = MAX_KILLMSG_TEAM_MEMBERS; + + Kill.m_VictimID = vStrongWeakSorted[0].first; + if(Kill.m_VictimID >= 0 && Kill.m_VictimID < MAX_CLIENTS) + { + Kill.m_VictimTeam = m_pClient->m_aClients[Kill.m_VictimID].m_Team; + Kill.m_VictimDDTeam = pMsg->m_Team; + Kill.m_VictimRenderInfo[0] = m_pClient->m_aClients[Kill.m_VictimID].m_RenderInfo; + + for(int i = 1; i < Kill.m_TeamSize; i++) + Kill.m_VictimRenderInfo[i] = m_pClient->m_aClients[vStrongWeakSorted[i].first].m_RenderInfo; + + str_format(Kill.m_aVictimName, sizeof(Kill.m_aVictimName), Localize("Team %d"), Kill.m_VictimDDTeam); + } + + Kill.m_KillerID = Kill.m_VictimID; + + Kill.m_Weapon = -1; + Kill.m_ModeSpecial = 0; + Kill.m_Tick = Client()->GameTick(g_Config.m_ClDummy); + + Kill.m_VitctimTextWidth = Kill.m_KillerTextWidth = 0.f; + + float Width = 400 * 3.0f * Graphics()->ScreenAspect(); + float Height = 400 * 3.0f; + + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + Graphics()->MapScreen(0, 0, Width * 1.5f, Height * 1.5f); + + CreateKillmessageNamesIfNotCreated(Kill); + + int VictimSkinsValid = 0; + for(int i = 0; i < Kill.m_TeamSize; i++) + { + if((Kill.m_VictimRenderInfo[i].m_CustomColoredSkin && Kill.m_VictimRenderInfo[i].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_VictimRenderInfo[i].m_CustomColoredSkin && Kill.m_VictimRenderInfo[i].m_OriginalRenderSkin.m_Body.IsValid())) + VictimSkinsValid++; + } + bool KillMsgValid = VictimSkinsValid == Kill.m_TeamSize; + + if(KillMsgValid) + { + // add the message + m_KillmsgCurrent = (m_KillmsgCurrent + 1) % MAX_KILLMSGS; + + TextRender()->DeleteTextContainer(m_aKillmsgs[m_KillmsgCurrent].m_VictimTextContainerIndex); + TextRender()->DeleteTextContainer(m_aKillmsgs[m_KillmsgCurrent].m_KillerTextContainerIndex); + + m_aKillmsgs[m_KillmsgCurrent] = Kill; + } + + Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); + } + if(MsgType == NETMSGTYPE_SV_KILLMSG) { CNetMsg_Sv_KillMsg *pMsg = (CNetMsg_Sv_KillMsg *)pRawMsg; @@ -112,13 +193,15 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) Kill.m_aVictimName[0] = '\0'; Kill.m_aKillerName[0] = '\0'; + Kill.m_TeamSize = 1; + Kill.m_VictimID = pMsg->m_Victim; if(Kill.m_VictimID >= 0 && Kill.m_VictimID < MAX_CLIENTS) { Kill.m_VictimTeam = m_pClient->m_aClients[Kill.m_VictimID].m_Team; Kill.m_VictimDDTeam = m_pClient->m_Teams.Team(Kill.m_VictimID); str_copy(Kill.m_aVictimName, m_pClient->m_aClients[Kill.m_VictimID].m_aName); - Kill.m_VictimRenderInfo = m_pClient->m_aClients[Kill.m_VictimID].m_RenderInfo; + Kill.m_VictimRenderInfo[0] = m_pClient->m_aClients[Kill.m_VictimID].m_RenderInfo; } Kill.m_KillerID = pMsg->m_Killer; @@ -146,7 +229,7 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg) CreateKillmessageNamesIfNotCreated(Kill); - bool KillMsgValid = (Kill.m_VictimRenderInfo.m_CustomColoredSkin && Kill.m_VictimRenderInfo.m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_VictimRenderInfo.m_CustomColoredSkin && Kill.m_VictimRenderInfo.m_OriginalRenderSkin.m_Body.IsValid()); + bool KillMsgValid = (Kill.m_VictimRenderInfo[0].m_CustomColoredSkin && Kill.m_VictimRenderInfo[0].m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_VictimRenderInfo[0].m_CustomColoredSkin && Kill.m_VictimRenderInfo[0].m_OriginalRenderSkin.m_Body.IsValid()); // if killer != victim, killer must be valid too KillMsgValid &= Kill.m_KillerID == Kill.m_VictimID || ((Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_ColorableRenderSkin.m_Body.IsValid()) || (!Kill.m_KillerRenderInfo.m_CustomColoredSkin && Kill.m_KillerRenderInfo.m_OriginalRenderSkin.m_Body.IsValid())); if(KillMsgValid) @@ -224,20 +307,23 @@ void CKillMessages::OnRender() if(m_aKillmsgs[r].m_VictimID >= 0) { - CTeeRenderInfo TeeInfo = m_aKillmsgs[r].m_VictimRenderInfo; + for(int j = (m_aKillmsgs[r].m_TeamSize - 1); j >= 0; j--) + { + CTeeRenderInfo TeeInfo = m_aKillmsgs[r].m_VictimRenderInfo[j]; - CAnimState *pIdleState = CAnimState::GetIdle(); - vec2 OffsetToMid; - RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); - vec2 TeeRenderPos(x, y + 46.0f / 2.0f + OffsetToMid.y); + CAnimState *pIdleState = CAnimState::GetIdle(); + vec2 OffsetToMid; + RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid); + vec2 TeeRenderPos(x, y + 46.0f / 2.0f + OffsetToMid.y); - RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_PAIN, vec2(-1, 0), TeeRenderPos); + RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_PAIN, vec2(-1, 0), TeeRenderPos); + + x -= 44.0f; + } } - x -= 32.0f; - // render weapon - x -= 44.0f; + x -= 32.0f; if(m_aKillmsgs[r].m_Weapon >= 0) { Graphics()->TextureSet(GameClient()->m_GameSkin.m_aSpriteWeapons[m_aKillmsgs[r].m_Weapon]); @@ -313,7 +399,7 @@ void CKillMessages::RefindSkins() { const CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_VictimID]; if(Client.m_aSkinName[0] != '\0') - m_aKillmsgs[r].m_VictimRenderInfo = Client.m_RenderInfo; + m_aKillmsgs[r].m_VictimRenderInfo[0] = Client.m_RenderInfo; else m_aKillmsgs[r].m_VictimID = -1; } diff --git a/src/game/client/components/killmessages.h b/src/game/client/components/killmessages.h index 73668b1e4..435a08d25 100644 --- a/src/game/client/components/killmessages.h +++ b/src/game/client/components/killmessages.h @@ -5,10 +5,14 @@ #include #include - class CKillMessages : public CComponent { int m_SpriteQuadContainerIndex; + enum + { + MAX_KILLMSGS = 5, + MAX_KILLMSG_TEAM_MEMBERS = 4, + }; public: // kill messages @@ -27,8 +31,7 @@ public: char m_aVictimName[64]; int m_VictimTextContainerIndex; float m_VitctimTextWidth; - CTeeRenderInfo m_VictimRenderInfo; - + CTeeRenderInfo m_VictimRenderInfo[MAX_KILLMSG_TEAM_MEMBERS]; int m_KillerID; int m_KillerTeam; char m_aKillerName[64]; @@ -39,11 +42,7 @@ public: int m_ModeSpecial; // for CTF, if the guy is carrying a flag for example int m_Tick; int m_FlagCarrierBlue; - }; - - enum - { - MAX_KILLMSGS = 5, + int m_TeamSize; }; private: diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index cd3d2448d..a397155dd 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -906,7 +906,7 @@ bool CCharacter::IncreaseArmor(int Amount) return true; } -void CCharacter::Die(int Killer, int Weapon) +void CCharacter::Die(int Killer, int Weapon, bool SendKillMsg) { if(Server()->IsRecording(m_pPlayer->GetCID())) Server()->StopRecord(m_pPlayer->GetCID()); @@ -920,12 +920,15 @@ void CCharacter::Die(int Killer, int Weapon) GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); // send the kill message - CNetMsg_Sv_KillMsg Msg; - Msg.m_Killer = Killer; - Msg.m_Victim = m_pPlayer->GetCID(); - Msg.m_Weapon = Weapon; - Msg.m_ModeSpecial = ModeSpecial; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); + if(SendKillMsg && (Team() == TEAM_FLOCK || Teams()->Count(Team()) == 1 || Teams()->GetTeamState(Team()) == CGameTeams::TEAMSTATE_OPEN)) + { + CNetMsg_Sv_KillMsg Msg; + Msg.m_Killer = Killer; + Msg.m_Victim = m_pPlayer->GetCID(); + Msg.m_Weapon = Weapon; + Msg.m_ModeSpecial = ModeSpecial; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); + } // a nice sound GameServer()->CreateSound(m_Pos, SOUND_PLAYER_DIE, TeamMask()); diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 4309e54d1..c0c5bfdea 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -63,7 +63,7 @@ public: void ResetInput(); void FireWeapon(); - void Die(int Killer, int Weapon); + void Die(int Killer, int Weapon, bool SendKillMsg = true); bool TakeDamage(vec2 Force, int Dmg, int From, int Weapon); bool Spawn(class CPlayer *pPlayer, vec2 Pos); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index b48b2d763..2c27bc0a4 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -559,11 +559,11 @@ CCharacter *CPlayer::GetCharacter() return 0; } -void CPlayer::KillCharacter(int Weapon) +void CPlayer::KillCharacter(int Weapon, bool SendKillMsg) { if(m_pCharacter) { - m_pCharacter->Die(m_ClientID, Weapon); + m_pCharacter->Die(m_ClientID, Weapon, SendKillMsg); delete m_pCharacter; m_pCharacter = 0; diff --git a/src/game/server/player.h b/src/game/server/player.h index 61f31fe97..0934a938f 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -59,7 +59,7 @@ public: void OnPredictedEarlyInput(CNetObj_PlayerInput *pNewInput); void OnDisconnect(); - void KillCharacter(int Weapon = WEAPON_GAME); + void KillCharacter(int Weapon = WEAPON_GAME, bool SendKillMsg = true); CCharacter *GetCharacter(); void SpectatePlayerName(const char *pName); diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index 06c1087ce..0cb15f182 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -453,7 +453,7 @@ void CGameTeams::KillTeam(int Team, int NewStrongID, int ExceptID) GameServer()->m_apPlayers[i]->m_VotedForPractice = false; if(i != ExceptID) { - GameServer()->m_apPlayers[i]->KillCharacter(WEAPON_SELF); + GameServer()->m_apPlayers[i]->KillCharacter(WEAPON_SELF, false); if(NewStrongID != -1 && i != NewStrongID) { GameServer()->m_apPlayers[i]->Respawn(true); // spawn the rest of team with weak hook on the killer @@ -461,6 +461,12 @@ void CGameTeams::KillTeam(int Team, int NewStrongID, int ExceptID) } } } + + // send the team kill message + CNetMsg_Sv_KillMsgTeam Msg; + Msg.m_Team = Team; + Msg.m_First = NewStrongID; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); } bool CGameTeams::TeamFinished(int Team)