From 996ca9adad948163492ca51d7dce47697451adb0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 20 Apr 2023 15:25:07 -0400 Subject: [PATCH 1/8] Z Voting --- src/CMakeLists.txt | 1 + src/d_clisrv.c | 10 +- src/d_clisrv.h | 24 +- src/d_netcmd.c | 5 + src/d_netcmd.h | 4 +- src/g_game.c | 6 + src/hu_stuff.c | 57 ++- src/hu_stuff.h | 12 +- src/k_hud.c | 11 +- src/k_hud.h | 1 + src/k_zvote.c | 1123 ++++++++++++++++++++++++++++++++++++++++++++ src/k_zvote.h | 269 +++++++++++ src/lua_hudlib.c | 4 +- src/p_saveg.c | 88 ++++ src/screen.c | 6 +- src/st_stuff.c | 18 +- src/typedef.h | 4 + src/v_video.cpp | 73 ++- src/v_video.h | 2 +- 19 files changed, 1663 insertions(+), 55 deletions(-) create mode 100644 src/k_zvote.c create mode 100644 src/k_zvote.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f92be390e..cb5b93fc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -142,6 +142,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_rank.c k_vote.c k_serverstats.c + k_zvote.c ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c9099d78c..8fcab32dc 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -62,6 +62,7 @@ #include "g_party.h" #include "k_vote.h" #include "k_serverstats.h" +#include "k_zvote.h" // cl loading screen #include "v_video.h" @@ -2308,6 +2309,7 @@ static void CL_ConnectToServer(void) Schedule_Clear(); Automate_Clear(); K_ClearClientPowerLevels(); + K_ResetMidVote(); pnumnodes = 1; oldtic = 0; @@ -3151,7 +3153,7 @@ static void Command_Kick(void) if (COM_Argc() == 2) { - WRITEUINT8(p, KICK_MSG_GO_AWAY); + WRITEUINT8(p, KICK_MSG_KICKED); SendNetXCmd(XD_KICK, &buf, 2); } else @@ -3253,7 +3255,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) // to keep it all in one place. if (server) { - if (msg == KICK_MSG_GO_AWAY || msg == KICK_MSG_CUSTOM_KICK) + if (msg == KICK_MSG_KICKED || msg == KICK_MSG_CUSTOM_KICK) { // Kick as a temporary ban. banMinutes = cv_kicktime.value; @@ -3293,7 +3295,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) switch (msg) { - case KICK_MSG_GO_AWAY: + case KICK_MSG_KICKED: HU_AddChatText(va("\x82*%s has been kicked (No reason given)", player_names[pnum]), false); kickreason = KR_KICK; break; @@ -3692,6 +3694,7 @@ void SV_ResetServer(void) Automate_Clear(); K_ClearClientPowerLevels(); G_ObliterateParties(); + K_ResetMidVote(); memset(splitscreen_invitations, -1, sizeof splitscreen_invitations); memset(player_name_changes, 0, sizeof player_name_changes); @@ -3788,6 +3791,7 @@ void D_QuitNetGame(void) Automate_Clear(); K_ClearClientPowerLevels(); G_ObliterateParties(); + K_ResetMidVote(); DEBFILE("===========================================================================\n" " Log finish\n" diff --git a/src/d_clisrv.h b/src/d_clisrv.h index fee63b0ec..7bc897d02 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -458,15 +458,20 @@ extern consvar_t cv_playbackspeed; #define FILETXHEADER offsetof(filetx_pak, data) #define BASESERVERTICSSIZE offsetof(doomdata_t, u.serverpak.cmds[0]) -#define KICK_MSG_GO_AWAY 1 -#define KICK_MSG_CON_FAIL 2 -#define KICK_MSG_PLAYER_QUIT 3 -#define KICK_MSG_TIMEOUT 4 -#define KICK_MSG_BANNED 5 -#define KICK_MSG_PING_HIGH 6 -#define KICK_MSG_CUSTOM_KICK 7 -#define KICK_MSG_CUSTOM_BAN 8 -#define KICK_MSG_SIGFAIL 9 +typedef enum +{ + KICK_MSG_PLAYER_QUIT = 0, // Player intentionally left + KICK_MSG_KICKED, // Server kick message w/ no reason + KICK_MSG_CUSTOM_KICK, // Server kick message w/ reason + KICK_MSG_VOTE_KICK, // Vote kick message + KICK_MSG_BANNED, // Ban message w/ no reason + KICK_MSG_CUSTOM_BAN, // Ban message w/ custom reason + KICK_MSG_TIMEOUT, // Player's connection timed out + KICK_MSG_PING_HIGH, // Player hit the ping limit + KICK_MSG_CON_FAIL, // Player failed to resync game state + KICK_MSG_SIGFAIL, // Player failed signature check + KICK_MSG__MAX // Number of unique messages +} kickmsg_t; typedef enum { @@ -476,7 +481,6 @@ typedef enum KR_TIMEOUT = 4, //Connection Timeout KR_BAN = 5, //Banned by server KR_LEAVE = 6, //Quit the game - } kickreason_t; /* the max number of name changes in some time period */ diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 0f351c201..f97203ce0 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -65,6 +65,7 @@ #include "k_race.h" #include "g_party.h" #include "k_vote.h" +#include "k_zvote.h" #ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES #include "m_avrecorder.h" @@ -632,6 +633,8 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "AUTOMATE", // XD_AUTOMATE "REQMAPQUEUE", // XD_REQMAPQUEUE "MAPQUEUE", // XD_MAPQUEUE + "CALLZVOTE", // XD_CALLZVOTE + "SETZVOTE", // XD_SETZVOTE }; // ========================================================================= @@ -844,6 +847,8 @@ void D_RegisterServerCommands(void) #ifdef LUA_ALLOW_BYTECODE COM_AddCommand("dumplua", Command_Dumplua_f); #endif + + K_RegisterMidVoteCVars(); } // ========================================================================= diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 94883ce92..5bfccb5f6 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -163,7 +163,7 @@ typedef enum XD_LUAVAR, // 20 XD_LUAFILE, // 21 - // SRB2Kart + // Ring Racers XD_SETUPVOTE, // 22 XD_MODIFYVOTE, // 23 XD_PICKVOTE, // 24 @@ -181,6 +181,8 @@ typedef enum XD_AUTOMATE, // 36 XD_REQMAPQUEUE, // 37 XD_MAPQUEUE, // 38 + XD_CALLZVOTE, // 39 + XD_SETZVOTE, // 40 MAXNETXCMD } netxcmd_t; diff --git a/src/g_game.c b/src/g_game.c index 5791fc7db..b06ab217f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -68,6 +68,7 @@ #include "g_party.h" #include "k_vote.h" #include "k_serverstats.h" +#include "k_zvote.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -2390,6 +2391,11 @@ void G_Ticker(boolean run) { memset(player_name_changes, 0, sizeof player_name_changes); } + + if (Playing() == true) + { + K_TickMidVote(); + } } } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 5b028662d..7cb20d7ca 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -316,6 +316,14 @@ void HU_Init(void) PR ("TLWFN"); REG; + ADIM (NUM); + PR ("OPPRF"); + REG; + + ADIM (NUM); + PR ("PINGF"); + REG; + #undef REG #undef DIG #undef PR @@ -2323,7 +2331,7 @@ Ping_gfx_color (int lag) // // HU_drawPing // -void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags, boolean offline) +void HU_drawPing(fixed_t x, fixed_t y, UINT32 lag, INT32 flags, boolean offline) { UINT8 *colormap = NULL; INT32 measureid = cv_pingmeasurement.value ? 1 : 0; @@ -2339,12 +2347,36 @@ void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags, boolean offline) gfxnum = Ping_gfx_num(lag); if (measureid == 1) - V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); + { + V_DrawFixedPatch( + x + ((11 - pingmeasure[measureid]->width) * FRACUNIT), + y + (9 * FRACUNIT), + FRACUNIT, flags, + pingmeasure[measureid], + NULL + ); + } if (drawlocal) - V_DrawScaledPatch(x+2, y, flags, pinglocal[0]); + { + V_DrawFixedPatch( + x + (2 * FRACUNIT), + y, + FRACUNIT, flags, + pinglocal[0], + NULL + ); + } else - V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]); + { + V_DrawFixedPatch( + x + (2 * FRACUNIT), + y, + FRACUNIT, flags, + pinggfx[gfxnum], + NULL + ); + } colormap = R_GetTranslationColormap(TC_RAINBOW, Ping_gfx_color(lag), GTC_CACHE); @@ -2359,10 +2391,23 @@ void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags, boolean offline) lag = (INT32)(lag * (1000.00f / TICRATE)); } - x = V_DrawPingNum(x + (measureid == 1 ? 11 - pingmeasure[measureid]->width : 10), y+9, flags, lag, colormap); + x = V_DrawPingNum( + x + (((measureid == 1) ? 11 - pingmeasure[measureid]->width : 10) * FRACUNIT), + y + (9 * FRACUNIT), + flags, lag, + colormap + ); if (measureid == 0) - V_DrawScaledPatch(x+1 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); + { + V_DrawFixedPatch( + x + ((1 - pingmeasure[measureid]->width) * FRACUNIT), + y + (9 * FRACUNIT), + FRACUNIT, flags, + pingmeasure[measureid], + NULL + ); + } } void diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 08b997bdc..bf897de41 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -32,7 +32,7 @@ extern "C" { #define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) // SRB2kart -#define KART_FONTSTART '\"' // the first font character +#define KART_FONTSTART '!' // the first font character #define KART_FONTEND 'Z' #define KART_FONTSIZE (KART_FONTEND - KART_FONTSTART + 1) @@ -41,6 +41,11 @@ extern "C" { #define AZ_FONTEND 'Z' #define AZ_FONTSIZE (AZ_FONTEND - AZ_FONTSTART + 1) + +#define NUM_FONTSTART '-' // the first font character +#define NUM_FONTEND '9' + +#define NUM_FONTSIZE (NUM_FONTEND - NUM_FONTSTART + 1) // // Level title font @@ -75,6 +80,9 @@ enum X (GM), X (LSHI), X (LSLOW), + + X (OPPRF), + X (PINGF), }; #undef X @@ -143,7 +151,7 @@ void HU_TickSongCredits(void); char HU_dequeueChatChar(void); void HU_Erase(void); void HU_clearChatChars(void); -void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags, boolean offline); // Lat': Ping drawer for scoreboard. +void HU_drawPing(fixed_t x, fixed_t y, UINT32 ping, INT32 flags, boolean offline); // Lat': Ping drawer for scoreboard. void HU_drawMiniPing(INT32 x, INT32 y, UINT32 ping, INT32 flags); INT32 HU_CreateTeamScoresTbl(playersort_t *tab, UINT32 dmtotals[]); diff --git a/src/k_hud.c b/src/k_hud.c index f4261f302..ee11dd568 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2322,7 +2322,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN } else if (tab[i].num != serverplayer || !server_lagless) { - HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0, false); + HU_drawPing((x + ((i < 8) ? -17 : rightoffset + 11)) * FRACUNIT, (y-4) * FRACUNIT, playerpingtable[tab[i].num], 0, false); } } @@ -4955,11 +4955,16 @@ K_drawMiniPing (void) } } +void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed) +{ + V_DrawFixedPatch(x, y, FRACUNIT, flags, button[pressed], NULL); +} + void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic) { const UINT8 anim_duration = 16; - const UINT8 anim = (animtic % (anim_duration * 2)) < anim_duration; - V_DrawScaledPatch(x, y, flags, button[anim]); + const boolean anim = ((animtic % (anim_duration * 2)) < anim_duration); + K_drawButton(x << FRACBITS, y << FRACBITS, flags, button, anim); } static void K_DrawDirectorButton(INT32 idx, const char *label, patch_t *kp[2], INT32 textflags) diff --git a/src/k_hud.h b/src/k_hud.h index ba50049f5..464737059 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -45,6 +45,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN void K_DrawMapThumbnail(INT32 x, INT32 y, INT32 width, UINT32 flags, UINT16 map, UINT8 *colormap); void K_DrawLikeMapThumbnail(INT32 x, INT32 y, INT32 width, UINT32 flags, patch_t *patch, UINT8 *colormap); void K_drawTargetHUD(const vector3_t *origin, player_t *player); +void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed); void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic); extern patch_t *kp_capsuletarget_arrow[2][2]; diff --git a/src/k_zvote.c b/src/k_zvote.c new file mode 100644 index 000000000..a2a736f31 --- /dev/null +++ b/src/k_zvote.c @@ -0,0 +1,1123 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_zvote.c +/// \brief Player callable mid-game vote + +#include "k_zvote.h" + +#include "doomdef.h" +#include "command.h" +#include "g_game.h" +#include "g_input.h" +#include "d_clisrv.h" +#include "p_local.h" +#include "hu_stuff.h" +#include "v_video.h" +#include "k_hud.h" +#include "r_draw.h" +#include "byteptr.h" + +static CV_PossibleValue_t modulate_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; +static consvar_t cv_zvote_quorum = CVAR_INIT ("zvote_quorum", "0.6", CV_SAVE|CV_NETVAR|CV_FLOAT, modulate_cons_t, NULL); + +static consvar_t cv_zvote_spectators = CVAR_INIT ("zvote_spectator_votes", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +static consvar_t cv_zvote_length = CVAR_INIT ("zvote_length", "90", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); +static consvar_t cv_zvote_delay = CVAR_INIT ("zvote_delay", "90", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); + +static consvar_t cv_zvote_allowed[MVT__MAX] = { // See also: midVoteType_e + CVAR_INIT ("zvote_kick_allowed", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), // MVT_KICK + CVAR_INIT ("zvote_rtv_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL) // MVT_RTV +}; + +const char *g_midVoteTypeNames[MVT__MAX] = { + "KICK", // MVT_KICK + "RTV" // MVT_RTV +}; + +midVote_t g_midVote = {0}; + +/*-------------------------------------------------- + static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) + + Specifies whenever or not a vote type is intended + to specify a "victim", or a player that would be + negatively affected by the vote. + + Input Arguments:- + voteType - The vote type to check. + + Return:- + true if it uses a victim, otherwise false. +--------------------------------------------------*/ +static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) +{ + switch (voteType) + { + case MVT_KICK: + { + return true; + } + default: + { + return false; + } + } +} + +/*-------------------------------------------------- + static void Command_CallVote(void) + + Callback function for the "zvote_call" console command. +--------------------------------------------------*/ +static void Command_CallVote(void) +{ + UINT8 buf[MAXTEXTCMD]; + UINT8 *buf_p = buf; + + size_t numArgs = 0; + + const char *voteTypeStr = NULL; + midVoteType_e voteType = MVT__MAX; + + const char *voteVariableStr = NULL; + INT32 voteVariable = 0; + + player_t *victim = NULL; + + INT32 i = INT32_MAX; + + if (netgame == false) + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + + numArgs = COM_Argc(); + if (numArgs < 2) + { + CONS_Printf("%s [variable]: calls a vote\n", COM_Argv(0)); + return; + } + + voteTypeStr = COM_Argv(1); + + for (voteType = 0; voteType < MVT__MAX; voteType++) + { + if (strcasecmp(voteTypeStr, g_midVoteTypeNames[voteType]) == 0) + { + break; + } + } + + if (voteType == MVT__MAX) + { + CONS_Printf("Unknown vote type \"%s\".\n", voteTypeStr); + return; + } + + voteVariableStr = COM_Argv(2); + voteVariable = atoi(voteVariableStr); + CONS_Printf("voteVariable: %d\n", voteVariable); + + if (K_MidVoteTypeUsesVictim(voteType) == true) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (strcasecmp(player_names[i], voteVariableStr) == 0) + { + voteVariable = i; + CONS_Printf("voteVariable as player name: %s\n", player_names[i]); + break; + } + } + + if (voteVariable >= 0 && voteVariable < MAXPLAYERS) + { + CONS_Printf("victim set to %d\n", voteVariable); + victim = &players[voteVariable]; + } + } + + if (K_AllowNewMidVote(&players[consoleplayer], voteType, voteVariable, victim) == false) + { + // Invalid vote inputs. + return; + } + + WRITEUINT8(buf_p, voteType); + WRITEINT32(buf_p, voteVariable); + + SendNetXCmd(XD_CALLZVOTE, buf, buf_p - buf); +} + +/*-------------------------------------------------- + static void Got_CallZVote(UINT8 **cp, INT32 playernum) + + Callback function for XD_CALLZVOTE NetXCmd. + Attempts to start a new vote using K_InitNewMidVote. + + Input Arguments:- + cp - Pointer to readable byte stream. + playernum - The player this packet came from. + + Return:- + N/A +--------------------------------------------------*/ +static void Got_CallZVote(UINT8 **cp, INT32 playernum) +{ + midVoteType_e type = MVT__MAX; + INT32 variable = 0; + player_t *victim = NULL; + + type = READUINT8(*cp); + variable = READINT32(*cp); + + if (K_MidVoteTypeUsesVictim(type) == true) + { + if (variable >= 0 && variable < MAXPLAYERS) + { + victim = &players[variable]; + } + } + + K_InitNewMidVote(&players[playernum], type, variable, victim); +} + +/*-------------------------------------------------- + static void K_PlayerSendMidVote(const UINT8 id) + + Sends a local player's confirmed vote to + the server. + + Input Arguments:- + id - Local splitscreen player ID. + + Return:- + N/A +--------------------------------------------------*/ +static void K_PlayerSendMidVote(const UINT8 id) +{ + if (id >= MAXSPLITSCREENPLAYERS) + { + return; + } + + SendNetXCmdForPlayer(id, XD_SETZVOTE, NULL, 0); +} + +/*-------------------------------------------------- + static void Got_SetZVote(UINT8 **cp, INT32 playernum) + + Callback function for XD_SETZVOTE NetXCmd. + Updates the vote table. + + Input Arguments:- + cp - Pointer to readable byte stream. + playernum - The player this packet came from. + + Return:- + N/A +--------------------------------------------------*/ +static void Got_SetZVote(UINT8 **cp, INT32 playernum) +{ + (void)cp; + g_midVote.votes[playernum] = true; +} + +/*-------------------------------------------------- + void K_RegisterMidVoteCVars(void) + + See header file for description. +--------------------------------------------------*/ +void K_RegisterMidVoteCVars(void) +{ + INT32 i = INT32_MAX; + + CV_RegisterVar(&cv_zvote_quorum); + + CV_RegisterVar(&cv_zvote_spectators); + + CV_RegisterVar(&cv_zvote_length); + CV_RegisterVar(&cv_zvote_delay); + + for (i = 0; i < MVT__MAX; i++) + { + CV_RegisterVar(&cv_zvote_allowed[i]); + } + + COM_AddCommand("zvote_call", Command_CallVote); + + RegisterNetXCmd(XD_CALLZVOTE, Got_CallZVote); + RegisterNetXCmd(XD_SETZVOTE, Got_SetZVote); +} + +/*-------------------------------------------------- + void K_ResetMidVote(void) + + See header file for description. +--------------------------------------------------*/ +void K_ResetMidVote(void) +{ + memset(&g_midVote, 0, sizeof(g_midVote)); +} + +/*-------------------------------------------------- + boolean K_AnyMidVotesAllowed(void) + + See header file for description. +--------------------------------------------------*/ +boolean K_AnyMidVotesAllowed(void) +{ + INT32 i = INT32_MAX; + + for (i = 0; i < MVT__MAX; i++) + { + if (cv_zvote_allowed[i].value != 0) + { + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + boolean K_PlayerIDAllowedInMidVote(const UINT8 id) + + See header file for description. +--------------------------------------------------*/ +boolean K_PlayerIDAllowedInMidVote(const UINT8 id) +{ + const player_t *player = &players[id]; + + if (playeringame[id] == false) + { + // Needs to be present to vote. + return false; + } + + if (player->bot == true) + { + // Bots don't vote on these issues. + return false; + } + + if (cv_zvote_spectators.value == 0 && player->spectator == true) + { + // Spectators don't vote on these issues, unless the server allows it. + return false; + } + + return true; +} + +/*-------------------------------------------------- + UINT8 K_RequiredMidVotes(void) + + See header file for description. +--------------------------------------------------*/ +UINT8 K_RequiredMidVotes(void) +{ + UINT8 allowedCount = 0; + UINT8 requiredCount = 0; + INT32 i = INT32_MAX; + + if (g_midVote.active == false) + { + // No vote is currently running. + return 0; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (K_PlayerIDAllowedInMidVote(i) == true) + { + allowedCount++; + } + } + + if (allowedCount > 1) + { + return max( + 2, // require at least 2 votes, regardless of how low the quorum is + (FixedMul( + (allowedCount << FRACBITS), + cv_zvote_quorum.value + ) + (FRACUNIT >> 1)) >> FRACBITS // Round up to bias towards more votes being required in small games + ); + } + else + { + // 1P session, just require the one vote. + return requiredCount; + } +} + +/*-------------------------------------------------- + boolean K_PlayerIDMidVoted(const UINT8 id) + + See header file for description. +--------------------------------------------------*/ +boolean K_PlayerIDMidVoted(const UINT8 id) +{ + const player_t *player = &players[id]; + + if (K_PlayerIDAllowedInMidVote(id) == false) + { + // This person isn't allowed to participate in votes. + return false; + } + + if (player == g_midVote.caller) + { + // The person who called the vote always votes for it. + return true; + } + else if (player == g_midVote.victim) + { + // The person being voted off never votes for it. + return false; + } + + return g_midVote.votes[id]; +} + +/*-------------------------------------------------- + UINT8 K_CountMidVotes(void) + + See header file for description. +--------------------------------------------------*/ +UINT8 K_CountMidVotes(void) +{ + UINT8 voteCount = 0; + INT32 i = INT32_MAX; + + if (g_midVote.active == false) + { + // No vote is currently running. + return 0; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (K_PlayerIDMidVoted(i) == true) + { + voteCount++; + } + } + + return voteCount; +} + +/*-------------------------------------------------- + boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim) + + See header file for description. +--------------------------------------------------*/ +boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim) +{ + (void)variable; + + if (g_midVote.active == true) + { + // Don't allow another vote if one is already running. + if (P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "A vote is already in progress.\n"); + } + + return false; + } + + if (g_midVote.delay > 0) + { + // Don't allow another vote if one has recently just ran. + if (P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "Another vote was called too recently.\n"); + } + + return false; + } + + if (type < 0 || type >= MVT__MAX) + { + // Invalid range. + if (P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "Invalid vote type.\n"); + } + + return false; + } + + if (cv_zvote_allowed[type].value == 0) + { + // These types of votes aren't allowed on this server. + if (P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "Vote type is not allowed in this server.\n"); + } + + return false; + } + + if (caller == NULL || K_PlayerIDAllowedInMidVote(caller - players) == false) + { + // Invalid calling player. + if (caller != NULL && P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "Invalid calling player.\n"); + } + + return false; + } + + if (K_MidVoteTypeUsesVictim(type) == true) + { + if (victim == NULL) + { + // Invalid victim. + if (P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "Can't kick this player; it's invalid.\n"); + } + + return false; + } + + if (caller == victim) + { + if (P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "Can't kick yourself.\n"); + } + + return false; + } + + if ((victim - players) == serverplayer +#ifndef DEVELOP + || IsPlayerAdmin((victim - players)) == true +#endif + ) + { + // Victim is the server or an admin. + if (P_IsLocalPlayer(caller) == true) + { + CONS_Alert(CONS_ERROR, "Can't kick this player; they are an administrator.\n"); + } + + return false; + } + } + + return true; +} + +/*-------------------------------------------------- + void K_InitNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim) + + See header file for description. +--------------------------------------------------*/ +void K_InitNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim) +{ + INT32 i = INT32_MAX; + + if (K_AllowNewMidVote(caller, type, variable, victim) == false) + { + // Invalid vote inputs. + return; + } + + K_ResetMidVote(); + + g_midVote.active = true; + g_midVote.caller = caller; + + g_midVote.type = type; + g_midVote.variable = variable; + g_midVote.victim = victim; + + g_midVote.votes[caller - players] = true; + + for (i = 0; i <= splitscreen; i++) + { + if (caller == &players[g_localplayers[i]]) + { + // The person who voted should already be confirmed. + g_midVote.gui[i].confirm = ZVOTE_GUI_CONFIRM; + } + } +} + +/*-------------------------------------------------- + static void K_MidVoteKick(void) + + MVT_KICK's success function. +--------------------------------------------------*/ +static void K_MidVoteKick(void) +{ + if (g_midVote.victim == NULL) + { + return; + } + + if (server) + { + SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK); + } +} + +/*-------------------------------------------------- + static void K_MidVoteRockTheVote(void) + + MVT_RTV's success function. +--------------------------------------------------*/ +static void K_MidVoteRockTheVote(void) +{ + G_ExitLevel(); +} + +/*-------------------------------------------------- + void K_MidVoteSuccess(void) + + See header file for description. +--------------------------------------------------*/ +void K_MidVoteSuccess(void) +{ + switch (g_midVote.type) + { + case MVT_KICK: + { + K_MidVoteKick(); + break; + } + case MVT_RTV: + { + K_MidVoteRockTheVote(); + break; + } + default: + { + break; + } + } + + K_ResetMidVote(); + g_midVote.delay = cv_zvote_delay.value * TICRATE; // Vote succeeded, so the delay is normal. +} + +/*-------------------------------------------------- + void K_MidVoteFailure(void) + + See header file for description. +--------------------------------------------------*/ +void K_MidVoteFailure(void) +{ + K_ResetMidVote(); + g_midVote.delay = (cv_zvote_delay.value * 2) * TICRATE; // Vote failed, so the delay is longer. +} + +/*-------------------------------------------------- + static void K_HandleMidVoteInput(void) + + See header file for description. +--------------------------------------------------*/ +static void K_HandleMidVoteInput(void) +{ + INT32 i = INT32_MAX; + + for (i = 0; i <= splitscreen; i++) + { + //player_t *const player = &players[ g_localplayers[i] ]; + midVoteGUI_t *const gui = &g_midVote.gui[i]; + boolean pressed = false; + + if (menuactive == false) + { + pressed = G_PlayerInputDown(i, gc_z, 0); + } + + // Between states, require us to unpress Z. + if (pressed == true) + { + if (gui->unpress == true) + { + pressed = false; + } + } + else + { + gui->unpress = false; + } + + if (gui->slide < ZVOTE_GUI_SLIDE) + { + if (gui->slide > 0 || pressed == true) + { + gui->slide++; + gui->unpress = true; + } + } + else if (gui->confirm < ZVOTE_GUI_CONFIRM) + { + if (K_PlayerIDAllowedInMidVote(g_localplayers[i]) == false) + { + gui->confirm = 0; + continue; + } + + if (pressed == true) + { + gui->confirm++; + + if (gui->confirm == ZVOTE_GUI_CONFIRM) + { + K_PlayerSendMidVote(i); + gui->unpress = true; + } + } + else + { + gui->confirm = 0; + } + } + } +} + +/*-------------------------------------------------- + void K_TickMidVote(void) + + See header file for description. +--------------------------------------------------*/ +void K_TickMidVote(void) +{ + UINT8 numVotes = 0; + UINT8 requiredVotes = 0; + + if (g_midVote.active == false) + { + // No vote is currently running. + if (g_midVote.delay > 0) + { + // Decrement timer for allowing the next vote. + g_midVote.delay--; + } + + return; + } + + if (g_midVote.end > 0) + { + g_midVote.end++; + + if (g_midVote.end > ZVOTE_GUI_SUCCESS) + { + if (g_midVote.endVotes >= g_midVote.endRequired) + { + K_MidVoteSuccess(); + } + else + { + K_MidVoteFailure(); + } + } + + return; + } + + numVotes = K_CountMidVotes(); + requiredVotes = K_RequiredMidVotes(); + + if (numVotes >= requiredVotes + || g_midVote.time > (unsigned)(cv_zvote_length.value * TICRATE)) + { + // Vote finished. + // Start the ending animation. + g_midVote.end++; + g_midVote.endVotes = numVotes; + g_midVote.endRequired = requiredVotes; + return; + } + + K_HandleMidVoteInput(); + g_midVote.time++; +} + +/*-------------------------------------------------- + void K_CacheMidVotePatches(void) + + See header file for description. +--------------------------------------------------*/ + +#define ZVOTE_PATCH_EXC_START (4) +#define ZVOTE_PATCH_EXC_LOOP (3) +#define ZVOTE_PATCH_BAR_SEGS (12) + +static patch_t *g_exclamationSlide = NULL; +static patch_t *g_exclamationStart[ZVOTE_PATCH_EXC_LOOP] = {NULL}; +static patch_t *g_exclamation = NULL; +static patch_t *g_exclamationLoop[ZVOTE_PATCH_EXC_LOOP] = {NULL}; + +static patch_t *g_zBar[2] = {NULL}; +static patch_t *g_zBarEnds[2][2][2] = {NULL}; + +void K_UpdateMidVotePatches(void) +{ + HU_UpdatePatch(&g_exclamationSlide, "TLSBA0"); + + HU_UpdatePatch(&g_exclamationStart[0], "TLSBB0"); + HU_UpdatePatch(&g_exclamationStart[1], "TLSBC0"); + HU_UpdatePatch(&g_exclamationStart[2], "TLSBD0"); + + HU_UpdatePatch(&g_exclamation, "TLSBE0"); + + HU_UpdatePatch(&g_exclamationLoop[0], "TLSBF0"); + HU_UpdatePatch(&g_exclamationLoop[1], "TLSBG0"); + HU_UpdatePatch(&g_exclamationLoop[2], "TLSBD0"); + + HU_UpdatePatch(&g_zBar[0], "TLBWE0"); + + HU_UpdatePatch(&g_zBarEnds[0][0][0], "TLBWC0"); + HU_UpdatePatch(&g_zBarEnds[0][0][1], "TLBWD0"); + + HU_UpdatePatch(&g_zBarEnds[0][1][0], "TLBWA0"); + HU_UpdatePatch(&g_zBarEnds[0][1][1], "TLBWB0"); + + HU_UpdatePatch(&g_zBar[1], "TLBXE0"); + + HU_UpdatePatch(&g_zBarEnds[1][0][0], "TLBXC0"); + HU_UpdatePatch(&g_zBarEnds[1][0][1], "TLBXD0"); + + HU_UpdatePatch(&g_zBarEnds[1][1][0], "TLBXA0"); + HU_UpdatePatch(&g_zBarEnds[1][1][1], "TLBXB0"); +} + +/*-------------------------------------------------- + static void K_DrawMidVoteBar(fixed_t x, fixed_t y, INT32 flags, fixed_t fill, skincolornum_t color, boolean flipped) + + Draws a bar + + Input Arguments:- + voteType - The vote type to check. + + Return:- + true if it uses a victim, otherwise false. +--------------------------------------------------*/ +static void K_DrawMidVoteBar(fixed_t x, fixed_t y, INT32 flags, fixed_t fill, skincolornum_t color, boolean flipped) +{ + const SINT8 sign = (flipped == true) ? -1 : 1; + patch_t *bar = g_zBar[0]; + UINT8 *clm = NULL; + INT32 i = INT32_MAX; + + if (color > SKINCOLOR_NONE) + { + clm = R_GetTranslationColormap(TC_BLINK, color, GTC_CACHE); + } + + for (i = 0; i < ZVOTE_PATCH_BAR_SEGS; i++) + { + bar = g_zBar[0]; + + if (i == 0) + { + bar = g_zBarEnds[0][flipped][0]; + } + else if (i == ZVOTE_PATCH_BAR_SEGS - 1) + { + bar = g_zBarEnds[0][flipped][1]; + } + + if (fill < FRACUNIT) + { + V_DrawFixedPatch( + x, y, + FRACUNIT, flags, + bar, NULL + ); + } + + x += bar->width * FRACUNIT * sign; + } + + if (fill > 0) + { + const INT32 fillSegs = FixedMul(fill, ZVOTE_PATCH_BAR_SEGS); + + for (i = 0; i < fillSegs; i++) + { + x -= bar->width * FRACUNIT * sign; + + bar = g_zBar[1]; + + if (i == fillSegs - 1) + { + bar = g_zBarEnds[1][flipped][0]; + } + else if (i == 0) + { + bar = g_zBarEnds[1][flipped][1]; + } + + V_DrawFixedPatch( + x, y, + FRACUNIT, flags, + bar, clm + ); + } + } +} + +/*-------------------------------------------------- + void K_DrawMidVote(void) + + See header file for description. +--------------------------------------------------*/ +void K_DrawMidVote(void) +{ + midVoteGUI_t *gui = NULL; + boolean pressed = false; + + UINT8 id = UINT8_MAX; + fixed_t x = INT32_MAX, y = INT32_MAX; + + INT32 i = INT32_MAX; + + // FIXME: We need a splitscreen version of this HUD! + for (i = 0; i <= splitscreen; i++) + { + if (stplyr == &players[ g_localplayers[i] ]) + { + id = i; + break; + } + } + + if (id == UINT8_MAX) + { + // Player we're drawing doesn't control the + // vote locally, so we shouldn't draw anything. + return; + } + + pressed = G_PlayerInputDown(id, gc_z, 0); + gui = &g_midVote.gui[id]; + + if (gui->slide == 0) + { + // Draw the exclamation indicator. + patch_t *exc = g_exclamation; + + x = 295 * FRACUNIT; + y = 127 * FRACUNIT; + + if (g_midVote.time < ZVOTE_GUI_SLIDE) + { + x += ((ZVOTE_GUI_SLIDE - g_midVote.time) * (ZVOTE_GUI_SLIDE - g_midVote.time)) << (FRACBITS - 1); + exc = g_exclamationSlide; + } + else + { + const tic_t spd = 2; + const tic_t anim = (g_midVote.time - ZVOTE_GUI_SLIDE) / spd; + const UINT8 frame = anim % (ZVOTE_PATCH_EXC_LOOP + ZVOTE_GUI_SLIDE); + + if (frame < ZVOTE_PATCH_EXC_LOOP) + { + if (anim > ZVOTE_GUI_SLIDE) + { + exc = g_exclamationLoop[frame]; + } + else + { + exc = g_exclamationStart[frame]; + } + } + } + + V_DrawFixedPatch( + x, y, FRACUNIT, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + exc, NULL + ); + K_drawButton( + x - (4 * FRACUNIT), + y + ((exc->height - kp_button_z[1][0]->height) * FRACUNIT), + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + kp_button_z[1], pressed + ); + } + else + { + // Draw the actual vote status + static const char *voteTitles[MVT__MAX] = { + "KICK PLAYER?", // MVT_KICK + "SKIP LEVEL?" // MVT_RTV + }; + + const fixed_t barHalf = (g_zBar[0]->width * FRACUNIT * (ZVOTE_PATCH_BAR_SEGS - 1)) >> 1; + const boolean blink = (gametic & 1); + boolean drawButton = blink; + boolean drawVotes = blink; + + fixed_t strWidth = 0; + + fixed_t fill = FRACUNIT; + skincolornum_t fillColor = SKINCOLOR_NONE; + + x = (BASEVIDWIDTH * FRACUNIT) - barHalf; + y = 144 * FRACUNIT; + + if (gui->slide < ZVOTE_GUI_SLIDE) + { + x += ((ZVOTE_GUI_SLIDE - gui->slide) * (ZVOTE_GUI_SLIDE - gui->slide)) << (FRACBITS - 1); + } + + // Hold bar + if (g_midVote.end > 0) + { + if (g_midVote.endVotes >= g_midVote.endRequired) + { + fillColor = SKINCOLOR_GREEN; + } + else + { + fillColor = SKINCOLOR_RED; + } + } + else + { + if (gui->confirm < ZVOTE_GUI_CONFIRM) + { + fill = FixedDiv(gui->confirm, ZVOTE_GUI_CONFIRM); + fillColor = SKINCOLOR_WHITE; + } + } + K_DrawMidVoteBar( + x - barHalf, y, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + fill, fillColor, + (id & 1) + ); + + // Vote main label + strWidth = V__OneScaleStringWidth( + FRACUNIT, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + KART_FONT, voteTitles[g_midVote.type] + ); + + V__DrawOneScaleString( + x - (strWidth >> 1), + y - (18 * FRACUNIT), + FRACUNIT, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, NULL, + KART_FONT, voteTitles[g_midVote.type] + ); + + // Vote extra text + switch (g_midVote.type) + { + case MVT_KICK: + { + // Draw victim name + if (g_midVote.victim != NULL) + { + strWidth = V__OneScaleStringWidth( + FRACUNIT, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN|V_6WIDTHSPACE, + TINY_FONT, player_names[g_midVote.victim - players] + ); + + V__DrawOneScaleString( + x - (strWidth >> 1), + y + (18 * FRACUNIT), + FRACUNIT, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN|V_6WIDTHSPACE, NULL, + TINY_FONT, player_names[g_midVote.victim - players] + ); + } + break; + } + default: + { + break; + } + } + + // Button + if (g_midVote.end == 0) + { + drawButton = true; + } + + if (K_PlayerIDAllowedInMidVote(g_localplayers[id]) == false) + { + // Player isn't allowed to vote, so don't show it. + drawButton = false; + } + + if (drawButton == true) + { + K_drawButton( + x - (20 * FRACUNIT), + y - (2 * FRACUNIT), + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + kp_button_z[0], pressed + ); + } + + // Vote count + if (g_midVote.end == 0) + { + if (gui->confirm == ZVOTE_GUI_CONFIRM || pressed == false) + { + drawVotes = true; + } + } + + if (drawVotes == true) + { + const fixed_t voteY = y + (2 * FRACUNIT); + fixed_t voteX = x + (8 * FRACUNIT); + fixed_t voteWidth = 0; + UINT8 votes = 0; + UINT8 require = 0; + + if (g_midVote.end > 0) + { + votes = g_midVote.endVotes; + require = g_midVote.endRequired; + } + else + { + votes = K_CountMidVotes(); + require = K_RequiredMidVotes(); + } + + voteWidth = V__OneScaleStringWidth( + FRACUNIT, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + OPPRF_FONT, va("%d/%d", votes, require) + ); + + V__DrawOneScaleString( + voteX - (voteWidth >> 1), + voteY, + FRACUNIT, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, NULL, + OPPRF_FONT, va("%d/%d", votes, require) + ); + } + } +} diff --git a/src/k_zvote.h b/src/k_zvote.h new file mode 100644 index 000000000..b004e88ca --- /dev/null +++ b/src/k_zvote.h @@ -0,0 +1,269 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_zvote.h +/// \brief Player callable mid-game vote + +#ifndef __K_ZVOTE__ +#define __K_ZVOTE__ + +#include "doomdef.h" +#include "doomstat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZVOTE_GUI_CONFIRM (TICRATE) +#define ZVOTE_GUI_SUCCESS (3 * TICRATE) +#define ZVOTE_GUI_SLIDE (TICRATE / 2) + +typedef enum +{ + MVT_KICK, // Kick another player in the server + MVT_RTV, // Exit level early + MVT__MAX, // Total number of vote types +} midVoteType_e; + +extern const char *g_midVoteTypeNames[MVT__MAX]; + +struct midVoteGUI_t +{ + tic_t slide; // Slide in when Z is first pressed. + tic_t confirm; // How long this player has held Z. + boolean unpress; // Z button needs unpressed to continue accepting input. +}; + +struct midVote_t +{ + boolean active; // If true, a vote is currently running. + + player_t *caller; // The player that called for this vote. + player_t *victim; // If non-NULL, then this vote targets a player (kicks), don't let them vote on it. + boolean votes[MAXPLAYERS]; // Votes recieved from each player. + + midVoteType_e type; // Type of vote that was called, see midVoteType_e. + INT32 variable; // Extra variable, unique purpose for each vote type. + + tic_t time; // Time until the vote times out and fails. + tic_t delay; // Delay before another vote is allowed to be called. + + tic_t end; // Ended animation, wait a second before activating callback. + UINT8 endVotes; // How many votes it got when the vote went through. + UINT8 endRequired; // How many votes were required when the vote went through. + + midVoteGUI_t gui[MAXSPLITSCREENPLAYERS]; // GUI / inputs struct +}; + +extern midVote_t g_midVote; + +/*-------------------------------------------------- + void K_RegisterMidVoteCVars(void); + + Registers the console variables related to + the Z-voting systems. +--------------------------------------------------*/ + +void K_RegisterMidVoteCVars(void); + + +/*-------------------------------------------------- + void K_ResetMidVote(void); + + Resets Z-voting variables to their default state. +--------------------------------------------------*/ + +void K_ResetMidVote(void); + + +/*-------------------------------------------------- + boolean K_AnyMidVotesAllowed(void); + + Determines if the server has enabled any types + of Z-votes. If this is false, then any menu options + for Z-voting should be disabled. + + Input Arguments:- + N/A + + Return:- + true if any vote types are enabled, otherwise false. +--------------------------------------------------*/ + +boolean K_AnyMidVotesAllowed(void); + + +/*-------------------------------------------------- + boolean K_PlayerIDAllowedInMidVote(const UINT8 id); + + Determines if this player ID is allowed to + vote or not. + + Input Arguments:- + id - Player index to check. + + Return:- + true if the player index can vote, otherwise false. +--------------------------------------------------*/ + +boolean K_PlayerIDAllowedInMidVote(const UINT8 id); + + +/*-------------------------------------------------- + UINT8 K_RequiredMidVotes(void); + + Calculates the number of votes needed for thr + vote to go through (aka, the "quorum"), as + per the server's settings. Usually at least + 2 players are required for the vote to go through. + + Input Arguments:- + N/A + + Return:- + Number of player votes needed before we should + call K_MidVoteSuccess. +--------------------------------------------------*/ + +UINT8 K_RequiredMidVotes(void); + + +/*-------------------------------------------------- + boolean K_PlayerIDMidVoted(const UINT8 id); + + Determines if this player ID has voted for + the current issue or not. Is mostly safety + checks for g_midVote.votes[id], to force + the player who called the vote to vote for it, + the victim being affected to vote against, + and invalid players not having a vote. + + Input Arguments:- + id - Player index to check. + + Return:- + true if the player index voted yes, otherwise false. +--------------------------------------------------*/ + +boolean K_PlayerIDMidVoted(const UINT8 id); + + +/*-------------------------------------------------- + UINT8 K_CountMidVotes(void); + + Counts the total number of votes in favor of + the current issue. + + Input Arguments:- + N/A + + Return:- + Number of votes that agree. +--------------------------------------------------*/ + +UINT8 K_CountMidVotes(void); + + +/*-------------------------------------------------- + boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); + + Returns if the variables given are a valid state for + K_InitNewMidVote. Creates console alerts if it's not. + + Input Arguments:- + caller - The player that is trying to call for a vote. + type - The type of vote they're trying to call. + variable - Extra arguments for the vote type. + victim - If this is a vote that negatively affects a + player, the player being affected would go here. + + Return:- + true if we can start a new vote, otherwise false. +--------------------------------------------------*/ + +boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); + + +/*-------------------------------------------------- + void K_InitNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); + + Tries to start a new Z-vote, or mid-game vote. This will + handle everything needed to be initialized. Also calls + K_AllowNewMidVote to prevent invalid states from happening. + + Input Arguments:- + caller - The player that is trying to call for a vote. + type - The type of vote they're trying to call. + variable - Extra arguments for the vote type. + victim - If this is a vote that negatively affects a + player, the player being affected would go here. + + Return:- + N/A +--------------------------------------------------*/ + +void K_InitNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); + + +/*-------------------------------------------------- + void K_MidVoteSuccess(void); + + Ran whenever a vote meets the quorum, activates + the effect that the current vote is intended to + have. +--------------------------------------------------*/ + +void K_MidVoteSuccess(void); + + +/*-------------------------------------------------- + void K_MidVoteFailure(void); + + Ran when a vote times out without meeting the quorum. + Doesn't do anything but set a very long delay before + accepting another vote. +--------------------------------------------------*/ + +void K_MidVoteFailure(void); + + +/*-------------------------------------------------- + void K_TickMidVote(void); + + Run every game tick when in a server to process + the vote in progress, if it exists. +--------------------------------------------------*/ + +void K_TickMidVote(void); + + +/*-------------------------------------------------- + void K_UpdateMidVotePatches(void); + + Caches the patches needed for drawing the + HUD elements for Z-voting. +--------------------------------------------------*/ + +void K_UpdateMidVotePatches(void); + + +/*-------------------------------------------------- + void K_DrawMidVote(void); + + Handles drawing the HUD elements for Z-voting. +--------------------------------------------------*/ + +void K_DrawMidVote(void); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __K_ZVOTE__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index cb81e2e73..5e08fd25d 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -807,8 +807,8 @@ static int libd_drawPingNum(lua_State *L) UINT8 *colormap = NULL; huddrawlist_h list; HUDONLY - x = luaL_checkinteger(L, 1); - y = luaL_checkinteger(L, 2); + x = luaL_checkfixed(L, 1); + y = luaL_checkfixed(L, 2); num = luaL_checkinteger(L, 3); flags = luaL_optinteger(L, 4, 0); flags &= ~V_PARAMMASK; // Don't let crashes happen. diff --git a/src/p_saveg.c b/src/p_saveg.c index 2b054b82b..c98c8e7a2 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -44,6 +44,7 @@ #include "acs/interface.h" #include "g_party.h" #include "k_vote.h" +#include "k_zvote.h" savedata_t savedata; @@ -53,6 +54,7 @@ savedata_t savedata; #define ARCHIVEBLOCK_PLAYERS 0x7F448008 #define ARCHIVEBLOCK_PARTIES 0x7F87AF0C #define ARCHIVEBLOCK_ROUNDQUEUE 0x7F721331 +#define ARCHIVEBLOCK_ZVOTE 0x7F54FF0D #define ARCHIVEBLOCK_WORLD 0x7F8C08C0 #define ARCHIVEBLOCK_POBJS 0x7F928546 #define ARCHIVEBLOCK_THINKERS 0x7F37037C @@ -985,6 +987,90 @@ static void P_NetUnArchiveRoundQueue(savebuffer_t *save) } } +static void P_NetArchiveZVote(savebuffer_t *save) +{ + INT32 i; + + WRITEUINT32(save->p, ARCHIVEBLOCK_ZVOTE); + WRITEUINT8(save->p, g_midVote.active); + + if (g_midVote.active == true) + { + WRITEUINT8( + save->p, + (g_midVote.caller != NULL) ? (g_midVote.caller - players) : UINT8_MAX + ); + + WRITEUINT8( + save->p, + (g_midVote.victim != NULL) ? (g_midVote.victim - players) : UINT8_MAX + ); + + for (i = 0; i < MAXPLAYERS; i++) + { + WRITEUINT8(save->p, g_midVote.votes[i]); + } + + WRITEUINT8(save->p, g_midVote.type); + WRITEINT32(save->p, g_midVote.variable); + + WRITEUINT32(save->p, g_midVote.time); + + WRITEUINT32(save->p, g_midVote.end); + WRITEUINT8(save->p, g_midVote.endVotes); + WRITEUINT8(save->p, g_midVote.endRequired); + } + + WRITEUINT32(save->p, g_midVote.delay); +} + +static void P_NetUnArchiveZVote(savebuffer_t *save) +{ + INT32 i; + + if (READUINT32(save->p) != ARCHIVEBLOCK_ZVOTE) + { + I_Error("Bad $$$.sav at archive block Z-Vote"); + } + + g_midVote.active = (boolean)READUINT8(save->p); + + if (g_midVote.active == true) + { + UINT8 callerID = READUINT8(save->p); + UINT8 victimID = READUINT8(save->p); + + if (callerID < MAXPLAYERS) + { + g_midVote.caller = &players[callerID]; + } + + if (victimID < MAXPLAYERS) + { + g_midVote.victim = &players[victimID]; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + g_midVote.votes[i] = (boolean)READUINT8(save->p); + } + + g_midVote.type = READUINT8(save->p); + g_midVote.variable = READINT32(save->p); + + g_midVote.time = (tic_t)READUINT32(save->p); + g_midVote.end = (tic_t)READUINT32(save->p); + g_midVote.endVotes = READUINT8(save->p); + g_midVote.endRequired = READUINT8(save->p); + } + else + { + K_ResetMidVote(); + } + + g_midVote.delay = (tic_t)READUINT32(save->p); +} + /// /// Colormaps /// @@ -5463,6 +5549,7 @@ void P_SaveNetGame(savebuffer_t *save, boolean resending) P_NetArchivePlayers(save); P_NetArchiveParties(save); P_NetArchiveRoundQueue(save); + P_NetArchiveZVote(save); if (gamestate == GS_LEVEL) { @@ -5514,6 +5601,7 @@ boolean P_LoadNetGame(savebuffer_t *save, boolean reloading) P_NetUnArchivePlayers(save); P_NetUnArchiveParties(save); P_NetUnArchiveRoundQueue(save); + P_NetUnArchiveZVote(save); if (gamestate == GS_LEVEL) { diff --git a/src/screen.c b/src/screen.c index 90ccd4bac..1b10c0373 100644 --- a/src/screen.c +++ b/src/screen.c @@ -637,7 +637,7 @@ void SCR_DisplayTicRate(void) } // draw total frame: - V_DrawPingNum(x, 190, V_SNAPTOBOTTOM|V_SNAPTORIGHT, cap, ticcntcolor); + V_DrawPingNum(x<> 1; @@ -2363,6 +2373,12 @@ void V_DrawStringScaled( else dim_fn = VariableCharacterDim; break; + case KART_FONT: + if (chw) + dim_fn = FixedCharacterDim; + else + dim_fn = BunchedCharacterDim; + break; case TINY_FONT: if (chw) dim_fn = FixedCharacterDim; @@ -2394,6 +2410,13 @@ void V_DrawStringScaled( else dim_fn = LSTitleCharacterDim; break; + case OPPRF_FONT: + case PINGF_FONT: + if (chw) + dim_fn = FixedCharacterDim; + else + dim_fn = VariableCharacterDim; + break; } cx = x; @@ -2535,7 +2558,7 @@ fixed_t V_StringScaledWidth( spacew = 16; break; case KART_FONT: - spacew = 12; + spacew = 3; switch (spacing) { case V_MONOSPACE: @@ -2556,6 +2579,12 @@ fixed_t V_StringScaledWidth( case LSLOW_FONT: spacew = 16; break; + case OPPRF_FONT: + spacew = 5; + break; + case PINGF_FONT: + spacew = 3; + break; } switch (fontno) @@ -2580,6 +2609,10 @@ fixed_t V_StringScaledWidth( case LSLOW_FONT: lfh = 38; break; + case OPPRF_FONT: + case PINGF_FONT: + lfh = 10; + break; } hchw = chw >> 1; @@ -2619,6 +2652,12 @@ fixed_t V_StringScaledWidth( else dim_fn = VariableCharacterDim; break; + case KART_FONT: + if (chw) + dim_fn = FixedCharacterDim; + else + dim_fn = BunchedCharacterDim; + break; case TINY_FONT: if (chw) dim_fn = FixedCharacterDim; @@ -2650,6 +2689,13 @@ fixed_t V_StringScaledWidth( else dim_fn = LSTitleCharacterDim; break; + case OPPRF_FONT: + case PINGF_FONT: + if (chw) + dim_fn = FixedCharacterDim; + else + dim_fn = VariableCharacterDim; + break; } cx = cy = 0; @@ -2734,23 +2780,22 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con // Draws a number using the PING font thingy. // TODO: Merge number drawing functions into one with "font name" selection. -INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap) +fixed_t V_DrawPingNum(fixed_t x, fixed_t y, INT32 flags, INT32 num, const UINT8 *colormap) { - INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist. - - if (flags & V_NOSCALESTART) - w *= vid.dupx; + // this SHOULD always be 5 but I guess custom graphics exist. + const fixed_t w = (fontv[PINGNUM_FONT].font[0]->width) * FRACUNIT; if (num < 0) - num = -num; - - // draw the number - do { - x -= (w-1); // Oni wanted their outline to intersect. - V_DrawFixedPatch(x< 0); return x; } diff --git a/src/v_video.h b/src/v_video.h index fdd119c88..28ab28d2a 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -353,7 +353,7 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) // Draw ping numbers. Used by the scoreboard and that one ping option. :P // This is a separate function because IMO lua should have access to it as well. -INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap); +fixed_t V_DrawPingNum(fixed_t x, fixed_t y, INT32 flags, INT32 num, const UINT8 *colormap); void V_DrawProfileNum(INT32 x, INT32 y, INT32 flags, UINT8 num); From 65d68772833af60e4da50d8a27b2520680c4f8f8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 20 Apr 2023 16:00:38 -0400 Subject: [PATCH 2/8] Z Voting: Remove debug prints --- src/k_zvote.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/k_zvote.c b/src/k_zvote.c index a2a736f31..738dbe31d 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -125,7 +125,6 @@ static void Command_CallVote(void) voteVariableStr = COM_Argv(2); voteVariable = atoi(voteVariableStr); - CONS_Printf("voteVariable: %d\n", voteVariable); if (K_MidVoteTypeUsesVictim(voteType) == true) { @@ -134,14 +133,12 @@ static void Command_CallVote(void) if (strcasecmp(player_names[i], voteVariableStr) == 0) { voteVariable = i; - CONS_Printf("voteVariable as player name: %s\n", player_names[i]); break; } } if (voteVariable >= 0 && voteVariable < MAXPLAYERS) { - CONS_Printf("victim set to %d\n", voteVariable); victim = &players[voteVariable]; } } From 49b1ecddffe1834c327637b81778da3d33f4b296 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 20 Apr 2023 20:49:39 -0400 Subject: [PATCH 3/8] Z Voting: Address toaster review --- src/k_zvote.c | 172 ++++++++++++++++++++++++++------------------------ src/p_saveg.c | 5 +- 2 files changed, 92 insertions(+), 85 deletions(-) diff --git a/src/k_zvote.c b/src/k_zvote.c index 738dbe31d..41a9e8b6c 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -32,18 +32,63 @@ static consvar_t cv_zvote_spectators = CVAR_INIT ("zvote_spectator_votes", "Off" static consvar_t cv_zvote_length = CVAR_INIT ("zvote_length", "90", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); static consvar_t cv_zvote_delay = CVAR_INIT ("zvote_delay", "90", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); -static consvar_t cv_zvote_allowed[MVT__MAX] = { // See also: midVoteType_e - CVAR_INIT ("zvote_kick_allowed", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), // MVT_KICK - CVAR_INIT ("zvote_rtv_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL) // MVT_RTV -}; - -const char *g_midVoteTypeNames[MVT__MAX] = { - "KICK", // MVT_KICK - "RTV" // MVT_RTV -}; - midVote_t g_midVote = {0}; +typedef void (*K_ZVoteFinishCallback)(void); + +typedef struct +{ + const char *name; + const char *label; + consvar_t cv_allowed; + K_ZVoteFinishCallback callback; +} midVoteTypeDef_t; + +/*-------------------------------------------------- + static void K_MidVoteKick(void) + + MVT_KICK's success function. +--------------------------------------------------*/ +static void K_MidVoteKick(void) +{ + if (g_midVote.victim == NULL) + { + return; + } + + if (server) + { + SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK); + } +} + +/*-------------------------------------------------- + static void K_MidVoteRockTheVote(void) + + MVT_RTV's success function. +--------------------------------------------------*/ +static void K_MidVoteRockTheVote(void) +{ + G_ExitLevel(); +} + +static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = +{ + { // MVT_KICK + "KICK", + "Kick Player?", + CVAR_INIT ("zvote_kick_allowed", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), + K_MidVoteKick + }, + + { // MVT_RTV + "RTV", + "Skip Level?", + CVAR_INIT ("zvote_rtv_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), + K_MidVoteRockTheVote + }, +}; + /*-------------------------------------------------- static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) @@ -111,7 +156,7 @@ static void Command_CallVote(void) for (voteType = 0; voteType < MVT__MAX; voteType++) { - if (strcasecmp(voteTypeStr, g_midVoteTypeNames[voteType]) == 0) + if (strcasecmp(voteTypeStr, g_midVoteTypeDefs[voteType].name) == 0) { break; } @@ -123,23 +168,26 @@ static void Command_CallVote(void) return; } - voteVariableStr = COM_Argv(2); - voteVariable = atoi(voteVariableStr); - - if (K_MidVoteTypeUsesVictim(voteType) == true) + if (numArgs > 2) { - for (i = 0; i < MAXPLAYERS; i++) - { - if (strcasecmp(player_names[i], voteVariableStr) == 0) - { - voteVariable = i; - break; - } - } + voteVariableStr = COM_Argv(2); + voteVariable = atoi(voteVariableStr); - if (voteVariable >= 0 && voteVariable < MAXPLAYERS) + if (K_MidVoteTypeUsesVictim(voteType) == true) { - victim = &players[voteVariable]; + for (i = 0; i < MAXPLAYERS; i++) + { + if (strcasecmp(player_names[i], voteVariableStr) == 0) + { + voteVariable = i; + break; + } + } + + if (voteVariable >= 0 && voteVariable < MAXPLAYERS) + { + victim = &players[voteVariable]; + } } } @@ -206,7 +254,7 @@ static void K_PlayerSendMidVote(const UINT8 id) { return; } - + SendNetXCmdForPlayer(id, XD_SETZVOTE, NULL, 0); } @@ -226,6 +274,12 @@ static void K_PlayerSendMidVote(const UINT8 id) static void Got_SetZVote(UINT8 **cp, INT32 playernum) { (void)cp; + + if (g_midVote.active == false) + { + return; + } + g_midVote.votes[playernum] = true; } @@ -247,7 +301,7 @@ void K_RegisterMidVoteCVars(void) for (i = 0; i < MVT__MAX; i++) { - CV_RegisterVar(&cv_zvote_allowed[i]); + CV_RegisterVar(&g_midVoteTypeDefs[i].cv_allowed); } COM_AddCommand("zvote_call", Command_CallVote); @@ -277,7 +331,7 @@ boolean K_AnyMidVotesAllowed(void) for (i = 0; i < MVT__MAX; i++) { - if (cv_zvote_allowed[i].value != 0) + if (g_midVoteTypeDefs[i].cv_allowed.value != 0) { return true; } @@ -456,7 +510,7 @@ boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, return false; } - if (cv_zvote_allowed[type].value == 0) + if (g_midVoteTypeDefs[type].cv_allowed.value == 0) { // These types of votes aren't allowed on this server. if (P_IsLocalPlayer(caller) == true) @@ -551,39 +605,13 @@ void K_InitNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, play if (caller == &players[g_localplayers[i]]) { // The person who voted should already be confirmed. + g_midVote.gui[i].slide = ZVOTE_GUI_SLIDE; g_midVote.gui[i].confirm = ZVOTE_GUI_CONFIRM; + g_midVote.gui[i].unpress = true; } } } -/*-------------------------------------------------- - static void K_MidVoteKick(void) - - MVT_KICK's success function. ---------------------------------------------------*/ -static void K_MidVoteKick(void) -{ - if (g_midVote.victim == NULL) - { - return; - } - - if (server) - { - SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK); - } -} - -/*-------------------------------------------------- - static void K_MidVoteRockTheVote(void) - - MVT_RTV's success function. ---------------------------------------------------*/ -static void K_MidVoteRockTheVote(void) -{ - G_ExitLevel(); -} - /*-------------------------------------------------- void K_MidVoteSuccess(void) @@ -591,22 +619,9 @@ static void K_MidVoteRockTheVote(void) --------------------------------------------------*/ void K_MidVoteSuccess(void) { - switch (g_midVote.type) + if (g_midVoteTypeDefs[ g_midVote.type ].callback != NULL) { - case MVT_KICK: - { - K_MidVoteKick(); - break; - } - case MVT_RTV: - { - K_MidVoteRockTheVote(); - break; - } - default: - { - break; - } + g_midVoteTypeDefs[ g_midVote.type ].callback(); } K_ResetMidVote(); @@ -957,11 +972,6 @@ void K_DrawMidVote(void) else { // Draw the actual vote status - static const char *voteTitles[MVT__MAX] = { - "KICK PLAYER?", // MVT_KICK - "SKIP LEVEL?" // MVT_RTV - }; - const fixed_t barHalf = (g_zBar[0]->width * FRACUNIT * (ZVOTE_PATCH_BAR_SEGS - 1)) >> 1; const boolean blink = (gametic & 1); boolean drawButton = blink; @@ -1011,7 +1021,7 @@ void K_DrawMidVote(void) strWidth = V__OneScaleStringWidth( FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, - KART_FONT, voteTitles[g_midVote.type] + KART_FONT, g_midVoteTypeDefs[g_midVote.type].label ); V__DrawOneScaleString( @@ -1019,7 +1029,7 @@ void K_DrawMidVote(void) y - (18 * FRACUNIT), FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, NULL, - KART_FONT, voteTitles[g_midVote.type] + KART_FONT, g_midVoteTypeDefs[g_midVote.type].label ); // Vote extra text @@ -1032,7 +1042,7 @@ void K_DrawMidVote(void) { strWidth = V__OneScaleStringWidth( FRACUNIT, - V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN|V_6WIDTHSPACE, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN|V_6WIDTHSPACE|V_ALLOWLOWERCASE, TINY_FONT, player_names[g_midVote.victim - players] ); @@ -1040,7 +1050,7 @@ void K_DrawMidVote(void) x - (strWidth >> 1), y + (18 * FRACUNIT), FRACUNIT, - V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN|V_6WIDTHSPACE, NULL, + V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN|V_6WIDTHSPACE|V_ALLOWLOWERCASE, NULL, TINY_FONT, player_names[g_midVote.victim - players] ); } diff --git a/src/p_saveg.c b/src/p_saveg.c index c98c8e7a2..b80b86c62 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -1033,6 +1033,7 @@ static void P_NetUnArchiveZVote(savebuffer_t *save) I_Error("Bad $$$.sav at archive block Z-Vote"); } + K_ResetMidVote(); g_midVote.active = (boolean)READUINT8(save->p); if (g_midVote.active == true) @@ -1063,10 +1064,6 @@ static void P_NetUnArchiveZVote(savebuffer_t *save) g_midVote.endVotes = READUINT8(save->p); g_midVote.endRequired = READUINT8(save->p); } - else - { - K_ResetMidVote(); - } g_midVote.delay = (tic_t)READUINT32(save->p); } From 68916559aa91eedf5eaf85b3f0e9f74f5dc7638b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 20 Apr 2023 21:11:25 -0400 Subject: [PATCH 4/8] Z Voting: Kick handling improvements - If a player being voted against leaves before it finishes, then properly activate their penalty. - This is handled with a special case for MVT_KICK (let's not even attempt to SendKick(self) during Got_Kick :p), but any possible future vote types that use a victim will simply call K_MidVoteSuccess. - Actually define behavior for KICK_MSG_VOTE_KICK. - Default kicktime is now 20 minutes instead of 10. - Read custom reasons before possibly overriding msg type. Prevents a rare invalid read pointer. --- src/d_clisrv.c | 30 ++++++++++++++++++++++++++---- src/k_zvote.c | 17 +++++++++++++---- src/k_zvote.h | 10 ++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8fcab32dc..3a6841085 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -221,7 +221,7 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL); -consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL); +consvar_t cv_kicktime = CVAR_INIT ("kicktime", "20", CV_SAVE, CV_Unsigned, NULL); // Generate a message for an authenticating client to sign, with some guarantees about who we are. void GenerateChallenge(uint8_t *buf) @@ -3199,6 +3199,11 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) return; } + if (msg == KICK_MSG_CUSTOM_BAN || msg == KICK_MSG_CUSTOM_KICK) + { + READSTRINGN(*p, reason, MAX_REASONLENGTH+1); + } + // Is playernum authorized to make this kick? if (playernum != serverplayer && !IsPlayerAdmin(playernum) /*&& !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2 @@ -3243,9 +3248,20 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) msg = KICK_MSG_CON_FAIL; } - if (msg == KICK_MSG_CUSTOM_BAN || msg == KICK_MSG_CUSTOM_KICK) + if (g_midVote.active == true && g_midVote.victim == &players[pnum]) { - READSTRINGN(*p, reason, MAX_REASONLENGTH+1); + if (g_midVote.type == MVT_KICK) + { + // Running the callback here would mean a very dumb infinite loop. + // We'll manually handle this here by changing the msg type. + msg = KICK_MSG_VOTE_KICK; + K_MidVoteFinalize(FRACUNIT); // Vote succeeded, so the delay is normal. + } + else + { + // It should be safe to run the vote callback directly. + K_MidVoteSuccess(); + } } //CONS_Printf("\x82%s ", player_names[pnum]); @@ -3255,7 +3271,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) // to keep it all in one place. if (server) { - if (msg == KICK_MSG_KICKED || msg == KICK_MSG_CUSTOM_KICK) + if (msg == KICK_MSG_KICKED || msg == KICK_MSG_VOTE_KICK || msg == KICK_MSG_CUSTOM_KICK) { // Kick as a temporary ban. banMinutes = cv_kicktime.value; @@ -3299,6 +3315,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) HU_AddChatText(va("\x82*%s has been kicked (No reason given)", player_names[pnum]), false); kickreason = KR_KICK; break; + case KICK_MSG_VOTE_KICK: + HU_AddChatText(va("\x82*%s has been kicked (Popular demand)", player_names[pnum]), false); + kickreason = KR_KICK; + break; case KICK_MSG_PING_HIGH: HU_AddChatText(va("\x82*%s left the game (Broke delay limit)", player_names[pnum]), false); kickreason = KR_PINGLIMIT; @@ -3406,6 +3426,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress (B)\n"), reason), NULL, MM_NOTHING); else if (msg == KICK_MSG_SIGFAIL) M_StartMessage(M_GetText("Server closed connection\n(Invalid signature)\nPress (B)\n"), NULL, MM_NOTHING); + else if (msg == KICK_MSG_VOTE_KICK) + M_StartMessage(M_GetText("You have been kicked by popular demand\n\nPress (B)\n"), NULL, MM_NOTHING); else M_StartMessage(M_GetText("You have been kicked by the server\n\nPress (B)\n"), NULL, MM_NOTHING); } diff --git a/src/k_zvote.c b/src/k_zvote.c index 41a9e8b6c..259950bfb 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -612,6 +612,17 @@ void K_InitNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, play } } +/*-------------------------------------------------- + void K_MidVoteFinalize(fixed_t delayMul) + + See header file for description. +--------------------------------------------------*/ +void K_MidVoteFinalize(fixed_t delayMul) +{ + K_ResetMidVote(); + g_midVote.delay = FixedMul(cv_zvote_delay.value * TICRATE, delayMul); +} + /*-------------------------------------------------- void K_MidVoteSuccess(void) @@ -624,8 +635,7 @@ void K_MidVoteSuccess(void) g_midVoteTypeDefs[ g_midVote.type ].callback(); } - K_ResetMidVote(); - g_midVote.delay = cv_zvote_delay.value * TICRATE; // Vote succeeded, so the delay is normal. + K_MidVoteFinalize(FRACUNIT); // Vote succeeded, so the delay is normal. } /*-------------------------------------------------- @@ -635,8 +645,7 @@ void K_MidVoteSuccess(void) --------------------------------------------------*/ void K_MidVoteFailure(void) { - K_ResetMidVote(); - g_midVote.delay = (cv_zvote_delay.value * 2) * TICRATE; // Vote failed, so the delay is longer. + K_MidVoteFinalize(2*FRACUNIT); // Vote failed, so the delay is longer. } /*-------------------------------------------------- diff --git a/src/k_zvote.h b/src/k_zvote.h index b004e88ca..7296770aa 100644 --- a/src/k_zvote.h +++ b/src/k_zvote.h @@ -211,6 +211,16 @@ boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, void K_InitNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); +/*-------------------------------------------------- + void K_MidVoteFinalize(fixed_t delayMul); + + Ran when a vote is totally done, and we need to + reset the struct and set the delay timer. +--------------------------------------------------*/ + +void K_MidVoteFinalize(fixed_t delayMul); + + /*-------------------------------------------------- void K_MidVoteSuccess(void); From d9b406f4d0987aa751e8226a956299f17b1613de Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 20 Apr 2023 23:45:38 -0700 Subject: [PATCH 5/8] Add TIMER_FONT, rename V_DrawKartString to V_DrawTimerString Separates timer and FREE PLAY fonts. --- src/hu_stuff.c | 6 +++++- src/hu_stuff.h | 6 ++++++ src/k_hud.c | 43 +++++++++++++++++++++++---------------- src/k_menudraw.c | 8 ++++---- src/lua_hudlib.c | 2 +- src/lua_hudlib_drawlist.c | 2 +- src/v_video.cpp | 12 +++++------ src/v_video.h | 12 +++++------ 8 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 7cb20d7ca..8b0c5c6a9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -305,6 +305,10 @@ void HU_Init(void) PR ("MKFNT"); REG; + ADIM (TIMER); + PR ("TMFNT"); + REG; + ADIM (LT); PR ("GAMEM"); REG; @@ -2732,4 +2736,4 @@ void HU_DoTitlecardCEcho(const char *msg) tcechotext[sizeof(tcechotext) - 1] = '\0'; tcechotimer = 1; tcechoduration = TICRATE*6 + strlen(tcechotext); -} \ No newline at end of file +} diff --git a/src/hu_stuff.h b/src/hu_stuff.h index bf897de41..eaef0a59d 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -37,6 +37,11 @@ extern "C" { #define KART_FONTSIZE (KART_FONTEND - KART_FONTSTART + 1) +#define TIMER_FONTSTART '"' // the first font character +#define TIMER_FONTEND '9' + +#define TIMER_FONTSIZE (TIMER_FONTEND - TIMER_FONTSTART + 1) + #define AZ_FONTSTART 'A' // the first font character #define AZ_FONTEND 'Z' @@ -77,6 +82,7 @@ enum X (PROFNUM), X (KART), + X (TIMER), X (GM), X (LSHI), X (LSLOW), diff --git a/src/k_hud.c b/src/k_hud.c index ee11dd568..145d9f502 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1448,7 +1448,7 @@ static void K_drawKartItem(void) else { V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx); - V_DrawKartString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", stplyr->itemamount)); + V_DrawTimerString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", stplyr->itemamount)); } } else @@ -1587,30 +1587,30 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, U } if (mode && !drawtime) - V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--")); + V_DrawTimerString(TX, TY+3, splitflags, va("--'--\"--")); else { // minutes time 00 __ __ - V_DrawKartString(TX, TY+3+jitter, splitflags, va("%d", worktime/10)); - V_DrawKartString(TX+12, TY+3-jitter, splitflags, va("%d", worktime%10)); + V_DrawTimerString(TX, TY+3+jitter, splitflags, va("%d", worktime/10)); + V_DrawTimerString(TX+12, TY+3-jitter, splitflags, va("%d", worktime%10)); // apostrophe location _'__ __ - V_DrawKartString(TX+24, TY+3, splitflags, va("'")); + V_DrawTimerString(TX+24, TY+3, splitflags, va("'")); worktime = (drawtime/TICRATE % 60); // seconds time _ 00 __ - V_DrawKartString(TX+36, TY+3+jitter, splitflags, va("%d", worktime/10)); - V_DrawKartString(TX+48, TY+3-jitter, splitflags, va("%d", worktime%10)); + V_DrawTimerString(TX+36, TY+3+jitter, splitflags, va("%d", worktime/10)); + V_DrawTimerString(TX+48, TY+3-jitter, splitflags, va("%d", worktime%10)); // quotation mark location _ __"__ - V_DrawKartString(TX+60, TY+3, splitflags, va("\"")); + V_DrawTimerString(TX+60, TY+3, splitflags, va("\"")); worktime = G_TicsToCentiseconds(drawtime); // tics _ __ 00 - V_DrawKartString(TX+72, TY+3+jitter, splitflags, va("%d", worktime/10)); - V_DrawKartString(TX+84, TY+3-jitter, splitflags, va("%d", worktime%10)); + V_DrawTimerString(TX+72, TY+3+jitter, splitflags, va("%d", worktime/10)); + V_DrawTimerString(TX+84, TY+3-jitter, splitflags, va("%d", worktime%10)); } // Medal data! @@ -2490,7 +2490,7 @@ static void K_drawKartLaps(void) { // Laps V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker); - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, numlaps), numlaps)); + V_DrawTimerString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, numlaps), numlaps)); } } @@ -2986,7 +2986,7 @@ static void K_drawKartBumpersOrKarma(void) V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulestickerwide, NULL); else V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulesticker, NULL); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", numtargets, maptargets)); + V_DrawTimerString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", numtargets, maptargets)); } else { @@ -2998,7 +2998,7 @@ static void K_drawKartBumpersOrKarma(void) else V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", bumpers, maxbumper)); + V_DrawTimerString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", bumpers, maxbumper)); } } } @@ -4507,7 +4507,7 @@ static void K_drawBattleFullscreen(void) else { V_DrawFixedPatch(x<karmadelay/TICRATE)); + V_DrawTimerString(x-txoff, ty, 0, va("%d", stplyr->karmadelay/TICRATE)); } } @@ -4919,8 +4919,17 @@ void K_drawKartFreePlay(void) if (((leveltime-lt_endtime) % TICRATE) < TICRATE/2) return; - V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - 72, // mirror the laps thingy - LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, "FREE PLAY"); + V_DrawStringScaled( + ((BASEVIDWIDTH - (LAPS_X+1)) - 72) * FRACUNIT, // mirror the laps thingy + (LAPS_Y+3) * FRACUNIT, + FRACUNIT, + FRACUNIT, + FRACUNIT, + V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, + NULL, + KART_FONT, + "FREE PLAY" + ); } static void @@ -5371,7 +5380,7 @@ void K_drawKartHUD(void) else { INT32 karlen = strlen(countstr)*6; // half of 12 - V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, V_SPLITSCREEN, countstr); + V_DrawTimerString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, V_SPLITSCREEN, countstr); } } diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 2111acfbb..06d8e95d0 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1701,7 +1701,7 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) if (p != NULL) { V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap); - V_DrawCenteredKartString(x+30, y+87, 0, va("%d", p->wins)); + V_DrawCenteredTimerString(x+30, y+87, 0, va("%d", p->wins)); } @@ -3839,7 +3839,7 @@ void M_DrawItemToggles(void) V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITMUL", PU_CACHE)); V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].mvar1, false), PU_CACHE)); V_DrawScaledPatch(onx+27, ony+39, translucent, W_CachePatchName("K_ITX", PU_CACHE)); - V_DrawKartString(onx+37, ony+34, translucent, va("%d", drawnum)); + V_DrawTimerString(onx+37, ony+34, translucent, va("%d", drawnum)); } else V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].mvar1, false), PU_CACHE)); @@ -5559,7 +5559,7 @@ challengedesc: } V_DrawFixedPatch((8+offs)*FRACUNIT, 5*FRACUNIT, FRACUNIT, 0, key, NULL); - V_DrawKartString((27+offs), 9-challengesmenu.unlockcount[CC_CHAOANIM], 0, va("%u", gamedata->chaokeys)); + V_DrawTimerString((27+offs), 9-challengesmenu.unlockcount[CC_CHAOANIM], 0, va("%u", gamedata->chaokeys)); offs = challengekeybarwidth; if (gamedata->chaokeys < GDMAX_CHAOKEYS) @@ -5577,7 +5577,7 @@ challengedesc: challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY], challengesmenu.unlockcount[CC_TOTAL] ); - V_DrawRightAlignedKartString(BASEVIDWIDTH-7, 9-challengesmenu.unlockcount[CC_ANIM], 0, str); + V_DrawRightAlignedTimerString(BASEVIDWIDTH-7, 9-challengesmenu.unlockcount[CC_ANIM], 0, str); } // Name bar diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 5e08fd25d..aab85e26c 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -984,7 +984,7 @@ static int libd_drawKartString(lua_State *L) if (LUA_HUD_IsDrawListValid(list)) LUA_HUD_AddDrawKartString(list, x, y, str, flags); else - V_DrawKartString(x, y, flags, str); + V_DrawTimerString(x, y, flags, str); return 0; } diff --git a/src/lua_hudlib_drawlist.c b/src/lua_hudlib_drawlist.c index ce5b9d49f..68515ae20 100644 --- a/src/lua_hudlib_drawlist.c +++ b/src/lua_hudlib_drawlist.c @@ -468,7 +468,7 @@ void LUA_HUD_DrawList(huddrawlist_h list) V_DrawTitleCardString(item->x, item->y, itemstr, item->flags, item->bossmode, item->timer, item->threshold); break; case DI_DrawKartString: - V_DrawKartString(item->x, item->y, item->flags, itemstr); + V_DrawTimerString(item->x, item->y, item->flags, itemstr); break; default: I_Error("can't draw draw list item: invalid draw list item type"); diff --git a/src/v_video.cpp b/src/v_video.cpp index 26369c281..afab505a7 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -2800,16 +2800,16 @@ fixed_t V_DrawPingNum(fixed_t x, fixed_t y, INT32 flags, INT32 num, const UINT8 return x; } -void V_DrawCenteredKartString(INT32 x, INT32 y, INT32 option, const char *string) +void V_DrawCenteredTimerString(INT32 x, INT32 y, INT32 option, const char *string) { - x -= V_KartStringWidth(string, option)/2; - V_DrawKartString(x, y, option, string); + x -= V_TimerStringWidth(string, option)/2; + V_DrawTimerString(x, y, option, string); } -void V_DrawRightAlignedKartString(INT32 x, INT32 y, INT32 option, const char *string) +void V_DrawRightAlignedTimerString(INT32 x, INT32 y, INT32 option, const char *string) { - x -= V_KartStringWidth(string, option); - V_DrawKartString(x, y, option, string); + x -= V_TimerStringWidth(string, option); + V_DrawTimerString(x, y, option, string); } void V_DrawCenteredGamemodeString(INT32 x, INT32 y, INT32 option, const UINT8 *colormap, const char *string) diff --git a/src/v_video.h b/src/v_video.h index 28ab28d2a..65529c112 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -364,14 +364,14 @@ void V_DrawProfileNum(INT32 x, INT32 y, INT32 flags, UINT8 num); V__IntegerStringWidth ( FRACUNIT,0,CRED_FONT,string ) // SRB2Kart -#define V_DrawKartString( x,y,option,string ) \ - V__DrawDupxString (x,y,FRACUNIT,option,NULL,KART_FONT,string) +#define V_DrawTimerString( x,y,option,string ) \ + V__DrawDupxString (x,y,FRACUNIT,option,NULL,TIMER_FONT,string) -#define V_KartStringWidth( string,option ) \ - V__IntegerStringWidth ( FRACUNIT,option,KART_FONT,string ) +#define V_TimerStringWidth( string,option ) \ + V__IntegerStringWidth ( FRACUNIT,option,TIMER_FONT,string ) -void V_DrawCenteredKartString(INT32 x, INT32 y, INT32 option, const char *string); -void V_DrawRightAlignedKartString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawCenteredTimerString(INT32 x, INT32 y, INT32 option, const char *string); +void V_DrawRightAlignedTimerString(INT32 x, INT32 y, INT32 option, const char *string); #define V_DrawGamemodeString( x,y,option,cm,string ) \ V__DrawDupxString (x,y,FRACUNIT,option,cm,GM_FONT,string) From e112e1f0dcf5fbd84c8f1fd278d64e2ffaf19418 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Apr 2023 00:40:31 -0400 Subject: [PATCH 6/8] Reduce length & delay --- src/k_zvote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_zvote.c b/src/k_zvote.c index 259950bfb..41fe9b9e4 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -29,8 +29,8 @@ static consvar_t cv_zvote_quorum = CVAR_INIT ("zvote_quorum", "0.6", CV_SAVE|CV_ static consvar_t cv_zvote_spectators = CVAR_INIT ("zvote_spectator_votes", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); -static consvar_t cv_zvote_length = CVAR_INIT ("zvote_length", "90", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); -static consvar_t cv_zvote_delay = CVAR_INIT ("zvote_delay", "90", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); +static consvar_t cv_zvote_length = CVAR_INIT ("zvote_length", "20", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); +static consvar_t cv_zvote_delay = CVAR_INIT ("zvote_delay", "20", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); midVote_t g_midVote = {0}; From f86030fcda89082ab3bbf412e715d2cd5ea91c02 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Apr 2023 01:04:57 -0400 Subject: [PATCH 7/8] Z Voting: More voting conditions - Attempt to make Z votes draw if spectator - Don't show kick votes to the victim - Fix required vote count when in 1P --- src/k_zvote.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/k_zvote.c b/src/k_zvote.c index 41fe9b9e4..fc142e2dc 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -22,6 +22,7 @@ #include "v_video.h" #include "k_hud.h" #include "r_draw.h" +#include "r_fps.h" #include "byteptr.h" static CV_PossibleValue_t modulate_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; @@ -367,6 +368,12 @@ boolean K_PlayerIDAllowedInMidVote(const UINT8 id) return false; } + if (g_midVote.active == true && player == g_midVote.victim) + { + // Don't allow the guy getting kicked to vote on it. + return false; + } + return true; } @@ -378,7 +385,6 @@ boolean K_PlayerIDAllowedInMidVote(const UINT8 id) UINT8 K_RequiredMidVotes(void) { UINT8 allowedCount = 0; - UINT8 requiredCount = 0; INT32 i = INT32_MAX; if (g_midVote.active == false) @@ -408,7 +414,7 @@ UINT8 K_RequiredMidVotes(void) else { // 1P session, just require the one vote. - return requiredCount; + return 1; } } @@ -906,31 +912,11 @@ static void K_DrawMidVoteBar(fixed_t x, fixed_t y, INT32 flags, fixed_t fill, sk --------------------------------------------------*/ void K_DrawMidVote(void) { + const INT32 id = R_GetViewNumber(); midVoteGUI_t *gui = NULL; boolean pressed = false; - - UINT8 id = UINT8_MAX; fixed_t x = INT32_MAX, y = INT32_MAX; - INT32 i = INT32_MAX; - - // FIXME: We need a splitscreen version of this HUD! - for (i = 0; i <= splitscreen; i++) - { - if (stplyr == &players[ g_localplayers[i] ]) - { - id = i; - break; - } - } - - if (id == UINT8_MAX) - { - // Player we're drawing doesn't control the - // vote locally, so we shouldn't draw anything. - return; - } - pressed = G_PlayerInputDown(id, gc_z, 0); gui = &g_midVote.gui[id]; @@ -1101,7 +1087,7 @@ void K_DrawMidVote(void) drawVotes = true; } } - + if (drawVotes == true) { const fixed_t voteY = y + (2 * FRACUNIT); From a631376f1f0368a5d1b3a25686565923d166846c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 23 Apr 2023 01:12:19 -0400 Subject: [PATCH 8/8] Z Voting: Undo victim thing --- src/k_zvote.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/k_zvote.c b/src/k_zvote.c index fc142e2dc..9958e58b7 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -368,12 +368,6 @@ boolean K_PlayerIDAllowedInMidVote(const UINT8 id) return false; } - if (g_midVote.active == true && player == g_midVote.victim) - { - // Don't allow the guy getting kicked to vote on it. - return false; - } - return true; }