diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5f81a30f4..0ad2c4712 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -152,6 +152,7 @@ add_executable(BLANKART MACOSX_BUNDLE WIN32 k_mapuser.c k_stats.c k_specialstage.c + k_vote.c h_timers.cpp stun.c lonesha256.c diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 061cdf278..33c06bb9c 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -59,6 +59,7 @@ #include "r_fps.h" #include "m_cond.h" // netUnlocked #include "g_party.h" +#include "k_vote.h" // cl loading screen #include "v_video.h" diff --git a/src/d_main.cpp b/src/d_main.cpp index d9a1a6e0f..0feac0c38 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -82,6 +82,7 @@ #include "m_random.h" // P_ClearRandom #include "acs/interface.h" #include "k_specialstage.h" +#include "k_vote.h" #define __STDC_FORMAT_MACROS #include diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cf75fa0e6..27b31bbf5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -71,6 +71,7 @@ #include "k_specialstage.h" #include "k_items.h" #include "k_itemlist.hpp" +#include "k_vote.h" #define CV_RESTRICT CV_NETVAR diff --git a/src/g_demo.c b/src/g_demo.c index 77f4f8ac0..2139722ae 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -54,6 +54,7 @@ #include "k_color.h" #include "k_follower.h" #include "k_grandprix.h" +#include "k_vote.h" static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL); diff --git a/src/g_game.c b/src/g_game.c index da82ad1fc..77c8c20fd 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -70,6 +70,7 @@ #include "g_party.h" #include "f_dscredits.hpp" #include "strbuf.h" +#include "k_vote.h" #ifdef HAVE_DISCORDRPC #include "discord.h" diff --git a/src/k_vote.c b/src/k_vote.c new file mode 100644 index 000000000..2f13ea4bf --- /dev/null +++ b/src/k_vote.c @@ -0,0 +1,1183 @@ +// BLANKART +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by Kart Krew. +// Copyright (C) 2026 by Team BlanKart. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_vote.c +/// \brief MK online style voting screen + +#include "doomdef.h" +#include "doomstat.h" +#include "g_game.h" +#include "i_video.h" +#include "r_skins.h" +#include "s_sound.h" +#include "st_stuff.h" +#include "v_video.h" +#include "w_wad.h" +#include "y_inter.h" +#include "z_zone.h" +#include "m_menu.h" +#include "r_draw.h" + +#include "p_local.h" + +#include "lua_hook.h" // IntermissionThinker hook + +#include "lua_hud.h" +#include "lua_hudlib_drawlist.h" + +#include "m_random.h" // M_RandomKey +#include "g_input.h" // G_PlayerInputDown +#include "k_bot.h" +#include "console.h" // cons_menuhighlight +#include "k_itemlist.hpp" +#include "k_vote.h" + +static INT32 votetimer; + +static huddrawlist_h luahuddrawlist_vote; + +// SRB2Kart: voting stuff +// Level images +typedef struct +{ + char str[62]; + UINT8 gtc; + const char *gts; + boolean encore; +} y_votelvlinfo; + +// Clientside & splitscreen player info. +typedef struct +{ + SINT8 selection; + UINT8 delay; +} y_voteplayer; + +typedef struct +{ + y_voteplayer playerinfo[4]; + UINT8 ranim; + UINT8 rtics; + UINT8 roffset; + UINT8 rsynctime; + UINT8 rendoff; + boolean loaded; +} y_voteclient; + +// votescreen stuff +votescreen_t VoteScreen = {0}; + +static y_votelvlinfo levelinfo[13]; +static y_voteclient voteclient; +static INT32 votetic; +static INT32 lastvotetic; +static INT32 voteendtic = -1; +static SINT8 votemax = 3; +static INT32 voterowmem = 0; +static boolean rowchange = false; +static boolean votenotyetpicked; + +static void Y_UnloadVoteData(void); + +// SRB2Kart: Voting! + +// +// Y_VoteScreenCheck +// +static void Y_VoteScreenCheck(void) +{ + strcpy(VoteScreen.Prefix, "INTS"); + + if (VoteScreen.luaPrefix[0] != 0) + strlcpy(VoteScreen.Prefix, VoteScreen.luaPrefix, sizeof(VoteScreen.Prefix)); + else if (gametype == GT_BATTLE) + strcpy(VoteScreen.Prefix, "BTLS"); + + VoteScreen.foundLuaVoteFrames = VoteScreen.foundLuaVoteWideFrames = 0; + VoteScreen.currentAnimFrame = 0; + + if (VoteScreen.timePerAnimFrame == 0) + VoteScreen.timePerAnimFrame = 2; + + INT32 i = 1; + + // check for lua vote background replacements + for (;;) + { + // Check if the lumps exist (checking for VEXTR(N|W)xx for race and VEXTRB(N|W)xx for battle) + boolean normalLumpExists = W_LumpExists(va("%sC%d", VoteScreen.Prefix, i)); + boolean wideLumpExists = W_LumpExists(va("%sW%d", VoteScreen.Prefix, i)); + + if (normalLumpExists || wideLumpExists) + { + if (normalLumpExists) + VoteScreen.foundLuaVoteFrames++; + + if (wideLumpExists) + VoteScreen.foundLuaVoteWideFrames++; + } + else // If we don't find at least frame 1 (e.g VEXTRN1), let's just stop looking + break; + + i++; + } + + // non lua vote background handling + boolean prefbattletype = ((g_voteLevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); + VoteScreen.widebgpatch = W_CachePatchName((prefbattletype ? "BATTLSCW" : "INTERSCW"), PU_PATCH); + VoteScreen.bgpatch = W_CachePatchName((prefbattletype ? "BATTLSCR" : "INTERSCR"), PU_PATCH); +} + +// +// Y_VoteBackgroundDrawer +// +// Determines which patch drawer to use for scaling +// +static void Y_VoteBackgroundDrawer(patch_t *patch) +{ + switch (cv_votebgscaling.value) + { + case 1: // adaptive + V_DrawAdaptiveScaledFullScreenPatch(patch); + break; + case 2: // vertical-fill + V_DrawVerticallyScaledFullScreenPatch(patch); + break; + case 3: // horizontal-fill + V_DrawHorizontallyScaledFullScreenPatch(patch); + break; + case 0: // vanilla + default: + V_DrawScaledPatch(((vid.width/2) / vid.dup) - (patch->width/2), + (vid.height / vid.dup) - patch->height, + V_SNAPTOTOP|V_SNAPTOLEFT, patch); + break; + } +} + +// +// Y_DrawLuaVoteScreenPatch +// +// Handles votebackgrounds set by "setVoteBackground" +// Aswell as animated patches +// +static void Y_DrawLuaVoteScreenPatch(boolean widePatch) +{ + INT32 nextframe = 0; + patch_t *votebg = NULL; + char tempPrefix[6]; + const INT32 tempfoundAnimLuaVoteFrames = ((widePatch ? VoteScreen.foundLuaVoteWideFrames : VoteScreen.foundLuaVoteFrames) - 1); + + strcpy(tempPrefix, va("%s%s", VoteScreen.Prefix, (widePatch ? "W" : "C"))); + + // Draw non animated patch + if (!tempfoundAnimLuaVoteFrames) + { + votebg = W_CachePatchName(va("%s1", tempPrefix), PU_PATCH); + Y_VoteBackgroundDrawer(votebg); + return; + } + + // Draw animated patch based on frame counter on vote screen + + // Just in case someone provides LESS widescreen frames than normal frames or vice versa, reset the frame counter to 0 + if (VoteScreen.currentAnimFrame > tempfoundAnimLuaVoteFrames) + VoteScreen.currentAnimFrame = 0; + + nextframe = (VoteScreen.currentAnimFrame + 1); + + votebg = W_CachePatchName(va("%s%d", tempPrefix, nextframe), PU_PATCH); + + Y_VoteBackgroundDrawer(votebg); + + if (renderisnewtic && (votetic % VoteScreen.timePerAnimFrame == 0) && !paused) + VoteScreen.currentAnimFrame = (nextframe > tempfoundAnimLuaVoteFrames) ? 0 : nextframe; +} + +// +// Y_DrawVoteScreenPatch +// + +static void Y_DrawVoteScreenPatch(void) +{ + patch_t *votebg = NULL; + const boolean widescreen = (vid.scaledwidth > 320); + + if (VoteScreen.foundLuaVoteWideFrames || VoteScreen.foundLuaVoteFrames) + { + Y_DrawLuaVoteScreenPatch(((widescreen && VoteScreen.foundLuaVoteWideFrames) || !VoteScreen.foundLuaVoteFrames)); + return; + } + + // non widescreen patch + votebg = VoteScreen.bgpatch; + + UINT8 prefgametype = (g_voteLevels[0][1] & ~VOTEMODIFIER_ENCORE); + const boolean widebgreplaced = (prefgametype == GT_BATTLE) ? VoteScreen.replaced.widebattle : VoteScreen.replaced.widerace; + const boolean bgreplaced = (prefgametype == GT_BATTLE) ? VoteScreen.replaced.battle : VoteScreen.replaced.race; + + // we check a bunch of stuff to always have a "valid" fallback + if ((widescreen && (widebgreplaced || !bgreplaced)) + || (!widescreen && (widebgreplaced && !bgreplaced))) + { + votebg = VoteScreen.widebgpatch; // widescreen patch + } + + Y_VoteBackgroundDrawer(votebg); +} + +static fixed_t Y_CalculatePicScale(fixed_t picscale, INT32 hypoti) +{ + picscale *= 10; + picscale /= (40-hypoti); + + if ( ((hypoti % 5) == 1) || ((hypoti % 5) == 4) || ((hypoti % 5) == 2) ) + { + if ((hypoti % 5) == 2) // scale DOWN the image + picscale -= (hypoti*16); + else + picscale += (hypoti*2); // scale UP the image + } + + return picscale; +} + +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 && !cv_botcanvote.value) + { + // Bots may only vote if the server allows it + return false; + } + + return true; +} + +// +// Y_VoteDrawer +// +// Draws the voting screen! +// +void Y_VoteDrawer(void) +{ + INT32 rowval, i, px, lvls, x, picdiff, y = 0, height = 0; + UINT8 selected[12]; + fixed_t rubyheight = 0; + fixed_t scale; + patch_t *pic; + mapnum_t mapnum; + fixed_t picwidth = 160; + + // CEP: scale by screen hypotenuse for extra voting rows + INT32 vidx = vid.scaledwidth; + INT32 vidy = ((vid.height) / vid.dup); + fixed_t hypotf = 0; + INT32 hypoti = 0; + + INT32 numplayers = 0; + boolean highplayers = false; + + // get the hypotenuse + hypoti = (vidx*vidx) + (vidy*vidy); + hypotf = FixedSqrt(hypoti); + + // convert the fixed_t back into an integer + hypoti = ((hypotf*10)/FRACUNIT); + + if ((voterowmem != cv_votemaxrows.value) && (votemax != cv_votemaxrows.value)) // voting rows were changed(?) + { + CONS_Printf(M_GetText("Max rows will be changed to %d on the next votescreen.\n"), cv_votemaxrows.value); // notify the players + voterowmem = cv_votemaxrows.value; + } + + // divisor for rescaling + INT32 hypotdiv = max(10, (40-hypoti)); + + // readjust the picscale + picwidth *= Y_CalculatePicScale(FRACUNIT, hypoti); + + if (rendermode == render_none) + return; + + if (votetic >= voteendtic && voteendtic != -1) + return; + + if (!voteclient.loaded) + return; + + { + static angle_t rubyfloattime = 0; + rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); + rubyfloattime += FixedMul(ANGLE_MAX/NEWTICRATE, renderdeltatics); + } + + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + + // decides which votebg to draw and draws it + Y_DrawVoteScreenPatch(); + + rowval = (votemax*3)+((votemax > 1) ? (votemax - 1) : 0); + for (i = 0; i < (rowval+1); i++) // First, we need to figure out the height of this thing... + { + UINT8 j; + selected[i] = 0; // Initialize + + for (j = 0; j <= splitscreen; j++) + { + if (voteclient.playerinfo[j].selection == i) + selected[i]++; + } + + if (selected[i]) + height += 25; // 50 + else + height += 25; + + if (i < rowval) + height += 5-splitscreen; + } + + height /= votemax; + + y = (200-height)/2; + picdiff = 80*( max(0, (votemax-1) ) ); // let's draw these in reverse order + lvls = -1; // shitty cheat + + for (i = 0; i < (rowval+1); i++) + { + const char *str; + UINT8 j, color; + + // CEP: hack hell + INT32 scaledpicdiff = ((picdiff*10)/hypotdiv); + INT32 fillscale = 800/hypotdiv; + INT32 hypotmod = (hypoti % 5); // hypotenuse mod 5, rescale the bounding box + INT32 hypotadd = ((hypotmod > 1) ? (hypotmod/4) : hypotmod); // how much do we add the bounding box by? + + // integer scaling makes me want to DIE + if (hypotmod == 3) + hypotadd += (1); + + scaledpicdiff *= 3; + scaledpicdiff /= 2; // 1.5 + + if (i == rowval) + { + str = "RANDOM"; + mapnum = NEXTMAP_INVALID; + } + else + { + str = levelinfo[i].str; + mapnum = g_voteLevels[i][0]; + } + scale = M_GetMapThumbnail(mapnum, &pic)/2; + + if (selected[i]) + { + 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 = VoteScreen.cursor[0]; + p = consoleplayer; + color = levelinfo[i].gtc; + colormap = NULL; + } + else + { + switch (j) + { + case 1: + thiscurs = VoteScreen.cursor[2]; + p = g_localplayers[1]; + break; + case 2: + thiscurs = VoteScreen.cursor[3]; + p = g_localplayers[2]; + break; + case 3: + thiscurs = VoteScreen.cursor[4]; + p = g_localplayers[3]; + break; + default: + thiscurs = VoteScreen.cursor[1]; + p = g_localplayers[0]; + break; + } + + color = skincolors[players[p].skincolor].ramp[7]; + colormap = R_GetTranslationColormap(TC_DEFAULT, players[p].skincolor, GTC_CACHE); + } + + if (g_votes[p] != -1 || !Y_PlayerIDCanVote(p)) + continue; + + handy += 3*(3-splitscreen) + (13*j); + V_DrawMappedPatch(BASEVIDWIDTH-(1600/hypotdiv)-scaledpicdiff, handy, V_SNAPTORIGHT, thiscurs, colormap); + + if (votetic % 10 < 4) + V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-sizeadd-scaledpicdiff, y-sizeadd, (fillscale + (sizeadd*2) + hypotadd), ((500/hypotdiv)+(sizeadd*2)) + hypotadd, 0|V_SNAPTORIGHT); + else + V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-sizeadd-scaledpicdiff, y-sizeadd, (fillscale + (sizeadd*2) + hypotadd), ((500/hypotdiv)+(sizeadd*2)) + hypotadd, color|V_SNAPTORIGHT); + + sizeadd--; + } + + if (!levelinfo[i].encore) + V_DrawFixedPatch((BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff)<= 16) + { + V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y+10, w+1, 2, V_SNAPTORIGHT|31); + V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y, w, 11, V_SNAPTORIGHT|levelinfo[i].gtc); + V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff+w+1, y, 12, V_SNAPTORIGHT|31); + V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff+w, y, 11, V_SNAPTORIGHT|levelinfo[i].gtc); + V_DrawThinString(BASEVIDWIDTH-(1188/hypotdiv)-scaledpicdiff, y+1, V_SNAPTORIGHT, levelinfo[i].gts); + } + else // literally almost entirely covers the map icon, let's just mark it red or something + { + V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y, 8, V_SNAPTORIGHT|31); + V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y, 6, V_SNAPTORIGHT|levelinfo[i].gtc); + } + } + + y += ((25*10) / (hypotdiv/2)); + lvls += 1; + + // screen height isn't doing us any favors + if (lvls >= 6) // loop over if we have an extra row + { + lvls = -2; + picdiff -= 90; // yes, this will overlap; no, I don't plan to change it + y = (200-height)/2; + y -= 5; + } + } + else + { + if (!levelinfo[i].encore) + V_DrawFixedPatch((BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff)<= 6) + { + lvls = -2; + picdiff -= 90; + y = (200-height)/2; + y -= 5; + } + } + + y += 5-splitscreen; + lvls += 1; + + if (lvls >= 6) + { + lvls = -2; + picdiff -= 90; + y = (200-height)/2; + y -= 5; + } + } + + x = 20; + y = 10; + + for (px = 0; px < MAXPLAYERS; px++) + { + if (playeringame[px]) + { + numplayers++; + } + } + + highplayers = numplayers > 16; + + for (i = 0; i < MAXPLAYERS; i++) + { + INT32 bigaddx = 60; INT32 bigaddy = 30; + INT32 smalladdx = 42; INT32 smalladdy = 18; + INT32 smallfaceheight = 0; INT32 bigfaceheight = 9; + INT32 smallrectheight = 18; INT32 bigrectheight = 27; + INT32 smallrubyoffset = 4<= ((votemax*3)+((votemax > 1) ? (votemax - 1) : 0)) && (i != g_pickedVote || voteendtic == -1)) + mapnum = NEXTMAP_INVALID; // randomlvl + else + mapnum = g_voteLevels[g_votes[i]][0]; + + scale = M_GetMapThumbnail(mapnum, &pic)/8; + + if (!votetimer && i == voteclient.ranim) + { + V_DrawScaledPatch(x-18, y+(highplayers ? 2 : bigfaceheight), V_SNAPTOLEFT, VoteScreen.cursor[0]); + if (voteendtic != -1 && !(votetic % 4)) + V_DrawFill(x-1, y-1, 42, (highplayers ? smallrectheight : bigrectheight), 0|V_SNAPTOLEFT); + else + V_DrawFill(x-1, y-1, 42, (highplayers ? smallrectheight : bigrectheight), levelinfo[g_votes[i]].gtc|V_SNAPTOLEFT); + } + + if (highplayers) + { + V_SetClipRect(x<width, scale))<leftoffset, y+(highplayers ? smallfaceheight : bigfaceheight)+facerank->topoffset, V_SNAPTOLEFT, facerank, colormap); + } + + if (!splitscreen && i == consoleplayer) + { + UINT8 cursorframe = (votetic / 4) % 8; + patch_t *highlight = W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_CACHE); + V_DrawScaledPatch(x+24+highlight->leftoffset, y+(highplayers ? smallfaceheight : bigfaceheight)+highlight->topoffset, V_SNAPTOLEFT, highlight); + } + } + + y += highplayers ? smalladdy : bigaddy; + + if (y > BASEVIDHEIGHT-40) + { + x += highplayers ? smalladdx : bigaddx; + y = 10; + } + } + + if (votetimer) + { + INT32 hilicol, tickdown = (votetimer+1)/TICRATE; + + if (cons_menuhighlight.value) + hilicol = cons_menuhighlight.value; + else + hilicol = gametypes[gametype]->color; + + V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, + va("Vote ends in %d", tickdown)); + } + + lastvotetic = votetic; + + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_vote); + LUA_HookHUD(luahuddrawlist_vote, HUD_HOOK(vote)); + } + LUA_HUD_DrawList(luahuddrawlist_vote); +} + +// +// Y_VoteStop +// +// Vote screen's selection stops moving +// +static void Y_FinalizeVote(const SINT8 level) +{ + nextmap = g_voteLevels[level][0]; + deferencoremode = (levelinfo[level].encore); + + if (gametype != g_voteLevels[level][1]) + { + INT16 lastgametype = gametype; + G_SetGametype(g_voteLevels[level][1]); + D_GameTypeChanged(lastgametype); + forceresetplayers = true; + } +} + +SINT8 deferredlevel = 0; +static void Y_VoteStops(const SINT8 pick, const SINT8 level) +{ + Y_FinalizeVote(level); + + /*if (level == 4) + { + S_StartSound(NULL, sfx_noooo2); // gasp + } + else */ + 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 + } +} + +// +// Y_VoteTicker +// +// Vote screen thinking :eggthinking: +// +void Y_VoteTicker(void) +{ + INT32 i, j; + boolean everyone_voted; + + if (paused || P_AutoPause() || !voteclient.loaded) + return; + + LUA_HOOK(VoteThinker); + + votetic++; + + if (votetic == voteendtic) + { + Y_EndVote(); + Y_FollowIntermission(); + return; + } + + for (i = 0; i < MAXPLAYERS; i++) // Correct votes as early as possible, before they're processed by the game at all + { + if (!Y_PlayerIDCanVote(i)) + 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] = (votemax*3)+((votemax > 1) ? (votemax - 1) : 0); // Slow people get random + } + + 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) + { + S_ChangeMusicInternal("vote", true); + S_ShowMusicCredit(0, 5*TICRATE, 0); + } + + if (votetimer) + votetimer--; + + if (g_pickedVote != VOTE_NOT_PICKED) + { + votetimer = 0; + voteclient.rsynctime++; + + if (voteendtic == -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(); + Y_FollowIntermission(); + return; + } + + voteclient.rtics--; + + if (voteclient.rtics <= 0) + { + voteclient.roffset++; + voteclient.rtics = min(20, (3*voteclient.roffset/4)+5); + S_StartSound(NULL, sfx_kc39); + } + + if (voteclient.rendoff == 0 || voteclient.roffset < voteclient.rendoff) + voteclient.ranim = tempvotes[((g_pickedVote + voteclient.roffset) % numvotes)]; + + if (voteclient.roffset >= 20) + { + if (voteclient.rendoff == 0) + { + if (voteclient.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[((g_pickedVote + voteclient.roffset + i) % numvotes)] == g_pickedVote) + { + voteclient.rendoff = voteclient.roffset+i; + if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~ + voteclient.rendoff++; + S_ChangeMusicInternal("voteeb", false); + S_ShowMusicCredit(0, 5*TICRATE, 0); + break; + } + } + } + } + else if (voteclient.roffset >= voteclient.rendoff) + { + voteendtic = votetic + (3*TICRATE); + Y_VoteStops(g_pickedVote, deferredlevel); + } + } + } + else + voteclient.ranim = g_pickedVote; + } + else if (votenotyetpicked) + { + if (votetic < 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 (votetimer) + { + for (i = 0; i <= splitscreen; i++) + { + UINT8 p; + boolean pressed = false; + SINT8 votewrap = 0; + + if (votemax == 2) + votewrap = 3; + else if (votemax == 3) + votewrap = 7; + + switch (i) + { + 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; + } + + if (voteclient.playerinfo[i].delay) + voteclient.playerinfo[i].delay--; + + if (Y_PlayerIDCanVote(p) + && !voteclient.playerinfo[i].delay + && g_pickedVote == VOTE_NOT_PICKED && g_votes[p] == VOTE_NOT_PICKED) + { + if (G_PlayerInputDown(i, gc_aimforward, false, DEADZONE_Y)) + { + voteclient.playerinfo[i].selection--; + pressed = true; + } + + if (G_PlayerInputDown(i, gc_aimbackward, false, DEADZONE_Y) && pressed == false) + { + voteclient.playerinfo[i].selection++; + pressed = true; + } + + if (votemax > 1) // only allow side-movements for multi-row selections + { + // HORRIBLE hack, my GOD + if (G_PlayerInputDown(i, gc_turnright, false, DEADZONE_X) && !pressed) // move right + { + if (voteclient.playerinfo[i].selection <= votewrap) + voteclient.playerinfo[i].selection += 4; + else + voteclient.playerinfo[i].selection -= ((votemax-1)*4); + pressed = true; + } + + if (G_PlayerInputDown(i, gc_turnleft, false, DEADZONE_X) && !pressed) // move left + { + if (voteclient.playerinfo[i].selection > 3) + voteclient.playerinfo[i].selection -= 4; + else + voteclient.playerinfo[i].selection += ((votemax-1)*4); + pressed = true; + } + } + + if (voteclient.playerinfo[i].selection < 0) + voteclient.playerinfo[i].selection = ((votemax*3)+((votemax > 1) ? (votemax-1) : 0) ); + if (voteclient.playerinfo[i].selection > ((votemax*3)+((votemax > 1) ? (votemax-1) : 0)) ) + voteclient.playerinfo[i].selection = 0; + + if (G_PlayerInputDown(i, gc_accelerate, false, DEADZONE_BUTTON) && pressed == false) + { + D_ModifyClientVote(g_localplayers[i], voteclient.playerinfo[i].selection); + pressed = true; + } + } + + if (pressed) + { + S_StartSound(NULL, sfx_kc4a); + voteclient.playerinfo[i].delay = NEWTICRATE/7; + } + } + } + + if (server) + { + everyone_voted = true;/* the default condition */ + + if (votetimer == 0) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (Y_PlayerIDCanVote(i) && g_votes[i] == VOTE_NOT_PICKED) + g_votes[i] = (votemax*3)+((votemax > 1) ? (votemax-1) : 0); + } + } + else + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!Y_PlayerIDCanVote(i)) + { + continue; + } + + if (server && players[i].bot && g_votes[i] == VOTE_NOT_PICKED) + { + if (( M_RandomFixed() % 100 ) == 0) + { +#define VOTEROWSADDSONE ((cv_votemaxrows.value*3) + 1 + ((cv_votemaxrows.value > 1) ? (cv_votemaxrows.value - 1) : 0)) + // bots vote randomly + INT32 rng = M_RandomKey(VOTEROWSADDSONE); + for (j = 0; j < VOTEROWSADDSONE; j++) + { + rng++; + + if (rng >= VOTEROWSADDSONE || rng < 0) + { + rng = 0; + } + } +#undef VOTEROWSADDSONE + + D_ModifyClientVote(i, rng); + } + } + + if (g_votes[i] == VOTE_NOT_PICKED) + { + everyone_voted = false; + } + } + } + + if (everyone_voted) + { + votetimer = 0; + if (voteendtic == -1) + { + votenotyetpicked = false;/* don't pick vote twice */ + D_PickVote(); + } + } + } + } +} + +// +// Y_StartVote +// +// MK online style voting screen, appears after intermission +// + +static void Y_InitVoteDrawing(void) +{ + if (dedicated) + return; + + // setup the background patches + Y_VoteScreenCheck(); + + VoteScreen.cursor[0] = W_CachePatchName("M_CURSOR", PU_PATCH); + VoteScreen.cursor[1] = W_CachePatchName("P1CURSOR", PU_PATCH); + VoteScreen.cursor[2] = W_CachePatchName("P2CURSOR", PU_PATCH); + VoteScreen.cursor[3] = W_CachePatchName("P3CURSOR", PU_PATCH); + VoteScreen.cursor[4] = W_CachePatchName("P4CURSOR", PU_PATCH); + + VoteScreen.rubyicon = W_CachePatchName("RUBYICON", PU_PATCH); +} + +void Y_StartVote(void) +{ + INT32 i = 0; + INT32 rowval = (cv_votemaxrows.value*3)+((cv_votemaxrows.value > 1) ? (cv_votemaxrows.value - 1) : 0); + + votemax = cv_votemaxrows.value; // can we please avoid SIGSEGVs + voterowmem = cv_votemaxrows.value; // this is just for the notif system + rowchange = false; + + votetic = -1; + +#ifdef PARANOIA + if (voteendtic != -1) + I_Error("voteendtic is dirty"); +#endif + + // cache vote patches + Y_InitVoteDrawing(); + + votetimer = cv_votetime.value*TICRATE; + g_pickedVote = VOTE_NOT_PICKED; + + votenotyetpicked = true; + + for (i = 0; i < 3; i++) + { + voteclient.playerinfo[i].selection = 0; + voteclient.playerinfo[i].delay = 0; + } + + voteclient.ranim = 0; + voteclient.rtics = 1; + voteclient.roffset = 0; + voteclient.rsynctime = 0; + voteclient.rendoff = 0; + + for (i = 0; i < MAXPLAYERS; i++) + g_votes[i] = VOTE_NOT_PICKED; + + for (i = 0; i < (rowval + 1); i++) + { + // set up the encore + levelinfo[i].encore = (g_voteLevels[i][1] & VOTEMODIFIER_ENCORE) ? true : false; + g_voteLevels[i][1] &= ~VOTEMODIFIER_ENCORE; + + // 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(levelinfo[i].str, + sizeof levelinfo[i].str, + "%s %s", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->actnum); + else + snprintf(levelinfo[i].str, + sizeof levelinfo[i].str, + "%s", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl); + } + else + { + if (mapheaderinfo[g_voteLevels[i][0]]->actnum[0]) + snprintf(levelinfo[i].str, + sizeof levelinfo[i].str, + "%s %s %s", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->zonttl, mapheaderinfo[g_voteLevels[i][0]]->actnum); + else + snprintf(levelinfo[i].str, + sizeof levelinfo[i].str, + "%s %s", + mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->zonttl); + } + + levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0'; + + // set up the gtc and gts + levelinfo[i].gtc = G_GetGametypeColor(g_voteLevels[i][1]); + if (i == 2 && g_voteLevels[i][1] != g_voteLevels[0][1]) + levelinfo[i].gts = gametypes[g_voteLevels[i][1]]->name; + else + levelinfo[i].gts = NULL; + } + + voteclient.loaded = true; + Automate_Run(AEV_VOTESTART); + + LUA_HUD_DestroyDrawList(luahuddrawlist_vote); + luahuddrawlist_vote = LUA_HUD_CreateDrawList(); +} + +// +// Y_EndVote +// +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(); + voteendtic = -1; +} + +#define UNLOAD(x) {if ((x) != NULL) {Patch_Free(x);} x = NULL;} + +// +// Y_UnloadVoteData +// +static void Y_UnloadVoteData(void) +{ + voteclient.loaded = false; + + if (dedicated) + return; + + UNLOAD(VoteScreen.widebgpatch); + UNLOAD(VoteScreen.bgpatch); + UNLOAD(VoteScreen.cursor[0]); + UNLOAD(VoteScreen.cursor[1]); + UNLOAD(VoteScreen.cursor[2]); + UNLOAD(VoteScreen.cursor[3]); + UNLOAD(VoteScreen.cursor[4]); + UNLOAD(VoteScreen.rubyicon); +} + +// +// Y_SetupVoteFinish +// +void Y_SetupVoteFinish(SINT8 pick, SINT8 level) +{ + if (!voteclient.loaded) + return; + + if (pick == VOTE_NOT_PICKED) // No other votes? We gotta get out of here, then! + { + Y_EndVote(); + Y_FollowIntermission(); + return; + } + + if (g_pickedVote == VOTE_NOT_PICKED) + { + INT32 i; + SINT8 votecompare = VOTE_NOT_PICKED; + INT32 endtype = 0; + + voteclient.rsynctime = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (Y_PlayerIDCanVote(i) && g_votes[i] == VOTE_NOT_PICKED) + g_votes[i] = (votemax*3)+((votemax > 1) ? (votemax - 1) : 0); + + if (g_votes[i] == VOTE_NOT_PICKED || endtype > 1) // Don't need to go on + continue; + + if (endtype == 2) + continue; + + if (votecompare == VOTE_NOT_PICKED) + { + votecompare = g_votes[i]; + endtype = 1; + } + else if (g_votes[i] != votecompare) + endtype = 2; + } + + if (endtype == 1) // Only one unique vote, so just end it immediately. + { + voteendtic = votetic + (5*TICRATE); + S_ChangeMusicInternal("voteeb", false); + S_ShowMusicCredit(0, 5*TICRATE, 0); + Y_VoteStops(pick, level); + } + else if (endtype == 0) // Might as well put this here, too. + { + Y_EndVote(); + Y_FollowIntermission(); + return; + } + else + { + S_ChangeMusicInternal("voteea", true); + S_ShowMusicCredit(0, 5*TICRATE, 0); + } + } + + deferredlevel = level; + g_pickedVote = pick; + votetimer = 0; +} diff --git a/src/k_vote.h b/src/k_vote.h new file mode 100644 index 000000000..20dcdf17f --- /dev/null +++ b/src/k_vote.h @@ -0,0 +1,74 @@ +// BLANKART +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by Kart Krew. +// Copyright (C) 2026 by Team BlanKart. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_vote.h +/// \brief MK online style voting screen + +#ifndef __K_VOTE_H__ +#define __K_VOTE_H__ + +#include "doomtype.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void Y_VoteDrawer(void); +void Y_VoteTicker(void); +void Y_StartVote(void); +void Y_EndVote(void); +void Y_SetupVoteFinish(SINT8 pick, SINT8 level); + +// Votescreen stuff +typedef struct +{ + boolean race; // non lua race patch replaced + boolean widerace; // non lua widescreen race patch replaced + boolean battle; // non lua battle patch replaced + boolean widebattle; // non lua widescreen battle patch replaced +} votereplace_t; + +// VEXTRN - Vote (V) Extra (EXT) Race (R) Normal (N - Normal sized patch) +// VEXTRW - Vote (V) Extra (EXT) Race (R) Normal (W - Wide patch) +// VEXTBN - Vote (V) Extra (EXT) Battle (B) Normal (N - Normal sized patch) +// VEXTBW - Vote (V) Extra (EXT) Battle (B) Normal (W - Wide patch) +typedef struct +{ + char Prefix[5]; // Race = INTSX, Battle = BTLSX + char luaPrefix[5]; // prefix for lua votescreens + + INT32 currentAnimFrame; // current animated background frame + INT32 timePerAnimFrame; // time for 1 frame in ticks, 2 by default + + INT32 foundLuaVoteFrames; // normal lua patch frames + INT32 foundLuaVoteWideFrames; // widescreen lua patch frames + + votereplace_t replaced; // checks which non lua patch has been replaced + + patch_t *bgpatch; // votebackground patch + patch_t *widebgpatch; // wide votebackground patch + + patch_t *cursor[5]; // cursor patches + patch_t *rubyicon; // encore ruby patch +} votescreen_t; +extern votescreen_t VoteScreen; + +boolean Y_PlayerIDCanVote(const UINT8 id); + +typedef enum +{ + VOTE_NOT_PICKED = -1, +} votetype_e; + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __K_VOTE_H__ diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 8e2477576..661bd9008 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -35,6 +35,7 @@ #include "k_hud.h" #include "h_timers.h" +#include "k_vote.h" #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!"); diff --git a/src/p_saveg.c b/src/p_saveg.c index 03eb9119b..0b056d781 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -46,6 +46,7 @@ #include "g_party.h" #include "k_waypoint.h" #include "k_itemlist.hpp" +#include "k_vote.h" #include diff --git a/src/p_setup.c b/src/p_setup.c index e8848937a..d1459a6dd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -75,6 +75,8 @@ #include "y_inter.h" +#include "k_vote.h" + // for MapLoad hook #include "lua_script.h" #include "lua_hook.h" diff --git a/src/y_inter.c b/src/y_inter.c index 1e421b565..8f4916112 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -47,10 +47,10 @@ #include "k_pwrlv.h" #include "console.h" // cons_menuhighlight #include "k_grandprix.h" -#include "k_bot.h" // cv_botcanvote #include "r_fps.h" // R_GetTimeFrac #include "k_hud.h" #include "k_itemlist.hpp" +#include "k_vote.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -117,55 +117,9 @@ static fixed_t xscroll = 0; intertype_t intertype = int_none; static huddrawlist_h luahuddrawlist_intermission; -static huddrawlist_h luahuddrawlist_vote; - -static void Y_FollowIntermission(void); static void Y_UnloadData(void); -// SRB2Kart: voting stuff -// Level images -typedef struct -{ - char str[62]; - UINT8 gtc; - const char *gts; - boolean encore; -} y_votelvlinfo; - -// Clientside & splitscreen player info. -typedef struct -{ - SINT8 selection; - UINT8 delay; -} y_voteplayer; - -typedef struct -{ - y_voteplayer playerinfo[4]; - UINT8 ranim; - UINT8 rtics; - UINT8 roffset; - UINT8 rsynctime; - UINT8 rendoff; - boolean loaded; -} y_voteclient; - -// votescreen stuff -votescreen_t VoteScreen = {0}; - -static y_votelvlinfo levelinfo[13]; -static y_voteclient voteclient; -static INT32 votetic; -static INT32 lastvotetic; -static INT32 voteendtic = -1; -static SINT8 votemax = 3; -static INT32 voterowmem = 0; -static boolean rowchange = false; -static boolean votenotyetpicked; - -static void Y_UnloadVoteData(void); - // // SRB2Kart - Y_CalculateMatchData and ancillary functions // @@ -1234,7 +1188,7 @@ void Y_EndIntermission(void) // // Y_FollowIntermission // -static void Y_FollowIntermission(void) +void Y_FollowIntermission(void) { // This handles whether to play a post-level cutscene, end the game, // or simply go to the next level. @@ -1250,1107 +1204,10 @@ static void Y_FollowIntermission(void) static void Y_UnloadData(void) { // unload the background patches - UNLOAD(VoteScreen.bgpatch); - UNLOAD(VoteScreen.widebgpatch); UNLOAD(bgtile); UNLOAD(interpic); } -// SRB2Kart: Voting! - -// -// Y_VoteScreenCheck -// -static void Y_VoteScreenCheck(void) -{ - strcpy(VoteScreen.Prefix, "INTS"); - - if (VoteScreen.luaPrefix[0] != 0) - strlcpy(VoteScreen.Prefix, VoteScreen.luaPrefix, sizeof(VoteScreen.Prefix)); - else if (gametype == GT_BATTLE) - strcpy(VoteScreen.Prefix, "BTLS"); - - VoteScreen.foundLuaVoteFrames = VoteScreen.foundLuaVoteWideFrames = 0; - VoteScreen.currentAnimFrame = 0; - - if (VoteScreen.timePerAnimFrame == 0) - VoteScreen.timePerAnimFrame = 2; - - INT32 i = 1; - - // check for lua vote background replacements - for (;;) - { - // Check if the lumps exist (checking for VEXTR(N|W)xx for race and VEXTRB(N|W)xx for battle) - boolean normalLumpExists = W_LumpExists(va("%sC%d", VoteScreen.Prefix, i)); - boolean wideLumpExists = W_LumpExists(va("%sW%d", VoteScreen.Prefix, i)); - - if (normalLumpExists || wideLumpExists) - { - if (normalLumpExists) - VoteScreen.foundLuaVoteFrames++; - - if (wideLumpExists) - VoteScreen.foundLuaVoteWideFrames++; - } - else // If we don't find at least frame 1 (e.g VEXTRN1), let's just stop looking - break; - - i++; - } - - // non lua vote background handling - boolean prefbattletype = ((g_voteLevels[0][1] & ~VOTEMODIFIER_ENCORE) == GT_BATTLE); - VoteScreen.widebgpatch = W_CachePatchName((prefbattletype ? "BATTLSCW" : "INTERSCW"), PU_PATCH); - VoteScreen.bgpatch = W_CachePatchName((prefbattletype ? "BATTLSCR" : "INTERSCR"), PU_PATCH); -} - -// -// Y_VoteBackgroundDrawer -// -// Determines which patch drawer to use for scaling -// -static void Y_VoteBackgroundDrawer(patch_t *patch) -{ - switch (cv_votebgscaling.value) - { - case 1: // adaptive - V_DrawAdaptiveScaledFullScreenPatch(patch); - break; - case 2: // vertical-fill - V_DrawVerticallyScaledFullScreenPatch(patch); - break; - case 3: // horizontal-fill - V_DrawHorizontallyScaledFullScreenPatch(patch); - break; - case 0: // vanilla - default: - V_DrawScaledPatch(((vid.width/2) / vid.dup) - (patch->width/2), - (vid.height / vid.dup) - patch->height, - V_SNAPTOTOP|V_SNAPTOLEFT, patch); - break; - } -} - -// -// Y_DrawLuaVoteScreenPatch -// -// Handles votebackgrounds set by "setVoteBackground" -// Aswell as animated patches -// -static void Y_DrawLuaVoteScreenPatch(boolean widePatch) -{ - INT32 nextframe = 0; - patch_t *votebg = NULL; - char tempPrefix[6]; - const INT32 tempfoundAnimLuaVoteFrames = ((widePatch ? VoteScreen.foundLuaVoteWideFrames : VoteScreen.foundLuaVoteFrames) - 1); - - strcpy(tempPrefix, va("%s%s", VoteScreen.Prefix, (widePatch ? "W" : "C"))); - - // Draw non animated patch - if (!tempfoundAnimLuaVoteFrames) - { - votebg = W_CachePatchName(va("%s1", tempPrefix), PU_PATCH); - Y_VoteBackgroundDrawer(votebg); - return; - } - - // Draw animated patch based on frame counter on vote screen - - // Just in case someone provides LESS widescreen frames than normal frames or vice versa, reset the frame counter to 0 - if (VoteScreen.currentAnimFrame > tempfoundAnimLuaVoteFrames) - VoteScreen.currentAnimFrame = 0; - - nextframe = (VoteScreen.currentAnimFrame + 1); - - votebg = W_CachePatchName(va("%s%d", tempPrefix, nextframe), PU_PATCH); - - Y_VoteBackgroundDrawer(votebg); - - if (renderisnewtic && (votetic % VoteScreen.timePerAnimFrame == 0) && !paused) - VoteScreen.currentAnimFrame = (nextframe > tempfoundAnimLuaVoteFrames) ? 0 : nextframe; -} - -// -// Y_DrawVoteScreenPatch -// - -static void Y_DrawVoteScreenPatch(void) -{ - patch_t *votebg = NULL; - const boolean widescreen = (vid.scaledwidth > 320); - - if (VoteScreen.foundLuaVoteWideFrames || VoteScreen.foundLuaVoteFrames) - { - Y_DrawLuaVoteScreenPatch(((widescreen && VoteScreen.foundLuaVoteWideFrames) || !VoteScreen.foundLuaVoteFrames)); - return; - } - - // non widescreen patch - votebg = VoteScreen.bgpatch; - - UINT8 prefgametype = (g_voteLevels[0][1] & ~VOTEMODIFIER_ENCORE); - const boolean widebgreplaced = (prefgametype == GT_BATTLE) ? VoteScreen.replaced.widebattle : VoteScreen.replaced.widerace; - const boolean bgreplaced = (prefgametype == GT_BATTLE) ? VoteScreen.replaced.battle : VoteScreen.replaced.race; - - // we check a bunch of stuff to always have a "valid" fallback - if ((widescreen && (widebgreplaced || !bgreplaced)) - || (!widescreen && (widebgreplaced && !bgreplaced))) - { - votebg = VoteScreen.widebgpatch; // widescreen patch - } - - Y_VoteBackgroundDrawer(votebg); -} - -static fixed_t Y_CalculatePicScale(fixed_t picscale, INT32 hypoti) -{ - picscale *= 10; - picscale /= (40-hypoti); - - if ( ((hypoti % 5) == 1) || ((hypoti % 5) == 4) || ((hypoti % 5) == 2) ) - { - if ((hypoti % 5) == 2) // scale DOWN the image - picscale -= (hypoti*16); - else - picscale += (hypoti*2); // scale UP the image - } - - return picscale; -} - -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 && !cv_botcanvote.value) - { - // Bots may only vote if the server allows it - return false; - } - - return true; -} - -// -// Y_VoteDrawer -// -// Draws the voting screen! -// -void Y_VoteDrawer(void) -{ - INT32 rowval, i, px, lvls, x, picdiff, y = 0, height = 0; - UINT8 selected[12]; - fixed_t rubyheight = 0; - fixed_t scale; - patch_t *pic; - mapnum_t mapnum; - fixed_t picwidth = 160; - - // CEP: scale by screen hypotenuse for extra voting rows - INT32 vidx = vid.scaledwidth; - INT32 vidy = ((vid.height) / vid.dup); - fixed_t hypotf = 0; - INT32 hypoti = 0; - - INT32 numplayers = 0; - boolean highplayers = false; - - // get the hypotenuse - hypoti = (vidx*vidx) + (vidy*vidy); - hypotf = FixedSqrt(hypoti); - - // convert the fixed_t back into an integer - hypoti = ((hypotf*10)/FRACUNIT); - - if ((voterowmem != cv_votemaxrows.value) && (votemax != cv_votemaxrows.value)) // voting rows were changed(?) - { - CONS_Printf(M_GetText("Max rows will be changed to %d on the next votescreen.\n"), cv_votemaxrows.value); // notify the players - voterowmem = cv_votemaxrows.value; - } - - // divisor for rescaling - INT32 hypotdiv = max(10, (40-hypoti)); - - // readjust the picscale - picwidth *= Y_CalculatePicScale(FRACUNIT, hypoti); - - if (rendermode == render_none) - return; - - if (votetic >= voteendtic && voteendtic != -1) - return; - - if (!voteclient.loaded) - return; - - { - static angle_t rubyfloattime = 0; - rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); - rubyfloattime += FixedMul(ANGLE_MAX/NEWTICRATE, renderdeltatics); - } - - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - - // decides which votebg to draw and draws it - Y_DrawVoteScreenPatch(); - - rowval = (votemax*3)+((votemax > 1) ? (votemax - 1) : 0); - for (i = 0; i < (rowval+1); i++) // First, we need to figure out the height of this thing... - { - UINT8 j; - selected[i] = 0; // Initialize - - for (j = 0; j <= splitscreen; j++) - { - if (voteclient.playerinfo[j].selection == i) - selected[i]++; - } - - if (selected[i]) - height += 25; // 50 - else - height += 25; - - if (i < rowval) - height += 5-splitscreen; - } - - height /= votemax; - - y = (200-height)/2; - picdiff = 80*( max(0, (votemax-1) ) ); // let's draw these in reverse order - lvls = -1; // shitty cheat - - for (i = 0; i < (rowval+1); i++) - { - const char *str; - UINT8 j, color; - - // CEP: hack hell - INT32 scaledpicdiff = ((picdiff*10)/hypotdiv); - INT32 fillscale = 800/hypotdiv; - INT32 hypotmod = (hypoti % 5); // hypotenuse mod 5, rescale the bounding box - INT32 hypotadd = ((hypotmod > 1) ? (hypotmod/4) : hypotmod); // how much do we add the bounding box by? - - // integer scaling makes me want to DIE - if (hypotmod == 3) - hypotadd += (1); - - scaledpicdiff *= 3; - scaledpicdiff /= 2; // 1.5 - - if (i == rowval) - { - str = "RANDOM"; - mapnum = NEXTMAP_INVALID; - } - else - { - str = levelinfo[i].str; - mapnum = g_voteLevels[i][0]; - } - scale = M_GetMapThumbnail(mapnum, &pic)/2; - - if (selected[i]) - { - 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 = VoteScreen.cursor[0]; - p = consoleplayer; - color = levelinfo[i].gtc; - colormap = NULL; - } - else - { - switch (j) - { - case 1: - thiscurs = VoteScreen.cursor[2]; - p = g_localplayers[1]; - break; - case 2: - thiscurs = VoteScreen.cursor[3]; - p = g_localplayers[2]; - break; - case 3: - thiscurs = VoteScreen.cursor[4]; - p = g_localplayers[3]; - break; - default: - thiscurs = VoteScreen.cursor[1]; - p = g_localplayers[0]; - break; - } - - color = skincolors[players[p].skincolor].ramp[7]; - colormap = R_GetTranslationColormap(TC_DEFAULT, players[p].skincolor, GTC_CACHE); - } - - if (g_votes[p] != -1 || !Y_PlayerIDCanVote(p)) - continue; - - handy += 3*(3-splitscreen) + (13*j); - V_DrawMappedPatch(BASEVIDWIDTH-(1600/hypotdiv)-scaledpicdiff, handy, V_SNAPTORIGHT, thiscurs, colormap); - - if (votetic % 10 < 4) - V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-sizeadd-scaledpicdiff, y-sizeadd, (fillscale + (sizeadd*2) + hypotadd), ((500/hypotdiv)+(sizeadd*2)) + hypotadd, 0|V_SNAPTORIGHT); - else - V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-sizeadd-scaledpicdiff, y-sizeadd, (fillscale + (sizeadd*2) + hypotadd), ((500/hypotdiv)+(sizeadd*2)) + hypotadd, color|V_SNAPTORIGHT); - - sizeadd--; - } - - if (!levelinfo[i].encore) - V_DrawFixedPatch((BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff)<= 16) - { - V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y+10, w+1, 2, V_SNAPTORIGHT|31); - V_DrawFill(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y, w, 11, V_SNAPTORIGHT|levelinfo[i].gtc); - V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff+w+1, y, 12, V_SNAPTORIGHT|31); - V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff+w, y, 11, V_SNAPTORIGHT|levelinfo[i].gtc); - V_DrawThinString(BASEVIDWIDTH-(1188/hypotdiv)-scaledpicdiff, y+1, V_SNAPTORIGHT, levelinfo[i].gts); - } - else // literally almost entirely covers the map icon, let's just mark it red or something - { - V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y, 8, V_SNAPTORIGHT|31); - V_DrawDiag(BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff, y, 6, V_SNAPTORIGHT|levelinfo[i].gtc); - } - } - - y += ((25*10) / (hypotdiv/2)); - lvls += 1; - - // screen height isn't doing us any favors - if (lvls >= 6) // loop over if we have an extra row - { - lvls = -2; - picdiff -= 90; // yes, this will overlap; no, I don't plan to change it - y = (200-height)/2; - y -= 5; - } - } - else - { - if (!levelinfo[i].encore) - V_DrawFixedPatch((BASEVIDWIDTH-(1200/hypotdiv)-scaledpicdiff)<= 6) - { - lvls = -2; - picdiff -= 90; - y = (200-height)/2; - y -= 5; - } - } - - y += 5-splitscreen; - lvls += 1; - - if (lvls >= 6) - { - lvls = -2; - picdiff -= 90; - y = (200-height)/2; - y -= 5; - } - } - - x = 20; - y = 10; - - for (px = 0; px < MAXPLAYERS; px++) - { - if (playeringame[px]) - { - numplayers++; - } - } - - highplayers = numplayers > 16; - - for (i = 0; i < MAXPLAYERS; i++) - { - INT32 bigaddx = 60; INT32 bigaddy = 30; - INT32 smalladdx = 42; INT32 smalladdy = 18; - INT32 smallfaceheight = 0; INT32 bigfaceheight = 9; - INT32 smallrectheight = 18; INT32 bigrectheight = 27; - INT32 smallrubyoffset = 4<= ((votemax*3)+((votemax > 1) ? (votemax - 1) : 0)) && (i != g_pickedVote || voteendtic == -1)) - mapnum = NEXTMAP_INVALID; // randomlvl - else - mapnum = g_voteLevels[g_votes[i]][0]; - - scale = M_GetMapThumbnail(mapnum, &pic)/8; - - if (!timer && i == voteclient.ranim) - { - V_DrawScaledPatch(x-18, y+(highplayers ? 2 : bigfaceheight), V_SNAPTOLEFT, VoteScreen.cursor[0]); - if (voteendtic != -1 && !(votetic % 4)) - V_DrawFill(x-1, y-1, 42, (highplayers ? smallrectheight : bigrectheight), 0|V_SNAPTOLEFT); - else - V_DrawFill(x-1, y-1, 42, (highplayers ? smallrectheight : bigrectheight), levelinfo[g_votes[i]].gtc|V_SNAPTOLEFT); - } - - if (highplayers) - { - V_SetClipRect(x<width, scale))<leftoffset, y+(highplayers ? smallfaceheight : bigfaceheight)+facerank->topoffset, V_SNAPTOLEFT, facerank, colormap); - } - - if (!splitscreen && i == consoleplayer) - { - UINT8 cursorframe = (votetic / 4) % 8; - patch_t *highlight = W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_CACHE); - V_DrawScaledPatch(x+24+highlight->leftoffset, y+(highplayers ? smallfaceheight : bigfaceheight)+highlight->topoffset, V_SNAPTOLEFT, highlight); - } - } - - y += highplayers ? smalladdy : bigaddy; - - if (y > BASEVIDHEIGHT-40) - { - x += highplayers ? smalladdx : bigaddx; - y = 10; - } - } - - if (timer) - { - INT32 hilicol, tickdown = (timer+1)/TICRATE; - - if (cons_menuhighlight.value) - hilicol = cons_menuhighlight.value; - else - hilicol = gametypes[gametype]->color; - - V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, - va("Vote ends in %d", tickdown)); - } - - lastvotetic = votetic; - - if (renderisnewtic) - { - LUA_HUD_ClearDrawList(luahuddrawlist_vote); - LUA_HookHUD(luahuddrawlist_vote, HUD_HOOK(vote)); - } - LUA_HUD_DrawList(luahuddrawlist_vote); -} - -// -// Y_VoteStop -// -// Vote screen's selection stops moving -// -static void Y_FinalizeVote(const SINT8 level) -{ - nextmap = g_voteLevels[level][0]; - deferencoremode = (levelinfo[level].encore); - - if (gametype != g_voteLevels[level][1]) - { - INT16 lastgametype = gametype; - G_SetGametype(g_voteLevels[level][1]); - D_GameTypeChanged(lastgametype); - forceresetplayers = true; - } -} - -SINT8 deferredlevel = 0; -static void Y_VoteStops(const SINT8 pick, const SINT8 level) -{ - Y_FinalizeVote(level); - - /*if (level == 4) - { - S_StartSound(NULL, sfx_noooo2); // gasp - } - else */ - 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 - } -} - -// -// Y_VoteTicker -// -// Vote screen thinking :eggthinking: -// -void Y_VoteTicker(void) -{ - INT32 i, j; - boolean everyone_voted; - - if (paused || P_AutoPause() || !voteclient.loaded) - return; - - LUA_HOOK(VoteThinker); - - votetic++; - - if (votetic == voteendtic) - { - Y_EndVote(); - Y_FollowIntermission(); - return; - } - - for (i = 0; i < MAXPLAYERS; i++) // Correct votes as early as possible, before they're processed by the game at all - { - if (!Y_PlayerIDCanVote(i)) - 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] = (votemax*3)+((votemax > 1) ? (votemax - 1) : 0); // Slow people get random - } - - 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) - { - S_ChangeMusicInternal("vote", true); - S_ShowMusicCredit(0, 5*TICRATE, 0); - } - - if (timer) - timer--; - - if (g_pickedVote != VOTE_NOT_PICKED) - { - timer = 0; - voteclient.rsynctime++; - - if (voteendtic == -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(); - Y_FollowIntermission(); - return; - } - - voteclient.rtics--; - - if (voteclient.rtics <= 0) - { - voteclient.roffset++; - voteclient.rtics = min(20, (3*voteclient.roffset/4)+5); - S_StartSound(NULL, sfx_kc39); - } - - if (voteclient.rendoff == 0 || voteclient.roffset < voteclient.rendoff) - voteclient.ranim = tempvotes[((g_pickedVote + voteclient.roffset) % numvotes)]; - - if (voteclient.roffset >= 20) - { - if (voteclient.rendoff == 0) - { - if (voteclient.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[((g_pickedVote + voteclient.roffset + i) % numvotes)] == g_pickedVote) - { - voteclient.rendoff = voteclient.roffset+i; - if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~ - voteclient.rendoff++; - S_ChangeMusicInternal("voteeb", false); - S_ShowMusicCredit(0, 5*TICRATE, 0); - break; - } - } - } - } - else if (voteclient.roffset >= voteclient.rendoff) - { - voteendtic = votetic + (3*TICRATE); - Y_VoteStops(g_pickedVote, deferredlevel); - } - } - } - else - voteclient.ranim = g_pickedVote; - } - else if (votenotyetpicked) - { - if (votetic < 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) - { - for (i = 0; i <= splitscreen; i++) - { - UINT8 p; - boolean pressed = false; - SINT8 votewrap = 0; - - if (votemax == 2) - votewrap = 3; - else if (votemax == 3) - votewrap = 7; - - switch (i) - { - 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; - } - - if (voteclient.playerinfo[i].delay) - voteclient.playerinfo[i].delay--; - - if (Y_PlayerIDCanVote(p) - && !voteclient.playerinfo[i].delay - && g_pickedVote == VOTE_NOT_PICKED && g_votes[p] == VOTE_NOT_PICKED) - { - if (G_PlayerInputDown(i, gc_aimforward, false, DEADZONE_Y)) - { - voteclient.playerinfo[i].selection--; - pressed = true; - } - - if (G_PlayerInputDown(i, gc_aimbackward, false, DEADZONE_Y) && pressed == false) - { - voteclient.playerinfo[i].selection++; - pressed = true; - } - - if (votemax > 1) // only allow side-movements for multi-row selections - { - // HORRIBLE hack, my GOD - if (G_PlayerInputDown(i, gc_turnright, false, DEADZONE_X) && !pressed) // move right - { - if (voteclient.playerinfo[i].selection <= votewrap) - voteclient.playerinfo[i].selection += 4; - else - voteclient.playerinfo[i].selection -= ((votemax-1)*4); - pressed = true; - } - - if (G_PlayerInputDown(i, gc_turnleft, false, DEADZONE_X) && !pressed) // move left - { - if (voteclient.playerinfo[i].selection > 3) - voteclient.playerinfo[i].selection -= 4; - else - voteclient.playerinfo[i].selection += ((votemax-1)*4); - pressed = true; - } - } - - if (voteclient.playerinfo[i].selection < 0) - voteclient.playerinfo[i].selection = ((votemax*3)+((votemax > 1) ? (votemax-1) : 0) ); - if (voteclient.playerinfo[i].selection > ((votemax*3)+((votemax > 1) ? (votemax-1) : 0)) ) - voteclient.playerinfo[i].selection = 0; - - if (G_PlayerInputDown(i, gc_accelerate, false, DEADZONE_BUTTON) && pressed == false) - { - D_ModifyClientVote(g_localplayers[i], voteclient.playerinfo[i].selection); - pressed = true; - } - } - - if (pressed) - { - S_StartSound(NULL, sfx_kc4a); - voteclient.playerinfo[i].delay = NEWTICRATE/7; - } - } - } - - if (server) - { - everyone_voted = true;/* the default condition */ - - if (timer == 0) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (Y_PlayerIDCanVote(i) && g_votes[i] == VOTE_NOT_PICKED) - g_votes[i] = (votemax*3)+((votemax > 1) ? (votemax-1) : 0); - } - } - else - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!Y_PlayerIDCanVote(i)) - { - continue; - } - - if (server && players[i].bot && g_votes[i] == VOTE_NOT_PICKED) - { - if (( M_RandomFixed() % 100 ) == 0) - { -#define VOTEROWSADDSONE ((cv_votemaxrows.value*3) + 1 + ((cv_votemaxrows.value > 1) ? (cv_votemaxrows.value - 1) : 0)) - // bots vote randomly - INT32 rng = M_RandomKey(VOTEROWSADDSONE); - for (j = 0; j < VOTEROWSADDSONE; j++) - { - rng++; - - if (rng >= VOTEROWSADDSONE || rng < 0) - { - rng = 0; - } - } -#undef VOTEROWSADDSONE - - D_ModifyClientVote(i, rng); - } - } - - if (g_votes[i] == VOTE_NOT_PICKED) - { - everyone_voted = false; - } - } - } - - if (everyone_voted) - { - timer = 0; - if (voteendtic == -1) - { - votenotyetpicked = false;/* don't pick vote twice */ - D_PickVote(); - } - } - } - } -} - -// -// Y_StartVote -// -// MK online style voting screen, appears after intermission -// - -static void Y_InitVoteDrawing(void) -{ - if (dedicated) - return; - - // setup the background patches - Y_VoteScreenCheck(); - - VoteScreen.cursor[0] = W_CachePatchName("M_CURSOR", PU_PATCH); - VoteScreen.cursor[1] = W_CachePatchName("P1CURSOR", PU_PATCH); - VoteScreen.cursor[2] = W_CachePatchName("P2CURSOR", PU_PATCH); - VoteScreen.cursor[3] = W_CachePatchName("P3CURSOR", PU_PATCH); - VoteScreen.cursor[4] = W_CachePatchName("P4CURSOR", PU_PATCH); - - VoteScreen.rubyicon = W_CachePatchName("RUBYICON", PU_PATCH); -} - -void Y_StartVote(void) -{ - INT32 i = 0; - INT32 rowval = (cv_votemaxrows.value*3)+((cv_votemaxrows.value > 1) ? (cv_votemaxrows.value - 1) : 0); - - votemax = cv_votemaxrows.value; // can we please avoid SIGSEGVs - voterowmem = cv_votemaxrows.value; // this is just for the notif system - rowchange = false; - - votetic = -1; - -#ifdef PARANOIA - if (voteendtic != -1) - I_Error("voteendtic is dirty"); -#endif - - // cache vote patches - Y_InitVoteDrawing(); - - timer = cv_votetime.value*TICRATE; - g_pickedVote = VOTE_NOT_PICKED; - - votenotyetpicked = true; - - for (i = 0; i < 3; i++) - { - voteclient.playerinfo[i].selection = 0; - voteclient.playerinfo[i].delay = 0; - } - - voteclient.ranim = 0; - voteclient.rtics = 1; - voteclient.roffset = 0; - voteclient.rsynctime = 0; - voteclient.rendoff = 0; - - for (i = 0; i < MAXPLAYERS; i++) - g_votes[i] = VOTE_NOT_PICKED; - - for (i = 0; i < (rowval + 1); i++) - { - // set up the encore - levelinfo[i].encore = (g_voteLevels[i][1] & VOTEMODIFIER_ENCORE) ? true : false; - g_voteLevels[i][1] &= ~VOTEMODIFIER_ENCORE; - - // 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(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s %s", - mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->actnum); - else - snprintf(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s", - mapheaderinfo[g_voteLevels[i][0]]->lvlttl); - } - else - { - if (mapheaderinfo[g_voteLevels[i][0]]->actnum[0]) - snprintf(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s %s %s", - mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->zonttl, mapheaderinfo[g_voteLevels[i][0]]->actnum); - else - snprintf(levelinfo[i].str, - sizeof levelinfo[i].str, - "%s %s", - mapheaderinfo[g_voteLevels[i][0]]->lvlttl, mapheaderinfo[g_voteLevels[i][0]]->zonttl); - } - - levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0'; - - // set up the gtc and gts - levelinfo[i].gtc = G_GetGametypeColor(g_voteLevels[i][1]); - if (i == 2 && g_voteLevels[i][1] != g_voteLevels[0][1]) - levelinfo[i].gts = gametypes[g_voteLevels[i][1]]->name; - else - levelinfo[i].gts = NULL; - } - - voteclient.loaded = true; - Automate_Run(AEV_VOTESTART); - - LUA_HUD_DestroyDrawList(luahuddrawlist_vote); - luahuddrawlist_vote = LUA_HUD_CreateDrawList(); -} - -// -// Y_EndVote -// -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(); - voteendtic = -1; -} - -// -// Y_UnloadVoteData -// -static void Y_UnloadVoteData(void) -{ - voteclient.loaded = false; - - if (dedicated) - return; - - UNLOAD(VoteScreen.widebgpatch); - UNLOAD(VoteScreen.bgpatch); - UNLOAD(VoteScreen.cursor[0]); - UNLOAD(VoteScreen.cursor[1]); - UNLOAD(VoteScreen.cursor[2]); - UNLOAD(VoteScreen.cursor[3]); - UNLOAD(VoteScreen.cursor[4]); - UNLOAD(VoteScreen.rubyicon); -} - -// -// Y_SetupVoteFinish -// -void Y_SetupVoteFinish(SINT8 pick, SINT8 level) -{ - if (!voteclient.loaded) - return; - - if (pick == VOTE_NOT_PICKED) // No other votes? We gotta get out of here, then! - { - Y_EndVote(); - Y_FollowIntermission(); - return; - } - - if (g_pickedVote == VOTE_NOT_PICKED) - { - INT32 i; - SINT8 votecompare = VOTE_NOT_PICKED; - INT32 endtype = 0; - - voteclient.rsynctime = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (Y_PlayerIDCanVote(i) && g_votes[i] == VOTE_NOT_PICKED) - g_votes[i] = (votemax*3)+((votemax > 1) ? (votemax - 1) : 0); - - if (g_votes[i] == VOTE_NOT_PICKED || endtype > 1) // Don't need to go on - continue; - - if (endtype == 2) - continue; - - if (votecompare == VOTE_NOT_PICKED) - { - votecompare = g_votes[i]; - endtype = 1; - } - else if (g_votes[i] != votecompare) - endtype = 2; - } - - if (endtype == 1) // Only one unique vote, so just end it immediately. - { - voteendtic = votetic + (5*TICRATE); - S_ChangeMusicInternal("voteeb", false); - S_ShowMusicCredit(0, 5*TICRATE, 0); - Y_VoteStops(pick, level); - } - else if (endtype == 0) // Might as well put this here, too. - { - Y_EndVote(); - Y_FollowIntermission(); - return; - } - else - { - S_ChangeMusicInternal("voteea", true); - S_ShowMusicCredit(0, 5*TICRATE, 0); - } - } - - deferredlevel = level; - g_pickedVote = pick; - timer = 0; -} - #undef ITEMLIST_PLAYER_YOFFSET #undef ITEMLIST_SCROLLSPEED #undef ITEMLIST_SCROLLDELAY diff --git a/src/y_inter.h b/src/y_inter.h index 2e2042592..7471c2410 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -30,12 +30,6 @@ void Y_CleanupScreenBuffer(void); void Y_DetermineIntermissionType(void); -void Y_VoteDrawer(void); -void Y_VoteTicker(void); -void Y_StartVote(void); -void Y_EndVote(void); -void Y_SetupVoteFinish(SINT8 pick, SINT8 level); - typedef enum { int_none, @@ -46,46 +40,7 @@ typedef enum extern intertype_t intertype; -// Votescreen stuff -typedef struct -{ - boolean race; // non lua race patch replaced - boolean widerace; // non lua widescreen race patch replaced - boolean battle; // non lua battle patch replaced - boolean widebattle; // non lua widescreen battle patch replaced -} votereplace_t; - -// VEXTRN - Vote (V) Extra (EXT) Race (R) Normal (N - Normal sized patch) -// VEXTRW - Vote (V) Extra (EXT) Race (R) Normal (W - Wide patch) -// VEXTBN - Vote (V) Extra (EXT) Battle (B) Normal (N - Normal sized patch) -// VEXTBW - Vote (V) Extra (EXT) Battle (B) Normal (W - Wide patch) -typedef struct -{ - char Prefix[5]; // Race = INTSX, Battle = BTLSX - char luaPrefix[5]; // prefix for lua votescreens - - INT32 currentAnimFrame; // current animated background frame - INT32 timePerAnimFrame; // time for 1 frame in ticks, 2 by default - - INT32 foundLuaVoteFrames; // normal lua patch frames - INT32 foundLuaVoteWideFrames; // widescreen lua patch frames - - votereplace_t replaced; // checks which non lua patch has been replaced - - patch_t *bgpatch; // votebackground patch - patch_t *widebgpatch; // wide votebackground patch - - patch_t *cursor[5]; // cursor patches - patch_t *rubyicon; // encore ruby patch -} votescreen_t; -extern votescreen_t VoteScreen; - -boolean Y_PlayerIDCanVote(const UINT8 id); - -typedef enum -{ - VOTE_NOT_PICKED = -1, -} votetype_e; +void Y_FollowIntermission(void); #ifdef __cplusplus } // extern "C"