1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include <base/math.h>
4#include <base/system.h>
5
6#include <engine/config.h>
7#include <engine/console.h>
8#include <engine/shared/config.h>
9
10#include "friends.h"
11
12CFriends::CFriends()
13{
14 mem_zero(block: m_aFriends, size: sizeof(m_aFriends));
15 m_NumFriends = 0;
16 m_Foes = false;
17}
18
19void CFriends::ConAddFriend(IConsole::IResult *pResult, void *pUserData)
20{
21 CFriends *pSelf = (CFriends *)pUserData;
22 pSelf->AddFriend(pName: pResult->GetString(Index: 0), pClan: pResult->GetString(Index: 1));
23}
24
25void CFriends::ConRemoveFriend(IConsole::IResult *pResult, void *pUserData)
26{
27 CFriends *pSelf = (CFriends *)pUserData;
28 pSelf->RemoveFriend(pName: pResult->GetString(Index: 0), pClan: pResult->GetString(Index: 1));
29}
30
31void CFriends::ConFriends(IConsole::IResult *pResult, void *pUserData)
32{
33 CFriends *pSelf = (CFriends *)pUserData;
34 pSelf->Friends();
35}
36
37void CFriends::Init(bool Foes)
38{
39 m_Foes = Foes;
40
41 IConfigManager *pConfigManager = Kernel()->RequestInterface<IConfigManager>();
42 if(pConfigManager)
43 pConfigManager->RegisterCallback(pfnFunc: ConfigSaveCallback, pUserData: this);
44
45 IConsole *pConsole = Kernel()->RequestInterface<IConsole>();
46 if(pConsole)
47 {
48 if(Foes)
49 {
50 pConsole->Register(pName: "add_foe", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConAddFriend, pUser: this, pHelp: "Add a foe");
51 pConsole->Register(pName: "remove_foe", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConRemoveFriend, pUser: this, pHelp: "Remove a foe");
52 pConsole->Register(pName: "foes", pParams: "", Flags: CFGFLAG_CLIENT, pfnFunc: ConFriends, pUser: this, pHelp: "List foes");
53 }
54 else
55 {
56 pConsole->Register(pName: "add_friend", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConAddFriend, pUser: this, pHelp: "Add a friend");
57 pConsole->Register(pName: "remove_friend", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConRemoveFriend, pUser: this, pHelp: "Remove a friend");
58 pConsole->Register(pName: "friends", pParams: "", Flags: CFGFLAG_CLIENT, pfnFunc: ConFriends, pUser: this, pHelp: "List friends");
59 }
60 }
61}
62
63const CFriendInfo *CFriends::GetFriend(int Index) const
64{
65 return &m_aFriends[maximum(a: 0, b: Index % m_NumFriends)];
66}
67
68int CFriends::GetFriendState(const char *pName, const char *pClan) const
69{
70 int Result = FRIEND_NO;
71 unsigned NameHash = str_quickhash(str: pName);
72 unsigned ClanHash = str_quickhash(str: pClan);
73 for(int i = 0; i < m_NumFriends; ++i)
74 {
75 if((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan)))
76 {
77 if(m_aFriends[i].m_aName[0] == 0)
78 Result = FRIEND_CLAN;
79 else if(m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName))
80 {
81 Result = FRIEND_PLAYER;
82 break;
83 }
84 }
85 }
86 return Result;
87}
88
89bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const
90{
91 unsigned NameHash = str_quickhash(str: pName);
92 unsigned ClanHash = str_quickhash(str: pClan);
93 for(int i = 0; i < m_NumFriends; ++i)
94 {
95 if(((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan))) &&
96 ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || (m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName))))
97 return true;
98 }
99 return false;
100}
101
102void CFriends::AddFriend(const char *pName, const char *pClan)
103{
104 if(m_NumFriends == MAX_FRIENDS || (pName[0] == 0 && pClan[0] == 0))
105 return;
106
107 // make sure we don't have the friend already
108 unsigned NameHash = str_quickhash(str: pName);
109 unsigned ClanHash = str_quickhash(str: pClan);
110 for(int i = 0; i < m_NumFriends; ++i)
111 {
112 if((m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName)) && ((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan))))
113 return;
114 }
115
116 str_copy(dst&: m_aFriends[m_NumFriends].m_aName, src: pName);
117 str_copy(dst&: m_aFriends[m_NumFriends].m_aClan, src: pClan);
118 m_aFriends[m_NumFriends].m_NameHash = NameHash;
119 m_aFriends[m_NumFriends].m_ClanHash = ClanHash;
120 ++m_NumFriends;
121}
122
123void CFriends::RemoveFriend(const char *pName, const char *pClan)
124{
125 unsigned NameHash = str_quickhash(str: pName);
126 unsigned ClanHash = str_quickhash(str: pClan);
127 for(int i = 0; i < m_NumFriends; ++i)
128 {
129 if((m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName)) &&
130 ((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan))))
131 {
132 RemoveFriend(Index: i);
133 return;
134 }
135 }
136}
137
138void CFriends::RemoveFriend(int Index)
139{
140 if(Index >= 0 && Index < m_NumFriends)
141 {
142 mem_move(dest: &m_aFriends[Index], source: &m_aFriends[Index + 1], size: sizeof(CFriendInfo) * (m_NumFriends - (Index + 1)));
143 --m_NumFriends;
144 }
145}
146
147void CFriends::Friends()
148{
149 char aBuf[128];
150 IConsole *pConsole = Kernel()->RequestInterface<IConsole>();
151 if(pConsole)
152 {
153 for(int i = 0; i < m_NumFriends; ++i)
154 {
155 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Name: %s, Clan: %s", m_aFriends[i].m_aName, m_aFriends[i].m_aClan);
156
157 pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: m_Foes ? "foes" : "friends", pStr: aBuf, PrintColor: color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClMessageHighlightColor)));
158 }
159 }
160}
161
162void CFriends::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData)
163{
164 CFriends *pSelf = (CFriends *)pUserData;
165 char aBuf[128];
166 const char *pEnd = aBuf + sizeof(aBuf) - 4;
167 for(int i = 0; i < pSelf->m_NumFriends; ++i)
168 {
169 str_copy(dst&: aBuf, src: pSelf->m_Foes ? "add_foe " : "add_friend ");
170
171 str_append(dst&: aBuf, src: "\"");
172 char *pDst = aBuf + str_length(str: aBuf);
173 str_escape(dst: &pDst, src: pSelf->m_aFriends[i].m_aName, end: pEnd);
174 str_append(dst&: aBuf, src: "\" \"");
175 pDst = aBuf + str_length(str: aBuf);
176 str_escape(dst: &pDst, src: pSelf->m_aFriends[i].m_aClan, end: pEnd);
177 str_append(dst&: aBuf, src: "\"");
178
179 pConfigManager->WriteLine(pLine: aBuf);
180 }
181}
182