X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fvote.qc;h=d25ae95132a771b06d8214f870247f15f3d9a050;hb=63cd9ca069e838bf30a785dfc3500c50fdf94340;hp=1fbdecad9fdf6714fff928c760ab9444eb140a35;hpb=06f163284faddd0c1e8d66e198eba4771e3ea2f8;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/vote.qc b/qcsrc/server/vote.qc index 1fbdecad9..d25ae9513 100644 --- a/qcsrc/server/vote.qc +++ b/qcsrc/server/vote.qc @@ -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(); } } }