Merge branch 'special-stages' into 'master'

[Special stage prep] GP Bonus Stages + timelimit rework + Hitlag ticking fixes

Closes #236

See merge request KartKrew/Kart!739
This commit is contained in:
Oni 2022-10-27 03:10:37 +00:00
commit 95096fbc9a
35 changed files with 1493 additions and 936 deletions

View file

@ -124,3 +124,4 @@ k_terrain.c
k_director.c
k_follower.c
k_profiles.c
k_specialstage.c

View file

@ -3877,31 +3877,23 @@ void SV_StopServer(void)
}
// called at singleplayer start and stopdemo
void SV_StartSinglePlayerServer(void)
void SV_StartSinglePlayerServer(INT32 dogametype, boolean donetgame)
{
INT32 lastgametype = gametype;
server = true;
netgame = false;
multiplayer = false;
multiplayer = (modeattacking == ATTACKING_NONE);
joinedIP[0] = '\0'; // Make sure to empty this so that we don't save garbage when we start our own game. (because yes we use this for netgames too....)
if ((modeattacking == ATTACKING_CAPSULES) || (bossinfo.boss == true))
{
G_SetGametype(GT_BATTLE);
}
else
{
G_SetGametype(GT_RACE);
}
netgame = false; // so setting timelimit works... (XD_NETVAR doesn't play nice with SV_StopServer)
G_SetGametype(dogametype);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
netgame = donetgame;
// no more tic the game with this settings!
SV_StopServer();
if (splitscreen)
multiplayer = true;
}
static void SV_SendRefuse(INT32 node, const char *reason)

View file

@ -473,7 +473,7 @@ void SendKick(UINT8 playernum, UINT8 msg);
void NetKeepAlive(void);
void NetUpdate(void);
void SV_StartSinglePlayerServer(void);
void SV_StartSinglePlayerServer(INT32 dogametype, boolean donetgame);
boolean SV_SpawnServer(void);
void SV_StopServer(void);
void SV_ResetServer(void);

View file

@ -75,6 +75,7 @@
#include "k_boss.h"
#include "doomstat.h"
#include "m_random.h" // P_ClearRandom
#include "k_specialstage.h"
#ifdef CMAKECONFIG
#include "config.h"
@ -983,6 +984,9 @@ void D_StartTitle(void)
// Reset boss info
K_ResetBossInfo();
// Reset Special Stage
K_ResetSpecialStage();
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
maptol = 0;
@ -1807,29 +1811,20 @@ void D_SRB2Main(void)
INT16 newskill = -1;
const char *sskill = M_GetNextParm();
const char *masterstr = "Master";
if (!strcasecmp(masterstr, sskill))
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
{
newskill = KARTGP_MASTER;
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, sskill))
{
newskill = (INT16)gpdifficulty_cons_t[j].value;
break;
}
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
{
if (!strcasecmp(kartspeed_cons_t[j].strvalue, sskill))
{
newskill = (INT16)kartspeed_cons_t[j].value;
break;
}
}
if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
{
j = atoi(sskill); // assume they gave us a skill number, which is okay too
if (j >= KARTSPEED_EASY && j <= KARTGP_MASTER)
newskill = (INT16)j;
}
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
{
j = atoi(sskill); // assume they gave us a skill number, which is okay too
if (j >= KARTSPEED_EASY && j <= KARTGP_MASTER)
newskill = (INT16)j;
}
if (grandprixinfo.gp == true)

View file

@ -62,6 +62,7 @@
#include "doomstat.h"
#include "deh_tables.h"
#include "m_perfstats.h"
#include "k_specialstage.h"
#ifdef HAVE_DISCORDRPC
#include "discord.h"
@ -448,6 +449,7 @@ consvar_t cv_kartdebugnodes = CVAR_INIT ("debugnodes", "Off", CV_CHEAT, CV_OnOff
consvar_t cv_kartdebugcolorize = CVAR_INIT ("debugcolorize", "Off", CV_CHEAT, CV_OnOff, NULL);
consvar_t cv_kartdebugdirector = CVAR_INIT ("debugdirector", "Off", CV_CHEAT, CV_OnOff, NULL);
consvar_t cv_spbtest = CVAR_INIT ("spbtest", "Off", CV_CHEAT|CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_gptest = CVAR_INIT ("gptest", "Off", CV_CHEAT|CV_NETVAR, CV_OnOff, NULL);
static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_votetime = CVAR_INIT ("votetime", "20", CV_NETVAR, votetime_cons_t, NULL);
@ -471,9 +473,9 @@ consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_NETVAR, CV_YesNo, NULL)
consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL);
static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"}, {0, "None"}, {0, NULL}};
consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange);
consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange);
static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "None"}, {0, NULL}};
consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange);
consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange);
static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {MAX_LAPS, "MAX"}, {0, "Map default"}, {0, NULL}};
consvar_t cv_numlaps = CVAR_INIT ("numlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, numlaps_cons_t, NumLaps_OnChange);
@ -2570,15 +2572,19 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypedefaultrules[newgametype] & GTR_CAMPAIGN)))
FLS = false;
if (grandprixinfo.gp == true)
{
// Too lazy to change the input value for every instance of this function.......
pencoremode = grandprixinfo.encore;
}
else if (bossinfo.boss == true)
// Too lazy to change the input value for every instance of this function.......
if (bossinfo.boss == true)
{
pencoremode = bossinfo.encore;
}
else if (specialStage.active == true)
{
pencoremode = specialStage.encore;
}
else if (grandprixinfo.gp == true)
{
pencoremode = grandprixinfo.encore;
}
if (delay != 2)
{
@ -2938,6 +2944,7 @@ static void Command_Map_f(void)
if (newgametype == GT_BATTLE)
{
grandprixinfo.gp = false;
specialStage.active = false;
K_ResetBossInfo();
if (mapheaderinfo[newmapnum-1] &&
@ -2947,66 +2954,71 @@ static void Command_Map_f(void)
bossinfo.encore = newencoremode;
}
}
else // default GP
else
{
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
grandprixinfo.masterbots = false;
if (option_skill)
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
{
const char *masterstr = "Master";
const char *skillname = COM_Argv(option_skill + 1);
INT32 newskill = -1;
INT32 j;
grandprixinfo.gp = false;
bossinfo.boss = false;
if (!strcasecmp(masterstr, skillname))
specialStage.active = true;
specialStage.encore = newencoremode;
}
else // default GP
{
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
grandprixinfo.masterbots = false;
if (option_skill)
{
newskill = KARTGP_MASTER;
}
else
{
for (j = 0; kartspeed_cons_t[j].strvalue; j++)
const char *skillname = COM_Argv(option_skill + 1);
INT32 newskill = -1;
INT32 j;
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
{
if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname))
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname))
{
newskill = (INT16)kartspeed_cons_t[j].value;
newskill = (INT16)gpdifficulty_cons_t[j].value;
break;
}
}
if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
{
INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too
if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER)
newskill = (INT16)num;
}
if (newskill != -1)
{
if (newskill == KARTGP_MASTER)
{
grandprixinfo.gamespeed = KARTSPEED_HARD;
grandprixinfo.masterbots = true;
}
else
{
grandprixinfo.gamespeed = newskill;
grandprixinfo.masterbots = false;
}
}
}
if (newskill != -1)
{
if (newskill == KARTGP_MASTER)
{
grandprixinfo.gamespeed = KARTSPEED_HARD;
grandprixinfo.masterbots = true;
}
else
{
grandprixinfo.gamespeed = newskill;
grandprixinfo.masterbots = false;
}
}
grandprixinfo.encore = newencoremode;
grandprixinfo.gp = true;
grandprixinfo.roundnum = 0;
grandprixinfo.cup = NULL;
grandprixinfo.wonround = false;
bossinfo.boss = false;
specialStage.active = false;
grandprixinfo.initalize = true;
}
grandprixinfo.encore = newencoremode;
grandprixinfo.gp = true;
grandprixinfo.roundnum = 0;
grandprixinfo.cup = NULL;
grandprixinfo.wonround = false;
bossinfo.boss = false;
grandprixinfo.initalize = true;
}
}
@ -4020,7 +4032,7 @@ void Schedule_Run(void)
return;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
// Don't engage in automation while in a restricted context.
return;
@ -4156,7 +4168,7 @@ void Automate_Run(automateEvents_t type)
return;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
// Don't engage in automation while in a restricted context.
return;
@ -4900,7 +4912,12 @@ void ItemFinder_OnChange(void)
*/
static void PointLimit_OnChange(void)
{
// Don't allow pointlimit in Single Player/Co-Op/Race!
if (K_CanChangeRules(false) == false)
{
return;
}
// Don't allow pointlimit in non-pointlimited gametypes!
if (server && Playing() && !(gametyperules & GTR_POINTLIMIT))
{
if (cv_pointlimit.value)
@ -4915,7 +4932,7 @@ static void PointLimit_OnChange(void)
cv_pointlimit.value,
cv_pointlimit.value > 1 ? "s" : "");
}
else if (netgame || multiplayer)
else
CONS_Printf(M_GetText("Point limit disabled\n"));
}
@ -4938,6 +4955,8 @@ Lagless_OnChange (void)
}
UINT32 timelimitintics = 0;
UINT32 extratimeintics = 0;
UINT32 secretextratime = 0;
/** Deals with a timelimit change by printing the change to the console.
* If the gametype is single player, cooperative, or race, the timelimit is
@ -4949,26 +4968,40 @@ UINT32 timelimitintics = 0;
*/
static void TimeLimit_OnChange(void)
{
// Don't allow timelimit in Single Player/Co-Op/Race!
if (server && Playing() && cv_timelimit.value != 0 && (bossinfo.boss || !(gametyperules & GTR_TIMELIMIT)))
if (K_CanChangeRules(false) == false)
{
CV_SetValue(&cv_timelimit, 0);
return;
}
if (cv_timelimit.value != 0)
if (gamestate == GS_LEVEL && leveltime < starttime)
{
CONS_Printf(M_GetText("Rounds will end after %d minute%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003
timelimitintics = cv_timelimit.value * (60*TICRATE);
if (cv_timelimit.value)
{
CONS_Printf(M_GetText("Time limit has been set to %d minute%s.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s");
}
else
{
CONS_Printf(M_GetText("Time limit has been disabled.\n"));
}
// Note the deliberate absence of any code preventing
// pointlimit and timelimit from being set simultaneously.
// Some people might like to use them together. It works.
}
timelimitintics = cv_timelimit.value * (60*TICRATE);
extratimeintics = secretextratime = 0;
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
DRPC_UpdatePresence();
#endif
}
else
{
if (cv_timelimit.value)
{
CONS_Printf(M_GetText("Time limit will be %d minute%s next round.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s");
}
else
{
CONS_Printf(M_GetText("Time limit will be disabled next round.\n"));
}
}
}
/** Adjusts certain settings to match a changed gametype.
@ -4995,7 +5028,7 @@ void D_GameTypeChanged(INT32 lastgametype)
// Only do the following as the server, not as remote admin.
// There will always be a server, and this only needs to be done once.
if (server && (multiplayer || netgame))
if (server && multiplayer)
{
if (!cv_timelimit.changed) // user hasn't changed limits
{
@ -5006,27 +5039,6 @@ void D_GameTypeChanged(INT32 lastgametype)
CV_SetValue(&cv_pointlimit, pointlimits[gametype]);
}
}
/* -- no longer useful
else if (!multiplayer && !netgame)
{
G_SetGametype(GT_RACE);
}
*/
// reset timelimit and pointlimit in race/coop, prevent stupid cheats
if (server)
{
if (!(gametyperules & GTR_TIMELIMIT))
{
if (cv_timelimit.value)
CV_SetValue(&cv_timelimit, 0);
}
if (!(gametyperules & GTR_POINTLIMIT))
{
if (cv_pointlimit.value)
CV_SetValue(&cv_pointlimit, 0);
}
}
// don't retain teams in other modes or between changes from ctf to team match.
// also, stop any and all forms of team scrambling that might otherwise take place.
@ -5752,6 +5764,10 @@ void Command_Retry_f(void)
{
CONS_Printf(M_GetText("This only works in singleplayer games.\n"));
}
else if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
{
CONS_Printf(M_GetText("You can't retry right now!\n"));
}
else
{
M_ClearMenus(true);
@ -6560,12 +6576,12 @@ static void Command_ShowTime_f(void)
// SRB2Kart: On change messages
static void NumLaps_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
if (leveltime < starttime)
if (gamestate == GS_LEVEL && leveltime < starttime)
{
CONS_Printf(M_GetText("Number of laps have been set to %d.\n"), cv_numlaps.value);
numlaps = (UINT8)cv_numlaps.value;
@ -6578,12 +6594,12 @@ static void NumLaps_OnChange(void)
static void KartFrantic_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
if (leveltime < starttime)
if (gamestate == GS_LEVEL && leveltime < starttime)
{
CONS_Printf(M_GetText("Frantic items has been set to %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
franticitems = (boolean)cv_kartfrantic.value;
@ -6596,12 +6612,12 @@ static void KartFrantic_OnChange(void)
static void KartSpeed_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
if (leveltime < starttime && cv_kartspeed.value != KARTSPEED_AUTO)
if (gamestate == GS_LEVEL && leveltime < starttime && cv_kartspeed.value != KARTSPEED_AUTO)
{
CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string);
gamespeed = (UINT8)cv_kartspeed.value;
@ -6614,7 +6630,7 @@ static void KartSpeed_OnChange(void)
static void KartEncore_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
return;
}
@ -6624,7 +6640,7 @@ static void KartEncore_OnChange(void)
static void KartEliminateLast_OnChange(void)
{
if (K_CanChangeRules() == false)
if (K_CanChangeRules(false) == false)
{
CV_StealthSet(&cv_karteliminatelast, cv_karteliminatelast.defaultvalue);
}

View file

@ -55,7 +55,7 @@ extern consvar_t cv_itemrespawn;
extern consvar_t cv_pointlimit;
extern consvar_t cv_timelimit;
extern consvar_t cv_numlaps;
extern UINT32 timelimitintics;
extern UINT32 timelimitintics, extratimeintics, secretextratime;
extern consvar_t cv_allowexitlevel;
extern consvar_t cv_autobalance;
@ -120,7 +120,8 @@ extern consvar_t cv_kartusepwrlv;
extern consvar_t cv_votetime;
extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution, cv_kartdebughuddrop;
extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector, cv_spbtest;
extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector;
extern consvar_t cv_spbtest, cv_gptest;
extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict;
extern consvar_t cv_itemfinder;

View file

@ -237,7 +237,8 @@ typedef enum
khud_lapanimation, // Used to show the lap start wing logo animation
khud_laphand, // Lap hand gfx to use; 0 = none, 1 = :ok_hand:, 2 = :thumbs_up:, 3 = :thumps_down:
// Start
// Big text
khud_finish, // Set when completing a round
khud_fault, // Set when faulting during the starting countdown
// Camera

View file

@ -3052,7 +3052,15 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
}
else if (fastcmp(word, "LEVELLIST"))
{
cup->numlevels = 0;
while (cup->numlevels > 0)
{
cup->numlevels--;
Z_Free(cup->levellist[cup->numlevels]);
cup->levellist[cup->numlevels] = NULL;
if (cup->cachedlevels[cup->numlevels] == NEXTMAP_INVALID)
continue;
mapheaderinfo[cup->cachedlevels[cup->numlevels]]->cup = NULL;
}
tmp = strtok(word2,",");
do {
@ -3069,11 +3077,15 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
}
else if (fastcmp(word, "BONUSGAME"))
{
Z_Free(cup->levellist[CUPCACHE_BONUS]);
cup->levellist[CUPCACHE_BONUS] = Z_StrDup(word2);
cup->cachedlevels[CUPCACHE_BONUS] = NEXTMAP_INVALID;
}
else if (fastcmp(word, "SPECIALSTAGE"))
{
Z_Free(cup->levellist[CUPCACHE_SPECIAL]);
cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2);
cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID;
}
else if (fastcmp(word, "EMERALDNUM"))
{

View file

@ -670,6 +670,11 @@ extern int compuncommitted;
/// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls.
//#define PAPER_COLLISIONCORRECTION
#ifdef DEVELOP
// Easily make it so that overtime works offline
#define TESTOVERTIMEINFREEPLAY
#endif
/// FINALLY some real clipping that doesn't make walls dissappear AND speeds the game up
/// (that was the original comment from SRB2CB, sadly it is a lie and actually slows game down)
/// on the bright side it fixes some weird issues with translucent walls

View file

@ -518,6 +518,7 @@ enum TypeOfLevel
TOL_RACE = 0x0001, ///< Race
TOL_BATTLE = 0x0002, ///< Battle
TOL_BOSS = 0x0004, ///< Boss (variant of battle, but forbidden)
TOL_SPECIAL = 0x0008, ///< Special Stage (variant of race, but forbidden)
// Modifiers
TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD

View file

@ -2386,10 +2386,8 @@ void F_EndCutScene(void)
F_StartGameEvaluation();
else if (cutnum == introtoplay-1)
D_StartTitle();
else if (nextmap < NEXTMAP_SPECIAL)
G_NextLevel();
else
G_EndGame();
G_NextLevel();
}
}

View file

@ -58,6 +58,7 @@
#include "k_respawn.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "k_bot.h"
#include "doomstat.h"
@ -2224,6 +2225,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 starpostnum;
INT32 exiting;
INT32 khudfinish;
INT32 khudcardanimation;
INT16 totalring;
UINT8 laps;
@ -2297,6 +2299,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
botdiffincrease = players[player].botvars.diffincrease;
botrival = players[player].botvars.rival;
totalring = players[player].totalring;
xtralife = players[player].xtralife;
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE));
// SRB2kart
@ -2315,12 +2320,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
nocontrol = 0;
laps = 0;
latestlap = 0;
totalring = 0;
roundscore = 0;
exiting = 0;
khudfinish = 0;
khudcardanimation = 0;
starpostnum = 0;
xtralife = 0;
follower = NULL;
}
@ -2357,16 +2361,22 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
laps = players[player].laps;
latestlap = players[player].latestlap;
totalring = players[player].totalring;
roundscore = players[player].roundscore;
exiting = players[player].exiting;
khudcardanimation = (exiting > 0) ? players[player].karthud[khud_cardanimation] : 0;
if (exiting > 0)
{
khudfinish = players[player].karthud[khud_finish];
khudcardanimation = players[player].karthud[khud_cardanimation];
}
else
{
khudfinish = 0;
khudcardanimation = 0;
}
starpostnum = players[player].starpostnum;
xtralife = players[player].xtralife;
follower = players[player].follower;
pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_FAULT|PF_LOSTLIFE));
@ -2406,6 +2416,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->starpostnum = starpostnum;
p->exiting = exiting;
p->karthud[khud_finish] = khudfinish;
p->karthud[khud_cardanimation] = khudcardanimation;
p->laps = laps;
@ -2908,7 +2919,7 @@ void G_ExitLevel(void)
}
}
}
else if (grandprixinfo.gp == true)
else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE)
{
youlost = (grandprixinfo.wonround != true);
}
@ -3144,6 +3155,7 @@ tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = {
{"RACE",TOL_RACE},
{"BATTLE",TOL_BATTLE},
{"BOSS",TOL_BOSS},
{"SPECIAL",TOL_SPECIAL},
{"TV",TOL_TV},
{NULL, 0}
};
@ -3221,14 +3233,14 @@ boolean G_GametypeUsesLives(void)
if (modeattacking || metalrecording) // NOT in Record Attack
return false;
if (bossinfo.boss == true) // Fighting a boss?
if ((grandprixinfo.gp == true) // In Grand Prix
&& (gametype == GT_RACE) // NOT in bonus round
&& grandprixinfo.eventmode == GPEVENT_NONE) // NOT in bonus
{
return true;
}
if ((grandprixinfo.gp == true) // In Grand Prix
&& (gametype == GT_RACE) // NOT in bonus round
&& !G_IsSpecialStage(gamemap)) // NOT in special stage
if (bossinfo.boss == true) // Fighting a boss?
{
return true;
}
@ -3643,11 +3655,11 @@ static void G_UpdateVisited(void)
return;
// Update visitation flags
mapheaderinfo[gamemap-1]->mapvisited |= MV_BEATEN;
mapheaderinfo[prevmap]->mapvisited |= MV_BEATEN;
if (encoremode == true)
{
mapheaderinfo[gamemap-1]->mapvisited |= MV_ENCORE;
mapheaderinfo[prevmap]->mapvisited |= MV_ENCORE;
}
if (modeattacking)
@ -3694,6 +3706,7 @@ static void G_HandleSaveLevel(void)
static void G_GetNextMap(void)
{
boolean spec = G_IsSpecialStage(prevmap+1);
INT32 i;
// go to next level
@ -3710,14 +3723,94 @@ static void G_GetNextMap(void)
}
else
{
if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
INT32 lastgametype = gametype;
// If we're in a GP event, don't immediately follow it up with another.
// I also suspect this will not work with online GP so I'm gonna prevent it right now.
// The server might have to communicate eventmode (alongside other GP data) in XD_MAP later.
if (netgame || grandprixinfo.eventmode != GPEVENT_NONE)
{
grandprixinfo.eventmode = GPEVENT_NONE;
G_SetGametype(GT_RACE);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
specialStage.active = false;
bossinfo.boss = false;
}
// Special stage
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
{
INT16 totaltotalring = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
if (players[i].bot)
continue;
totaltotalring += players[i].totalring;
}
if (totaltotalring >= 50)
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_SPECIAL|TOL_BOSS|TOL_BATTLE))
{
grandprixinfo.eventmode = GPEVENT_SPECIAL;
nextmap = cupLevelNum;
}
}
}
else if (grandprixinfo.roundnum == (grandprixinfo.cup->numlevels+1)/2) // 3 for a 5-map cup
{
// todo any other condition?
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_BOSS|TOL_BATTLE))
{
grandprixinfo.eventmode = GPEVENT_BONUS;
nextmap = cupLevelNum;
}
}
}
if (grandprixinfo.eventmode != GPEVENT_NONE)
{
// nextmap is set above
const INT32 newtol = mapheaderinfo[nextmap]->typeoflevel;
if (newtol & TOL_SPECIAL)
{
specialStage.active = true;
specialStage.encore = grandprixinfo.encore;
}
else //(if newtol & (TOL_BATTLE|TOL_BOSS)) -- safe to assume??
{
G_SetGametype(GT_BATTLE);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
if (newtol & TOL_BOSS)
{
K_ResetBossInfo();
bossinfo.boss = true;
bossinfo.encore = grandprixinfo.encore;
}
}
}
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
{
nextmap = NEXTMAP_CEREMONY; // ceremonymap
}
else
{
// Proceed to next map
const INT32 cupLevelNum =grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum];
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
@ -3799,14 +3892,7 @@ static void G_GetNextMap(void)
// Didn't get a nextmap before reaching the end?
if (gettingresult != 2)
{
if (marathonmode)
{
nextmap = NEXTMAP_CEREMONY; // ceremonymap
}
else
{
nextmap = NEXTMAP_TITLE;
}
nextmap = NEXTMAP_CEREMONY; // ceremonymap
}
}
else
@ -3834,19 +3920,39 @@ static void G_GetNextMap(void)
nextmap = cm;
}
if (!marathonmode)
if (K_CanChangeRules(true))
{
if (cv_advancemap.value == 0) // Stay on same map.
switch (cv_advancemap.value)
{
nextmap = prevmap;
}
else if (cv_advancemap.value == 2) // Go to random map.
{
nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, false, NULL);
}
else if (nextmap >= NEXTMAP_SPECIAL) // Loop back around
{
nextmap = G_GetFirstMapOfGametype(gametype);
case 0: // Stay on same map.
nextmap = prevmap;
break;
case 3: // Voting screen.
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
break;
}
if (i != MAXPLAYERS)
{
nextmap = NEXTMAP_VOTING;
break;
}
}
/* FALLTHRU */
case 2: // Go to random map.
nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, false, NULL);
break;
default:
if (nextmap >= NEXTMAP_SPECIAL) // Loop back around
{
nextmap = G_GetFirstMapOfGametype(gametype);
}
break;
}
}
}
@ -3854,6 +3960,9 @@ static void G_GetNextMap(void)
// We are committed to this map now.
if (nextmap == NEXTMAP_INVALID || (nextmap < NEXTMAP_SPECIAL && (nextmap >= nummapheaders || !mapheaderinfo[nextmap] || mapheaderinfo[nextmap]->lumpnum == LUMPERROR)))
I_Error("G_GetNextMap: Internal map ID %d not found (nummapheaders = %d)\n", nextmap, nummapheaders);
if (!spec)
lastmap = nextmap;
}
//
@ -3862,8 +3971,6 @@ static void G_GetNextMap(void)
static void G_DoCompleted(void)
{
INT32 i, j = 0;
boolean spec = G_IsSpecialStage(gamemap);
SINT8 powertype = K_UsingPowerLevels();
if (modeattacking && pausedelay)
pausedelay = 0;
@ -3906,8 +4013,9 @@ static void G_DoCompleted(void)
}
}
// See Y_StartIntermission timer handling
if ((gametyperules & GTR_CIRCUIT) && ((multiplayer && demo.playback) || j == r_splitscreen+1) && (!K_CanChangeRules(false) || cv_inttime.value > 0))
// play some generic music if there's no win/cool/lose music going on (for exitlevel commands)
if ((gametyperules & GTR_CIRCUIT) && ((multiplayer && demo.playback) || j == r_splitscreen+1) && (cv_inttime.value > 0))
S_ChangeMusicInternal("racent", true);
if (automapactive)
@ -3919,14 +4027,8 @@ static void G_DoCompleted(void)
if (!demo.playback)
{
G_GetNextMap();
// Remember last map for when you come out of the special stage.
if (!spec)
lastmap = nextmap;
// Set up power level gametype scrambles
K_SetPowerLevelScrambles(powertype);
K_SetPowerLevelScrambles(K_UsingPowerLevels());
}
// If the current gametype has no intermission screen set, then don't start it.
@ -3937,7 +4039,6 @@ static void G_DoCompleted(void)
|| (intertype == int_none))
{
G_UpdateVisited();
G_HandleSaveLevel();
G_AfterIntermission();
}
else
@ -3945,7 +4046,6 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_INTERMISSION);
Y_StartIntermission();
G_UpdateVisited();
G_HandleSaveLevel();
}
}
@ -3979,14 +4079,17 @@ void G_AfterIntermission(void)
return;
}
if (gamestate != GS_VOTING)
{
G_GetNextMap();
G_HandleSaveLevel();
}
if ((gametyperules & GTR_CAMPAIGN) && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[prevmap]->cutscenenum-1, false, false);
else
{
if (nextmap < NEXTMAP_SPECIAL)
G_NextLevel();
else
G_EndGame();
G_NextLevel();
}
}
@ -3998,25 +4101,15 @@ void G_AfterIntermission(void)
//
void G_NextLevel(void)
{
if (gamestate != GS_VOTING)
if (nextmap >= NEXTMAP_SPECIAL)
{
if ((cv_advancemap.value == 3) && grandprixinfo.gp == false && bossinfo.boss == false && !modeattacking && !skipstats && (multiplayer || netgame))
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
gameaction = ga_startvote;
return;
}
}
}
forceresetplayers = false;
deferencoremode = (cv_kartencore.value == 1);
G_EndGame();
return;
}
forceresetplayers = false;
deferencoremode = (cv_kartencore.value == 1);
gameaction = ga_worlddone;
}
@ -4043,7 +4136,11 @@ static void G_DoWorldDone(void)
static void G_DoStartVote(void)
{
if (server)
{
if (gamestate == GS_VOTING)
I_Error("G_DoStartVote: NEXTMAP_VOTING causes recursive vote!");
D_SetupVote();
}
gameaction = ga_nothing;
}
@ -4121,8 +4218,12 @@ static void G_DoContinued(void)
// when something new is added.
void G_EndGame(void)
{
if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING))
G_SaveDemo();
// Handle voting
if (nextmap == NEXTMAP_VOTING)
{
gameaction = ga_startvote;
return;
}
// Only do evaluation and credits in singleplayer contexts
if (!netgame && (gametyperules & GTR_CAMPAIGN))
@ -4711,6 +4812,7 @@ cleanup:
void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS)
{
UINT16 color = SKINCOLOR_NONE;
INT32 dogametype;
paused = false;
@ -4721,8 +4823,17 @@ void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ss
G_ResetRandMapBuffer();
if ((modeattacking == ATTACKING_CAPSULES) || (bossinfo.boss == true))
{
dogametype = GT_BATTLE;
}
else
{
dogametype = GT_RACE;
}
// this leave the actual game if needed
SV_StartSinglePlayerServer();
SV_StartSinglePlayerServer(dogametype, false);
if (splitscreen != ssplayers)
{

View file

@ -44,7 +44,8 @@ typedef enum
NEXTMAP_EVALUATION = INT16_MAX-2,
NEXTMAP_CREDITS = INT16_MAX-3,
NEXTMAP_CEREMONY = INT16_MAX-4,
NEXTMAP_INVALID = INT16_MAX-5, // Always last (swap with NEXTMAP_RESERVED when removing that)
NEXTMAP_VOTING = INT16_MAX-5,
NEXTMAP_INVALID = INT16_MAX-6, // Always last
NEXTMAP_SPECIAL = NEXTMAP_INVALID
} nextmapspecial_t;

View file

@ -2416,7 +2416,7 @@ static void HU_DrawRankings(void)
if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)) && !bossinfo.boss)
{
if ((gametyperules & GTR_TIMELIMIT) && cv_timelimit.value && timelimitintics > 0)
if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0)
{
UINT32 timeval = (timelimitintics + starttime + 1 - leveltime);
if (timeval > timelimitintics+1)

View file

@ -127,12 +127,16 @@ void K_CheckBumpers(void)
winnerscoreadd -= players[i].roundscore;
}
if (K_CanChangeRules() == false)
if (K_CanChangeRules(true) == false)
{
if (nobumpers)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
players[i].pflags |= PF_NOCONTEST;
P_DoPlayerExit(&players[i]);
}
@ -144,7 +148,8 @@ void K_CheckBumpers(void)
if (!battlecapsules)
{
// Reset map to turn on battle capsules
D_MapChange(gamemap, gametype, encoremode, true, 0, false, false);
if (server)
D_MapChange(gamemap, gametype, encoremode, true, 0, false, false);
}
else
{
@ -152,6 +157,10 @@ void K_CheckBumpers(void)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
players[i].pflags |= PF_NOCONTEST;
P_DoPlayerExit(&players[i]);
}
@ -173,7 +182,13 @@ void K_CheckBumpers(void)
K_KartUpdatePosition(&players[i]);
for (i = 0; i < MAXPLAYERS; i++) // and it can't be merged with this loop because it needs to be all updated before exiting... multi-loops suck...
{
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
P_DoPlayerExit(&players[i]);
}
}
void K_CheckEmeralds(player_t *player)
@ -795,29 +810,13 @@ void K_SpawnPlayerBattleBumpers(player_t *p)
}
}
void K_BattleInit(void)
void K_BattleInit(boolean singleplayercontext)
{
size_t i;
if ((gametyperules & GTR_CAPSULES) && !battlecapsules && !bossinfo.boss)
if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules && !bossinfo.boss)
{
mapthing_t *mt;
if (modeattacking != ATTACKING_CAPSULES)
{
UINT8 n = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
n++;
}
if (n > 1)
goto aftercapsules;
}
mt = mapthings;
mapthing_t *mt = mapthings;
for (i = 0; i < nummapthings; i++, mt++)
{
if (mt->type == mobjinfo[MT_BATTLECAPSULE].doomednum)
@ -826,7 +825,6 @@ void K_BattleInit(void)
battlecapsules = true;
}
aftercapsules:
if (gametyperules & GTR_BUMPERS)
{

View file

@ -29,6 +29,6 @@ void K_RunPaperItemSpawners(void);
void K_RunBattleOvertime(void);
void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj);
void K_SpawnPlayerBattleBumpers(player_t *p);
void K_BattleInit(void);
void K_BattleInit(boolean singleplayercontext);
#endif

View file

@ -1261,18 +1261,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
INT32 turnamt = 0;
line_t *botController = NULL;
// Can't build a ticcmd if we aren't spawned...
if (!player->mo)
{
return;
}
// Remove any existing controls
memset(cmd, 0, sizeof(ticcmd_t));
if (gamestate != GS_LEVEL)
if (gamestate != GS_LEVEL || !player->mo || player->spectator)
{
// Not in a level.
// Not in the level.
return;
}

View file

@ -12,6 +12,7 @@
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "doomdef.h"
#include "d_player.h"
#include "g_game.h"
@ -96,14 +97,14 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers)
}
/*--------------------------------------------------
SINT8 K_BotDefaultSkin(void)
UINT8 K_BotDefaultSkin(void)
See header file for description.
--------------------------------------------------*/
SINT8 K_BotDefaultSkin(void)
UINT8 K_BotDefaultSkin(void)
{
const char *defaultbotskinname = "eggrobo";
SINT8 defaultbotskin = R_SkinAvailable(defaultbotskinname);
INT32 defaultbotskin = R_SkinAvailable(defaultbotskinname);
if (defaultbotskin == -1)
{
@ -111,7 +112,7 @@ SINT8 K_BotDefaultSkin(void)
defaultbotskin = 0;
}
return defaultbotskin;
return (UINT8)defaultbotskin;
}
/*--------------------------------------------------
@ -121,7 +122,7 @@ SINT8 K_BotDefaultSkin(void)
--------------------------------------------------*/
void K_InitGrandPrixBots(void)
{
const SINT8 defaultbotskin = K_BotDefaultSkin();
const UINT8 defaultbotskin = K_BotDefaultSkin();
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
UINT8 difficultylevels[MAXPLAYERS];
@ -132,7 +133,9 @@ void K_InitGrandPrixBots(void)
UINT8 numplayers = 0;
UINT8 competitors[MAXSPLITSCREENPLAYERS];
boolean skinusable[MAXSKINS];
UINT8 usableskins;
UINT8 grabskins[MAXSKINS+1];
UINT8 botskinlist[MAXPLAYERS];
UINT8 botskinlistpos = 0;
@ -142,18 +145,12 @@ void K_InitGrandPrixBots(void)
memset(competitors, MAXPLAYERS, sizeof (competitors));
memset(botskinlist, defaultbotskin, sizeof (botskinlist));
// init usable bot skins list
for (i = 0; i < MAXSKINS; i++)
// Init usable bot skins list
for (usableskins = 0; usableskins < numskins; usableskins++)
{
if (i < numskins)
{
skinusable[i] = true;
}
else
{
skinusable[i] = false;
}
grabskins[usableskins] = usableskins;
}
grabskins[usableskins] = MAXSKINS;
#if MAXPLAYERS != 16
I_Error("GP bot difficulty levels need rebalanced for the new player count!\n");
@ -192,7 +189,7 @@ void K_InitGrandPrixBots(void)
if (numplayers < MAXSPLITSCREENPLAYERS && !players[i].spectator)
{
competitors[numplayers] = i;
skinusable[players[i].skin] = false;
grabskins[players[i].skin] = MAXSKINS;
numplayers++;
}
else
@ -219,52 +216,46 @@ void K_InitGrandPrixBots(void)
{
player_t *p = &players[competitors[j]];
char *rivalname = skins[p->skin].rivals[i];
SINT8 rivalnum = R_SkinAvailable(rivalname);
INT32 rivalnum = R_SkinAvailable(rivalname);
if (rivalnum != -1 && skinusable[rivalnum])
// Intentionally referenced before (currently dummied out) unlock check. Such a tease!
if (rivalnum != -1 && grabskins[(UINT8)rivalnum] != MAXSKINS)
{
botskinlist[botskinlistpos] = rivalnum;
skinusable[rivalnum] = false;
botskinlistpos++;
botskinlist[botskinlistpos++] = (UINT8)rivalnum;
grabskins[(UINT8)rivalnum] = MAXSKINS;
}
}
}
}
// Rearrange usable bot skins list to prevent gaps for randomised selection
for (i = 0; i < usableskins; i++)
{
if (!(grabskins[i] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
continue;
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
{
usableskins--;
}
grabskins[i] = grabskins[usableskins];
grabskins[usableskins] = MAXSKINS;
}
// Pad the remaining list with random skins if we need to
if (botskinlistpos < wantedbots)
{
for (i = botskinlistpos; i < wantedbots; i++)
while (botskinlistpos < wantedbots)
{
UINT8 val = M_RandomKey(numskins);
UINT8 loops = 0;
UINT8 skinnum = defaultbotskin;
while (!skinusable[val])
if (usableskins > 0)
{
if (loops >= numskins)
{
// no more skins
break;
}
val++;
if (val >= numskins)
{
val = 0;
}
loops++;
UINT8 index = M_RandomKey(usableskins);
skinnum = grabskins[index];
grabskins[index] = grabskins[--usableskins];
}
if (loops >= numskins)
{
// leave the rest of the table as the default skin
break;
}
botskinlist[i] = val;
skinusable[val] = false;
botskinlist[botskinlistpos++] = skinnum;
}
}
@ -341,6 +332,16 @@ void K_UpdateGrandPrixBots(void)
UINT16 newrivalscore = 0;
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || !players[i].bot)
{
continue;
}
players[i].spectator = (grandprixinfo.eventmode != GPEVENT_NONE);
}
// Find the rival.
for (i = 0; i < MAXPLAYERS; i++)
{
@ -519,38 +520,49 @@ void K_IncreaseBotDifficulty(player_t *bot)
--------------------------------------------------*/
void K_RetireBots(void)
{
const SINT8 defaultbotskin = K_BotDefaultSkin();
const UINT8 defaultbotskin = K_BotDefaultSkin();
SINT8 newDifficulty;
boolean skinusable[MAXSKINS];
UINT8 usableskins;
UINT8 grabskins[MAXSKINS+1];
UINT8 i;
if (grandprixinfo.gp == true && grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
if (grandprixinfo.gp == true
&& ((grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
|| grandprixinfo.eventmode != GPEVENT_NONE))
{
// Was last map, no replacement.
// No replacement.
return;
}
// init usable bot skins list
for (i = 0; i < MAXSKINS; i++)
// Init usable bot skins list
for (usableskins = 0; usableskins < numskins; usableskins++)
{
if (i < numskins)
{
skinusable[i] = true;
}
else
{
skinusable[i] = false;
}
grabskins[usableskins] = usableskins;
}
grabskins[usableskins] = MAXSKINS;
// Exclude player skins
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
skinusable[players[i].skin] = false;
}
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
grabskins[players[i].skin] = MAXSKINS;
}
// Rearrange usable bot skins list to prevent gaps for randomised selection
for (i = 0; i < usableskins; i++)
{
if (!(grabskins[i] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
continue;
while (usableskins > i && (grabskins[usableskins] == MAXSKINS /*|| K_SkinLocked(grabskins[i])*/))
usableskins--;
grabskins[i] = grabskins[usableskins];
grabskins[usableskins] = MAXSKINS;
}
if (!grandprixinfo.gp) // Sure, let's let this happen all the time :)
@ -576,49 +588,24 @@ void K_RetireBots(void)
{
player_t *bot = NULL;
if (!playeringame[i] || !players[i].bot)
if (!playeringame[i] || !players[i].bot || players[i].spectator)
{
continue;
}
bot = &players[i];
if (bot->spectator)
{
continue;
}
if (bot->pflags & PF_NOCONTEST)
{
UINT8 skinnum = P_RandomKey(PR_UNDEFINED, numskins);
UINT8 loops = 0;
UINT8 skinnum = defaultbotskin;
while (!skinusable[skinnum])
if (usableskins > 0)
{
if (loops >= numskins)
{
// no more skins
break;
}
skinnum++;
if (skinnum >= numskins)
{
skinnum = 0;
}
loops++;
UINT8 index = P_RandomKey(PR_RULESCRAMBLE, usableskins);
skinnum = grabskins[index];
grabskins[index] = grabskins[--usableskins];
}
if (loops >= numskins)
{
// Use default skin
skinnum = defaultbotskin;
}
skinusable[skinnum] = false;
bot->botvars.difficulty = newDifficulty;
bot->botvars.diffincrease = 0;
@ -671,7 +658,7 @@ void K_FakeBotResults(player_t *bot)
}
// hey, you "won"
bot->exiting = 2;
bot->exiting = 1;
bot->realtime += (bot->distancetofinish / distfactor);
bot->distancetofinish = 0;
K_IncreaseBotDifficulty(bot);
@ -710,18 +697,12 @@ void K_PlayerLoseLife(player_t *player)
}
/*--------------------------------------------------
boolean K_CanChangeRules(void)
boolean K_CanChangeRules(boolean allowdemos)
See header file for description.
--------------------------------------------------*/
boolean K_CanChangeRules(void)
boolean K_CanChangeRules(boolean allowdemos)
{
if (demo.playback)
{
// We've already got our important settings!
return false;
}
if (grandprixinfo.gp == true && grandprixinfo.roundnum > 0)
{
// Don't cheat the rules of the GP!
@ -734,11 +715,29 @@ boolean K_CanChangeRules(void)
return false;
}
if (modeattacking == true)
if (specialStage.active == true)
{
// Don't cheat special stages!
return false;
}
if (marathonmode)
{
// Don't cheat the endurance challenge!
return false;
}
if (modeattacking != ATTACKING_NONE)
{
// Don't cheat the rules of Time Trials!
return false;
}
if (!allowdemos && demo.playback)
{
// We've already got our important settings!
return false;
}
return true;
}

View file

@ -16,6 +16,10 @@
#include "doomdef.h"
#include "doomstat.h"
#define GPEVENT_NONE 0
#define GPEVENT_BONUS 1
#define GPEVENT_SPECIAL 2
extern struct grandprixinfo
{
boolean gp; ///< If true, then we are in a Grand Prix.
@ -26,6 +30,7 @@ extern struct grandprixinfo
boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode)
boolean initalize; ///< If true, we need to initialize a new session.
boolean wonround; ///< If false, then we retry the map instead of going to the next.
UINT8 eventmode; ///< See GPEVENT_ constants
} grandprixinfo;
@ -63,13 +68,13 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers);
/*--------------------------------------------------
SINT8 K_BotDefaultSkin(void);
UINT8 K_BotDefaultSkin(void);
Returns the skin number of the skin the game
uses as a fallback option.
--------------------------------------------------*/
SINT8 K_BotDefaultSkin(void);
UINT8 K_BotDefaultSkin(void);
/*--------------------------------------------------
@ -147,18 +152,18 @@ void K_PlayerLoseLife(player_t *player);
/*--------------------------------------------------
boolean K_CanChangeRules(void);
boolean K_CanChangeRules(boolean allowdemos);
Returns whenver or not the server is allowed
to change the game rules.
Input Arguments:-
None
allowdemos - permits this behavior during demo playback
Return:-
true if can change important gameplay rules, otherwise false.
--------------------------------------------------*/
boolean K_CanChangeRules(void);
boolean K_CanChangeRules(boolean allowdemos);
#endif

View file

@ -12,6 +12,7 @@
#include "k_hud.h"
#include "k_kart.h"
#include "k_battle.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_director.h"
@ -75,7 +76,9 @@ static patch_t *kp_winnernum[NUMPOSFRAMES];
static patch_t *kp_facenum[MAXPLAYERS+1];
static patch_t *kp_facehighlight[8];
static patch_t *kp_nocontestminimap;
static patch_t *kp_spbminimap;
static patch_t *kp_capsuleminimap[2];
static patch_t *kp_ringsticker[2];
static patch_t *kp_ringstickersplit[4];
@ -311,7 +314,11 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_facehighlight[i], "%s", buffer);
}
// Special minimap icons
HU_UpdatePatch(&kp_nocontestminimap, "MINIDEAD");
HU_UpdatePatch(&kp_spbminimap, "SPBMMAP");
HU_UpdatePatch(&kp_capsuleminimap[0], "MINICAP1");
HU_UpdatePatch(&kp_capsuleminimap[1], "MINICAP2");
// Rings & Lives
HU_UpdatePatch(&kp_ringsticker[0], "RNGBACKA");
@ -1385,33 +1392,40 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
// TIME_Y = 6; // 6
tic_t worktime;
boolean dontdraw = false;
INT32 jitter = 0;
INT32 splitflags = 0;
if (!mode)
{
splitflags = V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP|V_SNAPTORIGHT|V_SPLITSCREEN;
#ifndef TESTOVERTIMEINFREEPLAY
if (battlecapsules) // capsules override any time limit settings
;
else
#endif
if (bossinfo.boss == true)
;
else if (timelimitintics > 0 && (gametyperules & GTR_TIMELIMIT)) // TODO
if (timelimitintics > 0)
{
if (drawtime >= timelimitintics)
{
if (((drawtime-timelimitintics)/TICRATE) & 1)
{
dontdraw = true;
}
jitter = 2;
if (drawtime & 2)
jitter = -jitter;
drawtime = 0;
}
else
{
drawtime = timelimitintics - drawtime;
if (secretextratime)
;
else if (extratimeintics)
{
jitter = 2;
if (leveltime & 1)
jitter = -jitter;
}
else if (drawtime <= 5*TICRATE)
{
jitter = ((drawtime <= 3*TICRATE) && (((drawtime-1) % TICRATE) >= TICRATE-2))
? 3 : 1;
if (drawtime & 2)
jitter = -jitter;
}
}
}
}
@ -1422,57 +1436,39 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
worktime = drawtime/(60*TICRATE);
if (worktime >= 100)
{
jitter = (drawtime & 1 ? 1 : -1);
worktime = 99;
drawtime = (100*(60*TICRATE))-1;
}
if (mode && !drawtime)
V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--"));
else if (dontdraw) // overtime flash
;
else if (worktime < 100) // 99:99:99 only
else
{
// zero minute
if (worktime < 10)
{
V_DrawKartString(TX, TY+3, splitflags, va("0"));
// minutes time 0 __ __
V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime));
}
// minutes time 0 __ __
else
V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime));
// minutes time 00 __ __
V_DrawKartString(TX, TY+3+jitter, splitflags, va("%d", worktime/10));
V_DrawKartString(TX+12, TY+3-jitter, splitflags, va("%d", worktime%10));
// apostrophe location _'__ __
V_DrawKartString(TX+24, TY+3, splitflags, va("'"));
worktime = (drawtime/TICRATE % 60);
// zero second _ 0_ __
if (worktime < 10)
{
V_DrawKartString(TX+36, TY+3, splitflags, va("0"));
// seconds time _ _0 __
V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime));
}
// zero second _ 00 __
else
V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime));
// seconds time _ 00 __
V_DrawKartString(TX+36, TY+3+jitter, splitflags, va("%d", worktime/10));
V_DrawKartString(TX+48, TY+3-jitter, splitflags, va("%d", worktime%10));
// quotation mark location _ __"__
V_DrawKartString(TX+60, TY+3, splitflags, va("\""));
worktime = G_TicsToCentiseconds(drawtime);
// zero tick _ __ 0_
if (worktime < 10)
{
V_DrawKartString(TX+72, TY+3, splitflags, va("0"));
// tics _ __ _0
V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime));
}
// zero tick _ __ 00
else
V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime));
// tics _ __ 00
V_DrawKartString(TX+72, TY+3+jitter, splitflags, va("%d", worktime/10));
V_DrawKartString(TX+84, TY+3-jitter, splitflags, va("%d", worktime%10));
}
else if ((drawtime/TICRATE) & 1)
V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99"));
if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time!
{
@ -3326,16 +3322,15 @@ static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32
static void K_drawKartMinimap(void)
{
patch_t *AutomapPic;
patch_t *AutomapPic, *workingPic;
INT32 i = 0;
INT32 x, y;
INT32 minimaptrans = 4;
INT32 splitflags = 0;
UINT8 skin = 0;
UINT8 *colormap = NULL;
SINT8 localplayers[4];
SINT8 localplayers[MAXSPLITSCREENPLAYERS];
SINT8 numlocalplayers = 0;
INT32 hyu = hyudorotime;
mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!)
fixed_t interpx, interpy;
@ -3412,12 +3407,9 @@ static void K_drawKartMinimap(void)
}
// initialize
for (i = 0; i < 4; i++)
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
localplayers[i] = -1;
if (gametype == GT_RACE)
hyu *= 2; // double in race
// Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen)
if (ghosts)
{
@ -3448,8 +3440,7 @@ static void K_drawKartMinimap(void)
if (!stplyr->mo || stplyr->spectator || stplyr->exiting)
return;
localplayers[numlocalplayers] = stplyr-players;
numlocalplayers++;
localplayers[numlocalplayers++] = stplyr-players;
}
else
{
@ -3457,50 +3448,60 @@ static void K_drawKartMinimap(void)
{
if (!playeringame[i])
continue;
if (!players[i].mo || players[i].spectator || players[i].exiting)
if (!players[i].mo || players[i].spectator || !players[i].mo->skin || players[i].exiting)
continue;
if (i != displayplayers[0] || r_splitscreen)
{
if (gametype == GT_BATTLE && players[i].bumpers <= 0)
continue;
if (players[i].hyudorotimer > 0)
{
if (!((players[i].hyudorotimer < TICRATE/2
|| players[i].hyudorotimer > hyu-(TICRATE/2))
&& !(leveltime & 1)))
continue;
}
}
if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3])
{
// Draw display players on top of everything else
localplayers[numlocalplayers] = i;
numlocalplayers++;
localplayers[numlocalplayers++] = i;
continue;
}
if (players[i].mo->skin)
skin = ((skin_t*)players[i].mo->skin)-skins;
else
skin = 0;
// Now we know it's not a display player, handle non-local player exceptions.
if ((gametyperules & GTR_BUMPERS) && players[i].bumpers <= 0)
continue;
if (players[i].mo->color)
if (players[i].hyudorotimer > 0)
{
if (players[i].mo->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE);
if (!((players[i].hyudorotimer < TICRATE/2
|| players[i].hyudorotimer > hyudorotime-(TICRATE/2))
&& !(leveltime & 1)))
continue;
}
mobj = players[i].mo;
if (mobj->health <= 0 && (players[i].pflags & PF_NOCONTEST))
{
workingPic = kp_nocontestminimap;
R_GetTranslationColormap(0, mobj->color, GTC_CACHE);
if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
mobj = mobj->tracer;
}
else
colormap = NULL;
{
skin = ((skin_t*)mobj->skin)-skins;
interpx = R_InterpolateFixed(players[i].mo->old_x, players[i].mo->x);
interpy = R_InterpolateFixed(players[i].mo->old_y, players[i].mo->y);
workingPic = faceprefix[skin][FACE_MINIMAP];
if (mobj->color)
{
if (mobj->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, mobj->color, GTC_CACHE);
}
else
colormap = NULL;
}
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic);
// Target reticule
if ((gametype == GT_RACE && players[i].position == spbplace)
|| (gametype == GT_BATTLE && K_IsPlayerWanted(&players[i])))
@ -3510,27 +3511,48 @@ static void K_drawKartMinimap(void)
}
}
// draw SPB(s?)
// draw minimap-pertinent objects
for (mobj = kitemcap; mobj; mobj = next)
{
next = mobj->itnext;
if (mobj->type == MT_SPB)
workingPic = NULL;
colormap = NULL;
if (mobj->health <= 0)
continue;
switch (mobj->type)
{
colormap = NULL;
if (mobj->target && !P_MobjWasRemoved(mobj->target))
{
if (mobj->player && mobj->player->skincolor)
colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE);
else if (mobj->color)
case MT_SPB:
workingPic = kp_spbminimap;
#if 0
if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player && mobj->target->player->skincolor)
{
colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->target->player->skincolor, GTC_CACHE);
}
else
#endif
if (mobj->color)
{
colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE);
}
}
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_spbminimap, colormap, AutomapPic);
break;
case MT_BATTLECAPSULE:
workingPic = kp_capsuleminimap[(mobj->extravalue1 != 0 ? 1 : 0)];
break;
default:
break;
}
if (!workingPic)
continue;
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, AutomapPic);
}
// draw our local players here, opaque.
@ -3569,28 +3591,43 @@ static void K_drawKartMinimap(void)
for (i = 0; i < numlocalplayers; i++)
{
if (i == -1)
if (localplayers[i] == -1)
continue; // this doesn't interest us
if (players[localplayers[i]].mo->skin)
skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins;
else
skin = 0;
if ((players[i].hyudorotimer > 0) && (leveltime & 1))
continue;
if (players[localplayers[i]].mo->color)
mobj = players[localplayers[i]].mo;
if (mobj->health <= 0 && (players[localplayers[i]].pflags & PF_NOCONTEST))
{
if (players[localplayers[i]].mo->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE);
workingPic = kp_nocontestminimap;
R_GetTranslationColormap(0, mobj->color, GTC_CACHE);
if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
mobj = mobj->tracer;
}
else
colormap = NULL;
{
skin = ((skin_t*)mobj->skin)-skins;
interpx = R_InterpolateFixed(players[localplayers[i]].mo->old_x, players[localplayers[i]].mo->x);
interpy = R_InterpolateFixed(players[localplayers[i]].mo->old_y, players[localplayers[i]].mo->y);
workingPic = faceprefix[skin][FACE_MINIMAP];
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap, AutomapPic);
if (mobj->color)
{
if (mobj->colorized)
colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE);
else
colormap = R_GetTranslationColormap(skin, mobj->color, GTC_CACHE);
}
else
colormap = NULL;
}
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, AutomapPic);
// Target reticule
if ((gametype == GT_RACE && players[localplayers[i]].position == spbplace)
@ -3601,6 +3638,60 @@ static void K_drawKartMinimap(void)
}
}
static void K_drawKartFinish(boolean finish)
{
INT32 timer, minsplitstationary, pnum = 0, splitflags = V_SPLITSCREEN;
patch_t **kptodraw;
if (finish)
{
timer = stplyr->karthud[khud_finish];
kptodraw = kp_racefinish;
minsplitstationary = 2;
}
else
{
timer = stplyr->karthud[khud_fault];
kptodraw = kp_racefault;
minsplitstationary = 1;
}
if (!timer || timer > 2*TICRATE)
return;
if ((timer % (2*5)) / 5) // blink
pnum = 1;
if (r_splitscreen > 0)
pnum += (r_splitscreen > 1) ? 2 : 4;
if (r_splitscreen >= minsplitstationary) // 3/4p, stationary FIN
{
V_DrawScaledPatch(STCD_X - (SHORT(kptodraw[pnum]->width)/2), STCD_Y - (SHORT(kptodraw[pnum]->height)/2), splitflags, kptodraw[pnum]);
return;
}
//else -- 1/2p, scrolling FINISH
{
INT32 x, xval, ox, interpx;
x = ((vid.width<<FRACBITS)/vid.dupx);
xval = (SHORT(kptodraw[pnum]->width)<<FRACBITS);
x = ((TICRATE - timer)*(xval > x ? xval : x))/TICRATE;
ox = ((TICRATE - (timer - 1))*(xval > x ? xval : x))/TICRATE;
interpx = R_InterpolateFixed(ox, x);
if (r_splitscreen && stplyr == &players[displayplayers[1]])
interpx = -interpx;
V_DrawFixedPatch(interpx + (STCD_X<<FRACBITS) - (xval>>1),
(STCD_Y<<FRACBITS) - (SHORT(kptodraw[pnum]->height)<<(FRACBITS-1)),
FRACUNIT,
splitflags, kptodraw[pnum], NULL);
}
}
static void K_drawKartStartBulbs(void)
{
const UINT8 start_animation[14] = {
@ -3765,39 +3856,11 @@ static void K_drawKartStartCountdown(void)
if (stplyr->karthud[khud_fault] != 0)
{
INT32 x, xval;
if (r_splitscreen > 1) // 3/4p, stationary FIN
{
pnum += 2;
}
else if (r_splitscreen == 1) // wide splitscreen
{
pnum += 4;
}
if ((leveltime % (2*5)) / 5) // blink
pnum += 1;
if (r_splitscreen == 0)
{
x = ((vid.width<<FRACBITS)/vid.dupx);
xval = (SHORT(kp_racefault[pnum]->width)<<FRACBITS);
x = ((TICRATE - stplyr->karthud[khud_fault])*(xval > x ? xval : x))/TICRATE;
V_DrawFixedPatch(x + (STCD_X<<FRACBITS) - (xval>>1),
(STCD_Y<<FRACBITS) - (SHORT(kp_racefault[pnum]->height)<<(FRACBITS-1)),
FRACUNIT,
V_SPLITSCREEN, kp_racefault[pnum], NULL);
}
else
{
V_DrawScaledPatch(STCD_X - (SHORT(kp_racefault[pnum]->width)/2), STCD_Y - (SHORT(kp_racefault[pnum]->height)/2), V_SPLITSCREEN, kp_racefault[pnum]);
}
K_drawKartFinish(false);
}
else if (leveltime >= introtime && leveltime < starttime-(3*TICRATE))
{
if (bossinfo.boss == false)
if (numbulbs > 1)
K_drawKartStartBulbs();
}
else
@ -3839,47 +3902,6 @@ static void K_drawKartStartCountdown(void)
}
}
static void K_drawKartFinish(void)
{
INT32 pnum = 0, splitflags = V_SPLITSCREEN;
if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE)
return;
if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink
pnum = 1;
if (r_splitscreen > 1) // 3/4p, stationary FIN
{
pnum += 2;
V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]);
return;
}
//else -- 1/2p, scrolling FINISH
{
INT32 x, xval, ox, interpx;
if (r_splitscreen) // wide splitscreen
pnum += 4;
x = ((vid.width<<FRACBITS)/vid.dupx);
xval = (SHORT(kp_racefinish[pnum]->width)<<FRACBITS);
x = ((TICRATE - stplyr->karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE;
ox = ((TICRATE - (stplyr->karthud[khud_cardanimation] - 1))*(xval > x ? xval : x))/TICRATE;
interpx = R_InterpolateFixed(ox, x);
if (r_splitscreen && stplyr == &players[displayplayers[1]])
interpx = -interpx;
V_DrawFixedPatch(interpx + (STCD_X<<FRACBITS) - (xval>>1),
(STCD_Y<<FRACBITS) - (SHORT(kp_racefinish[pnum]->height)<<(FRACBITS-1)),
FRACUNIT,
splitflags, kp_racefinish[pnum], NULL);
}
}
static void K_drawBattleFullscreen(void)
{
INT32 x = BASEVIDWIDTH/2;
@ -3931,19 +3953,19 @@ static void K_drawBattleFullscreen(void)
{
if (stplyr == &players[displayplayers[0]])
V_DrawFadeScreen(0xFF00, 16);
if (stplyr->exiting < 6*TICRATE && !stplyr->spectator)
if (exitcountdown <= 6*TICRATE && !stplyr->spectator)
{
patch_t *p = kp_battlecool;
if (K_IsPlayerLosing(stplyr))
p = kp_battlelose;
else if (stplyr->position == 1)
else if (stplyr->position == 1 && (!battlecapsules || numtargets >= maptargets))
p = kp_battlewin;
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, scale, splitflags, p, NULL);
}
else
K_drawKartFinish();
K_drawKartFinish(true);
}
else if (stplyr->bumpers <= 0 && stplyr->karmadelay && !stplyr->spectator && drawcomebacktimer)
{
@ -3985,7 +4007,7 @@ static void K_drawBattleFullscreen(void)
}
}
if (netgame && !stplyr->spectator) // FREE PLAY?
// FREE PLAY?
{
UINT8 i;
@ -3994,11 +4016,11 @@ static void K_drawBattleFullscreen(void)
{
if (i == displayplayers[0])
continue;
if (playeringame[i] && !stplyr->spectator)
return;
if (playeringame[i] && !players[i].spectator)
break;
}
if (LUA_HudEnabled(hud_freeplay))
if (i != MAXPLAYERS)
K_drawKartFreePlay();
}
}
@ -4392,8 +4414,13 @@ static void K_drawTrickCool(void)
void K_drawKartFreePlay(void)
{
// no splitscreen support because it's not FREE PLAY if you have more than one player in-game
// (you fool, you can take splitscreen online. :V)
// Doesn't support splitscreens higher than 2 for real estate reasons.
if (!LUA_HudEnabled(hud_freeplay))
return;
if (modeattacking || grandprixinfo.gp || bossinfo.boss || stplyr->spectator)
return;
if (lt_exitticker < TICRATE/2)
return;
@ -4402,7 +4429,7 @@ void K_drawKartFreePlay(void)
return;
V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - 72, // mirror the laps thingy
LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY");
LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, "FREE PLAY");
}
static void
@ -4750,7 +4777,7 @@ void K_drawKartHUD(void)
if (gametype == GT_RACE && !freecam)
{
if (stplyr->exiting)
K_drawKartFinish();
K_drawKartFinish(true);
else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen)
K_drawLapStartAnim();
}
@ -4766,11 +4793,8 @@ void K_drawKartHUD(void)
V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
// Draw FREE PLAY.
if (islonesome && !modeattacking && !bossinfo.boss && !stplyr->spectator)
{
if (LUA_HudEnabled(hud_freeplay))
K_drawKartFreePlay();
}
if (islonesome)
K_drawKartFreePlay();
if (r_splitscreen == 0 && (stplyr->pflags & PF_WRONGWAY) && ((leveltime / 8) & 1))
{

View file

@ -40,6 +40,8 @@
#include "k_collide.h"
#include "k_follower.h"
#include "k_objects.h"
#include "k_grandprix.h"
#include "k_specialstage.h"
// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
// gamespeed is cc (0 for easy, 1 for normal, 2 for hard)
@ -95,70 +97,86 @@ void K_TimerReset(void)
{
starttime = introtime = 3;
numbulbs = 1;
inDuel = false;
inDuel = rainbowstartavailable = false;
timelimitintics = extratimeintics = secretextratime = 0;
}
void K_TimerInit(void)
{
UINT8 i;
UINT8 numPlayers = 0;//, numspec = 0;
UINT8 numPlayers = 0;
boolean domodeattack = ((modeattacking != ATTACKING_NONE)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
if (!bossinfo.boss)
if (specialStage.active == true)
{
for (i = 0; i < MAXPLAYERS; i++)
K_InitSpecialStage();
}
else if (bossinfo.boss == false)
{
if (!domodeattack)
{
if (!playeringame[i])
for (i = 0; i < MAXPLAYERS; i++)
{
continue;
if (!playeringame[i] || players[i].spectator)
{
continue;
}
numPlayers++;
}
if (players[i].spectator == true)
if (numPlayers < 2)
{
//numspec++;
continue;
domodeattack = true;
}
else
{
numbulbs = 5;
rainbowstartavailable = true;
numPlayers++;
}
// 1v1 activates DUEL rules!
inDuel = (numPlayers == 2);
// 1v1 activates DUEL rules!
inDuel = (numPlayers == 2);
if (numPlayers >= 2)
{
rainbowstartavailable = true;
}
else
{
rainbowstartavailable = false;
}
// No intro in Record Attack / 1v1
// Leave unset for the value in K_TimerReset
if (numPlayers > 2)
{
introtime = (108) + 5; // 108 for rotation, + 5 for white fade
}
numbulbs = 5;
if (numPlayers > 2)
{
numbulbs += (numPlayers-2);
if (!inDuel)
{
introtime = (108) + 5; // 108 for rotation, + 5 for white fade
numbulbs += (numPlayers-2); // Extra POSITION!! time
}
}
}
starttime = (introtime + (3*TICRATE)) + ((2*TICRATE) + (numbulbs * bulbtime)); // Start countdown time, + buffer time
}
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
K_BattleInit();
K_BattleInit(domodeattack);
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss && !modeattacking)
{
if (!K_CanChangeRules(true))
{
if (battlecapsules)
{
timelimitintics = (20*TICRATE);
}
else
{
timelimitintics = timelimits[gametype] * (60*TICRATE);
}
}
else
#ifndef TESTOVERTIMEINFREEPLAY
if (!battlecapsules)
#endif
{
timelimitintics = cv_timelimit.value * (60*TICRATE);
}
}
if (inDuel == true)
{
K_SpawnDuelOnlyItems();
}
//CONS_Printf("numbulbs set to %d (%d players, %d spectators) on tic %d\n", numbulbs, numPlayers, numspec, leveltime);
}
UINT32 K_GetPlayerDontDrawFlag(player_t *player)
@ -316,6 +334,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartdebugcolorize);
CV_RegisterVar(&cv_kartdebugdirector);
CV_RegisterVar(&cv_spbtest);
CV_RegisterVar(&cv_gptest);
}
//}
@ -325,10 +344,10 @@ boolean K_IsPlayerLosing(player_t *player)
INT32 winningpos = 1;
UINT8 i, pcount = 0;
if (battlecapsules && player->bumpers <= 0)
return true; // DNF in break the capsules
if (battlecapsules && numtargets == 0)
return true; // Didn't even TRY?
if (bossinfo.boss)
if (battlecapsules || bossinfo.boss)
return (player->bumpers <= 0); // anything short of DNF is COOL
if (player->position == 1)
@ -467,6 +486,40 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{ 5, 1 } // Jawz x2
};
static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
{
//M N O P
{ 1, 1, 0, 0 }, // Sneaker
{ 0, 0, 0, 0 }, // Rocket Sneaker
{ 0, 0, 0, 0 }, // Invincibility
{ 0, 0, 0, 0 }, // Banana
{ 0, 0, 0, 0 }, // Eggman Monitor
{ 1, 1, 0, 0 }, // Orbinaut
{ 1, 1, 0, 0 }, // Jawz
{ 0, 0, 0, 0 }, // Mine
{ 0, 0, 0, 0 }, // Land Mine
{ 0, 0, 0, 0 }, // Ballhog
{ 0, 0, 0, 1 }, // Self-Propelled Bomb
{ 0, 0, 0, 0 }, // Grow
{ 0, 0, 0, 0 }, // Shrink
{ 0, 0, 0, 0 }, // Lightning Shield
{ 0, 0, 0, 0 }, // Bubble Shield
{ 0, 0, 0, 0 }, // Flame Shield
{ 0, 0, 0, 0 }, // Hyudoro
{ 0, 0, 0, 0 }, // Pogo Spring
{ 0, 0, 0, 0 }, // Super Ring
{ 0, 0, 0, 0 }, // Kitchen Sink
{ 0, 0, 0, 0 }, // Drop Target
{ 0, 0, 0, 0 }, // Garden Top
{ 0, 1, 1, 0 }, // Sneaker x2
{ 0, 0, 1, 1 }, // Sneaker x3
{ 0, 0, 0, 0 }, // Banana x3
{ 0, 0, 0, 0 }, // Banana x10
{ 0, 1, 1, 0 }, // Orbinaut x3
{ 0, 0, 1, 1 }, // Orbinaut x4
{ 0, 0, 1, 1 } // Jawz x2
};
#define DISTVAR (2048) // Magic number distance for use with item roulette tiers
#define SPBSTARTDIST (6*DISTVAR) // Distance when SPB can start appearing
#define SPBFORCEDIST (12*DISTVAR) // Distance when SPB is forced onto the next person who rolls an item
@ -6449,7 +6502,8 @@ static void K_DoShrink(player_t *user)
{
next = mobj->itnext;
if (mobj->type == MT_SPB)
if (mobj->type == MT_SPB
|| mobj->type == MT_BATTLECAPSULE)
{
continue;
}
@ -7762,7 +7816,7 @@ void K_KartPlayerHUDUpdate(player_t *player)
if (!(player->pflags & PF_FAULT))
player->karthud[khud_fault] = 0;
else if (player->karthud[khud_fault] > 0 && player->karthud[khud_fault] < 2*TICRATE)
else if (player->karthud[khud_fault] > 0 && player->karthud[khud_fault] <= 2*TICRATE)
player->karthud[khud_fault]++;
if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0)
@ -7771,30 +7825,33 @@ void K_KartPlayerHUDUpdate(player_t *player)
player->karthud[khud_itemblink] = 0;
}
if (gametype == GT_RACE)
if (!(gametyperules & GTR_SPHERES))
{
// 0 is the fast spin animation, set at 30 tics of ring boost or higher!
if (player->ringboost >= 30)
player->karthud[khud_ringdelay] = 0;
else
player->karthud[khud_ringdelay] = ((RINGANIM_DELAYMAX+1) * (30 - player->ringboost)) / 30;
if (player->mo && player->mo->hitlag <= 0)
{
// 0 is the fast spin animation, set at 30 tics of ring boost or higher!
if (player->ringboost >= 30)
player->karthud[khud_ringdelay] = 0;
else
player->karthud[khud_ringdelay] = ((RINGANIM_DELAYMAX+1) * (30 - player->ringboost)) / 30;
if (player->karthud[khud_ringframe] == 0 && player->karthud[khud_ringdelay] > RINGANIM_DELAYMAX)
{
player->karthud[khud_ringframe] = 0;
player->karthud[khud_ringtics] = 0;
}
else if ((player->karthud[khud_ringtics]--) <= 0)
{
if (player->karthud[khud_ringdelay] == 0) // fast spin animation
if (player->karthud[khud_ringframe] == 0 && player->karthud[khud_ringdelay] > RINGANIM_DELAYMAX)
{
player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+2) % RINGANIM_NUMFRAMES);
player->karthud[khud_ringframe] = 0;
player->karthud[khud_ringtics] = 0;
}
else
else if ((player->karthud[khud_ringtics]--) <= 0)
{
player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+1) % RINGANIM_NUMFRAMES);
player->karthud[khud_ringtics] = min(RINGANIM_DELAYMAX, player->karthud[khud_ringdelay])-1;
if (player->karthud[khud_ringdelay] == 0) // fast spin animation
{
player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+2) % RINGANIM_NUMFRAMES);
player->karthud[khud_ringtics] = 0;
}
else
{
player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+1) % RINGANIM_NUMFRAMES);
player->karthud[khud_ringtics] = min(RINGANIM_DELAYMAX, player->karthud[khud_ringdelay])-1;
}
}
}
@ -7824,16 +7881,20 @@ void K_KartPlayerHUDUpdate(player_t *player)
player->karthud[khud_ringspblock] = (leveltime % 14); // reset to normal anim next time
}
if (player->exiting)
{
if (player->karthud[khud_finish] <= 2*TICRATE)
player->karthud[khud_finish]++;
}
else
player->karthud[khud_finish] = 0;
if ((gametyperules & GTR_BUMPERS) && (player->exiting || player->karmadelay))
{
if (player->exiting)
{
if (player->exiting < 6*TICRATE)
if (exitcountdown < 6*TICRATE)
player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1;
else if (player->exiting == 6*TICRATE)
player->karthud[khud_cardanimation] = 0;
else if (player->karthud[khud_cardanimation] < 2*TICRATE)
player->karthud[khud_cardanimation]++;
}
else
{
@ -7848,11 +7909,6 @@ void K_KartPlayerHUDUpdate(player_t *player)
if (player->karthud[khud_cardanimation] < 0)
player->karthud[khud_cardanimation] = 0;
}
else if (gametype == GT_RACE && player->exiting)
{
if (player->karthud[khud_cardanimation] < 2*TICRATE)
player->karthud[khud_cardanimation]++;
}
else
player->karthud[khud_cardanimation] = 0;
}
@ -8409,8 +8465,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_UpdateTripwire(player);
K_KartPlayerHUDUpdate(player);
if (battleovertime.enabled && !(player->pflags & PF_ELIMINATED) && player->bumpers <= 0 && player->karmadelay <= 0)
{
if (player->overtimekarma)
@ -11513,7 +11567,7 @@ void K_CheckSpectateStatus(void)
return;
continue;
}
else if (!(players[i].pflags & PF_WANTSTOJOIN))
else if (players[i].bot || !(players[i].pflags & PF_WANTSTOJOIN))
continue;
respawnlist[numjoiners++] = i;

View file

@ -3443,9 +3443,7 @@ void M_CupSelectHandler(INT32 choice)
// Don't restart the server if we're already in a game lol
if (gamestate == GS_MENU)
{
SV_StartSinglePlayerServer();
multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this...
netgame = levellist.netgame; // ^ ditto.
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
}
levelNum = grandprixinfo.cup->cachedlevels[0];
@ -3600,9 +3598,7 @@ void M_LevelSelectHandler(INT32 choice)
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer();
multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this...
netgame = levellist.netgame; // ^ ditto.
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string);
CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto");
@ -3700,7 +3696,7 @@ void M_StartTimeAttack(INT32 choice)
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer();
SV_StartSinglePlayerServer(levellist.newgametype, false);
gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s",
srb2home, timeattackfolder);
@ -4398,7 +4394,7 @@ void M_InitOptions(INT32 choice)
// enable gameplay & server options under the right circumstances.
if (gamestate == GS_MENU
|| ((server || IsPlayerAdmin(consoleplayer)) && K_CanChangeRules()))
|| ((server || IsPlayerAdmin(consoleplayer)) && K_CanChangeRules(false)))
{
OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU;
OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU;
@ -5734,7 +5730,7 @@ void M_OpenPauseMenu(void)
Dummymenuplayer_OnChange(); // Make sure the consvar is within bounds of the amount of splitscreen players we have.
if (K_CanChangeRules())
if (K_CanChangeRules(false))
{
PAUSE_Main[mpause_psetup].status = IT_STRING | IT_CALL;

139
src/k_specialstage.c Normal file
View file

@ -0,0 +1,139 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2022 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file k_specialstage.c
/// \brief Special Stage game logic
#include "k_specialstage.h"
#include "doomdef.h"
#include "d_player.h"
#include "g_game.h"
#include "p_local.h"
#include "k_kart.h"
#include "s_sound.h"
#include "st_stuff.h"
#include "z_zone.h"
#include "k_waypoint.h"
struct specialStage specialStage;
/*--------------------------------------------------
void K_ResetSpecialStage(void)
See header file for description.
--------------------------------------------------*/
void K_ResetSpecialStage(void)
{
memset(&specialStage, 0, sizeof(struct specialStage));
}
/*--------------------------------------------------
void K_InitSpecialStage(void)
See header file for description.
--------------------------------------------------*/
void K_InitSpecialStage(void)
{
INT32 i;
specialStage.beamDist = UINT32_MAX; // TODO: make proper value
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true)
{
continue;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
continue;
}
// Rolling start? lol
P_InstaThrust(player->mo, player->mo->angle, K_GetKartSpeed(player, false, false));
}
}
/*--------------------------------------------------
static void K_MoveExitBeam(void)
Updates the exit beam.
--------------------------------------------------*/
static void K_MoveExitBeam(void)
{
UINT32 moveDist = 0;
INT32 i;
if (leveltime <= 2)
{
return;
}
moveDist = (8 * mapobjectscale) / FRACUNIT;
if (specialStage.beamDist <= moveDist)
{
specialStage.beamDist = 0;
// TODO: Fail Special Stage
}
else
{
specialStage.beamDist -= moveDist;
}
// Find players who are now outside of the level.
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = NULL;
if (playeringame[i] == false)
{
continue;
}
player = &players[i];
if (player->spectator == true
|| player->exiting > 0
|| (player->pflags & PF_NOCONTEST))
{
continue;
}
if (player->distancetofinish > specialStage.beamDist)
{
P_DoTimeOver(player);
}
}
}
/*--------------------------------------------------
void K_TickSpecialStage(void)
See header file for description.
--------------------------------------------------*/
void K_TickSpecialStage(void)
{
if (specialStage.active == false)
{
return;
}
K_MoveExitBeam();
}

55
src/k_specialstage.h Normal file
View file

@ -0,0 +1,55 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
// Copyright (C) 2022 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file k_specialstage.h
/// \brief Special Stage game logic
#ifndef __K_SPECIALSTAGE__
#define __K_SPECIALSTAGE__
#include "doomdef.h"
#include "doomstat.h"
extern struct specialStage
{
boolean active; ///< If true, then we are in a special stage
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
UINT32 beamDist; ///< Where the exit beam is.
mobj_t *capsule; ///< The Chaos Emerald capsule.
} specialStage;
/*--------------------------------------------------
void K_ResetSpecialStage(void);
Resets Special Stage information to a clean slate.
--------------------------------------------------*/
void K_ResetSpecialStage(void);
/*--------------------------------------------------
void K_InitSpecialStage(void);
Initializes Special Stage data on map load.
--------------------------------------------------*/
void K_InitSpecialStage(void);
/*--------------------------------------------------
void K_TickSpecialStage(void);
Updates Special Stage data each frame.
--------------------------------------------------*/
void K_TickSpecialStage(void);
#endif

View file

@ -205,7 +205,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
lua_pushinteger(L, redscore);
return 1;
} else if (fastcmp(word,"timelimit")) {
lua_pushinteger(L, cv_timelimit.value);
lua_pushinteger(L, timelimitintics);
return 1;
} else if (fastcmp(word,"pointlimit")) {
lua_pushinteger(L, cv_pointlimit.value);

View file

@ -47,7 +47,7 @@ typedef enum
PR_PLAYERSTARTS, // Player starts
PR_VOICES, // Player voice sounds
PR_RULESCRAMBLE, // Netgame rule scrambing events
PR_RULESCRAMBLE, // Rule scrambing events
PR_ITEM_ROULETTE, // Item results
PR_ITEM_RINGS, // Flung ring/bumper/player (on death)

View file

@ -4032,9 +4032,10 @@ void A_AttractChase(mobj_t *actor)
if (
actor->tracer->player && actor->tracer->health
&& actor->tracer->player->itemtype == KITEM_LIGHTNINGSHIELD
&& RINGTOTAL(actor->tracer->player) < 20
&& !(actor->tracer->player->pflags & PF_RINGLOCK)
&& ((gametyperules & GTR_SPHERES)
|| (actor->tracer->player->itemtype == KITEM_LIGHTNINGSHIELD
&& RINGTOTAL(actor->tracer->player) < 20
&& !(actor->tracer->player->pflags & PF_RINGLOCK)))
//&& P_CheckSight(actor, actor->tracer)
)
{

View file

@ -590,14 +590,9 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost)
player->starpostnum = post->health;
}
// Easily make it so that overtime works offline
#define TESTOVERTIMEINFREEPLAY
/** Checks if the level timer is over the timelimit and the round should end,
* unless you are in overtime. In which case leveltime may stretch out beyond
* timelimitintics and overtime's status will be checked here each tick.
* Verify that the value of ::cv_timelimit is greater than zero before
* calling this function.
*
* \sa cv_timelimit, P_CheckPointLimit, P_UpdateSpecials
*/
@ -605,27 +600,61 @@ void P_CheckTimeLimit(void)
{
INT32 i;
if (!cv_timelimit.value)
if (exitcountdown)
return;
#ifndef TESTOVERTIMEINFREEPLAY
if (battlecapsules) // capsules override any time limit settings
return;
#endif
if (!(gametyperules & GTR_TIMELIMIT))
if (!timelimitintics)
return;
if (bossinfo.boss == true)
if (leveltime < starttime)
{
if (secretextratime)
secretextratime--;
return;
}
if (leveltime < (timelimitintics + starttime))
{
if (secretextratime)
{
secretextratime--;
timelimitintics++;
}
else if (extratimeintics)
{
timelimitintics++;
if (leveltime & 1)
;
else
{
if (extratimeintics > 20)
{
extratimeintics -= 20;
timelimitintics += 20;
}
else
{
timelimitintics += extratimeintics;
extratimeintics = 0;
}
S_StartSound(NULL, sfx_ptally);
}
}
else
{
if (timelimitintics + starttime - leveltime <= 3*TICRATE)
{
if (((timelimitintics + starttime - leveltime) % TICRATE) == 0)
S_StartSound(NULL, sfx_s3ka7);
}
}
return;
}
if (gameaction == ga_completed)
return;
if ((cv_overtime.value) && (gametyperules & GTR_OVERTIME))
if ((grandprixinfo.gp == false) && (cv_overtime.value) && (gametyperules & GTR_OVERTIME))
{
#ifndef TESTOVERTIMEINFREEPLAY
boolean foundone = false; // Overtime is used for closing off down to a specific item.
@ -700,8 +729,6 @@ void P_CheckTimeLimit(void)
}
/** Checks if a player's score is over the pointlimit and the round should end.
* Verify that the value of ::cv_pointlimit is greater than zero before
* calling this function.
*
* \sa cv_pointlimit, P_CheckTimeLimit, P_UpdateSpecials
*/
@ -709,16 +736,19 @@ void P_CheckPointLimit(void)
{
INT32 i;
if (!cv_pointlimit.value)
if (exitcountdown)
return;
if (!(multiplayer || netgame))
if (!K_CanChangeRules(true))
return;
if (!cv_pointlimit.value)
return;
if (!(gametyperules & GTR_POINTLIMIT))
return;
if (bossinfo.boss == true)
if (battlecapsules)
return;
// pointlimit is nonzero, check if it's been reached by this player
@ -852,7 +882,7 @@ boolean P_CheckRacers(void)
}
// Everyone should be done playing at this point now.
racecountdown = exitcountdown = 0;
racecountdown = 0;
return true;
}
@ -860,7 +890,7 @@ boolean P_CheckRacers(void)
{
// There might be bots that are still going,
// but all of the humans are done, so we can exit now.
racecountdown = exitcountdown = 0;
racecountdown = 0;
return true;
}
@ -881,7 +911,7 @@ boolean P_CheckRacers(void)
{
tic_t countdown = 30*TICRATE; // 30 seconds left to finish, get going!
if (K_CanChangeRules() == true)
if (K_CanChangeRules(true) == true)
{
// Custom timer
countdown = cv_countdowntime.value * TICRATE;
@ -1262,6 +1292,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
kart->old_x = target->old_x;
kart->old_y = target->old_y;
kart->old_z = target->old_z;
if (target->player->pflags & PF_NOCONTEST)
P_SetTarget(&target->tracer, kart);
}
if (source && !P_MobjWasRemoved(source))
@ -1426,40 +1459,94 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
case MT_BATTLECAPSULE:
{
UINT8 i;
mobj_t *cur;
angle_t dir = 0;
numtargets++;
target->fuse = 16;
target->flags |= MF_NOCLIP|MF_NOCLIPTHING;
if (inflictor)
{
dir = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y);
P_Thrust(target, dir, P_AproxDistance(inflictor->momx, inflictor->momy)/12);
}
else if (source)
dir = R_PointToAngle2(source->x, source->y, target->x, target->y);
target->momz += 8 * target->scale * P_MobjFlip(target);
target->flags &= ~MF_NOGRAVITY;
cur = target->hnext;
while (cur && !P_MobjWasRemoved(cur))
{
cur->momx = target->momx;
cur->momy = target->momy;
cur->momz = target->momz;
// Shoot every piece outward
if (!(cur->x == target->x && cur->y == target->y))
{
P_InstaThrust(cur,
P_Thrust(cur,
R_PointToAngle2(target->x, target->y, cur->x, cur->y),
R_PointToDist2(target->x, target->y, cur->x, cur->y) / 12
);
}
cur->momz = 8 * target->scale * P_MobjFlip(target);
cur->flags &= ~MF_NOGRAVITY;
cur->tics = TICRATE;
cur->frame &= ~FF_ANIMATE; // Stop animating the propellers
cur->hitlag = target->hitlag;
cur->eflags |= MFE_DAMAGEHITLAG;
cur = cur->hnext;
}
// All targets busted!
if (numtargets >= maptargets)
S_StartSound(target, sfx_mbs60);
if ((gametyperules & GTR_POINTLIMIT) && (source && source->player))
{
UINT8 i;
/*mobj_t * ring;
for (i = 0; i < 2; i++)
{
dir += (ANGLE_MAX/3);
ring = P_SpawnMobj(target->x, target->y, target->z, MT_RING);
ring->angle = dir;
P_InstaThrust(ring, dir, 16*ring->scale);
ring->momz = 8 * target->scale * P_MobjFlip(target);
P_SetTarget(&ring->tracer, source);
source->player->pickuprings++;
}*/
P_AddPlayerScore(source->player, 1);
K_SpawnBattlePoints(source->player, NULL, 1);
}
// All targets busted!
if (++numtargets >= maptargets)
{
boolean givelife = false;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
P_DoPlayerExit(&players[i]);
if (!grandprixinfo.gp)
continue;
P_GivePlayerLives(&players[i], 1);
givelife = true;
}
if (givelife)
S_StartSound(NULL, sfx_cdfm73);
}
else if (timelimitintics)
{
S_StartSound(NULL, sfx_s221);
extratimeintics += 10*TICRATE;
secretextratime = TICRATE/2;
}
}
break;

View file

@ -5157,10 +5157,14 @@ boolean P_IsKartItem(INT32 type)
case MT_JAWZ_SHIELD:
case MT_SSMINE_SHIELD:
case MT_SINK_SHIELD:
case MT_SPB:
case MT_HYUDORO:
return true;
// Primarily for minimap data, handle with care
case MT_SPB:
case MT_BATTLECAPSULE:
return true;
default:
return P_IsKartFieldItem(type);
}
@ -9407,6 +9411,9 @@ void P_MobjThinker(mobj_t *mobj)
K_HandleDirectionalInfluence(mobj->player);
}
if (P_IsKartItem(mobj->type)) // mobj is a kart item we want on the list:
P_AddKartItem(mobj); // add to kitem list
return;
}

View file

@ -4597,6 +4597,10 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT32(save_p, starttime);
WRITEUINT8(save_p, numbulbs);
WRITEUINT32(save_p, timelimitintics);
WRITEUINT32(save_p, extratimeintics);
WRITEUINT32(save_p, secretextratime);
// Is it paused?
if (paused)
WRITEUINT8(save_p, 0x2f);

View file

@ -96,8 +96,8 @@
#include "k_boss.h"
#include "k_terrain.h" // TRF_TRIPWIRE
#include "k_brightmap.h"
#include "k_terrain.h" // TRF_TRIPWIRE
#include "k_director.h" // K_InitDirector
#include "k_specialstage.h"
// Replay names have time
#if !defined (UNDER_CE)
@ -3565,7 +3565,7 @@ static void P_InitLevelSettings(void)
// SRB2Kart: map load variables
if (grandprixinfo.gp == true)
{
if (gametype == GT_BATTLE)
if ((gametyperules & GTR_BUMPERS))
{
gamespeed = KARTSPEED_EASY;
}
@ -3584,7 +3584,10 @@ static void P_InitLevelSettings(void)
else if (modeattacking)
{
// Just play it safe and set everything
gamespeed = KARTSPEED_HARD;
if ((gametyperules & GTR_BUMPERS))
gamespeed = KARTSPEED_EASY;
else
gamespeed = KARTSPEED_HARD;
franticitems = false;
}
else
@ -3848,20 +3851,29 @@ static void P_InitGametype(void)
if (modeattacking && !demo.playback)
P_LoadRecordGhosts();
numlaps = 0;
if (gametyperules & GTR_CIRCUIT)
{
if ((netgame || multiplayer) && cv_numlaps.value
if (K_CanChangeRules(true) && cv_numlaps.value
&& (!(mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
|| (mapheaderinfo[gamemap - 1]->numlaps > cv_numlaps.value)))
{
numlaps = cv_numlaps.value;
}
else if ((grandprixinfo.gp == true)
&& (grandprixinfo.eventmode == GPEVENT_NONE)
&& cv_gptest.value)
{
numlaps = 1;
}
else
{
numlaps = mapheaderinfo[gamemap - 1]->numlaps;
}
}
else
{
numlaps = 0;
}
wantedcalcdelay = wantedfrequency*2;
@ -4474,6 +4486,42 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
continue;
}
// Always check for cup cache reassociations.
// (The core assumption is that cups < headers.)
{
cupheader_t *cup = kartcupheaders;
INT32 j;
while (cup)
{
for (j = 0; j < CUPCACHE_MAX; j++)
{
// No level in this slot?
if (!cup->levellist[j])
continue;
// Already discovered?
if (cup->cachedlevels[j] != NEXTMAP_INVALID)
continue;
// Not your name?
if (strcasecmp(cup->levellist[j], name) != 0)
continue;
// Only panic about back-reference for non-bonus material.
if (j < MAXLEVELLIST)
{
if (mapheaderinfo[i]->cup)
I_Error("P_InitMapData: Map %s cannot appear in cups multiple times! (First in %s, now in %s)", name, mapheaderinfo[i]->cup->name, cup->name);
mapheaderinfo[i]->cup = cup;
}
cup->cachedlevels[j] = i;
}
cup = cup->next;
}
}
// No change?
if (mapheaderinfo[i]->lumpnum == maplump)
continue;
@ -4527,37 +4575,6 @@ UINT8 P_InitMapData(INT32 numexistingmapheaders)
}
vres_Free(virtmap);
// Now associate it with a cup cache.
// (The core assumption is that cups < headers.)
if (i >= numexistingmapheaders)
{
cupheader_t *cup = kartcupheaders;
INT32 j;
while (cup)
{
for (j = 0; j < CUPCACHE_MAX; j++)
{
// Already discovered?
if (cup->cachedlevels[j] != NEXTMAP_INVALID)
continue;
if (!cup->levellist[j] || strcasecmp(cup->levellist[j], name) != 0)
continue;
// Only panic about back-reference for non-bonus material.
if (j < MAXLEVELLIST || j == CUPCACHE_SPECIAL)
{
if (mapheaderinfo[i]->cup)
I_Error("P_InitMapData: Map %s cannot appear in cups multiple times! (First in %s, now in %s)", name, mapheaderinfo[i]->cup->name, cup->name);
mapheaderinfo[i]->cup = cup;
}
cup->cachedlevels[j] = i;
}
cup = cup->next;
}
}
}
}

View file

@ -37,6 +37,7 @@
#include "k_boss.h"
#include "k_waypoint.h"
#include "k_director.h"
#include "k_specialstage.h"
tic_t leveltime;
@ -594,20 +595,32 @@ void P_Ticker(boolean run)
ps_playerthink_time = I_GetPreciseTime();
#define PLAYERCONDITION(i) (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
// First loop: Ensure all players' distance to the finish line are all accurate
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
K_UpdateDistanceFromFinishLine(&players[i]);
{
if (!PLAYERCONDITION(i))
continue;
K_UpdateDistanceFromFinishLine(&players[i]);
}
// Second loop: Ensure all player positions reflect everyone's distances
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
K_KartUpdatePosition(&players[i]);
{
if (!PLAYERCONDITION(i))
continue;
K_KartUpdatePosition(&players[i]);
}
// OK! Now that we got all of that sorted, players can think!
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerThink(&players[i]);
{
if (!PLAYERCONDITION(i))
continue;
P_PlayerThink(&players[i]);
K_KartPlayerHUDUpdate(&players[i]);
}
#undef PLAYERCONDITION
ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time;
}
@ -690,12 +703,20 @@ void P_Ticker(boolean run)
racecountdown--;
if (exitcountdown > 1)
{
exitcountdown--;
if (server && exitcountdown == 1)
{
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}
K_RunItemCooldowns();
K_BossInfoTicker();
K_TickSpecialStage();
if ((gametyperules & GTR_BUMPERS))
{
if (wantedcalcdelay && --wantedcalcdelay <= 0)

View file

@ -505,7 +505,6 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
num_rings -= (test+20);
player->rings += num_rings;
//player->totalring += num_rings; // Used for GP lives later -- maybe you might want to move this earlier to discourage ring debt...
return num_rings;
}
@ -1261,72 +1260,81 @@ void P_DoPlayerExit(player_t *player)
K_PlayerLoseLife(player);
}
if ((gametyperules & GTR_CIRCUIT)) // If in Race Mode, allow
player->exiting = 1;
if (!player->spectator)
{
player->exiting = raceexittime+2;
K_KartUpdatePosition(player);
if (cv_kartvoices.value)
if ((gametyperules & GTR_CIRCUIT)) // If in Race Mode, allow
{
if (P_IsDisplayPlayer(player))
K_KartUpdatePosition(player);
if (cv_kartvoices.value)
{
sfxenum_t sfx_id;
if (losing)
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound];
else
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound];
S_StartSound(NULL, sfx_id);
}
else
{
if (losing)
S_StartSound(player->mo, sfx_klose);
else
S_StartSound(player->mo, sfx_kwin);
}
}
if (cv_inttime.value > 0)
P_EndingMusic(player);
if (P_CheckRacers())
player->exiting = raceexittime+1;
}
else if ((gametyperules & GTR_BUMPERS)) // Battle Mode exiting
{
player->exiting = battleexittime+1;
P_EndingMusic(player);
}
else
player->exiting = raceexittime+2; // Accidental death safeguard???
if (grandprixinfo.gp == true)
{
if (player->bot)
{
// Bots are going to get harder... :)
K_IncreaseBotDifficulty(player);
}
else if (!losing)
{
const UINT8 lifethreshold = 20;
UINT8 extra = 0;
// YOU WIN
grandprixinfo.wonround = true;
// Increase your total rings
if (RINGTOTAL(player) > 0)
{
player->totalring += RINGTOTAL(player);
extra = player->totalring / lifethreshold;
if (extra > player->xtralife)
if (P_IsDisplayPlayer(player))
{
P_GivePlayerLives(player, extra - player->xtralife);
S_StartSound(NULL, sfx_cdfm73);
player->xtralife = extra;
sfxenum_t sfx_id;
if (losing)
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound];
else
sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound];
S_StartSound(NULL, sfx_id);
}
else
{
if (losing)
S_StartSound(player->mo, sfx_klose);
else
S_StartSound(player->mo, sfx_kwin);
}
}
// See Y_StartIntermission timer handling
if (!K_CanChangeRules(false) || cv_inttime.value > 0)
P_EndingMusic(player);
if (P_CheckRacers() && !exitcountdown)
exitcountdown = raceexittime+1;
}
else if ((gametyperules & GTR_BUMPERS)) // Battle Mode exiting
{
if (!exitcountdown)
exitcountdown = battleexittime+1;
P_EndingMusic(player);
}
else // Accidental death safeguard???
{
if (!exitcountdown)
exitcountdown = raceexittime+2;
}
if (grandprixinfo.gp == true)
{
if (player->bot)
{
// Bots are going to get harder... :)
K_IncreaseBotDifficulty(player);
}
else if (!losing)
{
const UINT8 lifethreshold = 20;
UINT8 extra = 0;
// YOU WIN
grandprixinfo.wonround = true;
// Increase your total rings
if (RINGTOTAL(player) > 0)
{
player->totalring += RINGTOTAL(player);
extra = player->totalring / lifethreshold;
if (extra > player->xtralife)
{
P_GivePlayerLives(player, extra - player->xtralife);
S_StartSound(NULL, sfx_cdfm73);
player->xtralife = extra;
}
}
}
}
@ -3853,8 +3861,6 @@ void P_PlayerThink(player_t *player)
player->old_drawangle = player->drawangle;
player->pflags &= ~PF_HITFINISHLINE;
if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
{
P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
@ -3867,11 +3873,6 @@ void P_PlayerThink(player_t *player)
if (player->awayviewtics && player->awayviewtics != -1)
player->awayviewtics--;
if (player->mo->hitlag > 0)
{
return;
}
// Track airtime
if (P_IsObjectOnGround(player->mo)
&& !P_PlayerInPain(player)) // This isn't airtime, but it's control loss all the same.
@ -3946,21 +3947,6 @@ void P_PlayerThink(player_t *player)
{
if (gametyperules & GTR_CIRCUIT)
{
INT32 i;
// Check if all the players in the race have finished. If so, end the level.
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
if (!players[i].exiting && !(players[i].pflags & PF_NOCONTEST) && players[i].lives > 0)
break;
}
}
if (i == MAXPLAYERS && player->exiting == raceexittime+2) // finished
player->exiting = raceexittime+1;
#if 0
// If 10 seconds are left on the timer,
// begin the drown music for countdown!
@ -3985,45 +3971,6 @@ void P_PlayerThink(player_t *player)
}
}
}
// If it is set, start subtracting
// Don't allow it to go back to 0
if (player->exiting > 1 && (player->exiting < raceexittime+2 || !(gametyperules & GTR_CIRCUIT))) // SRB2kart - "&& player->exiting > 1"
player->exiting--;
if (player->exiting && exitcountdown)
player->exiting = 99; // SRB2kart
if (player->exiting == 2 || exitcountdown == 2)
{
if (server)
{
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
}
}
// check water content, set stuff in mobj
P_MobjCheckWater(player->mo);
#ifndef SECTORSPECIALSAFTERTHINK
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
player->onconveyor = 0;
// check special sectors : damage & secrets
if (!player->spectator)
P_PlayerInSpecialSector(player);
#endif
if (player->playerstate == PST_DEAD)
{
if (player->spectator)
player->mo->renderflags |= RF_GHOSTLY;
else
player->mo->renderflags &= ~RF_GHOSTLYMASK;
P_DeathThink(player);
LUA_HookPlayer(player, HOOK(PlayerThink));
return;
}
// Make sure spectators always have a score and ring count of 0.
@ -4062,7 +4009,103 @@ void P_PlayerThink(player_t *player)
}
}
if ((netgame || multiplayer) && player->spectator && cmd->buttons & BT_ATTACK && !player->flashing)
if (cmd->flags & TICCMD_TYPING)
{
/*
typing_duration is slow to start and slow to stop.
typing_timer counts down a grace period before the player is not
actually considered typing anymore.
*/
if (cmd->flags & TICCMD_KEYSTROKE)
{
/* speed up if we are typing quickly! */
if (player->typing_duration > 0 && player->typing_timer > 12)
{
if (player->typing_duration < 16)
{
player->typing_duration = 24;
}
else
{
/* slows down a tiny bit as it approaches the next dot */
const UINT8 step = (((player->typing_duration + 15) & ~15) -
player->typing_duration) / 2;
player->typing_duration += max(step, 4);
}
}
player->typing_timer = 15;
}
else if (player->typing_timer > 0)
{
player->typing_timer--;
}
/* if we are in the grace period (including currently typing) */
if (player->typing_timer + player->typing_duration > 0)
{
/* always end the cycle on two dots */
if (player->typing_timer == 0 &&
(player->typing_duration < 16 || player->typing_duration == 40))
{
player->typing_duration = 0;
}
else if (player->typing_duration < 63)
{
player->typing_duration++;
}
else
{
player->typing_duration = 16;
}
}
}
else
{
player->typing_timer = 0;
player->typing_duration = 0;
}
/* ------------------------------------------ /
ALL ABOVE THIS BLOCK OCCURS EVEN WITH HITLAG
/ ------------------------------------------ */
if (player->mo->hitlag > 0)
{
return;
}
/* ------------------------------------------ /
ALL BELOW THIS BLOCK IS STOPPED DURING HITLAG
/ ------------------------------------------ */
player->pflags &= ~PF_HITFINISHLINE;
// check water content, set stuff in mobj
P_MobjCheckWater(player->mo);
#ifndef SECTORSPECIALSAFTERTHINK
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
player->onconveyor = 0;
// check special sectors : damage & secrets
if (!player->spectator)
P_PlayerInSpecialSector(player);
#endif
if (player->playerstate == PST_DEAD)
{
if (player->spectator)
player->mo->renderflags |= RF_GHOSTLY;
else
player->mo->renderflags &= ~RF_GHOSTLYMASK;
P_DeathThink(player);
LUA_HookPlayer(player, HOOK(PlayerThink));
return;
}
if ((netgame || multiplayer) && player->spectator && !player->bot && cmd->buttons & BT_ATTACK && !player->flashing)
{
player->pflags ^= PF_WANTSTOJOIN;
player->flashing = TICRATE/2 + 1;
@ -4144,64 +4187,6 @@ void P_PlayerThink(player_t *player)
player->mo->renderflags &= ~RF_DONTDRAW;
}
if (cmd->flags & TICCMD_TYPING)
{
/*
typing_duration is slow to start and slow to stop.
typing_timer counts down a grace period before the player is not
actually considered typing anymore.
*/
if (cmd->flags & TICCMD_KEYSTROKE)
{
/* speed up if we are typing quickly! */
if (player->typing_duration > 0 && player->typing_timer > 12)
{
if (player->typing_duration < 16)
{
player->typing_duration = 24;
}
else
{
/* slows down a tiny bit as it approaches the next dot */
const UINT8 step = (((player->typing_duration + 15) & ~15) -
player->typing_duration) / 2;
player->typing_duration += max(step, 4);
}
}
player->typing_timer = 15;
}
else if (player->typing_timer > 0)
{
player->typing_timer--;
}
/* if we are in the grace period (including currently typing) */
if (player->typing_timer + player->typing_duration > 0)
{
/* always end the cycle on two dots */
if (player->typing_timer == 0 &&
(player->typing_duration < 16 || player->typing_duration == 40))
{
player->typing_duration = 0;
}
else if (player->typing_duration < 63)
{
player->typing_duration++;
}
else
{
player->typing_duration = 16;
}
}
}
else
{
player->typing_timer = 0;
player->typing_duration = 0;
}
if (player->stairjank > 0)
{
player->stairjank--;

View file

@ -437,6 +437,7 @@ static patch_t *tcol2;
static patch_t *tcroundbar;
static patch_t *tcround;
static patch_t *tcbonus;
static patch_t *tccircletop;
static patch_t *tccirclebottom;
@ -446,6 +447,8 @@ static patch_t *tcbanner;
static patch_t *tcbanner2;
static patch_t *tcroundnum[10];
static patch_t *tcroundbonus;
static patch_t *tcactnum[10];
static patch_t *tcact;
@ -495,6 +498,7 @@ static void ST_cacheLevelTitle(void)
tcroundbar = (patch_t *)W_CachePatchName("TCBB0", PU_HUDGFX);
tcround = (patch_t *)W_CachePatchName("TCROUND", PU_HUDGFX);
tcbonus = (patch_t *)W_CachePatchName("TCBONUS", PU_HUDGFX);
tccircletop = (patch_t *)W_CachePatchName("TCSN1", PU_HUDGFX);
tccirclebottom =(patch_t *)W_CachePatchName("TCSN2", PU_HUDGFX);
@ -514,6 +518,7 @@ static void ST_cacheLevelTitle(void)
sprintf(buf, "TT_RND%d", i);
tcroundnum[i-1] = (patch_t *)W_CachePatchName(buf, PU_HUDGFX);
}
tcroundbonus = (patch_t *)W_CachePatchName("TT_RNDB", PU_HUDGFX);
// Cache act #
for (i=0; i < 10; i++)
@ -579,7 +584,7 @@ void ST_runTitleCard(void)
{
boolean run = !(paused || P_AutoPause());
INT32 auxticker;
boolean gp = (grandprixinfo.gp && grandprixinfo.roundnum); // check whether we're in grandprix
boolean gp = (marathonmode || (grandprixinfo.gp && grandprixinfo.roundnum));
if (!G_IsTitleCardAvailable())
return;
@ -764,7 +769,7 @@ void ST_drawTitleCard(void)
char *lvlttl = mapheaderinfo[gamemap-1]->lvlttl;
char *zonttl = mapheaderinfo[gamemap-1]->zonttl; // SRB2kart
UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
boolean gp = (grandprixinfo.gp && grandprixinfo.roundnum);
boolean gp = (marathonmode || (grandprixinfo.gp && grandprixinfo.roundnum));
INT32 acttimer;
fixed_t actscale;
@ -895,7 +900,9 @@ void ST_drawTitleCard(void)
V_DrawFixedPatch(roundx*FRACUNIT, ((-32) + (lt_ticker%32))*FRACUNIT, FRACUNIT, V_SNAPTOTOP|V_SNAPTOLEFT, tcroundbar, NULL);
// Draw ROUND text
if (gp)
V_DrawFixedPatch((roundx+10)*FRACUNIT, roundy*FRACUNIT, FRACUNIT, V_SNAPTOTOP|V_SNAPTOLEFT, tcround, NULL);
V_DrawFixedPatch((roundx+10)*FRACUNIT, roundy*FRACUNIT, FRACUNIT, V_SNAPTOTOP|V_SNAPTOLEFT,
((grandprixinfo.gp && grandprixinfo.eventmode) ? tcbonus : tcround),
NULL);
// round num background
V_DrawFixedPatch(roundnumx*FRACUNIT, roundnumy*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tccirclebg, NULL);
@ -912,9 +919,31 @@ void ST_drawTitleCard(void)
}
}
// If possible, draw round number
if (gp && grandprixinfo.roundnum > 0 && grandprixinfo.roundnum < 11) // Check boundaries JUST IN CASE.
V_DrawFixedPatch(roundnumx*FRACUNIT, roundnumy*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tcroundnum[grandprixinfo.roundnum-1], NULL);
// If possible, draw round number/icon
if (gp)
{
patch_t *roundico = NULL;
if (marathonmode)
; // TODO: Ruby
else switch (grandprixinfo.eventmode)
{
case GPEVENT_BONUS:
roundico = tcroundbonus; // TODO don't show capsule if we have other bonus types
break;
/*case GPEVENT_SPECIAL:
; // TODO: Emerald/mount
break;*/
case GPEVENT_NONE:
if (grandprixinfo.roundnum > 0 && grandprixinfo.roundnum < 11) // Check boundaries JUST IN CASE.
roundico = tcroundnum[grandprixinfo.roundnum-1];
break;
default:
break;
}
if (roundico)
V_DrawFixedPatch(roundnumx*FRACUNIT, roundnumy*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, roundico, NULL);
}
// Draw both halves of the egg
V_DrawFixedPatch(eggx1*FRACUNIT, eggy1*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tccircletop, NULL);

View file

@ -657,18 +657,8 @@ void Y_Ticker(void)
P_DoTeamscrambling();
}*/
// multiplayer uses timer (based on cv_inttime)
if (timer)
{
if (!--timer)
{
Y_EndIntermission();
G_AfterIntermission();
return;
}
}
// single player is hardcoded to go away after awhile
else if (intertic == endtic)
if ((timer && !--timer)
|| (intertic == endtic))
{
Y_EndIntermission();
G_AfterIntermission();
@ -680,10 +670,10 @@ void Y_Ticker(void)
if (intertype == int_race || intertype == int_battle || intertype == int_battletime)
{
//if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays
{
if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8))
{
// Anything with post-intermission consequences here should also occur in Y_EndIntermission.
K_RetireBots();
Y_CalculateMatchData(1, Y_CompareRank);
}
@ -746,8 +736,8 @@ void Y_Ticker(void)
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 -- This is how to define an endtic, but we currently use timer for both SP and MP.
endtic = intertic + 3*TICRATE;*/
}
}
}
@ -763,21 +753,26 @@ void Y_DetermineIntermissionType(void)
// set to int_none initially
intertype = int_none;
if (intermissiontypes[gametype] != int_none)
intertype = intermissiontypes[gametype];
else if (gametype == GT_RACE)
if (gametype == GT_RACE)
intertype = int_race;
else if (gametype == GT_BATTLE)
{
UINT8 i = 0, nump = 0;
for (i = 0; i < MAXPLAYERS; i++)
if (grandprixinfo.gp == true && bossinfo.boss == false)
intertype = int_none;
else
{
if (!playeringame[i] || players[i].spectator)
continue;
nump++;
UINT8 i = 0, nump = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
nump++;
}
intertype = (nump < 2 ? int_battletime : int_battle);
}
intertype = (nump < 2 ? int_battletime : int_battle);
}
else //if (intermissiontypes[gametype] != int_none)
intertype = intermissiontypes[gametype];
}
//
@ -806,23 +801,32 @@ void Y_StartIntermission(void)
powertype = K_UsingPowerLevels();
// determine the tic the intermission ends
if (!multiplayer || demo.playback)
// Technically cv_inttime is saved to demos... but this permits having extremely long timers for post-netgame chatting without stranding you on the intermission in netreplays.
if (!K_CanChangeRules(false))
{
timer = ((nump >= 2) ? 10 : 5)*TICRATE;
timer = 10*TICRATE;
}
else
{
timer = cv_inttime.value*TICRATE;
if (!timer)
timer = 1; // prevent a weird bug
}
// determine the tic everybody's scores/PWR starts getting sorted
sorttic = -1;
if (multiplayer || nump >= 2)
if (!timer)
{
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); // 8 second pause after match results
// Prevent a weird bug
timer = 1;
}
else if (nump < 2 && !netgame)
{
// No PWR/global score, skip it
timer /= 2;
}
else
{
// Minimum two seconds for match results, then two second slideover approx halfway through
sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE);
}
if (intermissiontypes[gametype] != int_none)
@ -841,7 +845,7 @@ void Y_StartIntermission(void)
case int_battle:
case int_battletime:
{
if (cv_inttime.value > 0)
if (timer > 1)
S_ChangeMusicInternal("racent", true); // loop it
// Calculate who won
@ -897,7 +901,11 @@ void Y_StartIntermission(void)
//
void Y_EndIntermission(void)
{
K_RetireBots();
if (!data.rankingsmode)
{
K_RetireBots();
}
Y_UnloadData();
endtic = -1;