]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/vote.qc
Add ring stats
[voretournament/voretournament.git] / data / qcsrc / server / vote.qc
1 float VoteCheckNasty(string cmd)\r
2 {\r
3         if(strstrofs(cmd, ";", 0) >= 0)\r
4                 return TRUE;\r
5         if(strstrofs(cmd, "\n", 0) >= 0)\r
6                 return TRUE;\r
7         if(strstrofs(cmd, "\r", 0) >= 0)\r
8                 return TRUE;\r
9         if(strstrofs(cmd, "$", 0) >= 0)\r
10                 return TRUE;\r
11         return FALSE;\r
12 }\r
13 \r
14 string GetKickVoteVictim_newcommand;\r
15 string GetKickVoteVictim_reason;\r
16 \r
17 entity GetKickVoteVictim(string vote, string cmd, entity caller)\r
18 {\r
19         float tokens;\r
20         string ns;\r
21         entity e;\r
22         string reason;\r
23 \r
24         tokens = tokenize_console(vote);\r
25         ns = "";\r
26 \r
27         e = GetCommandPlayerSlotTargetFromTokenizedCommand(tokens, 1);\r
28         if(e)\r
29         {\r
30                 if(ParseCommandPlayerSlotTarget_firsttoken < tokens)\r
31                         GetKickVoteVictim_reason = substring(vote, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken));\r
32                 else\r
33                         GetKickVoteVictim_reason = "";\r
34 \r
35                 reason = "";\r
36                 if(cmd != "vdo" || GetKickVoteVictim_reason == "")\r
37                         reason = "~"; // by convention, ~ prefixes a "unverified" kickban which will not be networked\r
38 \r
39                 if(substring(GetKickVoteVictim_reason, 0, 1) == "~")\r
40                 {\r
41                         reason = "~";\r
42                         GetKickVoteVictim_reason = substring(GetKickVoteVictim_reason, 1, strlen(GetKickVoteVictim_reason) - 1);\r
43                 }\r
44 \r
45                 if(caller)\r
46                         reason = strcat(reason, "player ", strdecolorize(caller.netname));\r
47                 else\r
48                         reason = strcat(reason, "console vote");\r
49                 if(GetKickVoteVictim_reason != "")\r
50                         reason = strcat(reason, ": ", strdecolorize(GetKickVoteVictim_reason));\r
51 \r
52                 if not(cvar_value_issafe(reason))\r
53                         reason = uri_escape(reason);\r
54 \r
55                 GetKickVoteVictim_newcommand = strcat(argv(0), " # ", ftos(num_for_edict(e)));\r
56                 if(argv(0) == "kickban")\r
57                 {\r
58                         GetKickVoteVictim_newcommand = strcat(GetKickVoteVictim_newcommand, " ", cvar_string("g_ban_default_bantime"), " ", cvar_string("g_ban_default_masksize"), " ", reason);\r
59                 }\r
60                 else if(argv(0) == "kick")\r
61                 {\r
62                         GetKickVoteVictim_newcommand = strcat(GetKickVoteVictim_newcommand, " ", reason);\r
63                 }\r
64                 return e;\r
65         }\r
66 \r
67         print_to(caller, strcat("Usage: ", cmd, " ", argv(0), " #playernumber (as in \"status\")\n"));\r
68         return world;\r
69 }\r
70 \r
71 string RemapVote_display;\r
72 string RemapVote_vote;\r
73 float RemapVote(string vote, string cmd, entity e)\r
74 {\r
75         float vote_argc;\r
76         entity victim;\r
77         vote_argc = tokenize_console(vote);\r
78 \r
79         if(!VoteAllowed(argv(0), cmd))\r
80                 return FALSE;\r
81 \r
82         // VoteAllowed tokenizes!\r
83         vote_argc = tokenize_console(vote);\r
84 \r
85         // remap chmap to gotomap (forces intermission)\r
86         if(vote_argc < 2)\r
87                 if(argv(0) == "chmap" || argv(0) == "gotomap" || argv(0) == "kick" || argv(0) == "kickban") // won't work without arguments\r
88                         return FALSE;\r
89         if(argv(0) == "chmap")\r
90         {\r
91                 vote = strcat("gotomap ", substring(vote, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));\r
92                 vote_argc = tokenize_console(vote);\r
93         }\r
94         if(argv(0) == "gotomap")\r
95         {\r
96                 if(!(vote = ValidateMap(substring(vote, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), e)))\r
97                         return FALSE;\r
98                 vote = strcat("gotomap ", vote);\r
99                 vote_argc = tokenize_console(vote); // ValidateMap may have done some stuff to it\r
100         }\r
101 \r
102         // make kick and kickban votes a bit nicer (and reject them if formatted badly)\r
103         if(argv(0) == "kick" || argv(0) == "kickban")\r
104         {\r
105                 if(!(victim = GetKickVoteVictim(vote, cmd, e)))\r
106                         return FALSE;\r
107                 RemapVote_vote = GetKickVoteVictim_newcommand;\r
108                 RemapVote_display = strcat("^1", vote, " (^7", victim.netname, "^1): ", GetKickVoteVictim_reason);\r
109         }\r
110         else\r
111         {\r
112                 RemapVote_vote = vote;\r
113                 RemapVote_display = strzone(strcat("^1", vote));\r
114         }\r
115 \r
116         return TRUE;\r
117 }\r
118 \r
119 void VoteDialog_UpdateHighlight(float selected) {\r
120         WriteByte(MSG_ONE, SVC_TEMPENTITY);\r
121         WriteByte(MSG_ONE, TE_CSQC_VOTE);\r
122         WriteByte(MSG_ONE, 1);\r
123         WriteShort(MSG_ONE, selected);\r
124 }\r
125 \r
126 void VoteDialog_Reset() {\r
127         WriteByte(MSG_ALL, SVC_TEMPENTITY);\r
128         WriteByte(MSG_ALL, TE_CSQC_VOTERESET);\r
129 }\r
130 \r
131 float GameCommand_Vote(string s, entity e) {\r
132         float argc;\r
133         argc = tokenize_console(s);\r
134         if(argv(0) == "help") {\r
135                 print_to(e, "  vote COMMANDS ARGUMENTS. See 'vhelp' for more info.");\r
136                 return TRUE;\r
137         } else if(argv(0) == "vote") {\r
138                 if(argv(1) == "") {\r
139                         print_to(e, "^1You have to supply a vote command. See 'vhelp' for more info.");\r
140                 } else if(argv(1) == "help") {\r
141                         VoteHelp(e);\r
142                 } else if(argv(1) == "status") {\r
143                         if(votecalled) {\r
144                                 print_to(e, strcat("^7Vote for ", votecalledvote_display, "^7 called by ^7", VoteNetname(votecaller), "^7."));\r
145                         } else {\r
146                                 print_to(e, "^1No vote called.");\r
147                         }\r
148                 } else if(argv(1) == "call") {\r
149                         if(!e || cvar("sv_vote_call")) {\r
150                                 if(cvar("sv_vote_nospectators") && e && e.classname != "player") {\r
151                                         print_to(e, "^1Error: Only players can call a vote."); // TODO invent a cvar name for allowing votes by spectators during warmup anyway\r
152                                 }\r
153                                 else if(timeoutStatus) { //don't allow a vote call during a timeout\r
154                                         print_to(e, "^1Error: You can not call a vote while a timeout is active.");\r
155                                 }\r
156                                 else if(votecalled) {\r
157                                         print_to(e, "^1There is already a vote called.");\r
158                                 } else {\r
159                                         local string vote;\r
160                                         vote = VoteParse(s, argc);\r
161                                         if(vote == "") {\r
162                                                 print_to(e, "^1Your vote is empty. See 'vhelp' for more info.");\r
163                                         } else if(e\r
164                                                 && time < e.vote_next) {\r
165                                                         print_to(e, strcat("^1You have to wait ^2", ftos(e.vote_next - time), "^1 seconds before you can again call a vote."));\r
166                                         } else if(VoteCheckNasty(vote)) {\r
167                                                 print_to(e, "Syntax error in command. See 'vhelp' for more info.");\r
168                                         } else if(RemapVote(vote, "vcall", e)) {\r
169                                                 votecalledvote = strzone(RemapVote_vote);\r
170                                                 votecalledvote_display = strzone(RemapVote_display);\r
171                                                 votecalled = TRUE;\r
172                                                 votecalledmaster = FALSE;\r
173                                                 votefinished = time + cvar("sv_vote_timeout");\r
174                                                 votecaller = e; // remember who called the vote\r
175                                                 if(e) {\r
176                                                         e.vote_vote = 1; // of course you vote yes\r
177                                                         e.vote_next = time + cvar("sv_vote_wait");\r
178                                                 }\r
179                                                 bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2 calls a vote for ", votecalledvote_display, "\n");\r
180                                                 if(cvar("sv_eventlog"))\r
181                                                         GameLogEcho(strcat(":vote:vcall:", ftos(votecaller.playerid), ":", votecalledvote_display));\r
182                                                 VoteCount(); // needed if you are the only one\r
183                                                 Nagger_VoteChanged();\r
184                                                 msg_entity = e;\r
185                                                 VoteDialog_UpdateHighlight(1);\r
186                                         } else {\r
187                                                 print_to(e, "^1This vote is not ok. See 'vhelp' for more info.");\r
188                                         }\r
189                                 }\r
190                         } else {\r
191                                 print_to(e, "^1Vote calling is NOT allowed.");\r
192                         }\r
193                 } else if(argv(1) == "stop") {\r
194                         if(!votecalled) {\r
195                                 print_to(e, "^1No vote called.");\r
196                         } else if(e == votecaller) { // the votecaller can stop a vote\r
197                                 VoteDialog_Reset();\r
198                                 VoteStop(e);\r
199                         } else if(!e) { // server admin / console can too\r
200                                 VoteDialog_Reset();\r
201                                 VoteStop(e);\r
202                         } else if(e.vote_master) { // masters can too\r
203                                 VoteDialog_Reset();\r
204                                 VoteStop(e);\r
205                         } else {\r
206                                 print_to(e, "^1You are not allowed to stop that Vote.");\r
207                         }\r
208                 } else if(argv(1) == "master") {\r
209                         if(cvar("sv_vote_master")) {\r
210                                 if(votecalled) {\r
211                                         print_to(e, "^1There is already a vote called.");\r
212                                 } else {\r
213                                         votecalled = TRUE;\r
214                                         votecalledmaster = TRUE;\r
215                                         votecalledvote = strzone("XXX");\r
216                                         votecalledvote_display = strzone("^3master");\r
217                                         votefinished = time + cvar("sv_vote_timeout");\r
218                                         votecaller = e; // remember who called the vote\r
219                                         if(e) {\r
220                                                 e.vote_vote = 1; // of course you vote yes\r
221                                                 e.vote_next = time + cvar("sv_vote_wait");\r
222                                         }\r
223                                         bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2 calls a vote to become ^3master^2.\n");\r
224                                         if(cvar("sv_eventlog"))\r
225                                                 GameLogEcho(strcat(":vote:vcall:", ftos(votecaller.playerid), ":", votecalledvote_display));\r
226                                         VoteCount(); // needed if you are the only one\r
227                                         Nagger_VoteChanged();\r
228                                 }\r
229                         } else {\r
230                                 print_to(e, "^1Vote to become master is NOT allowed.");\r
231                         }\r
232                 } else if(argv(1) == "do") {\r
233                         if(!e || e.vote_master) {\r
234                                 local string dovote;\r
235                                 dovote = VoteParse(s, argc);\r
236                                 if(dovote == "") {\r
237                                         print_to(e, "^1Your command was empty. See 'vhelp' for more info.");\r
238                                 } else if(VoteCheckNasty(dovote)) {\r
239                                         print_to(e, "Syntax error in command. See 'vhelp' for more info.");\r
240                                 } else if(RemapVote(dovote, "vdo", e)) { // strcat seems to be necessary\r
241                                         bprint("\{1}^2* ^3", VoteNetname(e), "^2 used their ^3master^2 status to do \"^2", RemapVote_display, "^2\".\n");\r
242                                         if(cvar("sv_eventlog"))\r
243                                                 GameLogEcho(strcat(":vote:vdo:", ftos(e.playerid), ":", RemapVote_display));\r
244                                         localcmd(strcat(RemapVote_vote, "\n"));\r
245                                 } else {\r
246                                         print_to(e, "^1This command is not ok. See 'vhelp' for more info.");\r
247                                 }\r
248                         } else {\r
249                                 print_to(e, "^1You are NOT a master.  You might need to login or vote to become master first. See 'vhelp' for more info.");\r
250                         }\r
251                 } else if(argv(1) == "login") {\r
252                         local string masterpwd;\r
253                         masterpwd = cvar_string("sv_vote_master_password");\r
254                         if(masterpwd != "") {\r
255                                 local float granted;\r
256                                 granted = (masterpwd == argv(2));\r
257                                 if (e)\r
258                                         e.vote_master = granted;\r
259                                 if(granted) {\r
260                                         print("Accepted master login from ", VoteNetname(e), "\n");\r
261                                         bprint("\{1}^2* ^3", VoteNetname(e), "^2 logged in as ^3master^2\n");\r
262                                         if(cvar("sv_eventlog"))\r
263                                                 GameLogEcho(strcat(":vote:vlogin:", ftos(e.playerid)));\r
264                                 }\r
265                                 else\r
266                                         print("REJECTED master login from ", VoteNetname(e), "\n");\r
267                         }\r
268                         else\r
269                                 print_to(e, "^1Login to become master is NOT allowed.");\r
270                 } else if(argv(1) == "yes") {\r
271                         if(!votecalled) {\r
272                                 print_to(e, "^1No vote called.");\r
273                         } else if (!e) {\r
274                                 print_to(e, "^1You can't vote from the server console.");\r
275                         } else if(e.vote_vote == 0\r
276                                   || cvar("sv_vote_change")) {\r
277                                 msg_entity = e;\r
278                                 VoteDialog_UpdateHighlight(1);\r
279                                 print_to(e, "^1You accepted the vote.");\r
280                                 e.vote_vote = 1;\r
281                                 centerprint_expire(e, CENTERPRIO_VOTE);\r
282                                 if(!cvar("sv_vote_singlecount")) {\r
283                                         VoteCount();\r
284                                 }\r
285                         } else {\r
286                                 print_to(e, "^1You have already voted.");\r
287                         }\r
288                 } else if(argv(1) == "no") {\r
289                         if(!votecalled) {\r
290                                 print_to(e, "^1No vote called.");\r
291                         } else if (!e) {\r
292                                 print_to(e, "^1You can't vote from the server console.");\r
293                         } else if(e.vote_vote == 0\r
294                                   || cvar("sv_vote_change")) {\r
295                                 msg_entity = e;\r
296                                 VoteDialog_UpdateHighlight(2);\r
297                                 print_to(e, "^1You rejected the vote.");\r
298                                 e.vote_vote = -1;\r
299                                 centerprint_expire(e, CENTERPRIO_VOTE);\r
300                                 if(!cvar("sv_vote_singlecount")) {\r
301                                         VoteCount();\r
302                                 }\r
303                         } else {\r
304                                 print_to(e, "^1You have already voted.");\r
305                         }\r
306                 } else if(argv(1) == "abstain" || argv(1) == "dontcare") {\r
307                         if(!votecalled) {\r
308                                 print_to(e, "^1No vote called.");\r
309                         } else if (!e) {\r
310                                 print_to(e, "^1You can't vote from the server console.");\r
311                         } else if(e.vote_vote == 0\r
312                                   || cvar("sv_vote_change")) {\r
313                                 msg_entity = e;\r
314                                 VoteDialog_UpdateHighlight(3);\r
315                                 print_to(e, "^1You abstained from your vote.");\r
316                                 e.vote_vote = -2;\r
317                                 centerprint_expire(e, CENTERPRIO_VOTE);\r
318                                 if(!cvar("sv_vote_singlecount")) {\r
319                                         VoteCount();\r
320                                 }\r
321                         } else {\r
322                                 print_to(e, "^1You have already voted.");\r
323                         }\r
324                 } else {\r
325                         // ignore this?\r
326                         print_to(e, "^1Unknown vote command.");\r
327                 }\r
328                 return TRUE;\r
329         }\r
330         return FALSE;\r
331 }\r
332 \r
333 void VoteHelp(entity e) {\r
334         local string vmasterdis;\r
335         if(!cvar("sv_vote_master")) {\r
336                 vmasterdis = " ^1(disabled)";\r
337         }\r
338 \r
339         local string vlogindis;\r
340         if("" == cvar_string("sv_vote_master_password")) {\r
341                 vlogindis = " ^1(disabled)";\r
342         }\r
343 \r
344         local string vcalldis;\r
345         if(!cvar("sv_vote_call")) {\r
346                 vcalldis = " ^1(disabled)";\r
347         }\r
348 \r
349         print_to(e, "^7You can use voting with \"^2cmd vote help^7\" \"^2cmd vote status^7\" \"^2cmd vote call ^3COMMAND ARGUMENTS^7\" \"^2cmd vote stop^7\" \"^2cmd vote master^7\" \"^2cmd vote login^7\" \"^2cmd vote do ^3COMMAND ARGUMENTS^7\" \"^2cmd vote yes^7\" \"^2cmd vote no^7\" \"^2cmd vote abstain^7\" \"^2cmd vote dontcare^7\".");\r
350         print_to(e, "^7Or if your version is up to date you can use these aliases \"^2vhelp^7\" \"^2vstatus^7\" \"^2vcall ^3COMMAND ARGUMENTS^7\" \"^2vstop^7\" \"^2vmaster^7\" \"^2vlogin^7\" \"^2vdo ^3COMMAND ARGUMENTS^7\" \"^2vyes^7\" \"^2vno^7\" \"^2abstain^7\" \"^2vdontcare^7\".");\r
351         print_to(e, "^7\"^2help^7\" shows this info.");\r
352         print_to(e, "^7\"^2status^7\" shows if there is a vote called and who called it.");\r
353         print_to(e, strcat("^7\"^2call^7\" is used to call a vote. See the list of allowed commands.", vcalldis, "^7"));\r
354         print_to(e, "^7\"^2stop^7\" can be used by the vote caller or an admin to stop a vote and maybe correct it.");\r
355         print_to(e, strcat("^7\"^2master^7\" call a vote to become master who can execute commands without a vote", vmasterdis, "^7"));\r
356         print_to(e, strcat("^7\"^2login^7\" login to become master who can execute commands without a vote.", vlogindis, "^7"));\r
357         print_to(e, "^7\"^2do^7\" executes a command if you are a master. See the list of allowed commands.");\r
358         print_to(e, "^7\"^2yes^7\", \"^2no^7\", \"^2abstain^7\" and \"^2dontcare^7\" to make your vote.");\r
359         print_to(e, "^7If enough of the players vote yes the vote is accepted.");\r
360         print_to(e, "^7If enough of the players vote no the vote is rejected.");\r
361         print_to(e, strcat("^7If neither the vote will timeout after ", cvar_string("sv_vote_timeout"), "^7 seconds."));\r
362         print_to(e, "^7You can call a vote for or execute these commands:");\r
363         print_to(e, strcat("^3", cvar_string("sv_vote_commands"), "^7 and maybe further ^3arguments^7"));\r
364 }\r
365 \r
366 string VoteNetname(entity e)\r
367 {\r
368         if(e) {\r
369                 return e.netname;\r
370         } else {\r
371                 if(cvar_string("sv_adminnick") != "") {\r
372                         return cvar_string("sv_adminnick");\r
373                 } else {\r
374                         return cvar_string("hostname");\r
375                 }\r
376         }\r
377 }\r
378 \r
379 string ValidateMap(string m, entity e)\r
380 {\r
381         m = MapInfo_FixName(m);\r
382         if(!m)\r
383         {\r
384                 print_to(e, "This map is not available on this server.");\r
385                 return string_null;\r
386         }\r
387         if(!cvar("sv_vote_override_mostrecent"))\r
388                 if(Map_IsRecent(m))\r
389                 {\r
390                         print_to(e, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");\r
391                         return string_null;\r
392                 }\r
393         if(!MapInfo_CheckMap(m))\r
394         {\r
395                 print_to(e, strcat("^1Invalid mapname, \"^3", m, "^1\" does not support the current game mode."));\r
396                 return string_null;\r
397         }\r
398 \r
399         return m;\r
400 }\r
401 \r
402 \r
403 void VoteThink() {\r
404         if(votefinished > 0) // a vote was called\r
405         if(time > votefinished) // time is up\r
406         {\r
407                 VoteCount();\r
408         }\r
409 }\r
410 \r
411 string VoteParse(string all, float argc) {\r
412         if(argc < 3)\r
413                 return "";\r
414         return substring(all, argv_start_index(2), argv_end_index(-1) - argv_start_index(2));\r
415 }\r
416 \r
417 float VoteCommandInList(string votecommand, string list)\r
418 {\r
419         string l;\r
420         l = strcat(" ", list, " ");\r
421         \r
422         if(strstrofs(l, strcat(" ", votecommand, " "), 0) >= 0)\r
423                 return TRUE;\r
424         \r
425         // if gotomap is allowed, chmap is too, and vice versa\r
426         if(votecommand == "gotomap")\r
427                 if(strstrofs(l, " chmap ", 0) >= 0)\r
428                         return TRUE;\r
429         if(votecommand == "chmap")\r
430                 if(strstrofs(l, " gotomap ", 0) >= 0)\r
431                         return TRUE;\r
432         \r
433         return FALSE;\r
434 }\r
435 \r
436 float VoteAllowed(string votecommand, string cmd) {\r
437         if(VoteCommandInList(votecommand, cvar_string("sv_vote_commands")))\r
438                 return TRUE;\r
439 \r
440         if(cmd == "vdo")\r
441         {\r
442                 if(VoteCommandInList(votecommand, cvar_string("sv_vote_master_commands")))\r
443                         return TRUE;\r
444         }\r
445         else\r
446         {\r
447                 if(VoteCommandInList(votecommand, cvar_string("sv_vote_only_commands")))\r
448                         return TRUE;\r
449         }\r
450 \r
451         return FALSE;\r
452 }\r
453 \r
454 void VoteReset() {\r
455         local entity player;\r
456 \r
457         FOR_EACH_CLIENT(player)\r
458         {\r
459                 player.vote_vote = 0;\r
460                 centerprint_expire(player, CENTERPRIO_VOTE);\r
461         }\r
462 \r
463         if(votecalled)\r
464         {\r
465                 strunzone(votecalledvote);\r
466                 strunzone(votecalledvote_display);\r
467         }\r
468 \r
469         votecalled = FALSE;\r
470         votecalledmaster = FALSE;\r
471         votefinished = 0;\r
472 }\r
473 \r
474 void VoteAccept() {\r
475         bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ^1", votecalledvote_display, "^2 was accepted\n");\r
476         if(votecalledmaster)\r
477         {\r
478                 if(votecaller) {\r
479                         votecaller.vote_master = 1;\r
480                 }\r
481         } else {\r
482                 localcmd(strcat(votecalledvote, "\n"));\r
483         }\r
484         if(votecaller) {\r
485                 votecaller.vote_next = 0; // people like your votes,\r
486                                           // no wait for next vote\r
487         }\r
488         VoteReset();\r
489 }\r
490 \r
491 void VoteReject() {\r
492         bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ", votecalledvote_display, "^2 was rejected\n");\r
493         VoteReset();\r
494 }\r
495 \r
496 void VoteTimeout() {\r
497         bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2's vote for ", votecalledvote_display, "^2 timed out\n");\r
498         VoteReset();\r
499 }\r
500 \r
501 void VoteStop(entity stopper) {\r
502         bprint("\{1}^2* ^3", VoteNetname(stopper), "^2 stopped ^3", VoteNetname(votecaller), "^2's vote\n");\r
503         if(cvar("sv_eventlog"))\r
504                 GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));\r
505         if(stopper == votecaller) {\r
506                 // no wait for next vote so you can correct your vote\r
507                 if(votecaller) {\r
508                         votecaller.vote_next = time + cvar("sv_vote_stop");\r
509                 }\r
510         }\r
511         VoteReset();\r
512 }\r
513 \r
514 void VoteSpam(float yescount, float nocount, float abstaincount, float notvoters, float mincount, string result)\r
515 {\r
516         string s;\r
517         if(mincount >= 0)\r
518         {\r
519                 s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");\r
520                 s = strcat(s, ftos(nocount), "^2 (^1");\r
521                 s = strcat(s, ftos(mincount), "^2 needed), ^1");\r
522                 s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");\r
523                 s = strcat(s, ftos(notvoters), "^2 didn't vote\n");\r
524         }\r
525         else\r
526         {\r
527                 s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");\r
528                 s = strcat(s, ftos(nocount), "^2, ^1");\r
529                 s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");\r
530                 s = strcat(s, ftos(notvoters), "^2 didn't have to vote\n");\r
531         }\r
532         bprint(s);\r
533         if(cvar("sv_eventlog"))\r
534         {\r
535                 s = strcat(":vote:v", result, ":", ftos(yescount));\r
536                 s = strcat(s, ":", ftos(nocount));\r
537                 s = strcat(s, ":", ftos(abstaincount));\r
538                 s = strcat(s, ":", ftos(notvoters));\r
539                 s = strcat(s, ":", ftos(mincount));\r
540                 GameLogEcho(s);\r
541         }\r
542 }\r
543 \r
544 void VoteDialog_Update(float msg, float vyes, float vno, float needed) {\r
545         WriteByte(msg, SVC_TEMPENTITY);\r
546         WriteByte(msg, TE_CSQC_VOTE);\r
547         WriteByte(msg, 0);\r
548         WriteShort(msg, vyes);\r
549         WriteShort(msg, vno);\r
550         WriteShort(msg, needed);\r
551 }\r
552 \r
553 void VoteCount() {\r
554         local float playercount;\r
555         playercount = 0;\r
556         local float yescount;\r
557         yescount = 0;\r
558         local float nocount;\r
559         nocount = 0;\r
560         local float abstaincount;\r
561         abstaincount = 0;\r
562         local entity player;\r
563         //same for real players\r
564         local float realplayercount;\r
565         local float realplayeryescount;\r
566         local float realplayernocount;\r
567         local float realplayerabstaincount;\r
568         realplayercount = realplayernocount = realplayerabstaincount = realplayeryescount = 0;\r
569 \r
570         FOR_EACH_REALCLIENT(player)\r
571         {\r
572                 if(player.vote_vote == -1) {\r
573                         ++nocount;\r
574                 } else if(player.vote_vote == 1) {\r
575                         ++yescount;\r
576                 } else if(player.vote_vote == -2) {\r
577                         ++abstaincount;\r
578                 }\r
579                 ++playercount;\r
580                 //do the same for real players\r
581                 if(player.classname == "player") {\r
582                         if(player.vote_vote == -1) {\r
583                                 ++realplayernocount;\r
584                         } else if(player.vote_vote == 1) {\r
585                                 ++realplayeryescount;\r
586                         } else if(player.vote_vote == -2) {\r
587                                 ++realplayerabstaincount;\r
588                         }\r
589                         ++realplayercount;\r
590                 }\r
591         }\r
592 \r
593         //in tournament mode, if we have at least one player then don't make the vote dependent on spectators (so specs don't have to press F1)\r
594         if(cvar("sv_vote_nospectators"))\r
595         if(realplayercount > 0) {\r
596                 yescount = realplayeryescount;\r
597                 nocount = realplayernocount;\r
598                 abstaincount = realplayerabstaincount;\r
599                 playercount = realplayercount;\r
600         }\r
601 \r
602         float votefactor, simplevotefactor;\r
603         votefactor = bound(0.5, cvar("sv_vote_majority_factor"), 0.999);\r
604         simplevotefactor = cvar("sv_vote_simple_majority_factor");\r
605         float needed;\r
606         needed = floor((playercount - abstaincount) * max(votefactor, simplevotefactor)) + 1;\r
607         VoteDialog_Update(MSG_ALL, yescount, nocount, needed);\r
608 \r
609         if(votecalledmaster\r
610            && playercount == 1) {\r
611                 // if only one player is on the server becoming vote\r
612                 // master is not allowed.  This could be used for\r
613                 // trolling or worse. 'self' is the user who has\r
614                 // called the vote because this function is called\r
615                 // by SV_ParseClientCommand. Maybe all voting should\r
616                 // be disabled for a single player?\r
617                 print_to(votecaller, "^1You are the only player on this server so you can not become vote master.");\r
618                 if(votecaller) {\r
619                         votecaller.vote_next = 0;\r
620                 }\r
621                 VoteReset();\r
622         } else {\r
623                 if(yescount > (playercount - abstaincount) * votefactor)\r
624                 {\r
625                         VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1, "yes");\r
626                         VoteAccept();\r
627                         VoteDialog_Reset();\r
628                 }\r
629                 else if(nocount >= (playercount - abstaincount) * (1 - votefactor)) // that means, yescount cannot reach minyes any more\r
630                 {\r
631                         VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1, "no");\r
632                         VoteReject();\r
633                         VoteDialog_Reset();\r
634                 }\r
635                 else if(time > votefinished)\r
636                 {\r
637                         if(simplevotefactor)\r
638                         {\r
639                                 string result;\r
640                                 simplevotefactor = bound(votefactor, simplevotefactor, 0.999);\r
641                                 if(yescount > (yescount + nocount) * simplevotefactor)\r
642                                         result = "yes";\r
643                                 else if(yescount + nocount > 0)\r
644                                         result = "no";\r
645                                 else\r
646                                         result = "timeout";\r
647                                 VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor(min((playercount - abstaincount) * votefactor, (yescount + nocount) * simplevotefactor)) + 1, result);\r
648                                 if(result == "yes")\r
649                                         VoteAccept();\r
650                                 else if(result == "no")\r
651                                         VoteReject();\r
652                                 else\r
653                                         VoteTimeout();\r
654                         }\r
655                         else\r
656                         {\r
657                                 VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor((playercount - abstaincount) * votefactor) + 1, "timeout");\r
658                                 VoteTimeout();\r
659                         }\r
660                 VoteDialog_Reset();\r
661                 }\r
662         }\r
663 }\r