diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2849589ba..e5dde4921 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3255,7 +3255,11 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) { // 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; + if (msg != KICK_MSG_BANNED && msg != KICK_MSG_CUSTOM_BAN) + { + // of course, don't take the teeth out of a ban + msg = KICK_MSG_VOTE_KICK; + } K_MidVoteFinalize(FRACUNIT); // Vote succeeded, so the delay is normal. } else diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3ef453df5..e2ce5c178 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5648,6 +5648,22 @@ static void Command_Mapmd5_f(void) CONS_Printf(M_GetText("You must be in a level to use this.\n")); } +boolean G_GamestateUsesExitLevel(void) +{ + if (demo.playback) + return false; + + switch (gamestate) + { + case GS_LEVEL: + case GS_CREDITS: + return true; + + default: + return false; + } +} + static void Command_ExitLevel_f(void) { if (!(server || (IsPlayerAdmin(consoleplayer)))) @@ -5658,7 +5674,7 @@ static void Command_ExitLevel_f(void) { CONS_Printf(M_GetText("This cannot be used without cheats enabled.\n")); } - else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demo.playback) + else if (G_GamestateUsesExitLevel() == false) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); } @@ -5684,6 +5700,9 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) return; } + if (G_GamestateUsesExitLevel() == false) + return; + G_ExitLevel(); } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 3bb0fa7e5..93ad42bbf 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -242,6 +242,7 @@ void WeaponPref_Parse(UINT8 **cp, INT32 playernum); void D_SendPlayerConfig(UINT8 n); void Command_ExitGame_f(void); void Command_Retry_f(void); +boolean G_GamestateUsesExitLevel(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage); void D_SetupVote(void); diff --git a/src/k_menu.h b/src/k_menu.h index 7eefd06ea..921e0ecea 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -126,6 +126,9 @@ void M_PrevMenuGametype(UINT32 forbidden); void M_HandleHostMenuGametype(INT32 choice); void M_HandlePauseMenuGametype(INT32 choice); +extern UINT32 menucallvote; // not midVoteType_e to prevent #include k_zvote +void M_HandlePauseMenuCallVote(INT32 choice); + // // MENU TYPEDEFS // @@ -415,6 +418,8 @@ extern menu_t EXTRAS_EggTVDef; extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; +extern menu_t PAUSE_KickHandlerDef; + // EXTRAS extern menuitem_t MISC_Manual[]; extern menu_t MISC_ManualDef; @@ -447,6 +452,8 @@ typedef enum #ifdef HAVE_DISCORDRPC mpause_discordrequests, #endif + mpause_admin, + mpause_callvote, mpause_continue, mpause_spectate, @@ -1078,6 +1085,15 @@ void M_QuitPauseMenu(INT32 choice); boolean M_PauseInputs(INT32 ch); void M_PauseTick(void); +extern struct playerkickmenu_s { + tic_t ticker; + UINT8 player; + UINT8 poke; + boolean adminpowered; +} playerkickmenu; + +void M_KickHandler(INT32 choice); + extern consvar_t cv_dummymenuplayer; extern consvar_t cv_dummyspectator; @@ -1150,6 +1166,7 @@ void M_DrawMPServerBrowser(void); // Pause menu: void M_DrawPause(void); +void M_DrawKickHandler(void); // Replay Playback void M_DrawPlaybackMenu(void); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d01bd593d..40f96c9c7 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -52,6 +52,7 @@ #include "doomstat.h" // MAXSPLITSCREENPLAYERS #include "k_grandprix.h" // K_CanChangeRules #include "k_rank.h" // K_GetGradeColor +#include "k_zvote.h" // K_GetMidVoteLabel #include "y_inter.h" // Y_RoundQueueDrawer @@ -3065,7 +3066,7 @@ void M_DrawMPHost(void) } break; } - case IT_KEYHANDLER: + case IT_ARROWS: { if (currentMenu->menuitems[i].itemaction.routine != M_HandleHostMenuGametype) break; @@ -4262,11 +4263,6 @@ void M_DrawPause(void) INT16 arrxpos = 150 + 2*offset; // To draw the background arrow. INT16 j = 0; - char word1[MAXSTRINGLENGTH]; - INT16 word1len = 0; - char word2[MAXSTRINGLENGTH]; - INT16 word2len = 0; - boolean sok = false; patch_t *vertbg = W_CachePatchName("M_STRIPV", PU_CACHE); patch_t *arrstart = W_CachePatchName("M_PTIP", PU_CACHE); @@ -4364,54 +4360,107 @@ void M_DrawPause(void) } // Draw the string! - // ...but first get what we need to get. - while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH) - { - char c = currentMenu->menuitems[itemOn].text[j]; - if (c == ' ' && !sok) - { - sok = true; - j++; - continue; // We don't care about this :moyai: - } - - if (sok) - { - word2[word2len] = c; - word2len++; - } - else - { - word1[word1len] = c; - word1len++; - } - - j++; - } - - word1[word1len] = '\0'; - word2[word2len] = '\0'; + const char *maintext = NULL; + const char *selectabletext = NULL; + INT32 mainflags = V_YELLOWMAP, selectableflags = 0; if (itemOn == mpause_changegametype) { - INT32 w = V_LSTitleLowStringWidth(gametypes[menugametype]->name, 0)/2; + selectabletext = gametypes[menugametype]->name; + } + else if (itemOn == mpause_callvote) + { + selectabletext = K_GetMidVoteLabel(menucallvote); - if (word1len) - V_DrawCenteredLSTitleHighString(220 + offset*2, 75, 0, word1); + if (K_MinimalCheckNewMidVote(menucallvote) == false) + { + if (g_midVote.active == true) + { + maintext = "ACTIVE..."; + } + else if (g_midVote.delay > 0) + { + if (g_midVote.delay != 1) + maintext = va("%u", ((g_midVote.delay - 1) / TICRATE) + 1); + } + else if (K_PlayerIDAllowedInMidVote(consoleplayer) == false) + { + maintext = "SPECTATING"; + } + else + { + maintext = "INVALID!?"; + } - V_DrawLSTitleLowString(220-w + offset*2, 103, V_YELLOWMAP, gametypes[menugametype]->name); - V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | V_YELLOWMAP, false); // left arrow - V_DrawCharacter(220+w + offset*2 + 4 + (skullAnimCounter/5), 103+6, '\x1D' | V_YELLOWMAP, false); // right arrow + if (maintext != NULL) + selectableflags |= V_MODULATE; + } } else { + maintext = currentMenu->menuitems[itemOn].text; + mainflags = 0; + } + + if (selectabletext != NULL) + { + // We have a selection. Let's show the full menu text on top, and the choice below. + + if (currentMenu->menuitems[itemOn].text) + V_DrawCenteredLSTitleHighString(220 + offset*2, 75, selectableflags, currentMenu->menuitems[itemOn].text); + + selectableflags |= V_YELLOWMAP; + + INT32 w = V_LSTitleLowStringWidth(selectabletext, selectableflags)/2; + V_DrawLSTitleLowString(220-w + offset*2, 103, selectableflags, selectabletext); + + V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | selectableflags, false); // left arrow + V_DrawCharacter(220+w + offset*2 + (skullAnimCounter/5), 103+6, '\x1D' | selectableflags, false); // right arrow + } + + if (maintext != NULL) + { + // This is a regular menu option. Try to break it onto two lines. + + char word1[MAXSTRINGLENGTH]; + INT16 word1len = 0; + char word2[MAXSTRINGLENGTH]; + INT16 word2len = 0; + boolean sok = false; + + while (maintext[j] && j < MAXSTRINGLENGTH) + { + if (maintext[j] == ' ' && !sok) + { + sok = true; + j++; + continue; // We don't care about this :moyai: + } + + if (sok) + { + word2[word2len] = maintext[j]; + word2len++; + } + else + { + word1[word1len] = maintext[j]; + word1len++; + } + + j++; + } + + word1[word1len] = '\0'; + word2[word2len] = '\0'; + // If there's no 2nd word, take this opportunity to center this line of text. if (word1len) - V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); + V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), mainflags, word1); if (word2len) - V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2); + V_DrawCenteredLSTitleLowString(220 + offset*2, 103, mainflags, word2); } if (gamestate != GS_INTERMISSION && roundqueue.size > 0) @@ -4493,6 +4542,106 @@ void M_DrawPause(void) } } +void M_DrawKickHandler(void) +{ + // fake round queue drawer simply to make release + INT32 x = 29 + 4, y = 70, returny = y; + INT32 pokeamount = (playerkickmenu.poke & 1) ? -playerkickmenu.poke/2 : playerkickmenu.poke/2; + INT32 x2 = x + pokeamount - 9 - 8; + + boolean datarightofcolumn = false; + + patch_t *resbar = W_CachePatchName("R_RESBAR", PU_CACHE); // Results bars for players + + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + V_DrawMappedPatch( + x, y, + (playeringame[i] == true) + ? ((players[i].spectator == true) ? V_TRANSLUCENT : 0) + : V_MODULATE, + resbar, NULL + ); + + V_DrawRightAlignedThinString( + x+13, y-2, + ((i == playerkickmenu.player) + ? highlightflags + : 0 + ), + va("%u", i) + ); + + if (playeringame[i] == true) + { + if (players[i].skincolor != SKINCOLOR_NONE) + { + UINT8 *charcolormap; + if ((players[i].pflags & PF_NOCONTEST) && players[i].bot) + { + // RETIRED !! + charcolormap = R_GetTranslationColormap(TC_DEFAULT, players[i].skincolor, GTC_CACHE); + V_DrawMappedPatch(x+14, y-5, 0, W_CachePatchName("MINIDEAD", PU_CACHE), charcolormap); + } + else + { + charcolormap = R_GetTranslationColormap(players[i].skin, players[i].skincolor, GTC_CACHE); + V_DrawMappedPatch(x+14, y-5, 0, faceprefix[players[i].skin][FACE_MINIMAP], charcolormap); + } + } + + V_DrawThinString( + x+27, y-2, + ( + P_IsMachineLocalPlayer(&players[i]) + ? highlightflags + : 0 + )|V_ALLOWLOWERCASE|V_6WIDTHSPACE, + player_names[i] + ); + + V_DrawRightAlignedThinString( + x+118, y-2, + V_ALLOWLOWERCASE|V_6WIDTHSPACE, + (players[i].spectator) ? "SPECTATOR" : "PLAYING" + ); + } + + if (i == playerkickmenu.player) + { + V_DrawScaledPatch( + x2, y-1, + (datarightofcolumn ? V_FLIP : 0), + W_CachePatchName("M_CURSOR", PU_CACHE) + ); + } + + y += 13; + + if (i == (MAXPLAYERS-1)/2) + { + x = 169 - 4; + y = returny; + + datarightofcolumn = true; + x2 = x + 118 + 9 + 8 + 4 - pokeamount; + } + } + + //V_DrawFill(32 + (playerkickmenu.player & 8), 32 + (playerkickmenu.player & 7)*8, 8, 8, playeringame[playerkickmenu.player] ? 0 : 16); + + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + V_DrawCenteredThinString( + BASEVIDWIDTH/2, 12, + V_ALLOWLOWERCASE|V_6WIDTHSPACE, + (playerkickmenu.adminpowered) + ? "You are using ""\x85""Admin Tools""\x80"", ""\x83""(A)""\x80"" to kick and ""\x84""(C)""\x80"" to ban" + : K_GetMidVoteLabel(menucallvote) + ); +} + void M_DrawPlaybackMenu(void) { INT16 i; diff --git a/src/k_zvote.c b/src/k_zvote.c index 9958e58b7..8ce9fde7e 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -57,10 +57,7 @@ static void K_MidVoteKick(void) return; } - if (server) - { - SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK); - } + SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK); } /*-------------------------------------------------- @@ -70,7 +67,29 @@ static void K_MidVoteKick(void) --------------------------------------------------*/ static void K_MidVoteRockTheVote(void) { - G_ExitLevel(); + if (G_GamestateUsesExitLevel() == false) + { + return; + } + + SendNetXCmd(XD_EXITLEVEL, NULL, 0); +} + +/*-------------------------------------------------- + static void K_MidVoteRunItBack(void) + + MVT_RUNITBACK's success function. +--------------------------------------------------*/ +static void K_MidVoteRunItBack(void) +{ + boolean newencore = false; + + if (cv_kartencore.value != 0) + { + newencore = (cv_kartencore.value == 1) || encoremode; + } + + D_MapChange(gamemap, gametype, newencore, false, 0, false, false); } static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = @@ -88,22 +107,21 @@ static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = CVAR_INIT ("zvote_rtv_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), K_MidVoteRockTheVote }, + + { // MVT_RUNITBACK + "RUNITBACK", + "Redo Level?", + CVAR_INIT ("zvote_runitback_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), + K_MidVoteRunItBack + }, }; /*-------------------------------------------------- - static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) + 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. + See header file for description. --------------------------------------------------*/ -static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) +boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) { switch (voteType) { @@ -125,9 +143,6 @@ static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) --------------------------------------------------*/ static void Command_CallVote(void) { - UINT8 buf[MAXTEXTCMD]; - UINT8 *buf_p = buf; - size_t numArgs = 0; const char *voteTypeStr = NULL; @@ -136,8 +151,6 @@ static void Command_CallVote(void) const char *voteVariableStr = NULL; INT32 voteVariable = 0; - player_t *victim = NULL; - INT32 i = INT32_MAX; if (netgame == false) @@ -184,11 +197,26 @@ static void Command_CallVote(void) break; } } + } + } - if (voteVariable >= 0 && voteVariable < MAXPLAYERS) - { - victim = &players[voteVariable]; - } + K_SendCallMidVote(voteType, voteVariable); +} + +/*-------------------------------------------------- + void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable) + + See header file for description. +--------------------------------------------------*/ +void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable) +{ + player_t *victim = NULL; + + if (K_MidVoteTypeUsesVictim(voteType) == true) + { + if (voteVariable >= 0 && voteVariable < MAXPLAYERS) + { + victim = &players[voteVariable]; } } @@ -198,6 +226,9 @@ static void Command_CallVote(void) return; } + UINT8 buf[MAXTEXTCMD]; + UINT8 *buf_p = buf; + WRITEUINT8(buf_p, voteType); WRITEINT32(buf_p, voteVariable); @@ -341,6 +372,51 @@ boolean K_AnyMidVotesAllowed(void) return false; } +/*-------------------------------------------------- + midVoteType_e K_GetNextCallableMidVote(INT32 seed, boolean backwards) + + See header file for description. +--------------------------------------------------*/ + +midVoteType_e K_GetNextAllowedMidVote(midVoteType_e seed, boolean backwards) +{ + if (seed >= MVT__MAX) + seed = 0; + + midVoteType_e i = seed; + + if (backwards) + { + do + { + if (i <= 0) + i = MVT__MAX; + i--; + + if (g_midVoteTypeDefs[i].cv_allowed.value != 0) + return i; + + } + while (i != seed); + } + else + { + do + { + i++; + if (i >= MVT__MAX) + i = 0; + + if (g_midVoteTypeDefs[i].cv_allowed.value != 0) + return i; + + } + while (i != seed); + } + + return MVT__MAX; +} + /*-------------------------------------------------- boolean K_PlayerIDAllowedInMidVote(const UINT8 id) @@ -468,6 +544,46 @@ UINT8 K_CountMidVotes(void) return voteCount; } +/*-------------------------------------------------- + boolean K_MinimalCheckNewMidVote(midVoteType_e type) + + See header file for description. +--------------------------------------------------*/ +boolean K_MinimalCheckNewMidVote(midVoteType_e type) +{ + if (g_midVote.active == true) + { + // Don't allow another vote if one is already running. + return false; + } + + if (g_midVote.delay > 0) + { + // Don't allow another vote if one has recently just ran. + return false; + } + + if (type < 0 || type >= MVT__MAX) + { + // Invalid range. + return false; + } + + if (g_midVoteTypeDefs[type].cv_allowed.value == 0) + { + // These types of votes aren't allowed on this server. + return false; + } + + if (K_PlayerIDAllowedInMidVote(consoleplayer) == false) + { + // Invalid calling player. + return false; + } + + return true; +} + /*-------------------------------------------------- boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim) @@ -630,7 +746,11 @@ void K_MidVoteFinalize(fixed_t delayMul) --------------------------------------------------*/ void K_MidVoteSuccess(void) { - if (g_midVoteTypeDefs[ g_midVote.type ].callback != NULL) + if ( + server == true + && demo.playback == false + && g_midVoteTypeDefs[ g_midVote.type ].callback != NULL + ) { g_midVoteTypeDefs[ g_midVote.type ].callback(); } @@ -823,6 +943,25 @@ void K_UpdateMidVotePatches(void) HU_UpdatePatch(&g_zBarEnds[1][1][1], "TLBXB0"); } +/*-------------------------------------------------- + const char *K_GetMidVoteLabel(midVoteType_e i) + + See header file for description. +--------------------------------------------------*/ + +const char *K_GetMidVoteLabel(midVoteType_e i) +{ + if ( + i < 0 + || i >= MVT__MAX + || g_midVoteTypeDefs[i].label == NULL) + { + return "N/A"; + } + + return g_midVoteTypeDefs[i].label; +} + /*-------------------------------------------------- static void K_DrawMidVoteBar(fixed_t x, fixed_t y, INT32 flags, fixed_t fill, skincolornum_t color, boolean flipped) @@ -1006,11 +1145,13 @@ void K_DrawMidVote(void) (id & 1) ); + const char *label = K_GetMidVoteLabel(g_midVote.type); + // Vote main label strWidth = V__OneScaleStringWidth( FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, - KART_FONT, g_midVoteTypeDefs[g_midVote.type].label + KART_FONT, label ); V__DrawOneScaleString( @@ -1018,7 +1159,7 @@ void K_DrawMidVote(void) y - (18 * FRACUNIT), FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, NULL, - KART_FONT, g_midVoteTypeDefs[g_midVote.type].label + KART_FONT, label ); // Vote extra text diff --git a/src/k_zvote.h b/src/k_zvote.h index 7296770aa..61ee8c630 100644 --- a/src/k_zvote.h +++ b/src/k_zvote.h @@ -28,6 +28,7 @@ typedef enum { MVT_KICK, // Kick another player in the server MVT_RTV, // Exit level early + MVT_RUNITBACK, // Restart level fresh MVT__MAX, // Total number of vote types } midVoteType_e; @@ -63,6 +64,39 @@ struct midVote_t extern midVote_t g_midVote; +/*-------------------------------------------------- + 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. +--------------------------------------------------*/ + +boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType); + + +/*-------------------------------------------------- + void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable) + + Prepares and sends net packet for calling a midvote. + + Input Arguments:- + voteType - The type of vote a local player is trying to call. + variable - Extra arguments for the vote type. + + Return:- + N/A +--------------------------------------------------*/ + +void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable); + + /*-------------------------------------------------- void K_RegisterMidVoteCVars(void); @@ -99,6 +133,22 @@ void K_ResetMidVote(void); boolean K_AnyMidVotesAllowed(void); +/*-------------------------------------------------- + midVoteType_e K_GetNextCallableMidVote(midVoteType_e seed, boolean backwards) + + Gets the next enabled Z-vote type in the list. + + Input Arguments:- + seed - position in the list to start with + backwards - if true, traverses list in reverse order + + Return:- + next Z-vote id if any vote types are enabled, otherwise MVT__MAX. +--------------------------------------------------*/ + +midVoteType_e K_GetNextAllowedMidVote(midVoteType_e seed, boolean backwards); + + /*-------------------------------------------------- boolean K_PlayerIDAllowedInMidVote(const UINT8 id); @@ -170,6 +220,18 @@ boolean K_PlayerIDMidVoted(const UINT8 id); UINT8 K_CountMidVotes(void); +/*-------------------------------------------------- + boolean K_MinimalCheckNewMidVote(midVoteType_e type) + + Returns if the variables given are a valid state for + pause menu Z-vote flow. + + Input Arguments:- + type - The type of vote they're trying to call. +--------------------------------------------------*/ + +boolean K_MinimalCheckNewMidVote(midVoteType_e type); + /*-------------------------------------------------- boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); @@ -263,6 +325,19 @@ void K_TickMidVote(void); void K_UpdateMidVotePatches(void); +/*-------------------------------------------------- + const char *K_GetMidVoteLabel(midVoteType_e i) + + Input Arguments:- + i - id in the list to retrieve label for + + Return:- + label associated with that id, or a sensible default (not NULL) +--------------------------------------------------*/ + +const char *K_GetMidVoteLabel(midVoteType_e i); + + /*-------------------------------------------------- void K_DrawMidVote(void); diff --git a/src/menus/play-online-1.c b/src/menus/play-online-1.c index 8a9b74234..7f9a9c294 100644 --- a/src/menus/play-online-1.c +++ b/src/menus/play-online-1.c @@ -80,8 +80,8 @@ void M_MPOptSelectInit(INT32 choice) memcpy(&mpmenu.modewinextend, &arrcpy, sizeof(mpmenu.modewinextend)); // Guarantee menugametype is good - M_NextMenuGametype(forbidden); M_PrevMenuGametype(forbidden); + M_NextMenuGametype(forbidden); if (choice != -1) { diff --git a/src/menus/play-online-host.c b/src/menus/play-online-host.c index 7ab684c5c..a7061a32a 100644 --- a/src/menus/play-online-host.c +++ b/src/menus/play-online-host.c @@ -18,7 +18,7 @@ menuitem_t PLAY_MP_Host[] = {IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.", NULL, {.cvar = &cv_maxplayers}, 0, 0}, - {IT_STRING | IT_KEYHANDLER, "Gamemode", "Choose the type of play on your server.", + {IT_STRING | IT_ARROWS, "Gamemode", "Choose the type of play on your server.", NULL, {.routine = M_HandleHostMenuGametype}, 0, 0}, {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", @@ -54,41 +54,21 @@ void M_MPHostInit(INT32 choice) void M_HandleHostMenuGametype(INT32 choice) { - const UINT8 pid = 0; const UINT32 forbidden = GTR_FORBIDMP; - (void)choice; - - if (M_MenuBackPressed(pid)) - { - M_GoBack(0); - M_SetMenuDelay(pid); - return; - } - else if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid)) + if (choice > 0) { M_NextMenuGametype(forbidden); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); } - else if (menucmd[pid].dpad_lr < 0) + else if (choice == -1) + { + menugametype = GT_RACE; + M_PrevMenuGametype(forbidden); + M_NextMenuGametype(forbidden); + } + else { M_PrevMenuGametype(forbidden); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); - } - - if (menucmd[pid].dpad_ud > 0) - { - M_NextOpt(); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); - } - else if (menucmd[pid].dpad_ud < 0) - { - M_PrevOpt(); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); } } diff --git a/src/menus/transient/CMakeLists.txt b/src/menus/transient/CMakeLists.txt index 1b9309749..aa1397595 100644 --- a/src/menus/transient/CMakeLists.txt +++ b/src/menus/transient/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(SRB2SDL2 PRIVATE discord-requests.c message-box.c pause-game.c + pause-kick.c pause-replay.c virtual-keyboard.c ) diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index c3b640ea8..9a24e77ca 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -5,6 +5,7 @@ #include "../../k_grandprix.h" // K_CanChangeRules #include "../../m_cond.h" #include "../../s_sound.h" +#include "../../k_zvote.h" #ifdef HAVE_DISCORDRPC #include "../../discord.h" @@ -22,7 +23,7 @@ menuitem_t PAUSE_Main[] = {IT_STRING | IT_CALL, "STEREO MODE", "M_ICOSTM", NULL, {.routine = M_SoundTest}, 0, 0}, - {IT_STRING | IT_KEYHANDLER, "GAMETYPE", "M_ICOGAM", + {IT_STRING | IT_ARROWS, "GAMETYPE", "M_ICOGAM", NULL, {.routine = M_HandlePauseMenuGametype}, 0, 0}, {IT_STRING | IT_CALL, "CHANGE MAP", "M_ICOMAP", @@ -39,6 +40,12 @@ menuitem_t PAUSE_Main[] = NULL, {.routine = M_DiscordRequests}, 0, 0}, #endif + {IT_STRING | IT_ARROWS, "ADMIN TOOLS", "M_ICOADM", + NULL, {.routine = M_KickHandler}, 0, 0}, + + {IT_STRING | IT_ARROWS, "CALL VOTE", "M_ICOVOT", + NULL, {.routine = M_HandlePauseMenuCallVote}, 0, 0}, + {IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP", NULL, {.routine = M_QuitPauseMenu}, 0, 0}, @@ -71,7 +78,7 @@ menu_t PAUSE_MainDef = { PAUSE_Main, 0, 0, 0, 0, - 0, + MBF_SOUNDLESS, NULL, 1, 10, // For transition with some menus! M_DrawPause, @@ -123,6 +130,8 @@ void M_OpenPauseMenu(void) PAUSE_Main[mpause_switchmap].status = IT_DISABLED; PAUSE_Main[mpause_restartmap].status = IT_DISABLED; PAUSE_Main[mpause_tryagain].status = IT_DISABLED; + PAUSE_Main[mpause_callvote].status = IT_DISABLED; + PAUSE_Main[mpause_admin].status = IT_DISABLED; #ifdef HAVE_DISCORDRPC PAUSE_Main[mpause_discordrequests].status = IT_DISABLED; #endif @@ -146,7 +155,7 @@ void M_OpenPauseMenu(void) if (server || IsPlayerAdmin(consoleplayer)) { - PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_KEYHANDLER; + PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_ARROWS; menugametype = gametype; PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL; @@ -156,12 +165,21 @@ void M_OpenPauseMenu(void) { PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; } + + if (netgame) + { + PAUSE_Main[mpause_admin].status = IT_STRING | IT_CALL; + } } } else if (!netgame && !demo.playback) { boolean retryallowed = (modeattacking != ATTACKING_NONE); - if (G_GametypeUsesLives()) + if ( + retryallowed == false + && gamestate == GS_LEVEL + && G_GametypeUsesLives() + ) { for (i = 0; i <= splitscreen; i++) { @@ -178,6 +196,18 @@ void M_OpenPauseMenu(void) } } + if (netgame) // && (PAUSE_Main[mpause_admin].status == IT_DISABLED)) + { + menucallvote = K_GetNextAllowedMidVote(menucallvote, true); + + if (menucallvote != MVT__MAX) + { + menucallvote = K_GetNextAllowedMidVote(menucallvote, false); + + PAUSE_Main[mpause_callvote].status = IT_STRING | IT_ARROWS; + } + } + if (G_GametypeHasSpectators()) { if (splitscreen) @@ -267,12 +297,9 @@ boolean M_PauseInputs(INT32 ch) // Change gametype void M_HandlePauseMenuGametype(INT32 choice) { - const UINT8 pid = 0; const UINT32 forbidden = GTR_FORBIDMP; - (void)choice; - - if (M_MenuConfirmPressed(pid)) + if (choice == 2) { if (menugametype != gametype) { @@ -281,27 +308,60 @@ void M_HandlePauseMenuGametype(INT32 choice) return; } - M_SetMenuDelay(pid); S_StartSound(NULL, sfx_s3k7b); + + return; } - else if (M_MenuExtraPressed(pid)) + + if (choice == -1) { menugametype = gametype; - M_SetMenuDelay(pid); S_StartSound(NULL, sfx_s3k7b); + return; } - else if (menucmd[pid].dpad_lr > 0) - { - M_NextMenuGametype(forbidden); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); - } - else if (menucmd[pid].dpad_lr < 0) + + if (choice == 0) { M_PrevMenuGametype(forbidden); S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); } + else + { + M_NextMenuGametype(forbidden); + S_StartSound(NULL, sfx_s3k5b); + } +} + +// Call vote +UINT32 menucallvote = MVT__MAX; + +void M_HandlePauseMenuCallVote(INT32 choice) +{ + if (choice == 2) + { + if (K_MinimalCheckNewMidVote(menucallvote) == false) + { + // Invalid. + S_StartSound(NULL, sfx_s3k7b); + } + else if (K_MidVoteTypeUsesVictim(menucallvote) == true) + { + S_StartSound(NULL, sfx_s3k5b); + M_KickHandler(-1); + } + else + { + // Bog standard and victimless, let's send it on its way! + M_ClearMenus(true); + K_SendCallMidVote(menucallvote, 0); + return; + } + + return; + } + + menucallvote = K_GetNextAllowedMidVote(menucallvote, (choice == 0)); + S_StartSound(NULL, sfx_s3k5b); } // Restart map diff --git a/src/menus/transient/pause-kick.c b/src/menus/transient/pause-kick.c new file mode 100644 index 000000000..2f4a83ab5 --- /dev/null +++ b/src/menus/transient/pause-kick.c @@ -0,0 +1,135 @@ +/// \file menus/transient/pause-kick.c +/// \brief Player Kick menu + +#include "../../k_menu.h" +#include "../../s_sound.h" +#include "../../p_local.h" +#include "../../k_zvote.h" + +struct playerkickmenu_s playerkickmenu; + +static void M_PlayerKickHandler(INT32 choice) +{ + const UINT8 pid = 0; + + UINT8 kicktype = UINT8_MAX; + + (void)choice; + + if (menucmd[pid].dpad_lr != 0) // symmetrical in this case + { + S_StartSound(NULL, sfx_s3k5b); + + playerkickmenu.player = ((playerkickmenu.player + 8) % MAXPLAYERS); + + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_ud > 0) + { + S_StartSound(NULL, sfx_s3k5b); + + playerkickmenu.player = ((playerkickmenu.player + 1) & 7) + (playerkickmenu.player & 8); + + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_ud < 0) + { + S_StartSound(NULL, sfx_s3k5b); + + playerkickmenu.player = ((playerkickmenu.player + 7) & 7) + (playerkickmenu.player & 8); + + M_SetMenuDelay(pid); + } + + else if (M_MenuBackPressed(pid)) + { + M_GoBack(0); + M_SetMenuDelay(pid); + } + + else if (M_MenuExtraPressed(pid) && playerkickmenu.adminpowered) + { + kicktype = KICK_MSG_BANNED; + } + + else if (M_MenuConfirmPressed(pid)) + { + kicktype = KICK_MSG_KICKED; + } + + if (kicktype != UINT8_MAX) + { + M_SetMenuDelay(pid); + + if ( + playeringame[playerkickmenu.player] + && P_IsMachineLocalPlayer(&players[playerkickmenu.player]) == false + && playerkickmenu.player != serverplayer + ) + { + if (playerkickmenu.adminpowered) + { + if (consoleplayer == serverplayer || IsPlayerAdmin(consoleplayer)) + { + playerkickmenu.poke = (kicktype == KICK_MSG_BANNED) ? 16 : 12; + SendKick(playerkickmenu.player, kicktype); + return; + } + } + else if ( + K_MinimalCheckNewMidVote(menucallvote) == true +#ifndef DEVELOP + && IsPlayerAdmin(playerkickmenu.player) == false +#endif + ) + { + M_ClearMenus(true); + K_SendCallMidVote(menucallvote, playerkickmenu.player); + return; + } + } + + playerkickmenu.poke = 8; + S_StartSound(NULL, sfx_s3k7b); + } +} + +static menuitem_t PAUSE_KickHandler[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_PlayerKickHandler}, 0, 0}, +}; + +static void M_KickHandlerTick(void) +{ + playerkickmenu.ticker++; + + if (playerkickmenu.poke) + playerkickmenu.poke--; +} + +menu_t PAUSE_KickHandlerDef = { + sizeof(PAUSE_KickHandler) / sizeof(menuitem_t), + &PAUSE_MainDef, + 0, + PAUSE_KickHandler, + 0, 0, + 0, 0, + 0, + NULL, + 0, 0, + M_DrawKickHandler, + M_KickHandlerTick, + NULL, + NULL, + NULL, +}; + +void M_KickHandler(INT32 choice) +{ + playerkickmenu.adminpowered = (choice >= 0); + + PAUSE_KickHandlerDef.prevMenu = currentMenu; + M_SetupNextMenu(&PAUSE_KickHandlerDef, true); +} diff --git a/src/v_video.cpp b/src/v_video.cpp index d25778061..ae65de142 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -2286,7 +2286,7 @@ void V_DrawStringScaled( break; case LSHI_FONT: case LSLOW_FONT: - spacew = 16; + spacew = 10; break; case OPPRF_FONT: spacew = 5; @@ -2579,7 +2579,7 @@ fixed_t V_StringScaledWidth( break; case LSHI_FONT: case LSLOW_FONT: - spacew = 16; + spacew = 10; break; case OPPRF_FONT: spacew = 5;