]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/command/vote.qc
Merge branch 'master' into Lyberta/StandaloneOverkillWeapons
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / command / vote.qc
1 #include "vote.qh"
2
3 #include <server/defs.qh>
4 #include <server/miscfunctions.qh>
5
6 #include <common/command/_mod.qh>
7 #include "vote.qh"
8
9 #include "common.qh"
10
11 #include "../g_damage.qh"
12 #include "../g_world.qh"
13 #include "../race.qh"
14 #include "../round_handler.qh"
15 #include "../scores.qh"
16
17 #include "../mutators/_mod.qh"
18
19 #include <common/constants.qh>
20 #include <common/net_linked.qh>
21 #include <common/mapinfo.qh>
22 #include <common/notifications/all.qh>
23 #include <common/playerstats.qh>
24 #include <common/util.qh>
25
26 // =============================================
27 //  Server side voting code, reworked by Samual
28 //  Last updated: December 27th, 2011
29 // =============================================
30
31 //  Nagger for players to know status of voting
32 bool Nagger_SendEntity(entity this, entity to, float sendflags)
33 {
34         int nags, i, f, b;
35         entity e;
36         WriteHeader(MSG_ENTITY, ENT_CLIENT_NAGGER);
37
38         // bits:
39         //   1 = ready
40         //   2 = player needs to ready up
41         //   4 = vote
42         //   8 = player needs to vote
43         //  16 = warmup
44         // sendflags:
45         //  64 = vote counts
46         // 128 = vote string
47
48         nags = 0;
49         if (readycount)
50         {
51                 nags |= BIT(0);
52                 if (to.ready == 0) nags |= BIT(1);
53         }
54         if (vote_called)
55         {
56                 nags |= BIT(2);
57                 if (to.vote_selection == 0) nags |= BIT(3);
58         }
59         if (warmup_stage) nags |= BIT(4);
60
61         if (sendflags & BIT(6)) nags |= BIT(6);
62
63         if (sendflags & BIT(7)) nags |= BIT(7);
64
65         if (!(nags & 4))  // no vote called? send no string
66                 nags &= ~(BIT(6) | BIT(7));
67
68         WriteByte(MSG_ENTITY, nags);
69
70         if (nags & BIT(6))
71         {
72                 WriteByte(MSG_ENTITY, vote_accept_count);
73                 WriteByte(MSG_ENTITY, vote_reject_count);
74                 WriteByte(MSG_ENTITY, vote_needed_overall);
75                 WriteChar(MSG_ENTITY, to.vote_selection);
76         }
77
78         if (nags & BIT(7)) WriteString(MSG_ENTITY, vote_called_display);
79
80         if (nags & 1)
81         {
82                 for (i = 1; i <= maxclients; i += 8)
83                 {
84                         for (f = 0, e = edict_num(i), b = BIT(0); b < BIT(8); b <<= 1, e = nextent(e))
85                                 if (!IS_REAL_CLIENT(e) || e.ready)
86                                         f |= b;
87                         WriteByte(MSG_ENTITY, f);
88                 }
89         }
90
91         return true;
92 }
93
94 void Nagger_Init()
95 {
96         Net_LinkEntity(nagger = new_pure(nagger), false, 0, Nagger_SendEntity);
97 }
98
99 void Nagger_VoteChanged()
100 {
101         if (nagger) nagger.SendFlags |= BIT(7);
102 }
103
104 void Nagger_VoteCountChanged()
105 {
106         if (nagger) nagger.SendFlags |= BIT(6);
107 }
108
109 void Nagger_ReadyCounted()
110 {
111         if (nagger) nagger.SendFlags |= BIT(0);
112 }
113
114 // If the vote_caller is still here, return their name, otherwise vote_caller_name
115 string OriginalCallerName()
116 {
117         if (IS_REAL_CLIENT(vote_caller)) return playername(vote_caller, false);
118         return vote_caller_name;
119 }
120
121 // =======================
122 //  Game logic for voting
123 // =======================
124
125 void VoteReset()
126 {
127         FOREACH_CLIENT(true, { it.vote_selection = 0; });
128
129         if (vote_called)
130         {
131                 strunzone(vote_called_command);
132                 strunzone(vote_called_display);
133                 strunzone(vote_caller_name);
134         }
135
136         vote_called = VOTE_NULL;
137         vote_caller = NULL;
138         vote_caller_name = string_null;
139         vote_endtime = 0;
140
141         vote_called_command = string_null;
142         vote_called_display = string_null;
143
144         vote_parsed_command = string_null;
145         vote_parsed_display = string_null;
146
147         Nagger_VoteChanged();
148 }
149
150 void VoteStop(entity stopper)
151 {
152         bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n");
153         if (autocvar_sv_eventlog)   GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));
154         // Don't force them to wait for next vote, this way they can e.g. correct their vote.
155         if ((vote_caller) && (stopper == vote_caller))   vote_caller.vote_waittime = time + autocvar_sv_vote_stop;
156         VoteReset();
157 }
158
159 void VoteAccept()
160 {
161         bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ^1", vote_called_display, "^2 was accepted\n");
162
163         if ((vote_called == VOTE_MASTER) && vote_caller) vote_caller.vote_master = 1;
164         else localcmd(strcat(vote_called_command, "\n"));
165
166         if (vote_caller)   vote_caller.vote_waittime = 0;  // people like your votes, you don't need to wait to vote again
167
168         VoteReset();
169         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_ACCEPT);
170 }
171
172 void VoteReject()
173 {
174         bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 was rejected\n");
175         VoteReset();
176         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
177 }
178
179 void VoteTimeout()
180 {
181         bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 timed out\n");
182         VoteReset();
183         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
184 }
185
186 void VoteSpam(float notvoters, float mincount, string result)
187 {
188         bprint(strcat(
189                 strcat("\{1}^2* vote results: ^1", ftos(vote_accept_count)),
190                 strcat("^2:^1", ftos(vote_reject_count)),
191                 ((mincount >= 0) ? strcat("^2 (^1", ftos(mincount), "^2 needed)") : "^2"),
192                 strcat(", ^1", ftos(vote_abstain_count), "^2 didn't care"),
193                 strcat(", ^1", ftos(notvoters), strcat("^2 didn't ", ((mincount >= 0) ? "" : "have to "), "vote\n"))));
194
195         if (autocvar_sv_eventlog)
196         {
197                 GameLogEcho(strcat(
198                         strcat(":vote:v", result, ":", ftos(vote_accept_count)),
199                         strcat(":", ftos(vote_reject_count)),
200                         strcat(":", ftos(vote_abstain_count)),
201                         strcat(":", ftos(notvoters)),
202                         strcat(":", ftos(mincount))));
203         }
204 }
205
206 #define spectators_allowed (!autocvar_sv_vote_nospectators || (autocvar_sv_vote_nospectators == 1 && (warmup_stage || intermission_running)))
207
208 void VoteCount(float first_count)
209 {
210         // declarations
211         vote_accept_count = vote_reject_count = vote_abstain_count = 0;
212
213         float vote_player_count = 0, notvoters = 0;
214         float vote_real_player_count = 0, vote_real_accept_count = 0;
215         float vote_real_reject_count = 0, vote_real_abstain_count = 0;
216         float vote_needed_of_voted, final_needed_votes;
217         float vote_factor_overall, vote_factor_of_voted;
218
219         Nagger_VoteCountChanged();
220
221         // add up all the votes from each connected client
222         FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_CLIENT(it), {
223                 ++vote_player_count;
224                 if (IS_PLAYER(it))   ++vote_real_player_count;
225                 switch (it.vote_selection)
226                 {
227                         case VOTE_SELECT_REJECT:
228                         { ++vote_reject_count;
229                           { if (IS_PLAYER(it)) ++vote_real_reject_count; } break;
230                         }
231                         case VOTE_SELECT_ACCEPT:
232                         { ++vote_accept_count;
233                           { if (IS_PLAYER(it)) ++vote_real_accept_count; } break;
234                         }
235                         case VOTE_SELECT_ABSTAIN:
236                         { ++vote_abstain_count;
237                           { if (IS_PLAYER(it)) ++vote_real_abstain_count; } break;
238                         }
239                         default: break;
240                 }
241         });
242
243         // Check to see if there are enough players on the server to allow master voting... otherwise, vote master could be used for evil.
244         if ((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count)
245         {
246                 if (vote_caller)   vote_caller.vote_waittime = 0;
247                 print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master.");
248                 VoteReset();
249                 return;
250         }
251
252         // if spectators aren't allowed to vote and there are players in a match, then only count the players in the vote and ignore spectators.
253         if (!spectators_allowed && (vote_real_player_count > 0))
254         {
255                 vote_accept_count = vote_real_accept_count;
256                 vote_reject_count = vote_real_reject_count;
257                 vote_abstain_count = vote_real_abstain_count;
258                 vote_player_count = vote_real_player_count;
259         }
260
261         // people who have no opinion in any way :D
262         notvoters = (vote_player_count - vote_accept_count - vote_reject_count - vote_abstain_count);
263
264         // determine the goal for the vote to be passed or rejected normally
265         vote_factor_overall = bound(0.5, autocvar_sv_vote_majority_factor, 0.999);
266         vote_needed_overall = floor((vote_player_count - vote_abstain_count) * vote_factor_overall) + 1;
267
268         // if the vote times out, determine the amount of votes needed of the people who actually already voted
269         vote_factor_of_voted = bound(0.5, autocvar_sv_vote_majority_factor_of_voted, 0.999);
270         vote_needed_of_voted = floor((vote_accept_count + vote_reject_count) * vote_factor_of_voted) + 1;
271
272         // are there any players at all on the server? it could be an admin vote
273         if (vote_player_count == 0 && first_count)
274         {
275                 VoteSpam(0, -1, "yes");  // no players at all, just accept it
276                 VoteAccept();
277                 return;
278         }
279
280         // since there ARE players, finally calculate the result of the vote
281         if (vote_accept_count >= vote_needed_overall)
282         {
283                 VoteSpam(notvoters, -1, "yes");  // there is enough acceptions to pass the vote
284                 VoteAccept();
285                 return;
286         }
287
288         if (vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall)
289         {
290                 VoteSpam(notvoters, -1, "no");  // there is enough rejections to deny the vote
291                 VoteReject();
292                 return;
293         }
294
295         // there is not enough votes in either direction, now lets just calculate what the voters have said
296         if (time > vote_endtime)
297         {
298                 final_needed_votes = vote_needed_overall;
299
300                 if (autocvar_sv_vote_majority_factor_of_voted)
301                 {
302                         if (vote_accept_count >= vote_needed_of_voted)
303                         {
304                                 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "yes");
305                                 VoteAccept();
306                                 return;
307                         }
308
309                         if (vote_accept_count + vote_reject_count > 0)
310                         {
311                                 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "no");
312                                 VoteReject();
313                                 return;
314                         }
315
316                         final_needed_votes = min(vote_needed_overall, vote_needed_of_voted);
317                 }
318
319                 // it didn't pass or fail, so not enough votes to even make a decision.
320                 VoteSpam(notvoters, final_needed_votes, "timeout");
321                 VoteTimeout();
322         }
323 }
324
325 void VoteThink()
326 {
327         if (vote_endtime > 0)        // a vote was called
328         {
329                 if (time > vote_endtime) // time is up
330                         VoteCount(false);
331         }
332 }
333
334
335 // =======================
336 //  Game logic for warmup
337 // =======================
338
339 // Resets the state of all clients, items, weapons, waypoints, ... of the map.
340 void reset_map(bool dorespawn)
341 {
342         if (time <= game_starttime)
343         {
344                 if (game_stopped)
345                         return;
346                 if (round_handler_IsActive())
347                         round_handler_Reset(game_starttime);
348         }
349
350         MUTATOR_CALLHOOK(reset_map_global);
351
352         FOREACH_ENTITY_FLOAT_ORDERED(pure_data, false,
353         {
354                 if(IS_CLIENT(it))
355                         continue;
356                 if (it.reset)
357                 {
358                         it.reset(it);
359                         continue;
360                 }
361                 if (it.team_saved) it.team = it.team_saved;
362                 if (it.flags & FL_PROJECTILE) delete(it);  // remove any projectiles left
363         });
364
365         // Waypoints and assault start come LAST
366         FOREACH_ENTITY_ORDERED(IS_NOT_A_CLIENT(it), {
367                 if (it.reset2) it.reset2(it);
368         });
369
370         FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it); });
371
372         // Moving the player reset code here since the player-reset depends
373         // on spawnpoint entities which have to be reset first --blub
374         if (dorespawn)
375         {
376                 if (!MUTATOR_CALLHOOK(reset_map_players))
377                 {
378                         if (restart_mapalreadyrestarted || (time < game_starttime))
379                         {
380                                 FOREACH_CLIENT(IS_PLAYER(it),
381                                 {
382                                         /*
383                                         only reset players if a restart countdown is active
384                                         this can either be due to cvar sv_ready_restart_after_countdown having set
385                                         restart_mapalreadyrestarted to 1 after the countdown ended or when
386                                         sv_ready_restart_after_countdown is not used and countdown is still running
387                                         */
388                                         // NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
389                                         // PlayerScore_Clear(it);
390                                         CS(it).killcount = 0;
391                                         // stop the player from moving so that he stands still once he gets respawned
392                                         it.velocity = '0 0 0';
393                                         it.avelocity = '0 0 0';
394                                         CS(it).movement = '0 0 0';
395                                         PutClientInServer(it);
396                                 });
397                         }
398                 }
399         }
400 }
401
402 // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
403 void ReadyRestart_think(entity this)
404 {
405         restart_mapalreadyrestarted = true;
406         reset_map(true);
407         Score_ClearAll();
408         delete(this);
409 }
410
411 // Forces a restart of the game without actually reloading the map // this is a mess...
412 void ReadyRestart_force()
413 {
414         if (time <= game_starttime && game_stopped)
415                 return;
416
417         bprint("^1Server is restarting...\n");
418
419         VoteReset();
420
421         // clear overtime, we have to decrease timelimit to its original value again.
422         if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
423                 cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
424         checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
425
426         readyrestart_happened = true;
427         game_starttime = time + RESTART_COUNTDOWN;
428
429         // clear player attributes
430         FOREACH_CLIENT(IS_PLAYER(it), {
431                 it.alivetime = 0;
432                 CS(it).killcount = 0;
433                 float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
434                 PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
435         });
436
437         restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
438
439         // disable the warmup global for the server
440         warmup_stage = 0;                // once the game is restarted the game is in match stage
441
442         // reset the .ready status of all players (also spectators)
443         FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
444         readycount = 0;
445         Nagger_ReadyCounted();  // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
446
447         // lock teams with lockonrestart
448         if (autocvar_teamplay_lockonrestart && teamplay)
449         {
450                 lockteams = true;
451                 bprint("^1The teams are now locked.\n");
452         }
453
454         // initiate the restart-countdown-announcer entity
455         if (sv_ready_restart_after_countdown)
456         {
457                 entity restart_timer = new_pure(restart_timer);
458                 setthink(restart_timer, ReadyRestart_think);
459                 restart_timer.nextthink = game_starttime;
460         }
461
462         // after a restart every players number of allowed timeouts gets reset, too
463         if (autocvar_sv_timeout)
464         {
465                 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
466         }
467
468         if (!sv_ready_restart_after_countdown) reset_map(true);
469         if (autocvar_sv_eventlog) GameLogEcho(":restart");
470 }
471
472 void ReadyRestart()
473 {
474         if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || game_stopped || race_completing) localcmd("restart\n");
475         else localcmd("\nsv_hook_gamerestart\n");
476
477         // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
478         // Otherwise scores could be manipulated during the countdown.
479         if (!sv_ready_restart_after_countdown) Score_ClearAll();
480         ReadyRestart_force();
481 }
482
483 // Count the players who are ready and determine whether or not to restart the match
484 void ReadyCount()
485 {
486         float ready_needed_factor, ready_needed_count;
487         float t_ready = 0, t_players = 0;
488
489         FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), {
490                 ++t_players;
491                 if (it.ready) ++t_ready;
492         });
493
494         readycount = t_ready;
495
496         Nagger_ReadyCounted();
497
498         ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
499         ready_needed_count = floor(t_players * ready_needed_factor) + 1;
500
501         if (readycount >= ready_needed_count) ReadyRestart();
502 }
503
504
505 // ======================================
506 //  Supporting functions for VoteCommand
507 // ======================================
508
509 float Votecommand_check_assignment(entity caller, float assignment)
510 {
511         float from_server = (!caller);
512
513         if ((assignment == VC_ASGNMNT_BOTH)
514             || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
515             || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
516
517         return false;
518 }
519
520 string VoteCommand_extractcommand(string input, float startpos, float argc)
521 {
522         string output;
523
524         if ((argc - 1) < startpos) output = "";
525         else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
526
527         return output;
528 }
529
530 float VoteCommand_checknasty(string vote_command)
531 {
532         if ((strstrofs(vote_command, ";", 0) >= 0)
533             || (strstrofs(vote_command, "\n", 0) >= 0)
534             || (strstrofs(vote_command, "\r", 0) >= 0)
535             || (strstrofs(vote_command, "$", 0) >= 0)) return false;
536
537         return true;
538 }
539
540 float VoteCommand_checkinlist(string vote_command, string list)
541 {
542         string l = strcat(" ", list, " ");
543
544         if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true;
545
546         return false;
547 }
548
549 string ValidateMap(string validated_map, entity caller)
550 {
551         validated_map = MapInfo_FixName(validated_map);
552
553         if (!validated_map)
554         {
555                 print_to(caller, "This map is not available on this server.");
556                 return string_null;
557         }
558
559         if (!autocvar_sv_vote_override_mostrecent && caller)
560         {
561                 if (Map_IsRecent(validated_map))
562                 {
563                         print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
564                         return string_null;
565                 }
566         }
567
568         if (!MapInfo_CheckMap(validated_map))
569         {
570                 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode."));
571                 return string_null;
572         }
573
574         return validated_map;
575 }
576
577 float VoteCommand_checkargs(float startpos, float argc)
578 {
579         float p, q, check, minargs;
580         string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
581         string cmdrestriction = "";  // No we don't.
582         string charlist, arg;
583         float checkmate;
584
585         if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
586                 cmdrestriction = cvar_string(cvarname);
587         else
588                 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
589
590         if (cmdrestriction == "") return true;
591
592         ++startpos;  // skip command name
593
594         // check minimum arg count
595
596         // 0 args: argc == startpos
597         // 1 args: argc == startpos + 1
598         // ...
599
600         minargs = stof(cmdrestriction);
601         if (argc - startpos < minargs) return false;
602
603         p = strstrofs(cmdrestriction, ";", 0);  // find first semicolon
604
605         for ( ; ; )
606         {
607                 // we know that at any time, startpos <= argc - minargs
608                 // so this means: argc-minargs >= startpos >= argc, thus
609                 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
610                 // have been seen already
611
612                 if (startpos >= argc) // all args checked? GOOD
613                         break;
614
615                 if (p < 0)            // no more args? FAIL
616                 {
617                         // exception: exactly minargs left, this one included
618                         if (argc - startpos == minargs) break;
619
620                         // otherwise fail
621                         return false;
622                 }
623
624                 // cut to next semicolon
625                 q = strstrofs(cmdrestriction, ";", p + 1);  // find next semicolon
626                 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
627                 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
628
629                 // in case we ever want to allow semicolons in VoteCommand_checknasty
630                 // charlist = strreplace("^^", ";", charlist);
631
632                 if (charlist != "")
633                 {
634                         // verify the arg only contains allowed chars
635                         arg = argv(startpos);
636                         checkmate = strlen(arg);
637                         for (check = 0; check < checkmate; ++check)
638                                 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
639                         // not allowed character
640                         // all characters are allowed. FINE.
641                 }
642
643                 ++startpos;
644                 --minargs;
645                 p = q;
646         }
647
648         return true;
649 }
650
651 int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc)
652 {
653         string first_command = argv(startpos);
654         int missing_chars = argv_start_index(startpos);
655
656         if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
657                 return 0;
658
659         if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
660
661         if (!VoteCommand_checkargs(startpos, argc)) return 0;
662
663         switch (first_command) // now go through and parse the proper commands to adjust as needed.
664         {
665                 case "kick":
666                 case "kickban":    // catch all kick/kickban commands
667                 {
668                         entity victim = GetIndexedEntity(argc, (startpos + 1));
669                         float accepted = VerifyClientEntity(victim, true, false);
670
671                         if (accepted > 0)
672                         {
673                                 string reason = "No reason provided";
674                                 if(argc > next_token)
675                                         reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
676
677                                 string command_arguments = reason;
678                                 if (first_command == "kickban")
679                                         command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
680
681                                 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
682                                 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
683                         }
684                         else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
685
686                         break;
687                 }
688
689                 case "map":
690                 case "chmap":
691                 case "gotomap":  // re-direct all map selection commands to gotomap
692                 {
693                         vote_command = ValidateMap(argv(startpos + 1), caller);
694                         if (!vote_command)  return -1;
695                         vote_parsed_command = strcat("gotomap ", vote_command);
696                         vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
697
698                         break;
699                 }
700
701                 case "nextmap": // TODO: replicate the old behaviour of being able to vote for maps from different modes on multimode servers (possibly support it in gotomap too), maybe fallback instead of aborting if map name is invalid?
702                 {
703                         vote_command = ValidateMap(argv(startpos + 1), caller);
704                         if (!vote_command)  return -1;
705                         vote_parsed_command = strcat("nextmap ", vote_command);
706                         vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
707
708                         break;
709                 }
710
711                 default:
712                 {
713                         vote_parsed_command = vote_command;
714                         vote_parsed_display = strzone(strcat("^1", vote_command));
715
716                         break;
717                 }
718         }
719
720         return 1;
721 }
722
723
724 // =======================
725 //  Command Sub-Functions
726 // =======================
727
728 void VoteCommand_abstain(float request, entity caller)  // CLIENT ONLY
729 {
730         switch (request)
731         {
732                 case CMD_REQUEST_COMMAND:
733                 {
734                         if (!vote_called) { print_to(caller, "^1No vote called."); }
735                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
736                         {
737                                 print_to(caller, "^1You have already voted.");
738                         }
739
740                         else  // everything went okay, continue changing vote
741                         {
742                                 print_to(caller, "^1You abstained from your vote.");
743                                 caller.vote_selection = VOTE_SELECT_ABSTAIN;
744                                 msg_entity = caller;
745                                 if (!autocvar_sv_vote_singlecount)   VoteCount(false); }
746
747                         return;
748                 }
749
750                 default:
751                 case CMD_REQUEST_USAGE:
752                 {
753                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
754                         print_to(caller, "  No arguments required.");
755                         return;
756                 }
757         }
758 }
759
760 void VoteCommand_call(float request, entity caller, float argc, string vote_command)  // BOTH
761 {
762         switch (request)
763         {
764                 case CMD_REQUEST_COMMAND:
765                 {
766                         float tmp_playercount = 0;
767                         int parse_error;
768
769                         vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
770
771                         if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
772                         else if (!autocvar_sv_vote_gamestart && time < game_starttime)
773                         {
774                                 print_to(caller, "^1Vote calling is not allowed before the match has started.");
775                         }
776                         else if (vote_called)
777                         {
778                                 print_to(caller, "^1There is already a vote called.");
779                         }
780                         else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
781                         {
782                                 print_to(caller, "^1Only players can call a vote.");
783                         }
784                         else if (caller && !IS_CLIENT(caller))
785                         {
786                                 print_to(caller, "^1Only connected clients can vote.");
787                         }
788                         else if (timeout_status)
789                         {
790                                 print_to(caller, "^1You can not call a vote while a timeout is active.");
791                         }
792                         else if (caller && (time < caller.vote_waittime))
793                         {
794                                 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
795                         }
796                         else if (!VoteCommand_checknasty(vote_command))
797                         {
798                                 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
799                         }
800                         else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
801                         {
802                                 if(parse_error == 0)
803                                         print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
804                         }
805                         else  // everything went okay, continue with calling the vote
806                         {
807                                 vote_caller = caller;  // remember who called the vote
808                                 vote_caller_name = strzone(GetCallerName(vote_caller));
809                                 vote_called = VOTE_NORMAL;
810                                 vote_called_command = strzone(vote_parsed_command);
811                                 vote_called_display = strzone(vote_parsed_display);
812                                 vote_endtime = time + autocvar_sv_vote_timeout;
813
814                                 if (caller)
815                                 {
816                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
817                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
818                                         msg_entity = caller;
819                                 }
820
821                                 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
822                                 if (tmp_playercount > 1)
823                                         Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
824
825                                 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
826                                 if (autocvar_sv_eventlog)
827                                         GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
828                                 Nagger_VoteChanged();
829                                 VoteCount(true);  // needed if you are the only one
830                         }
831
832                         return;
833                 }
834
835                 default:
836                 case CMD_REQUEST_USAGE:
837                 {
838                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
839                         print_to(caller, "  Where 'command' is the command to request a vote upon.");
840                         print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
841                         print_to(caller, strcat("          ", GetCommandPrefix(caller), " vote call endmatch"));
842                         return;
843                 }
844         }
845 }
846
847 void VoteCommand_master(float request, entity caller, float argc, string vote_command)  // CLIENT ONLY
848 {
849         switch (request)
850         {
851                 case CMD_REQUEST_COMMAND:
852                 {
853                         if (autocvar_sv_vote_master)
854                         {
855                                 switch (strtolower(argv(2)))
856                                 {
857                                         case "do":
858                                         {
859                                                 int parse_error;
860                                                 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
861
862                                                 if (!caller.vote_master)
863                                                         print_to(caller, "^1You do not have vote master privileges.");
864                                                 else if (!VoteCommand_checknasty(vote_command))
865                                                 {
866                                                         print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
867                                                 }
868                                                 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
869                                                 {
870                                                         if(parse_error == 0)
871                                                                 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
872                                                 }
873                                                 else  // everything went okay, proceed with command
874                                                 {
875                                                         localcmd(strcat(vote_parsed_command, "\n"));
876                                                         print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
877                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
878                                                         if (autocvar_sv_eventlog)
879                                                                 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
880                                                 }
881
882                                                 return;
883                                         }
884
885                                         case "login":
886                                         {
887                                                 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
888                                                 else if (caller.vote_master)
889                                                 {
890                                                         print_to(caller, "^1You are already logged in as vote master.");
891                                                 }
892                                                 else if (autocvar_sv_vote_master_password != argv(3))
893                                                 {
894                                                         print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
895                                                 }
896                                                 else  // everything went okay, proceed with giving this player master privilages
897                                                 {
898                                                         caller.vote_master = true;
899                                                         print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
900                                                         bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
901                                                         if (autocvar_sv_eventlog)
902                                                                 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
903                                                 }
904
905                                                 return;
906                                         }
907
908                                         default:  // calling a vote for master
909                                         {
910                                                 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
911                                                 else if (vote_called)
912                                                 {
913                                                         print_to(caller, "^1There is already a vote called.");
914                                                 }
915                                                 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
916                                                 {
917                                                         print_to(caller, "^1Only players can call a vote.");
918                                                 }
919                                                 else if (timeout_status)
920                                                 {
921                                                         print_to(caller, "^1You can not call a vote while a timeout is active.");
922                                                 }
923                                                 else  // everything went okay, continue with creating vote
924                                                 {
925                                                         vote_caller = caller;
926                                                         vote_caller_name = strzone(GetCallerName(vote_caller));
927                                                         vote_called = VOTE_MASTER;
928                                                         vote_called_command = strzone("XXX");
929                                                         vote_called_display = strzone("^3master");
930                                                         vote_endtime = time + autocvar_sv_vote_timeout;
931
932                                                         caller.vote_selection = VOTE_SELECT_ACCEPT;
933                                                         caller.vote_waittime = time + autocvar_sv_vote_wait;
934
935                                                         bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
936                                                         if (autocvar_sv_eventlog)
937                                                                 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
938                                                         Nagger_VoteChanged();
939                                                         VoteCount(true);  // needed if you are the only one
940                                                 }
941
942                                                 return;
943                                         }
944                                 }
945                         }
946                         else { print_to(caller, "^1Master control of voting is not allowed."); }
947
948                         return;
949                 }
950
951                 default:
952                 case CMD_REQUEST_USAGE:
953                 {
954                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
955                         print_to(caller, "  If action is left blank, it calls a vote for you to become master.");
956                         print_to(caller, "  Otherwise the actions are either 'do' a command or 'login' as master.");
957                         return;
958                 }
959         }
960 }
961
962 void VoteCommand_no(float request, entity caller)  // CLIENT ONLY
963 {
964         switch (request)
965         {
966                 case CMD_REQUEST_COMMAND:
967                 {
968                         if (!vote_called) { print_to(caller, "^1No vote called."); }
969                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
970                         {
971                                 print_to(caller, "^1You have already voted.");
972                         }
973                         else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
974                         {
975                                 VoteStop(caller);
976                         }
977
978                         else  // everything went okay, continue changing vote
979                         {
980                                 print_to(caller, "^1You rejected the vote.");
981                                 caller.vote_selection = VOTE_SELECT_REJECT;
982                                 msg_entity = caller;
983                                 if (!autocvar_sv_vote_singlecount)
984                                         VoteCount(false);
985                         }
986
987                         return;
988                 }
989
990                 default:
991                 case CMD_REQUEST_USAGE:
992                 {
993                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
994                         print_to(caller, "  No arguments required.");
995                         return;
996                 }
997         }
998 }
999
1000 void VoteCommand_status(float request, entity caller)  // BOTH
1001 {
1002         switch (request)
1003         {
1004                 case CMD_REQUEST_COMMAND:
1005                 {
1006                         if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1007                         else print_to(caller, "^1No vote called.");
1008
1009                         return;
1010                 }
1011
1012                 default:
1013                 case CMD_REQUEST_USAGE:
1014                 {
1015                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1016                         print_to(caller, "  No arguments required.");
1017                         return;
1018                 }
1019         }
1020 }
1021
1022 void VoteCommand_stop(float request, entity caller)  // BOTH
1023 {
1024         switch (request)
1025         {
1026                 case CMD_REQUEST_COMMAND:
1027                 {
1028                         if (!vote_called)   print_to(caller, "^1No vote called.");
1029                         else if ((caller == vote_caller) || !caller || caller.vote_master)   VoteStop(caller);
1030                         else   print_to(caller, "^1You are not allowed to stop that vote.");
1031                         return;
1032                 }
1033
1034                 default:
1035                 case CMD_REQUEST_USAGE:
1036                 {
1037                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1038                         print_to(caller, "  No arguments required.");
1039                         return;
1040                 }
1041         }
1042 }
1043
1044 void VoteCommand_yes(float request, entity caller)  // CLIENT ONLY
1045 {
1046         switch (request)
1047         {
1048                 case CMD_REQUEST_COMMAND:
1049                 {
1050                         if (!vote_called) { print_to(caller, "^1No vote called."); }
1051                         else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1052                         {
1053                                 print_to(caller, "^1You have already voted.");
1054                         }
1055                         else  // everything went okay, continue changing vote
1056                         {
1057                                 print_to(caller, "^1You accepted the vote.");
1058                                 caller.vote_selection = VOTE_SELECT_ACCEPT;
1059                                 msg_entity = caller;
1060                                 if (!autocvar_sv_vote_singlecount)
1061                                         VoteCount(false);
1062                         }
1063
1064                         return;
1065                 }
1066
1067                 default:
1068                 case CMD_REQUEST_USAGE:
1069                 {
1070                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1071                         print_to(caller, "  No arguments required.");
1072                         return;
1073                 }
1074         }
1075 }
1076
1077 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1078 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1079 void VoteCommand_(float request)
1080 {
1081     switch(request)
1082     {
1083         case CMD_REQUEST_COMMAND:
1084         {
1085
1086             return;
1087         }
1088
1089         default:
1090         case CMD_REQUEST_USAGE:
1091         {
1092             print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1093             print_to(caller, "  No arguments required.");
1094             return;
1095         }
1096     }
1097 }
1098 */
1099
1100
1101 // ================================
1102 //  Macro system for vote commands
1103 // ================================
1104
1105 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1106 #define VOTE_COMMANDS(request, caller, arguments, command) \
1107         VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1108         VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1109         VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1110         VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1111         VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1112         VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1113         VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1114         VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1115         /* nothing */
1116
1117 void VoteCommand_macro_help(entity caller, float argc)
1118 {
1119         string command_origin = GetCommandPrefix(caller);
1120
1121         if (argc == 2 || argv(2) == "help")  // help display listing all commands
1122         {
1123                 print_to(caller, "\nVoting commands:\n");
1124                 #define VOTE_COMMAND(name, function, description, assignment) \
1125                         { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat("  ^2", name, "^7: ", description)); } }
1126
1127                 VOTE_COMMANDS(0, caller, 0, "");
1128 #undef VOTE_COMMAND
1129
1130                 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1131                 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1132                 print_to(caller, strcat("\n^7You can call a vote for or execute these commands: ^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7"));
1133         }
1134         else  // usage for individual command
1135         {
1136                 #define VOTE_COMMAND(name, function, description, assignment) \
1137                         { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1138
1139                 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1140 #undef VOTE_COMMAND
1141
1142                 string cvarname = strcat("sv_vote_command_help_", argv(2));
1143                 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1144                         wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1145                 else
1146                         print_to(caller, "No documentation exists for this vote");
1147         }
1148 }
1149
1150 float VoteCommand_macro_command(entity caller, float argc, string vote_command)
1151 {
1152         #define VOTE_COMMAND(name, function, description, assignment) \
1153                 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1154
1155         VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1156 #undef VOTE_COMMAND
1157
1158         return false;
1159 }
1160
1161
1162 // ======================================
1163 //  Main function handling vote commands
1164 // ======================================
1165
1166 void VoteCommand(float request, entity caller, float argc, string vote_command)
1167 {
1168         // Guide for working with argc arguments by example:
1169         // argc:   1    - 2      - 3     - 4
1170         // argv:   0    - 1      - 2     - 3
1171         // cmd     vote - master - login - password
1172
1173         switch (request)
1174         {
1175                 case CMD_REQUEST_COMMAND:
1176                 {
1177                         if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1178                 }
1179
1180                 default:
1181                         print_to(caller, strcat(((argv(1) != "") ? strcat("Unknown vote command \"", argv(1), "\"") : "No command provided"), ". For a list of supported commands, try ", GetCommandPrefix(caller), " vote help.\n"));
1182                 case CMD_REQUEST_USAGE:
1183                 {
1184                         VoteCommand_macro_help(caller, argc);
1185                         return;
1186                 }
1187         }
1188 }