]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/clientcommands.qc
Grabber: Don't behave differently while the player is crouching. Fixes a bug
[voretournament/voretournament.git] / data / qcsrc / server / clientcommands.qc
1 entity nagger;\r
2 float readycount;\r
3 float Nagger_SendEntity(entity to, float sendflags)\r
4 {\r
5         float nags, i, f, b;\r
6         entity e;\r
7         WriteByte(MSG_ENTITY, ENT_CLIENT_NAGGER);\r
8 \r
9         nags = 0;\r
10         if(readycount)\r
11         {\r
12                 nags |= 1;\r
13                 if(to.ready == 0)\r
14                         nags |= 2;\r
15         }\r
16         if(votecalled)\r
17         {\r
18                 nags |= 4;\r
19                 if(to.vote_vote == 0)\r
20                         nags |= 8;\r
21         }\r
22         if(inWarmupStage)\r
23                 nags |= 16;\r
24 \r
25         if(sendflags & 128)\r
26                 nags |= 128;\r
27 \r
28         WriteByte(MSG_ENTITY, nags);\r
29 \r
30         if(nags & 128)\r
31         {\r
32                 WriteString(MSG_ENTITY, votecalledvote_display);\r
33         }\r
34 \r
35         if(nags & 1)\r
36         {\r
37                 for(i = 1; i <= maxclients; i += 8)\r
38                 {\r
39                         for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))\r
40                                 if(clienttype(e) != CLIENTTYPE_REAL || e.ready)\r
41                                         f |= b;\r
42                         WriteByte(MSG_ENTITY, f);\r
43                 }\r
44         }\r
45 \r
46         return TRUE;\r
47 }\r
48 void Nagger_Init()\r
49 {\r
50         Net_LinkEntity(nagger = spawn(), FALSE, 0, Nagger_SendEntity);\r
51 }\r
52 void Nagger_VoteChanged()\r
53 {\r
54         if(nagger)\r
55                 nagger.SendFlags |= 128;\r
56 }\r
57 void Nagger_VoteCountChanged()\r
58 {\r
59         if(nagger)\r
60                 nagger.SendFlags |= 1;\r
61 }\r
62 void Nagger_ReadyCounted()\r
63 {\r
64         if(nagger)\r
65                 nagger.SendFlags |= 1;\r
66 }\r
67 \r
68 void ReadyCount();\r
69 string MapVote_Suggest(string m);\r
70 \r
71 entity GetPlayer(string name)\r
72 {\r
73         float num;\r
74         entity e;\r
75         string ns;\r
76 \r
77         if(substring(name, 0, 1) == "#") {\r
78                 num = stof(substring(name, 1, 999));\r
79                 if(num >= 1 && num <= maxclients) {\r
80                         for((e = world); num > 0; --num, (e = nextent(e)))\r
81                                 ;\r
82                         //if(clienttype(e) == CLIENTTYPE_REAL)\r
83                         if(e.classname == "player")\r
84                                 return e;\r
85                 }\r
86         } else {\r
87                 ns = strdecolorize(name);\r
88                 FOR_EACH_REALPLAYER(e) {\r
89                         if(!strcasecmp(strdecolorize(e.netname), ns)) {\r
90                                 return e;\r
91                         }\r
92                 }\r
93         }\r
94         return world;\r
95 }\r
96 \r
97 //float ctf_clientcommand();\r
98 float readyrestart_happened;\r
99 .float lms_spectate_warning;\r
100 void spawnfunc_func_breakable();\r
101 \r
102 .float cmd_floodtime;\r
103 .float cmd_floodcount;\r
104 float cmd_floodcheck()\r
105 {\r
106         if (timeoutStatus != 2)\r
107         {\r
108                 if(time == self.cmd_floodtime)\r
109                 {\r
110                         self.cmd_floodcount += 1;\r
111                         if(self.cmd_floodcount > 8)\r
112                                 return TRUE;\r
113                 }\r
114                 else\r
115                 {\r
116                         self.cmd_floodtime = time;\r
117                         self.cmd_floodcount = 1;\r
118                 }\r
119         }\r
120         return FALSE;\r
121 }\r
122 \r
123 void SV_ParseClientCommand(string s) {\r
124         string cmd;\r
125         float tokens;\r
126         float i;\r
127         entity e;\r
128 \r
129         tokens = tokenize_console(s);\r
130 \r
131         cmd = argv(0);\r
132         if(cmd != "reportcvar")\r
133         if(cmd != "sentcvar")\r
134         if(cmd != "pause")\r
135         if(cmd != "prespawn")\r
136         if(cmd != "spawn")\r
137         if(cmd != "begin")\r
138         {\r
139                 if(cmd_floodcheck())\r
140                         return;\r
141         }\r
142 \r
143         if(GameCommand_Vote(s, self)) {\r
144                 return;\r
145         } else if(GameCommand_MapVote(argv(0))) {\r
146                 return;\r
147         } else if(cmd == "autoswitch") {\r
148                 // be backwards compatible with older clients (enabled)\r
149                 self.autoswitch = ("0" != argv(1));\r
150                 local string autoswitchmsg;\r
151                 if (self.autoswitch) {\r
152                         autoswitchmsg = "on";\r
153                 } else {\r
154                         autoswitchmsg = "off";\r
155                 }\r
156                 sprint(self, strcat("^1autoswitch turned ", autoswitchmsg, "\n"));\r
157         } else if(cmd == "clientversion") {\r
158                 if not(self.flags & FL_CLIENT)\r
159                         return;\r
160                 if (argv(1) == "$gameversion") {\r
161                         //versionmsg = "^1client is too old to get versioninfo.\nUPDATE!!! (http://www.alientrap.org/voretournament/)^8";\r
162                         // either that or someone wants to be funny\r
163                         self.version = 1;\r
164                 } else {\r
165                         self.version = stof(argv(1));\r
166                 }\r
167                 if(self.version != cvar("gameversion"))\r
168                 {\r
169                         self.version_mismatch = 1;\r
170                         ClientKill_TeamChange(-2); // observe\r
171                 } else if(cvar("g_campaign") || cvar("g_balance_teams") || cvar("g_balance_teams_force")) {\r
172                         //JoinBestTeam(self, FALSE, TRUE);\r
173                 } else if(teams_matter && !cvar("sv_spectate")) {\r
174                         self.classname = "observer";\r
175                         stuffcmd(self,"menu_showteamselect\n");\r
176                 }\r
177         } else if(cmd == "reportcvar") { // old system\r
178                 if(substring(argv(2), 0, 1) == "$") // undefined cvar: use the default value on the server then\r
179                 {\r
180                         s = strcat(substring(s, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");\r
181                         tokens = tokenize_console(s);\r
182                 }\r
183                 GetCvars(1);\r
184 #ifdef UID\r
185         } else if(cmd == "uid") {\r
186                 if not(self.uid)\r
187                 {\r
188                         self.uid = strzone(argv(1));\r
189                         self.uid_kicktime = 0;\r
190                         print("Client ", etos(self), " has UID ", self.uid, "\n");\r
191                         Ban_MaybeEnforceBan(self);\r
192                 }\r
193 #endif\r
194         } else if(cmd == "sentcvar") { // new system\r
195                 if(tokens == 2) // undefined cvar: use the default value on the server then\r
196                 {\r
197                         s = strcat(substring(s, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");\r
198                         tokens = tokenize_console(s);\r
199                 }\r
200                 GetCvars(1);\r
201         } else if(cmd == "spectate") {\r
202                 if(cmd_floodcheck())\r
203                         return;\r
204                 if not(self.flags & FL_CLIENT)\r
205                         return;\r
206                 if(g_arena)\r
207                         return;\r
208                 if(g_lms)\r
209                 {\r
210                         if(self.lms_spectate_warning)\r
211                         {\r
212                                 // mark player as spectator\r
213                                 PlayerScore_Add(self, SP_LMS_RANK, 666 - PlayerScore_Add(self, SP_LMS_RANK, 0));\r
214                         }\r
215                         else\r
216                         {\r
217                                 self.lms_spectate_warning = 1;\r
218                                 sprint(self, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n");\r
219                                 return;\r
220                         }\r
221                 }\r
222                 if(self.classname == "player" && cvar("sv_spectate") == 1) {\r
223                         ClientKill_TeamChange(-2); // observe\r
224                 }\r
225         } else if(cmd == "join") {\r
226                 if not(self.flags & FL_CLIENT)\r
227                         return;\r
228                 if(!g_arena)\r
229                 if (self.classname != "player" && !lockteams)\r
230                 {\r
231                         if(isJoinAllowed()) {\r
232                                 self.classname = "player";\r
233                                 if(g_ca)\r
234                                         self.caplayer = 1;\r
235                                 PlayerScore_Clear(self);\r
236                                 bprint ("^4", self.netname, "^4 is playing now\n");\r
237                                 self.stat_count = WEP_LAST;\r
238                                 PutClientInServer();\r
239                                 if(cvar("g_campaign"))\r
240                                         campaign_bots_may_start = 1;\r
241                         }\r
242                         else {\r
243                                 //player may not join because of g_maxplayers is set\r
244                                 centerprint_atprio(self, CENTERPRIO_MAPVOTE, PREVENT_JOIN_TEXT);\r
245                         }\r
246                 }\r
247         } else if( cmd == "selectteam" ) {\r
248                 if not(self.flags & FL_CLIENT)\r
249                         return;\r
250                 if( !teams_matter ) {\r
251                         sprint( self, "selecteam can only be used in teamgames\n");\r
252                 } else if(cvar("g_campaign")) {\r
253                         //JoinBestTeam(self, 0);\r
254                 } else if(lockteams) {\r
255                         sprint( self, "^7The game has already begun, you must wait until the next map to be able to join a team.\n");\r
256                 } else if( argv(1) == "red" ) {\r
257                         ClientKill_TeamChange(COLOR_TEAM1);\r
258                 } else if( argv(1) == "blue" ) {\r
259                         ClientKill_TeamChange(COLOR_TEAM2);\r
260                 } else if( argv(1) == "yellow" ) {\r
261                         ClientKill_TeamChange(COLOR_TEAM3);\r
262                 } else if( argv(1) == "pink" ) {\r
263                         ClientKill_TeamChange(COLOR_TEAM4);\r
264                 } else if( argv(1) == "auto" ) {\r
265                         ClientKill_TeamChange(-1);\r
266                 } else {\r
267                         sprint( self, strcat( "selectteam none/red/blue/yellow/pink/auto - \"", argv(1), "\" not recognised\n" ) );\r
268                 }\r
269         } else if(cmd == "ready") {\r
270                 if not(self.flags & FL_CLIENT)\r
271                         return;\r
272 \r
273                 if((inWarmupStage && 0 >= g_warmup_limit) // with unlimited warmup players have to be able to restart\r
274                    || cvar("sv_ready_restart") || g_race_qualifying == 2)\r
275                 {\r
276                         if(!readyrestart_happened || cvar("sv_ready_restart_repeatable"))\r
277                         {\r
278                                 if (self.ready) // toggle\r
279                                 {\r
280                                         self.ready = FALSE;\r
281                                         bprint(self.netname, "^2 is ^1NOT^2 ready\n");\r
282                                 }\r
283                                 else\r
284                                 {\r
285                                         self.ready = TRUE;\r
286                                         bprint(self.netname, "^2 is ready\n");\r
287                                 }\r
288 \r
289                                 // cannot reset the game while a timeout is active!\r
290                                 if(!timeoutStatus)\r
291                                         ReadyCount();\r
292                         } else {\r
293                                 sprint(self, "^1Game has already been restarted\n");\r
294                         }\r
295                 }\r
296         } else if(cmd == "maplist") {\r
297                 sprint(self, maplist_reply);\r
298         } else if(cmd == "lsmaps") {\r
299                 sprint(self, lsmaps_reply);\r
300         } else if(cmd == "lsnewmaps") {\r
301                 sprint(self, lsnewmaps_reply);\r
302         } else if(cmd == "records") {\r
303                 for(i = 0; i < 10; ++i)\r
304                         sprint(self, records_reply[i]);\r
305         } else if(cmd == "rankings") {\r
306                 sprint(self, rankings_reply);\r
307         } else if(cmd == "voice") {\r
308                 if(tokens >= 3)\r
309                         VoiceMessage(argv(1), substring(s, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));\r
310                 else\r
311                         VoiceMessage(argv(1), "");\r
312         } else if(cmd == "say") {\r
313                 if(tokens >= 2)\r
314                         Say(self, FALSE, world, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);\r
315                 //clientcommand(self, formatmessage(s));\r
316         } else if(cmd == "say_team") {\r
317                 if(tokens >= 2)\r
318                         Say(self, TRUE, world, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);\r
319                 //clientcommand(self, formatmessage(s));\r
320         } else if(cmd == "tell") {\r
321                 e = GetCommandPlayerSlotTargetFromTokenizedCommand(tokens, 1);\r
322                 if(e && tokens > ParseCommandPlayerSlotTarget_firsttoken)\r
323                 {\r
324                         Say(self, FALSE, e, substring(s, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)), TRUE);\r
325                 }\r
326                 else\r
327                 {\r
328                         if(tokens > ParseCommandPlayerSlotTarget_firsttoken)\r
329                                 trigger_magicear_processmessage_forallears(self, -1, world, substring(s, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)));\r
330                         sprint(self, "ERROR: usage: tell # playerid text...\n");\r
331                 }\r
332                 //clientcommand(self, formatmessage(s));\r
333         } else if(cmd == "info") {\r
334                 cmd = cvar_string_builtin(strcat("sv_info_", argv(1))); // This needed fixed for the cvar check\r
335                 if(cmd == "")\r
336                         sprint(self, "ERROR: unsupported info command\n");\r
337                 else\r
338                         wordwrap_sprint(cmd, 1111);\r
339         } else if(cmd == "suggestmap") {\r
340                 sprint(self, strcat(MapVote_Suggest(argv(1)), "\n"));\r
341         } else if(cmd == "timeout") {\r
342                 if not(self.flags & FL_CLIENT)\r
343                         return;\r
344                 if(cvar("sv_timeout")) {\r
345                         if(self.classname == "player") {\r
346                                 if(votecalled)\r
347                                         sprint(self, "^7Error: you can not call a timeout while a vote is active!\n");\r
348                                 else\r
349                                         evaluateTimeout();\r
350                         }\r
351                         else\r
352                                 sprint(self, "^7Error: only players can call a timeout!\n");\r
353                 }\r
354         } else if(cmd == "timein") {\r
355                 if not(self.flags & FL_CLIENT)\r
356                         return;\r
357                 if(cvar("sv_timeout")) {\r
358                         evaluateTimein();\r
359                 }\r
360         } else if(cmd == "teamstatus") {\r
361                 Score_NicePrint(self);\r
362         } else if(cmd == "cvar_changes") {\r
363                 sprint(self, cvar_changes);\r
364         } else if(CheatCommand(tokens)) {\r
365         } else {\r
366                 //if(ctf_clientcommand())\r
367                 //      return;\r
368                 // grep for Cmd_AddCommand_WithClientCommand to find them all\r
369                 if(cmd != "status")\r
370                 //if(cmd != "say") // handled above\r
371                 //if(cmd != "say_team") // handled above\r
372                 if(cmd != "kill")\r
373                 if(cmd != "pause")\r
374                 if(cmd != "ping")\r
375                 if(cmd != "name")\r
376                 if(cmd != "color")\r
377                 if(cmd != "rate")\r
378                 if(cmd != "pmodel")\r
379                 if(cmd != "playermodel")\r
380                 if(cmd != "playerskin")\r
381                 if(cmd != "prespawn")\r
382                 if(cmd != "spawn")\r
383                 if(cmd != "begin")\r
384                 if(cmd != "pings")\r
385                 if(cmd != "sv_startdownload")\r
386                 if(cmd != "download")\r
387                 {\r
388                         print("WARNING: Invalid clientcommand by ", self.netname, ": ", s, "\n");\r
389                         return;\r
390                 }\r
391 \r
392                 if(self.jointime > 0 && time > self.jointime + 10 && time > self.nickspamtime) // allow any changes in the first 10 seconds since joining\r
393                 if(cmd == "name" || cmd == "playermodel") // TODO also playerskin and color?\r
394                 {\r
395                         if(self.nickspamtime == 0 || time > self.nickspamtime + cvar("g_nick_flood_timeout"))\r
396                                 // good, no serious flood\r
397                                 self.nickspamcount = 1;\r
398                         else\r
399                                 self.nickspamcount += 1;\r
400                         self.nickspamtime = time + cvar("g_nick_flood_penalty");\r
401 \r
402                         if (timeoutStatus == 2) //when game is paused, no flood protection\r
403                                 self.nickspamcount = self.nickspamtime = 0;\r
404                 }\r
405 \r
406                 clientcommand(self,s);\r
407         }\r
408 }\r
409 \r
410 void ReadyRestartForce()\r
411 {\r
412         local entity e;\r
413 \r
414         bprint("^1Server is restarting...\n");\r
415 \r
416         VoteReset();\r
417 \r
418         // clear overtime\r
419         if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) {\r
420                 //we have to decrease timelimit to its original value again!!\r
421                 float newTL;\r
422                 newTL = cvar("timelimit");\r
423                 newTL -= checkrules_overtimesadded * cvar("timelimit_overtime");\r
424                 cvar_set("timelimit", ftos(newTL));\r
425         }\r
426 \r
427         checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;\r
428 \r
429 \r
430         readyrestart_happened = 1;\r
431         game_starttime = time;\r
432         if(!g_ca)\r
433                 game_starttime += RESTART_COUNTDOWN;\r
434         restart_mapalreadyrestarted = 0; //reset this var, needed when cvar sv_ready_restart_repeatable is in use\r
435 \r
436         inWarmupStage = 0; //once the game is restarted the game is in match stage\r
437 \r
438         //reset the .ready status of all players (also spectators)\r
439         FOR_EACH_CLIENTSLOT(e)\r
440                 e.ready = 0;\r
441         readycount = 0;\r
442         Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client\r
443 \r
444         if(cvar("teamplay_lockonrestart") && teams_matter) {\r
445                 lockteams = 1;\r
446                 bprint("^1The teams are now locked.\n");\r
447         }\r
448 \r
449         //initiate the restart-countdown-announcer entity\r
450         if(cvar("sv_ready_restart_after_countdown"))\r
451         {\r
452                 restartTimer = spawn();\r
453                 restartTimer.think = restartTimer_Think;\r
454                 restartTimer.nextthink = game_starttime;\r
455         }\r
456 \r
457         //after a restart every players number of allowed timeouts gets reset, too\r
458         if(cvar("sv_timeout"))\r
459         {\r
460                 FOR_EACH_REALPLAYER(e)\r
461                         e.allowedTimeouts = cvar("sv_timeout_number");\r
462         }\r
463 \r
464         //reset map immediately if this cvar is not set\r
465         if (!cvar("sv_ready_restart_after_countdown"))\r
466                 reset_map(TRUE);\r
467 \r
468         if(cvar("sv_eventlog"))\r
469                 GameLogEcho(":restart");\r
470 }\r
471 \r
472 void ReadyRestart()\r
473 {\r
474         // no arena, assault support yet...\r
475         if(g_arena | g_assault | gameover | intermission_running | race_completing)\r
476                 localcmd("restart\n");\r
477         else\r
478                 localcmd("\nsv_grabber_gamerestart;");\r
479 \r
480         ReadyRestartForce();\r
481 \r
482         // reset ALL scores, but only do that at the beginning\r
483         //of the countdown if sv_ready_restart_after_countdown is off!\r
484         //Otherwise scores could be manipulated during the countdown!\r
485         if (!cvar("sv_ready_restart_after_countdown"))\r
486                 Score_ClearAll();\r
487 }\r
488 \r
489 /**\r
490  * Counts how many players are ready. If not enough players are ready, the function\r
491  * does nothing. If all players are ready, the timelimit will be extended and the\r
492  * restart_countdown variable is set to allow other functions like PlayerPostThink\r
493  * to detect that the countdown is now active. If the cvar sv_ready_restart_after_countdown\r
494  * is not set the map will be resetted.\r
495  *\r
496  * Function is called after the server receives a 'ready' sign from a player.\r
497  */\r
498 void ReadyCount()\r
499 {\r
500         local entity e;\r
501         local float r, p;\r
502 \r
503         r = p = 0;\r
504 \r
505         FOR_EACH_REALPLAYER(e)\r
506         {\r
507                 p += 1;\r
508                 if(e.ready)\r
509                         r += 1;\r
510         }\r
511 \r
512         readycount = r;\r
513 \r
514         Nagger_ReadyCounted();\r
515 \r
516         if(r) // at least one is ready\r
517         if(r == p) // and, everyone is ready\r
518                 ReadyRestart();\r
519 }\r
520 \r
521 /**\r
522  * Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown\r
523  * is set)\r
524  */\r
525 void restartTimer_Think() {\r
526         restart_mapalreadyrestarted = 1;\r
527         reset_map(TRUE);\r
528         Score_ClearAll();\r
529         remove(self);\r
530         return;\r
531 }\r
532 \r
533 /**\r
534  * Checks whether the player who calls the timeout is allowed to do so.\r
535  * If so, it initializes the timeout countdown. It also checks whether another\r
536  * timeout was already running at this time and reacts correspondingly.\r
537  *\r
538  * affected globals/fields: .allowedTimeouts, remainingTimeoutTime, remainingLeadTime,\r
539  *                          timeoutInitiator, timeoutStatus, timeoutHandler\r
540  *\r
541  * This function is called when a player issues the calltimeout command.\r
542  */\r
543 void evaluateTimeout() {\r
544         if (inWarmupStage && !g_warmup_allow_timeout)\r
545                 return sprint(self, "^7Error: You can not call a timeout in warmup-stage!\n");\r
546         if (time < game_starttime )\r
547                 return sprint(self, "^7Error: You can not call a timeout while the map is being restarted!\n");\r
548         if (timeoutStatus != 2) {\r
549                 //if the map uses a timelimit make sure that timeout cannot be called right before the map ends\r
550                 if (cvar("timelimit")) {\r
551                         //a timelimit was used\r
552                         local float myTl;\r
553                         myTl = cvar("timelimit");\r
554 \r
555                         local float lastPossibleTimeout;\r
556                         lastPossibleTimeout = (myTl*60) - cvar("sv_timeout_leadtime") - 1;\r
557 \r
558                         if (lastPossibleTimeout < time - game_starttime)\r
559                                 return sprint(self, "^7Error: It is too late to call a timeout now!\n");\r
560                 }\r
561         }\r
562         //player may not call a timeout if he has no calls left\r
563         if (self.allowedTimeouts < 1)\r
564                 return sprint(self, "^7Error: You already used all your timeout calls for this map!\n");\r
565         //now all required checks are passed\r
566         self.allowedTimeouts -= 1;\r
567         bprint(self.netname, " ^7called a timeout (", ftos(self.allowedTimeouts), " timeouts left)!\n"); //write a bprint who started the timeout (and how many he has left)\r
568         remainingTimeoutTime = cvar("sv_timeout_length");\r
569         remainingLeadTime = cvar("sv_timeout_leadtime");\r
570         timeoutInitiator = self;\r
571         if (timeoutStatus == 0) { //if another timeout was already active, don't change its status (which was 1 or 2) to 1, only change it to 1 if no timeout was active yet\r
572                 timeoutStatus = 1;\r
573                 //create the timeout indicator which centerprints the information to all players and takes care of pausing/unpausing\r
574                 timeoutHandler = spawn();\r
575                 timeoutHandler.think = timeoutHandler_Think;\r
576         }\r
577         timeoutHandler.nextthink = time; //always let the entity think asap\r
578 \r
579         //inform all connected clients about the timeout call\r
580         Announce("timeoutcalled");\r
581 }\r
582 \r
583 /**\r
584  * Checks whether a player is allowed to resume the game. If he is allowed to do it,\r
585  * and the lead time for the timeout is still active, this countdown just will be aborted (the\r
586  * game will never be paused). Otherwise the remainingTimeoutTime will be set to the corresponding\r
587  * value of the cvar sv_timeout_resumetime.\r
588  *\r
589  * This function is called when a player issues the resumegame command.\r
590  */\r
591 void evaluateTimein() {\r
592         if (!timeoutStatus)\r
593                 return sprint(self, "^7Error: There is no active timeout which could be aborted!\n");\r
594         if (self != timeoutInitiator)\r
595                 return sprint(self, "^7Error: You may not abort the active timeout. Only the player who called it can do that!\n");\r
596         if (timeoutStatus == 1) {\r
597                 remainingTimeoutTime = timeoutStatus = 0;\r
598                 timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately\r
599                 bprint(strcat("^7The timeout was aborted by ", self.netname, " !\n"));\r
600         }\r
601         else if (timeoutStatus == 2) {\r
602                 //only shorten the remainingTimeoutTime if it makes sense\r
603                 if( remainingTimeoutTime > (cvar("sv_timeout_resumetime") + 1) ) {\r
604                         bprint(strcat("^1Attention: ^7", self.netname, " resumed the game! Prepare for battle!\n"));\r
605                         remainingTimeoutTime = cvar("sv_timeout_resumetime");\r
606                         timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately\r
607                 }\r
608                 else\r
609                         sprint(self, "^7Error: Your resumegame call was discarded!\n");\r
610 \r
611         }\r
612 }\r