nuclide/src/server/vote.qc

285 lines
5.2 KiB
Plaintext

/*
* Copyright (c) 2016-2022 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
var string g_strVoteCmd;
var float g_flVoteTime;
var int g_iVoteState;
var int autocvar_mp_allowvote = TRUE;
enum
{
VOTE_INACTIVE,
VOTE_INPROGRESS,
VOTE_PASSED
};
/*
=================
Vote_End
=================
*/
void
Vote_End(void)
{
localcmd(sprintf("%s\n", g_strVoteCmd));
g_flVoteTime = 0.0f;
g_iVoteState = VOTE_INACTIVE;
}
/*
=================
Vote_Reset
Make sure all the cached stuff is reset.
=================
*/
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"));) {
player pl = (player)e;
pl.voted = 0;
}
}
/*
=================
Vote_Passed
Called whenever enough people agreed, or time has run out with enougn YES's
=================
*/
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();
}
/*
=================
Vote_Failed
Called whenever enough people disagreed, or time has run out below the limit
=================
*/
void
Vote_Failed(void)
{
g_flVoteTime = 0.0;
g_iVoteState = VOTE_INACTIVE;
bprint(PRINT_CHAT, "Vote failed.\n");
Vote_Reset();
}
/*
=================
Vote_Frame
Called in ServerFrame... every single frame.
=================
*/
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();
}
}
}
/*
=================
CSEv_VoteY
'vote yes'
=================
*/
void
CSEv_VoteY(void)
{
player pl = (player)self;
/* No vote is in progress */
if (g_iVoteState != VOTE_INPROGRESS) {
return;
}
if (self.classname != "player") {
return;
}
if (pl.voted) {
return;
}
forceinfokey(world, "votes_y", ftos(serverkeyfloat("votes_y")+1));
pl.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;
}
}
/*
=================
CSEv_VoteN
'vote no'
=================
*/
void
CSEv_VoteN(void)
{
player pl = (player)self;
/* No vote is in progress */
if (g_iVoteState != VOTE_INPROGRESS) {
return;
}
if (self.classname != "player") {
return;
}
if (pl.voted) {
return;
}
forceinfokey(world, "votes_n", ftos(serverkeyfloat("votes_n")+1));
pl.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;
}
}
/*
=================
Vote_InitiateVote
Send the info to the clients and set the timer up
=================
*/
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;
}
/*
=================
CSEv_CallVote_s
Initiates a vote via the 'callvote' client command.
=================
*/
void
CSEv_CallVote_s(string text)
{
if (autocvar_mp_allowvote == FALSE) {
return;
}
/* No vote is in progress */
if (g_iVoteState != VOTE_INACTIVE) {
sprint(self, PRINT_CHAT, "A vote is already in progress.\n");
return;
}
tokenize(text);
switch (argv(0)) {
case "map":
if (FileExists(sprintf("maps/%s.bsp", argv(1))) == false) {
sprint(self, PRINT_CHAT, sprintf("Map '%s' not available on server.\n", argv(1)));
break;
}
case "kick":
case "slowmo":
case "timelimit":
case "fraglimit":
case "map_restart":
case "nextmap":
Vote_InitiateVote(sprintf("%s %s", argv(0), argv(1)));
CSEv_VoteY();
break;
default:
sprint(self, PRINT_CHAT, sprintf("Cannot callvote for '%s'.\n", argv(0)));
}
}
void
Vote_Init(void)
{
Vote_Reset();
}