Files
SRB2Kart-Saturn/src/y_inter.c
T
alufolie91 cc6437285f check for missized rejectmatrix´s on load, pad them if needed
this is what dsda-doom/prboom does
ensures we wont crash from oob reads and makes us print a warning
2026-06-13 13:00:00 +02:00

1636 lines
41 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2004-2018 by Sonic Team Junior.
//
// 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 y_inter.c
/// \brief Tally screens, or "Intermissions" as they were formally called in Doom
#include "d_netcmd.h"
#include "doomdef.h"
#include "doomstat.h"
#include "d_main.h"
#include "f_finale.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "i_net.h"
#include "i_video.h"
#include "p_tick.h"
#include "r_defs.h"
#include "r_things.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 "m_misc.h"
#include "i_system.h"
#include "p_setup.h"
#include "r_local.h"
#include "p_local.h"
#include "m_cond.h" // condition sets
#include "m_random.h" // M_RandomKey
#include "g_input.h" // PLAYER1INPUTDOWN
#include "k_kart.h" // colortranslations
#include "k_hud.h"
#include "console.h" // cons_menuhighlight
#include "lua_hook.h" // IntermissionThinker hook
#include "lua_hud.h" // intermission hud hook
#include "r_fps.h" // R_GetTimeFrac
#ifdef HWRENDER
#include "hardware/hw_main.h"
#endif
static y_data_t data;
// graphics
static INT32 timer;
static INT32 intertic;
static INT32 endtic = -1;
static INT32 sorttic = -1;
intertype_t intertype = int_none;
static huddrawlist_h luahuddrawlist_intermission = NULL;
static huddrawlist_h luahuddrawlist_vote = NULL;
static void Y_FollowIntermission(void);
// SRB2Kart: voting stuff
// Level images
typedef struct
{
char str[62];
UINT8 gtc;
const char *gts;
patch_t *pic;
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 = {};
static y_votelvlinfo levelinfo[5];
static y_voteclient voteclient;
static INT32 votetic;
static INT32 voteendtic = -1;
//
// SRB2Kart - Y_CalculateMatchData and ancillary functions
//
static void Y_CompareRace(INT32 i)
{
UINT32 val = ((players[i].pflags & PF_TIMEOVER || players[i].realtime == UINT32_MAX)
? (UINT32_MAX-1) : players[i].realtime);
if (!(val < data.val[data.numplayers]))
return;
data.val[data.numplayers] = val;
data.num[data.numplayers] = i;
}
static void Y_CompareBattle(INT32 i)
{
UINT32 val = ((players[i].pflags & PF_TIMEOVER)
? (UINT32_MAX-1) : players[i].marescore);
if (!(data.val[data.numplayers] == UINT32_MAX
|| (!(players[i].pflags & PF_TIMEOVER) && val > data.val[data.numplayers])))
return;
data.val[data.numplayers] = val;
data.num[data.numplayers] = i;
}
static void Y_CompareRank(INT32 i)
{
UINT8 increase = ((data.increase[i] == UINT8_MAX) ? 0 : data.increase[i]);
if (!(data.val[data.numplayers] == UINT32_MAX || (players[i].score - increase) > data.val[data.numplayers]))
return;
data.val[data.numplayers] = (players[i].score - increase);
data.num[data.numplayers] = i;
}
static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{
INT32 i, j;
boolean completed[MAXPLAYERS];
INT32 numplayersingame = 0;
// Initialize variables
if (rankingsmode > 1)
;
else if ((data.rankingsmode = (boolean)rankingsmode))
{
sprintf(data.levelstring, "* Total Rankings *");
data.encore = false;
}
else
{
// set up the levelstring
if (mapheaderinfo[prevmap]->levelflags & LF_NOZONE)
{
if (mapheaderinfo[prevmap]->actnum[0])
snprintf(data.levelstring,
sizeof data.levelstring,
"* %s %s *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.levelstring,
sizeof data.levelstring,
"* %s *",
mapheaderinfo[prevmap]->lvlttl);
}
else
{
const char *zonttl = (mapheaderinfo[prevmap]->zonttl[0] ? mapheaderinfo[prevmap]->zonttl : "Zone");
if (mapheaderinfo[prevmap]->actnum[0])
snprintf(data.levelstring,
sizeof data.levelstring,
"* %s %s %s *",
mapheaderinfo[prevmap]->lvlttl, zonttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.levelstring,
sizeof data.levelstring,
"* %s %s *",
mapheaderinfo[prevmap]->lvlttl, zonttl);
}
data.levelstring[sizeof data.levelstring - 1] = '\0';
data.encore = encoremode;
memset(data.jitter, 0, sizeof (data.jitter));
}
for (i = 0; i < MAXPLAYERS; i++)
{
data.val[i] = UINT32_MAX;
if (!playeringame[i] || players[i].spectator)
{
data.increase[i] = UINT8_MAX;
continue;
}
if (!rankingsmode)
data.increase[i] = UINT8_MAX;
numplayersingame++;
}
memset(data.color, 0, sizeof (data.color));
memset(data.character, 0, sizeof (data.character));
memset(completed, 0, sizeof (completed));
data.numplayers = 0;
for (j = 0; j < numplayersingame; j++)
{
INT32 nump = ((G_RaceGametype() && nospectategrief > 0) ? nospectategrief : numplayersingame);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || completed[i])
continue;
comparison(i);
}
i = data.num[data.numplayers];
completed[i] = true;
data.color[data.numplayers] = &players[i].skincolor;
data.character[data.numplayers] = &players[i].skin;
data.name[data.numplayers] = player_names[i];
if (data.numplayers && (data.val[data.numplayers] == data.val[data.numplayers-1]))
data.pos[data.numplayers] = data.pos[data.numplayers-1];
else
data.pos[data.numplayers] = data.numplayers+1;
if (!rankingsmode && !(players[i].pflags & PF_TIMEOVER) && (data.pos[data.numplayers] < nump))
{
data.increase[i] = nump - data.pos[data.numplayers];
players[i].score += data.increase[i];
}
if (demo.recording && !rankingsmode)
G_WriteStanding(
data.pos[data.numplayers],
data.name[data.numplayers],
*data.character[data.numplayers],
*data.color[data.numplayers],
data.val[data.numplayers]
);
data.numplayers++;
}
}
// Y_PlayerStandingsDrawer
//
// Handles drawing the center-of-screen player standings.
//
static void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 x, INT32 hilicol)
{
if (standings->numplayers == 0)
{
return;
}
INT32 i;
#define NUMFORNEWCOLUMN 8
INT32 y = 41, gutter = ((standings->numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2));
INT32 dupadjust = cv_betainterscreen.value ? 314 : vid.scaledwidth, duptweak = cv_betainterscreen.value ? -3 : (dupadjust - BASEVIDWIDTH)/2;
const char *timeheader;
if (standings->rankingsmode)
timeheader = "RANK";
else
timeheader = (intertype == int_race ? "TIME" : "SCORE");
// draw the level name
V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12, 0, standings->levelstring);
V_DrawFill((x-3) - duptweak, 34, dupadjust-2, 1, 0);
if (standings->encore)
V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12-8, hilicol, "ENCORE MODE");
if (standings->numplayers > NUMFORNEWCOLUMN)
{
V_DrawFill(x+156, 24, 1, 158, 0);
V_DrawFill((x-3) - duptweak, 182, dupadjust-2, 1, 0);
V_DrawCenteredString(x+6+(BASEVIDWIDTH/2), 24, hilicol, "#");
V_DrawString(x+36+(BASEVIDWIDTH/2), 24, hilicol, "NAME");
V_DrawRightAlignedString(x+152, 24, hilicol, timeheader);
}
V_DrawCenteredString(x+6, 24, hilicol, "#");
V_DrawString(x+36, 24, hilicol, "NAME");
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+152, 24, hilicol, timeheader);
boolean (*_isHighlightedPlayer)(const player_t *) = (demo.playback ? P_IsDisplayPlayer : P_IsLocalPlayer);
for (i = 0; i < standings->numplayers; i++)
{
const UINT8 pnum = standings->num[i];
player_t *player = &players[pnum];
if (pnum == MAXPLAYERS)
;
else if (!playeringame[pnum] || player->spectator)
standings->num[i] = MAXPLAYERS; // this should be the only field setting in this function
else
{
char strtime[MAXPLAYERNAME+1];
const boolean whiteplayer = _isHighlightedPlayer(player);
const INT32 philicol = (whiteplayer ? V_SkinColorToHighlightcolor(player->skincolor) : 0);
// Apply the jitter offset (later reversed)
if (standings->jitter[pnum] > 0)
y--;
V_DrawCenteredString(x+6, y, 0, va("%d", standings->pos[i]));
// localskins are amazing
if (*standings->color[i] != SKINCOLOR_NONE)
{
UINT8 *colormap = R_GetTranslationColormap(*standings->character[i], *standings->color[i], GTC_CACHE);
INT32 skinnum = (player->localskin ? (player->localskin - 1) : *standings->character[i]);
patch_t *faceprefix = K_GetFacePrefix(player, skinnum);
if (K_UseHighResPortraits())
V_DrawSmallMappedPatch(x + 16, y - 4, 0, faceprefix, colormap);
else
V_DrawMappedPatch(x + 16, y - 4, 0, faceprefix, colormap);
}
if (pnum == whiteplayer)
{
UINT8 cursorframe = (intertic / 4) % 8;
V_DrawScaledPatch(x+16, y-4, 0, (patch_t *)W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_PATCH));
}
STRBUFCPY(strtime, standings->name[i]);
if (standings->numplayers > NUMFORNEWCOLUMN)
V_DrawThinString(x+36, y-1, philicol|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime);
else
V_DrawString(x+36, y, philicol|V_ALLOWLOWERCASE, strtime);
if (standings->rankingsmode)
{
if (standings->increase[pnum] != UINT8_MAX)
{
if (standings->increase[pnum] > 9)
snprintf(strtime, sizeof strtime, "(+%02d)", standings->increase[pnum]);
else
snprintf(strtime, sizeof strtime, "(+ %d)", standings->increase[pnum]);
if (standings->numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+135+gutter, y-1, V_6WIDTHSPACE, strtime);
else
V_DrawRightAlignedString(x+120+gutter, y, 0, strtime);
}
snprintf(strtime, sizeof strtime, "%d", standings->val[i]);
if (standings->numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime);
else
V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
}
else
{
if (standings->val[i] == (UINT32_MAX-1))
V_DrawRightAlignedThinString(x+152+gutter, y-1, (standings->numplayers > NUMFORNEWCOLUMN ? V_6WIDTHSPACE : 0), "NO CONTEST.");
else
{
if (intertype == int_race)
{
snprintf(strtime, sizeof strtime, "%i'%02i\"%02i", G_TicsToMinutes(standings->val[i], true),
G_TicsToSeconds(standings->val[i]), G_TicsToCentiseconds(standings->val[i]));
strtime[sizeof strtime - 1] = '\0';
if (standings->numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime);
else
V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
}
else
{
if (standings->numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, va("%i", standings->val[i]));
else
V_DrawRightAlignedString(x+152+gutter, y, 0, va("%i", standings->val[i]));
}
}
}
if (standings->jitter[pnum] > 0)
y++;
}
y += 18;
if (i == NUMFORNEWCOLUMN-1)
{
y = 41;
x += BASEVIDWIDTH/2;
}
#undef NUMFORNEWCOLUMN
}
}
//
// Y_IntermissionDrawer
//
// Called by D_Display. Nothing is modified here; all it does is draw. (SRB2Kart: er, about that...)
// Neat concept, huh?
//
void Y_IntermissionDrawer(void)
{
INT32 x = 4, hilicol = V_YELLOWMAP; // fallback
if (intertype == int_none || rendermode == render_none)
return;
if (cv_betainterscreen.value == 1
|| !intermissionbginit
#ifdef HWRENDER
|| (rendermode == render_opengl && cv_glscreentextures.value != 2) // use the neato kart bg for intermission on disabled screen textures
#endif
)
V_DrawPatchFill(srb2back); // use the neato kart bg for intermission on disabled screen textures
else
{
if (rendermode == render_soft)
VID_BlitLinearScreen(vid.screens[1], vid.screens[0], vid.width, vid.height, vid.width, vid.width);
#ifdef HWRENDER
else if (rendermode == render_opengl)
HWR_DrawIntermissionBG();
#endif
}
// Fade everything out
V_DrawFadeScreen(0xFF00, 22);
if (cons_menuhighlight.value)
hilicol = cons_menuhighlight.value;
else if (modeattacking)
hilicol = V_ORANGEMAP;
else
hilicol = ((intertype == int_race) ? V_SKYMAP : V_REDMAP);
if (sorttic != -1 && intertic >= sorttic && !demo.playback)
{
// yes this may actually overflow
INT64 count = (intertic - sorttic);
if (count < 8)
x -= ((((count<<FRACBITS) + R_GetTimeFrac(RTF_INTER)) * vid.width)>>FRACBITS) / (8 * vid.dup);
else if (count < 16)
x += (((((16 - count)<<FRACBITS) - R_GetTimeFrac(RTF_INTER)) * vid.width)>>FRACBITS) / (8 * vid.dup);
}
if (intertype == int_race || intertype == int_match)
Y_PlayerStandingsDrawer(&data, x, hilicol);
if (timer)
{
char *string;
INT32 tickdown = (timer+1)/TICRATE;
if (multiplayer && demo.playback)
string = va("Replay ends in %d", tickdown);
else
string = va("%s starts in %d", cv_advancemap.string, tickdown);
V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, string);
}
if ((demo.recording || demo.savemode == DSM_SAVED) && !demo.playback)
{
switch (demo.savemode)
{
case DSM_NOTSAVING:
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Look Backward: Save replay");
break;
case DSM_SAVED:
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Replay saved!");
break;
case DSM_TITLEENTRY:
ST_DrawDemoTitleEntry();
break;
default: // Don't render any text here
break;
}
}
if (renderisnewtic)
{
LUA_HUD_ClearDrawList(luahuddrawlist_intermission);
LUA_HUDHOOK(intermission, luahuddrawlist_intermission);
}
LUA_HUD_DrawList(luahuddrawlist_intermission);
}
//
// Y_Ticker
//
// Manages fake score tally for single player end of act, and decides when intermission is over.
//
void Y_Ticker(void)
{
if (intertype == int_none)
return;
if (demo.recording)
{
INT32 axis = JoyAxis(AXISLOOKBACK, 1);
if (demo.savemode == DSM_NOTSAVING && (InputDown(gc_lookback, 1) || (cv_usejoystick[0].value && axis > 0)))
demo.savemode = DSM_TITLEENTRY;
if (demo.savemode == DSM_WILLSAVE || demo.savemode == DSM_WILLAUTOSAVE)
G_SaveDemo();
}
// Check for pause or menu up in single player
if (paused || P_AutoPause())
return;
LUA_HOOK(IntermissionThinker);
intertic++;
// multiplayer uses timer (based on cv_inttime)
if (timer)
{
if (!--timer)
{
Y_EndIntermission();
Y_FollowIntermission();
return;
}
}
// single player is hardcoded to go away after awhile
else if (intertic == endtic)
{
Y_EndIntermission();
Y_FollowIntermission();
return;
}
if (intertic < TICRATE || endtic != -1)
{
return;
}
if (data.rankingsmode && intertic & 1)
{
memset(data.jitter, 0, sizeof (data.jitter));
return;
}
if (intertype == int_race || intertype == int_match)
{
if (netgame || multiplayer)
{
if (sorttic == -1)
sorttic = intertic + max((cv_inttime.value/2)-2, 2)*TICRATE; // 8 second pause after match results
else if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays
{
if (!data.rankingsmode && (intertic >= sorttic + 8))
Y_CalculateMatchData(1, Y_CompareRank);
if (data.rankingsmode && intertic > sorttic+16+(2*TICRATE))
{
INT32 q = 0, r = 0;
boolean kaching = true;
for (q = 0; q < data.numplayers; q++)
{
if (data.num[q] == MAXPLAYERS
|| !data.increase[data.num[q]]
|| data.increase[data.num[q]] == UINT8_MAX)
continue;
r++;
data.jitter[data.num[q]] = 1;
if (--data.increase[data.num[q]])
kaching = false;
}
if (r)
{
S_StartSound(NULL, (kaching ? sfx_chchng : sfx_ptally));
Y_CalculateMatchData(2, Y_CompareRank);
}
else
endtic = intertic + 3*TICRATE; // 3 second pause after end of tally
}
}
}
else
endtic = intertic + 8*TICRATE; // 8 second pause after end of tally
}
}
//
// Y_UpdateRecordReplays
//
// Update replay files/data, etc. for Record Attack
// See G_SetNightsRecords for NiGHTS Attack.
//
static void Y_UpdateRecordReplays(void)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
char lastdemo[256], bestdemo[256];
UINT8 earnedEmblems;
// Record new best time
if (!mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1);
if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((mainrecords[gamemap-1]->lap == 0) || (players[consoleplayer].laptime[LAP_BEST] < mainrecords[gamemap-1]->lap))
mainrecords[gamemap-1]->lap = players[consoleplayer].laptime[LAP_BEST];
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].laptime[LAP_BEST]);
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, cv_chooseskin.string);
if (FIL_FileExists(lastdemo))
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, cv_chooseskin.string);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-lap-best.lmp", gpath, cv_chooseskin.string);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & (1<<1))
{ // Better lap time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD LAP!"), M_GetText("Saved replay as"), bestdemo);
}
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
}
free(gpath);
// Check emblems when level data is updated
if ((earnedEmblems = M_CheckLevelEmblems()))
CONS_Printf(M_GetText("\x82" "Earned %hu medal%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
if (M_UpdateUnlockablesAndExtraEmblems(false))
S_StartSound(NULL, sfx_ncitem);
// SRB2Kart - save here so you NEVER lose your earned times/medals.
G_SaveGameData(false);
// Update timeattack menu's replay availability.
Nextmap_OnChange();
}
//
// Y_StartIntermission
//
// Called by G_DoCompleted. Sets up data for intermission drawer/ticker.
//
void Y_StartIntermission(void)
{
intertic = -1;
#ifdef PARANOIA
if (endtic != -1)
I_Error("endtic is dirty");
#endif
if (!multiplayer)
{
timer = 0;
/* // srb2kart: time attack tally is UGLY rn
if (modeattacking)
intertype = int_timeattack;
else
*/
intertype = int_race;
}
else
{
if (cv_inttime.value == 0 && gametype == GT_COOP)
timer = 0;
else if (demo.playback) // Override inttime (which is pulled from the replay anyway
timer = 10*TICRATE;
else
{
timer = cv_inttime.value*TICRATE;
if (!timer)
timer = 1;
}
if (gametype == GT_MATCH)
intertype = int_match;
else
intertype = int_race;
}
// This should always exist, but just in case...
if (!mapheaderinfo[prevmap])
P_AllocMapHeader(prevmap);
switch (intertype)
{
case int_match:
{
// Calculate who won
Y_CalculateMatchData(0, Y_CompareBattle);
if (cv_inttime.value > 0)
S_ChangeMusicInternal("racent", true); // loop it
break;
}
case int_race: // (time-only race)
{
if (!multiplayer && !demo.playback) // remove this once we have a proper time attack screen
{
// Update visitation flags
mapvisited[gamemap-1] |= MV_BEATEN;
if (ALL7EMERALDS(emeralds))
mapvisited[gamemap-1] |= MV_ALLEMERALDS;
if (modeattacking == ATTACKING_RECORD)
Y_UpdateRecordReplays();
}
// Calculate who won
Y_CalculateMatchData(0, Y_CompareRace);
break;
}
case int_none:
default:
break;
}
if (rendermode != render_none)
{
LUA_HUD_DestroyDrawList(luahuddrawlist_intermission);
luahuddrawlist_intermission = LUA_HUD_CreateDrawList();
}
}
// ======
//
// Y_EndIntermission
//
void Y_EndIntermission(void)
{
endtic = -1;
sorttic = -1;
intertype = int_none;
intermissionbginit = false; // not sure if this is the right place to reset, but i guess it works for now....
}
//
// Y_FollowIntermission
//
static void Y_FollowIntermission(void)
{
// This handles whether to play a post-level cutscene, end the game,
// or simply go to the next level.
// No need to duplicate the code here!
G_AfterIntermission();
}
// SRB2Kart: Voting!
//
// Y_VoteScreenCheck
//
void Y_VoteScreenCheck(void)
{
strcpy(VoteScreen.Prefix, "INTS");
if (VoteScreen.luaPrefix[0] != 0)
strlcpy(VoteScreen.Prefix, VoteScreen.luaPrefix, sizeof(VoteScreen.Prefix));
else if (G_BattleGametype())
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 = ((votelevels[0][1] & ~0x80) == GT_MATCH);
VoteScreen.widebgpatch = (patch_t *)W_CachePatchName((prefbattletype ? "BATTLSCW" : "INTERSCW"), PU_PATCH_LOWPRIORITY);
VoteScreen.bgpatch = (patch_t *)W_CachePatchName((prefbattletype ? "BATTLSCR" : "INTERSCR"), PU_PATCH_LOWPRIORITY);
}
//
// 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 = (patch_t *)W_CachePatchName(va("%s1", tempPrefix), PU_PATCH_LOWPRIORITY);
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 = (patch_t *)W_CachePatchName(va("%s%d", tempPrefix, nextframe), PU_PATCH_LOWPRIORITY);
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 = (votelevels[0][1] & ~0x80);
const boolean widebgreplaced = (prefgametype == GT_MATCH) ? VoteScreen.replaced.widebattle : VoteScreen.replaced.widerace;
const boolean bgreplaced = (prefgametype == GT_MATCH) ? 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);
}
//
// Y_VoteDrawer
//
// Draws the voting screen!
//
void Y_VoteDrawer(void)
{
INT32 i, x, y = 0, height = 0;
UINT8 selected[4];
fixed_t rubyheight = 0;
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();
for (i = 0; i < 4; 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 += 50;
else
height += 25;
if (i < 3)
height += 5-splitscreen;
}
y = (200-height)/2;
for (i = 0; i < 4; i++)
{
const char *str;
patch_t *pic;
UINT8 j, color;
if (i == 3)
{
str = "RANDOM";
pic = VoteScreen.randomlvl;
}
else
{
str = levelinfo[i].str;
pic = levelinfo[i].pic;
}
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 = displayplayers[1];
break;
case 2:
thiscurs = VoteScreen.cursor[3];
p = displayplayers[2];
break;
case 3:
thiscurs = VoteScreen.cursor[4];
p = displayplayers[3];
break;
default:
thiscurs = VoteScreen.cursor[1];
p = displayplayers[0];
break;
}
color = colortranslations[players[p].skincolor][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), 120|V_SNAPTORIGHT);
else
V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), color|V_SNAPTORIGHT);
sizeadd--;
}
if (!levelinfo[i].encore)
V_DrawSmallScaledPatch(BASEVIDWIDTH-100, y, V_SNAPTORIGHT, pic);
else
{
V_DrawFixedPatch((BASEVIDWIDTH-20)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/2, V_FLIP|V_SNAPTORIGHT, pic, NULL);
V_DrawFixedPatch((BASEVIDWIDTH-60)<<FRACBITS, ((y+25)<<FRACBITS) - (rubyheight<<1), FRACUNIT, V_SNAPTORIGHT, VoteScreen.rubyicon, NULL);
}
V_DrawRightAlignedThinString(BASEVIDWIDTH-21, 40+y, V_SNAPTORIGHT|V_6WIDTHSPACE, str);
if (levelinfo[i].gts)
{
INT32 w = V_ThinStringWidth(levelinfo[i].gts, V_SNAPTORIGHT)+1;
V_DrawFill(BASEVIDWIDTH-100, y+10, w+1, 2, V_SNAPTORIGHT|31);
V_DrawFill(BASEVIDWIDTH-100, y, w, 11, V_SNAPTORIGHT|levelinfo[i].gtc);
V_DrawDiag(BASEVIDWIDTH-100+w+1, y, 12, V_SNAPTORIGHT|31);
V_DrawDiag(BASEVIDWIDTH-100+w, y, 11, V_SNAPTORIGHT|levelinfo[i].gtc);
V_DrawThinString(BASEVIDWIDTH-99, y+1, V_SNAPTORIGHT, levelinfo[i].gts);
}
y += 50;
}
else
{
if (!levelinfo[i].encore)
V_DrawTinyScaledPatch(BASEVIDWIDTH-60, y, V_SNAPTORIGHT, pic);
else
{
V_DrawFixedPatch((BASEVIDWIDTH-20)<<FRACBITS, y<<FRACBITS, FRACUNIT/4, V_FLIP|V_SNAPTORIGHT, pic, NULL);
V_DrawFixedPatch((BASEVIDWIDTH-40)<<FRACBITS, (y<<FRACBITS) + (25<<(FRACBITS-1)) - rubyheight, FRACUNIT/2, V_SNAPTORIGHT, VoteScreen.rubyicon, NULL);
}
if (levelinfo[i].gts)
{
V_DrawDiag(BASEVIDWIDTH-60, y, 8, V_SNAPTORIGHT|31);
V_DrawDiag(BASEVIDWIDTH-60, y, 6, V_SNAPTORIGHT|levelinfo[i].gtc);
}
y += 25;
}
y += 5-splitscreen;
}
x = 20;
y = 10;
for (i = 0; i < MAXPLAYERS; 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;
player_t *player = &players[i];
if ((playeringame[i] && !player->spectator) && votes[i] != -1)
{
patch_t *pic;
if (votes[i] >= 3 && (i != pickedvote || voteendtic == -1))
pic = VoteScreen.randomlvl;
else
pic = levelinfo[votes[i]].pic;
if (!timer && i == voteclient.ranim)
{
V_DrawScaledPatch(x-18, y+9, V_SNAPTOLEFT, VoteScreen.cursor[0]);
if (voteendtic != -1 && !(votetic % 4))
V_DrawFill(x-1, y-1, 42, 27, 120|V_SNAPTOLEFT);
else
V_DrawFill(x-1, y-1, 42, 27, levelinfo[votes[i]].gtc|V_SNAPTOLEFT);
}
if (!levelinfo[votes[i]].encore)
V_DrawTinyScaledPatch(x, y, V_SNAPTOLEFT, pic);
else
{
V_DrawFixedPatch((x+40)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/4, V_SNAPTOLEFT|V_FLIP, pic, NULL);
V_DrawFixedPatch((x+20)<<FRACBITS, (y<<FRACBITS) + (25<<(FRACBITS-1)) - rubyheight, FRACUNIT/2, V_SNAPTOLEFT, VoteScreen.rubyicon, NULL);
}
if (levelinfo[votes[i]].gts)
{
V_DrawDiag(x, y, 8, V_SNAPTOLEFT|31);
V_DrawDiag(x, y, 6, V_SNAPTOLEFT|levelinfo[votes[i]].gtc);
}
if (player->skincolor)
{
UINT8 *colormap = R_GetTranslationColormap(player->skin, player->skincolor, GTC_CACHE);
patch_t *faceprefix = K_GetFacePrefix(player, K_GetSkinNum(player));
if (K_UseHighResPortraits())
V_DrawSmallMappedPatch(x+24, y+9, V_SNAPTOLEFT, faceprefix, colormap);
else
V_DrawMappedPatch(x+24, y+9, V_SNAPTOLEFT, faceprefix, colormap);
}
if (!splitscreen && i == consoleplayer)
{
UINT8 cursorframe = (votetic / 4) % 8;
V_DrawScaledPatch(x+24, y+9, V_SNAPTOLEFT, (patch_t *)W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_PATCH));
}
}
y += 30;
if (y > BASEVIDHEIGHT-40)
{
x += 60;
y = 10;
}
}
if (timer)
{
INT32 hilicol, tickdown = (timer+1)/TICRATE;
if (cons_menuhighlight.value)
hilicol = cons_menuhighlight.value;
else if (gametype == GT_RACE)
hilicol = V_SKYMAP;
else //if (gametype == GT_MATCH)
hilicol = V_REDMAP;
V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol,
va("Vote ends in %d", tickdown));
}
if (renderisnewtic)
{
LUA_HUD_ClearDrawList(luahuddrawlist_vote);
LUA_HUDHOOK(vote, luahuddrawlist_vote);
}
LUA_HUD_DrawList(luahuddrawlist_vote);
}
//
// Y_VoteStop
//
// Vote screen's selection stops moving
//
SINT8 deferredlevel = 0;
static void Y_VoteStops(SINT8 pick, SINT8 level)
{
nextmap = 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])
{
INT16 lastgametype = gametype;
gametype = votelevels[level][1];
D_GameTypeChanged(lastgametype);
forceresetplayers = true;
}
deferencoremode = (levelinfo[level].encore);
}
//
// Y_VoteTicker
//
// Vote screen thinking :eggthinking:
//
void Y_VoteTicker(void)
{
INT32 i;
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 (!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 (server && pickedvote != -1 && votes[pickedvote] == -1) // Uh oh! The person who got picked left! Recalculate, quick!
D_PickVote();
if (!votetic)
S_ChangeMusicInternal("vote", true);
if (timer)
timer--;
if (pickedvote != -1)
{
timer = 0;
voteclient.rsynctime++;
if (voteendtic == -1)
{
UINT8 tempvotes[MAXPLAYERS];
UINT8 numvotes = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (votes[i] == -1)
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[((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[((pickedvote + voteclient.roffset + i) % numvotes)] == pickedvote)
{
voteclient.rendoff = voteclient.roffset+i;
if (M_RandomChance(FRACUNIT/32)) // Let it cheat occasionally~
voteclient.rendoff++;
S_ChangeMusicInternal("voteeb", false);
break;
}
}
}
}
else if (voteclient.roffset >= voteclient.rendoff)
{
voteendtic = votetic + (3*TICRATE);
Y_VoteStops(pickedvote, deferredlevel);
}
}
}
else
voteclient.ranim = pickedvote;
}
else
{
if (votetic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V
return;
for (i = 0; i <= splitscreen; i++)
{
UINT8 p;
boolean pressed = false;
p = P_GetLocalPlayerNumForNum(i);
if (voteclient.playerinfo[i].delay)
voteclient.playerinfo[i].delay--;
if ((playeringame[p] && !players[p].spectator)
&& !voteclient.playerinfo[i].delay
&& pickedvote == -1 && votes[p] == -1)
{
if (InputDown(gc_aimforward, i+1) || JoyAxis(AXISAIM, i+1) < 0)
{
voteclient.playerinfo[i].selection--;
pressed = true;
}
if ((InputDown(gc_aimbackward, i+1) || JoyAxis(AXISAIM, i+1) > 0) && !pressed)
{
voteclient.playerinfo[i].selection++;
pressed = true;
}
if (voteclient.playerinfo[i].selection < 0)
voteclient.playerinfo[i].selection = 3;
if (voteclient.playerinfo[i].selection > 3)
voteclient.playerinfo[i].selection = 0;
if ((InputDown(gc_accelerate, i+1) || JoyAxis(AXISMOVE, i+1) > 0) && !pressed)
{
D_ModifyClientVote(voteclient.playerinfo[i].selection, p);
pressed = true;
}
}
if (pressed)
{
S_StartSound(NULL, sfx_kc4a);
voteclient.playerinfo[i].delay = NEWTICRATE/7;
}
}
if (server)
{
if (timer == 0)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if ((playeringame[i] && !players[i].spectator) && votes[i] == -1)
votes[i] = 3;
}
}
else
{
for (i = 0; i < MAXPLAYERS; i++)
{
if ((playeringame[i] && !players[i].spectator) && votes[i] == -1)
return;
}
}
timer = 0;
if (voteendtic == -1)
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] = (patch_t*)W_CachePatchName("M_CURSOR", PU_PATCH);
VoteScreen.cursor[1] = (patch_t*)W_CachePatchName("P1CURSOR", PU_PATCH_LOWPRIORITY);
VoteScreen.cursor[2] = (patch_t*)W_CachePatchName("P2CURSOR", PU_PATCH_LOWPRIORITY);
VoteScreen.cursor[3] = (patch_t*)W_CachePatchName("P3CURSOR", PU_PATCH_LOWPRIORITY);
VoteScreen.cursor[4] = (patch_t*)W_CachePatchName("P4CURSOR", PU_PATCH_LOWPRIORITY);
VoteScreen.randomlvl = (patch_t*)W_CachePatchName("RANDOMLV", PU_PATCH);
VoteScreen.rubyicon = (patch_t*)W_CachePatchName("RUBYICON", PU_PATCH);
}
void Y_StartVote(void)
{
INT32 i = 0;
votetic = -1;
#ifdef PARANOIA
if (voteendtic != -1)
I_Error("voteendtic is dirty");
#endif
// cache vote patches
Y_InitVoteDrawing();
timer = cv_votetime.value*TICRATE;
pickedvote = -1;
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++)
votes[i] = -1;
for (i = 0; i < 4; i++)
{
lumpnum_t lumpnum;
// set up the encore
levelinfo[i].encore = (votelevels[i][1] & 0x80);
votelevels[i][1] &= ~0x80;
// 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 %s",
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 %s",
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 = G_GetGametypeColor(votelevels[i][1]);
if (i == 2 && votelevels[i][1] != votelevels[0][1])
levelinfo[i].gts = gametype_cons_t[votelevels[i][1]].strvalue;
else
levelinfo[i].gts = NULL;
// set up the pic
if (rendermode != render_none)
{
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(votelevels[i][0]+1)));
if (lumpnum != LUMPERROR)
levelinfo[i].pic = (patch_t *)W_CachePatchName(va("%sP", G_BuildMapName(votelevels[i][0]+1)), PU_PATCH);
else
levelinfo[i].pic = (patch_t *)W_CachePatchName("BLANKLVL", PU_PATCH);
}
}
voteclient.loaded = true;
LUA_HUD_DestroyDrawList(luahuddrawlist_vote);
luahuddrawlist_vote = LUA_HUD_CreateDrawList();
}
//
// Y_EndVote
//
void Y_EndVote(void)
{
voteclient.loaded = false;
voteendtic = -1;
}
//
// Y_VoteClear
// Resets patches to prevent dangling pointers
//
void Y_VoteClear(void)
{
VoteScreen.bgpatch = NULL;
VoteScreen.widebgpatch = NULL;
for (size_t i = 0; i < sizeof(VoteScreen.cursor)/sizeof(VoteScreen.cursor[0]); ++i)
VoteScreen.cursor[i] = NULL;
VoteScreen.randomlvl = NULL;
VoteScreen.rubyicon = NULL;
}
//
// Y_SetupVoteFinish
//
void Y_SetupVoteFinish(SINT8 pick, SINT8 level)
{
if (!voteclient.loaded)
return;
if (pick == -1) // No other votes? We gotta get out of here, then!
{
Y_EndVote();
Y_FollowIntermission();
return;
}
if (pickedvote == -1)
{
INT32 i;
SINT8 votecompare = -1;
INT32 endtype = 0;
voteclient.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 (votecompare == -1)
{
votecompare = votes[i];
endtype = 1;
}
else if (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);
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);
}
deferredlevel = level;
pickedvote = pick;
timer = 0;
}