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