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