]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/vote.qc
List some new commands (their functions still need added)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / vote.qc
index 1fbdecad9fdf6714fff928c760ab9444eb140a35..d25ae95132a771b06d8214f870247f15f3d9a050 100644 (file)
@@ -1,3 +1,258 @@
+// =============================================
+//  Server side voting code, reworked by Samual
+//  Last updated: December 4th, 2011
+// =============================================
+
+#define VC_REQUEST_COMMAND 1
+#define VC_REQUEST_USAGE 2
+
+#define VC_ASGNMNT_BOTH 1
+#define VC_ASGNMNT_CLIENTONLY 2
+#define VC_ASGNMNT_SERVERONLY 3
+
+
+// ============================
+//  Misc. Supporting Functions
+// ============================
+
+float Votecommand_check_assignment(entity caller, float assignment)
+{
+       float from_server = (!caller);
+       
+       if((assignment == VC_ASGNMNT_BOTH) 
+               || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY) 
+               || (from_server && assignment == VC_ASGNMNT_SERVERONLY)))
+       {
+               print("check_assignment returned true\n");
+               return TRUE;
+       }
+       
+       print("check_assignment returned false\n");
+       return FALSE;
+}
+
+string VoteCommand_getprefix(entity caller)
+{
+       if(caller)
+               return "cmd";
+       else
+               return "sv_cmd";
+}
+
+float Nagger_SendEntity(entity to, float sendflags)
+{
+       float nags, i, f, b;
+       entity e;
+       WriteByte(MSG_ENTITY, ENT_CLIENT_NAGGER);
+
+       // bits:
+       //   1 = ready
+       //   2 = player needs to ready up
+       //   4 = vote
+       //   8 = player needs to vote
+       //  16 = warmup
+       // sendflags:
+       //  64 = vote counts
+       // 128 = vote string
+
+       nags = 0;
+       if(readycount)
+       {
+               nags |= 1;
+               if(to.ready == 0)
+                       nags |= 2;
+       }
+       if(votecalled)
+       {
+               nags |= 4;
+               if(to.vote_vote == 0)
+                       nags |= 8;
+       }
+       if(inWarmupStage)
+               nags |= 16;
+
+       if(sendflags & 64)
+               nags |= 64;
+
+       if(sendflags & 128)
+               nags |= 128;
+
+       if(!(nags & 4)) // no vote called? send no string
+               nags &~= (64 | 128);
+
+       WriteByte(MSG_ENTITY, nags);
+
+       if(nags & 64)
+       {
+               WriteByte(MSG_ENTITY, vote_yescount);
+               WriteByte(MSG_ENTITY, vote_nocount);
+               WriteByte(MSG_ENTITY, vote_needed_absolute);
+               WriteChar(MSG_ENTITY, to.vote_vote);
+       }
+
+       if(nags & 128)
+               WriteString(MSG_ENTITY, votecalledvote_display);
+
+       if(nags & 1)
+       {
+               for(i = 1; i <= maxclients; i += 8)
+               {
+                       for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
+                               if(clienttype(e) != CLIENTTYPE_REAL || e.ready)
+                                       f |= b;
+                       WriteByte(MSG_ENTITY, f);
+               }
+       }
+
+       return TRUE;
+}
+
+void Nagger_Init()
+{
+       Net_LinkEntity(nagger = spawn(), FALSE, 0, Nagger_SendEntity);
+}
+
+void Nagger_VoteChanged()
+{
+       if(nagger)
+               nagger.SendFlags |= 128;
+}
+
+void Nagger_VoteCountChanged()
+{
+       if(nagger)
+               nagger.SendFlags |= 64;
+}
+
+void Nagger_ReadyCounted()
+{
+       if(nagger)
+               nagger.SendFlags |= 1;
+}
+
+void ReadyRestartForce()
+{
+       local entity e;
+
+       bprint("^1Server is restarting...\n");
+
+       VoteReset();
+
+       // clear overtime
+       if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) {
+               //we have to decrease timelimit to its original value again!!
+               float newTL;
+               newTL = autocvar_timelimit;
+               newTL -= checkrules_overtimesadded * autocvar_timelimit_overtime;
+               cvar_set("timelimit", ftos(newTL));
+       }
+
+       checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
+
+
+       readyrestart_happened = 1;
+       game_starttime = time;
+       if(!g_ca && !g_arena)
+               game_starttime += RESTART_COUNTDOWN;
+       restart_mapalreadyrestarted = 0; //reset this var, needed when cvar sv_ready_restart_repeatable is in use
+
+       inWarmupStage = 0; //once the game is restarted the game is in match stage
+
+       //reset the .ready status of all players (also spectators)
+       FOR_EACH_CLIENTSLOT(e)
+               e.ready = 0;
+       readycount = 0;
+       Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
+
+       if(autocvar_teamplay_lockonrestart && teamplay) {
+               lockteams = 1;
+               bprint("^1The teams are now locked.\n");
+       }
+
+       //initiate the restart-countdown-announcer entity
+       if(autocvar_sv_ready_restart_after_countdown && !g_ca && !g_arena)
+       {
+               restartTimer = spawn();
+               restartTimer.think = restartTimer_Think;
+               restartTimer.nextthink = game_starttime;
+       }
+
+       //after a restart every players number of allowed timeouts gets reset, too
+       if(autocvar_sv_timeout)
+       {
+               FOR_EACH_REALPLAYER(e)
+                       e.allowedTimeouts = autocvar_sv_timeout_number;
+       }
+
+       //reset map immediately if this cvar is not set
+       if (!autocvar_sv_ready_restart_after_countdown)
+               reset_map(TRUE);
+
+       if(autocvar_sv_eventlog)
+               GameLogEcho(":restart");
+}
+
+void ReadyRestart()
+{
+       // no arena, assault support yet...
+       if(g_arena | g_assault | gameover | intermission_running | race_completing)
+               localcmd("restart\n");
+       else
+               localcmd("\nsv_hook_gamerestart\n");
+
+       ReadyRestartForce();
+
+       // reset ALL scores, but only do that at the beginning
+       //of the countdown if sv_ready_restart_after_countdown is off!
+       //Otherwise scores could be manipulated during the countdown!
+       if (!autocvar_sv_ready_restart_after_countdown)
+               Score_ClearAll();
+}
+
+/**
+ * Counts how many players are ready. If not enough players are ready, the function
+ * does nothing. If all players are ready, the timelimit will be extended and the
+ * restart_countdown variable is set to allow other functions like PlayerPostThink
+ * to detect that the countdown is now active. If the cvar sv_ready_restart_after_countdown
+ * is not set the map will be resetted.
+ *
+ * Function is called after the server receives a 'ready' sign from a player.
+ */
+void ReadyCount()
+{
+       local entity e;
+       local float r, p;
+
+       r = p = 0;
+
+       FOR_EACH_REALPLAYER(e)
+       {
+               p += 1;
+               if(e.ready)
+                       r += 1;
+       }
+
+       readycount = r;
+
+       Nagger_ReadyCounted();
+
+       if(r) // at least one is ready
+       if(r == p) // and, everyone is ready
+               ReadyRestart();
+}
+
+/**
+ * Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown
+ * is set)
+ */
+void restartTimer_Think() {
+       restart_mapalreadyrestarted = 1;
+       reset_map(TRUE);
+       Score_ClearAll();
+       remove(self);
+       return;
+}
+
 float VoteCheckNasty(string cmd)
 {
        if(strstrofs(cmd, ";", 0) >= 0)
@@ -55,7 +310,7 @@ entity GetKickVoteVictim(string vote, string cmd, entity caller)
                GetKickVoteVictim_newcommand = strcat(argv(0), " # ", ftos(num_for_edict(e)));
                if(argv(0) == "kickban")
                {
-                       GetKickVoteVictim_newcommand = strcat(GetKickVoteVictim_newcommand, " ", cvar_string("g_ban_default_bantime"), " ", cvar_string("g_ban_default_masksize"), " ", reason);
+                       GetKickVoteVictim_newcommand = strcat(GetKickVoteVictim_newcommand, " ", ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ", reason);
                }
                else if(argv(0) == "kick")
                {
@@ -116,242 +371,166 @@ float RemapVote(string vote, string cmd, entity e)
        return TRUE;
 }
 
-void VoteDialog_UpdateHighlight(float selected) {
-       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-       WriteByte(MSG_ONE, TE_CSQC_VOTE);
-       WriteByte(MSG_ONE, 1);
-       WriteByte(MSG_ONE, selected);
+
+// =======================
+//  Command Sub-Functions
+// =======================
+
+void VoteCommand_abstain(float request, entity caller)
+{
+       switch(request)
+       {
+               case VC_REQUEST_COMMAND:
+               {
+                       
+                       return;
+               }
+                       
+               default:
+               case VC_REQUEST_USAGE:
+               {
+                       print("\nUsage:^3 vote \n");
+                       print("  No arguments required.\n");
+                       return;
+               }
+       }
 }
 
-void VoteDialog_Reset() {
-       WriteByte(MSG_ALL, SVC_TEMPENTITY);
-       WriteByte(MSG_ALL, TE_CSQC_VOTERESET);
+void VoteCommand_stop(float request, entity caller)
+{
+       switch(request)
+       {
+               case VC_REQUEST_COMMAND:
+               {
+                       
+                       return;
+               }
+                       
+               default:
+               case VC_REQUEST_USAGE:
+               {
+                       print("\nUsage:^3 vote \n");
+                       print("  No arguments required.\n");
+                       return;
+               }
+       }
 }
 
-float GameCommand_Vote(string s, entity e) {
-       local float playercount;
-       float argc;
-       argc = tokenize_console(s);
-       if(argv(0) == "help") {
-               print_to(e, "  vote COMMANDS ARGUMENTS. See 'vhelp' for more info.");
-               return TRUE;
-       } else if(argv(0) == "vote") {
-               if(argv(1) == "") {
-                       print_to(e, "^1You have to supply a vote command. See 'vhelp' for more info.");
-               } else if(argv(1) == "help") {
-                       VoteHelp(e);
-               } else if(argv(1) == "status") {
-                       if(votecalled) {
-                               print_to(e, strcat("^7Vote for ", votecalledvote_display, "^7 called by ^7", VoteNetname(votecaller), "^7."));
-                       } else {
-                               print_to(e, "^1No vote called.");
-                       }
-               } else if(argv(1) == "call") {
-                       if(!e || cvar("sv_vote_call")) {
-                               if(cvar("sv_vote_nospectators") && e && e.classname != "player") {
-                                       print_to(e, "^1Error: Only players can call a vote."); // TODO invent a cvar name for allowing votes by spectators during warmup anyway
-                               }
-                               else if(timeoutStatus) { //don't allow a vote call during a timeout
-                                       print_to(e, "^1Error: You can not call a vote while a timeout is active.");
-                               }
-                               else if(votecalled) {
-                                       print_to(e, "^1There is already a vote called.");
-                               } else {
-                                       local string vote;
-                                       vote = VoteParse(s, argc);
-                                       if(vote == "") {
-                                               print_to(e, "^1Your vote is empty. See 'vhelp' for more info.");
-                                       } else if(e
-                                                       && time < e.vote_next) {
-                                               print_to(e, strcat("^1You have to wait ^2", ftos(ceil(e.vote_next - time)), "^1 seconds before you can again call a vote."));
-                                       } else if(VoteCheckNasty(vote)) {
-                                               print_to(e, "Syntax error in command. See 'vhelp' for more info.");
-                                       } else if(RemapVote(vote, "vcall", e)) {
-                                               votecalledvote = strzone(RemapVote_vote);
-                                               votecalledvote_display = strzone(RemapVote_display);
-                                               votecalled = TRUE;
-                                               votecalledmaster = FALSE;
-                                               votefinished = time + cvar("sv_vote_timeout");
-                                               votecaller = e; // remember who called the vote
-                                               if(e) {
-                                                       e.vote_vote = 1; // of course you vote yes
-                                                       e.vote_next = time + cvar("sv_vote_wait");
-                                               }
-                                               bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2 calls a vote for ", votecalledvote_display, "\n");
-                                               if(cvar("sv_eventlog"))
-                                                       GameLogEcho(strcat(":vote:vcall:", ftos(votecaller.playerid), ":", votecalledvote_display));
-                                               VoteCount(); // needed if you are the only one
-                                               Nagger_VoteChanged();
-                                               msg_entity = e;
-                                               VoteDialog_UpdateHighlight(1);
-
-                                               local entity player;
-                                               FOR_EACH_REALCLIENT(player)
-                                               {
-                                                       ++playercount;
-                                               }
-                                               if(playercount > 1) // don't announce a "vote now" sound if player is alone
-                                                       Announce("votecall");
-                                       } else {
-                                               print_to(e, "^1This vote is not ok. See 'vhelp' for more info.");
-                                       }
-                               }
-                       } else {
-                               print_to(e, "^1Vote calling is NOT allowed.");
-                       }
-               } else if(argv(1) == "stop") {
-                       if(!votecalled) {
-                               print_to(e, "^1No vote called.");
-                       } else if(e == votecaller) { // the votecaller can stop a vote
-                               VoteDialog_Reset();
-                               VoteStop(e);
-                       } else if(!e) { // server admin / console can too
-                               VoteDialog_Reset();
-                               VoteStop(e);
-                       } else if(e.vote_master) { // masters can too
-                               VoteDialog_Reset();
-                               VoteStop(e);
-                       } else {
-                               print_to(e, "^1You are not allowed to stop that Vote.");
-                       }
-               } else if(argv(1) == "master") {
-                       if(cvar("sv_vote_master")) {
-                               if(votecalled) {
-                                       print_to(e, "^1There is already a vote called.");
-                               } else {
-                                       votecalled = TRUE;
-                                       votecalledmaster = TRUE;
-                                       votecalledvote = strzone("XXX");
-                                       votecalledvote_display = strzone("^3master");
-                                       votefinished = time + cvar("sv_vote_timeout");
-                                       votecaller = e; // remember who called the vote
-                                       if(e) {
-                                               e.vote_vote = 1; // of course you vote yes
-                                               e.vote_next = time + cvar("sv_vote_wait");
-                                       }
-                                       bprint("\{1}^2* ^3", VoteNetname(votecaller), "^2 calls a vote to become ^3master^2.\n");
-                                       if(cvar("sv_eventlog"))
-                                               GameLogEcho(strcat(":vote:vcall:", ftos(votecaller.playerid), ":", votecalledvote_display));
-                                       VoteCount(); // needed if you are the only one
-                                       Nagger_VoteChanged();
-                               }
-                       } else {
-                               print_to(e, "^1Vote to become master is NOT allowed.");
-                       }
-               } else if(argv(1) == "do") {
-                       if(!e || e.vote_master) {
-                               local string dovote;
-                               dovote = VoteParse(s, argc);
-                               if(dovote == "") {
-                                       print_to(e, "^1Your command was empty. See 'vhelp' for more info.");
-                               } else if(VoteCheckNasty(dovote)) {
-                                       print_to(e, "Syntax error in command. See 'vhelp' for more info.");
-                               } else if(RemapVote(dovote, "vdo", e)) { // strcat seems to be necessary
-                                       bprint("\{1}^2* ^3", VoteNetname(e), "^2 used their ^3master^2 status to do \"^2", RemapVote_display, "^2\".\n");
-                                       if(cvar("sv_eventlog"))
-                                               GameLogEcho(strcat(":vote:vdo:", ftos(e.playerid), ":", RemapVote_display));
-                                       localcmd(strcat(RemapVote_vote, "\n"));
-                               } else {
-                                       print_to(e, "^1This command is not ok. See 'vhelp' for more info.");
-                               }
-                       } else {
-                               print_to(e, "^1You are NOT a master.  You might need to login or vote to become master first. See 'vhelp' for more info.");
-                       }
-               } else if(argv(1) == "login") {
-                       local string masterpwd;
-                       masterpwd = cvar_string("sv_vote_master_password");
-                       if(masterpwd != "") {
-                               local float granted;
-                               granted = (masterpwd == argv(2));
-                               if (e)
-                                       e.vote_master = granted;
-                               if(granted) {
-                                       print("Accepted master login from ", VoteNetname(e), "\n");
-                                       bprint("\{1}^2* ^3", VoteNetname(e), "^2 logged in as ^3master^2\n");
-                                       if(cvar("sv_eventlog"))
-                                               GameLogEcho(strcat(":vote:vlogin:", ftos(e.playerid)));
-                               }
-                               else
-                                       print("REJECTED master login from ", VoteNetname(e), "\n");
-                       }
-                       else
-                               print_to(e, "^1Login to become master is NOT allowed.");
-               } else if(argv(1) == "yes") {
-                       if(!votecalled) {
-                               print_to(e, "^1No vote called.");
-                       } else if (!e) {
-                               print_to(e, "^1You can't vote from the server console.");
-                       } else if(e.vote_vote == 0
-                                 || cvar("sv_vote_change")) {
-                               msg_entity = e;
-                               VoteDialog_UpdateHighlight(1);
-                               print_to(e, "^1You accepted the vote.");
-                               e.vote_vote = 1;
-                               centerprint_expire(e, CENTERPRIO_VOTE);
-                               if(!cvar("sv_vote_singlecount")) {
-                                       VoteCount();
-                               }
-                       } else {
-                               print_to(e, "^1You have already voted.");
-                       }
-               } else if(argv(1) == "no") {
-                       if(!votecalled) {
-                               print_to(e, "^1No vote called.");
-                       } else if (!e) {
-                               print_to(e, "^1You can't vote from the server console.");
-                       } else if(e.vote_vote == 0
-                                 || cvar("sv_vote_change")) {
-                               msg_entity = e;
-                               VoteDialog_UpdateHighlight(2);
-                               print_to(e, "^1You rejected the vote.");
-                               e.vote_vote = -1;
-                               centerprint_expire(e, CENTERPRIO_VOTE);
-                               if(!cvar("sv_vote_singlecount")) {
-                                       VoteCount();
-                               }
-                       } else {
-                               print_to(e, "^1You have already voted.");
-                       }
-               } else if(argv(1) == "abstain" || argv(1) == "dontcare") {
-                       if(!votecalled) {
-                               print_to(e, "^1No vote called.");
-                       } else if (!e) {
-                               print_to(e, "^1You can't vote from the server console.");
-                       } else if(e.vote_vote == 0
-                                 || cvar("sv_vote_change")) {
-                               msg_entity = e;
-                               VoteDialog_UpdateHighlight(3);
-                               print_to(e, "^1You abstained from your vote.");
-                               e.vote_vote = -2;
-                               centerprint_expire(e, CENTERPRIO_VOTE);
-                               if(!cvar("sv_vote_singlecount")) {
-                                       VoteCount();
-                               }
-                       } else {
-                               print_to(e, "^1You have already voted.");
-                       }
-               } else {
-                       // ignore this?
-                       print_to(e, "^1Unknown vote command.");
+/* use this when creating a new command, making sure to place it in alphabetical order.
+void VoteCommand_(float request)
+{
+       switch(request)
+       {
+               case VC_REQUEST_COMMAND:
+               {
+                       
+                       return;
+               }
+                       
+               default:
+               case VC_REQUEST_USAGE:
+               {
+                       print("\nUsage:^3 vote \n");
+                       print("  No arguments required.\n");
+                       return;
                }
-               return TRUE;
        }
+}
+*/
+
+
+// ==================================
+//  Macro system for server commands
+// ==================================
+
+// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
+#define VOTE_COMMANDS(request,caller,arguments) \
+       VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current poll", VC_ASGNMNT_CLIENTONLY) \
+       VOTE_COMMAND("force", VoteCommand_force(request, caller), "Force a result of a vote", VC_ASGNMNT_SERVERONLY) \
+       VOTE_COMMAND("help", VoteCommand_macro_help(caller), "Shows this information", VC_ASGNMNT_BOTH) \
+       VOTE_COMMAND("no", VoteCommand_no(request, caller), "Vote no in current poll", VC_ASGNMNT_CLIENTONLY) \
+       VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current poll", VC_ASGNMNT_BOTH) \
+       VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
+       VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Vote yes in current poll", VC_ASGNMNT_CLIENTONLY) \
+       //VOTE_COMMAND("", VoteCommand_(request, caller, arguments), "", ) \
+       /* nothing */
+
+void VoteCommand_macro_help(entity caller)
+{
+       print("\nUsage:^3 ", VoteCommand_getprefix(caller), " vote COMMAND...^7, where possible commands are:\n");
+       
+       #define VOTE_COMMAND(name,function,description,assignment) \
+               { if(Votecommand_check_assignment(caller, assignment)) { print("  ^2", name, "^7: ", description, "\n"); } }
+               
+       VOTE_COMMANDS(0, caller, 0)
+       #undef VOTE_COMMAND
+       
+       return;
+}
+
+float VoteCommand_macro_command(entity caller, float argc)
+{
+       #define VOTE_COMMAND(name,function,description,assignment) \
+               { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(0))) { function; return TRUE; } } }
+               
+       VOTE_COMMANDS(VC_REQUEST_COMMAND, caller, argc)
+       #undef VOTE_COMMAND
+       
+       return FALSE;
+}
+
+float VoteCommand_macro_usage(entity caller, float argc)
+{
+       #define VOTE_COMMAND(name,function,description,assignment) \
+               { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(1))) { function; return TRUE; } } }
+               
+       VOTE_COMMANDS(VC_REQUEST_USAGE, caller, argc)
+       #undef VOTE_COMMAND
+       
        return FALSE;
 }
 
+
+// ======================================
+//  Main function handling vote commands
+// ======================================
+
+void VoteCommand(entity caller, float argc) 
+{
+       if(strtolower(argv(0)) == "help") 
+       {
+               if(argc == 1)
+               {
+                       VoteCommand_macro_help(caller);
+                       return;
+               }
+               else if(VoteCommand_macro_usage(caller, argc))
+               {
+                       return;
+               }
+       }
+       else if(VoteCommand_macro_command(caller, argc))
+       {
+               return;
+       }
+       
+       // nothing above caught the command, must be invalid
+       //print("Unknown server command", ((command != "") ? strcat(" \"", command, "\"") : ""), ". For a list of supported commands, try sv_cmd help.\n");
+}
+
 void VoteHelp(entity e) {
-       local string vmasterdis;
-       if(!cvar("sv_vote_master")) {
+       string vmasterdis;
+       if(!autocvar_sv_vote_master) {
                vmasterdis = " ^1(disabled)";
        }
 
-       local string vlogindis;
-       if("" == cvar_string("sv_vote_master_password")) {
+       string vlogindis;
+       if("" == autocvar_sv_vote_master_password) {
                vlogindis = " ^1(disabled)";
        }
 
-       local string vcalldis;
-       if(!cvar("sv_vote_call")) {
+       string vcalldis;
+       if(!autocvar_sv_vote_call) {
                vcalldis = " ^1(disabled)";
        }
 
@@ -367,9 +546,9 @@ void VoteHelp(entity e) {
        print_to(e, "^7\"^2yes^7\", \"^2no^7\", \"^2abstain^7\" and \"^2dontcare^7\" to make your vote.");
        print_to(e, "^7If enough of the players vote yes the vote is accepted.");
        print_to(e, "^7If enough of the players vote no the vote is rejected.");
-       print_to(e, strcat("^7If neither the vote will timeout after ", cvar_string("sv_vote_timeout"), "^7 seconds."));
+       print_to(e, strcat("^7If neither the vote will timeout after ", ftos(autocvar_sv_vote_timeout), "^7 seconds."));
        print_to(e, "^7You can call a vote for or execute these commands:");
-       print_to(e, strcat("^3", cvar_string("sv_vote_commands"), "^7 and maybe further ^3arguments^7"));
+       print_to(e, strcat("^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7"));
 }
 
 string VoteNetname(entity e)
@@ -377,10 +556,10 @@ string VoteNetname(entity e)
        if(e) {
                return e.netname;
        } else {
-               if(cvar_string("sv_adminnick") != "") {
-                       return cvar_string("sv_adminnick");
+               if(autocvar_sv_adminnick != "") {
+                       return autocvar_sv_adminnick;
                } else {
-                       return cvar_string("hostname");
+                       return autocvar_hostname;
                }
        }
 }
@@ -393,7 +572,7 @@ string ValidateMap(string m, entity e)
                print_to(e, "This map is not available on this server.");
                return string_null;
        }
-       if(!cvar("sv_vote_override_mostrecent"))
+       if(!autocvar_sv_vote_override_mostrecent)
                if(Map_IsRecent(m))
                {
                        print_to(e, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
@@ -443,17 +622,17 @@ float VoteCommandInList(string votecommand, string list)
 }
 
 float VoteAllowed(string votecommand, string cmd) {
-       if(VoteCommandInList(votecommand, cvar_string("sv_vote_commands")))
+       if(VoteCommandInList(votecommand, autocvar_sv_vote_commands))
                return TRUE;
 
        if(cmd == "vdo")
        {
-               if(VoteCommandInList(votecommand, cvar_string("sv_vote_master_commands")))
+               if(VoteCommandInList(votecommand, autocvar_sv_vote_master_commands))
                        return TRUE;
        }
        else
        {
-               if(VoteCommandInList(votecommand, cvar_string("sv_vote_only_commands")))
+               if(VoteCommandInList(votecommand, autocvar_sv_vote_only_commands))
                        return TRUE;
        }
 
@@ -461,12 +640,11 @@ float VoteAllowed(string votecommand, string cmd) {
 }
 
 void VoteReset() {
-       local entity player;
+       entity player;
 
        FOR_EACH_CLIENT(player)
        {
                player.vote_vote = 0;
-               centerprint_expire(player, CENTERPRIO_VOTE);
        }
 
        if(votecalled)
@@ -478,6 +656,10 @@ void VoteReset() {
        votecalled = FALSE;
        votecalledmaster = FALSE;
        votefinished = 0;
+       votecalledvote = string_null;
+       votecalledvote_display = string_null;
+
+       Nagger_VoteChanged();
 }
 
 void VoteAccept() {
@@ -512,81 +694,71 @@ void VoteTimeout() {
 
 void VoteStop(entity stopper) {
        bprint("\{1}^2* ^3", VoteNetname(stopper), "^2 stopped ^3", VoteNetname(votecaller), "^2's vote\n");
-       if(cvar("sv_eventlog"))
+       if(autocvar_sv_eventlog)
                GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));
        if(stopper == votecaller) {
                // no wait for next vote so you can correct your vote
                if(votecaller) {
-                       votecaller.vote_next = time + cvar("sv_vote_stop");
+                       votecaller.vote_next = time + autocvar_sv_vote_stop;
                }
        }
        VoteReset();
 }
 
-void VoteSpam(float yescount, float nocount, float abstaincount, float notvoters, float mincount, string result)
+void VoteSpam(float notvoters, float mincount, string result)
 {
        string s;
        if(mincount >= 0)
        {
-               s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");
-               s = strcat(s, ftos(nocount), "^2 (^1");
+               s = strcat("\{1}^2* vote results: ^1", ftos(vote_yescount), "^2:^1");
+               s = strcat(s, ftos(vote_nocount), "^2 (^1");
                s = strcat(s, ftos(mincount), "^2 needed), ^1");
-               s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");
+               s = strcat(s, ftos(vote_abstaincount), "^2 didn't care, ^1");
                s = strcat(s, ftos(notvoters), "^2 didn't vote\n");
        }
        else
        {
-               s = strcat("\{1}^2* vote results: ^1", ftos(yescount), "^2:^1");
-               s = strcat(s, ftos(nocount), "^2, ^1");
-               s = strcat(s, ftos(abstaincount), "^2 didn't care, ^1");
+               s = strcat("\{1}^2* vote results: ^1", ftos(vote_yescount), "^2:^1");
+               s = strcat(s, ftos(vote_nocount), "^2, ^1");
+               s = strcat(s, ftos(vote_abstaincount), "^2 didn't care, ^1");
                s = strcat(s, ftos(notvoters), "^2 didn't have to vote\n");
        }
        bprint(s);
-       if(cvar("sv_eventlog"))
+       if(autocvar_sv_eventlog)
        {
-               s = strcat(":vote:v", result, ":", ftos(yescount));
-               s = strcat(s, ":", ftos(nocount));
-               s = strcat(s, ":", ftos(abstaincount));
+               s = strcat(":vote:v", result, ":", ftos(vote_yescount));
+               s = strcat(s, ":", ftos(vote_nocount));
+               s = strcat(s, ":", ftos(vote_abstaincount));
                s = strcat(s, ":", ftos(notvoters));
                s = strcat(s, ":", ftos(mincount));
                GameLogEcho(s);
        }
 }
 
-void VoteDialog_Update(float msg, float vyes, float vno, float needed) {
-       WriteByte(msg, SVC_TEMPENTITY);
-       WriteByte(msg, TE_CSQC_VOTE);
-       WriteByte(msg, 0);
-       WriteByte(msg, vyes);
-       WriteByte(msg, vno);
-       WriteByte(msg, needed);
-}
-
 void VoteCount() {
-       local float playercount;
+       float playercount;
        playercount = 0;
-       local float yescount;
-       yescount = 0;
-       local float nocount;
-       nocount = 0;
-       local float abstaincount;
-       abstaincount = 0;
-       local entity player;
+       vote_yescount = 0;
+       vote_nocount = 0;
+       vote_abstaincount = 0;
+       entity player;
        //same for real players
-       local float realplayercount;
-       local float realplayeryescount;
-       local float realplayernocount;
-       local float realplayerabstaincount;
+       float realplayercount;
+       float realplayeryescount;
+       float realplayernocount;
+       float realplayerabstaincount;
        realplayercount = realplayernocount = realplayerabstaincount = realplayeryescount = 0;
 
+       Nagger_VoteCountChanged();
+
        FOR_EACH_REALCLIENT(player)
        {
                if(player.vote_vote == -1) {
-                       ++nocount;
+                       ++vote_nocount;
                } else if(player.vote_vote == 1) {
-                       ++yescount;
+                       ++vote_yescount;
                } else if(player.vote_vote == -2) {
-                       ++abstaincount;
+                       ++vote_abstaincount;
                }
                ++playercount;
                //do the same for real players
@@ -603,20 +775,27 @@ void VoteCount() {
        }
 
        //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)
-       if(cvar("sv_vote_nospectators"))
+       if(autocvar_sv_vote_nospectators)
        if(realplayercount > 0) {
-               yescount = realplayeryescount;
-               nocount = realplayernocount;
-               abstaincount = realplayerabstaincount;
+               vote_yescount = realplayeryescount;
+               vote_nocount = realplayernocount;
+               vote_abstaincount = realplayerabstaincount;
                playercount = realplayercount;
        }
 
        float votefactor, simplevotefactor;
-       votefactor = bound(0.5, cvar("sv_vote_majority_factor"), 0.999);
-       simplevotefactor = cvar("sv_vote_simple_majority_factor");
-       float needed;
-       needed = floor((playercount - abstaincount) * max(votefactor, simplevotefactor)) + 1;
-       VoteDialog_Update(MSG_ALL, yescount, nocount, needed);
+       votefactor = bound(0.5, autocvar_sv_vote_majority_factor, 0.999);
+       simplevotefactor = autocvar_sv_vote_simple_majority_factor;
+
+       // FIXME this number is a guess
+       vote_needed_absolute = floor((playercount - vote_abstaincount) * votefactor) + 1;
+       if(simplevotefactor)
+       {
+               simplevotefactor = bound(votefactor, simplevotefactor, 0.999);
+               vote_needed_simple = floor((vote_yescount + vote_nocount) * simplevotefactor) + 1;
+       }
+       else
+               vote_needed_simple = 0;
 
        if(votecalledmaster
           && playercount == 1) {
@@ -632,31 +811,28 @@ void VoteCount() {
                }
                VoteReset();
        } else {
-               if(yescount > (playercount - abstaincount) * votefactor)
+               if(vote_yescount >= vote_needed_absolute)
                {
-                       VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1, "yes");
+                       VoteSpam(playercount - vote_yescount - vote_nocount - vote_abstaincount, -1, "yes");
                        VoteAccept();
-                       VoteDialog_Reset();
                }
-               else if(nocount >= (playercount - abstaincount) * (1 - votefactor)) // that means, yescount cannot reach minyes any more
+               else if(vote_nocount > playercount - vote_abstaincount - vote_needed_absolute) // that means, vote_yescount cannot reach vote_needed_absolute any more
                {
-                       VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, -1, "no");
+                       VoteSpam(playercount - vote_yescount - vote_nocount - vote_abstaincount, -1, "no");
                        VoteReject();
-                       VoteDialog_Reset();
                }
                else if(time > votefinished)
                {
                        if(simplevotefactor)
                        {
                                string result;
-                               simplevotefactor = bound(votefactor, simplevotefactor, 0.999);
-                               if(yescount > (yescount + nocount) * simplevotefactor)
+                               if(vote_yescount >= vote_needed_simple)
                                        result = "yes";
-                               else if(yescount + nocount > 0)
+                               else if(vote_yescount + vote_nocount > 0)
                                        result = "no";
                                else
                                        result = "timeout";
-                               VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor(min((playercount - abstaincount) * votefactor, (yescount + nocount) * simplevotefactor)) + 1, result);
+                               VoteSpam(playercount - vote_yescount - vote_nocount - vote_abstaincount, min(vote_needed_absolute, vote_needed_simple), result);
                                if(result == "yes")
                                        VoteAccept();
                                else if(result == "no")
@@ -666,10 +842,9 @@ void VoteCount() {
                        }
                        else
                        {
-                               VoteSpam(yescount, nocount, abstaincount, playercount - yescount - nocount - abstaincount, floor((playercount - abstaincount) * votefactor) + 1, "timeout");
+                               VoteSpam(playercount - vote_yescount - vote_nocount - vote_abstaincount, vote_needed_absolute, "timeout");
                                VoteTimeout();
                        }
-               VoteDialog_Reset();
                }
        }
 }