diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 8d01262f..fdfb845a 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -622,42 +622,119 @@ void flagmsg(int flag, int action) } } -void vote(int yes) +votedisplayinfo *newvotedisplayinfo(playerent *owner, int type, char *arg1, char *arg2) { + if(type < 0 || type >= SA_NUM) return NULL; + votedisplayinfo *v = new votedisplayinfo(); + v->owner = owner; + v->type = type; + v->millis = lastmillis + (60+10)*1000; + char *msgs[] = { "kick player %s", "ban player %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to the enemy team", "give master to player %s", "load map %s in mode %s" }; + char *msg = msgs[type]; + switch(v->type) + { + case SA_KICK: + case SA_BAN: + case SA_FORCETEAM: + case SA_GIVEMASTER: + { + int cn = atoi(arg1); + playerent *p = cn == getclientnum() ? player1 : getclient(cn); + if(!p) return NULL; + s_sprintf(v->desc)(msg, colorname(p)); + break; + } + case SA_MASTERMODE: + s_sprintf(v->desc)(msg, atoi(arg1) == 0 ? "Open" : "Private"); + break; + case SA_AUTOTEAM: + s_sprintf(v->desc)(msg, atoi(arg1) == 0 ? "disable" : "enable"); + break; + default: + s_sprintf(v->desc)(msg, arg1, arg2); + break; + } + return v; +} + +votedisplayinfo *curvote = NULL, *calledvote = NULL; + +void callvote(char *type, char *arg1, char *arg2) +{ + if(calledvote || !type) return; + votedisplayinfo *v = newvotedisplayinfo(player1, atoi(type), arg1, arg2); + if(v) + { + calledvote = v; + + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + ucharbuf p(packet->data, packet->dataLength); + putint(p, SV_CALLVOTE); + putint(p, v->type); + switch(v->type) + { + case SA_MAP: + sendstring(arg1, p); + putint(p, atoi(arg2)); + break; + default: + putint(p, atoi(arg1)); + break; + } + enet_packet_resize(packet, p.length()); + sendpackettoserv(1, packet); + } +} + +void vote(int v) +{ + if(!curvote || v < 0 || v >= VOTE_NUM) return; ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); ucharbuf p(packet->data, packet->dataLength); putint(p, SV_VOTE); - putint(p, yes > 0 ? VOTE_YES : VOTE_NO); + putint(p, v); enet_packet_resize(packet, p.length()); sendpackettoserv(1, packet); + curvote->stats[v]++; } -void callvote(char *type, char *arg1, char *arg2 = NULL) -{ - int t = atoi(type); - ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - ucharbuf p(packet->data, packet->dataLength); - putint(p, SV_CALLVOTE); - putint(p, t); - switch(t) - { - case SA_MAP: - sendstring(arg1, p); - putint(p, atoi(arg2)); - break; - case SA_KICK: - case SA_BAN: - case SA_MASTERMODE: - case SA_AUTOTEAM: - case SA_FORCETEAM: - case SA_GIVEMASTER: - putint(p, atoi(arg1)); - break; - } - enet_packet_resize(packet, p.length()); - sendpackettoserv(1, packet); +void displayvote(votedisplayinfo *v) +{ + if(!v) return; + DELETEP(curvote); + curvote = v; + conoutf("%s called a vote: %s", colorname(v->owner), curvote->desc); } +void callvotesuc() +{ + if(!calledvote) return; + displayvote(calledvote); + calledvote = NULL; + vote(VOTE_YES); // not automatically done by the callvote to keep a clear sequence +} + +void callvoteerr(int e) +{ + if(e < 0 || e >= VOTEE_NUM) return; + char *verr[VOTEE_NUM] = { "voting is currently disabled", "there is already a vote pending", "you have already voted", "vote limit reached for the current game" }; + conoutf("\f3could not vote: %s", verr[e]); + DELETEP(calledvote); +} + +void votecount(int v) { if(curvote && v >= 0 && v < VOTE_NUM) curvote->stats[v]++; } +void voteresult(int v) +{ + if(curvote && v >= 0 && v < VOTE_NUM) + { + curvote->result = v; + curvote->millis = lastmillis + 5000; + } +} + +COMMAND(callvote, ARG_3STR); +COMMAND(vote, ARG_1INT); + void setmaster(int claim) { addmsg(SV_SETMASTER, "ri", claim != 0 ? 1 : 0); @@ -670,8 +747,6 @@ void setadmin(char *claim, char *password) else addmsg(SV_SETADMIN, "ris", atoi(claim) != 0 ? 1 : 0, password); } -COMMAND(vote, ARG_1INT); -COMMAND(callvote, ARG_3STR); COMMAND(setmaster, ARG_1INT); COMMAND(setadmin, ARG_2STR); diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 9d3ec71a..ce0398eb 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -619,67 +619,41 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) case SV_CALLVOTE: { - if(!d) return; - string action; - int type; - switch(type = getint(p)) + int type = getint(p); + if(type < 0 || type >= SA_NUM || !d) return; + votedisplayinfo *v = NULL; + int a; + switch(type) { - case SA_KICK: - case SA_BAN: - { - playerent *v = getclient(getint(p)); - if(!v) return; - s_sprintf(action)("%s player %s", type==SA_KICK ? "kick" : "ban", colorname(v)); - break; - } - case SA_REMBANS: s_strcpy(action, "remove all bans"); break; - case SA_MASTERMODE: s_sprintf(action)("set mastermode to %s", getint(p) == MM_OPEN ? "open" : "private"); break; - case SA_AUTOTEAM: s_sprintf(action)("%s autoteam", getint(p) == 1 ? "enable" : "disable"); break; - case SA_FORCETEAM: - { - playerent *v = getclient(getint(p)); - if(!v) return; - s_sprintf(action)("force player %s to the enemy team", colorname(v)); - break; - } - case SA_GIVEMASTER: - { - playerent *v = getclient(getint(p)); - if(!v) return; - s_sprintf(action)("give master to player %s", colorname(v)); - break; - } case SA_MAP: getstring(text, p); - s_sprintf(action)("load map %s in mode %s", text, modestr(getint(p))); + a = getint(p); + v = newvotedisplayinfo(d, type, text, (char *)&a); //fixme + break; + default: + a = getint(p); + v = newvotedisplayinfo(d, type, (char *)&a, NULL); break; - default: return; // fixme } - s_sprintfd(msg)("%s voted: %s", colorname(d), action); - conoutf(msg); + displayvote(v); break; } + case SV_CALLVOTESUC: + callvotesuc(); + break; + + case SV_CALLVOTEERR: + callvoteerr(getint(p)); + break; + case SV_VOTE: - { - getint(p); + votecount(getint(p)); break; - } case SV_VOTERESULT: - { - getint(p); + voteresult(getint(p)); break; - } - - case SV_VOTEERR: - { - char *verr[VOTEE_NUM] = { "voting is currently disabled", "there is already a vote pending", "you have already voted", "vote limit reached for the current game" }; - int e = getint(p); - if(e < 0 || e >= VOTEE_NUM) return; - conoutf("\f3could not vote: %s", verr[e]); - break; - } default: neterr("type"); diff --git a/source/src/cube.h b/source/src/cube.h index 7d83d3fe..ccd4a7db 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -58,6 +58,7 @@ #include "sound.h" #include "entity.h" #include "command.h" +#include "vote.h" typedef vector cvector; typedef vector ivector; diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index 6827c621..0853930d 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -115,7 +115,8 @@ char msgsizesl[] = // size inclusive message token, 0 for variable SV_MODELSKIN, 2, SV_FLAGPICKUP, 2, SV_FLAGDROP, 2, SV_FLAGRETURN, 2, SV_FLAGSCORE, 2, SV_FLAGRESET, 2, SV_FLAGINFO, 0, SV_FLAGS, 2, SV_ARENAWIN, 2, - SV_SETMASTER, 2, SV_SETADMIN, 0, SV_SERVOPINFO, 3, SV_CALLVOTE, 0, SV_VOTE, 2, SV_VOTEERR, 2, SV_VOTERESULT, 2, + SV_SETMASTER, 2, SV_SETADMIN, 0, SV_SERVOPINFO, 3, + SV_CALLVOTE, 0, SV_CALLVOTESUC, 1, SV_CALLVOTEERR, 2, SV_VOTE, 2, SV_VOTERESULT, 2, SV_FORCETEAM, 2, SV_AUTOTEAM, 2, SV_CONNECT, 0, SV_CLIENT, 0, diff --git a/source/src/protocol.h b/source/src/protocol.h index fcb89729..ce3e7aeb 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -20,14 +20,15 @@ enum SV_MODELSKIN, SV_FLAGPICKUP, SV_FLAGDROP, SV_FLAGRETURN, SV_FLAGSCORE, SV_FLAGRESET, SV_FLAGINFO, SV_FLAGS, SV_ARENAWIN, - SV_SETMASTER, SV_SETADMIN, SV_SERVOPINFO, SV_CALLVOTE, SV_VOTE, SV_VOTEERR, SV_VOTERESULT, + SV_SETMASTER, SV_SETADMIN, SV_SERVOPINFO, + SV_CALLVOTE, SV_CALLVOTESUC, SV_CALLVOTEERR, SV_VOTE, SV_VOTERESULT, SV_FORCETEAM, SV_AUTOTEAM, SV_CONNECT, SV_CLIENT }; enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEMASTER, SA_MAP, SA_NUM}; -enum { VOTE_NEUTRAL = 0, VOTE_YES, VOTE_NO }; +enum { VOTE_NEUTRAL = 0, VOTE_YES, VOTE_NO, VOTE_NUM }; enum { VOTEE_DISABLED = 0, VOTEE_CUR, VOTEE_MUL, VOTEE_MAX, VOTEE_TLIMIT, VOTEE_NUM }; enum { MM_OPEN, MM_PRIVATE, MM_NUM }; diff --git a/source/src/protos.h b/source/src/protos.h index 2aebb693..e1061fd0 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -161,6 +161,12 @@ extern void serveropcommand(int cmd, int arg1); extern void refreshsopmenu(void *menu, bool init); extern char *colorname(playerent *d, int num = 0, char *name = NULL, char *prefix = ""); extern char *colorping(int ping); +extern void callvotesuc(); +extern void callvoteerr(int e); +extern void displayvote(votedisplayinfo *v); +extern void voteresult(int v); +extern void votecount(int v); +extern votedisplayinfo *newvotedisplayinfo(playerent *owner, int type, char *arg1, char *arg2); // scoreboard extern void showscores(bool on); diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index ee022163..26280789 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -38,6 +38,18 @@ void drawctficon(float x, float y, float s, int col, int row, float ts) if(tex) drawicon(tex, x, y, s, col, row, ts); } +void drawvoteicon(float x, float y, int col, int row) +{ + static Texture *tex = NULL; + if(!tex) tex = textureload("packages/misc/voteicons.png"); + if(tex) + { + glDisable(GL_BLEND); + drawicon(tex, x, y, 240, col, row, 1/2.0f); + glEnable(GL_BLEND); + } +} + VARP(crosshairsize, 0, 15, 50); int dblend = 0; @@ -49,6 +61,7 @@ VARP(hideradar, 0, 0, 1); VARP(radarres, 1, 64, 1024); VARP(radarentsize, 1, 4, 64); VARP(hidectfhud, 0, 0, 1); +VARP(hidevote, 0, 0, 1); VAR(showmap, 0, 0, 1); @@ -325,6 +338,29 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat draw_textf("wvt %d", left, top+240, curvert); draw_textf("evt %d", left, top+320, xtraverts); } + if(!hidevote) + { + extern votedisplayinfo *curvote; + if(curvote && curvote->millis >= lastmillis) + { + const int left = 20*2, top = VIRTH; + draw_textf("%s called a vote", left, top+240, colorname(curvote->owner, 10)); + draw_textf("\f3§ %s", left, top+320, curvote->desc); + glColor3f(1,1,1); + switch(curvote->result) + { + case VOTE_NEUTRAL: + drawvoteicon(left, top, 0, 0); + draw_textf("Press F1/F2 to vote yes or no", left, top+400); + break; + default: + drawvoteicon(left, top, (curvote->result-1)&1, 1); + draw_textf("vote %s", left, top+400, curvote->result == VOTE_YES ? "PASSED" : "FAILED"); + break; + } + draw_textf("%d yes vs. %d no", left, top+480, curvote->stats[VOTE_YES], curvote->stats[VOTE_NO]); + } + } if(player1->state==CS_ALIVE) { diff --git a/source/src/server.cpp b/source/src/server.cpp index cb281845..72fa5f90 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -1279,23 +1279,26 @@ void checkvotes(bool forceend = false) int stats[3] = {0}; loopv(clients) if(clients[i]->type!=ST_EMPTY) { stats[clients[i]->vote]++; }; int total = stats[VOTE_NO]+stats[VOTE_YES]+stats[VOTE_NEUTRAL]; - loopi(2) if(stats[i]/(float)total > 0.51f || (forceend && VOTE_NO==i)) + const float requiredcount = 0.51f; + if(stats[VOTE_YES]/(float)total > requiredcount) { - resetvotes(); - sendf(-1, 1, "ri2", SV_VOTERESULT, i); - if(VOTE_YES==i) curvote->pass(); - delete curvote; - curvote = NULL; - return; + sendf(-1, 1, "ri2", SV_VOTERESULT, VOTE_YES); + curvote->pass(); } + else if(forceend || stats[VOTE_YES]/(float)total > requiredcount) + { + sendf(-1, 1, "ri2", SV_VOTERESULT, VOTE_NO); + } + resetvotes(); + DELETEP(curvote); } -bool vote(int sender, int vote) +bool vote(int sender, int vote) // true the vote was placed successfull { if(!curvote || !valid_client(sender) || vote < VOTE_YES || vote > VOTE_NO) return false; if(clients[sender]->vote != VOTE_NEUTRAL) { - sendf(sender, 1, "ri2", SV_VOTEERR, VOTEE_MUL); + sendf(sender, 1, "ri2", SV_CALLVOTEERR, VOTEE_MUL); return false; } else @@ -1306,10 +1309,10 @@ bool vote(int sender, int vote) } } -bool callvote(voteinfo *v) +bool callvote(voteinfo *v) // true if a regular vote was called { if(!v || !v->isvalid()) return false; - if(isdedicated && clients[v->owner]->role >= v->action->role) // pass server op commands + if(isdedicated && clients[v->owner]->role >= v->action->role || clients[v->owner]->type == ST_LOCAL) // pass server op commands { v->action->perform(); return false; @@ -1318,56 +1321,27 @@ bool callvote(voteinfo *v) { if(curvote) { - sendf(v->owner, 1, "ri2", SV_VOTEERR, VOTEE_CUR); + sendf(v->owner, 1, "ri2", SV_CALLVOTEERR, VOTEE_CUR); return false; } else if(configsets.length() && curcfgset < configsets.length() && !configsets[curcfgset].vote && clients[v->owner]->role == CR_DEFAULT) { - sendf(v->owner, 1, "ri2", SV_VOTEERR, VOTEE_DISABLED); + sendf(v->owner, 1, "ri2", SV_CALLVOTEERR, VOTEE_DISABLED); return false; } else { curvote = v; - if(!vote(v->owner, VOTE_YES)) + /*if(!vote(v->owner, VOTE_YES)) { - delete curvote; - curvote = NULL; + DELETEP(curvote); return false; - } + }*/ + return true; } - return true; } } -/*bool vote(char *map, int reqmode, int sender) -{ - if(!valid_client(sender)) return false; - - if(configsets.length() && curcfgset < configsets.length() && !configsets[curcfgset].vote && clients[sender]->role == CR_DEFAULT) - { - s_sprintfd(msg)("%s voted, but voting is currently disabled", clients[sender]->name); - sendservmsg(msg); - return false; - } - - s_strcpy(clients[sender]->mapvote, map); - clients[sender]->modevote = reqmode; - int yes = 0, no = 0; - loopv(clients) if(clients[i]->type!=ST_EMPTY) - { - if(clients[i]->mapvote[0]) { if(strcmp(clients[i]->mapvote, map)==0 && clients[i]->modevote==reqmode) yes++; else no++; } - else no++; - } - if(yes==1 && no==0) return true; // single player - s_sprintfd(msg)("%s suggests mode \"%s\" on map %s (set map to vote)", clients[sender]->name, modestr(reqmode), map); - sendservmsg(msg); - if(yes/(float)(yes+no) <= 0.5f && clients[sender]->role == CR_DEFAULT) return false; - sendservmsg("vote passed"); - resetvotes(); - return true; -}*/ - // sending of maps between clients string copyname; @@ -1833,7 +1807,7 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 } vi->owner = sender; vi->secs = lastsec+60; - if(callvote(vi)) { QUEUE_MSG; } + if(callvote(vi)) { QUEUE_MSG; sendf(sender, 1, "ri", SV_CALLVOTESUC); } else delete vi; break; } diff --git a/source/src/vote.h b/source/src/vote.h new file mode 100644 index 00000000..c14b0729 --- /dev/null +++ b/source/src/vote.h @@ -0,0 +1,7 @@ +struct votedisplayinfo +{ + playerent *owner; + int type, stats[VOTE_NUM], result, millis; + string desc; + votedisplayinfo() : owner(NULL), result(VOTE_NEUTRAL), millis(0) { loopi(VOTE_NUM) stats[i] = VOTE_NEUTRAL; } +}; \ No newline at end of file