forked from id/quake-qw-qc
Add our first iteration of the callvote system.
This commit is contained in:
parent
6611c57cbf
commit
d499fc138d
3 changed files with 332 additions and 2 deletions
|
@ -1,4 +1,4 @@
|
|||
./qwprogs.dat
|
||||
../qwprogs.dat
|
||||
|
||||
defs.qc
|
||||
subs.qc
|
||||
|
@ -15,5 +15,5 @@ buttons.qc
|
|||
triggers.qc
|
||||
plats.qc
|
||||
misc.qc
|
||||
|
||||
server.qc
|
||||
voting.qc
|
||||
|
|
327
voting.qc
Normal file
327
voting.qc
Normal file
|
@ -0,0 +1,327 @@
|
|||
/* In order to incorporate this file into your mod, you have to do two things:
|
||||
|
||||
1. Include this file in your progs.src
|
||||
2. Call Vote_Init(); inside worldspawn.
|
||||
|
||||
You need fteqcc and fteqw-sv for this to work.
|
||||
|
||||
*/
|
||||
|
||||
/* builtins required */
|
||||
float(string) tokenize = #441;
|
||||
string(float) argv = #442;
|
||||
void(entity e, string s) clientcommand = #440;
|
||||
void(entity player, string key, string value) forceinfokey = #213;
|
||||
string(string key) serverkey = #354;
|
||||
float(string key, optional float assumevalue) serverkeyfloat = #0:serverkeyfloat;
|
||||
|
||||
void ChatMessage_Parse( entity, string );
|
||||
string Util_TimeToString(float fTime);
|
||||
void Vote_Cmd_VoteYes(void);
|
||||
void Vote_Cmd_VoteNo(void);
|
||||
void Vote_Cmd_CallVote(string);
|
||||
void Vote_Help(entity);
|
||||
|
||||
var string g_strVoteCmd;
|
||||
var float g_flVoteTime;
|
||||
var float g_iVoteState;
|
||||
var float autocvar_mp_allowvote = 1;
|
||||
|
||||
.float voted;
|
||||
entity voteHandler;
|
||||
|
||||
enum
|
||||
{
|
||||
VOTE_INACTIVE,
|
||||
VOTE_INPROGRESS,
|
||||
VOTE_PASSED
|
||||
};
|
||||
|
||||
/* callback from fteqw-sv */
|
||||
void
|
||||
SV_ParseClientCommand(string cmd)
|
||||
{
|
||||
float argc;
|
||||
|
||||
argc = tokenize(cmd);
|
||||
|
||||
switch (argv(0)) {
|
||||
case "say":
|
||||
clientcommand(self, cmd);
|
||||
ChatMessage_Parse(self, argv(1));
|
||||
break;
|
||||
default:
|
||||
clientcommand(self, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ChatMessage_Parse(entity sayingEnt, string commandString)
|
||||
{
|
||||
float isMap = 0;
|
||||
if (whichpack(strcat("maps/", commandString, ".bsp"))) {
|
||||
isMap = 1;
|
||||
}
|
||||
|
||||
float args = tokenize(commandString);
|
||||
|
||||
switch (argv(0)) {
|
||||
case "yes":
|
||||
self = sayingEnt;
|
||||
Vote_Cmd_VoteYes();
|
||||
break;
|
||||
case "no":
|
||||
self = sayingEnt;
|
||||
Vote_Cmd_VoteNo();
|
||||
break;
|
||||
case "rtv":
|
||||
bprint(PRINT_CHAT, sprintf("rock the vote requested by %s\n", sayingEnt.netname));
|
||||
break;
|
||||
case "timeleft":
|
||||
string msg;
|
||||
string timestring;
|
||||
float timeleft;
|
||||
timeleft = cvar("timelimit") - (time / 60);
|
||||
timestring = Util_TimeToString(timeleft);
|
||||
msg = sprintf("we have %s minutes remaining\n", timestring);
|
||||
sprint(sayingEnt, PRINT_CHAT, msg);
|
||||
break;
|
||||
case "callvote":
|
||||
Vote_Cmd_CallVote(commandString);
|
||||
break;
|
||||
case "votehelp":
|
||||
Vote_Help(sayingEnt);
|
||||
break;
|
||||
default:
|
||||
if (isMap) {
|
||||
bprint(PRINT_CHAT, sprintf("%s was nominated by %s\n", commandString, sayingEnt.netname));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
Util_TimeToString(float fTime)
|
||||
{
|
||||
fTime = rint(fTime);
|
||||
|
||||
switch (fTime) {
|
||||
case 0: return "less than one";
|
||||
case 1: return "one";
|
||||
case 2: return "two";
|
||||
case 3: return "three";
|
||||
case 4: return "four";
|
||||
case 5: return "five";
|
||||
case 6: return "six";
|
||||
case 7: return "seven";
|
||||
case 8: return "eight";
|
||||
case 9: return "nine";
|
||||
case 10: return "ten";
|
||||
default: return "over ten";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Vote_End(void)
|
||||
{
|
||||
|
||||
localcmd(sprintf("%s\n", g_strVoteCmd));
|
||||
g_flVoteTime = 0.0f;
|
||||
g_iVoteState = VOTE_INACTIVE;
|
||||
remove(voteHandler);
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Reset(void)
|
||||
{
|
||||
forceinfokey(world, "votes_y", "0");
|
||||
forceinfokey(world, "votes_n", "0");
|
||||
forceinfokey(world, "vote_cmd", "");
|
||||
|
||||
for (entity e = world; (e = find(e, ::classname, "player"));) {
|
||||
e.voted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Passed(void)
|
||||
{
|
||||
g_flVoteTime = time + 5.0f;
|
||||
g_iVoteState = VOTE_PASSED;
|
||||
bprint(PRINT_CHAT, "Vote passed.\n");
|
||||
g_strVoteCmd = serverkey("vote_cmd");
|
||||
Vote_Reset();
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Failed(void)
|
||||
{
|
||||
g_flVoteTime = 0.0;
|
||||
g_iVoteState = VOTE_INACTIVE;
|
||||
bprint(PRINT_CHAT, "Vote failed.\n");
|
||||
Vote_Reset();
|
||||
remove(voteHandler);
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Frame(void)
|
||||
{
|
||||
if (time >= g_flVoteTime) {
|
||||
if (g_iVoteState == VOTE_INPROGRESS) {
|
||||
if (serverkeyfloat("votes_y") > serverkeyfloat("votes_n")) {
|
||||
Vote_Passed();
|
||||
} else {
|
||||
Vote_Failed();
|
||||
}
|
||||
} else if (g_iVoteState == VOTE_PASSED) {
|
||||
Vote_End();
|
||||
}
|
||||
}
|
||||
|
||||
self.nextthink = time;
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Cmd_VoteYes(void)
|
||||
{
|
||||
/* No vote is in progress */
|
||||
if (g_iVoteState != VOTE_INPROGRESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.classname != "player") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.voted) {
|
||||
return;
|
||||
}
|
||||
|
||||
forceinfokey(world, "votes_y", ftos(serverkeyfloat("votes_y")+1));
|
||||
self.voted = 1;
|
||||
|
||||
/* HACK: Is there a better way to do this? */
|
||||
float playernums = 0;
|
||||
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
|
||||
playernums++;
|
||||
}
|
||||
|
||||
/* We need at least half the players agreeing. */
|
||||
if (serverkeyfloat("votes_y") > rint(playernums / 2)) {
|
||||
Vote_Passed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverkeyfloat("votes_n") + serverkeyfloat("votes_y") == playernums) {
|
||||
g_flVoteTime = time + 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Cmd_VoteNo(void)
|
||||
{
|
||||
/* No vote is in progress */
|
||||
if (g_iVoteState != VOTE_INPROGRESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.classname != "player") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.voted) {
|
||||
return;
|
||||
}
|
||||
|
||||
forceinfokey(world, "votes_n", ftos(serverkeyfloat("votes_n")+1));
|
||||
self.voted = 1;
|
||||
|
||||
/* HACK: Is there a better way to do this? */
|
||||
float playernums = 0;
|
||||
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
|
||||
playernums++;
|
||||
}
|
||||
|
||||
/* We need at least half the players disagreeing. */
|
||||
if (serverkeyfloat("votes_n") > rint(playernums / 2)) {
|
||||
Vote_Failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverkeyfloat("votes_n") + serverkeyfloat("votes_y") == playernums) {
|
||||
g_flVoteTime = time + 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Vote_InitiateVote(string votemsg)
|
||||
{
|
||||
/* A vote is in progress */
|
||||
if (g_iVoteState != VOTE_INACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.classname != "player") {
|
||||
return;
|
||||
}
|
||||
|
||||
Vote_Reset();
|
||||
|
||||
forceinfokey(world, "vote_cmd", votemsg);
|
||||
g_flVoteTime = time + 30.0f;
|
||||
g_iVoteState = VOTE_INPROGRESS;
|
||||
|
||||
bprint(PRINT_CHAT, sprintf("vote: %S, type yes/no in chat!\n", votemsg));
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Cmd_CallVote(string text)
|
||||
{
|
||||
if (autocvar_mp_allowvote == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* No vote is in progress */
|
||||
if (g_iVoteState != VOTE_INACTIVE) {
|
||||
sprint(self, PRINT_CHAT, "A vote is already in progress.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
voteHandler = spawn();
|
||||
voteHandler.think = Vote_Frame;
|
||||
voteHandler.nextthink = time;
|
||||
|
||||
tokenize(text);
|
||||
switch (argv(1)) {
|
||||
case "map":
|
||||
if not (whichpack(sprintf("maps/%s.bsp", argv(2)))) {
|
||||
sprint(self, PRINT_CHAT, sprintf("Map '%s' not available on server.\n", argv(2)));
|
||||
break;
|
||||
}
|
||||
case "kick":
|
||||
case "slowmo":
|
||||
case "timelimit":
|
||||
case "fraglimit":
|
||||
case "map_restart":
|
||||
case "nextmap":
|
||||
Vote_InitiateVote(sprintf("%s %s", argv(1), argv(2)));
|
||||
Vote_Cmd_VoteYes();
|
||||
break;
|
||||
default:
|
||||
sprint(self, PRINT_CHAT, sprintf("Cannot callvote for '%s'.\n", argv(1)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Init(void)
|
||||
{
|
||||
Vote_Reset();
|
||||
}
|
||||
|
||||
void
|
||||
Vote_Help(entity targetPlayer)
|
||||
{
|
||||
centerprint(targetPlayer, "Voting is easy:\nsay: callvote map dm3\n\nplayers say 'yes' or 'no'\nto decide the outcome!\n\ncommands include:\nmap, kick, fraglimit, timelimit");
|
||||
}
|
3
world.qc
3
world.qc
|
@ -26,6 +26,7 @@
|
|||
*/
|
||||
|
||||
void() InitBodyQue;
|
||||
void() Vote_Init;
|
||||
|
||||
|
||||
void() main =
|
||||
|
@ -370,6 +371,8 @@ void() worldspawn =
|
|||
|
||||
// 63 testing
|
||||
lightstyle(63, "a");
|
||||
|
||||
Vote_Init();
|
||||
};
|
||||
|
||||
void() StartFrame =
|
||||
|
|
Loading…
Reference in a new issue