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