Kart-Public/src/y_inter.c

1599 lines
40 KiB
C
Raw Normal View History

2014-03-15 09:59:03 -07:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
2018-11-25 04:35:38 -08:00
// Copyright (C) 2004-2018 by Sonic Team Junior.
2014-03-15 09:59:03 -07:00
//
// 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
2016-07-05 21:09:17 -07:00
/// \brief Tally screens, or "Intermissions" as they were formally called in Doom
2014-03-15 09:59:03 -07:00
#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
2018-06-04 23:11:47 -07:00
#include "k_kart.h" // colortranslations
#include "console.h" // cons_menuhighlight
2014-03-15 09:59:03 -07:00
#ifdef HWRENDER
#include "hardware/hw_main.h"
#endif
#ifdef PC_DOS
#include <stdio.h> // for snprintf
int snprintf(char *str, size_t n, const char *fmt, ...);
//int vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
#endif
typedef struct
{
char patch[9];
INT32 points;
UINT8 display;
} y_bonus_t;
typedef union
{
/*struct
2014-03-15 09:59:03 -07:00
{
2017-09-28 13:54:26 -07:00
char passed1[21]; // KNUCKLES GOT / CRAWLA HONCHO
2014-04-13 22:14:58 -07:00
char passed2[16]; // THROUGH THE ACT / PASSED THE ACT
2014-03-15 09:59:03 -07:00
INT32 passedx1;
INT32 passedx2;
y_bonus_t bonuses[4];
patch_t *bonuspatches[4];
SINT8 gotperfbonus; // Used for visitation flags.
UINT32 score, total; // fake score, total
UINT32 tics; // time
patch_t *ttlnum; // act number being displayed
patch_t *ptotal; // TOTAL
UINT8 gotlife; // Number of extra lives obtained
} coop;*/
2014-03-15 09:59:03 -07:00
struct
{
UINT8 *color[MAXPLAYERS]; // Winner's color #
INT32 *character[MAXPLAYERS]; // Winner's character #
INT32 num[MAXPLAYERS]; // Winner's player #
char *name[MAXPLAYERS]; // Winner's name
INT32 numplayers; // Number of players being displayed
2018-08-05 08:35:33 -07:00
char levelstring[64]; // holds levelnames up to 64 characters
// SRB2kart
UINT8 increase[MAXPLAYERS]; // how much did the score increase by?
UINT8 jitter[MAXPLAYERS]; // wiggle
UINT32 val[MAXPLAYERS]; // Gametype-specific value
UINT8 pos[MAXPLAYERS]; // player positions. used for ties
boolean rankingsmode; // rankings mode
boolean encore; // encore mode
2014-03-15 09:59:03 -07:00
} match;
} y_data;
static y_data data;
// graphics
static patch_t *bgpatch = NULL; // INTERSCR
static patch_t *widebgpatch = NULL; // INTERSCW
static patch_t *bgtile = NULL; // SPECTILE/SRB2BACK
static patch_t *interpic = NULL; // custom picture defined in map header
static boolean usetile;
2014-03-21 11:42:55 -07:00
boolean usebuffer = false;
2014-03-15 09:59:03 -07:00
static boolean useinterpic;
static INT32 timer;
static INT32 intertic;
static INT32 endtic = -1;
static INT32 sorttic = -1;
2014-03-15 09:59:03 -07:00
intertype_t intertype = int_none;
static void Y_FollowIntermission(void);
static void Y_UnloadData(void);
// SRB2Kart: voting stuff
2018-05-31 15:49:19 -07:00
// Level images
typedef struct
{
char str[62];
UINT8 gtc;
const char *gts;
patch_t *pic;
boolean encore;
} y_votelvlinfo;
2018-05-31 15:49:19 -07:00
// Clientside & splitscreen player info.
typedef struct
{
2018-02-22 17:04:52 -08:00
SINT8 selection;
UINT8 delay;
2018-05-31 15:49:19 -07:00
} y_voteplayer;
typedef struct
{
y_voteplayer playerinfo[4];
2018-02-10 16:02:15 -08:00
UINT8 ranim;
UINT8 rtics;
UINT8 roffset;
UINT8 rsynctime;
UINT8 rendoff;
boolean loaded;
} y_voteclient;
static y_votelvlinfo levelinfo[5];
static y_voteclient voteclient;
static INT32 votetic;
static INT32 voteendtic = -1;
static patch_t *cursor = NULL;
2018-06-04 23:11:47 -07:00
static patch_t *cursor1 = NULL;
static patch_t *cursor2 = NULL;
static patch_t *cursor3 = NULL;
static patch_t *cursor4 = NULL;
2018-01-27 20:52:01 -08:00
static patch_t *randomlvl = NULL;
static patch_t *rubyicon = NULL;
static void Y_UnloadVoteData(void);
//
// 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.match.val[data.match.numplayers]))
return;
data.match.val[data.match.numplayers] = val;
data.match.num[data.match.numplayers] = i;
}
static void Y_CompareBattle(INT32 i)
{
UINT32 val = ((players[i].pflags & PF_TIMEOVER)
? (UINT32_MAX-1) : players[i].marescore);
if (!(data.match.val[data.match.numplayers] == UINT32_MAX
|| (!(players[i].pflags & PF_TIMEOVER) && val > data.match.val[data.match.numplayers])))
return;
data.match.val[data.match.numplayers] = val;
data.match.num[data.match.numplayers] = i;
}
static void Y_CompareRank(INT32 i)
{
UINT8 increase = ((data.match.increase[i] == UINT8_MAX) ? 0 : data.match.increase[i]);
if (!(data.match.val[data.match.numplayers] == UINT32_MAX || (players[i].score - increase) > data.match.val[data.match.numplayers]))
return;
data.match.val[data.match.numplayers] = (players[i].score - increase);
data.match.num[data.match.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.match.rankingsmode = (boolean)rankingsmode))
{
sprintf(data.match.levelstring, "* Total Rankings *");
data.match.encore = false;
}
2018-08-05 08:35:33 -07:00
else
{
// set up the levelstring
if (mapheaderinfo[prevmap]->levelflags & LF_NOZONE)
{
if (mapheaderinfo[prevmap]->actnum[0])
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %s %s *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %s *",
mapheaderinfo[prevmap]->lvlttl);
}
else
{
const char *zonttl = (mapheaderinfo[prevmap]->zonttl[0] ? mapheaderinfo[prevmap]->zonttl : "ZONE");
if (mapheaderinfo[prevmap]->actnum[0])
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %s %s %s *",
mapheaderinfo[prevmap]->lvlttl, zonttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %s %s *",
mapheaderinfo[prevmap]->lvlttl, zonttl);
}
data.match.levelstring[sizeof data.match.levelstring - 1] = '\0';
data.match.encore = encoremode;
memset(data.match.jitter, 0, sizeof (data.match.jitter));
}
for (i = 0; i < MAXPLAYERS; i++)
{
data.match.val[i] = UINT32_MAX;
if (!playeringame[i] || players[i].spectator)
{
data.match.increase[i] = UINT8_MAX;
continue;
}
if (!rankingsmode)
data.match.increase[i] = UINT8_MAX;
numplayersingame++;
}
memset(data.match.color, 0, sizeof (data.match.color));
memset(data.match.character, 0, sizeof (data.match.character));
memset(completed, 0, sizeof (completed));
data.match.numplayers = 0;
for (j = 0; j < numplayersingame; j++)
{
2018-10-24 16:38:46 -07:00
INT32 nump = ((G_RaceGametype() && nospectategrief > 0) ? nospectategrief : numplayersingame);
2018-10-23 14:48:09 -07:00
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || completed[i])
continue;
comparison(i);
}
i = data.match.num[data.match.numplayers];
completed[i] = true;
data.match.color[data.match.numplayers] = &players[i].skincolor;
data.match.character[data.match.numplayers] = &players[i].skin;
data.match.name[data.match.numplayers] = player_names[i];
if (data.match.numplayers && (data.match.val[data.match.numplayers] == data.match.val[data.match.numplayers-1]))
data.match.pos[data.match.numplayers] = data.match.pos[data.match.numplayers-1];
else
data.match.pos[data.match.numplayers] = data.match.numplayers+1;
2018-10-23 14:48:09 -07:00
if (!rankingsmode && !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < nump))
{
2018-10-23 14:48:09 -07:00
data.match.increase[i] = nump - data.match.pos[data.match.numplayers];
players[i].score += data.match.increase[i];
}
data.match.numplayers++;
}
}
2014-03-15 09:59:03 -07:00
//
// Y_IntermissionDrawer
//
// Called by D_Display. Nothing is modified here; all it does is draw. (SRB2Kart: er, about that...)
2014-03-15 09:59:03 -07:00
// Neat concept, huh?
//
void Y_IntermissionDrawer(void)
{
INT32 i, whiteplayer = MAXPLAYERS, x = 4, hilicol = V_YELLOWMAP; // fallback
2014-03-15 09:59:03 -07:00
if (intertype == int_none || rendermode == render_none)
return;
if (!usebuffer)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
if (useinterpic)
V_DrawScaledPatch(0, 0, 0, interpic);
else if (!usetile)
{
if (rendermode == render_soft && usebuffer)
VID_BlitLinearScreen(screens[1], screens[0], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
#ifdef HWRENDER
else if(rendermode != render_soft && usebuffer)
{
HWR_DrawIntermissionBG();
}
#endif
else
{
if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx == 400)
V_DrawScaledPatch(0, 0, V_SNAPTOLEFT, widebgpatch);
else
V_DrawScaledPatch(0, 0, 0, bgpatch);
}
}
else
V_DrawPatchFill(bgtile);
if (usebuffer) // Fade everything out
V_DrawFadeScreen(0xFF00, 22);
if (!splitscreen)
whiteplayer = demoplayback ? displayplayer : consoleplayer;
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)
{
INT32 count = (intertic - sorttic);
if (count < 8)
x -= ((count * vid.width) / (8 * vid.dupx));
else if (count == 8)
goto dotimer;
else if (count < 16)
x += (((16 - count) * vid.width) / (8 * vid.dupx));
}
// SRB2kart 290117 - compeltely replaced this block.
/*if (intertype == int_timeattack)
2014-03-15 09:59:03 -07:00
{
// draw time
ST_DrawPatchFromHud(HUD_TIME, sbotime);
if (cv_timetic.value)
ST_DrawNumFromHud(HUD_SECONDS, data.coop.tics);
2014-03-15 09:59:03 -07:00
else
{
INT32 seconds, minutes, tictrn;
2014-03-15 09:59:03 -07:00
seconds = G_TicsToSeconds(data.coop.tics);
minutes = G_TicsToMinutes(data.coop.tics, true);
tictrn = G_TicsToCentiseconds(data.coop.tics);
ST_DrawNumFromHud(HUD_MINUTES, minutes); // Minutes
ST_DrawPatchFromHud(HUD_TIMECOLON, sbocolon); // Colon
ST_DrawPadNumFromHud(HUD_SECONDS, seconds, 2); // Seconds
// SRB2kart - pulled from old coop block, just in case we need it
// we should show centiseconds on the intermission screen too, if the conditions are right.
if (modeattacking || cv_timetic.value == 2)
{
ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod); // Period
ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2); // Tics
}
ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod); // Period
ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2); // Tics
}
2014-03-15 09:59:03 -07:00
// draw the "got through act" lines and act number
V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1);
V_DrawLevelTitle(data.coop.passedx2, 49+V_LevelNameHeight(data.coop.passed2)+2, 0, data.coop.passed2);
if (strlen(mapheaderinfo[prevmap]->actnum) > 0)
2014-03-15 09:59:03 -07:00
V_DrawScaledPatch(244, 57, 0, data.coop.ttlnum);
//if (gottimebonus && endtic != -1)
// V_DrawCenteredString(BASEVIDWIDTH/2, 172, V_YELLOWMAP, "TIME BONUS UNLOCKED!");
2014-03-15 09:59:03 -07:00
}
else*/ if (intertype == int_race || intertype == int_match)
2014-03-15 09:59:03 -07:00
{
#define NUMFORNEWCOLUMN 8
INT32 y = 41, gutter = ((data.match.numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2));
INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2;
const char *timeheader;
if (data.match.rankingsmode)
timeheader = "RANK";
else
timeheader = (intertype == int_race ? "TIME" : "SCORE");
2016-07-05 21:09:17 -07:00
// draw the level name
V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12, 0, data.match.levelstring);
V_DrawFill((x-3) - duptweak, 34, dupadjust-2, 1, 0);
2014-03-15 09:59:03 -07:00
if (data.match.encore)
V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12-8, hilicol, "ENCORE MODE");
if (data.match.numplayers > NUMFORNEWCOLUMN)
2014-03-15 09:59:03 -07:00
{
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);
2016-07-05 21:09:17 -07:00
}
V_DrawCenteredString(x+6, 24, hilicol, "#");
V_DrawString(x+36, 24, hilicol, "NAME");
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+152, 24, hilicol, timeheader);
for (i = 0; i < data.match.numplayers; i++)
2014-03-15 09:59:03 -07:00
{
boolean dojitter = data.match.jitter[data.match.num[i]];
data.match.jitter[data.match.num[i]] = 0;
if (data.match.num[i] != MAXPLAYERS && playeringame[data.match.num[i]] && !players[data.match.num[i]].spectator)
2014-03-15 09:59:03 -07:00
{
char strtime[MAXPLAYERNAME+1];
if (dojitter)
y--;
V_DrawCenteredString(x+6, y, 0, va("%d", data.match.pos[i]));
if (data.match.color[i])
{
UINT8 *colormap = R_GetTranslationColormap(*data.match.character[i], *data.match.color[i], GTC_CACHE);
V_DrawMappedPatch(x+16, y-4, 0, facerankprefix[*data.match.character[i]], colormap);
}
2014-03-15 09:59:03 -07:00
if (data.match.num[i] == whiteplayer)
{
UINT8 cursorframe = (intertic / 4) % 8;
V_DrawScaledPatch(x+16, y-4, 0, W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_CACHE));
}
2019-01-27 14:52:53 -08:00
STRBUFCPY(strtime, data.match.name[i]);
if (data.match.numplayers > NUMFORNEWCOLUMN)
V_DrawThinString(x+36, y-1, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime);
else
2019-01-27 14:52:53 -08:00
V_DrawString(x+36, y, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime);
2014-03-15 09:59:03 -07:00
if (data.match.rankingsmode)
{
if (data.match.increase[data.match.num[i]] != UINT8_MAX)
{
if (data.match.increase[data.match.num[i]] > 9)
snprintf(strtime, sizeof strtime, "(+%02d)", data.match.increase[data.match.num[i]]);
else
snprintf(strtime, sizeof strtime, "(+ %d)", data.match.increase[data.match.num[i]]);
if (data.match.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", data.match.val[i]);
if (data.match.numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime);
else
V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
}
else
{
if (data.match.val[i] == (UINT32_MAX-1))
V_DrawRightAlignedThinString(x+152+gutter, y-1, (data.match.numplayers > NUMFORNEWCOLUMN ? V_6WIDTHSPACE : 0), "NO CONTEST.");
else
{
if (intertype == int_race)
{
snprintf(strtime, sizeof strtime, "%i'%02i\"%02i", G_TicsToMinutes(data.match.val[i], true),
G_TicsToSeconds(data.match.val[i]), G_TicsToCentiseconds(data.match.val[i]));
strtime[sizeof strtime - 1] = '\0';
if (data.match.numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime);
else
V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
}
else
{
if (data.match.numplayers > NUMFORNEWCOLUMN)
V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, va("%i", data.match.val[i]));
else
V_DrawRightAlignedString(x+152+gutter, y, 0, va("%i", data.match.val[i]));
}
}
}
if (dojitter)
y++;
}
else
data.match.num[i] = MAXPLAYERS; // this should be the only field setting in this function
y += 18;
if (i == NUMFORNEWCOLUMN-1)
2014-03-15 09:59:03 -07:00
{
y = 41;
x += BASEVIDWIDTH/2;
2014-03-15 09:59:03 -07:00
}
#undef NUMFORNEWCOLUMN
2014-03-15 09:59:03 -07:00
}
}
dotimer:
2014-03-15 09:59:03 -07:00
if (timer)
{
INT32 tickdown = (timer+1)/TICRATE;
V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol,
2018-08-05 08:35:33 -07:00
va("%s starts in %d", cv_advancemap.string, tickdown));
}
2014-03-15 09:59:03 -07:00
// Make it obvious that scrambling is happening next round.
if (cv_scrambleonchange.value && cv_teamscramble.value && (intertic/TICRATE % 2 == 0))
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!"));
2014-03-15 09:59:03 -07:00
}
//
// 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;
// Check for pause or menu up in single player
2015-01-01 11:50:31 -08:00
if (paused || P_AutoPause())
2014-03-15 09:59:03 -07:00
return;
intertic++;
// Team scramble code for team match and CTF.
// Don't do this if we're going to automatically scramble teams next round.
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*if (G_GametypeHasTeams() && cv_teamscramble.value && !cv_scrambleonchange.value && server)
2014-03-15 09:59:03 -07:00
{
// If we run out of time in intermission, the beauty is that
// the P_Ticker() team scramble code will pick it up.
if ((intertic % (TICRATE/7)) == 0)
P_DoTeamscrambling();
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
}*/
2014-03-15 09:59:03 -07:00
// 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 || intertic & 1 || endtic != -1)
return;
2014-03-15 09:59:03 -07:00
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 (!data.match.rankingsmode && (intertic >= sorttic + 8))
Y_CalculateMatchData(1, Y_CompareRank);
if (data.match.rankingsmode && intertic > sorttic+16+(2*TICRATE))
{
INT32 q=0,r=0;
boolean kaching = true;
for (q = 0; q < data.match.numplayers; q++)
{
if (data.match.num[q] == MAXPLAYERS
|| !data.match.increase[data.match.num[q]]
|| data.match.increase[data.match.num[q]] == UINT8_MAX)
continue;
r++;
data.match.jitter[data.match.num[q]] = 1;
if (--data.match.increase[data.match.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
}
2014-03-15 09:59:03 -07:00
}
//
2014-11-11 16:55:07 -08:00
// Y_UpdateRecordReplays
2014-03-15 09:59:03 -07:00
//
2014-11-11 16:55:07 -08:00
// Update replay files/data, etc. for Record Attack
// See G_SetNightsRecords for NiGHTS Attack.
2014-03-15 09:59:03 -07:00
//
2014-11-11 16:55:07 -08:00
static void Y_UpdateRecordReplays(void)
2014-03-15 09:59:03 -07:00
{
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) || (bestlap < mainrecords[gamemap-1]->lap))
mainrecords[gamemap-1]->lap = bestlap;
2014-03-15 09:59:03 -07:00
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(players[consoleplayer].realtime, bestlap);
2014-03-15 09:59:03 -07:00
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);
}
2014-03-15 09:59:03 -07:00
//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" : "");
2014-03-15 09:59:03 -07:00
if (M_UpdateUnlockablesAndExtraEmblems(false))
S_StartSound(NULL, sfx_ncitem);
// SRB2Kart - save here so you NEVER lose your earned times/medals.
G_SaveGameData(false);
2014-03-15 09:59:03 -07:00
// Update timeattack menu's replay availability.
CV_AddValue(&cv_nextmap, 1);
CV_AddValue(&cv_nextmap, -1);
}
//
// 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;
2014-03-15 09:59:03 -07:00
}
else
{
if (cv_inttime.value == 0 && gametype == GT_COOP)
timer = 0;
else
{
timer = cv_inttime.value*TICRATE;
if (!timer)
timer = 1;
}
if (gametype == GT_MATCH)
2014-03-15 09:59:03 -07:00
intertype = int_match;
else //if (gametype == GT_RACE)
2014-03-15 09:59:03 -07:00
intertype = int_race;
}
// We couldn't display the intermission even if we wanted to.
// But we still need to give the players their score bonuses, dummy.
//if (dedicated) return;
2014-03-15 09:59:03 -07:00
// 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
2014-03-15 09:59:03 -07:00
break;
}
case int_race: // (time-only race)
{
if (!majormods && !multiplayer && !demoplayback) // 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 (ultimatemode)
mapvisited[gamemap-1] |= MV_ULTIMATE;
if (data.coop.gotperfbonus)
mapvisited[gamemap-1] |= MV_PERFECT;*/
if (modeattacking == ATTACKING_RECORD)
Y_UpdateRecordReplays();
}
2018-02-22 17:04:52 -08:00
2014-03-15 09:59:03 -07:00
// Calculate who won
Y_CalculateMatchData(0, Y_CompareRace);
2014-03-15 09:59:03 -07:00
break;
}
case int_none:
default:
break;
}
2018-08-05 08:35:33 -07:00
//if (intertype == int_race || intertype == int_match)
{
//bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
usetile = useinterpic = false;
usebuffer = true;
2014-03-15 09:59:03 -07:00
}
}
// ======
//
// Y_EndIntermission
//
void Y_EndIntermission(void)
{
Y_UnloadData();
endtic = -1;
sorttic = -1;
2014-03-15 09:59:03 -07:00
intertype = int_none;
2014-03-21 11:42:55 -07:00
usebuffer = false;
2014-03-15 09:59:03 -07:00
}
//
// 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();
2014-03-15 09:59:03 -07:00
}
#define UNLOAD(x) Z_ChangeTag(x, PU_CACHE); x = NULL
//
// Y_UnloadData
//
static void Y_UnloadData(void)
{
// In hardware mode, don't Z_ChangeTag a pointer returned by W_CachePatchName().
// It doesn't work and is unnecessary.
if (rendermode != render_soft)
return;
// unload the background patches
UNLOAD(bgpatch);
UNLOAD(widebgpatch);
UNLOAD(bgtile);
UNLOAD(interpic);
/*switch (intertype)
2014-03-15 09:59:03 -07:00
{
case int_coop:
// unload the coop and single player patches
UNLOAD(data.coop.ttlnum);
UNLOAD(data.coop.bonuspatches[3]);
UNLOAD(data.coop.bonuspatches[2]);
UNLOAD(data.coop.bonuspatches[1]);
UNLOAD(data.coop.bonuspatches[0]);
UNLOAD(data.coop.ptotal);
break;
case int_spec:
// unload the special stage patches
//UNLOAD(data.spec.cemerald);
//UNLOAD(data.spec.nowsuper);
UNLOAD(data.spec.bonuspatch);
UNLOAD(data.spec.pscore);
UNLOAD(data.spec.pcontinues);
break;
case int_match:
case int_race:
default:
//without this default,
//int_none, int_tag, int_chaos, and int_classicrace
//are not handled
break;
}*/
2014-03-15 09:59:03 -07:00
}
// SRB2Kart: Voting!
//
// Y_VoteDrawer
//
// Draws the voting screen!
//
void Y_VoteDrawer(void)
{
2018-05-31 15:49:19 -07:00
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;
{
angle_t rubyfloattime = (ANGLE_MAX/NEWTICRATE)*(votetic % NEWTICRATE);
rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT);
}
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
2018-01-27 20:52:01 -08:00
if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx > 320)
V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(widebgpatch->width)/2),
(vid.height / vid.dupy) - SHORT(widebgpatch->height),
V_SNAPTOTOP|V_SNAPTOLEFT, widebgpatch);
else
V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(bgpatch->width)/2), // Keep the width/height adjustments, for screens that are less wide than 320(?)
(vid.height / vid.dupy) - SHORT(bgpatch->height),
V_SNAPTOTOP|V_SNAPTOLEFT, bgpatch);
2018-05-31 15:49:19 -07:00
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;
2018-01-27 20:52:01 -08:00
patch_t *pic;
UINT8 j, color;
2018-01-27 20:52:01 -08:00
if (i == 3)
{
str = "RANDOM";
2018-01-27 20:52:01 -08:00
pic = randomlvl;
}
else
{
str = levelinfo[i].str;
pic = levelinfo[i].pic;
2018-01-27 20:52:01 -08:00
}
2018-05-31 15:49:19 -07:00
if (selected[i])
2018-01-27 20:52:01 -08:00
{
UINT8 sizeadd = selected[i];
2018-05-31 15:49:19 -07:00
for (j = 0; j <= splitscreen; j++) // another loop for drawing the selection backgrounds in the right order, grumble grumble..
2018-01-27 20:52:01 -08:00
{
2018-05-31 15:49:19 -07:00
INT32 handy = y;
2018-06-12 16:05:11 -07:00
UINT8 p;
2018-06-04 23:11:47 -07:00
UINT8 *colormap;
patch_t *thiscurs;
2018-05-31 15:49:19 -07:00
if (voteclient.playerinfo[j].selection != i)
continue;
if (!splitscreen)
2018-05-31 15:49:19 -07:00
{
2018-06-04 23:11:47 -07:00
thiscurs = cursor;
2018-06-12 16:05:11 -07:00
p = consoleplayer;
color = levelinfo[i].gtc;
2018-06-04 23:11:47 -07:00
colormap = NULL;
}
2018-01-27 20:52:01 -08:00
else
2018-06-04 23:11:47 -07:00
{
switch (j)
{
case 1:
thiscurs = cursor2;
2018-06-12 16:05:11 -07:00
p = secondarydisplayplayer;
2018-06-04 23:11:47 -07:00
break;
case 2:
thiscurs = cursor3;
2018-06-12 16:05:11 -07:00
p = thirddisplayplayer;
2018-06-04 23:11:47 -07:00
break;
case 3:
thiscurs = cursor4;
2018-06-12 16:05:11 -07:00
p = fourthdisplayplayer;
2018-06-04 23:11:47 -07:00
break;
default:
thiscurs = cursor1;
2018-06-12 16:05:11 -07:00
p = displayplayer;
2018-06-04 23:11:47 -07:00
break;
}
2018-06-12 16:05:11 -07:00
color = colortranslations[players[p].skincolor][7];
colormap = R_GetTranslationColormap(TC_DEFAULT, players[p].skincolor, GTC_CACHE);
2018-05-31 15:49:19 -07:00
}
2018-06-12 16:05:11 -07:00
if (votes[p] != -1 || players[p].spectator)
continue;
2018-05-31 15:49:19 -07:00
handy += 6*(3-splitscreen) + (13*j);
2018-06-04 23:11:47 -07:00
V_DrawMappedPatch(BASEVIDWIDTH-124, handy, V_SNAPTORIGHT, thiscurs, colormap);
2018-05-31 15:49:19 -07:00
if (votetic % 10 < 4)
2018-05-31 15:49:19 -07:00
V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), 120|V_SNAPTORIGHT);
2018-01-27 20:52:01 -08:00
else
2018-05-31 15:49:19 -07:00
V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), color|V_SNAPTORIGHT);
2018-06-04 23:11:47 -07:00
2018-05-31 15:49:19 -07:00
sizeadd--;
2018-01-27 20:52:01 -08:00
}
2018-05-31 15:49:19 -07:00
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, 0);
V_DrawFixedPatch((BASEVIDWIDTH-60)<<FRACBITS, ((y+25)<<FRACBITS) - (rubyheight<<1), FRACUNIT, V_SNAPTORIGHT, rubyicon, NULL);
}
2019-01-11 23:53:14 -08:00
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);
}
2018-05-31 15:49:19 -07:00
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, 0);
V_DrawFixedPatch((BASEVIDWIDTH-40)<<FRACBITS, (y<<FRACBITS) + (25<<(FRACBITS-1)) - rubyheight, FRACUNIT/2, V_SNAPTORIGHT, 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);
}
2018-05-31 15:49:19 -07:00
y += 25;
}
2018-05-31 15:49:19 -07:00
y += 5-splitscreen;
}
x = 20;
2018-02-27 18:17:34 -08:00
y = 10;
for (i = 0; i < MAXPLAYERS; i++)
{
2018-03-05 16:55:39 -08:00
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;
2018-02-16 22:53:44 -08:00
if ((playeringame[i] && !players[i].spectator) && votes[i] != -1)
{
2018-01-27 20:52:01 -08:00
patch_t *pic;
if (votes[i] >= 3 && (i != pickedvote || voteendtic == -1))
2018-01-27 20:52:01 -08:00
pic = randomlvl;
else
pic = levelinfo[votes[i]].pic;
2018-01-27 20:52:01 -08:00
2018-02-10 16:02:15 -08:00
if (!timer && i == voteclient.ranim)
2018-01-27 20:52:01 -08:00
{
V_DrawScaledPatch(x-18, y+9, V_SNAPTOLEFT, cursor);
if (voteendtic != -1 && !(votetic % 4))
2018-01-27 20:52:01 -08:00
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);
2018-01-27 20:52:01 -08:00
}
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, 0);
V_DrawFixedPatch((x+20)<<FRACBITS, (y<<FRACBITS) + (25<<(FRACBITS-1)) - rubyheight, FRACUNIT/2, V_SNAPTOLEFT, 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 (players[i].skincolor)
{
UINT8 *colormap = R_GetTranslationColormap(players[i].skin, players[i].skincolor, GTC_CACHE);
V_DrawMappedPatch(x+24, y+9, V_SNAPTOLEFT, facerankprefix[players[i].skin], colormap);
}
if (!splitscreen && i == consoleplayer)
{
UINT8 cursorframe = (votetic / 4) % 8;
V_DrawScaledPatch(x+24, y+9, V_SNAPTOLEFT, W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_CACHE));
}
}
y += 30;
2018-02-27 18:17:34 -08:00
if (y > BASEVIDHEIGHT-40)
{
2018-02-27 18:17:34 -08:00
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));
}
}
//
// 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
else if (mapheaderinfo[nextmap] && (mapheaderinfo[nextmap]->menuflags & LF2_HIDEINMENU))
S_StartSound(NULL, sfx_noooo1); // this is bad
2018-11-19 21:10:54 -08:00
else if (netgame && (pick == consoleplayer
|| pick == secondarydisplayplayer
|| pick == thirddisplayplayer
|| pick == fourthdisplayplayer))
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;
votetic++;
2018-01-27 20:52:01 -08:00
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)
2018-02-16 22:53:44 -08:00
votes[i] = -1; // Spectators are the lower class, and have effectively no voice in the government. Democracy sucks.
2018-05-31 15:49:19 -07:00
else if (pickedvote != -1 && votes[i] == -1)
votes[i] = 3; // Slow people get random
}
2018-02-16 22:53:44 -08:00
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++;
}
2018-03-05 16:55:39 -08:00
if (numvotes < 1) // Whoops! Get outta here.
{
Y_EndVote();
Y_FollowIntermission();
2018-03-05 16:55:39 -08:00
return;
}
2018-02-10 16:02:15 -08:00
voteclient.rtics--;
if (voteclient.rtics <= 0)
2018-02-10 16:02:15 -08:00
{
voteclient.roffset++;
2018-02-16 22:53:44 -08:00
voteclient.rtics = min(20, (3*voteclient.roffset/4)+5);
S_StartSound(NULL, sfx_kc39);
2018-02-10 16:02:15 -08:00
}
if (voteclient.rendoff == 0 || voteclient.roffset < voteclient.rendoff)
voteclient.ranim = tempvotes[((pickedvote + voteclient.roffset) % numvotes)];
2018-02-10 16:02:15 -08:00
2018-02-16 22:53:44 -08:00
if (voteclient.roffset >= 20)
2018-02-10 16:02:15 -08:00
{
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);
}
2018-02-10 16:02:15 -08:00
}
}
2018-02-10 16:02:15 -08:00
else
voteclient.ranim = pickedvote;
}
else
{
if (votetic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V
return;
2018-05-31 15:49:19 -07:00
for (i = 0; i <= splitscreen; i++)
{
2018-05-31 15:49:19 -07:00
UINT8 p;
boolean pressed = false;
switch (i)
{
2018-05-31 15:49:19 -07:00
case 1:
p = secondarydisplayplayer;
break;
case 2:
p = thirddisplayplayer;
break;
case 3:
p = fourthdisplayplayer;
break;
default:
p = consoleplayer;
break;
}
2018-05-31 15:49:19 -07:00
if (voteclient.playerinfo[i].delay)
voteclient.playerinfo[i].delay--;
if ((playeringame[p] && !players[p].spectator)
&& !voteclient.playerinfo[i].delay
&& pickedvote == -1 && votes[p] == -1)
{
2018-05-31 15:49:19 -07:00
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, i);
pressed = true;
}
}
2018-05-31 15:49:19 -07:00
if (pressed)
{
2018-05-31 15:49:19 -07:00
S_StartSound(NULL, sfx_kc4a);
voteclient.playerinfo[i].delay = NEWTICRATE/7;
}
}
if (server)
{
2018-02-27 17:46:01 -08:00
if (timer == 0)
{
2018-02-27 17:46:01 -08:00
for (i = 0; i < MAXPLAYERS; i++)
{
2018-05-31 15:49:19 -07:00
if ((playeringame[i] && !players[i].spectator) && votes[i] == -1)
2018-02-27 17:46:01 -08:00
votes[i] = 3;
}
}
else
{
2018-05-31 15:49:19 -07:00
for (i = 0; i < MAXPLAYERS; i++)
{
2018-05-31 15:49:19 -07:00
if ((playeringame[i] && !players[i].spectator) && votes[i] == -1)
2018-02-16 22:53:44 -08:00
return;
}
}
2018-02-16 22:53:44 -08:00
timer = 0;
if (voteendtic == -1)
D_PickVote();
}
}
}
//
// Y_StartVote
//
// MK online style voting screen, appears after intermission
//
void Y_StartVote(void)
{
INT32 i = 0;
votetic = -1;
#ifdef PARANOIA
if (voteendtic != -1)
I_Error("voteendtic is dirty");
#endif
widebgpatch = W_CachePatchName(((gametype == GT_MATCH) ? "BATTLSCW" : "INTERSCW"), PU_STATIC);
bgpatch = W_CachePatchName(((gametype == GT_MATCH) ? "BATTLSCR" : "INTERSCR"), PU_STATIC);
cursor = W_CachePatchName("M_CURSOR", PU_STATIC);
2018-06-04 23:11:47 -07:00
cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC);
cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC);
cursor3 = W_CachePatchName("P3CURSOR", PU_STATIC);
cursor4 = W_CachePatchName("P4CURSOR", PU_STATIC);
2018-01-27 20:52:01 -08:00
randomlvl = W_CachePatchName("RANDOMLV", PU_STATIC);
rubyicon = W_CachePatchName("RUBYICON", PU_STATIC);
timer = cv_votetime.value*TICRATE;
pickedvote = -1;
2018-05-31 15:49:19 -07:00
for (i = 0; i < 3; i++)
{
voteclient.playerinfo[i].selection = 0;
voteclient.playerinfo[i].delay = 0;
}
2018-02-10 16:02:15 -08:00
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 < 5; i++)
{
2018-01-27 20:52:01 -08:00
lumpnum_t lumpnum;
// set up the encore
levelinfo[i].encore = (votelevels[i][1] & 0x80);
votelevels[i][1] &= ~0x80;
2018-01-27 20:52:01 -08:00
// set up the str
if (i == 4)
levelinfo[i].str[0] = '\0';
else
{
// 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
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(votelevels[i][0]+1)));
2018-01-27 20:52:01 -08:00
if (lumpnum != LUMPERROR)
levelinfo[i].pic = W_CachePatchName(va("%sP", G_BuildMapName(votelevels[i][0]+1)), PU_STATIC);
2018-01-27 20:52:01 -08:00
else
levelinfo[i].pic = W_CachePatchName("BLANKLVL", PU_STATIC);
}
voteclient.loaded = true;
}
//
// Y_EndVote
//
void Y_EndVote(void)
{
Y_UnloadVoteData();
voteendtic = -1;
}
//
// Y_UnloadVoteData
//
static void Y_UnloadVoteData(void)
{
if (rendermode != render_soft)
return;
voteclient.loaded = false;
UNLOAD(widebgpatch);
UNLOAD(bgpatch);
UNLOAD(cursor);
2018-06-04 23:11:47 -07:00
UNLOAD(cursor1);
UNLOAD(cursor2);
UNLOAD(cursor3);
UNLOAD(cursor4);
2018-01-27 20:52:01 -08:00
UNLOAD(randomlvl);
UNLOAD(rubyicon);
UNLOAD(levelinfo[4].pic);
UNLOAD(levelinfo[3].pic);
UNLOAD(levelinfo[2].pic);
UNLOAD(levelinfo[1].pic);
UNLOAD(levelinfo[0].pic);
}
//
// Y_SetupVoteFinish
//
2018-02-22 17:04:52 -08:00
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;
}
2018-02-16 22:53:44 -08:00
if (pickedvote == -1)
{
INT32 i;
SINT8 votecompare = -1;
INT32 endtype = 0;
2018-02-16 22:53:44 -08:00
voteclient.rsynctime = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
2018-05-31 15:49:19 -07:00
if ((playeringame[i] && !players[i].spectator) && votes[i] == -1)
2018-02-16 22:53:44 -08:00
votes[i] = 3;
if (votes[i] == -1 || endtype > 1) // Don't need to go on
2018-02-16 22:53:44 -08:00
continue;
if (level == 4)
{
votes[i] = 4;
continue;
}
if (endtype == 2)
continue;
2018-02-16 22:53:44 -08:00
if (votecompare == -1)
{
2018-02-16 22:53:44 -08:00
votecompare = votes[i];
endtype = 1;
}
2018-02-16 22:53:44 -08:00
else if (votes[i] != votecompare)
endtype = 2;
2018-02-16 22:53:44 -08:00
}
if (level == 4 || 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;
}
2018-02-16 22:53:44 -08:00
else
S_ChangeMusicInternal("voteea", true);
}
deferredlevel = level;
pickedvote = pick;
timer = 0;
2018-06-07 16:39:45 -07:00
}