From f1ec39764f8ac897daec73961038c9190d20234b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 3 Apr 2023 22:34:17 -0400 Subject: [PATCH 01/16] New voting screen background --- src/k_vote.c | 120 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 19 deletions(-) diff --git a/src/k_vote.c b/src/k_vote.c index 0eefbacda..bf8fdb6ee 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -55,10 +55,6 @@ #include "hardware/hw_main.h" #endif -// graphics -static patch_t *bgpatch = NULL; // INTERSCR -static patch_t *widebgpatch = NULL; - static INT32 timer; #define UNLOAD(x) if (x) {Patch_Free(x);} x = NULL; @@ -105,6 +101,14 @@ static patch_t *cursor4 = NULL; static patch_t *randomlvl = NULL; static patch_t *rubyicon = NULL; +#define PLANET_FRAMES (9) +#define TEXT_LEVEL_SCROLL (2*FRACUNIT) +#define TEXT_DERR_SCROLL (2*FRACUNIT) +static patch_t *bg_planet[PLANET_FRAMES] = { NULL }; +static patch_t *bg_checker = NULL; +static patch_t *bg_levelText = NULL; +static patch_t *bg_derrText = NULL; + static void Y_UnloadVoteData(void); // @@ -112,6 +116,75 @@ static void Y_UnloadVoteData(void); // // Draws the voting screen! // +static void Y_DrawVoteBackground(void) +{ + static fixed_t bgTimer = 0; + + static fixed_t derrPos = 0; + const fixed_t derrLoop = bg_derrText->width * FRACUNIT; + + static fixed_t levelPos = 0; + const fixed_t levelLoop = bg_levelText->height * FRACUNIT; + + const UINT8 planetFrame = (bgTimer / FRACUNIT) % PLANET_FRAMES; + + V_DrawFixedPatch( + 0, 0, + FRACUNIT, 0, + bg_planet[planetFrame], NULL + ); + V_DrawFixedPatch( + (BASEVIDWIDTH - bg_checker->width) * FRACUNIT, 0, + FRACUNIT, V_ADD, + bg_checker, NULL + ); + V_DrawFixedPatch( + (BASEVIDWIDTH - bg_checker->width) * FRACUNIT, 0, + FRACUNIT, V_ADD, + bg_checker, NULL + ); + + levelPos += FixedMul(TEXT_DERR_SCROLL, renderdeltatics); + while (levelPos > levelLoop) + { + levelPos -= levelLoop; + } + + V_DrawFixedPatch( + ((BASEVIDWIDTH - bg_levelText->width) * FRACUNIT) - levelPos, + -levelPos, + FRACUNIT, V_ADD, + bg_levelText, NULL + ); + V_DrawFixedPatch( + ((BASEVIDWIDTH - bg_levelText->width) * FRACUNIT) - levelPos + levelLoop, + -levelPos + levelLoop, + FRACUNIT, V_ADD, + bg_levelText, NULL + ); + + derrPos += FixedMul(TEXT_DERR_SCROLL, renderdeltatics); + while (derrPos > derrLoop) + { + derrPos -= derrLoop; + } + + V_DrawFixedPatch( + -derrPos, + (BASEVIDHEIGHT - bg_derrText->height) * FRACUNIT, + FRACUNIT, V_SUBTRACT, + bg_derrText, NULL + ); + V_DrawFixedPatch( + -derrPos + derrLoop, + (BASEVIDHEIGHT - bg_derrText->height) * FRACUNIT, + FRACUNIT, V_SUBTRACT, + bg_derrText, NULL + ); + + bgTimer += renderdeltatics; +} + void Y_VoteDrawer(void) { INT32 i, x, y = 0, height = 0; @@ -138,16 +211,7 @@ void Y_VoteDrawer(void) rubyfloattime += FixedMul(ANGLE_MAX/NEWTICRATE, renderdeltatics); } - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - - if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx > 320) - V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(widebgpatch->width)/2), - (vid.height / vid.dupy) - SHORT(widebgpatch->height), - V_SNAPTOTOP|V_SNAPTOLEFT, widebgpatch); - else - V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(bgpatch->width)/2), // Keep the width/height adjustments, for screens that are less wide than 320(?) - (vid.height / vid.dupy) - SHORT(bgpatch->height), - V_SNAPTOTOP|V_SNAPTOLEFT, bgpatch); + Y_DrawVoteBackground(); for (i = 0; i < 4; i++) // First, we need to figure out the height of this thing... { @@ -395,6 +459,8 @@ void Y_VoteDrawer(void) V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP, va("Vote ends in %d", tickdown)); } + + M_DrawMenuForeground(); } // @@ -659,7 +725,7 @@ void Y_VoteTicker(void) void Y_StartVote(void) { INT32 i = 0; - boolean battlemode = ((votelevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); // todo gametyperules + //boolean battlemode = ((votelevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); // todo gametyperules votetic = -1; @@ -668,8 +734,6 @@ void Y_StartVote(void) I_Error("voteendtic is dirty"); #endif - widebgpatch = W_CachePatchName((battlemode ? "BATTLSCW" : "INTERSCW"), PU_STATIC); - bgpatch = W_CachePatchName((battlemode ? "BATTLSCR" : "INTERSCR"), PU_STATIC); cursor = W_CachePatchName("M_CURSOR", PU_STATIC); cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC); cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC); @@ -678,6 +742,15 @@ void Y_StartVote(void) randomlvl = W_CachePatchName("RANDOMLV", PU_STATIC); rubyicon = W_CachePatchName("RUBYICON", PU_STATIC); + for (i = 0; i < PLANET_FRAMES; i++) + { + bg_planet[i] = W_CachePatchName(va("VT_BG_%d", i + 1), PU_STATIC); + } + + bg_checker = W_CachePatchName("VT_RACE", PU_STATIC); + bg_levelText = W_CachePatchName("VT_WELC", PU_STATIC); + bg_derrText = W_CachePatchName("VT_DERR", PU_STATIC); + timer = cv_votetime.value*TICRATE; pickedvote = -1; @@ -760,13 +833,13 @@ void Y_EndVote(void) // static void Y_UnloadVoteData(void) { + INT32 i; + voteclient.loaded = false; if (rendermode != render_soft) return; - UNLOAD(widebgpatch); - UNLOAD(bgpatch); UNLOAD(cursor); UNLOAD(cursor1); UNLOAD(cursor2); @@ -774,6 +847,15 @@ static void Y_UnloadVoteData(void) UNLOAD(cursor4); UNLOAD(randomlvl); UNLOAD(rubyicon); + + for (i = 0; i < PLANET_FRAMES; i++) + { + UNLOAD(bg_planet[i]); + } + + UNLOAD(bg_checker); + UNLOAD(bg_levelText); + UNLOAD(bg_derrText); } // From 8432d7e5529201b1aea695a8fe63c253b6ba792b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Apr 2023 01:49:12 -0400 Subject: [PATCH 02/16] Big vote screen cleanup - 4th map is now a regular option instead of dice. - Add function to draw a maintained Combi Catcher object on screen. - Put all vote static variables into either a "vote" struct or a "vote_draw" struct, if it's logic or drawing code. - Prefix netcode vote globals with _g. - Add enums/defines for vote magic numbers. --- src/d_netcmd.c | 130 +++---- src/doomstat.h | 12 +- src/g_game.c | 76 ++-- src/g_game.h | 3 +- src/k_vote.c | 987 +++++++++++++++++++++++++------------------------ src/k_vote.h | 5 + src/p_saveg.c | 18 +- 7 files changed, 620 insertions(+), 611 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index bf9416ce9..2e845fa8f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2615,30 +2615,18 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r void D_SetupVote(void) { - UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes + UINT8 buf[(VOTE_NUM_LEVELS * 2) + 2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes UINT8 *p = buf; INT32 i; - UINT8 secondgt = G_SometimesGetDifferentGametype(); - INT16 votebuffer[4] = {-1,-1,-1,0}; + INT16 votebuffer[VOTE_NUM_LEVELS] = {-1}; - if ((cv_kartencore.value == 1) && (gametyperules & GTR_ENCORE)) - WRITEUINT8(p, (gametype|VOTEMODIFIER_ENCORE)); - else - WRITEUINT8(p, gametype); - WRITEUINT8(p, secondgt); - secondgt &= ~VOTEMODIFIER_ENCORE; + WRITEUINT8(p, ((cv_kartencore.value == 1) && (gametyperules & GTR_ENCORE))); + WRITEUINT8(p, G_SometimesGetDifferentEncore()); - for (i = 0; i < 4; i++) + for (i = 0; i < VOTE_NUM_LEVELS; i++) { - UINT16 m; - if (i == 2) // sometimes a different gametype - m = G_RandMap(G_TOLFlag(secondgt), prevmap, ((secondgt != gametype) ? 2 : 0), 0, true, votebuffer); - else if (i >= 3) // Don't Care. Pick any of the available choices. - m = votebuffer[M_RandomRange(0, 2)]; - else - m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer); - if (i < 3) - votebuffer[i] = m; + UINT16 m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer); + votebuffer[i] = m; WRITEUINT16(p, m); } @@ -2672,13 +2660,13 @@ void D_PickVote(void) { if (!playeringame[i] || players[i].spectator) continue; - if (votes[i] != -1) + if (g_votes[i] != -1) { temppicks[numvotes] = i; - templevels[numvotes] = votes[i]; + templevels[numvotes] = g_votes[i]; numvotes++; if (votecompare == -1) - votecompare = votes[i]; + votecompare = g_votes[i]; } } @@ -5371,75 +5359,55 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) { + boolean baseEncore = false; + boolean optionalEncore = false; + INT16 tempVoteLevels[VOTE_NUM_LEVELS][2]; INT32 i; - UINT8 gt, secondgt; - INT16 tempvotelevels[4][2]; if (playernum != serverplayer) // admin shouldn't be able to set up vote... { CONS_Alert(CONS_WARNING, M_GetText("Illegal vote setup received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); - return; - } - - gt = (UINT8)READUINT8(*cp); - secondgt = (UINT8)READUINT8(*cp); - - // Strip illegal Encore flag. - if ((gt & VOTEMODIFIER_ENCORE) - && !(gametypes[(gt & ~VOTEMODIFIER_ENCORE)]->rules & GTR_ENCORE)) - { - gt &= ~VOTEMODIFIER_ENCORE; - } - - if ((gt & ~VOTEMODIFIER_ENCORE) >= numgametypes) - { - gt &= ~VOTEMODIFIER_ENCORE; - if (server) - I_Error("Got_SetupVotecmd: Internal gametype ID %d not found (numgametypes = %d)", gt, numgametypes); - CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad gametype ID %d received from %s\n"), gt, player_names[playernum]); - return; - } - - if ((secondgt & ~VOTEMODIFIER_ENCORE) >= numgametypes) - { - secondgt &= ~VOTEMODIFIER_ENCORE; - if (server) - I_Error("Got_SetupVotecmd: Internal second gametype ID %d not found (numgametypes = %d)", secondgt, numgametypes); - CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad second gametype ID %d received from %s\n"), secondgt, player_names[playernum]); - return; - } - - for (i = 0; i < 4; i++) - { - tempvotelevels[i][0] = (UINT16)READUINT16(*cp); - tempvotelevels[i][1] = gt; - if (tempvotelevels[i][0] < nummapheaders && mapheaderinfo[tempvotelevels[i][0]]) - continue; - - if (server) - I_Error("Got_SetupVotecmd: Internal map ID %d not found (nummapheaders = %d)", tempvotelevels[i][0], nummapheaders); - CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad map ID %d received from %s\n"), tempvotelevels[i][0], player_names[playernum]); - return; - } - - // If third entry has an illelegal Encore flag... (illelegal!?) - if ((secondgt & VOTEMODIFIER_ENCORE) - && !(gametypes[(secondgt & ~VOTEMODIFIER_ENCORE)]->rules & GTR_ENCORE)) - { - secondgt &= ~VOTEMODIFIER_ENCORE; - // Apply it to the second entry instead, gametype permitting! - if (gametypes[gt]->rules & GTR_ENCORE) { - tempvotelevels[1][1] |= VOTEMODIFIER_ENCORE; + SendKick(playernum, KICK_MSG_CON_FAIL); } + return; } - // Finally, set third entry's gametype/Encore status. - tempvotelevels[2][1] = secondgt; + baseEncore = (boolean)READUINT8(*cp); + optionalEncore = (boolean)READUINT8(*cp); - memcpy(votelevels, tempvotelevels, sizeof(votelevels)); + if (!(gametyperules & GTR_ENCORE)) + { + // Strip illegal Encore flags. + baseEncore = optionalEncore = false; + } + + for (i = 0; i < VOTE_NUM_LEVELS; i++) + { + tempVoteLevels[i][0] = (UINT16)READUINT16(*cp); + tempVoteLevels[i][1] = (baseEncore == true) ? VOTE_MOD_ENCORE : 0; + + if (tempVoteLevels[i][0] < nummapheaders && mapheaderinfo[tempVoteLevels[i][0]]) + { + continue; + } + + if (server) + { + I_Error("Got_SetupVotecmd: Internal map ID %d not found (nummapheaders = %d)", tempVoteLevels[i][0], nummapheaders); + } + + CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad map ID %d received from %s\n"), tempVoteLevels[i][0], player_names[playernum]); + return; + } + + if (optionalEncore == true) + { + tempVoteLevels[VOTE_NUM_LEVELS - 1][1] ^= VOTE_MOD_ENCORE; + } + + memcpy(g_voteLevels, tempVoteLevels, sizeof(g_voteLevels)); G_SetGamestate(GS_VOTING); Y_StartVote(); @@ -5451,7 +5419,7 @@ static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum) UINT8 p = READUINT8(*cp); (void)playernum; - votes[p] = voted; + g_votes[p] = voted; } static void Got_PickVotecmd(UINT8 **cp, INT32 playernum) diff --git a/src/doomstat.h b/src/doomstat.h index 01f584dd4..fd74129dc 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -488,8 +488,8 @@ extern mapheader_t** mapheaderinfo; extern INT32 nummapheaders, mapallocsize; // Gametypes -#define NUMGAMETYPEFREESLOTS (MAXGAMETYPES-GT_FIRSTFREESLOT) -#define MAXGAMETYPELENGTH 32 +#define NUMGAMETYPEFREESLOTS (128) +#define MAXGAMETYPELENGTH (32) enum GameType { @@ -500,7 +500,7 @@ enum GameType GT_TUTORIAL, GT_FIRSTFREESLOT, - GT_LASTFREESLOT = 127, // Previously (GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1) - it would be necessary to rewrite VOTEMODIFIER_ENCORE to go higher than this. + GT_LASTFREESLOT = GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1, MAXGAMETYPES }; // If you alter this list, update defaultgametypes and *gametypes in g_game.c @@ -732,9 +732,9 @@ extern boolean legitimateexit; extern boolean comebackshowninfo; extern tic_t curlap, bestlap; -extern INT16 votelevels[4][2]; -extern SINT8 votes[MAXPLAYERS]; -extern SINT8 pickedvote; +extern INT16 g_voteLevels[4][2]; +extern SINT8 g_votes[MAXPLAYERS]; +extern SINT8 g_pickedVote; // =========================== // Internal parameters, fixed. diff --git a/src/g_game.c b/src/g_game.c index 72d4663f6..02c3cc682 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -298,9 +298,9 @@ boolean prevencoremode; boolean franticitems; // Frantic items currently enabled? // Voting system -INT16 votelevels[4][2]; // Levels that were rolled by the host -SINT8 votes[MAXPLAYERS]; // Each player's vote -SINT8 pickedvote; // What vote the host rolls +INT16 g_voteLevels[4][2]; // Levels that were rolled by the host +SINT8 g_votes[MAXPLAYERS]; // Each player's vote +SINT8 g_pickedVote; // What vote the host rolls // Server-sided, synched variables tic_t wantedcalcdelay; // Time before it recalculates WANTED @@ -3627,32 +3627,28 @@ boolean G_GametypeHasSpectators(void) } // -// G_SometimesGetDifferentGametype +// G_SometimesGetDifferentEncore // // Because gametypes are no longer on the vote screen, all this does is sometimes flip encore mode. // However, it remains a seperate function for long-term possibility. // -INT16 G_SometimesGetDifferentGametype(void) +INT16 G_SometimesGetDifferentEncore(void) { boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE, false) || encorescramble == 1) && (gametyperules & GTR_ENCORE)); UINT8 encoremodifier = 0; - // -- the below is only necessary if you want to use randmaps.mapbuffer here - //if (randmaps.lastnummapheaders != nummapheaders) - //G_ResetRandMapBuffer(); - // FORCE to what was scrambled on intermission? if (encorepossible && encorescramble != -1) { // FORCE to what was scrambled on intermission if ((encorescramble != 0) != (cv_kartencore.value == 1)) { - encoremodifier = VOTEMODIFIER_ENCORE; + encoremodifier = VOTE_MOD_ENCORE; } } - return (gametype|encoremodifier); + return encoremodifier; } /** Get the typeoflevel flag needed to indicate support of a gametype. @@ -3736,10 +3732,11 @@ INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphe UINT32 numokmaps = 0; INT16 ix, bufx; UINT16 extbufsize = 0; - boolean usehellmaps; // Only consider Hell maps in this pick if (randmaps.lastnummapheaders != nummapheaders) + { G_ResetRandMapBuffer(); + } if (!okmaps) { @@ -3750,33 +3747,39 @@ INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphe if (extbuffer != NULL) { bufx = 0; + while (extbuffer[bufx]) { - extbufsize++; bufx++; + extbufsize++; + bufx++; } } tryagain: - usehellmaps = (maphell == 0 ? false : (maphell == 2 || M_RandomChance(FRACUNIT/100))); // 1% chance of Hell - // Find all the maps that are ok and and put them in an array. for (ix = 0; ix < nummapheaders; ix++) { boolean isokmap = true; if (!mapheaderinfo[ix] || mapheaderinfo[ix]->lumpnum == LUMPERROR) + { continue; + } if (!(mapheaderinfo[ix]->typeoflevel & tolflags) || ix == pprevmap || M_MapLocked(ix+1) - || (usehellmaps != (mapheaderinfo[ix]->menuflags & LF2_HIDEINMENU))) // this is bad + || (mapheaderinfo[ix]->menuflags & LF2_HIDEINMENU)) // this is bad + { continue; //isokmap = false; + } if (pprevmap == -2 // title demo hack && mapheaderinfo[ix]->ghostCount == 0) + { continue; + } if (!ignorebuffer) { @@ -3785,7 +3788,10 @@ tryagain: for (bufx = 0; bufx < extbufsize; bufx++) { if (extbuffer[bufx] == -1) // Rest of buffer SHOULD be empty + { break; + } + if (ix == extbuffer[bufx]) { isokmap = false; @@ -3794,13 +3800,18 @@ tryagain: } if (!isokmap) + { continue; + } } for (bufx = 0; bufx < (maphell ? 3 : randmaps.lastnummapheaders); bufx++) { if (randmaps.mapbuffer[bufx] == -1) // Rest of buffer SHOULD be empty + { break; + } + if (ix == randmaps.mapbuffer[bufx]) { isokmap = false; @@ -3819,33 +3830,32 @@ tryagain: { if (!ignorebuffer) { - if (randmaps.mapbuffer[3] == -1) // Is the buffer basically empty? + if (randmaps.mapbuffer[VOTE_NUM_LEVELS] == -1) // Is the buffer basically empty? { ignorebuffer = 1; // This will probably only help in situations where there's very few maps, but it's folly not to at least try it //CONS_Printf("RANDMAP - ignoring buffer\n"); goto tryagain; } - for (bufx = 3; bufx < randmaps.lastnummapheaders; bufx++) // Let's clear all but the three most recent maps... + for (bufx = VOTE_NUM_LEVELS; bufx < randmaps.lastnummapheaders; bufx++) // Let's clear all but the three most recent maps... + { randmaps.mapbuffer[bufx] = -1; - //CONS_Printf("RANDMAP - emptying randmapbuffer\n"); - goto tryagain; - } + } - if (maphell) // Any wiggle room to loosen our restrictions here? - { - //CONS_Printf("RANDMAP -maphell decrement\n"); - maphell--; + //CONS_Printf("RANDMAP - emptying randmapbuffer\n"); goto tryagain; } //CONS_Printf("RANDMAP - defaulting to map01\n"); ix = 0; // Sorry, none match. You get MAP01. + if (ignorebuffer == 1) { //CONS_Printf("(emptying randmapbuffer entirely)\n"); for (bufx = 0; bufx < randmaps.lastnummapheaders; bufx++) + { randmaps.mapbuffer[bufx] = -1; // if we're having trouble finding a map we should probably clear it + } } } else @@ -3867,10 +3877,12 @@ tryagain: void G_AddMapToBuffer(INT16 map) { INT16 bufx; - INT16 refreshnum = (TOLMaps(gametype))-3; + INT16 refreshnum = (TOLMaps(gametype)) - VOTE_NUM_LEVELS; if (refreshnum < 0) - refreshnum = 3; + { + refreshnum = 0; + } if (nummapheaders != randmaps.lastnummapheaders) { @@ -3878,8 +3890,10 @@ void G_AddMapToBuffer(INT16 map) } else { - for (bufx = randmaps.lastnummapheaders-1; bufx > 0; bufx--) + for (bufx = randmaps.lastnummapheaders - 1; bufx > 0; bufx--) + { randmaps.mapbuffer[bufx] = randmaps.mapbuffer[bufx-1]; + } } randmaps.mapbuffer[0] = map; @@ -3887,9 +3901,11 @@ void G_AddMapToBuffer(INT16 map) // We're getting pretty full, so lets flush this for future usage. if (randmaps.mapbuffer[refreshnum] != -1) { - // Clear all but the five most recent maps. - for (bufx = 5; bufx < randmaps.lastnummapheaders; bufx++) + // Clear all but the most recent maps. + for (bufx = VOTE_NUM_LEVELS; bufx < randmaps.lastnummapheaders; bufx++) + { randmaps.mapbuffer[bufx] = -1; + } //CONS_Printf("Random map buffer has been flushed.\n"); } } diff --git a/src/g_game.h b/src/g_game.h index 41a22a6c5..90c19f440 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -182,8 +182,7 @@ INT32 G_GuessGametypeByTOL(UINT32 tol); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); -#define VOTEMODIFIER_ENCORE 0x80 -INT16 G_SometimesGetDifferentGametype(void); +INT16 G_SometimesGetDifferentEncore(void); void G_ExitLevel(void); void G_NextLevel(void); void G_Continue(void); diff --git a/src/k_vote.c b/src/k_vote.c index bf8fdb6ee..58cab978a 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -50,66 +50,147 @@ #include "k_boss.h" #include "k_pwrlv.h" #include "k_grandprix.h" +#include "k_color.h" #ifdef HWRENDER #include "hardware/hw_main.h" #endif -static INT32 timer; - #define UNLOAD(x) if (x) {Patch_Free(x);} x = NULL; #define CLEANUP(x) x = NULL; -// SRB2Kart: voting stuff -// Level images +#define PLANET_FRAMES (9) +#define TEXT_LEVEL_SCROLL (2*FRACUNIT) +#define TEXT_DERR_SCROLL (2*FRACUNIT) + +#define ARM_FRAMES (4) +#define BULB_FRAMES (4) + +// Catcher data typedef struct { - char str[62]; - UINT8 gtc; - const char *gts; - boolean encore; -} y_votelvlinfo; + fixed_t x, y; + UINT8 spr; + boolean small; +} y_vote_catcher; // Clientside & splitscreen player info. typedef struct { + y_vote_catcher catcher; SINT8 selection; UINT8 delay; -} y_voteplayer; +} y_vote_player; typedef struct { - y_voteplayer playerinfo[4]; + INT32 timer; + INT32 tic, endtic; + boolean notYetPicked; + boolean loaded; + + y_vote_player players[MAXSPLITSCREENPLAYERS]; UINT8 ranim; UINT8 rtics; UINT8 roffset; UINT8 rsynctime; UINT8 rendoff; - boolean loaded; -} y_voteclient; -static y_votelvlinfo levelinfo[5]; -static y_voteclient voteclient; -static INT32 votetic; -static INT32 voteendtic = -1; -static boolean votenotyetpicked; -static patch_t *cursor = NULL; -static patch_t *cursor1 = NULL; -static patch_t *cursor2 = NULL; -static patch_t *cursor3 = NULL; -static patch_t *cursor4 = NULL; -static patch_t *randomlvl = NULL; -static patch_t *rubyicon = NULL; + SINT8 deferredLevel; +} y_vote_data; -#define PLANET_FRAMES (9) -#define TEXT_LEVEL_SCROLL (2*FRACUNIT) -#define TEXT_DERR_SCROLL (2*FRACUNIT) -static patch_t *bg_planet[PLANET_FRAMES] = { NULL }; -static patch_t *bg_checker = NULL; -static patch_t *bg_levelText = NULL; -static patch_t *bg_derrText = NULL; +typedef struct +{ + char str[62]; + boolean encore; + fixed_t hop; +} y_vote_draw_level; -static void Y_UnloadVoteData(void); +typedef struct +{ + patch_t *ruby_icon; + patch_t *bg_planet[PLANET_FRAMES]; + patch_t *bg_checker; + patch_t *bg_levelText; + patch_t *bg_derrText; + patch_t *catcher_ufo; + patch_t *catcher_arms[ARM_FRAMES]; + patch_t *catcher_pole; + patch_t *catcher_bulb[BULB_FRAMES]; + y_vote_draw_level levels[VOTE_NUM_LEVELS]; +} y_vote_draw; + +static y_vote_data vote = {0}; +static y_vote_draw vote_draw = {0}; + +static boolean Y_PlayerIDCanVote(const UINT8 id) +{ + if (id >= MAXPLAYERS) + { + return false; + } + + if (playeringame[id] == false || players[id].spectator == true) + { + return false; + } + + if (players[id].bot == true) + { + return false; + } + + return true; +} + +static void Y_DrawCatcher(y_vote_catcher *catcher) +{ +#define NUM_BULB_COLORS (2) + static const skincolornum_t bulbColors[NUM_BULB_COLORS] = { + SKINCOLOR_JAWZ, + SKINCOLOR_LILAC, + }; + + const tic_t anim = gametic / 3; // Using gametic for this is probably a bit goofy + + fixed_t x = catcher->x - (vote_draw.catcher_ufo->width * FRACUNIT / 2); + fixed_t y = catcher->y - (vote_draw.catcher_ufo->height * FRACUNIT * 7 / 8); + + UINT8 *craneColor = NULL; + UINT8 *bulbColor = NULL; + + craneColor = R_GetTranslationColormap(TC_DEFAULT, K_RainbowColor(anim), GTC_MENUCACHE); + bulbColor = R_GetTranslationColormap(TC_DEFAULT, bulbColors[(anim / BULB_FRAMES) % NUM_BULB_COLORS], GTC_MENUCACHE); + + V_DrawFixedPatch( + x, y, + FRACUNIT, 0, + vote_draw.catcher_arms[catcher->spr % ARM_FRAMES], + craneColor + ); + + V_DrawFixedPatch( + x, y, + FRACUNIT, 0, + vote_draw.catcher_bulb[anim % BULB_FRAMES], + bulbColor + ); + + V_DrawFixedPatch( + x, y, + FRACUNIT, 0, + vote_draw.catcher_ufo, + craneColor + ); + + V_DrawFixedPatch( + x, y, + FRACUNIT, 0, + vote_draw.catcher_pole, + NULL + ); +#undef NUM_BULB_COLORS +} // // Y_VoteDrawer @@ -121,27 +202,27 @@ static void Y_DrawVoteBackground(void) static fixed_t bgTimer = 0; static fixed_t derrPos = 0; - const fixed_t derrLoop = bg_derrText->width * FRACUNIT; + const fixed_t derrLoop = vote_draw.bg_derrText->width * FRACUNIT; static fixed_t levelPos = 0; - const fixed_t levelLoop = bg_levelText->height * FRACUNIT; + const fixed_t levelLoop = vote_draw.bg_levelText->height * FRACUNIT; const UINT8 planetFrame = (bgTimer / FRACUNIT) % PLANET_FRAMES; V_DrawFixedPatch( 0, 0, FRACUNIT, 0, - bg_planet[planetFrame], NULL + vote_draw.bg_planet[planetFrame], NULL ); V_DrawFixedPatch( - (BASEVIDWIDTH - bg_checker->width) * FRACUNIT, 0, + (BASEVIDWIDTH - vote_draw.bg_checker->width) * FRACUNIT, 0, FRACUNIT, V_ADD, - bg_checker, NULL + vote_draw.bg_checker, NULL ); V_DrawFixedPatch( - (BASEVIDWIDTH - bg_checker->width) * FRACUNIT, 0, + (BASEVIDWIDTH - vote_draw.bg_checker->width) * FRACUNIT, 0, FRACUNIT, V_ADD, - bg_checker, NULL + vote_draw.bg_checker, NULL ); levelPos += FixedMul(TEXT_DERR_SCROLL, renderdeltatics); @@ -151,16 +232,16 @@ static void Y_DrawVoteBackground(void) } V_DrawFixedPatch( - ((BASEVIDWIDTH - bg_levelText->width) * FRACUNIT) - levelPos, + ((BASEVIDWIDTH - vote_draw.bg_levelText->width) * FRACUNIT) - levelPos, -levelPos, FRACUNIT, V_ADD, - bg_levelText, NULL + vote_draw.bg_levelText, NULL ); V_DrawFixedPatch( - ((BASEVIDWIDTH - bg_levelText->width) * FRACUNIT) - levelPos + levelLoop, + ((BASEVIDWIDTH - vote_draw.bg_levelText->width) * FRACUNIT) - levelPos + levelLoop, -levelPos + levelLoop, FRACUNIT, V_ADD, - bg_levelText, NULL + vote_draw.bg_levelText, NULL ); derrPos += FixedMul(TEXT_DERR_SCROLL, renderdeltatics); @@ -171,15 +252,15 @@ static void Y_DrawVoteBackground(void) V_DrawFixedPatch( -derrPos, - (BASEVIDHEIGHT - bg_derrText->height) * FRACUNIT, + (BASEVIDHEIGHT - vote_draw.bg_derrText->height) * FRACUNIT, FRACUNIT, V_SUBTRACT, - bg_derrText, NULL + vote_draw.bg_derrText, NULL ); V_DrawFixedPatch( -derrPos + derrLoop, - (BASEVIDHEIGHT - bg_derrText->height) * FRACUNIT, + (BASEVIDHEIGHT - vote_draw.bg_derrText->height) * FRACUNIT, FRACUNIT, V_SUBTRACT, - bg_derrText, NULL + vote_draw.bg_derrText, NULL ); bgTimer += renderdeltatics; @@ -187,200 +268,116 @@ static void Y_DrawVoteBackground(void) void Y_VoteDrawer(void) { - INT32 i, x, y = 0, height = 0; - UINT8 selected[4]; - fixed_t rubyheight = 0; + fixed_t x, y; + fixed_t rubyHeight = 0; + INT32 i, j; // If we early return, skip drawing the 3D scene (software buffer) so it doesn't clobber the frame for the wipe g_wipeskiprender = true; if (rendermode == render_none) + { return; + } - if (votetic >= voteendtic && voteendtic != -1) + if (vote.tic >= vote.endtic && vote.endtic != -1) + { return; + } - if (!voteclient.loaded) + if (vote.loaded == false) + { return; + } g_wipeskiprender = false; { - static angle_t rubyfloattime = 0; - rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); - rubyfloattime += FixedMul(ANGLE_MAX/NEWTICRATE, renderdeltatics); + static angle_t rubyFloatTime = 0; + rubyHeight = FINESINE(rubyFloatTime >> ANGLETOFINESHIFT); + rubyFloatTime += FixedMul(ANGLE_MAX / NEWTICRATE, renderdeltatics); } Y_DrawVoteBackground(); - for (i = 0; i < 4; i++) // First, we need to figure out the height of this thing... - { - UINT8 j; - selected[i] = 0; // Initialize + x = 10 * FRACUNIT; + y = 144 * FRACUNIT; - for (j = 0; j <= splitscreen; j++) + for (i = 0; i < VOTE_NUM_LEVELS; i++) + { + boolean selected = false; + INT32 flags = 0; + fixed_t destHop = 0; + + for (j = 0; j <= splitscreen; j++) // another loop for drawing the selection backgrounds in the right order, grumble grumble.. { - if (voteclient.playerinfo[j].selection == i) - selected[i]++; + const UINT8 p = g_localplayers[j]; + + if (vote.players[j].selection != i) + { + continue; + } + + if (g_votes[p] != VOTE_NOT_PICKED || Y_PlayerIDCanVote(p) == false) + { + continue; + } + + selected = true; + break; } - if (selected[i]) - height += 50; - else - height += 25; - - if (i < 3) - height += 5-splitscreen; - } - - y = (200-height)/2; - for (i = 0; i < 4; i++) - { - UINT8 j, color; - - if (selected[i]) + if (selected == true) { - const char *str; - UINT8 sizeadd = selected[i]; - - for (j = 0; j <= splitscreen; j++) // another loop for drawing the selection backgrounds in the right order, grumble grumble.. - { - INT32 handy = y; - UINT8 p; - UINT8 *colormap; - patch_t *thiscurs; - - if (voteclient.playerinfo[j].selection != i) - continue; - - if (!splitscreen) - { - thiscurs = cursor; - p = consoleplayer; - color = levelinfo[i].gtc; - colormap = NULL; - } - else - { - switch (j) - { - case 1: - thiscurs = cursor2; - p = g_localplayers[1]; - break; - case 2: - thiscurs = cursor3; - p = g_localplayers[2]; - break; - case 3: - thiscurs = cursor4; - p = g_localplayers[3]; - break; - default: - thiscurs = cursor1; - p = g_localplayers[0]; - break; - } - - color = skincolors[players[p].skincolor].ramp[7]; - colormap = R_GetTranslationColormap(TC_DEFAULT, players[p].skincolor, GTC_CACHE); - } - - if (votes[p] != -1 || players[p].spectator) - continue; - - handy += 6*(3-splitscreen) + (13*j); - V_DrawMappedPatch(BASEVIDWIDTH-124, handy, V_SNAPTORIGHT, thiscurs, colormap); - - if (votetic % 10 < 4) - V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), 0|V_SNAPTORIGHT); - else - V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), color|V_SNAPTORIGHT); - - sizeadd--; - } - - if (i == 3) - { - str = "RANDOM"; - K_DrawLikeMapThumbnail( - (BASEVIDWIDTH-100)<= 3 && (i != pickedvote || voteendtic == -1)) - { - K_DrawLikeMapThumbnail( - (x)< 0) { - INT32 tickdown = (timer+1)/TICRATE; - V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP, - va("Vote ends in %d", tickdown)); + const INT32 tickDown = (vote.timer + 1) / TICRATE; + + V_DrawCenteredString( + BASEVIDWIDTH/2, 188, + V_YELLOWMAP, + va("Vote ends in %d", tickDown) + ); } M_DrawMenuForeground(); @@ -468,29 +458,20 @@ void Y_VoteDrawer(void) // // Vote screen's selection stops moving // -SINT8 deferredlevel = 0; static void Y_VoteStops(SINT8 pick, SINT8 level) { - nextmap = votelevels[level][0]; + nextmap = g_voteLevels[level][0]; - //if (level == 4) - // S_StartSound(NULL, sfx_noooo2); // gasp - if (mapheaderinfo[nextmap] && (mapheaderinfo[nextmap]->menuflags & LF2_HIDEINMENU)) - S_StartSound(NULL, sfx_noooo1); // this is bad - else if (netgame && P_IsLocalPlayer(&players[pick])) - S_StartSound(NULL, sfx_yeeeah); // yeeeah! - else - S_StartSound(NULL, sfx_kc48); // just a cool sound - - if (gametype != votelevels[level][1]) + if (netgame && P_IsLocalPlayer(&players[pick])) { - INT16 lastgametype = gametype; - G_SetGametype(votelevels[level][1]); - D_GameTypeChanged(lastgametype); - forceresetplayers = true; + S_StartSound(NULL, sfx_yeeeah); // yeeeah! + } + else + { + S_StartSound(NULL, sfx_kc48); // just a cool sound } - deferencoremode = (levelinfo[level].encore); + deferencoremode = (g_voteLevels[level][1] & VOTE_MOD_ENCORE); } // @@ -503,14 +484,16 @@ void Y_VoteTicker(void) INT32 i; boolean everyone_voted; - if (paused || P_AutoPause() || !voteclient.loaded) + if (paused || P_AutoPause() || vote.loaded == false) + { return; + } LUA_HOOK(VoteThinker); - votetic++; + vote.tic++; - if (votetic == voteendtic) + if (vote.tic == vote.endtic) { Y_EndVote(); G_AfterIntermission(); @@ -519,38 +502,49 @@ void Y_VoteTicker(void) for (i = 0; i < MAXPLAYERS; i++) // Correct votes as early as possible, before they're processed by the game at all { - if (!playeringame[i] || players[i].spectator) - votes[i] = -1; // Spectators are the lower class, and have effectively no voice in the government. Democracy sucks. - else if (pickedvote != -1 && votes[i] == -1) - votes[i] = 3; // Slow people get random + if (playeringame[i] == false || players[i].spectator == true) + { + g_votes[i] = VOTE_NOT_PICKED; // Spectators are the lower class, and have effectively no voice in the government. Democracy sucks. + } + else if (g_pickedVote != VOTE_NOT_PICKED && g_votes[i] == VOTE_NOT_PICKED) + { + g_votes[i] = 3; // Slow people get random values -- TODO: random vote doesn't exist anymore + } } - if (server && pickedvote != -1 && votes[pickedvote] == -1) // Uh oh! The person who got picked left! Recalculate, quick! + if (server && g_pickedVote != VOTE_NOT_PICKED && g_votes[g_pickedVote] == VOTE_NOT_PICKED) // Uh oh! The person who got picked left! Recalculate, quick! + { D_PickVote(); + } - if (!votetic) + if (vote.tic == 0) { S_ChangeMusicInternal("vote", true); S_ShowMusicCredit(); } - if (timer) - timer--; - - if (pickedvote != -1) + if (vote.timer) { - timer = 0; - voteclient.rsynctime++; + vote.timer--; + } - if (voteendtic == -1) + if (g_pickedVote != VOTE_NOT_PICKED) + { + vote.timer = 0; + vote.rsynctime++; + + if (vote.endtic == -1) { UINT8 tempvotes[MAXPLAYERS]; UINT8 numvotes = 0; for (i = 0; i < MAXPLAYERS; i++) { - if (votes[i] == -1) + if (g_votes[i] == VOTE_NOT_PICKED) + { continue; + } + tempvotes[numvotes] = i; numvotes++; } @@ -562,114 +556,116 @@ void Y_VoteTicker(void) return; } - voteclient.rtics--; + vote.rtics--; - if (voteclient.rtics <= 0) + if (vote.rtics <= 0) { - voteclient.roffset++; - voteclient.rtics = min(20, (3*voteclient.roffset/4)+5); + vote.roffset++; + vote.rtics = min(20, (3*vote.roffset/4)+5); S_StartSound(NULL, sfx_kc39); } - if (voteclient.rendoff == 0 || voteclient.roffset < voteclient.rendoff) - voteclient.ranim = tempvotes[((pickedvote + voteclient.roffset) % numvotes)]; - - if (voteclient.roffset >= 20) + if (vote.rendoff == 0 || vote.roffset < vote.rendoff) { - if (voteclient.rendoff == 0) + vote.ranim = tempvotes[((g_pickedVote + vote.roffset) % numvotes)]; + } + + if (vote.roffset >= 20) + { + if (vote.rendoff == 0) { - if (voteclient.rsynctime % 51 == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V) + if (vote.rsynctime % 51 == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V) { for (i = 5; i >= 3; i--) // Find a suitable place to stop { - if (tempvotes[((pickedvote + voteclient.roffset + i) % numvotes)] == pickedvote) + if (tempvotes[((g_pickedVote + vote.roffset + i) % numvotes)] == g_pickedVote) { - voteclient.rendoff = voteclient.roffset+i; + vote.rendoff = vote.roffset + i; + if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~ - voteclient.rendoff++; + { + vote.rendoff++; + } + S_ChangeMusicInternal("voteeb", false); break; } } } } - else if (voteclient.roffset >= voteclient.rendoff) + else if (vote.roffset >= vote.rendoff) { - voteendtic = votetic + (3*TICRATE); - Y_VoteStops(pickedvote, deferredlevel); + vote.endtic = vote.tic + (3*TICRATE); + Y_VoteStops(g_pickedVote, vote.deferredLevel); } } } else - voteclient.ranim = pickedvote; + { + vote.ranim = g_pickedVote; + } } - else if (votenotyetpicked) + else if (vote.notYetPicked) { - if (votetic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V + if (vote.tic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V + { return; + } /* The vote ended, but it will take at least a tic for that to reach us from the server. Don't let me change the vote now, it won't matter anyway! */ - if (timer) + if (vote.timer) { for (i = 0; i <= splitscreen; i++) { - UINT8 p; - boolean pressed = false; + const UINT8 p = g_localplayers[i]; + boolean moved = false; - switch (i) + if (vote.players[i].delay) { - case 1: - p = g_localplayers[1]; - break; - case 2: - p = g_localplayers[2]; - break; - case 3: - p = g_localplayers[3]; - break; - default: - p = consoleplayer; - break; + vote.players[i].delay--; } - if (voteclient.playerinfo[i].delay) - voteclient.playerinfo[i].delay--; - - if ((playeringame[p] && !players[p].spectator) - && !voteclient.playerinfo[i].delay - && pickedvote == -1 && votes[p] == -1 && menuactive == false) + if (Y_PlayerIDCanVote(p) == true + && vote.players[i].delay == 0 + && g_pickedVote == VOTE_NOT_PICKED && g_votes[p] == VOTE_NOT_PICKED + && menuactive == false) { - if (G_PlayerInputDown(i, gc_up, 0)) + if (G_PlayerInputDown(i, gc_left, 0)) { - voteclient.playerinfo[i].selection--; - pressed = true; + vote.players[i].selection--; + moved = true; } - if (G_PlayerInputDown(i, gc_down, 0) && pressed == false) + if (G_PlayerInputDown(i, gc_right, 0)) { - voteclient.playerinfo[i].selection++; - pressed = true; + vote.players[i].selection++; + moved = true; } - if (voteclient.playerinfo[i].selection < 0) - voteclient.playerinfo[i].selection = 3; - if (voteclient.playerinfo[i].selection > 3) - voteclient.playerinfo[i].selection = 0; - - if (G_PlayerInputDown(i, gc_a, 0) && pressed == false) + if (vote.players[i].selection < 0) { - D_ModifyClientVote(consoleplayer, voteclient.playerinfo[i].selection, i); - pressed = true; + vote.players[i].selection = VOTE_NUM_LEVELS - 1; + } + + if (vote.players[i].selection >= VOTE_NUM_LEVELS) + { + vote.players[i].selection = 0; + } + + if (G_PlayerInputDown(i, gc_a, 0) && moved == false) + { + D_ModifyClientVote(consoleplayer, vote.players[i].selection, i); + moved = true; } } - if (pressed) + if (moved) { S_StartSound(NULL, sfx_kc4a); - voteclient.playerinfo[i].delay = NEWTICRATE/7; + vote.players[i].delay = NEWTICRATE/7; } } } @@ -678,38 +674,33 @@ void Y_VoteTicker(void) { everyone_voted = true;/* the default condition */ - if (timer == 0) + for (i = 0; i < MAXPLAYERS; i++) { - for (i = 0; i < MAXPLAYERS; i++) + if (Y_PlayerIDCanVote(i) == false) { - if ((playeringame[i] && !players[i].spectator) && votes[i] == -1) - votes[i] = 3; + continue; } - } - else - { - for (i = 0; i < MAXPLAYERS; i++) - { - if ((playeringame[i] && !players[i].spectator) && votes[i] == -1) - { - if (players[i].bot) - { - if (( M_RandomFixed() % 100 ) == 0) - D_ModifyClientVote(i, M_RandomKey(4), 0); - } - if (votes[i] == -1) - everyone_voted = false; + if (g_votes[i] == VOTE_NOT_PICKED) + { + if (vote.timer == 0) + { + g_votes[i] = 3; // RANDOMIZE LATER + } + else + { + everyone_voted = false; } } } - if (everyone_voted) + if (everyone_voted == true) { - timer = 0; - if (voteendtic == -1) + vote.timer = 0; + + if (vote.endtic == -1) { - votenotyetpicked = false;/* don't pick vote twice */ + vote.notYetPicked = false; /* don't pick vote twice */ D_PickVote(); } } @@ -725,107 +716,93 @@ void Y_VoteTicker(void) void Y_StartVote(void) { INT32 i = 0; - //boolean battlemode = ((votelevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); // todo gametyperules - votetic = -1; + vote.tic = vote.endtic = -1; -#ifdef PARANOIA - if (voteendtic != -1) - I_Error("voteendtic is dirty"); -#endif - - cursor = W_CachePatchName("M_CURSOR", PU_STATIC); - cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC); - cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC); - cursor3 = W_CachePatchName("P3CURSOR", PU_STATIC); - cursor4 = W_CachePatchName("P4CURSOR", PU_STATIC); - randomlvl = W_CachePatchName("RANDOMLV", PU_STATIC); - rubyicon = W_CachePatchName("RUBYICON", PU_STATIC); + vote_draw.ruby_icon = W_CachePatchName("RUBYICON", PU_STATIC); for (i = 0; i < PLANET_FRAMES; i++) { - bg_planet[i] = W_CachePatchName(va("VT_BG_%d", i + 1), PU_STATIC); + vote_draw.bg_planet[i] = W_CachePatchName(va("VT_BG_%d", i + 1), PU_STATIC); } - bg_checker = W_CachePatchName("VT_RACE", PU_STATIC); - bg_levelText = W_CachePatchName("VT_WELC", PU_STATIC); - bg_derrText = W_CachePatchName("VT_DERR", PU_STATIC); + vote_draw.bg_checker = W_CachePatchName("VT_RACE", PU_STATIC); + vote_draw.bg_levelText = W_CachePatchName("VT_WELC", PU_STATIC); + vote_draw.bg_derrText = W_CachePatchName("VT_DERR", PU_STATIC); - timer = cv_votetime.value*TICRATE; - pickedvote = -1; - - votenotyetpicked = true; - - for (i = 0; i < 3; i++) + vote_draw.catcher_ufo = W_CachePatchName("VT_UFO1", PU_STATIC); + for (i = 0; i < ARM_FRAMES; i++) { - voteclient.playerinfo[i].selection = 0; - voteclient.playerinfo[i].delay = 0; + vote_draw.catcher_arms[i] = W_CachePatchName(va("VT_ARMS%d", i + 1), PU_STATIC); + } + vote_draw.catcher_pole = W_CachePatchName("VT_POLE", PU_STATIC); + for (i = 0; i < BULB_FRAMES; i++) + { + vote_draw.catcher_bulb[i] = W_CachePatchName(va("VT_BULB%d", i + 1), PU_STATIC); } - voteclient.ranim = 0; - voteclient.rtics = 1; - voteclient.roffset = 0; - voteclient.rsynctime = 0; - voteclient.rendoff = 0; + vote.timer = cv_votetime.value * TICRATE; + + g_pickedVote = VOTE_NOT_PICKED; + vote.notYetPicked = true; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + vote.players[i].selection = 0; + vote.players[i].delay = 0; + vote.players[i].catcher.x = 10*FRACUNIT + (72*FRACUNIT/2); + vote.players[i].catcher.y = 144*FRACUNIT + (48*FRACUNIT/2); + } + + vote.ranim = 0; + vote.rtics = 1; + vote.roffset = 0; + vote.rsynctime = 0; + vote.rendoff = 0; for (i = 0; i < MAXPLAYERS; i++) - votes[i] = -1; - - for (i = 0; i < 4; i++) { - // set up the encore - levelinfo[i].encore = (votelevels[i][1] & VOTEMODIFIER_ENCORE); - votelevels[i][1] &= ~VOTEMODIFIER_ENCORE; - - // set up the levelstring - if (mapheaderinfo[votelevels[i][0]]->levelflags & LF_NOZONE || !mapheaderinfo[votelevels[i][0]]->zonttl[0]) - { - if (mapheaderinfo[votelevels[i][0]]->actnum > 0) - snprintf(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s %d", - mapheaderinfo[votelevels[i][0]]->lvlttl, mapheaderinfo[votelevels[i][0]]->actnum); - else - snprintf(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s", - mapheaderinfo[votelevels[i][0]]->lvlttl); - } - else - { - if (mapheaderinfo[votelevels[i][0]]->actnum > 0) - snprintf(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s %s %d", - mapheaderinfo[votelevels[i][0]]->lvlttl, mapheaderinfo[votelevels[i][0]]->zonttl, mapheaderinfo[votelevels[i][0]]->actnum); - else - snprintf(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s %s", - mapheaderinfo[votelevels[i][0]]->lvlttl, mapheaderinfo[votelevels[i][0]]->zonttl); - } - - levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0'; - - // set up the gtc and gts - levelinfo[i].gtc = 73; // yellowmap[0] -- TODO rewrite vote screen - if (i == 2 && votelevels[i][1] != votelevels[0][1]) - levelinfo[i].gts = gametypes[votelevels[i][1]]->name; - else - levelinfo[i].gts = NULL; + g_votes[i] = VOTE_NOT_PICKED; } - voteclient.loaded = true; - Automate_Run(AEV_VOTESTART); -} + for (i = 0; i < VOTE_NUM_LEVELS; i++) + { + // set up the encore + vote_draw.levels[i].encore = (g_voteLevels[i][1] & VOTE_MOD_ENCORE); -// -// Y_EndVote -// -void Y_EndVote(void) -{ - Y_UnloadVoteData(); - voteendtic = -1; + // set up the levelstring + if (mapheaderinfo[g_voteLevels[i][0]]->levelflags & LF_NOZONE || !mapheaderinfo[g_voteLevels[i][0]]->zonttl[0]) + { + if (mapheaderinfo[g_voteLevels[i][0]]->actnum > 0) + snprintf(vote_draw.levels[i].str, + sizeof vote_draw.levels[i].str, + "%s %d", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->actnum); + else + snprintf(vote_draw.levels[i].str, + sizeof vote_draw.levels[i].str, + "%s", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl); + } + else + { + if (mapheaderinfo[g_voteLevels[i][0]]->actnum > 0) + snprintf(vote_draw.levels[i].str, + sizeof vote_draw.levels[i].str, + "%s %s %d", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->zonttl, mapheaderinfo[g_voteLevels[i][0]]->actnum); + else + snprintf(vote_draw.levels[i].str, + sizeof vote_draw.levels[i].str, + "%s %s", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->zonttl); + } + + vote_draw.levels[i].str[sizeof vote_draw.levels[i].str - 1] = '\0'; + } + + vote.loaded = true; + Automate_Run(AEV_VOTESTART); } // @@ -835,89 +812,131 @@ static void Y_UnloadVoteData(void) { INT32 i; - voteclient.loaded = false; + vote.loaded = false; if (rendermode != render_soft) + { return; + } - UNLOAD(cursor); - UNLOAD(cursor1); - UNLOAD(cursor2); - UNLOAD(cursor3); - UNLOAD(cursor4); - UNLOAD(randomlvl); - UNLOAD(rubyicon); + UNLOAD(vote_draw.ruby_icon); for (i = 0; i < PLANET_FRAMES; i++) { - UNLOAD(bg_planet[i]); + UNLOAD(vote_draw.bg_planet[i]); } + UNLOAD(vote_draw.bg_checker); + UNLOAD(vote_draw.bg_levelText); + UNLOAD(vote_draw.bg_derrText); - UNLOAD(bg_checker); - UNLOAD(bg_levelText); - UNLOAD(bg_derrText); + UNLOAD(vote_draw.catcher_ufo); + for (i = 0; i < ARM_FRAMES; i++) + { + UNLOAD(vote_draw.catcher_arms[i]); + } + UNLOAD(vote_draw.catcher_pole); + for (i = 0; i < BULB_FRAMES; i++) + { + UNLOAD(vote_draw.catcher_bulb[i]); + } +} + +// +// Y_EndVote +// +void Y_EndVote(void) +{ + Y_UnloadVoteData(); + vote.endtic = -1; } // // Y_SetupVoteFinish // + +enum +{ + VOTE_END_IMMEDIATE = 0, + VOTE_END_QUICK, + VOTE_END_NORMAL, +}; + void Y_SetupVoteFinish(SINT8 pick, SINT8 level) { - if (!voteclient.loaded) + if (vote.loaded == false) + { return; + } - if (pick == -1) // No other votes? We gotta get out of here, then! + if (pick == VOTE_NOT_PICKED) // No other votes? We gotta get out of here, then! { Y_EndVote(); G_AfterIntermission(); return; } - if (pickedvote == -1) + if (g_pickedVote == VOTE_NOT_PICKED) { INT32 i; - SINT8 votecompare = -1; - INT32 endtype = 0; + SINT8 votecompare = VOTE_NOT_PICKED; + INT32 endtype = VOTE_END_IMMEDIATE; - voteclient.rsynctime = 0; + vote.rsynctime = 0; for (i = 0; i < MAXPLAYERS; i++) { - if ((playeringame[i] && !players[i].spectator) && votes[i] == -1) - votes[i] = 3; - - if (votes[i] == -1 || endtype > 1) // Don't need to go on - continue; - - if (endtype == 2) - continue; - - if (votecompare == -1) + if (Y_PlayerIDCanVote(i) == true && g_votes[i] == VOTE_NOT_PICKED) { - votecompare = votes[i]; - endtype = 1; + g_votes[i] = 3; // RANDOMIZE + } + + if (g_votes[i] == VOTE_NOT_PICKED || endtype > VOTE_END_QUICK) // Don't need to go on + { + continue; + } + + if (endtype == VOTE_END_NORMAL) + { + continue; + } + + if (votecompare == VOTE_NOT_PICKED) + { + votecompare = g_votes[i]; + endtype = VOTE_END_QUICK; + } + else if (g_votes[i] != votecompare) + { + endtype = VOTE_END_NORMAL; } - else if (votes[i] != votecompare) - endtype = 2; } - if (endtype == 1) // Only one unique vote, so just end it immediately. + switch (endtype) { - voteendtic = votetic + (5*TICRATE); - S_ChangeMusicInternal("voteeb", false); - Y_VoteStops(pick, level); + case VOTE_END_IMMEDIATE: + { + // Might as well put it here, too, just in case. + Y_EndVote(); + G_AfterIntermission(); + return; + } + case VOTE_END_QUICK: + { + // Only one unique vote, so just end it immediately. + vote.endtic = vote.tic + (5*TICRATE); + S_ChangeMusicInternal("voteeb", false); + Y_VoteStops(pick, level); + break; + } + default: + { + S_ChangeMusicInternal("voteea", true); + break; + } } - else if (endtype == 0) // Might as well put this here, too. - { - Y_EndVote(); - G_AfterIntermission(); - return; - } - else - S_ChangeMusicInternal("voteea", true); } - deferredlevel = level; - pickedvote = pick; - timer = 0; + vote.deferredLevel = level; + g_pickedVote = pick; + vote.timer = 0; } diff --git a/src/k_vote.h b/src/k_vote.h index 1e82b40b5..3f3d12b44 100644 --- a/src/k_vote.h +++ b/src/k_vote.h @@ -19,6 +19,11 @@ extern "C" { #endif +#define VOTE_NUM_LEVELS (4) +#define VOTE_NOT_PICKED (-1) + +#define VOTE_MOD_ENCORE (0x01) + void Y_VoteDrawer(void); void Y_VoteTicker(void); void Y_StartVote(void); diff --git a/src/p_saveg.c b/src/p_saveg.c index be5b65132..05c55e0ea 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4999,14 +4999,14 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) for (i = 0; i < 4; i++) { - WRITEINT16(save->p, votelevels[i][0]); - WRITEINT16(save->p, votelevels[i][1]); + WRITEINT16(save->p, g_voteLevels[i][0]); + WRITEINT16(save->p, g_voteLevels[i][1]); } for (i = 0; i < MAXPLAYERS; i++) - WRITESINT8(save->p, votes[i]); + WRITESINT8(save->p, g_votes[i]); - WRITESINT8(save->p, pickedvote); + WRITESINT8(save->p, g_pickedVote); WRITEUINT16(save->p, emeralds); { @@ -5171,14 +5171,16 @@ static inline boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading) for (i = 0; i < 4; i++) { - votelevels[i][0] = READINT16(save->p); - votelevels[i][1] = READINT16(save->p); + g_voteLevels[i][0] = READINT16(save->p); + g_voteLevels[i][1] = READINT16(save->p); } for (i = 0; i < MAXPLAYERS; i++) - votes[i] = READSINT8(save->p); + { + g_votes[i] = READSINT8(save->p); + } - pickedvote = READSINT8(save->p); + g_pickedVote = READSINT8(save->p); emeralds = READUINT16(save->p); { From 7641c6a276351130f9d7aeac44f35dc265cefab0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Apr 2023 02:07:30 -0400 Subject: [PATCH 03/16] ModifyClientVote sends a netxcmd for the player It was implemented before SendNetXCmdForPlayer, so it used to just send it from the consoleplayer and just attach the player number separately. Now the packet will actually be guaranteed to come from the player that wants to change their vote. (Probably was fuckin possible to just change everyone else's votes with a hacked client before, right) --- src/d_netcmd.c | 35 +++++++++++++++++++---------------- src/d_netcmd.h | 2 +- src/k_vote.c | 8 ++++---- src/k_vote.h | 1 + 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2e845fa8f..9337e98c6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2633,17 +2633,18 @@ void D_SetupVote(void) SendNetXCmd(XD_SETUPVOTE, buf, p - buf); } -void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer) +void D_ModifyClientVote(UINT8 player, SINT8 voted) { - char buf[2]; + char buf[1]; char *p = buf; - if (splitplayer > 0) - player = g_localplayers[splitplayer]; + if (player >= MAXSPLITSCREENPLAYERS) + { + return; + } WRITESINT8(p, voted); - WRITEUINT8(p, player); - SendNetXCmd(XD_MODIFYVOTE, &buf, 2); + SendNetXCmdForPlayer(player, XD_MODIFYVOTE, &buf, 2); } void D_PickVote(void) @@ -2652,21 +2653,27 @@ void D_PickVote(void) char* p = buf; SINT8 temppicks[MAXPLAYERS]; SINT8 templevels[MAXPLAYERS]; - SINT8 votecompare = -1; + SINT8 votecompare = VOTE_NOT_PICKED; UINT8 numvotes = 0, key = 0; INT32 i; for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator) + if (Y_PlayerIDCanVote(i) == false) + { continue; - if (g_votes[i] != -1) + } + + if (g_votes[i] != VOTE_NOT_PICKED) { temppicks[numvotes] = i; templevels[numvotes] = g_votes[i]; numvotes++; - if (votecompare == -1) + + if (votecompare == VOTE_NOT_PICKED) + { votecompare = g_votes[i]; + } } } @@ -2679,7 +2686,7 @@ void D_PickVote(void) } else { - WRITESINT8(p, -1); + WRITESINT8(p, VOTE_NOT_PICKED); WRITESINT8(p, 0); } @@ -5415,11 +5422,7 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum) { - SINT8 voted = READSINT8(*cp); - UINT8 p = READUINT8(*cp); - - (void)playernum; - g_votes[p] = voted; + g_votes[playernum] = READSINT8(*cp); } static void Got_PickVotecmd(UINT8 **cp, INT32 playernum) diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c7e75b83c..d4c7d82a4 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -240,7 +240,7 @@ void Command_Retry_f(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect); void D_SetupVote(void); -void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer); +void D_ModifyClientVote(UINT8 player, SINT8 voted); void D_PickVote(void); void ObjectPlace_OnChange(void); boolean IsPlayerAdmin(INT32 playernum); diff --git a/src/k_vote.c b/src/k_vote.c index 58cab978a..542bd5566 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -123,7 +123,7 @@ typedef struct static y_vote_data vote = {0}; static y_vote_draw vote_draw = {0}; -static boolean Y_PlayerIDCanVote(const UINT8 id) +boolean Y_PlayerIDCanVote(const UINT8 id) { if (id >= MAXPLAYERS) { @@ -386,7 +386,7 @@ void Y_VoteDrawer(void) if (dedicated && i == 0) // While leaving blank spots for non-existent players is largely intentional, the first spot *always* being blank looks a tad silly :V continue; - if ((playeringame[i] && !players[i].spectator) && g_votes[i] != VOTE_NOT_PICKED) + if (Y_PlayerIDCanVote(i) == true && g_votes[i] != VOTE_NOT_PICKED) { if (!timer && i == voteclient.ranim) { @@ -502,7 +502,7 @@ void Y_VoteTicker(void) for (i = 0; i < MAXPLAYERS; i++) // Correct votes as early as possible, before they're processed by the game at all { - if (playeringame[i] == false || players[i].spectator == true) + if (Y_PlayerIDCanVote(i) == false) { g_votes[i] = VOTE_NOT_PICKED; // Spectators are the lower class, and have effectively no voice in the government. Democracy sucks. } @@ -657,7 +657,7 @@ void Y_VoteTicker(void) if (G_PlayerInputDown(i, gc_a, 0) && moved == false) { - D_ModifyClientVote(consoleplayer, vote.players[i].selection, i); + D_ModifyClientVote(i, vote.players[i].selection); moved = true; } } diff --git a/src/k_vote.h b/src/k_vote.h index 3f3d12b44..6705c994a 100644 --- a/src/k_vote.h +++ b/src/k_vote.h @@ -24,6 +24,7 @@ extern "C" { #define VOTE_MOD_ENCORE (0x01) +boolean Y_PlayerIDCanVote(const UINT8 id); void Y_VoteDrawer(void); void Y_VoteTicker(void); void Y_StartVote(void); From b799862ccbf32abc59e9262015b3aa5559ead148 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Apr 2023 22:09:37 -0400 Subject: [PATCH 04/16] Implement FG catchers - Catcher comes down to grab your selection. (Needs per-player arrows, as discussed last night.) - Catcher has more accurate rainbow cycle. Just looks a bit cleaner. - Vote thumbnails now have outlines & dims, instead of transparency. - Put voting roulette code into its own struct. - Sped up voting roulette. (although it's not visible currently) - Made modify vote stricter (kick hacked clients trying to do funky stuff) - Added VOTE_TIME_WAIT_FOR_VOTE define. If enabled, voting timer will only start when any player gives a vote. Currently disable because it'd be exploitable without mid-game vote joining. --- src/d_netcmd.c | 27 +- src/k_vote.c | 649 +++++++++++++++++++++++++++++++++++------------- src/k_vote.h | 4 +- src/v_video.cpp | 2 +- 4 files changed, 503 insertions(+), 179 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9337e98c6..f70f556ae 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2635,7 +2635,7 @@ void D_SetupVote(void) void D_ModifyClientVote(UINT8 player, SINT8 voted) { - char buf[1]; + char buf[2]; char *p = buf; if (player >= MAXSPLITSCREENPLAYERS) @@ -2643,8 +2643,10 @@ void D_ModifyClientVote(UINT8 player, SINT8 voted) return; } + WRITEUINT8(p, g_localplayers[player]); WRITESINT8(p, voted); - SendNetXCmdForPlayer(player, XD_MODIFYVOTE, &buf, 2); + + SendNetXCmdForPlayer(player, XD_MODIFYVOTE, buf, p - buf); } void D_PickVote(void) @@ -5422,7 +5424,26 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum) { - g_votes[playernum] = READSINT8(*cp); + UINT8 targetID = READUINT8(*cp); + SINT8 vote = READSINT8(*cp); + + if (targetID >= MAXPLAYERS + || playernode[targetID] != playernode[playernum]) + { + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal modify vote command received from %s\n"), + player_names[playernum] + ); + + if (server) + { + SendKick(playernum, KICK_MSG_CON_FAIL); + } + + return; + } + + Y_SetPlayersVote(targetID, vote); } static void Got_PickVotecmd(UINT8 **cp, INT32 playernum) diff --git a/src/k_vote.c b/src/k_vote.c index 542bd5566..b5eefe7a9 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -56,6 +56,15 @@ #include "hardware/hw_main.h" #endif +// Wait for any player to vote before starting the timer. +// Disabled because a player can join and be sent to +// the waiting screen, and if there's only 1 player in +// the vote screen they can idle for as long as they want, +// effectively locking the server up. +// This can be re-enabled if you want to send the vote +// screen in gamestate. (I don't feel like it.) +//#define VOTE_TIME_WAIT_FOR_VOTE + #define UNLOAD(x) if (x) {Patch_Free(x);} x = NULL; #define CLEANUP(x) x = NULL; @@ -66,12 +75,48 @@ #define ARM_FRAMES (4) #define BULB_FRAMES (4) +#define CATCHER_SPEED (8*FRACUNIT) +#define CATCHER_Y_OFFSET (32*FRACUNIT) +#define CATCHER_OFFSCREEN (-CATCHER_Y_OFFSET * 2) + +#define SELECTION_WIDTH (72*FRACUNIT) +#define SELECTION_HEIGHT ((SELECTION_WIDTH * BASEVIDHEIGHT) / BASEVIDWIDTH) +#define SELECTION_X (10*FRACUNIT + (SELECTION_WIDTH >> 1)) +#define SELECTION_Y (144*FRACUNIT + (SELECTION_HEIGHT >> 1)) +#define SELECTION_SPACE (4*FRACUNIT) +#define SELECTION_SPACING_W (SELECTION_WIDTH + SELECTION_SPACE) +#define SELECTION_SPACING_H (SELECTION_HEIGHT + SELECTION_SPACE) +#define SELECTION_HOP (10*FRACUNIT) + // Catcher data +enum +{ + CATCHER_NA = 0, + + CATCHER_FG_LOWER, + CATCHER_FG_GRAB, + CATCHER_FG_STRUGGLE, + CATCHER_FG_POPUP, + CATCHER_FG_RISE, + + CATCHER_BG_LOWER, + CATCHER_BG_RELEASE, + CATCHER_BG_RISE, +}; + typedef struct { fixed_t x, y; + fixed_t destX, destY; + UINT8 spr; boolean small; + + UINT8 action; + tic_t delay; + + SINT8 level; + UINT8 player; } y_vote_catcher; // Clientside & splitscreen player info. @@ -82,23 +127,38 @@ typedef struct UINT8 delay; } y_vote_player; +// Vote "pile" data. Objects for each vote scattered about. +typedef struct +{ + fixed_t x, y; + fixed_t destX, destY; + y_vote_catcher catcher; +} y_vote_pile; + +// Voting roulette variables. +typedef struct +{ + y_vote_pile pile[MAXPLAYERS]; + UINT8 anim; + UINT8 tics; + UINT32 offset; + UINT32 endOffset; + UINT8 syncTime; +} y_vote_roulette; + +// General vote variables typedef struct { INT32 timer; INT32 tic, endtic; boolean notYetPicked; boolean loaded; - - y_vote_player players[MAXSPLITSCREENPLAYERS]; - UINT8 ranim; - UINT8 rtics; - UINT8 roffset; - UINT8 rsynctime; - UINT8 rendoff; - SINT8 deferredLevel; + y_vote_player players[MAXSPLITSCREENPLAYERS]; + y_vote_roulette roulette; } y_vote_data; +// Voting level drawing typedef struct { char str[62]; @@ -106,36 +166,40 @@ typedef struct fixed_t hop; } y_vote_draw_level; +// General vote drawing typedef struct { patch_t *ruby_icon; + fixed_t ruby_height; + patch_t *bg_planet[PLANET_FRAMES]; patch_t *bg_checker; patch_t *bg_levelText; patch_t *bg_derrText; + patch_t *catcher_ufo; patch_t *catcher_arms[ARM_FRAMES]; patch_t *catcher_pole; patch_t *catcher_bulb[BULB_FRAMES]; + + fixed_t selectTransition; y_vote_draw_level levels[VOTE_NUM_LEVELS]; } y_vote_draw; static y_vote_data vote = {0}; static y_vote_draw vote_draw = {0}; -boolean Y_PlayerIDCanVote(const UINT8 id) +boolean Y_PlayerIDCanVote(const UINT8 playerId) { - if (id >= MAXPLAYERS) + player_t *player = NULL; + + if (playerId >= MAXPLAYERS || playeringame[playerId] == false) { return false; } - if (playeringame[id] == false || players[id].spectator == true) - { - return false; - } - - if (players[id].bot == true) + player = &players[playerId]; + if (player->spectator == true || player->bot == true) { return false; } @@ -143,24 +207,167 @@ boolean Y_PlayerIDCanVote(const UINT8 id) return true; } +void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote) +{ + if (gamestate != GS_VOTING) + { + return; + } + + if (newVote < 0 || newVote >= VOTE_NUM_LEVELS) + { + newVote = VOTE_NOT_PICKED; + } + + g_votes[playerId] = newVote; + +#ifdef VOTE_TIME_WAIT_FOR_VOTE + if (vote.timer == -1) + { + // Someone has voted, so start the timer now. + vote.timer = cv_votetime.value * TICRATE; + } +#endif +} + +static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags, SINT8 v, boolean dim) +{ + const fixed_t height = (width * BASEVIDHEIGHT) / BASEVIDWIDTH; + INT32 fx, fy, fw, fh; + INT32 dupx, dupy; + + if (v < 0 || v >= VOTE_NUM_LEVELS) + { + return; + } + + x -= width / 2; + y -= height / 2; + + dupx = vid.dupx; + dupy = vid.dupy; + + if (flags & V_SCALEPATCHMASK) + { + switch ((flags & V_SCALEPATCHMASK) >> V_SCALEPATCHSHIFT) + { + case 1: // V_NOSCALEPATCH + dupx = dupy = 1; + break; + case 2: // V_SMALLSCALEPATCH + dupx = vid.smalldupx; + dupy = vid.smalldupy; + break; + case 3: // V_MEDSCALEPATCH + dupx = vid.meddupx; + dupy = vid.meddupy; + break; + default: + break; + } + } + + // only use one dup, to avoid stretching (har har) + dupx = dupy = (dupx < dupy ? dupx : dupy); + + fx = FixedMul(x, dupx << FRACBITS) >> FRACBITS; + fy = FixedMul(y, dupy << FRACBITS) >> FRACBITS; + fw = FixedMul(width - 1, dupx << FRACBITS) >> FRACBITS; // Why does only this need -1 to match up? IDFK + fh = FixedMul(height, dupy << FRACBITS) >> FRACBITS; + + V_AdjustXYWithSnap(&fx, &fy, flags, dupx, dupy); + + V_DrawFill( + fx - dupx, fy - dupy, + fw + (dupx << 1), fh + (dupy << 1), + 0|flags|V_NOSCALESTART + ); + + K_DrawMapThumbnail( + x, y, + width, flags, + g_voteLevels[v][0], + NULL + ); + + if (dim == true) + { + V_DrawFadeFill( + fx, fy, + fw, fh, + flags|V_NOSCALESTART, + 31, 5 + ); + } +} + static void Y_DrawCatcher(y_vote_catcher *catcher) { +#define NUM_UFO_COLORS (8) + static const skincolornum_t ufoColors[NUM_UFO_COLORS] = { + SKINCOLOR_EMERALD, + SKINCOLOR_SWAMP, + SKINCOLOR_TAFFY, + SKINCOLOR_ROSE, + SKINCOLOR_CYAN, + SKINCOLOR_NAVY, + SKINCOLOR_GOLD, + SKINCOLOR_BRONZE, + }; + #define NUM_BULB_COLORS (2) static const skincolornum_t bulbColors[NUM_BULB_COLORS] = { SKINCOLOR_JAWZ, SKINCOLOR_LILAC, }; - const tic_t anim = gametic / 3; // Using gametic for this is probably a bit goofy + static fixed_t anim = 0; + tic_t colorTic = 0; - fixed_t x = catcher->x - (vote_draw.catcher_ufo->width * FRACUNIT / 2); - fixed_t y = catcher->y - (vote_draw.catcher_ufo->height * FRACUNIT * 7 / 8); + fixed_t baseX = INT32_MAX; + fixed_t x = INT32_MAX; + fixed_t y = INT32_MAX; UINT8 *craneColor = NULL; UINT8 *bulbColor = NULL; - craneColor = R_GetTranslationColormap(TC_DEFAULT, K_RainbowColor(anim), GTC_MENUCACHE); - bulbColor = R_GetTranslationColormap(TC_DEFAULT, bulbColors[(anim / BULB_FRAMES) % NUM_BULB_COLORS], GTC_MENUCACHE); + if (catcher->action == CATCHER_NA) + { + // Don't display in the empty state + return; + } + + anim += renderdeltatics; + colorTic = (anim / 3) / FRACUNIT; + + baseX = catcher->x; + + if (catcher->action == CATCHER_FG_STRUGGLE) + { + if ((anim / FRACUNIT) & 1) + { + baseX += FRACUNIT; + } + else + { + baseX -= FRACUNIT; + } + } + + x = baseX - (vote_draw.catcher_ufo->width * FRACUNIT / 2); + y = catcher->y - (vote_draw.catcher_ufo->height * FRACUNIT) + CATCHER_Y_OFFSET; + + craneColor = R_GetTranslationColormap(TC_DEFAULT, ufoColors[colorTic % NUM_UFO_COLORS], GTC_MENUCACHE); + bulbColor = R_GetTranslationColormap(TC_DEFAULT, bulbColors[(colorTic / BULB_FRAMES) % NUM_BULB_COLORS], GTC_MENUCACHE); + + if (catcher->level != VOTE_NOT_PICKED) + { + Y_DrawVoteThumbnail( + baseX, catcher->y, + SELECTION_WIDTH, 0, + catcher->level, false + ); + } V_DrawFixedPatch( x, y, @@ -172,7 +379,7 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) V_DrawFixedPatch( x, y, FRACUNIT, 0, - vote_draw.catcher_bulb[anim % BULB_FRAMES], + vote_draw.catcher_bulb[colorTic % BULB_FRAMES], bulbColor ); @@ -189,6 +396,7 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) vote_draw.catcher_pole, NULL ); +#undef NUM_UFO_COLORS #undef NUM_BULB_COLORS } @@ -216,12 +424,12 @@ static void Y_DrawVoteBackground(void) ); V_DrawFixedPatch( (BASEVIDWIDTH - vote_draw.bg_checker->width) * FRACUNIT, 0, - FRACUNIT, V_ADD, + FRACUNIT, V_ADD|V_TRANSLUCENT, vote_draw.bg_checker, NULL ); V_DrawFixedPatch( (BASEVIDWIDTH - vote_draw.bg_checker->width) * FRACUNIT, 0, - FRACUNIT, V_ADD, + FRACUNIT, V_ADD|V_TRANSLUCENT, vote_draw.bg_checker, NULL ); @@ -266,48 +474,21 @@ static void Y_DrawVoteBackground(void) bgTimer += renderdeltatics; } -void Y_VoteDrawer(void) +static void Y_DrawVoteSelection(fixed_t offset) { - fixed_t x, y; - fixed_t rubyHeight = 0; - INT32 i, j; - - // If we early return, skip drawing the 3D scene (software buffer) so it doesn't clobber the frame for the wipe - g_wipeskiprender = true; - - if (rendermode == render_none) - { - return; - } - - if (vote.tic >= vote.endtic && vote.endtic != -1) - { - return; - } - - if (vote.loaded == false) - { - return; - } - - g_wipeskiprender = false; - - { - static angle_t rubyFloatTime = 0; - rubyHeight = FINESINE(rubyFloatTime >> ANGLETOFINESHIFT); - rubyFloatTime += FixedMul(ANGLE_MAX / NEWTICRATE, renderdeltatics); - } - - Y_DrawVoteBackground(); - - x = 10 * FRACUNIT; - y = 144 * FRACUNIT; + fixed_t x = SELECTION_X; + fixed_t y = SELECTION_Y + FixedMul(offset, SELECTION_HEIGHT * 2); + INT32 i; + // + // Draw map icons + // for (i = 0; i < VOTE_NUM_LEVELS; i++) { boolean selected = false; INT32 flags = 0; fixed_t destHop = 0; + INT32 j; for (j = 0; j <= splitscreen; j++) // another loop for drawing the selection backgrounds in the right order, grumble grumble.. { @@ -329,11 +510,7 @@ void Y_VoteDrawer(void) if (selected == true) { - destHop = 10*FRACUNIT; - } - else - { - flags |= V_TRANSLUCENT; + destHop = SELECTION_HOP; } if (vote_draw.levels[i].encore == true) @@ -341,103 +518,80 @@ void Y_VoteDrawer(void) flags |= V_FLIP; } - if (vote_draw.levels[i].hop < destHop) - { - vote_draw.levels[i].hop += FixedMul( - (destHop - vote_draw.levels[i].hop) / 2, - renderdeltatics - ); - } - else - { - vote_draw.levels[i].hop = destHop; - } + vote_draw.levels[i].hop += FixedMul( + (destHop - vote_draw.levels[i].hop) / 2, + renderdeltatics + ); - K_DrawMapThumbnail( + Y_DrawVoteThumbnail( x, y - vote_draw.levels[i].hop, - 72 << FRACBITS, flags, - g_voteLevels[i][0], - NULL + SELECTION_WIDTH, flags, + i, (selected == false) ); if (vote_draw.levels[i].encore == true) { V_DrawFixedPatch( - x + (40 << FRACBITS), - ((y + 25) << FRACBITS) - (rubyHeight << 1), + x - (vote_draw.ruby_icon->width * (FRACUNIT >> 1)), + y - (vote_draw.ruby_icon->height * (FRACUNIT >> 1)) - (vote_draw.ruby_height << 1), FRACUNIT, (flags & ~V_FLIP), - vote_draw.ruby_icon, + vote_draw.ruby_icon, NULL ); } - x += (75 << FRACBITS); + x += SELECTION_SPACING_W; } - // TODO: draw + update catchers - Y_DrawCatcher(&vote.players[0].catcher); - - /* - x = 20; - y = 10; - - for (i = 0; i < MAXPLAYERS; i++) + // + // Draw our catchers + // + for (i = 0; i <= splitscreen; i++) { - if (dedicated && i == 0) // While leaving blank spots for non-existent players is largely intentional, the first spot *always* being blank looks a tad silly :V - continue; - - if (Y_PlayerIDCanVote(i) == true && g_votes[i] != VOTE_NOT_PICKED) - { - if (!timer && i == voteclient.ranim) - { - V_DrawScaledPatch(x-18, y+9, V_SNAPTOLEFT, cursor); - if (voteendtic != -1 && !(votetic % 4)) - V_DrawFill(x-1, y-1, 42, 27, 0|V_SNAPTOLEFT); - else - V_DrawFill(x-1, y-1, 42, 27, levelinfo[g_votes[i]].gtc|V_SNAPTOLEFT); - } - - K_DrawMapThumbnail( - (x)< BASEVIDHEIGHT-40) - { - x += 60; - y = 10; - } + Y_DrawCatcher(&vote.players[i].catcher); } - */ +} + +static void Y_DrawVotePile(void) +{ + // TODO +} + +void Y_VoteDrawer(void) +{ + static angle_t rubyFloatTime = 0; + + // If we early return, skip drawing the 3D scene (software buffer) so it doesn't clobber the frame for the wipe + g_wipeskiprender = true; + + if (rendermode == render_none) + { + return; + } + + if (vote.tic >= vote.endtic && vote.endtic != -1) + { + return; + } + + if (vote.loaded == false) + { + return; + } + + g_wipeskiprender = false; + + vote_draw.ruby_height = FINESINE(rubyFloatTime >> ANGLETOFINESHIFT); + rubyFloatTime += FixedMul(ANGLE_MAX / NEWTICRATE, renderdeltatics); + + vote_draw.selectTransition += FixedMul( + (((g_pickedVote != VOTE_NOT_PICKED) ? FRACUNIT : 0) - vote_draw.selectTransition) / 2, + renderdeltatics + ); + + Y_DrawVoteBackground(); + Y_DrawVotePile(); + Y_DrawVoteSelection(vote_draw.selectTransition); if (vote.timer > 0) { @@ -474,6 +628,139 @@ static void Y_VoteStops(SINT8 pick, SINT8 level) deferencoremode = (g_voteLevels[level][1] & VOTE_MOD_ENCORE); } +static void Y_PlayerSendVote(const UINT8 localPlayer) +{ + y_vote_player *const player = &vote.players[localPlayer]; + y_vote_catcher *const catcher = &player->catcher; + + catcher->action = CATCHER_FG_LOWER; + + catcher->x = catcher->destX = SELECTION_X + (SELECTION_SPACING_W * player->selection); + catcher->y = CATCHER_OFFSCREEN; + catcher->destY = SELECTION_Y - SELECTION_HOP; + catcher->spr = 0; + catcher->level = VOTE_NOT_PICKED; + + S_StartSound(NULL, sfx_kc37); +} + +static void Y_TickPlayerCatcher(const UINT8 localPlayer) +{ + y_vote_player *const player = &vote.players[localPlayer]; + y_vote_catcher *const catcher = &player->catcher; + + fixed_t spd = CATCHER_SPEED; + fixed_t xDelta = catcher->destX - catcher->x; + + if (xDelta != 0) + { + // Move X position first + if (abs(xDelta) <= spd) + { + catcher->x = catcher->destX; + } + else + { + if (xDelta < 0) + { + catcher->x -= spd; + } + else + { + catcher->x += spd; + } + } + } + else + { + // Then start moving Y position + fixed_t yDelta = catcher->destY - catcher->y; + + if (abs(yDelta) <= spd) + { + catcher->y = catcher->destY; + } + else + { + if (yDelta < 0) + { + catcher->y -= spd; + } + else + { + catcher->y += spd; + } + } + } + + if (catcher->delay > 0) + { + catcher->delay--; + return; + } + + switch (catcher->action) + { + case CATCHER_FG_LOWER: + { + if (catcher->x == catcher->destX && catcher->y == catcher->destY) + { + catcher->action++; + + S_StopSoundByNum(sfx_kc37); + S_StartSound(NULL, sfx_kc68); + } + break; + } + + case CATCHER_FG_GRAB: + { + catcher->spr++; + + if (catcher->spr >= ARM_FRAMES-1) + { + catcher->action = CATCHER_FG_STRUGGLE; + catcher->level = vote.players[localPlayer].selection; + catcher->delay = 20; + } + break; + } + + case CATCHER_FG_STRUGGLE: + { + catcher->action = CATCHER_FG_POPUP; + catcher->destY -= SELECTION_HOP * 3; + catcher->delay = 15; + break; + } + + case CATCHER_FG_POPUP: + { + catcher->action = CATCHER_FG_RISE; + catcher->destY = CATCHER_OFFSCREEN; + S_StartSound(NULL, sfx_kc37); + break; + } + + case CATCHER_FG_RISE: + { + if (catcher->x == catcher->destX && catcher->y == catcher->destY) + { + D_ModifyClientVote(localPlayer, vote.players[localPlayer].selection); + catcher->action = CATCHER_NA; + S_StopSoundByNum(sfx_kc37); + } + break; + } + + default: + { + catcher->action = CATCHER_NA; + break; + } + } +} + // // Y_VoteTicker // @@ -523,15 +810,20 @@ void Y_VoteTicker(void) S_ShowMusicCredit(); } - if (vote.timer) + if (vote.timer > 0) { vote.timer--; } + for (i = 0; i <= splitscreen; i++) + { + Y_TickPlayerCatcher(i); + } + if (g_pickedVote != VOTE_NOT_PICKED) { vote.timer = 0; - vote.rsynctime++; + vote.roulette.syncTime++; if (vote.endtic == -1) { @@ -556,35 +848,37 @@ void Y_VoteTicker(void) return; } - vote.rtics--; - - if (vote.rtics <= 0) + if (vote.roulette.tics > 0) { - vote.roffset++; - vote.rtics = min(20, (3*vote.roffset/4)+5); + vote.roulette.tics--; + } + else + { + vote.roulette.offset++; + vote.roulette.tics = min(6, 9 * vote.roulette.offset / 40); S_StartSound(NULL, sfx_kc39); } - if (vote.rendoff == 0 || vote.roffset < vote.rendoff) + if (vote.roulette.endOffset == 0 || vote.roulette.offset < vote.roulette.endOffset) { - vote.ranim = tempvotes[((g_pickedVote + vote.roffset) % numvotes)]; + vote.roulette.anim = tempvotes[((g_pickedVote + vote.roulette.offset) % numvotes)]; } - if (vote.roffset >= 20) + if (vote.roulette.offset > 40) { - if (vote.rendoff == 0) + if (vote.roulette.endOffset == 0) { - if (vote.rsynctime % 51 == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V) + if (vote.roulette.syncTime % 51 == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V) { for (i = 5; i >= 3; i--) // Find a suitable place to stop { - if (tempvotes[((g_pickedVote + vote.roffset + i) % numvotes)] == g_pickedVote) + if (tempvotes[((g_pickedVote + vote.roulette.offset + i) % numvotes)] == g_pickedVote) { - vote.rendoff = vote.roffset + i; + vote.roulette.endOffset = vote.roulette.offset + i; if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~ { - vote.rendoff++; + vote.roulette.endOffset++; } S_ChangeMusicInternal("voteeb", false); @@ -593,7 +887,7 @@ void Y_VoteTicker(void) } } } - else if (vote.roffset >= vote.rendoff) + else if (vote.roulette.offset >= vote.roulette.endOffset) { vote.endtic = vote.tic + (3*TICRATE); Y_VoteStops(g_pickedVote, vote.deferredLevel); @@ -602,7 +896,7 @@ void Y_VoteTicker(void) } else { - vote.ranim = g_pickedVote; + vote.roulette.anim = g_pickedVote; } } else if (vote.notYetPicked) @@ -616,7 +910,7 @@ void Y_VoteTicker(void) The vote ended, but it will take at least a tic for that to reach us from the server. Don't let me change the vote now, it won't matter anyway! */ - if (vote.timer) + if (vote.timer != 0) { for (i = 0; i <= splitscreen; i++) { @@ -629,9 +923,10 @@ void Y_VoteTicker(void) } if (Y_PlayerIDCanVote(p) == true + && menuactive == false && vote.players[i].delay == 0 && g_pickedVote == VOTE_NOT_PICKED && g_votes[p] == VOTE_NOT_PICKED - && menuactive == false) + && vote.players[i].catcher.action == CATCHER_NA) { if (G_PlayerInputDown(i, gc_left, 0)) { @@ -657,7 +952,7 @@ void Y_VoteTicker(void) if (G_PlayerInputDown(i, gc_a, 0) && moved == false) { - D_ModifyClientVote(i, vote.players[i].selection); + Y_PlayerSendVote(i); moved = true; } } @@ -741,7 +1036,11 @@ void Y_StartVote(void) vote_draw.catcher_bulb[i] = W_CachePatchName(va("VT_BULB%d", i + 1), PU_STATIC); } +#ifdef VOTE_TIME_WAIT_FOR_VOTE + vote.timer = -1; // Timer is not set until the first vote is added +#else vote.timer = cv_votetime.value * TICRATE; +#endif g_pickedVote = VOTE_NOT_PICKED; vote.notYetPicked = true; @@ -750,15 +1049,13 @@ void Y_StartVote(void) { vote.players[i].selection = 0; vote.players[i].delay = 0; - vote.players[i].catcher.x = 10*FRACUNIT + (72*FRACUNIT/2); - vote.players[i].catcher.y = 144*FRACUNIT + (48*FRACUNIT/2); } - vote.ranim = 0; - vote.rtics = 1; - vote.roffset = 0; - vote.rsynctime = 0; - vote.rendoff = 0; + vote.roulette.anim = 0; + vote.roulette.tics = 0; + vote.roulette.offset = 0; + vote.roulette.endOffset = 0; + vote.roulette.syncTime = 0; for (i = 0; i < MAXPLAYERS; i++) { @@ -801,6 +1098,8 @@ void Y_StartVote(void) vote_draw.levels[i].str[sizeof vote_draw.levels[i].str - 1] = '\0'; } + vote_draw.selectTransition = FRACUNIT; + vote.loaded = true; Automate_Run(AEV_VOTESTART); } @@ -881,7 +1180,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) SINT8 votecompare = VOTE_NOT_PICKED; INT32 endtype = VOTE_END_IMMEDIATE; - vote.rsynctime = 0; + vote.roulette.syncTime = 0; for (i = 0; i < MAXPLAYERS; i++) { @@ -920,6 +1219,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) G_AfterIntermission(); return; } + /* case VOTE_END_QUICK: { // Only one unique vote, so just end it immediately. @@ -928,6 +1228,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) Y_VoteStops(pick, level); break; } + */ default: { S_ChangeMusicInternal("voteea", true); @@ -938,5 +1239,5 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) vote.deferredLevel = level; g_pickedVote = pick; - vote.timer = 0; + vote.timer = -1; } diff --git a/src/k_vote.h b/src/k_vote.h index 6705c994a..bf45c7688 100644 --- a/src/k_vote.h +++ b/src/k_vote.h @@ -24,7 +24,9 @@ extern "C" { #define VOTE_MOD_ENCORE (0x01) -boolean Y_PlayerIDCanVote(const UINT8 id); +boolean Y_PlayerIDCanVote(const UINT8 playerId); +void Y_SetPlayersVote(const UINT8 playerId, SINT8 vote); + void Y_VoteDrawer(void); void Y_VoteTicker(void); void Y_StartVote(void); diff --git a/src/v_video.cpp b/src/v_video.cpp index 674f7a3b5..f38e18616 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1307,7 +1307,7 @@ void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, U h *= dupy; // Center it if necessary - // adjustxy + V_AdjustXYWithSnap(&x, &y, c, dupx, dupy); } if (x >= vid.width || y >= vid.height) From 81871bc73b08d6a3d2d84379729e69d09ad01c66 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 4 Apr 2023 23:22:12 -0400 Subject: [PATCH 05/16] Rewrite random map buffer Each map now just has a countdown for when they'll reappear (stored in mapheader), which gets decremented each time a new map is played. This means it's now compatible across gametype switches, is a lot less complex, and is easy to retrieve the value for a specific map without needing to iterate constantly. Lots of the old unused code surrounding this function was also removed. Lastly, added a PARANOIA check for callAgainSoon being mishandled. --- src/d_netcmd.c | 14 ++- src/doomstat.h | 2 + src/f_finale.c | 2 +- src/g_game.c | 237 ++++++++++++++++++++--------------------------- src/g_game.h | 2 +- src/k_menudraw.c | 14 +-- src/k_vote.c | 2 +- 7 files changed, 123 insertions(+), 150 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f70f556ae..65f59c5c4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2617,15 +2617,23 @@ void D_SetupVote(void) { UINT8 buf[(VOTE_NUM_LEVELS * 2) + 2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes UINT8 *p = buf; + INT32 i; - INT16 votebuffer[VOTE_NUM_LEVELS] = {-1}; + + INT16 votebuffer[VOTE_NUM_LEVELS + 1] = {-1}; + votebuffer[VOTE_NUM_LEVELS] = 0; // End marker for G_RandMap WRITEUINT8(p, ((cv_kartencore.value == 1) && (gametyperules & GTR_ENCORE))); WRITEUINT8(p, G_SometimesGetDifferentEncore()); for (i = 0; i < VOTE_NUM_LEVELS; i++) { - UINT16 m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer); + UINT16 m = G_RandMap( + G_TOLFlag(gametype), + prevmap, false, + (i < VOTE_NUM_LEVELS-1), + votebuffer + ); votebuffer[i] = m; WRITEUINT16(p, m); } @@ -3202,7 +3210,7 @@ static void Command_RandomMap(void) oldmapnum = -1; } - newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, 0, 0, false, NULL) + 1; + newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, false, false, NULL) + 1; D_MapChange(newmapnum, newgametype, newencore, newresetplayers, 0, false, false); } diff --git a/src/doomstat.h b/src/doomstat.h index fd74129dc..80d7f3635 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -410,6 +410,8 @@ struct mapheader_t cupheader_t *cup; ///< Cached cup + size_t justPlayed; ///< Prevent this map from showing up in votes if it was recently picked. + // Titlecard information char lvlttl[22]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) char subttl[33]; ///< Subtitle for level diff --git a/src/f_finale.c b/src/f_finale.c index 1d18eb9d2..281786109 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2250,7 +2250,7 @@ void F_TitleScreenTicker(boolean run) // prevent console spam if failed demoIdleLeft = demoIdleTime; - mapnum = G_RandMap(TOL_RACE|TOL_BATTLE, -2, 2, 0, false, NULL); + mapnum = G_RandMap(TOL_RACE|TOL_BATTLE, -2, true, false, NULL); if (mapnum == 0) // gotta have ONE { return; diff --git a/src/g_game.c b/src/g_game.c index 02c3cc682..1627c996d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -319,23 +319,6 @@ boolean comebackshowninfo; // Have you already seen the "ATTACK OR PROTECT" mess tic_t curlap; // Current lap time tic_t bestlap; // Best lap time -typedef struct -{ - INT16 *mapbuffer; // Pointer to zone memory - INT32 lastnummapheaders; // Reset if nummapheaders != this -} randmaps_t; -static randmaps_t randmaps = {NULL, 0}; - -static void G_ResetRandMapBuffer(void) -{ - INT32 i; - Z_Free(randmaps.mapbuffer); - randmaps.lastnummapheaders = nummapheaders; - randmaps.mapbuffer = Z_Malloc(randmaps.lastnummapheaders * sizeof(INT16), PU_STATIC, NULL); - for (i = 0; i < randmaps.lastnummapheaders; i++) - randmaps.mapbuffer[i] = -1; -} - typedef struct joystickvector2_s { INT32 xaxis; @@ -3726,188 +3709,170 @@ static INT32 TOLMaps(UINT8 pgametype) * has those flags. * \author Graue */ -static INT16 *okmaps = NULL; -INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphell, boolean callagainsoon, INT16 *extbuffer) +static INT16 *g_allowedMaps = NULL; + +#ifdef PARANOIA +static INT32 g_randMapStack = 0; +#endif + +INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, INT16 *extBuffer) { - UINT32 numokmaps = 0; - INT16 ix, bufx; - UINT16 extbufsize = 0; + INT32 allowedMapsCount = 0; + INT32 extBufferCount = 0; + INT16 ret = 0; + INT32 i, j; - if (randmaps.lastnummapheaders != nummapheaders) +#ifdef PARANOIA + g_randMapStack++; +#endif + + if (g_allowedMaps == NULL) { - G_ResetRandMapBuffer(); + g_allowedMaps = Z_Malloc(nummapheaders * sizeof(INT16), PU_STATIC, NULL); } - if (!okmaps) + if (extBuffer != NULL) { - //CONS_Printf("(making okmaps)\n"); - okmaps = Z_Malloc(nummapheaders * sizeof(INT16), PU_STATIC, NULL); - } - - if (extbuffer != NULL) - { - bufx = 0; - - while (extbuffer[bufx]) + for (i = 0; extBuffer[i] != 0; i++) { - extbufsize++; - bufx++; + extBufferCount++; } } -tryagain: +tryAgain: - // Find all the maps that are ok and and put them in an array. - for (ix = 0; ix < nummapheaders; ix++) + for (i = 0; i < nummapheaders; i++) { - boolean isokmap = true; - - if (!mapheaderinfo[ix] || mapheaderinfo[ix]->lumpnum == LUMPERROR) + if (mapheaderinfo[i] == NULL || mapheaderinfo[i]->lumpnum == LUMPERROR) { + // Doesn't exist? continue; } - if (!(mapheaderinfo[ix]->typeoflevel & tolflags) - || ix == pprevmap - || M_MapLocked(ix+1) - || (mapheaderinfo[ix]->menuflags & LF2_HIDEINMENU)) // this is bad + if (i == pprevmap) { - continue; //isokmap = false; + // We were just here. + continue; + } + + if ((mapheaderinfo[i]->typeoflevel & tolflags) == 0) + { + // Doesn't match our gametype. + continue; } if (pprevmap == -2 // title demo hack - && mapheaderinfo[ix]->ghostCount == 0) + && mapheaderinfo[i]->ghostCount == 0) { + // Doesn't have any ghosts, so it's not suitable for title demos. continue; } - if (!ignorebuffer) + if ((mapheaderinfo[i]->menuflags & LF2_HIDEINMENU) == LF2_HIDEINMENU) { - if (extbufsize > 0) + // Not intended to be accessed in multiplayer. + continue; + } + + if (M_MapLocked(i + 1) == true) + { + // We haven't earned this one. + continue; + } + + if (ignoreBuffers == false) + { + if (mapheaderinfo[i]->justPlayed > 0) { - for (bufx = 0; bufx < extbufsize; bufx++) + // We just played this map, don't play it again. + continue; + } + + if (extBufferCount > 0) + { + // An optional additional buffer, + // to avoid duplicates on the voting screen. + for (j = 0; j < extBufferCount; j++) { - if (extbuffer[bufx] == -1) // Rest of buffer SHOULD be empty + if (extBuffer[j] < 0 || extBuffer[j] >= nummapheaders) { + // Rest of buffer SHOULD be empty. break; } - if (ix == extbuffer[bufx]) + if (i == extBuffer[j]) { - isokmap = false; + // Map is in this other buffer, don't duplicate. break; } } - if (!isokmap) + if (j < extBufferCount) { + // Didn't make it out of this buffer, so don't add this map. continue; } } - - for (bufx = 0; bufx < (maphell ? 3 : randmaps.lastnummapheaders); bufx++) - { - if (randmaps.mapbuffer[bufx] == -1) // Rest of buffer SHOULD be empty - { - break; - } - - if (ix == randmaps.mapbuffer[bufx]) - { - isokmap = false; - break; - } - } - - if (!isokmap) - continue; } - okmaps[numokmaps++] = ix; + // Got past the gauntlet, so we can allow this one. + g_allowedMaps[ allowedMapsCount++ ] = i; } - if (numokmaps == 0) // If there's no matches... (Goodbye, incredibly silly function chains :V) + if (allowedMapsCount == 0) { - if (!ignorebuffer) + // No maps are available. + if (ignoreBuffers == false) { - if (randmaps.mapbuffer[VOTE_NUM_LEVELS] == -1) // Is the buffer basically empty? - { - ignorebuffer = 1; // This will probably only help in situations where there's very few maps, but it's folly not to at least try it - //CONS_Printf("RANDMAP - ignoring buffer\n"); - goto tryagain; - } - - for (bufx = VOTE_NUM_LEVELS; bufx < randmaps.lastnummapheaders; bufx++) // Let's clear all but the three most recent maps... - { - randmaps.mapbuffer[bufx] = -1; - } - - //CONS_Printf("RANDMAP - emptying randmapbuffer\n"); - goto tryagain; + // Try again with ignoring the buffer before giving up. + ignoreBuffers = true; + goto tryAgain; } - //CONS_Printf("RANDMAP - defaulting to map01\n"); - ix = 0; // Sorry, none match. You get MAP01. - - if (ignorebuffer == 1) - { - //CONS_Printf("(emptying randmapbuffer entirely)\n"); - for (bufx = 0; bufx < randmaps.lastnummapheaders; bufx++) - { - randmaps.mapbuffer[bufx] = -1; // if we're having trouble finding a map we should probably clear it - } - } + // Nothing else actually worked. Welp! + // You just get whatever was added first. + ret = 0; } else { - //CONS_Printf("RANDMAP - %d maps available to grab\n", numokmaps); - ix = okmaps[M_RandomKey(numokmaps)]; + ret = g_allowedMaps[ M_RandomKey(allowedMapsCount) ]; } - if (!callagainsoon) + if (callAgainSoon == false) { - //CONS_Printf("(freeing okmaps)\n"); - Z_Free(okmaps); - okmaps = NULL; + Z_Free(g_allowedMaps); + g_allowedMaps = NULL; + +#ifdef PARANOIA + // Crash if callAgainSoon was mishandled. + I_Assert(g_randMapStack == 1); +#endif } - return ix; +#ifdef PARANOIA + g_randMapStack--; +#endif + + return ret; } void G_AddMapToBuffer(INT16 map) { - INT16 bufx; - INT16 refreshnum = (TOLMaps(gametype)) - VOTE_NUM_LEVELS; - - if (refreshnum < 0) + if (mapheaderinfo[map]->justPlayed == 0) // Started playing a new map. { - refreshnum = 0; - } - - if (nummapheaders != randmaps.lastnummapheaders) - { - G_ResetRandMapBuffer(); - } - else - { - for (bufx = randmaps.lastnummapheaders - 1; bufx > 0; bufx--) + // Decrement every maps' justPlayed value. + INT32 i; + for (i = 0; i < nummapheaders; i++) { - randmaps.mapbuffer[bufx] = randmaps.mapbuffer[bufx-1]; + if (mapheaderinfo[i]->justPlayed > 0) + { + mapheaderinfo[i]->justPlayed--; + } } } - randmaps.mapbuffer[0] = map; - - // We're getting pretty full, so lets flush this for future usage. - if (randmaps.mapbuffer[refreshnum] != -1) - { - // Clear all but the most recent maps. - for (bufx = VOTE_NUM_LEVELS; bufx < randmaps.lastnummapheaders; bufx++) - { - randmaps.mapbuffer[bufx] = -1; - } - //CONS_Printf("Random map buffer has been flushed.\n"); - } + // Set our map's justPlayed value. + mapheaderinfo[map]->justPlayed = TOLMaps(gametype) - VOTE_NUM_LEVELS; } // @@ -4234,7 +4199,7 @@ static void G_GetNextMap(void) } /* FALLTHRU */ case 2: // Go to random map. - nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, false, NULL); + nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, false, NULL); break; default: if (nextmap >= NEXTMAP_SPECIAL) // Loop back around @@ -5394,8 +5359,6 @@ void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ss G_FreeGhosts(); // TODO: do we actually need to do this? - G_ResetRandMapBuffer(); - // this leave the actual game if needed SV_StartSinglePlayerServer(gametype, false); diff --git a/src/g_game.h b/src/g_game.h index 90c19f440..46c93a5c1 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -255,7 +255,7 @@ FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics); UINT32 G_TOLFlag(INT32 pgametype); INT16 G_GetFirstMapOfGametype(UINT8 pgametype); -INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphell, boolean callagainsoon, INT16 *extbuffer); +INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, INT16 *extBuffer); void G_AddMapToBuffer(INT16 map); #ifdef __cplusplus diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 3b63a5b97..c8c78f10d 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -5282,7 +5282,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 encoremapcache = NEXTMAP_INVALID; if (encoremapcache > nummapheaders) { - encoremapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, 2, 0, false, NULL); + encoremapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); } specialmap = encoremapcache; break; @@ -5292,7 +5292,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 tamapcache = NEXTMAP_INVALID; if (tamapcache > nummapheaders) { - tamapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, 2, 0, false, NULL); + tamapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); } specialmap = tamapcache; break; @@ -5302,7 +5302,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 btcmapcache = NEXTMAP_INVALID; if (btcmapcache > nummapheaders) { - btcmapcache = G_RandMap(G_TOLFlag(GT_BATTLE), -1, 2, 0, false, NULL); + btcmapcache = G_RandMap(G_TOLFlag(GT_BATTLE), -1, true, false, NULL); } specialmap = btcmapcache; break; @@ -5312,7 +5312,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 sscmapcache = NEXTMAP_INVALID; if (sscmapcache > nummapheaders) { - sscmapcache = G_RandMap(G_TOLFlag(GT_SPECIAL), -1, 2, 0, false, NULL); + sscmapcache = G_RandMap(G_TOLFlag(GT_SPECIAL), -1, true, false, NULL); } specialmap = sscmapcache; break; @@ -5322,7 +5322,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 spbmapcache = NEXTMAP_INVALID; if (spbmapcache > nummapheaders) { - spbmapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, 2, 0, false, NULL); + spbmapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); } specialmap = spbmapcache; break; @@ -5332,7 +5332,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 hardmapcache = NEXTMAP_INVALID; if (hardmapcache > nummapheaders) { - hardmapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, 2, 0, false, NULL); + hardmapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); } specialmap = hardmapcache; break; @@ -5342,7 +5342,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 mastermapcache = NEXTMAP_INVALID; if (mastermapcache > nummapheaders) { - mastermapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, 2, 0, false, NULL); + mastermapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); } specialmap = mastermapcache; break; diff --git a/src/k_vote.c b/src/k_vote.c index b5eefe7a9..1fe45dd23 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -76,7 +76,7 @@ #define BULB_FRAMES (4) #define CATCHER_SPEED (8*FRACUNIT) -#define CATCHER_Y_OFFSET (32*FRACUNIT) +#define CATCHER_Y_OFFSET (48*FRACUNIT) #define CATCHER_OFFSCREEN (-CATCHER_Y_OFFSET * 2) #define SELECTION_WIDTH (72*FRACUNIT) From 63fdd48b8fd0e6dca7d6ee98a07baddefc70e1c7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 6 Apr 2023 02:23:43 -0400 Subject: [PATCH 06/16] Implement BG catchers --- src/k_vote.c | 729 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 514 insertions(+), 215 deletions(-) diff --git a/src/k_vote.c b/src/k_vote.c index 1fe45dd23..4af47426c 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -79,6 +79,8 @@ #define CATCHER_Y_OFFSET (48*FRACUNIT) #define CATCHER_OFFSCREEN (-CATCHER_Y_OFFSET * 2) +#define CATCHER_Y_OFFSET_SMALL (CATCHER_Y_OFFSET / 2) + #define SELECTION_WIDTH (72*FRACUNIT) #define SELECTION_HEIGHT ((SELECTION_WIDTH * BASEVIDHEIGHT) / BASEVIDWIDTH) #define SELECTION_X (10*FRACUNIT + (SELECTION_WIDTH >> 1)) @@ -88,6 +90,14 @@ #define SELECTION_SPACING_H (SELECTION_HEIGHT + SELECTION_SPACE) #define SELECTION_HOP (10*FRACUNIT) +#define PILE_WIDTH (46*FRACUNIT) +#define PILE_HEIGHT ((PILE_WIDTH * BASEVIDHEIGHT) / BASEVIDWIDTH) +#define PILE_SPACE (4*FRACUNIT) +#define PILE_SPACING_W (PILE_WIDTH + PILE_SPACE) +#define PILE_SPACING_H (PILE_HEIGHT + PILE_SPACE) + +//#define TEST_VOTES (11) + // Catcher data enum { @@ -117,6 +127,8 @@ typedef struct SINT8 level; UINT8 player; + + fixed_t anim; // UI scope variable } y_vote_catcher; // Clientside & splitscreen player info. @@ -177,10 +189,10 @@ typedef struct patch_t *bg_levelText; patch_t *bg_derrText; - patch_t *catcher_ufo; - patch_t *catcher_arms[ARM_FRAMES]; - patch_t *catcher_pole; - patch_t *catcher_bulb[BULB_FRAMES]; + patch_t *catcher_ufo[2]; + patch_t *catcher_arms[2][ARM_FRAMES]; + patch_t *catcher_pole[2]; + patch_t *catcher_bulb[2][BULB_FRAMES]; fixed_t selectTransition; y_vote_draw_level levels[VOTE_NUM_LEVELS]; @@ -207,8 +219,111 @@ boolean Y_PlayerIDCanVote(const UINT8 playerId) return true; } +static void Y_SortPile(void) +{ + UINT8 numVotes = 0; + UINT8 votesLeft = 0; + INT32 i; + +#ifdef TEST_VOTES + numVotes = TEST_VOTES; +#else + for (i = 0; i < MAXPLAYERS; i++) + { + if (g_votes[i] == VOTE_NOT_PICKED) + { + continue; + } + + numVotes++; + } +#endif + + if (numVotes == 0) + { + return; + } + + votesLeft = numVotes; + + for (i = 0; i < MAXPLAYERS; i++) + { + y_vote_pile *const pile = &vote.roulette.pile[i]; + +#ifndef TEST_VOTES + if (g_votes[i] == VOTE_NOT_PICKED) + { + continue; + } +#endif + + // Just center it for now. + pile->destX = BASEVIDWIDTH << FRACBITS >> 1; + pile->destY = BASEVIDHEIGHT << FRACBITS >> 1; + + if (numVotes <= 1) + { + ; // NOP + } + else if (numVotes == 2) + { + // Offset just a bit from the center. + if (votesLeft <= 1) + { + pile->destX += PILE_SPACING_W >> 1; + } + else + { + pile->destX -= PILE_SPACING_W >> 1; + } + } + else if (numVotes <= 12) + { + const boolean odd = ((numVotes % 2) != 0); + UINT8 rowSize = (numVotes + 1) / 2; + INT32 xOffset = 0; + + if (votesLeft > rowSize) + { + if (odd == true) + { + rowSize--; + } + + const SINT8 topRowIndex = (rowSize - ((votesLeft - 1) % rowSize)) - 1; + xOffset = -(rowSize - 1) + (topRowIndex << 1); + + pile->destY -= PILE_SPACING_H >> 1; + } + else + { + const SINT8 botRowIndex = votesLeft - 1; + xOffset = -(rowSize - 1) + (botRowIndex << 1); + + pile->destY += PILE_SPACING_H >> 1; + } + + pile->destX += (PILE_SPACING_W >> 1) * xOffset; + } + else + { + // TODO: 13+ votes + } + + votesLeft--; + + if (votesLeft == 0) + { + break; + } + } +} + void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote) { + y_vote_pile *const pile = &vote.roulette.pile[playerId]; + y_vote_catcher *const catcher = &pile->catcher; + if (gamestate != GS_VOTING) { return; @@ -221,6 +336,19 @@ void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote) g_votes[playerId] = newVote; + Y_SortPile(); + + pile->x = pile->destX; + pile->y = pile->destY; + + catcher->action = CATCHER_BG_LOWER; + + catcher->x = catcher->destX = pile->x; + catcher->y = CATCHER_OFFSCREEN; + catcher->destY = pile->y; + catcher->spr = ARM_FRAMES-1; + catcher->level = g_votes[playerId]; + #ifdef VOTE_TIME_WAIT_FOR_VOTE if (vote.timer == -1) { @@ -321,7 +449,7 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) SKINCOLOR_LILAC, }; - static fixed_t anim = 0; + const UINT8 sizeOffset = (catcher->small == true) ? 1 : 0; tic_t colorTic = 0; fixed_t baseX = INT32_MAX; @@ -337,14 +465,14 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) return; } - anim += renderdeltatics; - colorTic = (anim / 3) / FRACUNIT; + catcher->anim += renderdeltatics; + colorTic = (catcher->anim / 3) / FRACUNIT; baseX = catcher->x; if (catcher->action == CATCHER_FG_STRUGGLE) { - if ((anim / FRACUNIT) & 1) + if ((catcher->anim / FRACUNIT) & 1) { baseX += FRACUNIT; } @@ -354,8 +482,8 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) } } - x = baseX - (vote_draw.catcher_ufo->width * FRACUNIT / 2); - y = catcher->y - (vote_draw.catcher_ufo->height * FRACUNIT) + CATCHER_Y_OFFSET; + x = baseX - (vote_draw.catcher_ufo[sizeOffset]->width * FRACUNIT / 2); + y = catcher->y - (vote_draw.catcher_ufo[sizeOffset]->height * FRACUNIT) + ((catcher->small == true) ? CATCHER_Y_OFFSET_SMALL : CATCHER_Y_OFFSET); craneColor = R_GetTranslationColormap(TC_DEFAULT, ufoColors[colorTic % NUM_UFO_COLORS], GTC_MENUCACHE); bulbColor = R_GetTranslationColormap(TC_DEFAULT, bulbColors[(colorTic / BULB_FRAMES) % NUM_BULB_COLORS], GTC_MENUCACHE); @@ -364,7 +492,7 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) { Y_DrawVoteThumbnail( baseX, catcher->y, - SELECTION_WIDTH, 0, + ((catcher->small == true) ? PILE_WIDTH : SELECTION_WIDTH), 0, catcher->level, false ); } @@ -372,28 +500,28 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) V_DrawFixedPatch( x, y, FRACUNIT, 0, - vote_draw.catcher_arms[catcher->spr % ARM_FRAMES], + vote_draw.catcher_arms[sizeOffset][catcher->spr % ARM_FRAMES], craneColor ); V_DrawFixedPatch( x, y, FRACUNIT, 0, - vote_draw.catcher_bulb[colorTic % BULB_FRAMES], + vote_draw.catcher_bulb[sizeOffset][colorTic % BULB_FRAMES], bulbColor ); V_DrawFixedPatch( x, y, FRACUNIT, 0, - vote_draw.catcher_ufo, + vote_draw.catcher_ufo[sizeOffset], craneColor ); V_DrawFixedPatch( x, y, FRACUNIT, 0, - vote_draw.catcher_pole, + vote_draw.catcher_pole[sizeOffset], NULL ); #undef NUM_UFO_COLORS @@ -554,7 +682,41 @@ static void Y_DrawVoteSelection(fixed_t offset) static void Y_DrawVotePile(void) { - // TODO + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + y_vote_pile *const pile = &vote.roulette.pile[i]; + y_vote_catcher *const catcher = &pile->catcher; + + if (catcher->level != VOTE_NOT_PICKED) + { + continue; + } + +#ifndef TEST_VOTES + if (g_votes[i] == VOTE_NOT_PICKED) + { + continue; + } +#endif + + Y_DrawVoteThumbnail( + pile->x, pile->y, + PILE_WIDTH, 0, +#ifdef TEST_VOTES + 0, +#else + g_votes[i], +#endif + (i != vote.roulette.anim || g_pickedVote == VOTE_NOT_PICKED) + ); + } + + for (i = 0; i < MAXPLAYERS; i++) + { + Y_DrawCatcher(&vote.roulette.pile[i].catcher); + } } void Y_VoteDrawer(void) @@ -584,10 +746,13 @@ void Y_VoteDrawer(void) vote_draw.ruby_height = FINESINE(rubyFloatTime >> ANGLETOFINESHIFT); rubyFloatTime += FixedMul(ANGLE_MAX / NEWTICRATE, renderdeltatics); - vote_draw.selectTransition += FixedMul( - (((g_pickedVote != VOTE_NOT_PICKED) ? FRACUNIT : 0) - vote_draw.selectTransition) / 2, - renderdeltatics - ); + if (vote.loaded == true) + { + vote_draw.selectTransition += FixedMul( + (((g_pickedVote != VOTE_NOT_PICKED) ? FRACUNIT : 0) - vote_draw.selectTransition) / 2, + renderdeltatics + ); + } Y_DrawVoteBackground(); Y_DrawVotePile(); @@ -644,11 +809,8 @@ static void Y_PlayerSendVote(const UINT8 localPlayer) S_StartSound(NULL, sfx_kc37); } -static void Y_TickPlayerCatcher(const UINT8 localPlayer) +static boolean Y_TickGenericCatcher(y_vote_catcher *const catcher) { - y_vote_player *const player = &vote.players[localPlayer]; - y_vote_catcher *const catcher = &player->catcher; - fixed_t spd = CATCHER_SPEED; fixed_t xDelta = catcher->destX - catcher->x; @@ -696,6 +858,19 @@ static void Y_TickPlayerCatcher(const UINT8 localPlayer) if (catcher->delay > 0) { catcher->delay--; + return false; + } + + return true; +} + +static void Y_TickPlayerCatcher(const UINT8 localPlayer) +{ + y_vote_player *const player = &vote.players[localPlayer]; + y_vote_catcher *const catcher = &player->catcher; + + if (Y_TickGenericCatcher(catcher) == false) + { return; } @@ -761,6 +936,278 @@ static void Y_TickPlayerCatcher(const UINT8 localPlayer) } } +static void Y_TickPileCatcher(const UINT8 playerId) +{ + y_vote_pile *const pile = &vote.roulette.pile[playerId]; + y_vote_catcher *const catcher = &pile->catcher; + + if (Y_TickGenericCatcher(catcher) == false) + { + return; + } + + switch (catcher->action) + { + case CATCHER_BG_LOWER: + { + if (catcher->x == catcher->destX && catcher->y == catcher->destY) + { + catcher->level = VOTE_NOT_PICKED; + catcher->action++; + } + break; + } + + case CATCHER_BG_RELEASE: + { + catcher->spr--; + + if (catcher->spr == 0) + { + catcher->destY = CATCHER_OFFSCREEN; + catcher->action = CATCHER_BG_RISE; + } + break; + } + + case CATCHER_BG_RISE: + { + if (catcher->x == catcher->destX && catcher->y == catcher->destY) + { + catcher->action = CATCHER_NA; + } + break; + } + + default: + { + catcher->action = CATCHER_NA; + break; + } + } +} + +static void Y_TickPlayerPile(const UINT8 playerId) +{ + y_vote_pile *const pile = &vote.roulette.pile[playerId]; + y_vote_catcher *const catcher = &pile->catcher; + + fixed_t movedX = 0; + fixed_t movedY = 0; + +#ifndef TEST_VOTES + if (g_votes[playerId] == VOTE_NOT_PICKED) + { + catcher->action = CATCHER_NA; + return; + } +#endif + + movedX = (pile->destX - pile->x) / 2; + movedY = (pile->destY - pile->y) / 2; + + if (movedX != 0 || movedY != 0) + { + pile->x += movedX; + pile->y += movedY; + + catcher->x += movedX; + catcher->y += movedY; + + catcher->destX += movedX; + catcher->destY += movedY; + } + + Y_TickPileCatcher(playerId); +} + +static void Y_TickVoteRoulette(void) +{ + INT32 i; + + vote.timer = 0; + vote.roulette.syncTime++; + + if (vote.endtic == -1) + { + UINT8 tempvotes[MAXPLAYERS]; + UINT8 numvotes = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (g_votes[i] == VOTE_NOT_PICKED) + { + continue; + } + + tempvotes[numvotes] = i; + numvotes++; + } + + if (numvotes < 1) // Whoops! Get outta here. + { + Y_EndVote(); + G_AfterIntermission(); + return; + } + + if (vote.roulette.tics > 0) + { + vote.roulette.tics--; + } + else + { + vote.roulette.offset++; + vote.roulette.tics = min(6, 9 * vote.roulette.offset / 40); + S_StartSound(NULL, sfx_kc39); + } + + if (vote.roulette.endOffset == 0 || vote.roulette.offset < vote.roulette.endOffset) + { + vote.roulette.anim = tempvotes[((g_pickedVote + vote.roulette.offset) % numvotes)]; + } + + if (vote.roulette.offset > 30) + { + if (vote.roulette.endOffset == 0) + { + if (vote.roulette.syncTime % 51 == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V) + { + for (i = 5; i >= 3; i--) // Find a suitable place to stop + { + if (tempvotes[((g_pickedVote + vote.roulette.offset + i) % numvotes)] == g_pickedVote) + { + vote.roulette.endOffset = vote.roulette.offset + i; + + if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~ + { + vote.roulette.endOffset++; + } + + S_ChangeMusicInternal("voteeb", false); + break; + } + } + } + } + else if (vote.roulette.offset >= vote.roulette.endOffset) + { + vote.endtic = vote.tic + (3*TICRATE); + Y_VoteStops(g_pickedVote, vote.deferredLevel); + } + } + } + else + { + vote.roulette.anim = g_pickedVote; + } +} + +static void Y_TickVoteSelection(void) +{ + INT32 i; + + if (vote.tic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V + { + return; + } + + /* + The vote ended, but it will take at least a tic for that to reach us from + the server. Don't let me change the vote now, it won't matter anyway! + */ + if (vote.timer != 0) + { + for (i = 0; i <= splitscreen; i++) + { + const UINT8 p = g_localplayers[i]; + boolean moved = false; + + if (vote.players[i].delay) + { + vote.players[i].delay--; + } + + if (Y_PlayerIDCanVote(p) == true + && menuactive == false + && vote.players[i].delay == 0 + && g_pickedVote == VOTE_NOT_PICKED && g_votes[p] == VOTE_NOT_PICKED + && vote.players[i].catcher.action == CATCHER_NA) + { + if (G_PlayerInputDown(i, gc_left, 0)) + { + vote.players[i].selection--; + moved = true; + } + + if (G_PlayerInputDown(i, gc_right, 0)) + { + vote.players[i].selection++; + moved = true; + } + + if (vote.players[i].selection < 0) + { + vote.players[i].selection = VOTE_NUM_LEVELS - 1; + } + + if (vote.players[i].selection >= VOTE_NUM_LEVELS) + { + vote.players[i].selection = 0; + } + + if (G_PlayerInputDown(i, gc_a, 0) && moved == false) + { + Y_PlayerSendVote(i); + moved = true; + } + } + + if (moved) + { + S_StartSound(NULL, sfx_kc4a); + vote.players[i].delay = NEWTICRATE/7; + } + } + } + + if (server) + { + boolean everyone_voted = true;/* the default condition */ + + for (i = 0; i < MAXPLAYERS; i++) + { + if (Y_PlayerIDCanVote(i) == false) + { + continue; + } + + if (g_votes[i] == VOTE_NOT_PICKED) + { + if (vote.timer == 0) + { + g_votes[i] = 3; // RANDOMIZE LATER + } + else + { + everyone_voted = false; + } + } + } + + if (everyone_voted == true) + { + vote.timer = 0; + + if (vote.endtic == -1) + { + vote.notYetPicked = false; /* don't pick vote twice */ + D_PickVote(); + } + } + } +} + // // Y_VoteTicker // @@ -769,7 +1216,6 @@ static void Y_TickPlayerCatcher(const UINT8 localPlayer) void Y_VoteTicker(void) { INT32 i; - boolean everyone_voted; if (paused || P_AutoPause() || vote.loaded == false) { @@ -820,186 +1266,20 @@ void Y_VoteTicker(void) Y_TickPlayerCatcher(i); } + Y_SortPile(); + + for (i = 0; i < MAXPLAYERS; i++) + { + Y_TickPlayerPile(i); + } + if (g_pickedVote != VOTE_NOT_PICKED) { - vote.timer = 0; - vote.roulette.syncTime++; - - if (vote.endtic == -1) - { - UINT8 tempvotes[MAXPLAYERS]; - UINT8 numvotes = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (g_votes[i] == VOTE_NOT_PICKED) - { - continue; - } - - tempvotes[numvotes] = i; - numvotes++; - } - - if (numvotes < 1) // Whoops! Get outta here. - { - Y_EndVote(); - G_AfterIntermission(); - return; - } - - if (vote.roulette.tics > 0) - { - vote.roulette.tics--; - } - else - { - vote.roulette.offset++; - vote.roulette.tics = min(6, 9 * vote.roulette.offset / 40); - S_StartSound(NULL, sfx_kc39); - } - - if (vote.roulette.endOffset == 0 || vote.roulette.offset < vote.roulette.endOffset) - { - vote.roulette.anim = tempvotes[((g_pickedVote + vote.roulette.offset) % numvotes)]; - } - - if (vote.roulette.offset > 40) - { - if (vote.roulette.endOffset == 0) - { - if (vote.roulette.syncTime % 51 == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V) - { - for (i = 5; i >= 3; i--) // Find a suitable place to stop - { - if (tempvotes[((g_pickedVote + vote.roulette.offset + i) % numvotes)] == g_pickedVote) - { - vote.roulette.endOffset = vote.roulette.offset + i; - - if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~ - { - vote.roulette.endOffset++; - } - - S_ChangeMusicInternal("voteeb", false); - break; - } - } - } - } - else if (vote.roulette.offset >= vote.roulette.endOffset) - { - vote.endtic = vote.tic + (3*TICRATE); - Y_VoteStops(g_pickedVote, vote.deferredLevel); - } - } - } - else - { - vote.roulette.anim = g_pickedVote; - } + Y_TickVoteRoulette(); } else if (vote.notYetPicked) { - if (vote.tic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V - { - return; - } - - /* - The vote ended, but it will take at least a tic for that to reach us from - the server. Don't let me change the vote now, it won't matter anyway! - */ - if (vote.timer != 0) - { - for (i = 0; i <= splitscreen; i++) - { - const UINT8 p = g_localplayers[i]; - boolean moved = false; - - if (vote.players[i].delay) - { - vote.players[i].delay--; - } - - if (Y_PlayerIDCanVote(p) == true - && menuactive == false - && vote.players[i].delay == 0 - && g_pickedVote == VOTE_NOT_PICKED && g_votes[p] == VOTE_NOT_PICKED - && vote.players[i].catcher.action == CATCHER_NA) - { - if (G_PlayerInputDown(i, gc_left, 0)) - { - vote.players[i].selection--; - moved = true; - } - - if (G_PlayerInputDown(i, gc_right, 0)) - { - vote.players[i].selection++; - moved = true; - } - - if (vote.players[i].selection < 0) - { - vote.players[i].selection = VOTE_NUM_LEVELS - 1; - } - - if (vote.players[i].selection >= VOTE_NUM_LEVELS) - { - vote.players[i].selection = 0; - } - - if (G_PlayerInputDown(i, gc_a, 0) && moved == false) - { - Y_PlayerSendVote(i); - moved = true; - } - } - - if (moved) - { - S_StartSound(NULL, sfx_kc4a); - vote.players[i].delay = NEWTICRATE/7; - } - } - } - - if (server) - { - everyone_voted = true;/* the default condition */ - - for (i = 0; i < MAXPLAYERS; i++) - { - if (Y_PlayerIDCanVote(i) == false) - { - continue; - } - - if (g_votes[i] == VOTE_NOT_PICKED) - { - if (vote.timer == 0) - { - g_votes[i] = 3; // RANDOMIZE LATER - } - else - { - everyone_voted = false; - } - } - } - - if (everyone_voted == true) - { - vote.timer = 0; - - if (vote.endtic == -1) - { - vote.notYetPicked = false; /* don't pick vote twice */ - D_PickVote(); - } - } - } + Y_TickVoteSelection(); } } @@ -1025,15 +1305,19 @@ void Y_StartVote(void) vote_draw.bg_levelText = W_CachePatchName("VT_WELC", PU_STATIC); vote_draw.bg_derrText = W_CachePatchName("VT_DERR", PU_STATIC); - vote_draw.catcher_ufo = W_CachePatchName("VT_UFO1", PU_STATIC); + vote_draw.catcher_ufo[0] = W_CachePatchName("VT_UFO1", PU_STATIC); + vote_draw.catcher_ufo[1] = W_CachePatchName("VS_UFO1", PU_STATIC); for (i = 0; i < ARM_FRAMES; i++) { - vote_draw.catcher_arms[i] = W_CachePatchName(va("VT_ARMS%d", i + 1), PU_STATIC); + vote_draw.catcher_arms[0][i] = W_CachePatchName(va("VT_ARMS%d", i + 1), PU_STATIC); + vote_draw.catcher_arms[1][i] = W_CachePatchName(va("VS_ARMS%d", i + 1), PU_STATIC); } - vote_draw.catcher_pole = W_CachePatchName("VT_POLE", PU_STATIC); + vote_draw.catcher_pole[0] = W_CachePatchName("VT_POLE", PU_STATIC); + vote_draw.catcher_pole[1] = W_CachePatchName("VS_POLE", PU_STATIC); for (i = 0; i < BULB_FRAMES; i++) { - vote_draw.catcher_bulb[i] = W_CachePatchName(va("VT_BULB%d", i + 1), PU_STATIC); + vote_draw.catcher_bulb[0][i] = W_CachePatchName(va("VT_BULB%d", i + 1), PU_STATIC); + vote_draw.catcher_bulb[1][i] = W_CachePatchName(va("VS_BULB%d", i + 1), PU_STATIC); } #ifdef VOTE_TIME_WAIT_FOR_VOTE @@ -1047,8 +1331,14 @@ void Y_StartVote(void) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - vote.players[i].selection = 0; - vote.players[i].delay = 0; + y_vote_player *const player = &vote.players[i]; + y_vote_catcher *const catcher = &player->catcher; + + player->selection = 0; + player->delay = 0; + + catcher->action = CATCHER_NA; + catcher->small = false; } vote.roulette.anim = 0; @@ -1059,7 +1349,13 @@ void Y_StartVote(void) for (i = 0; i < MAXPLAYERS; i++) { + y_vote_pile *const pile = &vote.roulette.pile[i]; + y_vote_catcher *const catcher = &pile->catcher; + g_votes[i] = VOTE_NOT_PICKED; + + catcher->action = CATCHER_NA; + catcher->small = true; } for (i = 0; i < VOTE_NUM_LEVELS; i++) @@ -1109,7 +1405,7 @@ void Y_StartVote(void) // static void Y_UnloadVoteData(void) { - INT32 i; + INT32 i, j; vote.loaded = false; @@ -1128,15 +1424,18 @@ static void Y_UnloadVoteData(void) UNLOAD(vote_draw.bg_levelText); UNLOAD(vote_draw.bg_derrText); - UNLOAD(vote_draw.catcher_ufo); - for (i = 0; i < ARM_FRAMES; i++) + for (j = 0; j < 2; j++) { - UNLOAD(vote_draw.catcher_arms[i]); - } - UNLOAD(vote_draw.catcher_pole); - for (i = 0; i < BULB_FRAMES; i++) - { - UNLOAD(vote_draw.catcher_bulb[i]); + UNLOAD(vote_draw.catcher_ufo[j]); + for (i = 0; i < ARM_FRAMES; i++) + { + UNLOAD(vote_draw.catcher_arms[j][i]); + } + UNLOAD(vote_draw.catcher_pole[j]); + for (i = 0; i < BULB_FRAMES; i++) + { + UNLOAD(vote_draw.catcher_bulb[j][i]); + } } } From 11eadd4e75c48a666dc366fa6dc19d40ba345cb8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Apr 2023 16:58:30 -0400 Subject: [PATCH 07/16] Add player icon to BG votes --- src/k_vote.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/k_vote.c b/src/k_vote.c index 4af47426c..326ac09a4 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -348,6 +348,7 @@ void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote) catcher->destY = pile->y; catcher->spr = ARM_FRAMES-1; catcher->level = g_votes[playerId]; + catcher->player = playerId; #ifdef VOTE_TIME_WAIT_FOR_VOTE if (vote.timer == -1) @@ -358,7 +359,7 @@ void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote) #endif } -static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags, SINT8 v, boolean dim) +static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags, SINT8 v, boolean dim, SINT8 playerID) { const fixed_t height = (width * BASEVIDHEIGHT) / BASEVIDWIDTH; INT32 fx, fy, fw, fh; @@ -427,6 +428,25 @@ static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags 31, 5 ); } + + if (playerID >= 0) + { + if (playerID < MAXPLAYERS) + { + UINT8 *playerMap = R_GetTranslationColormap(players[playerID].skin, players[playerID].skincolor, GTC_CACHE); + patch_t *playerPatch = faceprefix[players[playerID].skin][FACE_RANK]; + V_DrawFixedPatch( + x + width - (playerPatch->width * FRACUNIT) + FRACUNIT - 1, + y + height - (playerPatch->height * FRACUNIT) + FRACUNIT, + FRACUNIT, flags, + playerPatch, playerMap + ); + } + else + { + ; // angry level goes here + } + } } static void Y_DrawCatcher(y_vote_catcher *catcher) @@ -493,7 +513,8 @@ static void Y_DrawCatcher(y_vote_catcher *catcher) Y_DrawVoteThumbnail( baseX, catcher->y, ((catcher->small == true) ? PILE_WIDTH : SELECTION_WIDTH), 0, - catcher->level, false + catcher->level, false, + catcher->player ); } @@ -654,7 +675,7 @@ static void Y_DrawVoteSelection(fixed_t offset) Y_DrawVoteThumbnail( x, y - vote_draw.levels[i].hop, SELECTION_WIDTH, flags, - i, (selected == false) + i, (selected == false), -1 ); if (vote_draw.levels[i].encore == true) @@ -709,7 +730,8 @@ static void Y_DrawVotePile(void) #else g_votes[i], #endif - (i != vote.roulette.anim || g_pickedVote == VOTE_NOT_PICKED) + (i != vote.roulette.anim || g_pickedVote == VOTE_NOT_PICKED), + i ); } @@ -1339,6 +1361,7 @@ void Y_StartVote(void) catcher->action = CATCHER_NA; catcher->small = false; + catcher->player = -1; } vote.roulette.anim = 0; @@ -1356,6 +1379,7 @@ void Y_StartVote(void) catcher->action = CATCHER_NA; catcher->small = true; + catcher->player = i; } for (i = 0; i < VOTE_NUM_LEVELS; i++) From 2d8905c8160d3f5712ca9aead35ad9409aa8d994 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Apr 2023 17:14:24 -0400 Subject: [PATCH 08/16] Handle Encore ruby in Y_DrawVoteThumbnail --- src/k_vote.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/k_vote.c b/src/k_vote.c index 326ac09a4..82dd24c10 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -361,6 +361,7 @@ void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote) static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags, SINT8 v, boolean dim, SINT8 playerID) { + const boolean encore = vote_draw.levels[v].encore; const fixed_t height = (width * BASEVIDHEIGHT) / BASEVIDWIDTH; INT32 fx, fy, fw, fh; INT32 dupx, dupy; @@ -414,11 +415,22 @@ static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags K_DrawMapThumbnail( x, y, - width, flags, + width, flags | ((encore == true) ? V_FLIP : 0), g_voteLevels[v][0], NULL ); + if (encore == true) + { + V_DrawFixedPatch( + x + (width / 2) - (vote_draw.ruby_icon->width * (FRACUNIT >> 1)), + y + (height / 2) - (vote_draw.ruby_icon->height * (FRACUNIT >> 1)) - (vote_draw.ruby_height << 1), + FRACUNIT, flags, + vote_draw.ruby_icon, + NULL + ); + } + if (dim == true) { V_DrawFadeFill( @@ -662,11 +674,6 @@ static void Y_DrawVoteSelection(fixed_t offset) destHop = SELECTION_HOP; } - if (vote_draw.levels[i].encore == true) - { - flags |= V_FLIP; - } - vote_draw.levels[i].hop += FixedMul( (destHop - vote_draw.levels[i].hop) / 2, renderdeltatics @@ -675,20 +682,10 @@ static void Y_DrawVoteSelection(fixed_t offset) Y_DrawVoteThumbnail( x, y - vote_draw.levels[i].hop, SELECTION_WIDTH, flags, - i, (selected == false), -1 + i, (selected == false), + -1 ); - if (vote_draw.levels[i].encore == true) - { - V_DrawFixedPatch( - x - (vote_draw.ruby_icon->width * (FRACUNIT >> 1)), - y - (vote_draw.ruby_icon->height * (FRACUNIT >> 1)) - (vote_draw.ruby_height << 1), - FRACUNIT, (flags & ~V_FLIP), - vote_draw.ruby_icon, - NULL - ); - } - x += SELECTION_SPACING_W; } From 505595b5d899c2b03e6c5a1e595d89be097c1717 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Apr 2023 17:42:46 -0400 Subject: [PATCH 09/16] Fix ruby scale & position on vote screen --- src/k_vote.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/k_vote.c b/src/k_vote.c index 82dd24c10..30e865fe1 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -359,10 +359,12 @@ void Y_SetPlayersVote(const UINT8 playerId, SINT8 newVote) #endif } -static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags, SINT8 v, boolean dim, SINT8 playerID) +static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t width, INT32 flags, SINT8 v, boolean dim, SINT8 playerID) { const boolean encore = vote_draw.levels[v].encore; const fixed_t height = (width * BASEVIDHEIGHT) / BASEVIDWIDTH; + const fixed_t x = center_x - (width >> 1); + const fixed_t y = center_y - (height >> 1); INT32 fx, fy, fw, fh; INT32 dupx, dupy; @@ -371,9 +373,6 @@ static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags return; } - x -= width / 2; - y -= height / 2; - dupx = vid.dupx; dupy = vid.dupy; @@ -422,10 +421,10 @@ static void Y_DrawVoteThumbnail(fixed_t x, fixed_t y, fixed_t width, INT32 flags if (encore == true) { + const fixed_t rubyScale = width / 72; V_DrawFixedPatch( - x + (width / 2) - (vote_draw.ruby_icon->width * (FRACUNIT >> 1)), - y + (height / 2) - (vote_draw.ruby_icon->height * (FRACUNIT >> 1)) - (vote_draw.ruby_height << 1), - FRACUNIT, flags, + center_x, center_y - FixedMul(vote_draw.ruby_height << 1, rubyScale), + rubyScale, flags, vote_draw.ruby_icon, NULL ); From 95540888ce25abdcd3ecc1da522492b3d19044d4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 9 Apr 2023 20:53:49 -0400 Subject: [PATCH 10/16] Handle timed out votes properly again Instead of picking random, we ask the client for what their cursor is on. If it takes too long to arrive, then we ignore their vote. Also: fixed a crash if the nextmap was never set during voting (can happen via many 0-players fallbacks, and especially now that it needs to check for client responses on a time out), by always initializing to the first map in the vote. --- src/d_netcmd.c | 3 +- src/k_vote.c | 278 ++++++++++++++++++++++++++++--------------------- 2 files changed, 163 insertions(+), 118 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 65f59c5c4..87219af7f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2687,10 +2687,9 @@ void D_PickVote(void) } } - key = M_RandomKey(numvotes); - if (numvotes > 0) { + key = M_RandomKey(numvotes); WRITESINT8(p, temppicks[key]); WRITESINT8(p, templevels[key]); } diff --git a/src/k_vote.c b/src/k_vote.c index 30e865fe1..8db929bb6 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -96,6 +96,10 @@ #define PILE_SPACING_W (PILE_WIDTH + PILE_SPACE) #define PILE_SPACING_H (PILE_HEIGHT + PILE_SPACE) +// Give time for the animations to finish before finalizing the vote stages. +#define SELECT_DELAY_TIME (TICRATE*4) +#define PICK_DELAY_TIME (TICRATE/2) + //#define TEST_VOTES (11) // Catcher data @@ -137,6 +141,7 @@ typedef struct y_vote_catcher catcher; SINT8 selection; UINT8 delay; + boolean sentTimeOutVote; } y_vote_player; // Vote "pile" data. Objects for each vote scattered about. @@ -163,6 +168,7 @@ typedef struct { INT32 timer; INT32 tic, endtic; + INT32 selectFinalize, pickFinalize; boolean notYetPicked; boolean loaded; SINT8 deferredLevel; @@ -795,9 +801,15 @@ void Y_VoteDrawer(void) // // Vote screen's selection stops moving // -static void Y_VoteStops(SINT8 pick, SINT8 level) +static void Y_FinalizeVote(const SINT8 level) { nextmap = g_voteLevels[level][0]; + deferencoremode = (g_voteLevels[level][1] & VOTE_MOD_ENCORE); +} + +static void Y_VoteStops(SINT8 pick, SINT8 level) +{ + Y_FinalizeVote(level); if (netgame && P_IsLocalPlayer(&players[pick])) { @@ -807,8 +819,6 @@ static void Y_VoteStops(SINT8 pick, SINT8 level) { S_StartSound(NULL, sfx_kc48); // just a cool sound } - - deferencoremode = (g_voteLevels[level][1] & VOTE_MOD_ENCORE); } static void Y_PlayerSendVote(const UINT8 localPlayer) @@ -1121,8 +1131,31 @@ static void Y_TickVoteRoulette(void) } } +static boolean Y_PlayerCanSelect(const UINT8 localId) +{ + const UINT8 p = g_localplayers[localId]; + + if (g_pickedVote != VOTE_NOT_PICKED) + { + return false; + } + + if (g_votes[p] != VOTE_NOT_PICKED) + { + return false; + } + + if (vote.players[localId].catcher.action != CATCHER_NA) + { + return false; + } + + return Y_PlayerIDCanVote(p); +} + static void Y_TickVoteSelection(void) { + boolean everyone_voted = true;/* the default condition */ INT32 i; if (vote.tic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V @@ -1134,23 +1167,27 @@ static void Y_TickVoteSelection(void) The vote ended, but it will take at least a tic for that to reach us from the server. Don't let me change the vote now, it won't matter anyway! */ - if (vote.timer != 0) + for (i = 0; i <= splitscreen; i++) { - for (i = 0; i <= splitscreen; i++) + boolean moved = false; + + if (vote.players[i].delay) { - const UINT8 p = g_localplayers[i]; - boolean moved = false; + vote.players[i].delay--; + } - if (vote.players[i].delay) + if (Y_PlayerCanSelect(i) == true) + { + if (vote.timer == 0) { - vote.players[i].delay--; + // Time's up, send our vote ASAP. + if (vote.players[i].sentTimeOutVote == false) + { + Y_PlayerSendVote(i); + vote.players[i].sentTimeOutVote = true; + } } - - if (Y_PlayerIDCanVote(p) == true - && menuactive == false - && vote.players[i].delay == 0 - && g_pickedVote == VOTE_NOT_PICKED && g_votes[p] == VOTE_NOT_PICKED - && vote.players[i].catcher.action == CATCHER_NA) + else if (menuactive == false && vote.players[i].delay == 0) { if (G_PlayerInputDown(i, gc_left, 0)) { @@ -1180,50 +1217,68 @@ static void Y_TickVoteSelection(void) moved = true; } } + } - if (moved) - { - S_StartSound(NULL, sfx_kc4a); - vote.players[i].delay = NEWTICRATE/7; - } + if (moved == true) + { + S_StartSound(NULL, sfx_kc4a); + vote.players[i].delay = NEWTICRATE/7; } } - if (server) + for (i = 0; i < MAXPLAYERS; i++) { - boolean everyone_voted = true;/* the default condition */ - - for (i = 0; i < MAXPLAYERS; i++) + if (Y_PlayerIDCanVote(i) == false) { - if (Y_PlayerIDCanVote(i) == false) - { - continue; - } - - if (g_votes[i] == VOTE_NOT_PICKED) - { - if (vote.timer == 0) - { - g_votes[i] = 3; // RANDOMIZE LATER - } - else - { - everyone_voted = false; - } - } + continue; } - if (everyone_voted == true) + if (g_votes[i] == VOTE_NOT_PICKED) { - vote.timer = 0; + everyone_voted = false; + break; + } + } - if (vote.endtic == -1) + if (everyone_voted == true) + { + vote.timer = 0; + vote.selectFinalize = SELECT_DELAY_TIME; + } + + if (vote.timer == 0) + { + if (vote.selectFinalize < SELECT_DELAY_TIME) + { + vote.selectFinalize++; + } + } + else + { + vote.selectFinalize = 0; + } + + if (vote.selectFinalize >= SELECT_DELAY_TIME) + { + if (vote.pickFinalize < PICK_DELAY_TIME) + { + vote.pickFinalize++; + } + else if (vote.endtic == -1) + { + vote.notYetPicked = false; /* don't pick vote twice */ + + if (server) { - vote.notYetPicked = false; /* don't pick vote twice */ D_PickVote(); } } } + else if (vote.timer > 0) + { + vote.timer--; + vote.pickFinalize = 0; + } } // @@ -1257,10 +1312,6 @@ void Y_VoteTicker(void) { g_votes[i] = VOTE_NOT_PICKED; // Spectators are the lower class, and have effectively no voice in the government. Democracy sucks. } - else if (g_pickedVote != VOTE_NOT_PICKED && g_votes[i] == VOTE_NOT_PICKED) - { - g_votes[i] = 3; // Slow people get random values -- TODO: random vote doesn't exist anymore - } } if (server && g_pickedVote != VOTE_NOT_PICKED && g_votes[g_pickedVote] == VOTE_NOT_PICKED) // Uh oh! The person who got picked left! Recalculate, quick! @@ -1274,9 +1325,13 @@ void Y_VoteTicker(void) S_ShowMusicCredit(); } - if (vote.timer > 0) + if (g_pickedVote != VOTE_NOT_PICKED) { - vote.timer--; + Y_TickVoteRoulette(); + } + else if (vote.notYetPicked == true) + { + Y_TickVoteSelection(); } for (i = 0; i <= splitscreen; i++) @@ -1290,15 +1345,6 @@ void Y_VoteTicker(void) { Y_TickPlayerPile(i); } - - if (g_pickedVote != VOTE_NOT_PICKED) - { - Y_TickVoteRoulette(); - } - else if (vote.notYetPicked) - { - Y_TickVoteSelection(); - } } // @@ -1306,12 +1352,10 @@ void Y_VoteTicker(void) // // MK online style voting screen, appears after intermission // -void Y_StartVote(void) +static void Y_InitVoteDrawing(void) { INT32 i = 0; - vote.tic = vote.endtic = -1; - vote_draw.ruby_icon = W_CachePatchName("RUBYICON", PU_STATIC); for (i = 0; i < PLANET_FRAMES; i++) @@ -1338,46 +1382,6 @@ void Y_StartVote(void) vote_draw.catcher_bulb[1][i] = W_CachePatchName(va("VS_BULB%d", i + 1), PU_STATIC); } -#ifdef VOTE_TIME_WAIT_FOR_VOTE - vote.timer = -1; // Timer is not set until the first vote is added -#else - vote.timer = cv_votetime.value * TICRATE; -#endif - - g_pickedVote = VOTE_NOT_PICKED; - vote.notYetPicked = true; - - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - y_vote_player *const player = &vote.players[i]; - y_vote_catcher *const catcher = &player->catcher; - - player->selection = 0; - player->delay = 0; - - catcher->action = CATCHER_NA; - catcher->small = false; - catcher->player = -1; - } - - vote.roulette.anim = 0; - vote.roulette.tics = 0; - vote.roulette.offset = 0; - vote.roulette.endOffset = 0; - vote.roulette.syncTime = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - y_vote_pile *const pile = &vote.roulette.pile[i]; - y_vote_catcher *const catcher = &pile->catcher; - - g_votes[i] = VOTE_NOT_PICKED; - - catcher->action = CATCHER_NA; - catcher->small = true; - catcher->player = i; - } - for (i = 0; i < VOTE_NUM_LEVELS; i++) { // set up the encore @@ -1415,6 +1419,49 @@ void Y_StartVote(void) } vote_draw.selectTransition = FRACUNIT; +} + +void Y_StartVote(void) +{ + INT32 i = 0; + + memset(&vote, 0, sizeof(vote)); + memset(&vote_draw, 0, sizeof(vote_draw)); + + vote.tic = vote.endtic = -1; + +#ifdef VOTE_TIME_WAIT_FOR_VOTE + vote.timer = -1; // Timer is not set until the first vote is added +#else + vote.timer = cv_votetime.value * TICRATE; +#endif + + g_pickedVote = VOTE_NOT_PICKED; + vote.notYetPicked = true; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + y_vote_player *const player = &vote.players[i]; + y_vote_catcher *const catcher = &player->catcher; + + catcher->action = CATCHER_NA; + catcher->small = false; + catcher->player = -1; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + y_vote_pile *const pile = &vote.roulette.pile[i]; + y_vote_catcher *const catcher = &pile->catcher; + + g_votes[i] = VOTE_NOT_PICKED; + + catcher->action = CATCHER_NA; + catcher->small = true; + catcher->player = i; + } + + Y_InitVoteDrawing(); vote.loaded = true; Automate_Run(AEV_VOTESTART); @@ -1464,6 +1511,14 @@ static void Y_UnloadVoteData(void) // void Y_EndVote(void) { + if (nextmap >= NEXTMAP_SPECIAL) + { + // Don't leave nextmap unset if the vote is ended through + // weird means! (such as a dedicated server becoming empty) + // If nextmap was left at NEXTMAP_VOTING, we'd crash! + Y_FinalizeVote(0); + } + Y_UnloadVoteData(); vote.endtic = -1; } @@ -1486,7 +1541,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) return; } - if (pick == VOTE_NOT_PICKED) // No other votes? We gotta get out of here, then! + if (pick == VOTE_NOT_PICKED || level == VOTE_NOT_PICKED) // No other votes? We gotta get out of here, then! { Y_EndVote(); G_AfterIntermission(); @@ -1503,17 +1558,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) for (i = 0; i < MAXPLAYERS; i++) { - if (Y_PlayerIDCanVote(i) == true && g_votes[i] == VOTE_NOT_PICKED) - { - g_votes[i] = 3; // RANDOMIZE - } - - if (g_votes[i] == VOTE_NOT_PICKED || endtype > VOTE_END_QUICK) // Don't need to go on - { - continue; - } - - if (endtype == VOTE_END_NORMAL) + if (g_votes[i] == VOTE_NOT_PICKED) { continue; } @@ -1526,6 +1571,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) else if (g_votes[i] != votecompare) { endtype = VOTE_END_NORMAL; + break; } } @@ -1538,7 +1584,6 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) G_AfterIntermission(); return; } - /* case VOTE_END_QUICK: { // Only one unique vote, so just end it immediately. @@ -1547,7 +1592,6 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) Y_VoteStops(pick, level); break; } - */ default: { S_ChangeMusicInternal("voteea", true); @@ -1559,4 +1603,6 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) vote.deferredLevel = level; g_pickedVote = pick; vote.timer = -1; + vote.selectFinalize = SELECT_DELAY_TIME; + vote.pickFinalize = PICK_DELAY_TIME; } From 9c4ace6fbcf8d874199c54d321ee54581f667304 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 10 Apr 2023 02:08:48 -0400 Subject: [PATCH 11/16] Map anger - Maps build anger every time a map isn't selected by anyone. - If a map is ignored for 4 votes in a row, then on the 5th vote it shows up it will be angry enough to vote for itself when everyone else finishes voting. - Once it gives its funny vote, or it gets played, it will calm down again. - 13P+ vote icons are implemented; it's just a basic circle though cuz lazy. - Made the roulette finish even faster. - Bots can vote again but now behind a debug cvar. --- src/d_netcmd.c | 81 ++++++++++++++++----- src/d_netcmd.h | 1 + src/doomstat.h | 5 +- src/g_game.c | 27 +++++-- src/k_kart.c | 1 + src/k_vote.c | 191 +++++++++++++++++++++++++++++++++++++++---------- src/p_saveg.c | 10 +-- src/p_setup.c | 3 + 8 files changed, 255 insertions(+), 64 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 87219af7f..76758aa41 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -468,6 +468,8 @@ consvar_t cv_reducevfx = CVAR_INIT ("reducevfx", "No", CV_SAVE, CV_YesNo, NULL); static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}}; consvar_t cv_votetime = CVAR_INIT ("votetime", "20", CV_NETVAR, votetime_cons_t, NULL); +consvar_t cv_botscanvote = CVAR_INIT ("botscanvote", "No", CV_CHEAT, CV_YesNo, NULL); + consvar_t cv_gravity = CVAR_INIT ("gravity", "0.8", CV_CHEAT|CV_FLOAT|CV_CALL, NULL, Gravity_OnChange); // change DEFAULT_GRAVITY if you change this consvar_t cv_soundtest = CVAR_INIT ("soundtest", "0", CV_CALL, NULL, SoundTest_OnChange); @@ -2645,29 +2647,52 @@ void D_ModifyClientVote(UINT8 player, SINT8 voted) { char buf[2]; char *p = buf; + UINT8 sendPlayer = consoleplayer; - if (player >= MAXSPLITSCREENPLAYERS) + if (player == UINT8_MAX) { - return; + // Special game vote (map anger, duel) + if (!server) + { + return; + } + } + + if (player == UINT8_MAX) + { + // special vote + WRITEUINT8(p, UINT8_MAX); + } + else + { + INT32 i = 0; + WRITEUINT8(p, player); + + for (i = 0; i <= splitscreen; i++) + { + if (g_localplayers[i] == player) + { + sendPlayer = i; + } + } } - WRITEUINT8(p, g_localplayers[player]); WRITESINT8(p, voted); - SendNetXCmdForPlayer(player, XD_MODIFYVOTE, buf, p - buf); + SendNetXCmdForPlayer(sendPlayer, XD_MODIFYVOTE, buf, p - buf); } void D_PickVote(void) { char buf[2]; char* p = buf; - SINT8 temppicks[MAXPLAYERS]; - SINT8 templevels[MAXPLAYERS]; + SINT8 temppicks[VOTE_TOTAL]; + SINT8 templevels[VOTE_TOTAL]; SINT8 votecompare = VOTE_NOT_PICKED; UINT8 numvotes = 0, key = 0; INT32 i; - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { if (Y_PlayerIDCanVote(i) == false) { @@ -5434,23 +5459,45 @@ static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum) UINT8 targetID = READUINT8(*cp); SINT8 vote = READSINT8(*cp); - if (targetID >= MAXPLAYERS - || playernode[targetID] != playernode[playernum]) + if (targetID == UINT8_MAX) { - CONS_Alert(CONS_WARNING, - M_GetText ("Illegal modify vote command received from %s\n"), - player_names[playernum] - ); - - if (server) + if (playernum != serverplayer) // server-only special vote { - SendKick(playernum, KICK_MSG_CON_FAIL); + goto fail; } - return; + targetID = VOTE_SPECIAL; + } + else if (playeringame[targetID] == true && players[targetID].bot == true) + { + if (targetID >= MAXPLAYERS + || playernum != serverplayer) + { + goto fail; + } + } + else + { + if (targetID >= MAXPLAYERS + || playernode[targetID] != playernode[playernum]) + { + goto fail; + } } Y_SetPlayersVote(targetID, vote); + return; + +fail: + CONS_Alert(CONS_WARNING, + M_GetText ("Illegal modify vote command received from %s\n"), + player_names[playernum] + ); + + if (server) + { + SendKick(playernum, KICK_MSG_CON_FAIL); + } } static void Got_PickVotecmd(UINT8 **cp, INT32 playernum) diff --git a/src/d_netcmd.h b/src/d_netcmd.h index d4c7d82a4..8cab1e10f 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -90,6 +90,7 @@ extern consvar_t cv_karteliminatelast; extern consvar_t cv_kartusepwrlv; extern consvar_t cv_votetime; +extern consvar_t cv_botscanvote; extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution, cv_kartdebughuddrop; extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector; diff --git a/src/doomstat.h b/src/doomstat.h index 80d7f3635..e044ed0cf 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -411,6 +411,7 @@ struct mapheader_t cupheader_t *cup; ///< Cached cup size_t justPlayed; ///< Prevent this map from showing up in votes if it was recently picked. + size_t anger; ///< No one picked this map... it's mad now. // Titlecard information char lvlttl[22]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) @@ -734,8 +735,10 @@ extern boolean legitimateexit; extern boolean comebackshowninfo; extern tic_t curlap, bestlap; +#define VOTE_SPECIAL (MAXPLAYERS) +#define VOTE_TOTAL (MAXPLAYERS+1) extern INT16 g_voteLevels[4][2]; -extern SINT8 g_votes[MAXPLAYERS]; +extern SINT8 g_votes[VOTE_TOTAL]; extern SINT8 g_pickedVote; // =========================== diff --git a/src/g_game.c b/src/g_game.c index 1627c996d..e9168df51 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -299,7 +299,7 @@ boolean franticitems; // Frantic items currently enabled? // Voting system INT16 g_voteLevels[4][2]; // Levels that were rolled by the host -SINT8 g_votes[MAXPLAYERS]; // Each player's vote +SINT8 g_votes[VOTE_TOTAL]; // Each player's vote SINT8 g_pickedVote; // What vote the host rolls // Server-sided, synched variables @@ -3687,14 +3687,33 @@ static INT32 TOLMaps(UINT8 pgametype) // Find all the maps that are ok for (i = 0; i < nummapheaders; i++) { - if (!mapheaderinfo[i]) + if (mapheaderinfo[i] == NULL) + { continue; + } + if (mapheaderinfo[i]->lumpnum == LUMPERROR) + { continue; - if (!(mapheaderinfo[i]->typeoflevel & tolflag)) + } + + if ((mapheaderinfo[i]->typeoflevel & tolflag) == 0) + { continue; - if (mapheaderinfo[i]->menuflags & LF2_HIDEINMENU) // Don't include Map Hell + } + + if (mapheaderinfo[i]->menuflags & LF2_HIDEINMENU) + { + // Don't include hidden continue; + } + + if (M_MapLocked(i + 1)) + { + // Don't include locked + continue; + } + num++; } diff --git a/src/k_kart.c b/src/k_kart.c index c06b15bd7..89d02517c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -342,6 +342,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_karteliminatelast); CV_RegisterVar(&cv_kartusepwrlv); CV_RegisterVar(&cv_votetime); + CV_RegisterVar(&cv_botscanvote); CV_RegisterVar(&cv_kartdebugitem); CV_RegisterVar(&cv_kartdebugamount); diff --git a/src/k_vote.c b/src/k_vote.c index 8db929bb6..5e8f78b2d 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -96,11 +96,14 @@ #define PILE_SPACING_W (PILE_WIDTH + PILE_SPACE) #define PILE_SPACING_H (PILE_HEIGHT + PILE_SPACE) +#define LOTS_OF_VOTES_X (120*FRACUNIT) +#define LOTS_OF_VOTES_Y (80*FRACUNIT) + // Give time for the animations to finish before finalizing the vote stages. #define SELECT_DELAY_TIME (TICRATE*4) #define PICK_DELAY_TIME (TICRATE/2) -//#define TEST_VOTES (11) +#define MAP_ANGER_MAX (VOTE_NUM_LEVELS) // Catcher data enum @@ -155,7 +158,7 @@ typedef struct // Voting roulette variables. typedef struct { - y_vote_pile pile[MAXPLAYERS]; + y_vote_pile pile[VOTE_TOTAL]; UINT8 anim; UINT8 tics; UINT32 offset; @@ -211,17 +214,29 @@ boolean Y_PlayerIDCanVote(const UINT8 playerId) { player_t *player = NULL; + if (playerId == VOTE_SPECIAL) + { + // Special vote spot, always allow + return true; + } + if (playerId >= MAXPLAYERS || playeringame[playerId] == false) { return false; } player = &players[playerId]; - if (player->spectator == true || player->bot == true) + if (player->spectator == true) { return false; } + if (player->bot == true && cv_botscanvote.value == 0) + { + // Bots may only vote if the server allows it + return false; + } + return true; } @@ -231,10 +246,7 @@ static void Y_SortPile(void) UINT8 votesLeft = 0; INT32 i; -#ifdef TEST_VOTES - numVotes = TEST_VOTES; -#else - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { if (g_votes[i] == VOTE_NOT_PICKED) { @@ -243,7 +255,6 @@ static void Y_SortPile(void) numVotes++; } -#endif if (numVotes == 0) { @@ -252,16 +263,14 @@ static void Y_SortPile(void) votesLeft = numVotes; - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { y_vote_pile *const pile = &vote.roulette.pile[i]; -#ifndef TEST_VOTES if (g_votes[i] == VOTE_NOT_PICKED) { continue; } -#endif // Just center it for now. pile->destX = BASEVIDWIDTH << FRACBITS >> 1; @@ -313,7 +322,9 @@ static void Y_SortPile(void) } else { - // TODO: 13+ votes + angle_t a = ANGLE_90 + (ANGLE_MAX / numVotes) * (votesLeft - 1); + pile->destX += FixedMul(LOTS_OF_VOTES_X, FINECOSINE(a >> ANGLETOFINESHIFT)); + pile->destY += FixedMul(LOTS_OF_VOTES_Y, -FINESINE(a >> ANGLETOFINESHIFT)); } votesLeft--; @@ -448,20 +459,51 @@ static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t widt if (playerID >= 0) { + const INT32 whiteSq = 16 * dupx; + if (playerID < MAXPLAYERS) { UINT8 *playerMap = R_GetTranslationColormap(players[playerID].skin, players[playerID].skincolor, GTC_CACHE); patch_t *playerPatch = faceprefix[players[playerID].skin][FACE_RANK]; + V_DrawFixedPatch( - x + width - (playerPatch->width * FRACUNIT) + FRACUNIT - 1, - y + height - (playerPatch->height * FRACUNIT) + FRACUNIT, - FRACUNIT, flags, + (fx + fw - whiteSq + dupx) * FRACUNIT, + (fy + fh - whiteSq + dupy) * FRACUNIT, + FRACUNIT, flags|V_NOSCALESTART, playerPatch, playerMap ); } else { - ; // angry level goes here + const fixed_t iconHeight = (14 << FRACBITS); + const fixed_t iconWidth = (iconHeight * 320) / 200; + + V_DrawFill( + fx + fw - whiteSq + dupx, + fy + fh - whiteSq + dupy, + whiteSq, + whiteSq, + 0|flags|V_NOSCALESTART + ); + + V_SetClipRect( + fx + fw - whiteSq + (2 * dupx), + fy + fh - whiteSq + (2 * dupy), + whiteSq - (2 * dupx), + whiteSq - (2 * dupy), + flags|V_NOSCALESTART + ); + + K_DrawMapThumbnail( + ((fx + fw - whiteSq + (2 * dupx)) * FRACUNIT) - (iconWidth - iconHeight), + (fy + fh - whiteSq + (2 * dupy)) * FRACUNIT, + iconWidth, + flags | V_NOSCALESTART | ((encore == true) ? V_FLIP : 0), + g_voteLevels[v][0], + NULL + ); + + V_ClearClipRect(); } } } @@ -707,7 +749,7 @@ static void Y_DrawVotePile(void) { INT32 i; - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { y_vote_pile *const pile = &vote.roulette.pile[i]; y_vote_catcher *const catcher = &pile->catcher; @@ -717,27 +759,21 @@ static void Y_DrawVotePile(void) continue; } -#ifndef TEST_VOTES if (g_votes[i] == VOTE_NOT_PICKED) { continue; } -#endif Y_DrawVoteThumbnail( pile->x, pile->y, PILE_WIDTH, 0, -#ifdef TEST_VOTES - 0, -#else g_votes[i], -#endif (i != vote.roulette.anim || g_pickedVote == VOTE_NOT_PICKED), i ); } - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { Y_DrawCatcher(&vote.roulette.pile[i].catcher); } @@ -949,7 +985,7 @@ static void Y_TickPlayerCatcher(const UINT8 localPlayer) { if (catcher->x == catcher->destX && catcher->y == catcher->destY) { - D_ModifyClientVote(localPlayer, vote.players[localPlayer].selection); + D_ModifyClientVote(g_localplayers[localPlayer], vote.players[localPlayer].selection); catcher->action = CATCHER_NA; S_StopSoundByNum(sfx_kc37); } @@ -1023,13 +1059,11 @@ static void Y_TickPlayerPile(const UINT8 playerId) fixed_t movedX = 0; fixed_t movedY = 0; -#ifndef TEST_VOTES if (g_votes[playerId] == VOTE_NOT_PICKED) { catcher->action = CATCHER_NA; return; } -#endif movedX = (pile->destX - pile->x) / 2; movedY = (pile->destY - pile->y) / 2; @@ -1058,10 +1092,10 @@ static void Y_TickVoteRoulette(void) if (vote.endtic == -1) { - UINT8 tempvotes[MAXPLAYERS]; + UINT8 tempvotes[VOTE_TOTAL]; UINT8 numvotes = 0; - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { if (g_votes[i] == VOTE_NOT_PICKED) { @@ -1086,7 +1120,7 @@ static void Y_TickVoteRoulette(void) else { vote.roulette.offset++; - vote.roulette.tics = min(6, 9 * vote.roulette.offset / 40); + vote.roulette.tics = min(5, 7 * vote.roulette.offset / 40); S_StartSound(NULL, sfx_kc39); } @@ -1095,7 +1129,7 @@ static void Y_TickVoteRoulette(void) vote.roulette.anim = tempvotes[((g_pickedVote + vote.roulette.offset) % numvotes)]; } - if (vote.roulette.offset > 30) + if (vote.roulette.offset > 20) { if (vote.roulette.endOffset == 0) { @@ -1107,7 +1141,7 @@ static void Y_TickVoteRoulette(void) { vote.roulette.endOffset = vote.roulette.offset + i; - if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~ + if (M_RandomChance(FRACUNIT/4)) // Let it cheat occasionally~ { vote.roulette.endOffset++; } @@ -1153,6 +1187,74 @@ static boolean Y_PlayerCanSelect(const UINT8 localId) return Y_PlayerIDCanVote(p); } +static void Y_TryMapAngerVote(void) +{ + SINT8 angryMaps[VOTE_NUM_LEVELS] = { -1 }; + size_t angryMapsCount = 0; + + boolean mapVoted[VOTE_NUM_LEVELS] = { false }; + INT32 pick = 0; + + INT32 numPlayers = 0; + INT32 i = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (Y_PlayerIDCanVote(i) == false) + { + continue; + } + + numPlayers++; + + if (g_votes[i] != VOTE_NOT_PICKED) + { + mapVoted[ g_votes[i] ] = true; + } + } + + if (numPlayers < 3) + { + // Don't handle map anger if there's not enough players. + return; + } + + for (i = 0; i < VOTE_NUM_LEVELS; i++) + { + const INT16 mapID = g_voteLevels[i][0]; + + if (mapVoted[i] == true) + { + // Someone voted for us, no need to be angry anymore :) + mapheaderinfo[ mapID ]->anger = 0; + } + else + { + // Increment map anger for maps that weren't picked by a single soul. + mapheaderinfo[ mapID ]->anger++; + + if (mapheaderinfo[ mapID ]->anger > MAP_ANGER_MAX) + { + // If they are angry enough, then it can vote for itself! + angryMaps[ angryMapsCount ] = i; + angryMapsCount++; + } + } + } + + if (angryMapsCount == 0) + { + return; + } + + // Set the special vote to a random angry map. + pick = M_RandomKey(angryMapsCount); + D_ModifyClientVote(UINT8_MAX, angryMaps[pick]); + + // Make it not angry anymore. + mapheaderinfo[ g_voteLevels[ angryMaps[pick] ][0] ]->anger = 0; +} + static void Y_TickVoteSelection(void) { boolean everyone_voted = true;/* the default condition */ @@ -1233,10 +1335,18 @@ static void Y_TickVoteSelection(void) continue; } + if (players[i].bot == true && g_votes[i] == VOTE_NOT_PICKED) + { + if (( M_RandomFixed() % 100 ) == 0) + { + // bots vote randomly + D_ModifyClientVote(i, M_RandomKey(VOTE_NUM_LEVELS)); + } + } + if (g_votes[i] == VOTE_NOT_PICKED) { everyone_voted = false; - break; } } @@ -1270,6 +1380,7 @@ static void Y_TickVoteSelection(void) if (server) { + Y_TryMapAngerVote(); D_PickVote(); } } @@ -1306,11 +1417,15 @@ void Y_VoteTicker(void) return; } - for (i = 0; i < MAXPLAYERS; i++) // Correct votes as early as possible, before they're processed by the game at all + // Correct invalid votes as early as possible, + // before they're processed by the rest of the ticker + for (i = 0; i < MAXPLAYERS; i++) { if (Y_PlayerIDCanVote(i) == false) { - g_votes[i] = VOTE_NOT_PICKED; // Spectators are the lower class, and have effectively no voice in the government. Democracy sucks. + // Spectators are the lower class, and have + // effectively no voice in the government. Democracy sucks. + g_votes[i] = VOTE_NOT_PICKED; } } @@ -1341,7 +1456,7 @@ void Y_VoteTicker(void) Y_SortPile(); - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { Y_TickPlayerPile(i); } @@ -1449,7 +1564,7 @@ void Y_StartVote(void) catcher->player = -1; } - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { y_vote_pile *const pile = &vote.roulette.pile[i]; y_vote_catcher *const catcher = &pile->catcher; @@ -1556,7 +1671,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) vote.roulette.syncTime = 0; - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { if (g_votes[i] == VOTE_NOT_PICKED) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 05c55e0ea..081eceffe 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4997,14 +4997,16 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) WRITEINT16(save->p, lastmap); WRITEUINT16(save->p, bossdisabled); - for (i = 0; i < 4; i++) + for (i = 0; i < VOTE_NUM_LEVELS; i++) { WRITEINT16(save->p, g_voteLevels[i][0]); WRITEINT16(save->p, g_voteLevels[i][1]); } - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) + { WRITESINT8(save->p, g_votes[i]); + } WRITESINT8(save->p, g_pickedVote); @@ -5169,13 +5171,13 @@ static inline boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading) lastmap = READINT16(save->p); bossdisabled = READUINT16(save->p); - for (i = 0; i < 4; i++) + for (i = 0; i < VOTE_NUM_LEVELS; i++) { g_voteLevels[i][0] = READINT16(save->p); g_voteLevels[i][1] = READINT16(save->p); } - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < VOTE_TOTAL; i++) { g_votes[i] = READSINT8(save->p); } diff --git a/src/p_setup.c b/src/p_setup.c index 6de8c86b1..232be6670 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -429,6 +429,9 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) Z_Free(mapheaderinfo[num]->mainrecord); mapheaderinfo[num]->mainrecord = NULL; + mapheaderinfo[num]->justPlayed = 0; + mapheaderinfo[num]->anger = 0; + mapheaderinfo[num]->customopts = NULL; mapheaderinfo[num]->numCustomOptions = 0; } From 31728166cab6a1b202511bad7deb296ef97d307b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 10 Apr 2023 02:27:48 -0400 Subject: [PATCH 12/16] Adjust map anger conditions Don't reset map anger when it votes for itself -- wait until the map gets played or it gets a genuine vote from someone. --- src/g_game.c | 1 + src/k_vote.c | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index e9168df51..5988ce3bb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3892,6 +3892,7 @@ void G_AddMapToBuffer(INT16 map) // Set our map's justPlayed value. mapheaderinfo[map]->justPlayed = TOLMaps(gametype) - VOTE_NUM_LEVELS; + mapheaderinfo[map]->anger = 0; // Reset voting anger now that we're playing it } // diff --git a/src/k_vote.c b/src/k_vote.c index 5e8f78b2d..0f9c17520 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -1250,9 +1250,6 @@ static void Y_TryMapAngerVote(void) // Set the special vote to a random angry map. pick = M_RandomKey(angryMapsCount); D_ModifyClientVote(UINT8_MAX, angryMaps[pick]); - - // Make it not angry anymore. - mapheaderinfo[ g_voteLevels[ angryMaps[pick] ][0] ]->anger = 0; } static void Y_TickVoteSelection(void) From d2960ce93da9b814a706a4ca98663b25504eccc9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 10 Apr 2023 23:12:08 -0400 Subject: [PATCH 13/16] Don't allow resending vote if you have delay --- src/k_vote.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/k_vote.c b/src/k_vote.c index 0f9c17520..05db3d48d 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -1270,23 +1270,23 @@ static void Y_TickVoteSelection(void) { boolean moved = false; - if (vote.players[i].delay) - { - vote.players[i].delay--; - } - if (Y_PlayerCanSelect(i) == true) { - if (vote.timer == 0) + if (vote.players[i].delay > 0) + { + vote.players[i].delay--; + } + else if (vote.timer == 0) { // Time's up, send our vote ASAP. if (vote.players[i].sentTimeOutVote == false) { Y_PlayerSendVote(i); vote.players[i].sentTimeOutVote = true; + vote.players[i].delay = NEWTICRATE/7; } } - else if (menuactive == false && vote.players[i].delay == 0) + else if (menuactive == false) { if (G_PlayerInputDown(i, gc_left, 0)) { From 9de36ce2a92e6933c212c7acb4bce40934de2ce9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 10 Apr 2023 23:14:25 -0400 Subject: [PATCH 14/16] Slide out votes when you're done voting --- src/k_vote.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/k_vote.c b/src/k_vote.c index 05db3d48d..afdd40491 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -808,8 +808,20 @@ void Y_VoteDrawer(void) if (vote.loaded == true) { + boolean slideOut = true; + INT32 i; + + for (i = 0; i <= splitscreen; i++) + { + if (g_votes[ g_localplayers[i] ] == VOTE_NOT_PICKED) + { + slideOut = false; + break; + } + } + vote_draw.selectTransition += FixedMul( - (((g_pickedVote != VOTE_NOT_PICKED) ? FRACUNIT : 0) - vote_draw.selectTransition) / 2, + ((slideOut ? FRACUNIT : 0) - vote_draw.selectTransition) / 2, renderdeltatics ); } From 16df36b65b1de74f449c4e9d8fc7bfb71c17a63b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 10 Apr 2023 23:52:37 -0400 Subject: [PATCH 15/16] Draw fill instead of BG for Ivo until RHI bug fix --- src/k_vote.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/k_vote.c b/src/k_vote.c index afdd40491..96c4c590f 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -625,6 +625,18 @@ static void Y_DrawVoteBackground(void) const UINT8 planetFrame = (bgTimer / FRACUNIT) % PLANET_FRAMES; + if (cv_reducevfx.value) + { + // REMOVE ONCE THE RHI BUG IS FIXED, + // BUT UNTIL THEN, IVO NEEDS TO NOT DIE + V_DrawFill( + 0, 0, + BASEVIDWIDTH, BASEVIDHEIGHT, + 31 + ); + return; + } + V_DrawFixedPatch( 0, 0, FRACUNIT, 0, From 198672c8455d37e063e5104ee68994b7c99458a9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 11 Apr 2023 01:19:30 -0400 Subject: [PATCH 16/16] Fix vote buffer for G_RandMap being broken --- src/d_netcmd.c | 4 ++-- src/doomstat.h | 2 +- src/f_finale.c | 2 +- src/g_game.c | 27 ++++++++++++++++----------- src/g_game.h | 4 ++-- src/k_menudraw.c | 14 +++++++------- src/p_saveg.c | 8 ++++---- src/p_setup.c | 2 -- 8 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 76758aa41..4f595411e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2622,8 +2622,8 @@ void D_SetupVote(void) INT32 i; - INT16 votebuffer[VOTE_NUM_LEVELS + 1] = {-1}; - votebuffer[VOTE_NUM_LEVELS] = 0; // End marker for G_RandMap + UINT16 votebuffer[VOTE_NUM_LEVELS + 1]; + memset(votebuffer, UINT16_MAX, sizeof(votebuffer)); WRITEUINT8(p, ((cv_kartencore.value == 1) && (gametyperules & GTR_ENCORE))); WRITEUINT8(p, G_SometimesGetDifferentEncore()); diff --git a/src/doomstat.h b/src/doomstat.h index e044ed0cf..a16e5baa8 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -737,7 +737,7 @@ extern tic_t curlap, bestlap; #define VOTE_SPECIAL (MAXPLAYERS) #define VOTE_TOTAL (MAXPLAYERS+1) -extern INT16 g_voteLevels[4][2]; +extern UINT16 g_voteLevels[4][2]; extern SINT8 g_votes[VOTE_TOTAL]; extern SINT8 g_pickedVote; diff --git a/src/f_finale.c b/src/f_finale.c index 281786109..aef80562e 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2250,7 +2250,7 @@ void F_TitleScreenTicker(boolean run) // prevent console spam if failed demoIdleLeft = demoIdleTime; - mapnum = G_RandMap(TOL_RACE|TOL_BATTLE, -2, true, false, NULL); + mapnum = G_RandMap(TOL_RACE|TOL_BATTLE, UINT16_MAX-1, true, false, NULL); if (mapnum == 0) // gotta have ONE { return; diff --git a/src/g_game.c b/src/g_game.c index 5988ce3bb..f5173e642 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -298,7 +298,7 @@ boolean prevencoremode; boolean franticitems; // Frantic items currently enabled? // Voting system -INT16 g_voteLevels[4][2]; // Levels that were rolled by the host +UINT16 g_voteLevels[4][2]; // Levels that were rolled by the host SINT8 g_votes[VOTE_TOTAL]; // Each player's vote SINT8 g_pickedVote; // What vote the host rolls @@ -3728,17 +3728,17 @@ static INT32 TOLMaps(UINT8 pgametype) * has those flags. * \author Graue */ -static INT16 *g_allowedMaps = NULL; +static UINT16 *g_allowedMaps = NULL; #ifdef PARANOIA -static INT32 g_randMapStack = 0; +static size_t g_randMapStack = 0; #endif -INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, INT16 *extBuffer) +UINT16 G_RandMap(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer) { INT32 allowedMapsCount = 0; INT32 extBufferCount = 0; - INT16 ret = 0; + UINT16 ret = 0; INT32 i, j; #ifdef PARANOIA @@ -3747,12 +3747,12 @@ INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, boolean ignoreBuffers, boolean if (g_allowedMaps == NULL) { - g_allowedMaps = Z_Malloc(nummapheaders * sizeof(INT16), PU_STATIC, NULL); + g_allowedMaps = Z_Malloc(nummapheaders * sizeof(UINT16), PU_STATIC, NULL); } if (extBuffer != NULL) { - for (i = 0; extBuffer[i] != 0; i++) + for (i = 0; extBuffer[i] != UINT16_MAX; i++) { extBufferCount++; } @@ -3780,7 +3780,7 @@ tryAgain: continue; } - if (pprevmap == -2 // title demo hack + if (pprevmap == UINT16_MAX-1 // title demo hack (FUCK YOU, MAKE IT A BOOL) && mapheaderinfo[i]->ghostCount == 0) { // Doesn't have any ghosts, so it's not suitable for title demos. @@ -3809,11 +3809,13 @@ tryAgain: if (extBufferCount > 0) { + boolean inExt = false; + // An optional additional buffer, // to avoid duplicates on the voting screen. for (j = 0; j < extBufferCount; j++) { - if (extBuffer[j] < 0 || extBuffer[j] >= nummapheaders) + if (extBuffer[j] >= nummapheaders) { // Rest of buffer SHOULD be empty. break; @@ -3822,11 +3824,12 @@ tryAgain: if (i == extBuffer[j]) { // Map is in this other buffer, don't duplicate. + inExt = true; break; } } - if (j < extBufferCount) + if (inExt == true) { // Didn't make it out of this buffer, so don't add this map. continue; @@ -3875,7 +3878,7 @@ tryAgain: return ret; } -void G_AddMapToBuffer(INT16 map) +void G_AddMapToBuffer(UINT16 map) { if (mapheaderinfo[map]->justPlayed == 0) // Started playing a new map. { @@ -5507,6 +5510,8 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr } CON_LogMessage("\"\n"); } + + G_AddMapToBuffer(gamemap - 1); } diff --git a/src/g_game.h b/src/g_game.h index 46c93a5c1..9fe875c09 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -255,8 +255,8 @@ FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics); UINT32 G_TOLFlag(INT32 pgametype); INT16 G_GetFirstMapOfGametype(UINT8 pgametype); -INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, INT16 *extBuffer); -void G_AddMapToBuffer(INT16 map); +UINT16 G_RandMap(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer); +void G_AddMapToBuffer(UINT16 map); #ifdef __cplusplus } // extern "C" diff --git a/src/k_menudraw.c b/src/k_menudraw.c index c8c78f10d..33cbf3aec 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -5282,7 +5282,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 encoremapcache = NEXTMAP_INVALID; if (encoremapcache > nummapheaders) { - encoremapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); + encoremapcache = G_RandMap(G_TOLFlag(GT_RACE), UINT16_MAX, true, false, NULL); } specialmap = encoremapcache; break; @@ -5292,7 +5292,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 tamapcache = NEXTMAP_INVALID; if (tamapcache > nummapheaders) { - tamapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); + tamapcache = G_RandMap(G_TOLFlag(GT_RACE), UINT16_MAX, true, false, NULL); } specialmap = tamapcache; break; @@ -5302,7 +5302,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 btcmapcache = NEXTMAP_INVALID; if (btcmapcache > nummapheaders) { - btcmapcache = G_RandMap(G_TOLFlag(GT_BATTLE), -1, true, false, NULL); + btcmapcache = G_RandMap(G_TOLFlag(GT_BATTLE), UINT16_MAX, true, false, NULL); } specialmap = btcmapcache; break; @@ -5312,7 +5312,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 sscmapcache = NEXTMAP_INVALID; if (sscmapcache > nummapheaders) { - sscmapcache = G_RandMap(G_TOLFlag(GT_SPECIAL), -1, true, false, NULL); + sscmapcache = G_RandMap(G_TOLFlag(GT_SPECIAL), UINT16_MAX, true, false, NULL); } specialmap = sscmapcache; break; @@ -5322,7 +5322,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 spbmapcache = NEXTMAP_INVALID; if (spbmapcache > nummapheaders) { - spbmapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); + spbmapcache = G_RandMap(G_TOLFlag(GT_RACE), UINT16_MAX, true, false, NULL); } specialmap = spbmapcache; break; @@ -5332,7 +5332,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 hardmapcache = NEXTMAP_INVALID; if (hardmapcache > nummapheaders) { - hardmapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); + hardmapcache = G_RandMap(G_TOLFlag(GT_RACE), UINT16_MAX, true, false, NULL); } specialmap = hardmapcache; break; @@ -5342,7 +5342,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) static UINT16 mastermapcache = NEXTMAP_INVALID; if (mastermapcache > nummapheaders) { - mastermapcache = G_RandMap(G_TOLFlag(GT_RACE), -1, true, false, NULL); + mastermapcache = G_RandMap(G_TOLFlag(GT_RACE), UINT16_MAX, true, false, NULL); } specialmap = mastermapcache; break; diff --git a/src/p_saveg.c b/src/p_saveg.c index 081eceffe..d66ea3f20 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4999,8 +4999,8 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) for (i = 0; i < VOTE_NUM_LEVELS; i++) { - WRITEINT16(save->p, g_voteLevels[i][0]); - WRITEINT16(save->p, g_voteLevels[i][1]); + WRITEUINT16(save->p, g_voteLevels[i][0]); + WRITEUINT16(save->p, g_voteLevels[i][1]); } for (i = 0; i < VOTE_TOTAL; i++) @@ -5173,8 +5173,8 @@ static inline boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading) for (i = 0; i < VOTE_NUM_LEVELS; i++) { - g_voteLevels[i][0] = READINT16(save->p); - g_voteLevels[i][1] = READINT16(save->p); + g_voteLevels[i][0] = READUINT16(save->p); + g_voteLevels[i][1] = READUINT16(save->p); } for (i = 0; i < VOTE_TOTAL; i++) diff --git a/src/p_setup.c b/src/p_setup.c index 232be6670..35010f715 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8045,8 +8045,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) G_SaveGameData(); } - G_AddMapToBuffer(gamemap-1); - P_MapEnd(); // tm.thing is no longer needed from this point onwards // Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...