1/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
2#include "gamecontext.h"
3#include <engine/shared/config.h>
4#include <engine/shared/protocol.h>
5#include <game/mapitems.h>
6#include <game/server/gamemodes/DDRace.h>
7#include <game/server/teams.h>
8#include <game/version.h>
9
10#include "entities/character.h"
11#include "player.h"
12#include "score.h"
13
14bool CheckClientId(int ClientId);
15
16void CGameContext::ConCredits(IConsole::IResult *pResult, void *pUserData)
17{
18 CGameContext *pSelf = (CGameContext *)pUserData;
19
20 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
21 pStr: "DDNet is run by the DDNet staff (DDNet.org/staff)");
22 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
23 pStr: "Great maps and many ideas from the great community");
24 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
25 pStr: "Help and code by eeeee, HMH, east, CookieMichal, Learath2,");
26 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
27 pStr: "Savander, laxa, Tobii, BeaR, Wohoo, nuborn, timakro, Shiki,");
28 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
29 pStr: "trml, Soreu, hi_leute_gll, Lady Saavik, Chairn, heinrich5991,");
30 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
31 pStr: "swick, oy, necropotame, Ryozuki, Redix, d3fault, marcelherd,");
32 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
33 pStr: "BannZay, ACTom, SiuFuWong, PathosEthosLogos, TsFreddie,");
34 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
35 pStr: "Jupeyy, noby, ChillerDragon, ZombieToad, weez15, z6zzz,");
36 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
37 pStr: "Piepow, QingGo, RafaelFF, sctt, jao, daverck, fokkonaut,");
38 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
39 pStr: "Bojidar, FallenKN, ardadem, archimede67, sirius1242, Aerll,");
40 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
41 pStr: "trafilaw, Zwelf, Patiga, Konsti, ElXreno, MikiGamer,");
42 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
43 pStr: "Fireball, Banana090, axblk, yangfl, Kaffeine,");
44 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
45 pStr: "Zodiac, c0d3d3v, GiuCcc, Ravie, Robyt3, simpygirl,");
46 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
47 pStr: "sjrc6, Cellegen, srdante, Nouaa, Voxel, luk51,");
48 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
49 pStr: "Vy0x2, Avolicious, louis, Marmare314, hus3h,");
50 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
51 pStr: "ArijanJ, tarunsamanta2k20, Possseidon, M0REKZ,");
52 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
53 pStr: "Teero, furo, dobrykafe, Moiman, JSaurusRex,");
54 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
55 pStr: "Steinchen & others");
56 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
57 pStr: "Based on DDRace by the DDRace developers,");
58 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
59 pStr: "which is a mod of Teeworlds by the Teeworlds developers.");
60}
61
62void CGameContext::ConInfo(IConsole::IResult *pResult, void *pUserData)
63{
64 CGameContext *pSelf = (CGameContext *)pUserData;
65 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
66 pStr: "DDraceNetwork Mod. Version: " GAME_VERSION);
67 if(GIT_SHORTREV_HASH)
68 {
69 char aBuf[64];
70 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Git revision hash: %s", GIT_SHORTREV_HASH);
71 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
72 }
73 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
74 pStr: "Official site: DDNet.org");
75 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
76 pStr: "For more info: /cmdlist");
77 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
78 pStr: "Or visit DDNet.org");
79}
80
81void CGameContext::ConList(IConsole::IResult *pResult, void *pUserData)
82{
83 CGameContext *pSelf = (CGameContext *)pUserData;
84 int ClientId = pResult->m_ClientId;
85 if(!CheckClientId(ClientId))
86 return;
87
88 char zerochar = 0;
89 if(pResult->NumArguments() > 0)
90 pSelf->List(ClientId, pFilter: pResult->GetString(Index: 0));
91 else
92 pSelf->List(ClientId, pFilter: &zerochar);
93}
94
95void CGameContext::ConHelp(IConsole::IResult *pResult, void *pUserData)
96{
97 CGameContext *pSelf = (CGameContext *)pUserData;
98
99 if(pResult->NumArguments() == 0)
100 {
101 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
102 pStr: "/cmdlist will show a list of all chat commands");
103 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
104 pStr: "/help + any command will show you the help for this command");
105 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
106 pStr: "Example /help settings will display the help about /settings");
107 }
108 else
109 {
110 const char *pArg = pResult->GetString(Index: 0);
111 const IConsole::CCommandInfo *pCmdInfo =
112 pSelf->Console()->GetCommandInfo(pName: pArg, FlagMask: CFGFLAG_SERVER, Temp: false);
113 if(pCmdInfo)
114 {
115 if(pCmdInfo->m_pParams)
116 {
117 char aBuf[256];
118 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Usage: %s %s", pCmdInfo->m_pName, pCmdInfo->m_pParams);
119 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
120 }
121
122 if(pCmdInfo->m_pHelp)
123 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: pCmdInfo->m_pHelp);
124 }
125 else
126 pSelf->Console()->Print(
127 Level: IConsole::OUTPUT_LEVEL_STANDARD,
128 pFrom: "chatresp",
129 pStr: "Command is either unknown or you have given a blank command without any parameters.");
130 }
131}
132
133void CGameContext::ConSettings(IConsole::IResult *pResult, void *pUserData)
134{
135 CGameContext *pSelf = (CGameContext *)pUserData;
136
137 if(pResult->NumArguments() == 0)
138 {
139 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
140 pStr: "to check a server setting say /settings and setting's name, setting names are:");
141 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
142 pStr: "teams, cheats, collision, hooking, endlesshooking, me, ");
143 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
144 pStr: "hitting, oldlaser, timeout, votes, pause and scores");
145 }
146 else
147 {
148 const char *pArg = pResult->GetString(Index: 0);
149 char aBuf[256];
150 float ColTemp;
151 float HookTemp;
152 pSelf->m_Tuning.Get(pName: "player_collision", pValue: &ColTemp);
153 pSelf->m_Tuning.Get(pName: "player_hooking", pValue: &HookTemp);
154 if(str_comp_nocase(a: pArg, b: "teams") == 0)
155 {
156 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s %s",
157 g_Config.m_SvTeam == SV_TEAM_ALLOWED ?
158 "Teams are available on this server" :
159 (g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO) ?
160 "Teams are not available on this server" :
161 "You have to be in a team to play on this server", /*g_Config.m_SvTeamStrict ? "and if you die in a team all of you die" : */
162 "and all of your team will die if the team is locked");
163 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
164 }
165 else if(str_comp_nocase(a: pArg, b: "cheats") == 0)
166 {
167 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
168 pStr: g_Config.m_SvTestingCommands ?
169 "Cheats are enabled on this server" :
170 "Cheats are disabled on this server");
171 }
172 else if(str_comp_nocase(a: pArg, b: "collision") == 0)
173 {
174 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
175 pStr: ColTemp ?
176 "Players can collide on this server" :
177 "Players can't collide on this server");
178 }
179 else if(str_comp_nocase(a: pArg, b: "hooking") == 0)
180 {
181 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
182 pStr: HookTemp ?
183 "Players can hook each other on this server" :
184 "Players can't hook each other on this server");
185 }
186 else if(str_comp_nocase(a: pArg, b: "endlesshooking") == 0)
187 {
188 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
189 pStr: g_Config.m_SvEndlessDrag ?
190 "Players hook time is unlimited" :
191 "Players hook time is limited");
192 }
193 else if(str_comp_nocase(a: pArg, b: "hitting") == 0)
194 {
195 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
196 pStr: g_Config.m_SvHit ?
197 "Players weapons affect others" :
198 "Players weapons has no affect on others");
199 }
200 else if(str_comp_nocase(a: pArg, b: "oldlaser") == 0)
201 {
202 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
203 pStr: g_Config.m_SvOldLaser ?
204 "Lasers can hit you if you shot them and they pull you towards the bounce origin (Like DDRace Beta)" :
205 "Lasers can't hit you if you shot them, and they pull others towards the shooter");
206 }
207 else if(str_comp_nocase(a: pArg, b: "me") == 0)
208 {
209 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
210 pStr: g_Config.m_SvSlashMe ?
211 "Players can use /me commands the famous IRC Command" :
212 "Players can't use the /me command");
213 }
214 else if(str_comp_nocase(a: pArg, b: "timeout") == 0)
215 {
216 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "The Server Timeout is currently set to %d seconds", g_Config.m_ConnTimeout);
217 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
218 }
219 else if(str_comp_nocase(a: pArg, b: "votes") == 0)
220 {
221 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
222 pStr: g_Config.m_SvVoteKick ?
223 "Players can use Callvote menu tab to kick offenders" :
224 "Players can't use the Callvote menu tab to kick offenders");
225 if(g_Config.m_SvVoteKick)
226 {
227 str_format(buffer: aBuf, buffer_size: sizeof(aBuf),
228 format: "Players are banned for %d minute(s) if they get voted off", g_Config.m_SvVoteKickBantime);
229
230 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
231 pStr: g_Config.m_SvVoteKickBantime ?
232 aBuf :
233 "Players are just kicked and not banned if they get voted off");
234 }
235 }
236 else if(str_comp_nocase(a: pArg, b: "pause") == 0)
237 {
238 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
239 pStr: g_Config.m_SvPauseable ?
240 "/spec will pause you and your tee will vanish" :
241 "/spec will pause you but your tee will not vanish");
242 }
243 else if(str_comp_nocase(a: pArg, b: "scores") == 0)
244 {
245 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
246 pStr: g_Config.m_SvHideScore ?
247 "Scores are private on this server" :
248 "Scores are public on this server");
249 }
250 else
251 {
252 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
253 pStr: "no matching settings found, type /settings to view them");
254 }
255 }
256}
257
258void CGameContext::ConRules(IConsole::IResult *pResult, void *pUserData)
259{
260 CGameContext *pSelf = (CGameContext *)pUserData;
261 bool Printed = false;
262 if(g_Config.m_SvDDRaceRules)
263 {
264 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
265 pStr: "Be nice.");
266 Printed = true;
267 }
268 char *apRuleLines[] = {
269 g_Config.m_SvRulesLine1,
270 g_Config.m_SvRulesLine2,
271 g_Config.m_SvRulesLine3,
272 g_Config.m_SvRulesLine4,
273 g_Config.m_SvRulesLine5,
274 g_Config.m_SvRulesLine6,
275 g_Config.m_SvRulesLine7,
276 g_Config.m_SvRulesLine8,
277 g_Config.m_SvRulesLine9,
278 g_Config.m_SvRulesLine10,
279 };
280 for(auto &pRuleLine : apRuleLines)
281 {
282 if(pRuleLine[0])
283 {
284 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD,
285 pFrom: "chatresp", pStr: pRuleLine);
286 Printed = true;
287 }
288 }
289 if(!Printed)
290 {
291 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
292 pStr: "No Rules Defined, Kill em all!!");
293 }
294}
295
296void ToggleSpecPause(IConsole::IResult *pResult, void *pUserData, int PauseType)
297{
298 if(!CheckClientId(ClientId: pResult->m_ClientId))
299 return;
300
301 CGameContext *pSelf = (CGameContext *)pUserData;
302 IServer *pServ = pSelf->Server();
303 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
304 if(!pPlayer)
305 return;
306
307 int PauseState = pPlayer->IsPaused();
308 if(PauseState > 0)
309 {
310 char aBuf[128];
311 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "You are force-paused for %d seconds.", (PauseState - pServ->Tick()) / pServ->TickSpeed());
312 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
313 }
314 else if(pResult->NumArguments() > 0)
315 {
316 if(-PauseState == PauseType && pPlayer->m_SpectatorId != pResult->m_ClientId && pServ->ClientIngame(ClientId: pPlayer->m_SpectatorId) && !str_comp(a: pServ->ClientName(ClientId: pPlayer->m_SpectatorId), b: pResult->GetString(Index: 0)))
317 {
318 pPlayer->Pause(State: CPlayer::PAUSE_NONE, Force: false);
319 }
320 else
321 {
322 pPlayer->Pause(State: PauseType, Force: false);
323 pPlayer->SpectatePlayerName(pName: pResult->GetString(Index: 0));
324 }
325 }
326 else if(-PauseState != CPlayer::PAUSE_NONE && PauseType != CPlayer::PAUSE_NONE)
327 {
328 pPlayer->Pause(State: CPlayer::PAUSE_NONE, Force: false);
329 }
330 else if(-PauseState != PauseType)
331 {
332 pPlayer->Pause(State: PauseType, Force: false);
333 }
334}
335
336void ToggleSpecPauseVoted(IConsole::IResult *pResult, void *pUserData, int PauseType)
337{
338 if(!CheckClientId(ClientId: pResult->m_ClientId))
339 return;
340
341 CGameContext *pSelf = (CGameContext *)pUserData;
342 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
343 if(!pPlayer)
344 return;
345
346 int PauseState = pPlayer->IsPaused();
347 if(PauseState > 0)
348 {
349 IServer *pServ = pSelf->Server();
350 char aBuf[128];
351 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "You are force-paused for %d seconds.", (PauseState - pServ->Tick()) / pServ->TickSpeed());
352 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
353 return;
354 }
355
356 bool IsPlayerBeingVoted = pSelf->m_VoteCloseTime &&
357 (pSelf->IsKickVote() || pSelf->IsSpecVote()) &&
358 pResult->m_ClientId != pSelf->m_VoteVictim;
359 if((!IsPlayerBeingVoted && -PauseState == PauseType) ||
360 (IsPlayerBeingVoted && PauseState && pPlayer->m_SpectatorId == pSelf->m_VoteVictim))
361 {
362 pPlayer->Pause(State: CPlayer::PAUSE_NONE, Force: false);
363 }
364 else
365 {
366 pPlayer->Pause(State: PauseType, Force: false);
367 if(IsPlayerBeingVoted)
368 pPlayer->m_SpectatorId = pSelf->m_VoteVictim;
369 }
370}
371
372void CGameContext::ConToggleSpec(IConsole::IResult *pResult, void *pUserData)
373{
374 ToggleSpecPause(pResult, pUserData, PauseType: g_Config.m_SvPauseable ? CPlayer::PAUSE_SPEC : CPlayer::PAUSE_PAUSED);
375}
376
377void CGameContext::ConToggleSpecVoted(IConsole::IResult *pResult, void *pUserData)
378{
379 ToggleSpecPauseVoted(pResult, pUserData, PauseType: g_Config.m_SvPauseable ? CPlayer::PAUSE_SPEC : CPlayer::PAUSE_PAUSED);
380}
381
382void CGameContext::ConTogglePause(IConsole::IResult *pResult, void *pUserData)
383{
384 ToggleSpecPause(pResult, pUserData, PauseType: CPlayer::PAUSE_PAUSED);
385}
386
387void CGameContext::ConTogglePauseVoted(IConsole::IResult *pResult, void *pUserData)
388{
389 ToggleSpecPauseVoted(pResult, pUserData, PauseType: CPlayer::PAUSE_PAUSED);
390}
391
392void CGameContext::ConTeamTop5(IConsole::IResult *pResult, void *pUserData)
393{
394 CGameContext *pSelf = (CGameContext *)pUserData;
395 if(!CheckClientId(ClientId: pResult->m_ClientId))
396 return;
397
398 if(g_Config.m_SvHideScore)
399 {
400 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
401 pStr: "Showing the team top 5 is not allowed on this server.");
402 return;
403 }
404
405 if(pResult->NumArguments() == 0)
406 {
407 pSelf->Score()->ShowTeamTop5(ClientId: pResult->m_ClientId, Offset: 1);
408 }
409 else if(pResult->NumArguments() == 1)
410 {
411 if(pResult->GetInteger(Index: 0) != 0)
412 {
413 pSelf->Score()->ShowTeamTop5(ClientId: pResult->m_ClientId, Offset: pResult->GetInteger(Index: 0));
414 }
415 else
416 {
417 const char *pRequestedName = (str_comp_nocase(a: pResult->GetString(Index: 0), b: "me") == 0) ?
418 pSelf->Server()->ClientName(ClientId: pResult->m_ClientId) :
419 pResult->GetString(Index: 0);
420 pSelf->Score()->ShowPlayerTeamTop5(ClientId: pResult->m_ClientId, pName: pRequestedName, Offset: 0);
421 }
422 }
423 else if(pResult->NumArguments() == 2 && pResult->GetInteger(Index: 1) != 0)
424 {
425 const char *pRequestedName = (str_comp_nocase(a: pResult->GetString(Index: 0), b: "me") == 0) ?
426 pSelf->Server()->ClientName(ClientId: pResult->m_ClientId) :
427 pResult->GetString(Index: 0);
428 pSelf->Score()->ShowPlayerTeamTop5(ClientId: pResult->m_ClientId, pName: pRequestedName, Offset: pResult->GetInteger(Index: 1));
429 }
430 else
431 {
432 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "/top5team needs 0, 1 or 2 parameter. 1. = name, 2. = start number");
433 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Example: /top5team, /top5team me, /top5team Hans, /top5team \"Papa Smurf\" 5");
434 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Bad: /top5team Papa Smurf 5 # Good: /top5team \"Papa Smurf\" 5 ");
435 }
436}
437
438void CGameContext::ConTop(IConsole::IResult *pResult, void *pUserData)
439{
440 CGameContext *pSelf = (CGameContext *)pUserData;
441 if(!CheckClientId(ClientId: pResult->m_ClientId))
442 return;
443
444 if(g_Config.m_SvHideScore)
445 {
446 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
447 pStr: "Showing the top is not allowed on this server.");
448 return;
449 }
450
451 if(pResult->NumArguments() > 0)
452 pSelf->Score()->ShowTop(ClientId: pResult->m_ClientId, Offset: pResult->GetInteger(Index: 0));
453 else
454 pSelf->Score()->ShowTop(ClientId: pResult->m_ClientId);
455}
456
457void CGameContext::ConTimes(IConsole::IResult *pResult, void *pUserData)
458{
459 CGameContext *pSelf = (CGameContext *)pUserData;
460 if(!CheckClientId(ClientId: pResult->m_ClientId))
461 return;
462
463 if(pResult->NumArguments() == 0)
464 {
465 pSelf->Score()->ShowTimes(ClientId: pResult->m_ClientId, Offset: 1);
466 }
467 else if(pResult->NumArguments() == 1)
468 {
469 if(pResult->GetInteger(Index: 0) != 0)
470 {
471 pSelf->Score()->ShowTimes(ClientId: pResult->m_ClientId, Offset: pResult->GetInteger(Index: 0));
472 }
473 else
474 {
475 const char *pRequestedName = (str_comp_nocase(a: pResult->GetString(Index: 0), b: "me") == 0) ?
476 pSelf->Server()->ClientName(ClientId: pResult->m_ClientId) :
477 pResult->GetString(Index: 0);
478 pSelf->Score()->ShowTimes(ClientId: pResult->m_ClientId, pName: pRequestedName, Offset: pResult->GetInteger(Index: 1));
479 }
480 }
481 else if(pResult->NumArguments() == 2 && pResult->GetInteger(Index: 1) != 0)
482 {
483 const char *pRequestedName = (str_comp_nocase(a: pResult->GetString(Index: 0), b: "me") == 0) ?
484 pSelf->Server()->ClientName(ClientId: pResult->m_ClientId) :
485 pResult->GetString(Index: 0);
486 pSelf->Score()->ShowTimes(ClientId: pResult->m_ClientId, pName: pRequestedName, Offset: pResult->GetInteger(Index: 1));
487 }
488 else
489 {
490 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "/times needs 0, 1 or 2 parameter. 1. = name, 2. = start number");
491 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Example: /times, /times me, /times Hans, /times \"Papa Smurf\" 5");
492 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Bad: /times Papa Smurf 5 # Good: /times \"Papa Smurf\" 5 ");
493 }
494}
495
496void CGameContext::ConDND(IConsole::IResult *pResult, void *pUserData)
497{
498 CGameContext *pSelf = (CGameContext *)pUserData;
499 if(!CheckClientId(ClientId: pResult->m_ClientId))
500 return;
501
502 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
503 if(!pPlayer)
504 return;
505
506 if(pPlayer->m_DND)
507 {
508 pPlayer->m_DND = false;
509 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "You will receive global chat and server messages");
510 }
511 else
512 {
513 pPlayer->m_DND = true;
514 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "You will not receive any further global chat and server messages");
515 }
516}
517
518void CGameContext::ConMap(IConsole::IResult *pResult, void *pUserData)
519{
520 CGameContext *pSelf = (CGameContext *)pUserData;
521 if(!CheckClientId(ClientId: pResult->m_ClientId))
522 return;
523
524 if(g_Config.m_SvMapVote == 0)
525 {
526 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
527 pStr: "/map is disabled");
528 return;
529 }
530
531 if(pResult->NumArguments() <= 0)
532 {
533 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Example: /map adr3 to call vote for Adrenaline 3. This means that the map name must start with 'a' and contain the characters 'd', 'r' and '3' in that order");
534 return;
535 }
536
537 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
538 if(!pPlayer)
539 return;
540
541 if(pSelf->RateLimitPlayerVote(ClientId: pResult->m_ClientId) || pSelf->RateLimitPlayerMapVote(ClientId: pResult->m_ClientId))
542 return;
543
544 pSelf->Score()->MapVote(ClientId: pResult->m_ClientId, pMapName: pResult->GetString(Index: 0));
545}
546
547void CGameContext::ConMapInfo(IConsole::IResult *pResult, void *pUserData)
548{
549 CGameContext *pSelf = (CGameContext *)pUserData;
550 if(!CheckClientId(ClientId: pResult->m_ClientId))
551 return;
552
553 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
554 if(!pPlayer)
555 return;
556
557 if(pResult->NumArguments() > 0)
558 pSelf->Score()->MapInfo(ClientId: pResult->m_ClientId, pMapName: pResult->GetString(Index: 0));
559 else
560 pSelf->Score()->MapInfo(ClientId: pResult->m_ClientId, pMapName: g_Config.m_SvMap);
561}
562
563void CGameContext::ConTimeout(IConsole::IResult *pResult, void *pUserData)
564{
565 CGameContext *pSelf = (CGameContext *)pUserData;
566 if(!CheckClientId(ClientId: pResult->m_ClientId))
567 return;
568
569 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
570 if(!pPlayer)
571 return;
572
573 const char *pTimeout = pResult->NumArguments() > 0 ? pResult->GetString(Index: 0) : pPlayer->m_aTimeoutCode;
574
575 if(!pSelf->Server()->IsSixup(ClientId: pResult->m_ClientId))
576 {
577 for(int i = 0; i < pSelf->Server()->MaxClients(); i++)
578 {
579 if(i == pResult->m_ClientId)
580 continue;
581 if(!pSelf->m_apPlayers[i])
582 continue;
583 if(str_comp(a: pSelf->m_apPlayers[i]->m_aTimeoutCode, b: pTimeout))
584 continue;
585 if(pSelf->Server()->SetTimedOut(ClientId: i, OrigId: pResult->m_ClientId))
586 {
587 if(pSelf->m_apPlayers[i]->GetCharacter())
588 pSelf->SendTuningParams(ClientId: i, Zone: pSelf->m_apPlayers[i]->GetCharacter()->m_TuneZone);
589 return;
590 }
591 }
592 }
593 else
594 {
595 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
596 pStr: "Your timeout code has been set. 0.7 clients can not reclaim their tees on timeout; however, a 0.6 client can claim your tee ");
597 }
598
599 pSelf->Server()->SetTimeoutProtected(pResult->m_ClientId);
600 str_copy(dst: pPlayer->m_aTimeoutCode, src: pResult->GetString(Index: 0), dst_size: sizeof(pPlayer->m_aTimeoutCode));
601}
602
603void CGameContext::ConPractice(IConsole::IResult *pResult, void *pUserData)
604{
605 CGameContext *pSelf = (CGameContext *)pUserData;
606 if(!CheckClientId(ClientId: pResult->m_ClientId))
607 return;
608
609 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
610 if(!pPlayer)
611 return;
612
613 if(pSelf->ProcessSpamProtection(ClientId: pResult->m_ClientId, RespectChatInitialDelay: false))
614 return;
615
616 if(!g_Config.m_SvPractice)
617 {
618 pSelf->Console()->Print(
619 Level: IConsole::OUTPUT_LEVEL_STANDARD,
620 pFrom: "chatresp",
621 pStr: "Practice mode is disabled");
622 return;
623 }
624
625 CGameTeams &Teams = pSelf->m_pController->Teams();
626
627 int Team = Teams.m_Core.Team(ClientId: pResult->m_ClientId);
628
629 if(Team < TEAM_FLOCK || (Team == TEAM_FLOCK && g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO) || Team >= TEAM_SUPER)
630 {
631 pSelf->Console()->Print(
632 Level: IConsole::OUTPUT_LEVEL_STANDARD,
633 pFrom: "chatresp",
634 pStr: "Join a team to enable practice mode, which means you can use /r, but can't earn a rank.");
635 return;
636 }
637
638 if(Teams.TeamFlock(Team))
639 {
640 pSelf->Console()->Print(
641 Level: IConsole::OUTPUT_LEVEL_STANDARD,
642 pFrom: "chatresp",
643 pStr: "Practice mode can't be enabled in team 0 mode.");
644 return;
645 }
646
647 if(Teams.GetSaving(TeamId: Team))
648 {
649 pSelf->Console()->Print(
650 Level: IConsole::OUTPUT_LEVEL_STANDARD,
651 pFrom: "chatresp",
652 pStr: "Practice mode can't be enabled while team save or load is in progress");
653 return;
654 }
655
656 if(Teams.IsPractice(Team))
657 {
658 pSelf->Console()->Print(
659 Level: IConsole::OUTPUT_LEVEL_STANDARD,
660 pFrom: "chatresp",
661 pStr: "Team is already in practice mode");
662 return;
663 }
664
665 bool VotedForPractice = pResult->NumArguments() == 0 || pResult->GetInteger(Index: 0);
666
667 if(VotedForPractice == pPlayer->m_VotedForPractice)
668 return;
669
670 pPlayer->m_VotedForPractice = VotedForPractice;
671
672 int NumCurrentVotes = 0;
673 int TeamSize = 0;
674
675 for(int i = 0; i < MAX_CLIENTS; i++)
676 {
677 if(Teams.m_Core.Team(ClientId: i) == Team)
678 {
679 CPlayer *pPlayer2 = pSelf->m_apPlayers[i];
680 if(pPlayer2 && pPlayer2->m_VotedForPractice)
681 NumCurrentVotes++;
682 TeamSize++;
683 }
684 }
685
686 int NumRequiredVotes = TeamSize / 2 + 1;
687
688 char aBuf[512];
689 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' voted to %s /practice mode for your team, which means you can use practice commands, but you can't earn a rank. Type /practice to vote (%d/%d required votes)", pSelf->Server()->ClientName(ClientId: pResult->m_ClientId), VotedForPractice ? "enable" : "disable", NumCurrentVotes, NumRequiredVotes);
690 pSelf->SendChatTeam(Team, pText: aBuf);
691
692 if(NumCurrentVotes >= NumRequiredVotes)
693 {
694 Teams.SetPractice(Team, Enabled: true);
695 pSelf->SendChatTeam(Team, pText: "Practice mode enabled for your team, happy practicing!");
696
697 char aPracticeCommands[256];
698 mem_zero(block: aPracticeCommands, size: sizeof(aPracticeCommands));
699 str_append(dst&: aPracticeCommands, src: "Available practice commands: ");
700 for(const IConsole::CCommandInfo *pCmd = pSelf->Console()->FirstCommandInfo(AccessLevel: IConsole::ACCESS_LEVEL_USER, Flagmask: CMDFLAG_PRACTICE);
701 pCmd; pCmd = pCmd->NextCommandInfo(AccessLevel: IConsole::ACCESS_LEVEL_USER, FlagMask: CMDFLAG_PRACTICE))
702 {
703 char aCommand[64];
704
705 str_format(buffer: aCommand, buffer_size: sizeof(aCommand), format: "/%s%s", pCmd->m_pName, pCmd->NextCommandInfo(AccessLevel: IConsole::ACCESS_LEVEL_USER, FlagMask: CMDFLAG_PRACTICE) ? ", " : "");
706 str_append(dst&: aPracticeCommands, src: aCommand);
707 }
708 pSelf->SendChatTeam(Team, pText: aPracticeCommands);
709 }
710}
711
712void CGameContext::ConSwap(IConsole::IResult *pResult, void *pUserData)
713{
714 CGameContext *pSelf = (CGameContext *)pUserData;
715 const char *pName = pResult->GetString(Index: 0);
716
717 if(!CheckClientId(ClientId: pResult->m_ClientId))
718 return;
719
720 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
721 if(!pPlayer)
722 return;
723
724 if(!g_Config.m_SvSwap)
725 {
726 pSelf->Console()->Print(
727 Level: IConsole::OUTPUT_LEVEL_STANDARD,
728 pFrom: "chatresp",
729 pStr: "Swap is disabled on this server.");
730 return;
731 }
732
733 CGameTeams &Teams = pSelf->m_pController->Teams();
734
735 int Team = Teams.m_Core.Team(ClientId: pResult->m_ClientId);
736
737 if(Team < TEAM_FLOCK || Team >= TEAM_SUPER)
738 {
739 pSelf->Console()->Print(
740 Level: IConsole::OUTPUT_LEVEL_STANDARD,
741 pFrom: "chatresp",
742 pStr: "Join a team to use swap feature, which means you can swap positions with each other.");
743 return;
744 }
745
746 int TargetClientId = -1;
747 if(pResult->NumArguments() == 1)
748 {
749 for(int i = 0; i < MAX_CLIENTS; i++)
750 {
751 if(pSelf->m_apPlayers[i] && !str_comp(a: pName, b: pSelf->Server()->ClientName(ClientId: i)))
752 {
753 TargetClientId = i;
754 break;
755 }
756 }
757 }
758 else
759 {
760 int TeamSize = 1;
761 for(int i = 0; i < MAX_CLIENTS; i++)
762 {
763 if(pSelf->m_apPlayers[i] && Teams.m_Core.Team(ClientId: i) == Team && i != pResult->m_ClientId)
764 {
765 TargetClientId = i;
766 TeamSize++;
767 }
768 }
769 if(TeamSize != 2)
770 TargetClientId = -1;
771 }
772
773 if(TargetClientId < 0)
774 {
775 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Player not found");
776 return;
777 }
778
779 if(TargetClientId == pResult->m_ClientId)
780 {
781 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Can't swap with yourself");
782 return;
783 }
784
785 int TargetTeam = Teams.m_Core.Team(ClientId: TargetClientId);
786 if(TargetTeam != Team)
787 {
788 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Player is on a different team");
789 return;
790 }
791
792 CPlayer *pSwapPlayer = pSelf->m_apPlayers[TargetClientId];
793 if((Team == TEAM_FLOCK || Teams.TeamFlock(Team)) && g_Config.m_SvTeam != 3)
794 {
795 CCharacter *pChr = pPlayer->GetCharacter();
796 CCharacter *pSwapChr = pSwapPlayer->GetCharacter();
797 if(!pChr || !pSwapChr || pChr->m_DDRaceState != DDRACE_STARTED || pSwapChr->m_DDRaceState != DDRACE_STARTED)
798 {
799 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "You and other player need to have started the map");
800 return;
801 }
802 }
803 else if(!Teams.IsStarted(Team) && !Teams.TeamFlock(Team))
804 {
805 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Need to have started the map to swap with a player.");
806 return;
807 }
808 if(pSelf->m_World.m_Core.m_apCharacters[pResult->m_ClientId] == nullptr || pSelf->m_World.m_Core.m_apCharacters[TargetClientId] == nullptr)
809 {
810 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "You and the other player must not be paused.");
811 return;
812 }
813
814 bool SwapPending = pSwapPlayer->m_SwapTargetsClientId != pResult->m_ClientId;
815 if(SwapPending)
816 {
817 if(pSelf->ProcessSpamProtection(ClientId: pResult->m_ClientId))
818 return;
819
820 Teams.RequestTeamSwap(pPlayer, pTargetPlayer: pSwapPlayer, Team);
821 return;
822 }
823
824 Teams.SwapTeamCharacters(pPrimaryPlayer: pPlayer, pTargetPlayer: pSwapPlayer, Team);
825}
826
827void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData)
828{
829 CGameContext *pSelf = (CGameContext *)pUserData;
830 if(!CheckClientId(ClientId: pResult->m_ClientId))
831 return;
832
833 if(!g_Config.m_SvSaveGames)
834 {
835 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Save-function is disabled on this server");
836 return;
837 }
838
839 const char *pCode = "";
840 if(pResult->NumArguments() > 0)
841 pCode = pResult->GetString(Index: 0);
842
843 pSelf->Score()->SaveTeam(ClientId: pResult->m_ClientId, pCode, pServer: g_Config.m_SvSqlServerName);
844}
845
846void CGameContext::ConLoad(IConsole::IResult *pResult, void *pUserData)
847{
848 CGameContext *pSelf = (CGameContext *)pUserData;
849 if(!CheckClientId(ClientId: pResult->m_ClientId))
850 return;
851
852 if(!g_Config.m_SvSaveGames)
853 {
854 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Save-function is disabled on this server");
855 return;
856 }
857
858 if(pResult->NumArguments() > 0)
859 pSelf->Score()->LoadTeam(pCode: pResult->GetString(Index: 0), ClientId: pResult->m_ClientId);
860 else
861 pSelf->Score()->GetSaves(ClientId: pResult->m_ClientId);
862}
863
864void CGameContext::ConTeamRank(IConsole::IResult *pResult, void *pUserData)
865{
866 CGameContext *pSelf = (CGameContext *)pUserData;
867 if(!CheckClientId(ClientId: pResult->m_ClientId))
868 return;
869
870 if(pResult->NumArguments() > 0)
871 {
872 if(!g_Config.m_SvHideScore)
873 pSelf->Score()->ShowTeamRank(ClientId: pResult->m_ClientId, pName: pResult->GetString(Index: 0));
874 else
875 pSelf->Console()->Print(
876 Level: IConsole::OUTPUT_LEVEL_STANDARD,
877 pFrom: "chatresp",
878 pStr: "Showing the team rank of other players is not allowed on this server.");
879 }
880 else
881 pSelf->Score()->ShowTeamRank(ClientId: pResult->m_ClientId,
882 pName: pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
883}
884
885void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData)
886{
887 CGameContext *pSelf = (CGameContext *)pUserData;
888 if(!CheckClientId(ClientId: pResult->m_ClientId))
889 return;
890
891 if(pResult->NumArguments() > 0)
892 {
893 if(!g_Config.m_SvHideScore)
894 pSelf->Score()->ShowRank(ClientId: pResult->m_ClientId, pName: pResult->GetString(Index: 0));
895 else
896 pSelf->Console()->Print(
897 Level: IConsole::OUTPUT_LEVEL_STANDARD,
898 pFrom: "chatresp",
899 pStr: "Showing the rank of other players is not allowed on this server.");
900 }
901 else
902 pSelf->Score()->ShowRank(ClientId: pResult->m_ClientId,
903 pName: pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
904}
905
906void CGameContext::ConLock(IConsole::IResult *pResult, void *pUserData)
907{
908 CGameContext *pSelf = (CGameContext *)pUserData;
909 if(!CheckClientId(ClientId: pResult->m_ClientId))
910 return;
911
912 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
913 {
914 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
915 pStr: "Teams are disabled");
916 return;
917 }
918
919 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
920
921 bool Lock = pSelf->m_pController->Teams().TeamLocked(Team);
922
923 if(pResult->NumArguments() > 0)
924 Lock = !pResult->GetInteger(Index: 0);
925
926 if(Team <= TEAM_FLOCK || Team >= TEAM_SUPER)
927 {
928 pSelf->Console()->Print(
929 Level: IConsole::OUTPUT_LEVEL_STANDARD,
930 pFrom: "chatresp",
931 pStr: "This team can't be locked");
932 return;
933 }
934
935 if(pSelf->ProcessSpamProtection(ClientId: pResult->m_ClientId, RespectChatInitialDelay: false))
936 return;
937
938 char aBuf[512];
939 if(Lock)
940 {
941 pSelf->UnlockTeam(ClientId: pResult->m_ClientId, Team);
942 }
943 else
944 {
945 pSelf->m_pController->Teams().SetTeamLock(Team, Lock: true);
946
947 if(pSelf->m_pController->Teams().TeamFlock(Team))
948 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' locked your team.", pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
949 else
950 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' locked your team. After the race starts, killing will kill everyone in your team.", pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
951 pSelf->SendChatTeam(Team, pText: aBuf);
952 }
953}
954
955void CGameContext::ConUnlock(IConsole::IResult *pResult, void *pUserData)
956{
957 CGameContext *pSelf = (CGameContext *)pUserData;
958 if(!CheckClientId(ClientId: pResult->m_ClientId))
959 return;
960
961 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
962 {
963 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
964 pStr: "Teams are disabled");
965 return;
966 }
967
968 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
969
970 if(Team <= TEAM_FLOCK || Team >= TEAM_SUPER)
971 return;
972
973 if(pSelf->ProcessSpamProtection(ClientId: pResult->m_ClientId, RespectChatInitialDelay: false))
974 return;
975
976 pSelf->UnlockTeam(ClientId: pResult->m_ClientId, Team);
977}
978
979void CGameContext::UnlockTeam(int ClientId, int Team) const
980{
981 m_pController->Teams().SetTeamLock(Team, Lock: false);
982
983 char aBuf[512];
984 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' unlocked your team.", Server()->ClientName(ClientId));
985 SendChatTeam(Team, pText: aBuf);
986}
987
988void CGameContext::AttemptJoinTeam(int ClientId, int Team)
989{
990 CPlayer *pPlayer = m_apPlayers[ClientId];
991 if(!pPlayer)
992 return;
993
994 if(m_VoteCloseTime && m_VoteCreator == ClientId && (IsKickVote() || IsSpecVote()))
995 {
996 Console()->Print(
997 Level: IConsole::OUTPUT_LEVEL_STANDARD,
998 pFrom: "chatresp",
999 pStr: "You are running a vote please try again after the vote is done!");
1000 return;
1001 }
1002 else if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
1003 {
1004 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
1005 pStr: "Teams are disabled");
1006 return;
1007 }
1008 else if(g_Config.m_SvTeam == SV_TEAM_MANDATORY && Team == 0 && pPlayer->GetCharacter() && pPlayer->GetCharacter()->m_LastStartWarning < Server()->Tick() - 3 * Server()->TickSpeed())
1009 {
1010 Console()->Print(
1011 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1012 pFrom: "chatresp",
1013 pStr: "You must join a team and play with somebody or else you can\'t play");
1014 pPlayer->GetCharacter()->m_LastStartWarning = Server()->Tick();
1015 }
1016
1017 if(pPlayer->GetCharacter() == 0)
1018 {
1019 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
1020 pStr: "You can't change teams while you are dead/a spectator.");
1021 }
1022 else
1023 {
1024 if(Team < 0 || Team >= MAX_CLIENTS)
1025 Team = m_pController->Teams().GetFirstEmptyTeam();
1026
1027 if(pPlayer->m_Last_Team + (int64_t)Server()->TickSpeed() * g_Config.m_SvTeamChangeDelay > Server()->Tick())
1028 {
1029 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
1030 pStr: "You can\'t change teams that fast!");
1031 }
1032 else if(Team > 0 && Team < MAX_CLIENTS && m_pController->Teams().TeamLocked(Team) && !m_pController->Teams().IsInvited(Team, ClientId))
1033 {
1034 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
1035 pStr: g_Config.m_SvInvite ?
1036 "This team is locked using /lock. Only members of the team can unlock it using /lock." :
1037 "This team is locked using /lock. Only members of the team can invite you or unlock it using /lock.");
1038 }
1039 else if(Team > 0 && Team < MAX_CLIENTS && m_pController->Teams().Count(Team) >= g_Config.m_SvMaxTeamSize && !m_pController->Teams().TeamFlock(Team))
1040 {
1041 char aBuf[512];
1042 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "This team already has the maximum allowed size of %d players", g_Config.m_SvMaxTeamSize);
1043 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
1044 }
1045 else if(const char *pError = m_pController->Teams().SetCharacterTeam(ClientId: pPlayer->GetCid(), Team))
1046 {
1047 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: pError);
1048 }
1049 else
1050 {
1051 char aBuf[512];
1052 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' joined team %d",
1053 Server()->ClientName(ClientId: pPlayer->GetCid()),
1054 Team);
1055 SendChat(ClientId: -1, Team: CGameContext::CHAT_ALL, pText: aBuf);
1056 pPlayer->m_Last_Team = Server()->Tick();
1057
1058 if(m_pController->Teams().IsPractice(Team))
1059 SendChatTarget(To: pPlayer->GetCid(), pText: "Practice mode enabled for your team, happy practicing!");
1060
1061 if(m_pController->Teams().TeamFlock(Team))
1062 SendChatTarget(To: pPlayer->GetCid(), pText: "Team 0 mode enabled for your team. This will make your team behave like team 0.");
1063 }
1064 }
1065}
1066
1067void CGameContext::ConInvite(IConsole::IResult *pResult, void *pUserData)
1068{
1069 CGameContext *pSelf = (CGameContext *)pUserData;
1070 auto *pController = pSelf->m_pController;
1071 const char *pName = pResult->GetString(Index: 0);
1072
1073 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
1074 {
1075 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
1076 pStr: "Teams are disabled");
1077 return;
1078 }
1079
1080 if(!g_Config.m_SvInvite)
1081 {
1082 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Invites are disabled");
1083 return;
1084 }
1085
1086 int Team = pController->Teams().m_Core.Team(ClientId: pResult->m_ClientId);
1087 if(Team > TEAM_FLOCK && Team < TEAM_SUPER)
1088 {
1089 int Target = -1;
1090 for(int i = 0; i < MAX_CLIENTS; i++)
1091 {
1092 if(!str_comp(a: pName, b: pSelf->Server()->ClientName(ClientId: i)))
1093 {
1094 Target = i;
1095 break;
1096 }
1097 }
1098
1099 if(Target < 0)
1100 {
1101 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Player not found");
1102 return;
1103 }
1104
1105 if(pController->Teams().IsInvited(Team, ClientId: Target))
1106 {
1107 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Player already invited");
1108 return;
1109 }
1110
1111 if(pSelf->m_apPlayers[pResult->m_ClientId] && pSelf->m_apPlayers[pResult->m_ClientId]->m_LastInvited + g_Config.m_SvInviteFrequency * pSelf->Server()->TickSpeed() > pSelf->Server()->Tick())
1112 {
1113 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Can't invite this quickly");
1114 return;
1115 }
1116
1117 pController->Teams().SetClientInvited(Team, ClientId: Target, Invited: true);
1118 pSelf->m_apPlayers[pResult->m_ClientId]->m_LastInvited = pSelf->Server()->Tick();
1119
1120 char aBuf[512];
1121 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' invited you to team %d. Use /team %d to join.", pSelf->Server()->ClientName(ClientId: pResult->m_ClientId), Team, Team);
1122 pSelf->SendChatTarget(To: Target, pText: aBuf);
1123
1124 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' invited '%s' to your team.", pSelf->Server()->ClientName(ClientId: pResult->m_ClientId), pSelf->Server()->ClientName(ClientId: Target));
1125 pSelf->SendChatTeam(Team, pText: aBuf);
1126 }
1127 else
1128 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Can't invite players to this team");
1129}
1130
1131void CGameContext::ConTeam0Mode(IConsole::IResult *pResult, void *pUserData)
1132{
1133 CGameContext *pSelf = (CGameContext *)pUserData;
1134 auto *pController = pSelf->m_pController;
1135
1136 if(!CheckClientId(ClientId: pResult->m_ClientId))
1137 return;
1138
1139 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO || g_Config.m_SvTeam == SV_TEAM_MANDATORY)
1140 {
1141 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
1142 pStr: "Team mode change disabled");
1143 return;
1144 }
1145
1146 if(!g_Config.m_SvTeam0Mode)
1147 {
1148 pSelf->Console()->Print(
1149 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1150 pFrom: "chatresp",
1151 pStr: "Team mode change is disabled on this server.");
1152 return;
1153 }
1154
1155 int Team = pController->Teams().m_Core.Team(ClientId: pResult->m_ClientId);
1156 bool Mode = pController->Teams().TeamFlock(Team);
1157
1158 if(Team <= TEAM_FLOCK || Team >= TEAM_SUPER)
1159 {
1160 pSelf->Console()->Print(
1161 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1162 pFrom: "chatresp",
1163 pStr: "This team can't have the mode changed");
1164 return;
1165 }
1166
1167 if(pController->Teams().GetTeamState(Team) != CGameTeams::TEAMSTATE_OPEN)
1168 {
1169 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Team mode can't be changed while racing");
1170 return;
1171 }
1172
1173 if(pResult->NumArguments() > 0)
1174 Mode = !pResult->GetInteger(Index: 0);
1175
1176 if(pSelf->ProcessSpamProtection(ClientId: pResult->m_ClientId, RespectChatInitialDelay: false))
1177 return;
1178
1179 char aBuf[512];
1180 if(Mode)
1181 {
1182 if(pController->Teams().Count(Team) > g_Config.m_SvMaxTeamSize)
1183 {
1184 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Can't disable team 0 mode. This team exceeds the maximum allowed size of %d players for regular team", g_Config.m_SvMaxTeamSize);
1185 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: aBuf);
1186 }
1187 else
1188 {
1189 pController->Teams().SetTeamFlock(Team, Mode: false);
1190
1191 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' disabled team 0 mode.", pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
1192 pSelf->SendChatTeam(Team, pText: aBuf);
1193 }
1194 }
1195 else
1196 {
1197 if(pController->Teams().IsPractice(Team))
1198 {
1199 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Can't enable team 0 mode with practice mode on.");
1200 }
1201 else
1202 {
1203 pController->Teams().SetTeamFlock(Team, Mode: true);
1204
1205 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' enabled team 0 mode. This will make your team behave like team 0.", pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
1206 pSelf->SendChatTeam(Team, pText: aBuf);
1207 }
1208 }
1209}
1210
1211void CGameContext::ConTeam(IConsole::IResult *pResult, void *pUserData)
1212{
1213 CGameContext *pSelf = (CGameContext *)pUserData;
1214 if(!CheckClientId(ClientId: pResult->m_ClientId))
1215 return;
1216
1217 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1218 if(!pPlayer)
1219 return;
1220
1221 if(pResult->NumArguments() > 0)
1222 {
1223 pSelf->AttemptJoinTeam(ClientId: pResult->m_ClientId, Team: pResult->GetInteger(Index: 0));
1224 }
1225 else
1226 {
1227 char aBuf[512];
1228 if(!pPlayer->IsPlaying())
1229 {
1230 pSelf->Console()->Print(
1231 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1232 pFrom: "chatresp",
1233 pStr: "You can't check your team while you are dead/a spectator.");
1234 }
1235 else
1236 {
1237 str_format(
1238 buffer: aBuf,
1239 buffer_size: sizeof(aBuf),
1240 format: "You are in team %d",
1241 pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId));
1242 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
1243 }
1244 }
1245}
1246
1247void CGameContext::ConJoin(IConsole::IResult *pResult, void *pUserData)
1248{
1249 CGameContext *pSelf = (CGameContext *)pUserData;
1250 if(!CheckClientId(ClientId: pResult->m_ClientId))
1251 return;
1252
1253 int Target = -1;
1254 const char *pName = pResult->GetString(Index: 0);
1255 for(int i = 0; i < MAX_CLIENTS; i++)
1256 {
1257 if(!str_comp(a: pName, b: pSelf->Server()->ClientName(ClientId: i)))
1258 {
1259 Target = i;
1260 break;
1261 }
1262 }
1263
1264 if(Target == -1)
1265 {
1266 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Player not found");
1267 return;
1268 }
1269
1270 int Team = pSelf->GetDDRaceTeam(ClientId: Target);
1271 if(pSelf->ProcessSpamProtection(ClientId: pResult->m_ClientId, RespectChatInitialDelay: false))
1272 return;
1273
1274 pSelf->AttemptJoinTeam(ClientId: pResult->m_ClientId, Team);
1275}
1276
1277void CGameContext::ConMe(IConsole::IResult *pResult, void *pUserData)
1278{
1279 CGameContext *pSelf = (CGameContext *)pUserData;
1280 if(!CheckClientId(ClientId: pResult->m_ClientId))
1281 return;
1282
1283 char aBuf[256 + 24];
1284
1285 str_format(buffer: aBuf, buffer_size: 256 + 24, format: "'%s' %s",
1286 pSelf->Server()->ClientName(ClientId: pResult->m_ClientId),
1287 pResult->GetString(Index: 0));
1288 if(g_Config.m_SvSlashMe)
1289 pSelf->SendChat(ClientId: -2, Team: CGameContext::CHAT_ALL, pText: aBuf, SpamProtectionClientId: pResult->m_ClientId);
1290 else
1291 pSelf->Console()->Print(
1292 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1293 pFrom: "chatresp",
1294 pStr: "/me is disabled on this server");
1295}
1296
1297void CGameContext::ConConverse(IConsole::IResult *pResult, void *pUserData)
1298{
1299 // This will never be called
1300}
1301
1302void CGameContext::ConWhisper(IConsole::IResult *pResult, void *pUserData)
1303{
1304 // This will never be called
1305}
1306
1307void CGameContext::ConSetEyeEmote(IConsole::IResult *pResult,
1308 void *pUserData)
1309{
1310 CGameContext *pSelf = (CGameContext *)pUserData;
1311 if(!CheckClientId(ClientId: pResult->m_ClientId))
1312 return;
1313
1314 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1315 if(!pPlayer)
1316 return;
1317 if(pResult->NumArguments() == 0)
1318 {
1319 pSelf->Console()->Print(
1320 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1321 pFrom: "chatresp",
1322 pStr: (pPlayer->m_EyeEmoteEnabled) ?
1323 "You can now use the preset eye emotes." :
1324 "You don't have any eye emotes, remember to bind some.");
1325 return;
1326 }
1327 else if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "on") == 0)
1328 pPlayer->m_EyeEmoteEnabled = true;
1329 else if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "off") == 0)
1330 pPlayer->m_EyeEmoteEnabled = false;
1331 else if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "toggle") == 0)
1332 pPlayer->m_EyeEmoteEnabled = !pPlayer->m_EyeEmoteEnabled;
1333 pSelf->Console()->Print(
1334 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1335 pFrom: "chatresp",
1336 pStr: (pPlayer->m_EyeEmoteEnabled) ?
1337 "You can now use the preset eye emotes." :
1338 "You don't have any eye emotes, remember to bind some.");
1339}
1340
1341void CGameContext::ConEyeEmote(IConsole::IResult *pResult, void *pUserData)
1342{
1343 CGameContext *pSelf = (CGameContext *)pUserData;
1344 if(g_Config.m_SvEmotionalTees == -1)
1345 {
1346 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
1347 pStr: "Emotes are disabled.");
1348 return;
1349 }
1350
1351 if(!CheckClientId(ClientId: pResult->m_ClientId))
1352 return;
1353
1354 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1355 if(!pPlayer)
1356 return;
1357
1358 if(pResult->NumArguments() == 0)
1359 {
1360 pSelf->Console()->Print(
1361 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1362 pFrom: "chatresp",
1363 pStr: "Emote commands are: /emote surprise /emote blink /emote close /emote angry /emote happy /emote pain /emote normal");
1364 pSelf->Console()->Print(
1365 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1366 pFrom: "chatresp",
1367 pStr: "Example: /emote surprise 10 for 10 seconds or /emote surprise (default 1 second)");
1368 }
1369 else
1370 {
1371 if(!pPlayer->CanOverrideDefaultEmote())
1372 return;
1373
1374 int EmoteType = 0;
1375 if(!str_comp_nocase(a: pResult->GetString(Index: 0), b: "angry"))
1376 EmoteType = EMOTE_ANGRY;
1377 else if(!str_comp_nocase(a: pResult->GetString(Index: 0), b: "blink"))
1378 EmoteType = EMOTE_BLINK;
1379 else if(!str_comp_nocase(a: pResult->GetString(Index: 0), b: "close"))
1380 EmoteType = EMOTE_BLINK;
1381 else if(!str_comp_nocase(a: pResult->GetString(Index: 0), b: "happy"))
1382 EmoteType = EMOTE_HAPPY;
1383 else if(!str_comp_nocase(a: pResult->GetString(Index: 0), b: "pain"))
1384 EmoteType = EMOTE_PAIN;
1385 else if(!str_comp_nocase(a: pResult->GetString(Index: 0), b: "surprise"))
1386 EmoteType = EMOTE_SURPRISE;
1387 else if(!str_comp_nocase(a: pResult->GetString(Index: 0), b: "normal"))
1388 EmoteType = EMOTE_NORMAL;
1389 else
1390 {
1391 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD,
1392 pFrom: "chatresp", pStr: "Unknown emote... Say /emote");
1393 return;
1394 }
1395
1396 int Duration = 1;
1397 if(pResult->NumArguments() > 1)
1398 Duration = clamp(val: pResult->GetInteger(Index: 1), lo: 1, hi: 86400);
1399
1400 pPlayer->OverrideDefaultEmote(Emote: EmoteType, Tick: pSelf->Server()->Tick() + Duration * pSelf->Server()->TickSpeed());
1401 }
1402}
1403
1404void CGameContext::ConNinjaJetpack(IConsole::IResult *pResult, void *pUserData)
1405{
1406 CGameContext *pSelf = (CGameContext *)pUserData;
1407 if(!CheckClientId(ClientId: pResult->m_ClientId))
1408 return;
1409
1410 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1411 if(!pPlayer)
1412 return;
1413 if(pResult->NumArguments())
1414 pPlayer->m_NinjaJetpack = pResult->GetInteger(Index: 0);
1415 else
1416 pPlayer->m_NinjaJetpack = !pPlayer->m_NinjaJetpack;
1417}
1418
1419void CGameContext::ConShowOthers(IConsole::IResult *pResult, void *pUserData)
1420{
1421 CGameContext *pSelf = (CGameContext *)pUserData;
1422 if(!CheckClientId(ClientId: pResult->m_ClientId))
1423 return;
1424
1425 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1426 if(!pPlayer)
1427 return;
1428 if(g_Config.m_SvShowOthers)
1429 {
1430 if(pResult->NumArguments())
1431 pPlayer->m_ShowOthers = pResult->GetInteger(Index: 0);
1432 else
1433 pPlayer->m_ShowOthers = !pPlayer->m_ShowOthers;
1434 }
1435 else
1436 pSelf->Console()->Print(
1437 Level: IConsole::OUTPUT_LEVEL_STANDARD,
1438 pFrom: "chatresp",
1439 pStr: "Showing players from other teams is disabled");
1440}
1441
1442void CGameContext::ConShowAll(IConsole::IResult *pResult, void *pUserData)
1443{
1444 CGameContext *pSelf = (CGameContext *)pUserData;
1445 if(!CheckClientId(ClientId: pResult->m_ClientId))
1446 return;
1447
1448 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1449 if(!pPlayer)
1450 return;
1451
1452 if(pResult->NumArguments())
1453 {
1454 if(pPlayer->m_ShowAll == (bool)pResult->GetInteger(Index: 0))
1455 return;
1456
1457 pPlayer->m_ShowAll = pResult->GetInteger(Index: 0);
1458 }
1459 else
1460 {
1461 pPlayer->m_ShowAll = !pPlayer->m_ShowAll;
1462 }
1463
1464 if(pPlayer->m_ShowAll)
1465 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "You will now see all tees on this server, no matter the distance");
1466 else
1467 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "You will no longer see all tees on this server");
1468}
1469
1470void CGameContext::ConSpecTeam(IConsole::IResult *pResult, void *pUserData)
1471{
1472 CGameContext *pSelf = (CGameContext *)pUserData;
1473 if(!CheckClientId(ClientId: pResult->m_ClientId))
1474 return;
1475
1476 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1477 if(!pPlayer)
1478 return;
1479
1480 if(pResult->NumArguments())
1481 pPlayer->m_SpecTeam = pResult->GetInteger(Index: 0);
1482 else
1483 pPlayer->m_SpecTeam = !pPlayer->m_SpecTeam;
1484}
1485
1486bool CheckClientId(int ClientId)
1487{
1488 return ClientId >= 0 && ClientId < MAX_CLIENTS;
1489}
1490
1491void CGameContext::ConSayTime(IConsole::IResult *pResult, void *pUserData)
1492{
1493 CGameContext *pSelf = (CGameContext *)pUserData;
1494 if(!CheckClientId(ClientId: pResult->m_ClientId))
1495 return;
1496
1497 int ClientId;
1498 char aBufName[MAX_NAME_LENGTH];
1499
1500 if(pResult->NumArguments() > 0)
1501 {
1502 for(ClientId = 0; ClientId < MAX_CLIENTS; ClientId++)
1503 if(str_comp(a: pResult->GetString(Index: 0), b: pSelf->Server()->ClientName(ClientId)) == 0)
1504 break;
1505
1506 if(ClientId == MAX_CLIENTS)
1507 return;
1508
1509 str_format(buffer: aBufName, buffer_size: sizeof(aBufName), format: "%s's", pSelf->Server()->ClientName(ClientId));
1510 }
1511 else
1512 {
1513 str_copy(dst: aBufName, src: "Your", dst_size: sizeof(aBufName));
1514 ClientId = pResult->m_ClientId;
1515 }
1516
1517 CPlayer *pPlayer = pSelf->m_apPlayers[ClientId];
1518 if(!pPlayer)
1519 return;
1520 CCharacter *pChr = pPlayer->GetCharacter();
1521 if(!pChr)
1522 return;
1523 if(pChr->m_DDRaceState != DDRACE_STARTED)
1524 return;
1525
1526 char aBufTime[32];
1527 char aBuf[64];
1528 int64_t Time = (int64_t)100 * (float)(pSelf->Server()->Tick() - pChr->m_StartTime) / ((float)pSelf->Server()->TickSpeed());
1529 str_time(centisecs: Time, format: TIME_HOURS, buffer: aBufTime, buffer_size: sizeof(aBufTime));
1530 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s current race time is %s", aBufName, aBufTime);
1531 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
1532}
1533
1534void CGameContext::ConSayTimeAll(IConsole::IResult *pResult, void *pUserData)
1535{
1536 CGameContext *pSelf = (CGameContext *)pUserData;
1537 if(!CheckClientId(ClientId: pResult->m_ClientId))
1538 return;
1539
1540 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1541 if(!pPlayer)
1542 return;
1543 CCharacter *pChr = pPlayer->GetCharacter();
1544 if(!pChr)
1545 return;
1546 if(pChr->m_DDRaceState != DDRACE_STARTED)
1547 return;
1548
1549 char aBufTime[32];
1550 char aBuf[64];
1551 int64_t Time = (int64_t)100 * (float)(pSelf->Server()->Tick() - pChr->m_StartTime) / ((float)pSelf->Server()->TickSpeed());
1552 const char *pName = pSelf->Server()->ClientName(ClientId: pResult->m_ClientId);
1553 str_time(centisecs: Time, format: TIME_HOURS, buffer: aBufTime, buffer_size: sizeof(aBufTime));
1554 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s\'s current race time is %s", pName, aBufTime);
1555 pSelf->SendChat(ClientId: -1, Team: CGameContext::CHAT_ALL, pText: aBuf, SpamProtectionClientId: pResult->m_ClientId);
1556}
1557
1558void CGameContext::ConTime(IConsole::IResult *pResult, void *pUserData)
1559{
1560 CGameContext *pSelf = (CGameContext *)pUserData;
1561 if(!CheckClientId(ClientId: pResult->m_ClientId))
1562 return;
1563
1564 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1565 if(!pPlayer)
1566 return;
1567 CCharacter *pChr = pPlayer->GetCharacter();
1568 if(!pChr)
1569 return;
1570
1571 char aBufTime[32];
1572 char aBuf[64];
1573 int64_t Time = (int64_t)100 * (float)(pSelf->Server()->Tick() - pChr->m_StartTime) / ((float)pSelf->Server()->TickSpeed());
1574 str_time(centisecs: Time, format: TIME_HOURS, buffer: aBufTime, buffer_size: sizeof(aBufTime));
1575 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Your time is %s", aBufTime);
1576 pSelf->SendBroadcast(pText: aBuf, ClientId: pResult->m_ClientId);
1577}
1578
1579static const char s_aaMsg[4][128] = {"game/round timer.", "broadcast.", "both game/round timer and broadcast.", "racetime."};
1580
1581void CGameContext::ConSetTimerType(IConsole::IResult *pResult, void *pUserData)
1582{
1583 CGameContext *pSelf = (CGameContext *)pUserData;
1584
1585 if(!CheckClientId(ClientId: pResult->m_ClientId))
1586 return;
1587
1588 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1589 if(!pPlayer)
1590 return;
1591
1592 char aBuf[128];
1593
1594 if(pResult->NumArguments() > 0)
1595 {
1596 int OldType = pPlayer->m_TimerType;
1597 bool Result = false;
1598
1599 if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "default") == 0)
1600 Result = pPlayer->SetTimerType(CPlayer::TIMERTYPE_DEFAULT);
1601 else if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "gametimer") == 0)
1602 Result = pPlayer->SetTimerType(CPlayer::TIMERTYPE_GAMETIMER);
1603 else if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "broadcast") == 0)
1604 Result = pPlayer->SetTimerType(CPlayer::TIMERTYPE_BROADCAST);
1605 else if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "both") == 0)
1606 Result = pPlayer->SetTimerType(CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST);
1607 else if(str_comp_nocase(a: pResult->GetString(Index: 0), b: "none") == 0)
1608 Result = pPlayer->SetTimerType(CPlayer::TIMERTYPE_NONE);
1609 else
1610 {
1611 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Unknown parameter. Accepted values: default, gametimer, broadcast, both, none");
1612 return;
1613 }
1614
1615 if(!Result)
1616 {
1617 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: "Selected timertype is not supported by your client");
1618 return;
1619 }
1620
1621 if((OldType == CPlayer::TIMERTYPE_BROADCAST || OldType == CPlayer::TIMERTYPE_GAMETIMER_AND_BROADCAST) && (pPlayer->m_TimerType == CPlayer::TIMERTYPE_GAMETIMER || pPlayer->m_TimerType == CPlayer::TIMERTYPE_NONE))
1622 pSelf->SendBroadcast(pText: "", ClientId: pResult->m_ClientId);
1623 }
1624
1625 if(pPlayer->m_TimerType <= CPlayer::TIMERTYPE_SIXUP && pPlayer->m_TimerType >= CPlayer::TIMERTYPE_GAMETIMER)
1626 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Timer is displayed in %s", s_aaMsg[pPlayer->m_TimerType]);
1627 else if(pPlayer->m_TimerType == CPlayer::TIMERTYPE_NONE)
1628 str_copy(dst&: aBuf, src: "Timer isn't displayed.");
1629
1630 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp", pStr: aBuf);
1631}
1632
1633void CGameContext::ConRescue(IConsole::IResult *pResult, void *pUserData)
1634{
1635 CGameContext *pSelf = (CGameContext *)pUserData;
1636 if(!CheckClientId(ClientId: pResult->m_ClientId))
1637 return;
1638 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1639 if(!pPlayer)
1640 return;
1641 CCharacter *pChr = pPlayer->GetCharacter();
1642 if(!pChr)
1643 return;
1644
1645 CGameTeams &Teams = pSelf->m_pController->Teams();
1646 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1647 if(!g_Config.m_SvRescue && !Teams.IsPractice(Team))
1648 {
1649 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "Rescue is not enabled on this server and you're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1650 return;
1651 }
1652
1653 pChr->Rescue();
1654 pChr->UnFreeze();
1655}
1656
1657void CGameContext::ConTeleTo(IConsole::IResult *pResult, void *pUserData)
1658{
1659 CGameContext *pSelf = (CGameContext *)pUserData;
1660 if(!CheckClientId(ClientId: pResult->m_ClientId))
1661 return;
1662 CPlayer *pCallingPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1663 if(!pCallingPlayer)
1664 return;
1665 CCharacter *pCallingCharacter = pCallingPlayer->GetCharacter();
1666 if(!pCallingCharacter)
1667 return;
1668
1669 CGameTeams &Teams = pSelf->m_pController->Teams();
1670 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1671 if(!Teams.IsPractice(Team))
1672 {
1673 pSelf->SendChatTarget(To: pCallingPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1674 return;
1675 }
1676
1677 vec2 Pos = {};
1678
1679 if(pResult->NumArguments() == 0)
1680 {
1681 // Set calling tee's position to the origin of its spectating viewport
1682 Pos = pCallingPlayer->m_ViewPos;
1683 }
1684 else
1685 {
1686 // Search for player with this name
1687 int ClientId;
1688 for(ClientId = 0; ClientId < MAX_CLIENTS; ClientId++)
1689 {
1690 if(str_comp(a: pResult->GetString(Index: 0), b: pSelf->Server()->ClientName(ClientId)) == 0)
1691 break;
1692 }
1693 if(ClientId == MAX_CLIENTS)
1694 {
1695 pSelf->SendChatTarget(To: pCallingPlayer->GetCid(), pText: "No player with this name found.");
1696 return;
1697 }
1698
1699 CPlayer *pDestPlayer = pSelf->m_apPlayers[ClientId];
1700 if(!pDestPlayer)
1701 return;
1702 CCharacter *pDestCharacter = pDestPlayer->GetCharacter();
1703 if(!pDestCharacter)
1704 return;
1705
1706 // Set calling tee's position to that of the destination tee
1707 Pos = pDestCharacter->m_Pos;
1708 }
1709
1710 // Teleport tee
1711 pSelf->Teleport(pChr: pCallingCharacter, Pos);
1712 pCallingCharacter->ResetJumps();
1713 pCallingCharacter->UnFreeze();
1714 pCallingCharacter->ResetVelocity();
1715 pCallingPlayer->m_LastTeleTee.Save(pchr: pCallingCharacter);
1716}
1717
1718void CGameContext::ConTeleXY(IConsole::IResult *pResult, void *pUserData)
1719{
1720 CGameContext *pSelf = (CGameContext *)pUserData;
1721 if(!CheckClientId(ClientId: pResult->m_ClientId))
1722 return;
1723 CPlayer *pCallingPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1724 if(!pCallingPlayer)
1725 return;
1726 CCharacter *pCallingCharacter = pCallingPlayer->GetCharacter();
1727 if(!pCallingCharacter)
1728 return;
1729
1730 CGameTeams &Teams = pSelf->m_pController->Teams();
1731 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1732 if(!Teams.IsPractice(Team))
1733 {
1734 pSelf->SendChatTarget(To: pCallingPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1735 return;
1736 }
1737
1738 vec2 Pos = {};
1739
1740 if(pResult->NumArguments() != 2)
1741 {
1742 pSelf->SendChatTarget(To: pCallingPlayer->GetCid(), pText: "Can't recognize specified arguments. Usage: /tpxy x y, e.g. /tpxy 9 3.");
1743 return;
1744 }
1745 else
1746 {
1747 float BaseX = 0.f, BaseY = 0.f;
1748
1749 CMapItemLayerTilemap *pGameLayer = pSelf->m_Layers.GameLayer();
1750 constexpr float OuterKillTileBoundaryDistance = 201 * 32.f;
1751 float MapWidth = (pGameLayer->m_Width * 32) + (OuterKillTileBoundaryDistance * 2.f), MapHeight = (pGameLayer->m_Height * 32) + (OuterKillTileBoundaryDistance * 2.f);
1752
1753 const auto DetermineCoordinateRelativity = [](const char *pInString, const float AbsoluteDefaultValue, float &OutFloat) -> bool {
1754 // mode 0 = abs, 1 = sub, 2 = add
1755
1756 // Relative?
1757 const char *pStrDelta = str_startswith(str: pInString, prefix: "~");
1758
1759 float d;
1760 if(!str_tofloat(str: pStrDelta ? pStrDelta : pInString, out: &d))
1761 return false;
1762
1763 // Is the number valid?
1764 if(std::isnan(x: d) || std::isinf(x: d))
1765 return false;
1766
1767 // Convert our gleaned 'display' coordinate to an actual map coordinate
1768 d *= 32.f;
1769
1770 OutFloat = (pStrDelta ? AbsoluteDefaultValue : 0) + d;
1771 return true;
1772 };
1773
1774 if(!DetermineCoordinateRelativity(pResult->GetString(Index: 0), pCallingPlayer->m_ViewPos.x, BaseX))
1775 {
1776 pSelf->SendChatTarget(To: pCallingPlayer->GetCid(), pText: "Invalid X coordinate.");
1777 return;
1778 }
1779 if(!DetermineCoordinateRelativity(pResult->GetString(Index: 1), pCallingPlayer->m_ViewPos.y, BaseY))
1780 {
1781 pSelf->SendChatTarget(To: pCallingPlayer->GetCid(), pText: "Invalid Y coordinate.");
1782 return;
1783 }
1784
1785 Pos = {std::clamp(val: BaseX, lo: (-OuterKillTileBoundaryDistance) + 1.f, hi: (-OuterKillTileBoundaryDistance) + MapWidth - 1.f), std::clamp(val: BaseY, lo: (-OuterKillTileBoundaryDistance) + 1.f, hi: (-OuterKillTileBoundaryDistance) + MapHeight - 1.f)};
1786 }
1787
1788 // Teleport tee
1789 pSelf->Teleport(pChr: pCallingCharacter, Pos);
1790 pCallingCharacter->ResetJumps();
1791 pCallingCharacter->UnFreeze();
1792 pCallingCharacter->ResetVelocity();
1793 pCallingPlayer->m_LastTeleTee.Save(pchr: pCallingCharacter);
1794}
1795
1796void CGameContext::ConTeleCursor(IConsole::IResult *pResult, void *pUserData)
1797{
1798 CGameContext *pSelf = (CGameContext *)pUserData;
1799 if(!CheckClientId(ClientId: pResult->m_ClientId))
1800 return;
1801 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1802 if(!pPlayer)
1803 return;
1804 CCharacter *pChr = pPlayer->GetCharacter();
1805 if(!pChr)
1806 return;
1807
1808 CGameTeams &Teams = pSelf->m_pController->Teams();
1809 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1810 if(!Teams.IsPractice(Team))
1811 {
1812 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1813 return;
1814 }
1815
1816 vec2 Pos = pPlayer->m_ViewPos;
1817 if(pResult->NumArguments() == 0 && !pPlayer->IsPaused())
1818 {
1819 Pos += vec2(pChr->Core()->m_Input.m_TargetX, pChr->Core()->m_Input.m_TargetY);
1820 }
1821 else if(pResult->NumArguments() > 0)
1822 {
1823 int ClientId;
1824 for(ClientId = 0; ClientId < MAX_CLIENTS; ClientId++)
1825 {
1826 if(str_comp(a: pResult->GetString(Index: 0), b: pSelf->Server()->ClientName(ClientId)) == 0)
1827 break;
1828 }
1829 if(ClientId == MAX_CLIENTS)
1830 {
1831 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "No player with this name found.");
1832 return;
1833 }
1834 CPlayer *pPlayerTo = pSelf->m_apPlayers[ClientId];
1835 if(!pPlayerTo)
1836 return;
1837 CCharacter *pChrTo = pPlayerTo->GetCharacter();
1838 if(!pChrTo)
1839 return;
1840 Pos = pChrTo->m_Pos;
1841 }
1842 pSelf->Teleport(pChr, Pos);
1843 pChr->ResetJumps();
1844 pChr->UnFreeze();
1845 pChr->ResetVelocity();
1846 pPlayer->m_LastTeleTee.Save(pchr: pChr);
1847}
1848
1849void CGameContext::ConLastTele(IConsole::IResult *pResult, void *pUserData)
1850{
1851 CGameContext *pSelf = (CGameContext *)pUserData;
1852 if(!CheckClientId(ClientId: pResult->m_ClientId))
1853 return;
1854 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1855 if(!pPlayer)
1856 return;
1857 CCharacter *pChr = pPlayer->GetCharacter();
1858 if(!pChr)
1859 return;
1860
1861 CGameTeams &Teams = pSelf->m_pController->Teams();
1862 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1863 if(!Teams.IsPractice(Team))
1864 {
1865 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1866 return;
1867 }
1868 if(!pPlayer->m_LastTeleTee.GetPos().x)
1869 {
1870 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "You haven't previously teleported. Use /tp before using this command.");
1871 return;
1872 }
1873 pPlayer->m_LastTeleTee.Load(pchr: pChr, Team: pChr->Team(), IsSwap: true);
1874 pPlayer->Pause(State: CPlayer::PAUSE_NONE, Force: true);
1875}
1876
1877void CGameContext::ConPracticeUnSolo(IConsole::IResult *pResult, void *pUserData)
1878{
1879 CGameContext *pSelf = (CGameContext *)pUserData;
1880 if(!CheckClientId(ClientId: pResult->m_ClientId))
1881 return;
1882 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1883 if(!pPlayer)
1884 return;
1885 CCharacter *pChr = pPlayer->GetCharacter();
1886 if(!pChr)
1887 return;
1888
1889 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
1890 {
1891 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "Command is not available on solo servers");
1892 return;
1893 }
1894
1895 CGameTeams &Teams = pSelf->m_pController->Teams();
1896 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1897 if(!Teams.IsPractice(Team))
1898 {
1899 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1900 return;
1901 }
1902
1903 pChr->SetSolo(false);
1904}
1905
1906void CGameContext::ConPracticeSolo(IConsole::IResult *pResult, void *pUserData)
1907{
1908 CGameContext *pSelf = (CGameContext *)pUserData;
1909 if(!CheckClientId(ClientId: pResult->m_ClientId))
1910 return;
1911 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1912 if(!pPlayer)
1913 return;
1914 CCharacter *pChr = pPlayer->GetCharacter();
1915 if(!pChr)
1916 return;
1917
1918 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
1919 {
1920 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "Command is not available on solo servers");
1921 return;
1922 }
1923
1924 CGameTeams &Teams = pSelf->m_pController->Teams();
1925 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1926 if(!Teams.IsPractice(Team))
1927 {
1928 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1929 return;
1930 }
1931
1932 pChr->SetSolo(true);
1933}
1934
1935void CGameContext::ConPracticeUnDeep(IConsole::IResult *pResult, void *pUserData)
1936{
1937 CGameContext *pSelf = (CGameContext *)pUserData;
1938 if(!CheckClientId(ClientId: pResult->m_ClientId))
1939 return;
1940 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1941 if(!pPlayer)
1942 return;
1943 CCharacter *pChr = pPlayer->GetCharacter();
1944 if(!pChr)
1945 return;
1946
1947 CGameTeams &Teams = pSelf->m_pController->Teams();
1948 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1949 if(!Teams.IsPractice(Team))
1950 {
1951 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1952 return;
1953 }
1954
1955 pChr->SetDeepFrozen(false);
1956 pChr->UnFreeze();
1957}
1958
1959void CGameContext::ConPracticeDeep(IConsole::IResult *pResult, void *pUserData)
1960{
1961 CGameContext *pSelf = (CGameContext *)pUserData;
1962 if(!CheckClientId(ClientId: pResult->m_ClientId))
1963 return;
1964 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1965 if(!pPlayer)
1966 return;
1967 CCharacter *pChr = pPlayer->GetCharacter();
1968 if(!pChr)
1969 return;
1970
1971 CGameTeams &Teams = pSelf->m_pController->Teams();
1972 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
1973 if(!Teams.IsPractice(Team))
1974 {
1975 pSelf->SendChatTarget(To: pPlayer->GetCid(), pText: "You're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled.");
1976 return;
1977 }
1978
1979 pChr->SetDeepFrozen(true);
1980}
1981
1982void CGameContext::ConProtectedKill(IConsole::IResult *pResult, void *pUserData)
1983{
1984 CGameContext *pSelf = (CGameContext *)pUserData;
1985 if(!CheckClientId(ClientId: pResult->m_ClientId))
1986 return;
1987 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
1988 if(!pPlayer)
1989 return;
1990 CCharacter *pChr = pPlayer->GetCharacter();
1991 if(!pChr)
1992 return;
1993
1994 int CurrTime = (pSelf->Server()->Tick() - pChr->m_StartTime) / pSelf->Server()->TickSpeed();
1995 if(g_Config.m_SvKillProtection != 0 && CurrTime >= (60 * g_Config.m_SvKillProtection) && pChr->m_DDRaceState == DDRACE_STARTED)
1996 {
1997 pPlayer->KillCharacter(Weapon: WEAPON_SELF);
1998 }
1999}
2000
2001void CGameContext::ConPoints(IConsole::IResult *pResult, void *pUserData)
2002{
2003 CGameContext *pSelf = (CGameContext *)pUserData;
2004 if(!CheckClientId(ClientId: pResult->m_ClientId))
2005 return;
2006
2007 if(pResult->NumArguments() > 0)
2008 {
2009 if(!g_Config.m_SvHideScore)
2010 pSelf->Score()->ShowPoints(ClientId: pResult->m_ClientId, pName: pResult->GetString(Index: 0));
2011 else
2012 pSelf->Console()->Print(
2013 Level: IConsole::OUTPUT_LEVEL_STANDARD,
2014 pFrom: "chatresp",
2015 pStr: "Showing the global points of other players is not allowed on this server.");
2016 }
2017 else
2018 pSelf->Score()->ShowPoints(ClientId: pResult->m_ClientId,
2019 pName: pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
2020}
2021
2022void CGameContext::ConTopPoints(IConsole::IResult *pResult, void *pUserData)
2023{
2024 CGameContext *pSelf = (CGameContext *)pUserData;
2025 if(!CheckClientId(ClientId: pResult->m_ClientId))
2026 return;
2027
2028 if(g_Config.m_SvHideScore)
2029 {
2030 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
2031 pStr: "Showing the global top points is not allowed on this server.");
2032 return;
2033 }
2034
2035 if(pResult->NumArguments() > 0)
2036 pSelf->Score()->ShowTopPoints(ClientId: pResult->m_ClientId, Offset: pResult->GetInteger(Index: 0));
2037 else
2038 pSelf->Score()->ShowTopPoints(ClientId: pResult->m_ClientId);
2039}
2040
2041void CGameContext::ConTimeCP(IConsole::IResult *pResult, void *pUserData)
2042{
2043 CGameContext *pSelf = (CGameContext *)pUserData;
2044 if(!CheckClientId(ClientId: pResult->m_ClientId))
2045 return;
2046
2047 if(g_Config.m_SvHideScore)
2048 {
2049 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "chatresp",
2050 pStr: "Showing the checkpoint times is not allowed on this server.");
2051 return;
2052 }
2053
2054 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
2055 if(!pPlayer)
2056 return;
2057
2058 const char *pName = pResult->GetString(Index: 0);
2059 pSelf->Score()->LoadPlayerTimeCp(ClientId: pResult->m_ClientId, pName);
2060}
2061