Kart-Public/src/y_inter.c
TehRealSalt ce74a4c964 Attempted to fix how voting handles no votes
I don't know exactly what it did previously since I never actually
observed what happens, but now in dedicated servers it will wait for
time to run out, then if there's no votes by that time it will just go
onto nextlevel
2018-03-01 17:05:36 -05:00

2566 lines
No EOL
67 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2004-2016 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 "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" // P_RandomKey
#include "g_input.h" // PLAYER1INPUTDOWN
#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
{
char passed1[21]; // KNUCKLES GOT / CRAWLA HONCHO
char passed2[16]; // THROUGH THE ACT / PASSED THE ACT
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;
struct
{
char passed1[29]; // KNUCKLES GOT / CRAWLA HONCHO
char passed2[17]; // A CHAOS EMERALD / GOT THEM ALL!
char passed3[15]; // CAN NOW BECOME
char passed4[SKINNAMESIZE+7]; // SUPER CRAWLA HONCHO
INT32 passedx1;
INT32 passedx2;
INT32 passedx3;
INT32 passedx4;
y_bonus_t bonus;
patch_t *bonuspatch;
patch_t *pscore; // SCORE
UINT32 score; // fake score
// Continues
UINT8 continues;
patch_t *pcontinues;
INT32 *playerchar; // Continue HUD
UINT8 *playercolor;
UINT8 gotlife; // Number of extra lives obtained
} spec;
struct
{
UINT32 scores[MAXPLAYERS]; // Winner's score
UINT8 *color[MAXPLAYERS]; // Winner's color #
boolean spectator[MAXPLAYERS]; // Spectator list
INT32 *character[MAXPLAYERS]; // Winner's character #
INT32 num[MAXPLAYERS]; // Winner's player #
char *name[MAXPLAYERS]; // Winner's name
patch_t *result; // RESULT
patch_t *blueflag;
patch_t *redflag; // int_ctf uses this struct too.
INT32 numplayers; // Number of players being displayed
char levelstring[40]; // holds levelnames up to 32 characters
// SRB2kart
int increase[MAXPLAYERS]; //how much did the score increase by?
int time[MAXPLAYERS]; //Tournament Time
} match;
struct
{
UINT8 *color[MAXPLAYERS]; // Winner's color #
INT32 *character[MAXPLAYERS]; // Winner's character #
INT32 num[MAXPLAYERS]; // Winner's player #
char name[MAXPLAYERS][9]; // Winner's name
UINT32 times[MAXPLAYERS];
UINT32 rings[MAXPLAYERS];
UINT32 maxrings[MAXPLAYERS];
UINT32 monitors[MAXPLAYERS];
UINT32 scores[MAXPLAYERS];
UINT32 points[MAXPLAYERS];
INT32 numplayers; // Number of players being displayed
char levelstring[40]; // holds levelnames up to 32 characters
} competition;
} 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;
boolean usebuffer = false;
static boolean useinterpic;
static INT32 timer;
static INT32 intertic;
static INT32 endtic = -1;
intertype_t intertype = int_none;
//static void Y_AwardCoopBonuses(void);
//static void Y_AwardSpecialStageBonus(void);
static void Y_CalculateTournamentPoints(void); // SRB2kart
static void Y_CalculateCompetitionWinners(void);
//static void Y_CalculateTimeRaceWinners(void);
static void Y_CalculateMatchWinners(void);
static void Y_FollowIntermission(void);
static void Y_UnloadData(void);
// SRB2Kart: voting stuff
typedef struct
{
char str[40];
patch_t *pic;
} y_votelvlinfo;
typedef struct
{
SINT8 selection;
UINT8 delay;
UINT8 ranim;
UINT8 rtics;
UINT8 roffset;
UINT8 rsynctime;
UINT8 rendoff;
} y_voteclient;
static y_votelvlinfo levelinfo[4];
static y_voteclient voteclient;
static INT32 votetic;
static INT32 voteendtic = -1;
static patch_t *cursor = NULL;
static patch_t *randomlvl = NULL;
static void Y_UnloadVoteData(void);
// Stuff copy+pasted from st_stuff.c
static INT32 SCX(INT32 x)
{
return FixedInt(FixedMul(x<<FRACBITS, vid.fdupx));
}
static INT32 SCY(INT32 z)
{
return FixedInt(FixedMul(z<<FRACBITS, vid.fdupy));
}
#define ST_DrawNumFromHud(h,n) V_DrawTallNum(SCX(hudinfo[h].x), SCY(hudinfo[h].y), V_NOSCALESTART, n)
#define ST_DrawPadNumFromHud(h,n,q) V_DrawPaddedTallNum(SCX(hudinfo[h].x), SCY(hudinfo[h].y), V_NOSCALESTART, n, q)
#define ST_DrawPatchFromHud(h,p) V_DrawScaledPatch(SCX(hudinfo[h].x), SCY(hudinfo[h].y), V_NOSCALESTART, p)
//
// Y_IntermissionDrawer
//
// Called by D_Display. Nothing is modified here; all it does is draw.
// Neat concept, huh?
//
void Y_IntermissionDrawer(void)
{
// Bonus loops
INT32 i;
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);
// SRB2kart 290117 - compeltely replaced this block.
if (intertype == int_timeattack)
{
// draw time
ST_DrawPatchFromHud(HUD_TIME, sbotime);
if (cv_timetic.value)
ST_DrawNumFromHud(HUD_SECONDS, data.coop.tics);
else
{
INT32 seconds, minutes, tictrn;
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
}
// 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 (mapheaderinfo[gamemap-1]->actnum)
V_DrawScaledPatch(244, 57, 0, data.coop.ttlnum);
//if (gottimebonus && endtic != -1)
// V_DrawCenteredString(BASEVIDWIDTH/2, 172, V_YELLOWMAP, "TIME BONUS UNLOCKED!");
}
else if (intertype == int_race)
{
INT32 j = 0;
INT32 x = 4;
INT32 y = 48;
char name[MAXPLAYERNAME+1];
boolean completed[MAXPLAYERS];
memset(completed, 0, sizeof (completed));
// draw the level name
V_DrawCenteredString(BASEVIDWIDTH/2, 20, 0, data.match.levelstring);
V_DrawFill(4, 42, 312, 1, 0);
if (data.match.numplayers > 8)
{
V_DrawFill(160, 32, 1, 152, 0);
V_DrawCenteredString(x+6+(BASEVIDWIDTH/2), 32, V_YELLOWMAP, "#");
V_DrawString(x+36+(BASEVIDWIDTH/2), 32, V_YELLOWMAP, "NAME");
V_DrawRightAlignedString(x+110, 32, V_YELLOWMAP, "TIME");
V_DrawRightAlignedString(x+152, 32, V_YELLOWMAP, "SCORE");
}
V_DrawCenteredString(x+6, 32, V_YELLOWMAP, "#");
V_DrawString(x+36, 32, V_YELLOWMAP, "NAME");
if (data.match.numplayers > 8)
{
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+110, 32, V_YELLOWMAP, "TIME");
}
else
{
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+62, 32, V_YELLOWMAP, "TIME");
}
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+152, 32, V_YELLOWMAP, "SCORE");
for (i = 0; i < data.match.numplayers; i++)
{
char strtime[10];
if (data.match.spectator[i])
continue;
V_DrawCenteredString(x+6, y, 0, va("%d", j+1));
j++; //We skip spectators, but not their number.
if (playeringame[data.match.num[i]])
{
if (data.match.color[i] == 0)
V_DrawSmallScaledPatch(x+16, y-4, 0,faceprefix[*data.match.character[i]]);
else
{
UINT8 *colormap = R_GetTranslationColormap(*data.match.character[i], *data.match.color[i], GTC_CACHE);
V_DrawSmallMappedPatch(x+16, y-4, 0,faceprefix[*data.match.character[i]], colormap);
}
if (data.match.numplayers > 8)
{
strlcpy(name, data.match.name[i], 6);
}
else
STRBUFCPY(name, data.match.name[i]);
V_DrawString(x+36, y, V_ALLOWLOWERCASE, name);
snprintf(strtime, sizeof strtime, "%d", data.match.scores[i]-data.match.increase[i]);
if (data.match.numplayers > 8)
{
V_DrawRightAlignedString(x+152, y, V_YELLOWMAP, strtime);
}
else
{
V_DrawRightAlignedString(x+152+BASEVIDWIDTH/2, y, V_YELLOWMAP, strtime);
}
if (data.match.increase[i] > 9)
snprintf(strtime, sizeof strtime, "(+%02d)", data.match.increase[i]);
else
snprintf(strtime, sizeof strtime, "(+ %d)", data.match.increase[i]);
if (data.match.numplayers <= 8) // Only draw this with less than 8 players, otherwise we won't be able to fit the times in
{
V_DrawString(x+84+BASEVIDWIDTH/2, y, 0, strtime);
}
snprintf(strtime, sizeof strtime, "%i:%02i.%02i", G_TicsToMinutes(data.match.time[i], true),
G_TicsToSeconds(data.match.time[i]), G_TicsToCentiseconds(data.match.time[i]));
strtime[sizeof strtime - 1] = '\0';
if (data.match.numplayers > 8)
{
V_DrawRightAlignedString(x+134, y, 0, strtime);
}
else
{
V_DrawRightAlignedString(x+80+BASEVIDWIDTH/2, y, 0, strtime);
}
completed[i] = true;
}
y += 16;
if (y > 160)
{
y = 48;
x += BASEVIDWIDTH/2;
}
}
}
else if (intertype == int_match)
{
INT32 j = 0;
INT32 x = 4;
INT32 y = 48;
char name[MAXPLAYERNAME+1];
char strtime[10];
// draw the header
V_DrawScaledPatch(112, 2, 0, data.match.result);
// draw the level name
V_DrawCenteredString(BASEVIDWIDTH/2, 20, 0, data.match.levelstring);
V_DrawFill(4, 42, 312, 1, 0);
if (data.match.numplayers > 9)
{
V_DrawFill(160, 32, 1, 152, 0);
if (intertype == int_race)
V_DrawRightAlignedString(x+152, 32, V_YELLOWMAP, "TIME");
else
V_DrawRightAlignedString(x+152, 32, V_YELLOWMAP, "SCORE");
V_DrawCenteredString(x+(BASEVIDWIDTH/2)+6, 32, V_YELLOWMAP, "#");
V_DrawString(x+(BASEVIDWIDTH/2)+36, 32, V_YELLOWMAP, "NAME");
}
V_DrawCenteredString(x+6, 32, V_YELLOWMAP, "#");
V_DrawString(x+36, 32, V_YELLOWMAP, "NAME");
if (intertype == int_race)
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+152, 32, V_YELLOWMAP, "TIME");
else
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+152, 32, V_YELLOWMAP, "SCORE");
for (i = 0; i < data.match.numplayers; i++)
{
if (data.match.spectator[i])
continue; //Ignore spectators.
V_DrawCenteredString(x+6, y, 0, va("%d", j+1));
j++; //We skip spectators, but not their number.
if (playeringame[data.match.num[i]])
{
// Draw the back sprite, it looks ugly if we don't
V_DrawSmallScaledPatch(x+16, y-4, 0, livesback);
if (data.match.color[i] == 0)
V_DrawSmallScaledPatch(x+16, y-4, 0,faceprefix[*data.match.character[i]]);
else
{
UINT8 *colormap = R_GetTranslationColormap(*data.match.character[i], *data.match.color[i], GTC_CACHE);
V_DrawSmallMappedPatch(x+16, y-4, 0,faceprefix[*data.match.character[i]], colormap);
}
if (data.match.numplayers > 9)
{
if (intertype == int_race)
strlcpy(name, data.match.name[i], 8);
else
strlcpy(name, data.match.name[i], 9);
}
else
STRBUFCPY(name, data.match.name[i]);
V_DrawString(x+36, y, V_ALLOWLOWERCASE, name);
if (data.match.numplayers > 9)
{
if (intertype == int_match)
V_DrawRightAlignedString(x+152, y, 0, va("%i", data.match.scores[i]));
else if (intertype == int_race)
{
if (players[data.match.num[i]].pflags & PF_TIMEOVER)
snprintf(strtime, sizeof strtime, "DNF");
else
snprintf(strtime, sizeof strtime,
"%i:%02i.%02i",
G_TicsToMinutes(data.match.scores[i], true),
G_TicsToSeconds(data.match.scores[i]), G_TicsToCentiseconds(data.match.scores[i]));
strtime[sizeof strtime - 1] = '\0';
V_DrawRightAlignedString(x+152, y, 0, strtime);
}
}
else
{
if (intertype == int_match)
V_DrawRightAlignedString(x+152+BASEVIDWIDTH/2, y, 0, va("%u", data.match.scores[i]));
else if (intertype == int_race)
{
if (players[data.match.num[i]].pflags & PF_TIMEOVER)
snprintf(strtime, sizeof strtime, "DNF");
else
snprintf(strtime, sizeof strtime, "%i:%02i.%02i", G_TicsToMinutes(data.match.scores[i], true),
G_TicsToSeconds(data.match.scores[i]), G_TicsToCentiseconds(data.match.scores[i]));
strtime[sizeof strtime - 1] = '\0';
V_DrawRightAlignedString(x+152+BASEVIDWIDTH/2, y, 0, strtime);
}
}
}
y += 16;
if (y > 176)
{
y = 48;
x += BASEVIDWIDTH/2;
}
}
}
else if (intertype == int_ctf || intertype == int_teammatch)
{
INT32 x = 4, y = 0;
INT32 redplayers = 0, blueplayers = 0;
char name[MAXPLAYERNAME+1];
// Show the team flags and the team score at the top instead of "RESULTS"
V_DrawSmallScaledPatch(128 - SHORT(data.match.blueflag->width)/4, 2, 0, data.match.blueflag);
V_DrawCenteredString(128, 16, 0, va("%u", bluescore));
V_DrawSmallScaledPatch(192 - SHORT(data.match.redflag->width)/4, 2, 0, data.match.redflag);
V_DrawCenteredString(192, 16, 0, va("%u", redscore));
// draw the level name
V_DrawCenteredString(BASEVIDWIDTH/2, 24, 0, data.match.levelstring);
V_DrawFill(4, 42, 312, 1, 0);
//vert. line
V_DrawFill(160, 32, 1, 152, 0);
//strings at the top of the list
V_DrawCenteredString(x+6, 32, V_YELLOWMAP, "#");
V_DrawCenteredString(x+(BASEVIDWIDTH/2)+6, 32, V_YELLOWMAP, "#");
V_DrawString(x+36, 32, V_YELLOWMAP, "NAME");
V_DrawString(x+(BASEVIDWIDTH/2)+36, 32, V_YELLOWMAP, "NAME");
V_DrawRightAlignedString(x+152, 32, V_YELLOWMAP, "SCORE");
V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+152, 32, V_YELLOWMAP, "SCORE");
for (i = 0; i < data.match.numplayers; i++)
{
if (playeringame[data.match.num[i]] && !(data.match.spectator[i]))
{
UINT8 *colormap = R_GetTranslationColormap(*data.match.character[i], *data.match.color[i], GTC_CACHE);
if (*data.match.color[i] == SKINCOLOR_RED) //red
{
if (redplayers++ > 9)
continue;
x = 4 + (BASEVIDWIDTH/2);
y = (redplayers * 16) + 32;
V_DrawCenteredString(x+6, y, 0, va("%d", redplayers));
}
else if (*data.match.color[i] == SKINCOLOR_BLUE) //blue
{
if (blueplayers++ > 9)
continue;
x = 4;
y = (blueplayers * 16) + 32;
V_DrawCenteredString(x+6, y, 0, va("%d", blueplayers));
}
else
continue;
// Draw the back sprite, it looks ugly if we don't
V_DrawSmallScaledPatch(x+16, y-4, 0, livesback);
//color is ALWAYS going to be 6/7 here, no need to check if it's nonzero.
V_DrawSmallMappedPatch(x+16, y-4, 0,faceprefix[*data.match.character[i]], colormap);
strlcpy(name, data.match.name[i], 9);
V_DrawString(x+36, y, V_ALLOWLOWERCASE, name);
V_DrawRightAlignedString(x+152, y, 0, va("%u", data.match.scores[i]));
}
}
}
else if (intertype == int_classicrace)
{
INT32 x = 4;
INT32 y = 48;
UINT32 ptime, pring, pmaxring, pmonitor, pscore;
char sstrtime[10];
// draw the level name
V_DrawCenteredString(BASEVIDWIDTH/2, 8, 0, data.competition.levelstring);
V_DrawFill(4, 42, 312, 1, 0);
V_DrawCenteredString(x+6, 32, V_YELLOWMAP, "#");
V_DrawString(x+36, 32, V_YELLOWMAP, "NAME");
// Time
V_DrawRightAlignedString(x+160, 32, V_YELLOWMAP, "TIME");
// Rings
V_DrawThinString(x+168, 31, V_YELLOWMAP, "RING");
// Total rings
V_DrawThinString(x+191, 22, V_YELLOWMAP, "TOTAL");
V_DrawThinString(x+196, 31, V_YELLOWMAP, "RING");
// Monitors
V_DrawThinString(x+223, 22, V_YELLOWMAP, "ITEM");
V_DrawThinString(x+229, 31, V_YELLOWMAP, "BOX");
// Score
V_DrawRightAlignedString(x+288, 32, V_YELLOWMAP, "SCORE");
// Points
V_DrawRightAlignedString(x+312, 32, V_YELLOWMAP, "PT");
for (i = 0; i < data.competition.numplayers; i++)
{
ptime = (data.competition.times[i] & ~0x80000000);
pring = (data.competition.rings[i] & ~0x80000000);
pmaxring = (data.competition.maxrings[i] & ~0x80000000);
pmonitor = (data.competition.monitors[i] & ~0x80000000);
pscore = (data.competition.scores[i] & ~0x80000000);
V_DrawCenteredString(x+6, y, 0, va("%d", i+1));
if (playeringame[data.competition.num[i]])
{
// Draw the back sprite, it looks ugly if we don't
V_DrawSmallScaledPatch(x+16, y-4, 0, livesback);
if (data.competition.color[i] == 0)
V_DrawSmallScaledPatch(x+16, y-4, 0,faceprefix[*data.competition.character[i]]);
else
{
UINT8 *colormap = R_GetTranslationColormap(*data.competition.character[i], *data.competition.color[i], GTC_CACHE);
V_DrawSmallMappedPatch(x+16, y-4, 0,faceprefix[*data.competition.character[i]], colormap);
}
// already constrained to 8 characters
V_DrawString(x+36, y, V_ALLOWLOWERCASE, data.competition.name[i]);
if (players[data.competition.num[i]].pflags & PF_TIMEOVER)
snprintf(sstrtime, sizeof sstrtime, "Time Over");
else if (players[data.competition.num[i]].lives <= 0)
snprintf(sstrtime, sizeof sstrtime, "Game Over");
else
snprintf(sstrtime, sizeof sstrtime, "%i:%02i.%02i", G_TicsToMinutes(ptime, true),
G_TicsToSeconds(ptime), G_TicsToCentiseconds(ptime));
sstrtime[sizeof sstrtime - 1] = '\0';
// Time
V_DrawRightAlignedThinString(x+160, y-1, ((data.competition.times[i] & 0x80000000) ? V_YELLOWMAP : 0), sstrtime);
// Rings
V_DrawRightAlignedThinString(x+188, y-1, V_MONOSPACE|((data.competition.rings[i] & 0x80000000) ? V_YELLOWMAP : 0), va("%u", pring));
// Total rings
V_DrawRightAlignedThinString(x+216, y-1, V_MONOSPACE|((data.competition.maxrings[i] & 0x80000000) ? V_YELLOWMAP : 0), va("%u", pmaxring));
// Monitors
V_DrawRightAlignedThinString(x+244, y-1, V_MONOSPACE|((data.competition.monitors[i] & 0x80000000) ? V_YELLOWMAP : 0), va("%u", pmonitor));
// Score
V_DrawRightAlignedThinString(x+288, y-1, V_MONOSPACE|((data.competition.scores[i] & 0x80000000) ? V_YELLOWMAP : 0), va("%u", pscore));
// Final Points
V_DrawRightAlignedString(x+312, y, V_YELLOWMAP, va("%d", data.competition.points[i]));
}
y += 16;
if (y > 176)
break;
}
}
if (timer)
V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP,
va("start in %d seconds", timer/TICRATE));
// 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, V_YELLOWMAP, M_GetText("Teams will be scrambled next round!"));
}
//
// 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
if (paused || P_AutoPause())
return;
intertic++;
// Team scramble code for team match and CTF.
// Don't do this if we're going to automatically scramble teams next round.
if (G_GametypeHasTeams() && cv_teamscramble.value && !cv_scrambleonchange.value && server)
{
// 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();
}
// 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 (endtic != -1)
return; // tally is done
/* // SRB2kart
if (intertype == int_coop) // coop or single player, normal level
{
INT32 i;
UINT32 oldscore = data.coop.score;
boolean skip = false;
boolean anybonuses = false;
if (!intertic) // first time only
S_ChangeMusicInternal("lclear", false); // don't loop it
if (intertic < TICRATE) // one second pause before tally begins
return;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && (players[i].cmd.buttons & BT_BRAKE || players[i].cmd.buttons & BT_ACCELERATE))
skip = true;
// bonuses count down by 222 each tic
for (i = 0; i < 4; ++i)
{
if (!data.coop.bonuses[i].points)
continue;
data.coop.bonuses[i].points -= 222;
data.coop.total += 222;
data.coop.score += 222;
if (data.coop.bonuses[i].points < 0 || skip == true) // too far?
{
data.coop.score += data.coop.bonuses[i].points;
data.coop.total += data.coop.bonuses[i].points;
data.coop.bonuses[i].points = 0;
}
if (data.coop.bonuses[i].points > 0)
anybonuses = true;
}
if (!anybonuses)
{
endtic = intertic + 3*TICRATE; // 3 second pause after end of tally
S_StartSound(NULL, sfx_chchng); // cha-ching!
// Update when done with tally
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback)
{
if (M_UpdateUnlockablesAndExtraEmblems())
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData();
}
}
else if (!(intertic & 1))
S_StartSound(NULL, sfx_ptally); // tally sound effect
if (data.coop.gotlife > 0 && (skip == true || data.coop.score % 50000 < oldscore % 50000)) // just passed a 50000 point mark
{
// lives are already added since tally is fake, but play the music
P_PlayLivesJingle(NULL);
--data.coop.gotlife;
}
}
else if (intertype == int_spec) // coop or single player, special stage
{
INT32 i;
UINT32 oldscore = data.spec.score;
boolean skip = false;
static INT32 tallydonetic = 0;
if (!intertic) // first time only
{
S_ChangeMusicInternal("lclear", false); // don't loop it
tallydonetic = 0;
}
if (intertic < TICRATE) // one second pause before tally begins
return;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && (players[i].cmd.buttons & BT_BRAKE || players[i].cmd.buttons & BT_ACCELERATE))
skip = true;
if (tallydonetic != 0)
{
if (intertic > tallydonetic)
{
endtic = intertic + 4*TICRATE; // 4 second pause after end of tally
S_StartSound(NULL, sfx_flgcap); // cha-ching!
}
return;
}
// ring bonus counts down by 222 each tic
data.spec.bonus.points -= 222;
data.spec.score += 222;
if (data.spec.bonus.points < 0 || skip == true) // went too far
{
data.spec.score += data.spec.bonus.points;
data.spec.bonus.points = 0;
}
if (!data.spec.bonus.points)
{
if (data.spec.continues & 0x80) // don't set endtic yet!
tallydonetic = intertic + (3*TICRATE)/2;
else // okay we're good.
endtic = intertic + 4*TICRATE; // 4 second pause after end of tally
S_StartSound(NULL, sfx_chchng); // cha-ching!
// Update when done with tally
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback)
{
if (M_UpdateUnlockablesAndExtraEmblems())
S_StartSound(NULL, sfx_ncitem);
G_SaveGameData();
}
}
else if (!(intertic & 1))
S_StartSound(NULL, sfx_ptally); // tally sound effect
if (data.spec.gotlife > 0 && (skip == true || data.spec.score % 50000 < oldscore % 50000)) // just passed a 50000 point mark
{
// lives are already added since tally is fake, but play the music
P_PlayLivesJingle(NULL);
--data.spec.gotlife;
}
}
*/
if (intertype == int_timeattack)
{
if (!intertic)
endtic = intertic + 10*TICRATE; // 10 second pause after end of tally
}
else if (intertype == int_race)
{
INT32 q=0,r=0;
/* // SRB2kart - removed temporarily.
if (!intertic) {
if (!((music_playing == "karwin") // Mario Kart Win
|| (music_playing == "karok") // Mario Kart Ok
|| (music_playing == "karlos"))) // Mario Kart Lose
S_ChangeMusicInternal("racent", true); // Backup Plan
}*/
if (intertic < TICRATE || intertic % 8)
return;
for (q = 0; q < data.match.numplayers; q++)
{
if (data.match.increase[q]) {
data.match.increase[q]--;
r++;
}
}
if (r)
S_StartSound(NULL, sfx_menu1);
else
if (modeattacking)
endtic = intertic + 10*TICRATE; // 10 second pause after end of tally
else
endtic = intertic + 3*TICRATE; // 3 second pause after end of tally
}
else if (intertype == int_match || intertype == int_ctf || intertype == int_teammatch) // match
{
if (!intertic) // first time only
S_ChangeMusicInternal("racent", true); // loop it
// If a player has left or joined, recalculate scores.
if (data.match.numplayers != D_NumPlayers())
Y_CalculateMatchWinners();
}
else if (intertype == int_race || intertype == int_classicrace) // race
{
if (!intertic) // first time only
S_ChangeMusicInternal("racent", true); // loop it
// Don't bother recalcing for race. It doesn't make as much sense.
}
}
//
// 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) || (bestlap < mainrecords[gamemap-1]->lap))
mainrecords[gamemap-1]->lap = bestlap;
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(players[consoleplayer].realtime, bestlap);
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 emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// 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)
{
INT32 i;
intertic = -1;
#ifdef PARANOIA
if (endtic != -1)
I_Error("endtic is dirty");
#endif
if (!multiplayer)
{
timer = 0;
/*
if (G_IsSpecialStage(gamemap))
intertype = (maptol & TOL_NIGHTS) ? int_nightsspec : int_spec;
else
intertype = (maptol & TOL_NIGHTS) ? int_nights : int_coop;
*/
/* // 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
{
timer = cv_inttime.value*TICRATE;
if (!timer)
timer = 1;
}
/* // SRB2kart
if (gametype == GT_COOP)
{
// Nights intermission is single player only
// Don't add it here
if (G_IsSpecialStage(gamemap))
intertype = int_spec;
else
intertype = int_coop;
}
else */
if (gametype == GT_TEAMMATCH)
intertype = int_teammatch;
else if (gametype == GT_MATCH
|| gametype == GT_TAG
|| gametype == GT_HIDEANDSEEK)
intertype = int_match;
else if (gametype == GT_RACE)
intertype = int_race;
else if (gametype == GT_COMPETITION)
intertype = int_classicrace;
else if (gametype == GT_CTF)
intertype = int_ctf;
}
// 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;
// This should always exist, but just in case...
if(!mapheaderinfo[prevmap])
P_AllocMapHeader(prevmap);
switch (intertype)
{
case int_nights:
// Can't fail
//G_SetNightsRecords();
// Check records
{
UINT8 earnedEmblems = M_CheckLevelEmblems();
if (earnedEmblems)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
// fall back into the coop intermission for now
intertype = int_timeattack;
/* FALLTHRU */
case int_timeattack: // coop or single player, normal level // SRB2kart 230117 - replaced int_coop
{
// award time and ring bonuses
// Y_AwardCoopBonuses();
// setup time data
data.coop.tics = players[consoleplayer].realtime;
if ((!modifiedgame || savemoddata) && !multiplayer && !demoplayback)
{
// 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();
}
for (i = 0; i < 4; ++i)
data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_STATIC);
data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC);
// get act number
if (mapheaderinfo[prevmap]->actnum)
data.coop.ttlnum = W_CachePatchName(va("TTL%.2d", mapheaderinfo[prevmap]->actnum),
PU_STATIC);
else
data.coop.ttlnum = W_CachePatchName("TTL01", PU_STATIC);
// get background patches
widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC);
bgpatch = W_CachePatchName("INTERSCR", PU_STATIC);
// grab an interscreen if appropriate
if (mapheaderinfo[gamemap-1]->interscreen[0] != '#')
{
interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_STATIC);
useinterpic = true;
usebuffer = false;
}
else
{
useinterpic = false;
#ifdef HWRENDER
if (rendermode == render_opengl)
usebuffer = true; // This needs to be here for OpenGL, otherwise usebuffer is never set to true for it, and thus there's no screenshot in the intermission
#endif
}
usetile = false;
// set up the "got through act" message according to skin name
// too long so just show "YOU GOT THROUGH THE ACT"
if (strlen(skins[players[consoleplayer].skin].realname) > 13)
{
strcpy(data.coop.passed1, "YOU GOT");
strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "THROUGH ACT" : "THROUGH THE ACT");
}
// long enough that "X GOT" won't fit so use "X PASSED THE ACT"
else if (strlen(skins[players[consoleplayer].skin].realname) > 8)
{
strcpy(data.coop.passed1, skins[players[consoleplayer].skin].realname);
strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "PASSED ACT" : "PASSED THE ACT");
}
// length is okay for normal use
else
{
snprintf(data.coop.passed1, sizeof data.coop.passed1, "%s GOT",
skins[players[consoleplayer].skin].realname);
strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "THROUGH ACT" : "THROUGH THE ACT");
}
// set X positions
if (mapheaderinfo[gamemap-1]->actnum)
{
data.coop.passedx1 = 62 + (176 - V_LevelNameWidth(data.coop.passed1))/2;
data.coop.passedx2 = 62 + (176 - V_LevelNameWidth(data.coop.passed2))/2;
}
else
{
data.coop.passedx1 = (BASEVIDWIDTH - V_LevelNameWidth(data.coop.passed1))/2;
data.coop.passedx2 = (BASEVIDWIDTH - V_LevelNameWidth(data.coop.passed2))/2;
}
// The above value is not precalculated because it needs only be computed once
// at the start of intermission, and precalculating it would preclude mods
// changing the font to one of a slightly different width.
break;
}
/* // SRB2kart 230117 - removed
case int_nightsspec:
if (modeattacking && stagefailed)
{
// Nuh-uh. Get out of here.
Y_EndIntermission();
Y_FollowIntermission();
break;
}
if (!stagefailed)
G_SetNightsRecords();
// Check records
{
UINT8 earnedEmblems = M_CheckLevelEmblems();
if (earnedEmblems)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
// fall back into the special stage intermission for now
intertype = int_spec;
// FALLTHRU
case int_spec: // coop or single player, special stage
{
// Update visitation flags?
if ((!modifiedgame || savemoddata) && !multiplayer && !demoplayback)
{
if (!stagefailed)
mapvisited[gamemap-1] |= MV_BEATEN;
}
// give out ring bonuses
Y_AwardSpecialStageBonus();
data.spec.bonuspatch = W_CachePatchName(data.spec.bonus.patch, PU_STATIC);
data.spec.pscore = W_CachePatchName("YB_SCORE", PU_STATIC);
data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_STATIC);
// get background tile
bgtile = W_CachePatchName("SPECTILE", PU_STATIC);
// grab an interscreen if appropriate
if (mapheaderinfo[gamemap-1]->interscreen[0] != '#')
{
interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_STATIC);
useinterpic = true;
}
else
useinterpic = false;
// tile if using the default background
usetile = !useinterpic;
// get special stage specific patches
if (!stagefailed && ALL7EMERALDS(emeralds))
{
data.spec.cemerald = W_CachePatchName("GOTEMALL", PU_STATIC);
data.spec.headx = 70;
data.spec.nowsuper = players[consoleplayer].skin
? NULL : W_CachePatchName("NOWSUPER", PU_STATIC);
}
else
{
data.spec.cemerald = W_CachePatchName("CEMERALD", PU_STATIC);
data.spec.headx = 48;
data.spec.nowsuper = NULL;
}
// Super form stuff (normally blank)
data.spec.passed3[0] = '\0';
data.spec.passed4[0] = '\0';
// Super form stuff (normally blank)
data.spec.passed3[0] = '\0';
data.spec.passed4[0] = '\0';
// set up the "got through act" message according to skin name
if (stagefailed)
{
strcpy(data.spec.passed2, "SPECIAL STAGE");
data.spec.passed1[0] = '\0';
}
else if (ALL7EMERALDS(emeralds))
{
snprintf(data.spec.passed1,
sizeof data.spec.passed1, "%s",
skins[players[consoleplayer].skin].realname);
data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0';
strcpy(data.spec.passed2, "GOT THEM ALL!");
if (skins[players[consoleplayer].skin].flags & SF_SUPER)
{
strcpy(data.spec.passed3, "CAN NOW BECOME");
snprintf(data.spec.passed4,
sizeof data.spec.passed4, "SUPER %s",
skins[players[consoleplayer].skin].realname);
data.spec.passed4[sizeof data.spec.passed4 - 1] = '\0';
}
}
else
{
if (strlen(skins[players[consoleplayer].skin].realname) <= SKINNAMESIZE-5)
{
snprintf(data.spec.passed1,
sizeof data.spec.passed1, "%s GOT",
skins[players[consoleplayer].skin].realname);
data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0';
}
else
strcpy(data.spec.passed1, "YOU GOT");
strcpy(data.spec.passed2, "A CHAOS EMERALD");
}
data.spec.passedx1 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed1))/2;
data.spec.passedx2 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed2))/2;
data.spec.passedx3 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed3))/2;
data.spec.passedx4 = (BASEVIDWIDTH - V_LevelNameWidth(data.spec.passed4))/2;
break;
}*/
case int_match:
{
// Calculate who won
Y_CalculateMatchWinners();
// set up the levelstring
if (mapheaderinfo[prevmap]->zonttl)
{
if (mapheaderinfo[prevmap]->actnum)
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"%.32s %.32s * %d *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->zonttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %.32s %.32s *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->zonttl);
}
else
{
if (mapheaderinfo[prevmap]->actnum)
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"%.32s * %d *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %.32s *",
mapheaderinfo[prevmap]->lvlttl);
}
data.match.levelstring[sizeof data.match.levelstring - 1] = '\0';
// get RESULT header
data.match.result =
W_CachePatchName("RESULT", PU_STATIC);
bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
usetile = true;
useinterpic = false;
break;
}
case int_race: // (time-only race)
{
if ((!modifiedgame || savemoddata) && !multiplayer && !demoplayback) // remove this once we have a proper time attack screen
{
// setup time data
data.coop.tics = players[consoleplayer].realtime;
// 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();
}
// Calculate who won
Y_CalculateTournamentPoints();
// set up the levelstring
if (mapheaderinfo[prevmap]->zonttl)
{
if (mapheaderinfo[prevmap]->actnum)
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"%.32s %.32s * %d *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->zonttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %.32s %.32s *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->zonttl);
}
else
{
if (mapheaderinfo[prevmap]->actnum)
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"%.32s * %d *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %.32s *",
mapheaderinfo[prevmap]->lvlttl);
}
data.match.levelstring[sizeof data.match.levelstring - 1] = '\0';
// get RESULT header
//data.match.result = W_CachePatchName("RESULT", PU_STATIC);
bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
usetile = true;
useinterpic = false;
break;
}
case int_teammatch:
case int_ctf:
{
// Calculate who won
Y_CalculateMatchWinners();
// set up the levelstring
if (mapheaderinfo[prevmap]->actnum)
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"%.32s * %d *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.match.levelstring,
sizeof data.match.levelstring,
"* %.32s *",
mapheaderinfo[prevmap]->lvlttl);
data.match.levelstring[sizeof data.match.levelstring - 1] = '\0';
if (intertype == int_ctf)
{
data.match.redflag = rflagico;
data.match.blueflag = bflagico;
}
else // team match
{
data.match.redflag = rmatcico;
data.match.blueflag = bmatcico;
}
bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
usetile = true;
useinterpic = false;
break;
}
case int_classicrace: // classic (full race)
{
// find out who won
Y_CalculateCompetitionWinners();
// set up the levelstring
if (mapheaderinfo[prevmap]->actnum)
snprintf(data.competition.levelstring,
sizeof data.competition.levelstring,
"%.32s * %d *",
mapheaderinfo[prevmap]->lvlttl, mapheaderinfo[prevmap]->actnum);
else
snprintf(data.competition.levelstring,
sizeof data.competition.levelstring,
"* %.32s *",
mapheaderinfo[prevmap]->lvlttl);
data.competition.levelstring[sizeof data.competition.levelstring - 1] = '\0';
// get background tile
bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
usetile = true;
useinterpic = false;
break;
}
case int_none:
default:
break;
}
}
//
// Y_CalculateMatchWinners
//
static void Y_CalculateMatchWinners(void)
{
INT32 i, j;
boolean completed[MAXPLAYERS];
// Initialize variables
memset(data.match.scores, 0, sizeof (data.match.scores));
memset(data.match.color, 0, sizeof (data.match.color));
memset(data.match.character, 0, sizeof (data.match.character));
memset(data.match.spectator, 0, sizeof (data.match.spectator));
memset(completed, 0, sizeof (completed));
data.match.numplayers = 0;
i = j = 0;
for (j = 0; j < MAXPLAYERS; j++)
{
if (!playeringame[j])
continue;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].score >= data.match.scores[data.match.numplayers] && completed[i] == false)
{
data.match.scores[data.match.numplayers] = players[i].score;
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];
data.match.spectator[data.match.numplayers] = players[i].spectator;
data.match.num[data.match.numplayers] = i;
}
}
completed[data.match.num[data.match.numplayers]] = true;
data.match.numplayers++;
}
}
/*
//
// Y_CalculateTimeRaceWinners
//
static void Y_CalculateTimeRaceWinners(void)
{
INT32 i, j;
boolean completed[MAXPLAYERS];
// Initialize variables
for (i = 0; i < MAXPLAYERS; i++)
data.match.scores[i] = INT32_MAX;
memset(data.match.color, 0, sizeof (data.match.color));
memset(data.match.character, 0, sizeof (data.match.character));
memset(data.match.spectator, 0, sizeof (data.match.spectator));
memset(completed, 0, sizeof (completed));
data.match.numplayers = 0;
i = j = 0;
for (j = 0; j < MAXPLAYERS; j++)
{
if (!playeringame[j])
continue;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].realtime <= data.match.scores[data.match.numplayers] && completed[i] == false)
{
data.match.scores[data.match.numplayers] = players[i].realtime;
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];
data.match.num[data.match.numplayers] = i;
}
}
completed[data.match.num[data.match.numplayers]] = true;
data.match.numplayers++;
}
}
*/
//
// Y_CalculateCompetitionWinners
//
static void Y_CalculateCompetitionWinners(void)
{
INT32 i, j;
boolean bestat[5];
boolean completed[MAXPLAYERS];
INT32 winner; // shortcut
UINT32 points[MAXPLAYERS];
UINT32 times[MAXPLAYERS];
UINT32 rings[MAXPLAYERS];
UINT32 maxrings[MAXPLAYERS];
UINT32 monitors[MAXPLAYERS];
UINT32 scores[MAXPLAYERS];
memset(data.competition.points, 0, sizeof (data.competition.points));
memset(points, 0, sizeof (points));
memset(completed, 0, sizeof (completed));
// Award points.
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
for (j = 0; j < 5; j++)
bestat[j] = true;
times[i] = players[i].realtime;
rings[i] = (UINT32)max(players[i].health-1, 0);
maxrings[i] = (UINT32)players[i].totalring;
monitors[i] = (UINT32)players[i].numboxes;
scores[i] = (UINT32)min(players[i].score, 99999990);
for (j = 0; j < MAXPLAYERS; j++)
{
if (!playeringame[j] || j == i)
continue;
if (players[i].realtime <= players[j].realtime)
points[i]++;
else
bestat[0] = false;
if (max(players[i].health-1, 0) >= max(players[j].health-1, 0))
points[i]++;
else
bestat[1] = false;
if (players[i].totalring >= players[j].totalring)
points[i]++;
else
bestat[2] = false;
if (players[i].numboxes >= players[j].numboxes)
points[i]++;
else
bestat[3] = false;
if (players[i].score >= players[j].score)
points[i]++;
else
bestat[4] = false;
}
// Highlight best scores
if (bestat[0])
times[i] |= 0x80000000;
if (bestat[1])
rings[i] |= 0x80000000;
if (bestat[2])
maxrings[i] |= 0x80000000;
if (bestat[3])
monitors[i] |= 0x80000000;
if (bestat[4])
scores[i] |= 0x80000000;
}
// Now we go through and set the data.competition struct properly
data.competition.numplayers = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
winner = 0;
for (j = 0; j < MAXPLAYERS; j++)
{
if (!playeringame[j])
continue;
if (points[j] >= data.competition.points[data.competition.numplayers] && completed[j] == false)
{
data.competition.points[data.competition.numplayers] = points[j];
data.competition.num[data.competition.numplayers] = winner = j;
}
}
// We know this person won this spot, now let's set everything appropriately
data.competition.times[data.competition.numplayers] = times[winner];
data.competition.rings[data.competition.numplayers] = rings[winner];
data.competition.maxrings[data.competition.numplayers] = maxrings[winner];
data.competition.monitors[data.competition.numplayers] = monitors[winner];
data.competition.scores[data.competition.numplayers] = scores[winner];
snprintf(data.competition.name[data.competition.numplayers], 9, "%s", player_names[winner]);
data.competition.name[data.competition.numplayers][8] = '\0';
data.competition.color[data.competition.numplayers] = &players[winner].skincolor;
data.competition.character[data.competition.numplayers] = &players[winner].skin;
completed[winner] = true;
data.competition.numplayers++;
}
}
// ============
// COOP BONUSES
// ============
//
// Y_SetNullBonus
// No bonus in this slot, but we need to set some things anyway.
//
static void Y_SetNullBonus(player_t *player, y_bonus_t *bstruct)
{
(void)player;
memset(bstruct, 0, sizeof(y_bonus_t));
strncpy(bstruct->patch, "MISSING", sizeof(bstruct->patch));
}
//
// Y_SetTimeBonus
//
static void Y_SetTimeBonus(player_t *player, y_bonus_t *bstruct)
{
INT32 secs, bonus;
strncpy(bstruct->patch, "YB_TIME", sizeof(bstruct->patch));
bstruct->display = true;
// calculate time bonus
secs = player->realtime / TICRATE;
if (secs < 30) /* :30 */ bonus = 100000;
else if (secs < 45) /* :45 */ bonus = 50000;
else if (secs < 60) /* 1:00 */ bonus = 10000;
else if (secs < 90) /* 1:30 */ bonus = 5000;
else if (secs < 120) /* 2:00 */ bonus = 4000;
else if (secs < 180) /* 3:00 */ bonus = 3000;
else if (secs < 240) /* 4:00 */ bonus = 2000;
else if (secs < 300) /* 5:00 */ bonus = 1000;
else if (secs < 360) /* 6:00 */ bonus = 500;
else if (secs < 420) /* 7:00 */ bonus = 400;
else if (secs < 480) /* 8:00 */ bonus = 300;
else if (secs < 540) /* 9:00 */ bonus = 200;
else if (secs < 600) /* 10:00 */ bonus = 100;
else /* TIME TAKEN: TOO LONG */ bonus = 0;
bstruct->points = bonus;
}
//
// Y_SetRingBonus
//
static void Y_SetRingBonus(player_t *player, y_bonus_t *bstruct)
{
strncpy(bstruct->patch, "YB_RING", sizeof(bstruct->patch));
bstruct->display = true;
bstruct->points = max(0, (player->health-1) * 100);
}
//
// Y_SetLinkBonus
//
/*
static void Y_SetLinkBonus(player_t *player, y_bonus_t *bstruct) // SRB2kart - unused.
{
strncpy(bstruct->patch, "YB_LINK", sizeof(bstruct->patch));
bstruct->display = true;
bstruct->points = max(0, (player->maxlink - 1) * 100);
}
*/
//
// Y_SetGuardBonus
//
static void Y_SetGuardBonus(player_t *player, y_bonus_t *bstruct)
{
INT32 bonus;
strncpy(bstruct->patch, "YB_GUARD", sizeof(bstruct->patch));
bstruct->display = true;
if (player->timeshit == 0) bonus = 10000;
else if (player->timeshit == 1) bonus = 5000;
else if (player->timeshit == 2) bonus = 1000;
else if (player->timeshit == 3) bonus = 500;
else if (player->timeshit == 4) bonus = 100;
else bonus = 0;
bstruct->points = bonus;
}
//
// Y_SetPerfectBonus
//
static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct)
{
INT32 i;
(void)player;
memset(bstruct, 0, sizeof(y_bonus_t));
strncpy(bstruct->patch, "YB_PERFE", sizeof(bstruct->patch));
if (data.coop.gotperfbonus == -1)
{
INT32 sharedringtotal = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i]) continue;
sharedringtotal += players[i].health - 1;
}
if (!sharedringtotal || sharedringtotal < nummaprings)
data.coop.gotperfbonus = 0;
else
data.coop.gotperfbonus = 1;
}
if (!data.coop.gotperfbonus)
return;
bstruct->display = true;
bstruct->points = 50000;
}
// This list can be extended in the future with SOC/Lua, perhaps.
typedef void (*bonus_f)(player_t *, y_bonus_t *);
bonus_f bonuses_list[4][4] = {
{
Y_SetNullBonus,
Y_SetNullBonus,
Y_SetNullBonus,
Y_SetNullBonus,
},
{
Y_SetNullBonus,
Y_SetTimeBonus,
Y_SetRingBonus,
Y_SetPerfectBonus,
},
{
Y_SetNullBonus,
Y_SetGuardBonus,
Y_SetRingBonus,
Y_SetNullBonus,
},
{
Y_SetNullBonus,
Y_SetGuardBonus,
Y_SetRingBonus,
Y_SetPerfectBonus,
},
};
/* // SRB2kart 230117 - Replaced with Y_CalculateTournamentPoints
//
// Y_AwardCoopBonuses
//
// Awards the time and ring bonuses.
//
static void Y_AwardCoopBonuses(void)
{
INT32 i, j, bonusnum, oldscore, ptlives;
y_bonus_t localbonuses[4];
// set score/total first
data.coop.total = 0;
data.coop.score = players[consoleplayer].score;
data.coop.gotperfbonus = -1;
memset(data.coop.bonuses, 0, sizeof(data.coop.bonuses));
memset(data.coop.bonuspatches, 0, sizeof(data.coop.bonuspatches));
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i] || players[i].lives < 1) // not active or game over
bonusnum = 0; // all null
else
bonusnum = mapheaderinfo[prevmap]->bonustype + 1; // -1 is none
oldscore = players[i].score;
for (j = 0; j < 4; ++j) // Set bonuses
{
(bonuses_list[bonusnum][j])(&players[i], &localbonuses[j]);
players[i].score += localbonuses[j].points;
}
ptlives = (!ultimatemode && !modeattacking) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0;
if (ptlives)
P_GivePlayerLives(&players[i], ptlives);
if (i == consoleplayer)
{
data.coop.gotlife = ptlives;
M_Memcpy(&data.coop.bonuses, &localbonuses, sizeof(data.coop.bonuses));
}
}
// Just in case the perfect bonus wasn't checked.
if (data.coop.gotperfbonus < 0)
data.coop.gotperfbonus = 0;
}
//
// Y_AwardSpecialStageBonus
//
// Gives a ring bonus only.
static void Y_AwardSpecialStageBonus(void)
{
INT32 i, oldscore, ptlives;
y_bonus_t localbonus;
data.spec.score = players[consoleplayer].score;
memset(&data.spec.bonus, 0, sizeof(data.spec.bonus));
data.spec.bonuspatch = NULL;
for (i = 0; i < MAXPLAYERS; i++)
{
oldscore = players[i].score;
if (!playeringame[i] || players[i].lives < 1) // not active or game over
Y_SetNullBonus(&players[i], &localbonus);
else if (useNightsSS) // Link instead of Score
Y_SetLinkBonus(&players[i], &localbonus);
else
Y_SetRingBonus(&players[i], &localbonus);
players[i].score += localbonus.points;
// grant extra lives right away since tally is faked
ptlives = (!ultimatemode && !modeattacking) ? max((players[i].score/50000) - (oldscore/50000), 0) : 0;
if (ptlives)
P_GivePlayerLives(&players[i], ptlives);
if (i == consoleplayer)
{
M_Memcpy(&data.spec.bonus, &localbonus, sizeof(data.spec.bonus));
data.spec.gotlife = ptlives;
// Continues related
data.spec.continues = min(players[i].continues, 8);
if (players[i].gotcontinue)
data.spec.continues |= 0x80;
data.spec.playercolor = &players[i].skincolor;
data.spec.playerchar = &players[i].skin;
}
}
}
*/
//
// Y_CalculateTournamentPoints
//
static void Y_CalculateTournamentPoints(void)
{
INT32 i, j;
boolean completed[MAXPLAYERS];
INT32 numplayersingame = 0;
INT32 increase[MAXPLAYERS];
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
numplayersingame++;
}
for (i = 0; i < numplayersingame; i++)
{
increase[i] = numplayersingame-i;
if (increase[i] < 0)
increase[i] = 0;
}
// Initialize variables
for (j = 0; j < MAXPLAYERS; j++)
data.match.scores[j] = INT32_MAX;
memset(data.match.time, 0, sizeof (data.match.time));
memset(data.match.color, 0, sizeof (data.match.color));
memset(data.match.character, 0, sizeof (data.match.character));
memset(data.match.spectator, 0, sizeof (data.match.spectator));
memset(data.match.increase, 0, sizeof (data.match.increase));
memset(completed, 0, sizeof (completed));
data.match.numplayers = 0;
i = j = 0;
for (j = 0; j < MAXPLAYERS; j++)
{
if (!playeringame[j])
continue;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].realtime <= data.match.scores[data.match.numplayers] && completed[i] == false)
{
data.match.time[data.match.numplayers] = players[i].realtime;
data.match.scores[data.match.numplayers] = players[i].realtime;
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];
data.match.spectator[data.match.numplayers] = players[i].spectator;
data.match.num[data.match.numplayers] = i;
}
}
completed[data.match.num[data.match.numplayers]] = true;
if (!(players[data.match.num[data.match.numplayers]].pflags & PF_TIMEOVER
|| players[data.match.num[data.match.numplayers]].spectator))
data.match.increase[data.match.numplayers] = increase[data.match.numplayers];
players[data.match.num[data.match.numplayers]].score += data.match.increase[data.match.numplayers];
data.match.scores[data.match.numplayers] = players[data.match.num[data.match.numplayers]].score;
data.match.numplayers++;
}
}
// ======
//
// Y_EndIntermission
//
void Y_EndIntermission(void)
{
Y_UnloadData();
endtic = -1;
intertype = int_none;
usebuffer = false;
}
//
// Y_EndGame
//
// Why end the game?
// Because Y_FollowIntermission and F_EndCutscene would
// both do this exact same thing *in different ways* otherwise,
// which made it so that you could only unlock Ultimate mode
// if you had a cutscene after the final level and crap like that.
// This function simplifies it so only one place has to be updated
// when something new is added.
void Y_EndGame(void)
{
// Only do evaluation and credits in coop games.
if (gametype == GT_COOP)
{
if (nextmap == 1102-1) // end game with credits
{
F_StartCredits();
return;
}
if (nextmap == 1101-1) // end game with evaluation
{
F_StartGameEvaluation();
return;
}
}
// 1100 or competitive multiplayer, so go back to title screen.
D_StartTitle();
}
//
// Y_FollowIntermission
//
static void Y_FollowIntermission(void)
{
if (modeattacking)
{
M_EndModeAttackRun();
return;
}
if (nextmap < 1100-1)
{
// normal level
G_AfterIntermission();
return;
}
// Start a custom cutscene if there is one.
if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking)
{
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
return;
}
Y_EndGame();
}
#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)
{
/*
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:
UNLOAD(data.match.result);
break;
case int_ctf:
UNLOAD(data.match.blueflag);
UNLOAD(data.match.redflag);
break;
default:
//without this default,
//int_none, int_tag, int_chaos, and int_classicrace
//are not handled
break;
}
}
// SRB2Kart: Voting!
//
// Y_VoteDrawer
//
// Draws the voting screen!
//
void Y_VoteDrawer(void)
{
INT32 i, x, y = 0;
if (rendermode == render_none)
return;
if (votetic >= voteendtic && voteendtic != -1)
return;
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx > 320)
V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(widebgpatch->width)/2),
(vid.height / vid.dupy) - SHORT(widebgpatch->height),
V_SNAPTOTOP|V_SNAPTOLEFT, widebgpatch);
else
V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(bgpatch->width)/2), // Keep the width/height adjustments, for screens that are less wide than 320(?)
(vid.height / vid.dupy) - SHORT(bgpatch->height),
V_SNAPTOTOP|V_SNAPTOLEFT, bgpatch);
y = 30;
for (i = 0; i < 4; i++)
{
char str[40];
patch_t *pic;
if (i == 3)
{
snprintf(str, sizeof str, "%.32s", "RANDOM");
str[sizeof str - 1] = '\0';
pic = randomlvl;
}
else
{
strcpy(str, levelinfo[i].str);
pic = levelinfo[i].pic;
}
if (i == voteclient.selection)
{
if (votes[consoleplayer] == -1)
{
V_DrawScaledPatch(BASEVIDWIDTH-124, y+21, V_SNAPTORIGHT, cursor);
if (votetic % 4 > 1)
V_DrawFill(BASEVIDWIDTH-101, y-1, 82, 52, 120|V_SNAPTORIGHT);
else
V_DrawFill(BASEVIDWIDTH-101, y-1, 82, 52, 103|V_SNAPTORIGHT);
}
V_DrawSmallScaledPatch(BASEVIDWIDTH-100, y, V_SNAPTORIGHT, pic);
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 40+y, V_SNAPTORIGHT, str);
y += 55;
}
else
{
V_DrawTinyScaledPatch(BASEVIDWIDTH-60, y, V_SNAPTORIGHT, pic);
y += 30;
}
}
x = 20;
y = 10;
for (i = 0; i < MAXPLAYERS; i++)
{
if ((playeringame[i] && !players[i].spectator) && votes[i] != -1)
{
patch_t *pic;
if (votes[i] == 3 && (i != pickedvote || voteendtic == -1))
pic = randomlvl;
else
pic = levelinfo[votes[i]].pic;
if (!timer && i == voteclient.ranim)
{
V_DrawScaledPatch(x-18, y+9, V_SNAPTOLEFT, cursor);
if (votetic % 4 > 1)
V_DrawFill(x-1, y-1, 42, 27, 120|V_SNAPTOLEFT);
else
V_DrawFill(x-1, y-1, 42, 27, 103|V_SNAPTOLEFT);
}
V_DrawTinyScaledPatch(x, y, V_SNAPTOLEFT, pic);
if (players[i].skincolor == 0)
V_DrawSmallScaledPatch(x+24, y+9, V_SNAPTOLEFT, faceprefix[players[i].skin]);
else
{
UINT8 *colormap = R_GetTranslationColormap(players[i].skin, players[i].skincolor, GTC_CACHE);
V_DrawSmallMappedPatch(x+24, y+9, V_SNAPTOLEFT, faceprefix[players[i].skin], colormap);
}
}
if (splitscreen) // only 1p has a vote in splitscreen
break;
y += 30;
if (y > BASEVIDHEIGHT-40)
{
x += 60;
y = 10;
}
}
if (timer)
V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP|V_SNAPTOBOTTOM,
va("Vote ends in %d seconds", timer/TICRATE));
}
//
// Y_VoteTicker
//
// Vote screen thinking :eggthinking:
//
void Y_VoteTicker(void)
{
boolean pressed = false;
INT32 i;
if (paused || P_AutoPause())
return;
votetic++;
if (votetic == voteendtic)
{
Y_UnloadVoteData(); // Y_EndVote resets voteendtic too early apparently, causing the game to try to render patches that we just unloaded...
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 && !splitscreen)
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 (voteclient.delay)
voteclient.delay--;
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++;
}
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 (tempvotes[((pickedvote + voteclient.roffset + 4) % numvotes)] == pickedvote
&& voteclient.rsynctime % 50 == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V)
{
voteclient.rendoff = voteclient.roffset+4;
S_ChangeMusicInternal("voteeb", false);
}
}
else if (voteclient.roffset >= voteclient.rendoff)
{
voteendtic = votetic + (3*TICRATE);
S_StartSound(NULL, sfx_kc48);
}
}
}
else
voteclient.ranim = pickedvote;
}
else
{
if (votetic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V
return;
if ((playeringame[consoleplayer] && !players[consoleplayer].spectator)
&& !voteclient.delay && pickedvote == -1 && votes[consoleplayer] == -1)
{
if (InputDown(gc_aimforward, 1) || JoyAxis(AXISMOVE, 1) < 0)
{
voteclient.selection--;
pressed = true;
}
if ((InputDown(gc_aimbackward, 1) || JoyAxis(AXISMOVE, 1) > 0) && !pressed)
{
voteclient.selection++;
pressed = true;
}
if (voteclient.selection < 0)
voteclient.selection = 3;
if (voteclient.selection > 3)
voteclient.selection = 0;
if (InputDown(gc_accelerate, 1) && !pressed)
{
D_ModifyClientVote(voteclient.selection);
pressed = true;
}
}
if (pressed)
{
S_StartSound(NULL, sfx_kc4a);
voteclient.delay = NEWTICRATE/7;
}
if (server)
{
if (timer == 0)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if ((playeringame[i] && !players[i].spectator) && votes[i] == -1 && !splitscreen)
votes[i] = 3;
}
}
else
{
if (splitscreen)
{
if (votes[0] == -1)
return;
}
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
//
void Y_StartVote(void)
{
INT32 i = 0;
votetic = -1;
#ifdef PARANOIA
if (voteendtic != -1)
I_Error("voteendtic is dirty");
#endif
widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC);
bgpatch = W_CachePatchName("INTERSCR", PU_STATIC);
cursor = W_CachePatchName("M_CURSOR", PU_STATIC);
randomlvl = W_CachePatchName("RANDOMLV", PU_STATIC);
timer = cv_votetime.value*TICRATE;
pickedvote = -1;
voteclient.selection = 0;
voteclient.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 str
if (mapheaderinfo[votelevels[i]]->zonttl)
{
if (mapheaderinfo[votelevels[i]]->actnum)
snprintf(levelinfo[i].str,
sizeof levelinfo[i].str,
"%.32s %.32s %d",
mapheaderinfo[votelevels[i]]->lvlttl, mapheaderinfo[votelevels[i]]->zonttl, mapheaderinfo[votelevels[i]]->actnum);
else
snprintf(levelinfo[i].str,
sizeof levelinfo[i].str,
"%.32s %.32s",
mapheaderinfo[votelevels[i]]->lvlttl, mapheaderinfo[votelevels[i]]->zonttl);
}
else
{
if (mapheaderinfo[votelevels[i]]->actnum)
snprintf(levelinfo[i].str,
sizeof levelinfo[i].str,
"%.32s %d",
mapheaderinfo[votelevels[i]]->lvlttl, mapheaderinfo[votelevels[i]]->actnum);
else
snprintf(levelinfo[i].str,
sizeof levelinfo[i].str,
"%.32s",
mapheaderinfo[votelevels[i]]->lvlttl);
}
levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0';
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(votelevels[i]+1)));
if (lumpnum != LUMPERROR)
levelinfo[i].pic = W_CachePatchName(va("%sP", G_BuildMapName(votelevels[i]+1)), PU_STATIC);
else
levelinfo[i].pic = W_CachePatchName("BLANKLVL", PU_STATIC);
}
}
//
// Y_EndVote
//
void Y_EndVote(void)
{
Y_UnloadVoteData();
voteendtic = -1;
}
//
// Y_UnloadVoteData
//
static void Y_UnloadVoteData(void)
{
if (rendermode != render_soft)
return;
UNLOAD(widebgpatch);
UNLOAD(bgpatch);
UNLOAD(cursor);
UNLOAD(randomlvl);
UNLOAD(levelinfo[3].pic);
UNLOAD(levelinfo[2].pic);
UNLOAD(levelinfo[1].pic);
UNLOAD(levelinfo[0].pic);
}
//
// Y_SetupVoteFinish
//
void Y_SetupVoteFinish(SINT8 pick, SINT8 level)
{
if (pick == -1) // No other votes? We gotta get out of here, then!
{
timer = 0;
Y_UnloadVoteData();
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 && !splitscreen)
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 == 0) // Might as well put this here, too.
{
timer = 0;
Y_UnloadVoteData();
Y_FollowIntermission();
return;
}
else if (endtype == 1) // Only one unique vote, so just end it immediately.
{
voteendtic = votetic + (5*TICRATE);
S_StartSound(NULL, sfx_kc48);
S_ChangeMusicInternal("voteeb", false);
}
else
S_ChangeMusicInternal("voteea", true);
}
pickedvote = pick;
nextmap = votelevels[level];
timer = 0;
}