diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt index 0c87d4ba5..d2c645224 100644 --- a/data/languages/bosnian.txt +++ b/data/languages/bosnian.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Prikaži chat -Show friends +Show friends only == Prikaži prijatelje Show ingame HUD diff --git a/data/languages/bulgarian.txt b/data/languages/bulgarian.txt index 89b95af72..403941dec 100644 --- a/data/languages/bulgarian.txt +++ b/data/languages/bulgarian.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Показвай чата -Show friends +Show friends only == Покажи приятели Show ingame HUD @@ -674,12 +674,3 @@ Strict gametype filter ##### old translations ##### -Page %d of %d -== Страница %d/%d - -Next -== Нататък - -Prev -== Назад - diff --git a/data/languages/czech.txt b/data/languages/czech.txt index 33aca462b..b132f398e 100644 --- a/data/languages/czech.txt +++ b/data/languages/czech.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Zobrazit chat -Show friends +Show friends only == Zobrazit přátele Show ingame HUD diff --git a/data/languages/danish.txt b/data/languages/danish.txt index db69e06c2..c78d848f4 100644 --- a/data/languages/danish.txt +++ b/data/languages/danish.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Vis chat -Show friends +Show friends only == Vis venner Show ingame HUD @@ -674,6 +674,3 @@ Strict gametype filter ##### old translations ##### -##### translated strings ##### -== - diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt index fbd488a1d..bd03032a9 100644 --- a/data/languages/dutch.txt +++ b/data/languages/dutch.txt @@ -511,7 +511,7 @@ Shotgun Show chat == Laat chat zien -Show friends +Show friends only == Laat vrienden zien Show ingame HUD diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt index 6f418c4bd..74f7d4f22 100644 --- a/data/languages/finnish.txt +++ b/data/languages/finnish.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Näytä chatti -Show friends +Show friends only == Näytä ystävät Show ingame HUD diff --git a/data/languages/french.txt b/data/languages/french.txt index 137e36820..629a885c6 100644 --- a/data/languages/french.txt +++ b/data/languages/french.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Montrer le chat -Show friends +Show friends only == Montrer les amis Show ingame HUD diff --git a/data/languages/german.txt b/data/languages/german.txt index c3e74bbf4..38fd30a62 100644 --- a/data/languages/german.txt +++ b/data/languages/german.txt @@ -514,7 +514,7 @@ Shotgun Show chat == Chat anzeigen -Show friends +Show friends only == Nur Freunde zeigen Show ingame HUD diff --git a/data/languages/italian.txt b/data/languages/italian.txt index 6ee030544..8c9fa07ee 100644 --- a/data/languages/italian.txt +++ b/data/languages/italian.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Mostra chat -Show friends +Show friends only == Mostra amici Show ingame HUD diff --git a/data/languages/norwegian.txt b/data/languages/norwegian.txt index a17365d56..7f04e19db 100644 --- a/data/languages/norwegian.txt +++ b/data/languages/norwegian.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Vis samtale -Show friends +Show friends only == Vis venner Show ingame HUD diff --git a/data/languages/polish.txt b/data/languages/polish.txt index c3223a9ce..b8e74a962 100644 --- a/data/languages/polish.txt +++ b/data/languages/polish.txt @@ -612,7 +612,7 @@ Server address: Server filter == -Show friends +Show friends only == Show ingame HUD diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt index 144862475..1d2681adf 100644 --- a/data/languages/portuguese.txt +++ b/data/languages/portuguese.txt @@ -1,4 +1,4 @@ - + ##### translated strings ##### %d Bytes @@ -514,7 +514,7 @@ Shotgun Show chat == Mostrar conversa -Show friends +Show friends only == Mostrar amigos Show ingame HUD @@ -672,9 +672,5 @@ no limit ##### needs translation ##### - ##### old translations ##### - -== ## translated strings ##### - diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt index c7bbb6950..6c6ce05f2 100644 --- a/data/languages/romanian.txt +++ b/data/languages/romanian.txt @@ -1,3 +1,4 @@ + ##### translated strings ##### %d Bytes @@ -141,12 +142,12 @@ Delete Delete demo == Șterge demonstrația -Demofile: %s -== Fișier demo: %s - Demo details == Detalii demo +Demofile: %s +== Fișier demo: %s + Demos == Demo @@ -513,7 +514,7 @@ Shotgun Show chat == Afișare chat -Show friends +Show friends only == Arată prietenii Show ingame HUD diff --git a/data/languages/russian.txt b/data/languages/russian.txt index 87cf31688..52e49eb04 100644 --- a/data/languages/russian.txt +++ b/data/languages/russian.txt @@ -511,7 +511,7 @@ Shotgun Show chat == Показать чат -Show friends +Show friends only == Показывать друзей Show ingame HUD diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt index aaabeabdd..516d50c8f 100644 --- a/data/languages/serbian.txt +++ b/data/languages/serbian.txt @@ -612,7 +612,7 @@ Server address: Server filter == -Show friends +Show friends only == Show ingame HUD diff --git a/data/languages/slovak.txt b/data/languages/slovak.txt index 203940967..37754d857 100644 --- a/data/languages/slovak.txt +++ b/data/languages/slovak.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Ukázať chat -Show friends +Show friends only == Ukázať priateľov Show ingame HUD diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt index d587cf026..df5084432 100644 --- a/data/languages/spanish.txt +++ b/data/languages/spanish.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Mostrar chat -Show friends +Show friends only == Mostrar amigos Show ingame HUD @@ -674,12 +674,3 @@ Strict gametype filter ##### old translations ##### -Page %d of %d -== Página %d de %d - -Next -== Siguiente - -Prev -== Anterior - diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt index 6dea5ed4b..6b1804aaa 100644 --- a/data/languages/swedish.txt +++ b/data/languages/swedish.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Visa chatt -Show friends +Show friends only == Visa kompisar Show ingame HUD diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt index a62e6073f..a3fa958b8 100644 --- a/data/languages/turkish.txt +++ b/data/languages/turkish.txt @@ -508,7 +508,7 @@ Shotgun Show chat == Sohbeti göster -Show friends +Show friends only == Arkadaşları göster Show ingame HUD diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt index 5019714bf..f6e69f003 100644 --- a/data/languages/ukrainian.txt +++ b/data/languages/ukrainian.txt @@ -618,7 +618,7 @@ Server address: Server filter == -Show friends +Show friends only == Show ingame HUD diff --git a/src/engine/client/friends.cpp b/src/engine/client/friends.cpp index 9ef00ed1e..99f82b505 100644 --- a/src/engine/client/friends.cpp +++ b/src/engine/client/friends.cpp @@ -1,6 +1,7 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include #include #include @@ -44,12 +45,35 @@ const CFriendInfo *CFriends::GetFriend(int Index) const return &m_aFriends[max(0, Index%m_NumFriends)]; } -bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const +int CFriends::GetFriendState(const char *pName, const char *pClan) const { + int Result = FRIEND_NO; + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); for(int i = 0; i < m_NumFriends; ++i) { - if(!str_comp(m_aFriends[i].m_aClan, pClan) && - ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || !str_comp(m_aFriends[i].m_aName, pName))) + if(m_aFriends[i].m_ClanHash == ClanHash) + { + if(m_aFriends[i].m_aName[0] == 0) + Result = FRIEND_CLAN; + else if(m_aFriends[i].m_NameHash == NameHash) + { + Result = FRIEND_PLAYER; + break; + } + } + } + return Result; +} + +bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const +{ + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); + for(int i = 0; i < m_NumFriends; ++i) + { + if(m_aFriends[i].m_ClanHash == ClanHash && + ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || m_aFriends[i].m_NameHash == NameHash)) return true; } return false; @@ -61,22 +85,28 @@ void CFriends::AddFriend(const char *pName, const char *pClan) return; // make sure we don't have the friend already + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); for(int i = 0; i < m_NumFriends; ++i) { - if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan)) + if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash) return; } str_copy(m_aFriends[m_NumFriends].m_aName, pName, sizeof(m_aFriends[m_NumFriends].m_aName)); str_copy(m_aFriends[m_NumFriends].m_aClan, pClan, sizeof(m_aFriends[m_NumFriends].m_aClan)); + m_aFriends[m_NumFriends].m_NameHash = NameHash; + m_aFriends[m_NumFriends].m_ClanHash = ClanHash; ++m_NumFriends; } void CFriends::RemoveFriend(const char *pName, const char *pClan) { + unsigned NameHash = str_quickhash(pName); + unsigned ClanHash = str_quickhash(pClan); for(int i = 0; i < m_NumFriends; ++i) { - if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan)) + if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash) { RemoveFriend(i); return; diff --git a/src/engine/client/friends.h b/src/engine/client/friends.h index be0cfa498..d4c539e14 100644 --- a/src/engine/client/friends.h +++ b/src/engine/client/friends.h @@ -22,6 +22,7 @@ public: int NumFriends() const { return m_NumFriends; } const CFriendInfo *GetFriend(int Index) const; + int GetFriendState(const char *pName, const char *pClan) const; bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const; void AddFriend(const char *pName, const char *pClan); diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 424acb22c..c3ada10e8 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -144,101 +144,98 @@ void CServerBrowser::Filter() { int Filtered = 0; - if(g_Config.m_BrFilterFriends) + if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0)) + Filtered = 1; + else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) || + m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients)) + Filtered = 1; + else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD) + Filtered = 1; + else if(g_Config.m_BrFilterPure && + (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 && + str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 && + str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0)) { Filtered = 1; + } + else if(g_Config.m_BrFilterPureMap && + !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 || + str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0) + ) + { + Filtered = 1; + } + else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency) + Filtered = 1; + else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0) + Filtered = 1; + else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress)) + Filtered = 1; + else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) + Filtered = 1; + else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) + Filtered = 1; + else if(g_Config.m_BrFilterString[0] != 0) + { + int MatchFound = 0; + + m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0; + + // match against server name + if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString)) + { + MatchFound = 1; + m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME; + } + + // match against players for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++) { - if(m_pFriends->IsFriend(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, false)) + if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) || + str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString)) { - Filtered = 0; + MatchFound = 1; + m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER; break; } } - } - else - { - if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0)) - Filtered = 1; - else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) || - m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients)) - Filtered = 1; - else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD) - Filtered = 1; - else if(g_Config.m_BrFilterPure && - (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 && - str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 && - str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0)) + + // match against map + if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString)) { - Filtered = 1; + MatchFound = 1; + m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME; } - else if(g_Config.m_BrFilterPureMap && - !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 || - str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0) - ) - { - Filtered = 1; - } - else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency) - Filtered = 1; - else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0) - Filtered = 1; - else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress)) - Filtered = 1; - else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) - Filtered = 1; - else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype)) - Filtered = 1; - else if(g_Config.m_BrFilterString[0] != 0) - { - int MatchFound = 0; - m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0; - - // match against server name - if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString)) - { - MatchFound = 1; - m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME; - } - - // match against players - for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++) - { - if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) || - str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString)) - { - MatchFound = 1; - m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER; - break; - } - } - - // match against map - if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString)) - { - MatchFound = 1; - m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME; - } - - if(!MatchFound) - Filtered = 1; - } + if(!MatchFound) + Filtered = 1; } if(Filtered == 0) - m_pSortedServerlist[m_NumSortedServers++] = i; + { + // check for friend + m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO; + for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++) + { + m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, + m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan); + m_ppServerlist[i]->m_Info.m_FriendState = max(m_ppServerlist[i]->m_Info.m_FriendState, m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState); + } + + if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO) + m_pSortedServerlist[m_NumSortedServers++] = i; + } } } diff --git a/src/engine/friends.h b/src/engine/friends.h index bf9df904a..164e3461a 100644 --- a/src/engine/friends.h +++ b/src/engine/friends.h @@ -11,6 +11,8 @@ struct CFriendInfo { char m_aName[MAX_NAME_LENGTH]; char m_aClan[MAX_CLAN_LENGTH]; + unsigned m_NameHash; + unsigned m_ClanHash; }; class IFriends : public IInterface @@ -19,6 +21,10 @@ class IFriends : public IInterface public: enum { + FRIEND_NO=0, + FRIEND_CLAN, + FRIEND_PLAYER, + MAX_FRIENDS=128, }; @@ -26,11 +32,11 @@ public: virtual int NumFriends() const = 0; virtual const CFriendInfo *GetFriend(int Index) const = 0; + virtual int GetFriendState(const char *pName, const char *pClan) const = 0; virtual bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const = 0; virtual void AddFriend(const char *pName, const char *pClan) = 0; virtual void RemoveFriend(const char *pName, const char *pClan) = 0; - virtual void RemoveFriend(int Index) = 0; }; #endif diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 3ca59f9cf..1a49eaf0e 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -24,7 +24,9 @@ public: int m_Country; int m_Score; bool m_Player; - } ; + + int m_FriendState; + }; int m_SortedIndex; int m_ServerIndex; @@ -32,6 +34,7 @@ public: NETADDR m_NetAddr; int m_QuickSearchHit; + int m_FriendState; int m_MaxClients; int m_NumClients; diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 64754c68c..6ec18ce15 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -734,6 +734,8 @@ void CMenus::OnInit() Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this); Console()->Chain("remove_favorite", ConchainServerbrowserUpdate, this); + Console()->Chain("add_friend", ConchainFriendlistUpdate, this); + Console()->Chain("remove_friend", ConchainFriendlistUpdate, this); // setup load amount m_LoadCurrent = 0; @@ -1205,7 +1207,9 @@ int CMenus::Render() // remove friend if(m_FriendlistSelectedIndex >= 0) { - m_pClient->Friends()->RemoveFriend(m_FriendlistSelectedIndex); + m_pClient->Friends()->RemoveFriend(m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName, + m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aClan); + FriendlistOnUpdate(); Client()->ServerBrowserUpdate(); } } diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 51b8a1f85..959c366e4 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -202,8 +203,34 @@ class CMenus : public CComponent void DemolistPopulate(); static int DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser); + // friends + struct CFriendItem + { + const CFriendInfo *m_pFriendInfo; + int m_NumFound; + + bool operator<(const CFriendItem &Other) + { + if(m_NumFound && !Other.m_NumFound) + return true; + else if(!m_NumFound && Other.m_NumFound) + return false; + else + { + int Result = str_comp(m_pFriendInfo->m_aName, Other.m_pFriendInfo->m_aName); + if(Result) + return Result < 0; + else + return str_comp(m_pFriendInfo->m_aClan, Other.m_pFriendInfo->m_aClan) < 0; + } + } + }; + + sorted_array m_lFriends; int m_FriendlistSelectedIndex; + void FriendlistOnUpdate(); + // found in menus.cpp int Render(); //void render_background(); @@ -225,11 +252,13 @@ class CMenus : public CComponent // found in menus_browser.cpp int m_SelectedIndex; + int m_ScrollOffset; void RenderServerbrowserServerList(CUIRect View); void RenderServerbrowserServerDetail(CUIRect View); void RenderServerbrowserFilters(CUIRect View); void RenderServerbrowserFriends(CUIRect View); void RenderServerbrowser(CUIRect MainView); + static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); // found in menus_settings.cpp diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 09c8c4943..5a0ebfb62 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -154,6 +154,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) int ScrollNum = NumServers-Num+1; if(ScrollNum > 0) { + if(m_ScrollOffset) + { + s_ScrollValue = (float)(m_ScrollOffset)/ScrollNum; + m_ScrollOffset = 0; + } if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View)) s_ScrollValue -= 3.0f/ScrollNum; if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View)) @@ -213,16 +218,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) m_SelectedIndex = -1; - for (int i = 0; i < NumServers; i++) - { - const CServerInfo *pItem = ServerBrowser()->SortedGet(i); - NumPlayers += pItem->m_NumPlayers; - } + // reset friend counter + for(int i = 0; i < m_lFriends.size(); m_lFriends[i++].m_NumFound = 0); for (int i = 0; i < NumServers; i++) { int ItemIndex = i; const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); + NumPlayers += pItem->m_NumPlayers; CUIRect Row; CUIRect SelectHitBox; @@ -234,6 +237,28 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) if(Selected) m_SelectedIndex = i; + // update friend counter + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + for(int j = 0; j < pItem->m_NumClients; ++j) + { + if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO) + { + unsigned NameHash = str_quickhash(pItem->m_aClients[j].m_aName); + unsigned ClanHash = str_quickhash(pItem->m_aClients[j].m_aClan); + for(int f = 0; f < m_lFriends.size(); ++f) + { + if(ClanHash == m_lFriends[f].m_pFriendInfo->m_ClanHash && + (!m_lFriends[f].m_pFriendInfo->m_aName[0] || NameHash == m_lFriends[f].m_pFriendInfo->m_NameHash)) + { + m_lFriends[f].m_NumFound++; + break; + } + } + } + } + } + // make sure that only those in view can be selected if(Row.y+Row.h > OriginalView.y && Row.y < OriginalView.y+OriginalView.h) { @@ -353,6 +378,15 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } else if(ID == COL_PLAYERS) { + CUIRect Icon; + Button.VMargin(4.0f, &Button); + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + Button.VSplitLeft(Button.h, &Icon, &Button); + Icon.Margin(2.0f, &Icon); + DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_HEART, &Icon); + } + if(g_Config.m_BrFilterSpectators) str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumPlayers, pItem->m_MaxPlayers); else @@ -463,7 +497,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View) g_Config.m_BrFilterFull ^= 1; ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); - if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends"), g_Config.m_BrFilterFriends, &Button)) + if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends only"), g_Config.m_BrFilterFriends, &Button)) g_Config.m_BrFilterFriends ^= 1; ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter); @@ -622,14 +656,26 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) RenderTools()->DrawUIRect(&ServerScoreBoard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f); UI()->DoLabelScaled(&ServerHeader, Localize("Scoreboard"), FontSize+2.0f, 0); - if (pSelectedServer) + if(pSelectedServer) { ServerScoreBoard.Margin(3.0f, &ServerScoreBoard); for (int i = 0; i < pSelectedServer->m_NumClients; i++) { CUIRect Name, Clan, Score, Flag; ServerScoreBoard.HSplitTop(25.0f, &Name, &ServerScoreBoard); - RenderTools()->DrawUIRect(&Name, vec4(1,1,1,(i%2+1)*0.05f), CUI::CORNER_ALL, 4.0f); + if(UI()->DoButtonLogic(&pSelectedServer->m_aClients[i], "", 0, &Name)) + { + if(pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER) + m_pClient->Friends()->RemoveFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan); + else + m_pClient->Friends()->AddFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan); + FriendlistOnUpdate(); + Client()->ServerBrowserUpdate(); + } + + vec4 Colour = pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_NO ? vec4(1.0f, 1.0f, 1.0f, (i%2+1)*0.05f) : + vec4(0.5f, 1.0f, 0.5f, 0.15f+(i%2+1)*0.05f); + RenderTools()->DrawUIRect(&Name, Colour, CUI::CORNER_ALL, 4.0f); Name.VSplitLeft(5.0f, 0, &Name); Name.VSplitLeft(30.0f, &Score, &Name); Name.VSplitRight(34.0f, &Name, &Flag); @@ -701,45 +747,102 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) } } +void CMenus::FriendlistOnUpdate() +{ + m_lFriends.clear(); + for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i) + { + CFriendItem Item; + Item.m_pFriendInfo = m_pClient->Friends()->GetFriend(i); + Item.m_NumFound = 0; + m_lFriends.add_unsorted(Item); + } + m_lFriends.sort_range(); +} + void CMenus::RenderServerbrowserFriends(CUIRect View) { + static int s_Inited = 0; + if(!s_Inited) + { + FriendlistOnUpdate(); + s_Inited = 1; + } + CUIRect ServerFriends = View, FilterHeader; - const float FontSize = 12.0f; + const float FontSize = 10.0f; // header ServerFriends.HSplitTop(ms_ListheaderHeight, &FilterHeader, &ServerFriends); RenderTools()->DrawUIRect(&FilterHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f); RenderTools()->DrawUIRect(&ServerFriends, vec4(0,0,0,0.15f), 0, 4.0f); - UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+2.0f, 0); + UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+4.0f, 0); CUIRect Button, List; - ServerFriends.VSplitLeft(5.0f, 0, &ServerFriends); ServerFriends.Margin(3.0f, &ServerFriends); - ServerFriends.VMargin(5.0f, &ServerFriends); + ServerFriends.VMargin(3.0f, &ServerFriends); ServerFriends.HSplitBottom(100.0f, &List, &ServerFriends); // friends list(remove friend) - static int s_FriendList = 0; static float s_ScrollValue = 0; - UiDoListboxStart(&s_FriendList, &List, 40.0f, "", "", m_pClient->Friends()->NumFriends(), 1, m_FriendlistSelectedIndex, s_ScrollValue); + UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue); - for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i) + m_lFriends.sort_range(); + for(int i = 0; i < m_lFriends.size(); ++i) { - const CFriendInfo *pFriend = m_pClient->Friends()->GetFriend(i); - CListboxItem Item = UiDoListboxNextItem(pFriend); + CListboxItem Item = UiDoListboxNextItem(&m_lFriends[i]); if(Item.m_Visible) { - Item.m_Rect.Margin(2.5f, &Item.m_Rect); - RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_ALL, 4.0f); - Item.m_Rect.Margin(2.5f, &Item.m_Rect); - Item.m_Rect.HSplitTop(14.0f, &Item.m_Rect, &Button); - UI()->DoLabelScaled(&Item.m_Rect, pFriend->m_aName, FontSize, -1); - UI()->DoLabelScaled(&Button, pFriend->m_aClan, FontSize, -1); + Item.m_Rect.Margin(1.5f, &Item.m_Rect); + CUIRect OnState; + Item.m_Rect.VSplitRight(30.0f, &Item.m_Rect, &OnState); + RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_L, 4.0f); + + Item.m_Rect.VMargin(2.5f, &Item.m_Rect); + Item.m_Rect.HSplitTop(12.0f, &Item.m_Rect, &Button); + UI()->DoLabelScaled(&Item.m_Rect, m_lFriends[i].m_pFriendInfo->m_aName, FontSize, -1); + UI()->DoLabelScaled(&Button, m_lFriends[i].m_pFriendInfo->m_aClan, FontSize, -1); + + RenderTools()->DrawUIRect(&OnState, m_lFriends[i].m_NumFound ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(1.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_R, 4.0f); + OnState.HMargin((OnState.h-FontSize)/3, &OnState); + OnState.VMargin(5.0f, &OnState); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "%i", m_lFriends[i].m_NumFound); + UI()->DoLabelScaled(&OnState, aBuf, FontSize+2, 1); } } - m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, 0); + bool Activated = false; + m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, &Activated); + + // activate found server with friend + if(Activated && !m_EnterPressed && m_lFriends[m_FriendlistSelectedIndex].m_NumFound) + { + bool Found = false; + int NumServers = ServerBrowser()->NumSortedServers(); + for (int i = 0; i < NumServers && !Found; i++) + { + int ItemIndex = m_SelectedIndex != -1 ? (m_SelectedIndex+i+1)%NumServers : i; + const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex); + if(pItem->m_FriendState != IFriends::FRIEND_NO) + { + for(int j = 0; j < pItem->m_NumClients && !Found; ++j) + { + if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO && + str_quickhash(pItem->m_aClients[j].m_aClan) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_ClanHash && + (!m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName[0] || + str_quickhash(pItem->m_aClients[j].m_aName) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_NameHash)) + { + str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress)); + m_ScrollOffset = ItemIndex; + m_SelectedIndex = ItemIndex; + Found = true; + } + } + } + } + } ServerFriends.HSplitTop(2.5f, 0, &ServerFriends); ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends); @@ -774,10 +877,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View) ServerFriends.HSplitTop(3.0f, 0, &ServerFriends); ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends); - static int s_RemoveButton = 0; - if(DoButton_Menu(&s_RemoveButton, Localize("Add Friend"), 0, &Button)) + static int s_AddButton = 0; + if(DoButton_Menu(&s_AddButton, Localize("Add Friend"), 0, &Button)) { m_pClient->Friends()->AddFriend(s_aName, s_aClan); + FriendlistOnUpdate(); Client()->ServerBrowserUpdate(); } } @@ -916,6 +1020,16 @@ void CMenus::RenderServerbrowser(CUIRect MainView) } } +void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() == 2 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE) + { + ((CMenus *)pUserData)->FriendlistOnUpdate(); + ((CMenus *)pUserData)->Client()->ServerBrowserUpdate(); + } +} + void CMenus::ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) { pfnCallback(pResult, pCallbackUserData);