"Special Mode" (Sealed Stars) and "Versus Mode" (bosses) are now gametypes

* The existing structs are now exclusively for handling extra data.
    * `specialStage` has been renamed to `specialstageinfo`, to reflect that it is not the sole arbiter.
    * `specialstageinfo.valid` and `bossinfo.valid` are what must be checked before grabbing data from either struct.
        * These are turned on when the gametype extra data is successfully initialised, not on map start.
            * `K_InitBossHealthBar(...)` for `bossinfo.valid`
            * `K_InitSpecialStage(void)` for `specialstageinfo.valid`
        * `K_CanChangeRules(...)` no longer checks these
    * No longer uses duplicate encore information.
* The map command (and -warp) now guesses gametype using a general `G_GuessGametypeByTOL(UINT32)` function
    * Grabs the first gametype with an overlap between the requested TOL and the gametype's TOL.
* The cool Versus-specific intro is now checked via `K_CheckBossIntro()`.
This commit is contained in:
toaster 2022-12-26 23:06:24 +00:00
parent d29e43f80d
commit 17dd15b998
29 changed files with 257 additions and 238 deletions

View file

@ -54,7 +54,6 @@
#include "k_pwrlv.h" #include "k_pwrlv.h"
#include "k_bot.h" #include "k_bot.h"
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h" #include "doomstat.h"
#include "s_sound.h" // sfx_syfail #include "s_sound.h" // sfx_syfail
#include "m_cond.h" // netUnlocked #include "m_cond.h" // netUnlocked

View file

@ -72,7 +72,6 @@
// SRB2Kart // SRB2Kart
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h" #include "doomstat.h"
#include "m_random.h" // P_ClearRandom #include "m_random.h" // P_ClearRandom
#include "k_specialstage.h" #include "k_specialstage.h"
@ -969,12 +968,6 @@ void D_StartTitle(void)
// Reset GP // Reset GP
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
// 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 // empty maptol so mario/etc sounds don't play in sound test when they shouldn't
maptol = 0; maptol = 0;
@ -1204,13 +1197,14 @@ D_ConvertVersionNumbers (void)
// //
void D_SRB2Main(void) void D_SRB2Main(void)
{ {
INT32 i, p; INT32 i, j, p;
#ifdef DEVELOP #ifdef DEVELOP
INT32 pstartmap = 1; // default to first loaded map (Test Run) INT32 pstartmap = 1; // default to first loaded map (Test Run)
#else #else
INT32 pstartmap = 0; // default to random map (0 is not a valid map number) INT32 pstartmap = 0; // default to random map (0 is not a valid map number)
#endif #endif
boolean autostart = false; boolean autostart = false;
INT32 newgametype = -1;
/* break the version string into version numbers, for netplay */ /* break the version string into version numbers, for netplay */
D_ConvertVersionNumbers(); D_ConvertVersionNumbers();
@ -1788,8 +1782,6 @@ void D_SRB2Main(void)
if (M_CheckParm("-gametype") && M_IsNextParm()) if (M_CheckParm("-gametype") && M_IsNextParm())
{ {
// from Command_Map_f // from Command_Map_f
INT32 j;
INT16 newgametype = -1;
const char *sgametype = M_GetNextParm(); const char *sgametype = M_GetNextParm();
newgametype = G_GetGametypeByName(sgametype); newgametype = G_GetGametypeByName(sgametype);
@ -1811,7 +1803,6 @@ void D_SRB2Main(void)
if (M_CheckParm("-skill") && M_IsNextParm()) if (M_CheckParm("-skill") && M_IsNextParm())
{ {
INT32 j;
INT16 newskill = -1; INT16 newskill = -1;
const char *sskill = M_GetNextParm(); const char *sskill = M_GetNextParm();
@ -1864,11 +1855,20 @@ void D_SRB2Main(void)
if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1]) if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1])
{ {
if (mapheaderinfo[pstartmap-1]->typeoflevel & TOL_SPECIAL) if (newgametype == -1)
{ {
specialStage.active = true; newgametype = G_GuessGametypeByTOL(mapheaderinfo[pstartmap-1]->typeoflevel);
specialStage.encore = grandprixinfo.encore; if (newgametype != -1)
{
j = gametype;
G_SetGametype(newgametype);
D_GameTypeChanged(j);
}
if (gametyperules & (GTR_BOSS|GTR_CATCHER))
grandprixinfo.eventmode = GPEVENT_SPECIAL; grandprixinfo.eventmode = GPEVENT_SPECIAL;
else if (gametype != GT_RACE)
grandprixinfo.eventmode = GPEVENT_BONUS;
} }
G_SetUsedCheats(); G_SetUsedCheats();

View file

@ -57,7 +57,6 @@
#include "k_color.h" #include "k_color.h"
#include "k_respawn.h" #include "k_respawn.h"
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h"
#include "k_follower.h" #include "k_follower.h"
#include "doomstat.h" #include "doomstat.h"
#include "deh_tables.h" #include "deh_tables.h"
@ -2525,15 +2524,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
FLS = false; FLS = false;
// Too lazy to change the input value for every instance of this function....... // Too lazy to change the input value for every instance of this function.......
if (bossinfo.boss == true) if (grandprixinfo.gp == true)
{
pencoremode = bossinfo.encore;
}
else if (specialStage.active == true)
{
pencoremode = specialStage.encore;
}
else if (grandprixinfo.gp == true)
{ {
pencoremode = grandprixinfo.encore; pencoremode = grandprixinfo.encore;
} }
@ -2843,7 +2834,15 @@ static void Command_Map_f(void)
if (mapheaderinfo[newmapnum-1]) if (mapheaderinfo[newmapnum-1])
{ {
// Let's just guess so we don't have to specify the gametype EVERY time... // Let's just guess so we don't have to specify the gametype EVERY time...
newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & (TOL_BATTLE|TOL_BOSS)) ? GT_BATTLE : GT_RACE; newgametype = G_GuessGametypeByTOL(mapheaderinfo[newmapnum-1]->typeoflevel);
if (newgametype == -1)
{
CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support any known gametype!\n"), realmapname, G_BuildMapName(newmapnum));
Z_Free(realmapname);
Z_Free(mapname);
return;
}
} }
} }
@ -2855,6 +2854,8 @@ static void Command_Map_f(void)
if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats) if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats)
{ {
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n")); CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
Z_Free(realmapname);
Z_Free(mapname);
return; return;
} }
} }
@ -2875,7 +2876,7 @@ static void Command_Map_f(void)
mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype) mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)
)) ))
{ {
CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), (gametype_cons_t[newgametype].strvalue)); CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum), gametypes[newgametype]->name);
Z_Free(realmapname); Z_Free(realmapname);
Z_Free(mapname); Z_Free(mapname);
return; return;
@ -2940,35 +2941,18 @@ static void Command_Map_f(void)
grandprixinfo.eventmode = GPEVENT_NONE; grandprixinfo.eventmode = GPEVENT_NONE;
if (newgametype == GT_BATTLE) if (gametypes[newgametype]->rules & (GTR_BOSS|GTR_CATCHER))
{ {
grandprixinfo.eventmode = GPEVENT_BONUS;
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS)
{
bossinfo.boss = true;
bossinfo.encore = newencoremode;
}
else
{
bossinfo.boss = false;
K_ResetBossInfo();
}
}
else
{
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
{
specialStage.active = true;
specialStage.encore = newencoremode;
grandprixinfo.eventmode = GPEVENT_SPECIAL; grandprixinfo.eventmode = GPEVENT_SPECIAL;
} }
else else if (newgametype != GT_RACE)
{ {
specialStage.active = false; grandprixinfo.eventmode = GPEVENT_BONUS;
} }
if (!Playing())
{
multiplayer = true;
} }
} }
@ -3022,7 +3006,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
else if (gametype != lastgametype) else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!(gametyperules & GTR_ENCORE) && !bossinfo.boss) if (!(gametyperules & GTR_ENCORE))
pencoremode = false; pencoremode = false;
skipprecutscene = ((flags & (1<<2)) != 0); skipprecutscene = ((flags & (1<<2)) != 0);
@ -5737,11 +5721,11 @@ void Command_Retry_f(void)
{ {
CONS_Printf(M_GetText("You must be in a level to use this.\n")); CONS_Printf(M_GetText("You must be in a level to use this.\n"));
} }
else if (grandprixinfo.gp == false && bossinfo.boss == false) else if (grandprixinfo.gp == false)
{ {
CONS_Printf(M_GetText("This only works in singleplayer games.\n")); CONS_Printf(M_GetText("This only works in singleplayer games.\n"));
} }
else if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE) else if (grandprixinfo.eventmode == GPEVENT_BONUS)
{ {
CONS_Printf(M_GetText("You can't retry right now!\n")); CONS_Printf(M_GetText("You can't retry right now!\n"));
} }

View file

@ -455,6 +455,8 @@ enum GameType
{ {
GT_RACE = 0, GT_RACE = 0,
GT_BATTLE, GT_BATTLE,
GT_SPECIAL,
GT_VERSUS,
GT_FIRSTFREESLOT, GT_FIRSTFREESLOT,
GT_LASTFREESLOT = 127, // Previously (GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1) - it would be necessary to rewrite VOTEMODIFIER_ENCORE to go higher than this. GT_LASTFREESLOT = 127, // Previously (GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1) - it would be necessary to rewrite VOTEMODIFIER_ENCORE to go higher than this.
@ -509,12 +511,16 @@ enum GameTypeRules
GTR_TEAMSTARTS = 1<<16, // Use team-based start positions GTR_TEAMSTARTS = 1<<16, // Use team-based start positions
// To be rearranged later // To be rearranged later
GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup. ...mutually exclusive with GTR_CAMPAIGN. GTR_CATCHER = 1<<17, // UFO Catcher (only works with GTR_CIRCUIT)
GTR_BOSS = 1<<18, // Boss intro and spawning
GTR_NOCUPSELECT = 1<<20, // Your maps are not selected via cup.
GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos GTR_CLOSERPLAYERS = 1<<21, // Buffs spindash and draft power to bring everyone together, nerfs invincibility and grow to prevent excessive combos
GTR_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping GTR_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping
// free: to and including 1<<31 // free: to and including 1<<31
}; };
// Remember to update GAMETYPERULE_LIST in deh_soc.c
// TODO: replace every instance // TODO: replace every instance
#define gametyperules (gametypes[gametype]->rules) #define gametyperules (gametypes[gametype]->rules)
@ -531,6 +537,7 @@ enum TypeOfLevel
// Modifiers // Modifiers
TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD
}; };
// Make sure to update TYPEOFLEVEL too
#define MAXTOL (1<<31) #define MAXTOL (1<<31)
#define NUMBASETOLNAMES (5) #define NUMBASETOLNAMES (5)

View file

@ -1425,9 +1425,9 @@ void G_StartTitleCard(void)
// play the sound // play the sound
{ {
sfxenum_t kstart = sfx_kstart; sfxenum_t kstart = sfx_kstart;
if (bossinfo.boss) if (K_CheckBossIntro() == true)
kstart = sfx_ssa021; kstart = sfx_ssa021;
else if (encoremode) else if (encoremode == true)
kstart = sfx_ruby2; kstart = sfx_ruby2;
S_StartSound(NULL, kstart); S_StartSound(NULL, kstart);
} }
@ -2790,22 +2790,9 @@ mapthing_t *G_FindMapStart(INT32 playernum)
if (!playeringame[playernum]) if (!playeringame[playernum])
return NULL; return NULL;
// -- Spectators -- // -- Time Attack --
// Order in platform gametypes: Race->DM->CTF
// And, with deathmatch starts: DM->CTF->Race
if (players[playernum].spectator)
{
// In platform gametypes, spawn in Co-op starts first
// Overriden by GTR_BATTLESTARTS.
if (gametyperules & GTR_BATTLESTARTS && bossinfo.boss == false)
spawnpoint = G_FindBattleStartOrFallback(playernum);
else
spawnpoint = G_FindRaceStartOrFallback(playernum);
}
// -- Grand Prix / Time Attack --
// Order: Race->DM->CTF // Order: Race->DM->CTF
else if (grandprixinfo.gp || modeattacking) if (K_TimeAttackRules() == true)
spawnpoint = G_FindRaceStartOrFallback(playernum); spawnpoint = G_FindRaceStartOrFallback(playernum);
// -- CTF -- // -- CTF --
@ -2911,7 +2898,7 @@ void G_ExitLevel(void)
{ {
UINT8 i; UINT8 i;
boolean youlost = false; boolean youlost = false;
if (bossinfo.boss == true) if (gametyperules & GTR_BOSS)
{ {
youlost = true; youlost = true;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -3021,12 +3008,35 @@ static gametype_t defaultgametypes[] =
0, 0,
2, 2,
}, },
// GT_SPECIAL
{
"Special",
"GT_SPECIAL",
GTR_CATCHER|GTR_CIRCUIT,
TOL_SPECIAL,
int_race,
0,
0,
},
// GT_VERSUS
{
"Versus",
"GT_VERSUS",
GTR_BOSS|GTR_SPHERES|GTR_BUMPERS|GTR_POINTLIMIT|GTR_CLOSERPLAYERS|GTR_NOCUPSELECT|GTR_ENCORE,
TOL_BOSS,
int_battle,
0,
0,
},
}; };
gametype_t *gametypes[MAXGAMETYPES+1] = gametype_t *gametypes[MAXGAMETYPES+1] =
{ {
&defaultgametypes[GT_RACE], &defaultgametypes[GT_RACE],
&defaultgametypes[GT_BATTLE], &defaultgametypes[GT_BATTLE],
&defaultgametypes[GT_SPECIAL],
&defaultgametypes[GT_VERSUS],
}; };
// //
@ -3048,6 +3058,25 @@ INT32 G_GetGametypeByName(const char *gametypestr)
return -1; // unknown gametype return -1; // unknown gametype
} }
//
// G_GuessGametypeByTOL
//
// Returns the first valid number for the given typeoflevel, or -1 if not valid.
//
INT32 G_GuessGametypeByTOL(UINT32 tol)
{
INT32 i = 0;
while (gametypes[i] != NULL)
{
if (tol & gametypes[i]->tol)
return i;
i++;
}
return -1; // unknown gametype
}
// //
// G_SetGametype // G_SetGametype
// //
@ -3651,7 +3680,7 @@ static void G_GetNextMap(void)
} }
else else
{ {
INT32 lastgametype = gametype; INT32 lastgametype = gametype, newgametype = GT_RACE;
// 5 levels, 2 bonus stages: after rounds 2 and 4 (but flexible enough to accomodate other solutions) // 5 levels, 2 bonus stages: after rounds 2 and 4 (but flexible enough to accomodate other solutions)
UINT8 bonusmodulo = (grandprixinfo.cup->numlevels+1)/(grandprixinfo.cup->numbonus+1); UINT8 bonusmodulo = (grandprixinfo.cup->numlevels+1)/(grandprixinfo.cup->numbonus+1);
UINT8 bonusindex = (grandprixinfo.roundnum / bonusmodulo) - 1; UINT8 bonusindex = (grandprixinfo.roundnum / bonusmodulo) - 1;
@ -3668,9 +3697,6 @@ static void G_GetNextMap(void)
G_SetGametype(GT_RACE); G_SetGametype(GT_RACE);
if (gametype != lastgametype) if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); D_GameTypeChanged(lastgametype);
specialStage.active = false;
bossinfo.boss = false;
} }
// Special stage // Special stage
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
@ -3691,11 +3717,11 @@ static void G_GetNextMap(void)
if (totaltotalring >= 50) if (totaltotalring >= 50)
{ {
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL]; const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_SPECIAL|TOL_BOSS|TOL_BATTLE))
{ {
grandprixinfo.eventmode = GPEVENT_SPECIAL; grandprixinfo.eventmode = GPEVENT_SPECIAL;
nextmap = cupLevelNum; nextmap = cupLevelNum;
newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel);
} }
} }
} }
@ -3706,37 +3732,27 @@ static void G_GetNextMap(void)
// todo any other condition? // todo any other condition?
{ {
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS + bonusindex]; const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS + bonusindex];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_BOSS|TOL_BATTLE))
{ {
grandprixinfo.eventmode = GPEVENT_BONUS; grandprixinfo.eventmode = GPEVENT_BONUS;
nextmap = cupLevelNum; nextmap = cupLevelNum;
newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel);
} }
} }
} }
if (newgametype == -1)
{
// Don't permit invalid changes.
grandprixinfo.eventmode = GPEVENT_NONE;
newgametype = gametype;
}
if (grandprixinfo.eventmode != GPEVENT_NONE) if (grandprixinfo.eventmode != GPEVENT_NONE)
{ {
// nextmap is set above G_SetGametype(newgametype);
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) if (gametype != lastgametype)
D_GameTypeChanged(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 else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
{ {
@ -3760,10 +3776,6 @@ static void G_GetNextMap(void)
} }
} }
} }
else if (bossinfo.boss == true)
{
nextmap = NEXTMAP_TITLE; // temporary
}
else else
{ {
UINT32 tolflag = G_TOLFlag(gametype); UINT32 tolflag = G_TOLFlag(gametype);

View file

@ -184,6 +184,8 @@ char *G_PrepareGametypeConstant(const char *newgtconst);
void G_UpdateGametypeSelections(void); void G_UpdateGametypeSelections(void);
void G_AddTOL(UINT32 newtol, const char *tolname); void G_AddTOL(UINT32 newtol, const char *tolname);
INT32 G_GetGametypeByName(const char *gametypestr); INT32 G_GetGametypeByName(const char *gametypestr);
INT32 G_GuessGametypeByTOL(UINT32 tol);
boolean G_IsSpecialStage(INT32 mapnum); boolean G_IsSpecialStage(INT32 mapnum);
boolean G_GametypeUsesLives(void); boolean G_GametypeUsesLives(void);
boolean G_GametypeHasTeams(void); boolean G_GametypeHasTeams(void);

View file

@ -55,7 +55,6 @@
// SRB2Kart // SRB2Kart
#include "s_sound.h" // song credits #include "s_sound.h" // song credits
#include "k_kart.h" #include "k_kart.h"
#include "k_boss.h"
#include "k_color.h" #include "k_color.h"
#include "k_hud.h" #include "k_hud.h"
#include "r_fps.h" #include "r_fps.h"
@ -2409,7 +2408,7 @@ static void HU_DrawRankings(void)
else if (gametype >= 0 && gametype < numgametypes) else if (gametype >= 0 && gametype < numgametypes)
V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametypes[gametype]->name); V_DrawString(4, 188, hilicol|V_SNAPTOBOTTOM|V_SNAPTOLEFT, gametypes[gametype]->name);
if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)) && !bossinfo.boss) if ((gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT)))
{ {
if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0) if ((gametyperules & GTR_TIMELIMIT) && timelimitintics > 0)
{ {

View file

@ -2,7 +2,6 @@
/// \brief SRB2Kart Battle Mode specific code /// \brief SRB2Kart Battle Mode specific code
#include "k_battle.h" #include "k_battle.h"
#include "k_boss.h"
#include "k_kart.h" #include "k_kart.h"
#include "doomtype.h" #include "doomtype.h"
#include "doomdata.h" #include "doomdata.h"
@ -357,7 +356,7 @@ void K_RunPaperItemSpawners(void)
UINT8 pcount = 0; UINT8 pcount = 0;
INT16 i; INT16 i;
if (battlecapsules || bossinfo.boss) if (battlecapsules)
{ {
// Gametype uses paper items, but this specific expression doesn't // Gametype uses paper items, but this specific expression doesn't
return; return;
@ -794,7 +793,7 @@ void K_BattleInit(boolean singleplayercontext)
{ {
size_t i; size_t i;
if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules && !bossinfo.boss) if ((gametyperules & GTR_CAPSULES) && singleplayercontext && !battlecapsules)
{ {
mapthing_t *mt = mapthings; mapthing_t *mt = mapthings;
for (i = 0; i < nummapthings; i++, mt++) for (i = 0; i < nummapthings; i++, mt++)

View file

@ -23,7 +23,7 @@
struct bossinfo bossinfo; struct bossinfo bossinfo;
/*-------------------------------------------------- /*--------------------------------------------------
void K_ClearBossInfo(void) void K_ResetBossInfo(void)
See header file for description. See header file for description.
--------------------------------------------------*/ --------------------------------------------------*/
@ -32,6 +32,8 @@ void K_ResetBossInfo(void)
Z_Free(bossinfo.enemyname); Z_Free(bossinfo.enemyname);
Z_Free(bossinfo.subtitle); Z_Free(bossinfo.subtitle);
memset(&bossinfo, 0, sizeof(struct bossinfo)); memset(&bossinfo, 0, sizeof(struct bossinfo));
bossinfo.barlen = BOSSHEALTHBARLEN;
bossinfo.titlesound = sfx_typri1;
} }
/*-------------------------------------------------- /*--------------------------------------------------
@ -43,7 +45,7 @@ void K_BossInfoTicker(void)
{ {
UINT8 i; UINT8 i;
if (bossinfo.boss == false) if (bossinfo.valid == false)
return; return;
// Update healthbar data. (only if the hud is visible) // Update healthbar data. (only if the hud is visible)
@ -108,6 +110,18 @@ void K_BossInfoTicker(void)
void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions) void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t titlesound, fixed_t pinchmagnitude, UINT8 divisions)
{ {
if (!(gametyperules & GTR_BOSS))
{
return;
}
bossinfo.valid = true;
if (!leveltime)
{
bossinfo.coolintro = true;
}
if (enemyname && enemyname[0]) if (enemyname && enemyname[0])
{ {
Z_Free(bossinfo.enemyname); Z_Free(bossinfo.enemyname);
@ -158,6 +172,9 @@ void K_InitBossHealthBar(const char *enemyname, const char *subtitle, sfxenum_t
void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen) void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen)
{ {
if (bossinfo.valid == false)
return;
if (magnitude > FRACUNIT) if (magnitude > FRACUNIT)
magnitude = FRACUNIT; magnitude = FRACUNIT;
else if (magnitude < 0) else if (magnitude < 0)
@ -177,6 +194,9 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean
{ {
UINT8 i; UINT8 i;
if (bossinfo.valid == false)
return;
// First check whether the spot is already in the list and simply redeclaring weakness (for example, a vulnerable moment in the pattern). // First check whether the spot is already in the list and simply redeclaring weakness (for example, a vulnerable moment in the pattern).
for (i = 0; i < NUMWEAKSPOTS; i++) for (i = 0; i < NUMWEAKSPOTS; i++)
if (bossinfo.weakspots[i].spot == spot) if (bossinfo.weakspots[i].spot == spot)
@ -206,3 +226,16 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean
bossinfo.doweakspotsound = spottype; bossinfo.doweakspotsound = spottype;
} }
} }
/*--------------------------------------------------
boolean K_CheckBossIntro(void);
See header file for description.
--------------------------------------------------*/
boolean K_CheckBossIntro(void)
{
if (bossinfo.valid == false)
return false;
return bossinfo.coolintro;
}

View file

@ -39,7 +39,8 @@ struct weakspot_t
extern struct bossinfo extern struct bossinfo
{ {
boolean boss; ///< If true, then we are fighting a boss boolean valid; ///< If true, then data in this struct is valid
UINT8 healthbar; ///< Actual health bar fill amount UINT8 healthbar; ///< Actual health bar fill amount
UINT8 visualbar; ///< Tracks above, but with delay UINT8 visualbar; ///< Tracks above, but with delay
fixed_t visualdiv; ///< How far apart health bar divisions should appear fixed_t visualdiv; ///< How far apart health bar divisions should appear
@ -48,7 +49,7 @@ extern struct bossinfo
UINT8 barlen; ///< The length of the bar (only reduced when a boss is deceased) UINT8 barlen; ///< The length of the bar (only reduced when a boss is deceased)
char *enemyname; ///< The name next to the bar char *enemyname; ///< The name next to the bar
weakspot_t weakspots[NUMWEAKSPOTS]; ///< Array of weak spots (for minimap/object tracking) weakspot_t weakspots[NUMWEAKSPOTS]; ///< Array of weak spots (for minimap/object tracking)
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars boolean coolintro; ///< Determines whether the map start(s/ed) with a boss-specific intro.
spottype_t doweakspotsound; ///< If nonzero, at least one weakspot was declared this tic spottype_t doweakspotsound; ///< If nonzero, at least one weakspot was declared this tic
tic_t titleshow; ///< Show this many letters on the titlecard tic_t titleshow; ///< Show this many letters on the titlecard
sfxenum_t titlesound; ///< Sound to play when title typing sfxenum_t titlesound; ///< Sound to play when title typing
@ -112,4 +113,16 @@ void K_UpdateBossHealthBar(fixed_t magnitude, tic_t jitterlen);
void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean minimap); void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean minimap);
/*--------------------------------------------------
boolean K_CheckBossIntro(void);
Checks whether the Versus-specific intro is playing for this map start.
Return:-
true if cool intro in action,
otherwise false.
--------------------------------------------------*/
boolean K_CheckBossIntro(void);
#endif #endif

View file

@ -27,7 +27,6 @@
#include "m_random.h" #include "m_random.h"
#include "r_things.h" // numskins #include "r_things.h" // numskins
#include "k_race.h" // finishBeamLine #include "k_race.h" // finishBeamLine
#include "k_boss.h"
#include "m_perfstats.h" #include "m_perfstats.h"
@ -168,7 +167,7 @@ void K_UpdateMatchRaceBots(void)
} }
} }
if (difficulty == 0 || !(gametyperules & GTR_BOTS) || bossinfo.boss == true) if (difficulty == 0 || !(gametyperules & GTR_BOTS))
{ {
wantedbots = 0; wantedbots = 0;
} }

View file

@ -11,7 +11,6 @@
/// \brief Grand Prix mode game logic & bot behaviors /// \brief Grand Prix mode game logic & bot behaviors
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h" #include "k_specialstage.h"
#include "doomdef.h" #include "doomdef.h"
#include "d_player.h" #include "d_player.h"
@ -529,7 +528,7 @@ void K_RetireBots(void)
UINT8 i; UINT8 i;
if (grandprixinfo.gp == true if (grandprixinfo.gp == true
&& ((grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) && ((grandprixinfo.cup != NULL && grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
|| grandprixinfo.eventmode != GPEVENT_NONE)) || grandprixinfo.eventmode != GPEVENT_NONE))
{ {
// No replacement. // No replacement.
@ -703,24 +702,12 @@ void K_PlayerLoseLife(player_t *player)
--------------------------------------------------*/ --------------------------------------------------*/
boolean K_CanChangeRules(boolean allowdemos) boolean K_CanChangeRules(boolean allowdemos)
{ {
if (grandprixinfo.gp == true && grandprixinfo.roundnum > 0) if (grandprixinfo.gp == true /*&& grandprixinfo.roundnum > 0*/)
{ {
// Don't cheat the rules of the GP! // Don't cheat the rules of the GP!
return false; return false;
} }
if (bossinfo.boss == true)
{
// Don't cheat the boss!
return false;
}
if (specialStage.active == true)
{
// Don't cheat special stages!
return false;
}
if (marathonmode) if (marathonmode)
{ {
// Don't cheat the endurance challenge! // Don't cheat the endurance challenge!

View file

@ -3360,7 +3360,7 @@ static void K_drawKartNameTags(void)
c.z = viewz; c.z = viewz;
// Maybe shouldn't be handling this here... but the camera info is too good. // Maybe shouldn't be handling this here... but the camera info is too good.
if (bossinfo.boss) if (bossinfo.valid == true)
{ {
weakspotdraw_t weakspotdraw[NUMWEAKSPOTS]; weakspotdraw_t weakspotdraw[NUMWEAKSPOTS];
UINT8 numdraw = 0; UINT8 numdraw = 0;
@ -3923,7 +3923,7 @@ static void K_drawKartMinimap(void)
// Target reticule // Target reticule
if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace) if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace)
|| ((gametyperules & GTR_POINTLIMIT) && K_IsPlayerWanted(&players[i]))) || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i])))
{ {
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic);
} }
@ -3981,7 +3981,7 @@ static void K_drawKartMinimap(void)
} }
// ...but first, any boss targets. // ...but first, any boss targets.
if (bossinfo.boss) if (bossinfo.valid == true)
{ {
for (i = 0; i < NUMWEAKSPOTS; i++) for (i = 0; i < NUMWEAKSPOTS; i++)
{ {
@ -4050,7 +4050,7 @@ static void K_drawKartMinimap(void)
// Target reticule // Target reticule
if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace) if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace)
|| ((gametyperules & GTR_POINTLIMIT) && K_IsPlayerWanted(&players[localplayers[i]]))) || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]])))
{ {
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic);
} }
@ -4838,7 +4838,7 @@ void K_drawKartFreePlay(void)
if (!LUA_HudEnabled(hud_freeplay)) if (!LUA_HudEnabled(hud_freeplay))
return; return;
if (modeattacking || grandprixinfo.gp || bossinfo.boss || stplyr->spectator) if (modeattacking || grandprixinfo.gp || bossinfo.valid || stplyr->spectator)
return; return;
if (lt_exitticker < TICRATE/2) if (lt_exitticker < TICRATE/2)
@ -5098,7 +5098,7 @@ void K_drawKartHUD(void)
{ {
if (LUA_HudEnabled(hud_position)) if (LUA_HudEnabled(hud_position))
{ {
if (bossinfo.boss) if (bossinfo.valid)
{ {
K_drawBossHealthBar(); K_drawBossHealthBar();
} }
@ -5140,7 +5140,7 @@ void K_drawKartHUD(void)
K_drawBlueSphereMeter(); K_drawBlueSphereMeter();
} }
if (modeattacking && !bossinfo.boss) if (modeattacking && !bossinfo.valid)
{ {
// Draw the input UI // Draw the input UI
if (LUA_HudEnabled(hud_position)) if (LUA_HudEnabled(hud_position))

View file

@ -6,7 +6,6 @@
#include "k_kart.h" #include "k_kart.h"
#include "k_battle.h" #include "k_battle.h"
#include "k_boss.h"
#include "k_pwrlv.h" #include "k_pwrlv.h"
#include "k_color.h" #include "k_color.h"
#include "k_respawn.h" #include "k_respawn.h"
@ -41,6 +40,7 @@
#include "k_follower.h" #include "k_follower.h"
#include "k_objects.h" #include "k_objects.h"
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h" #include "k_specialstage.h"
#include "k_roulette.h" #include "k_roulette.h"
@ -109,11 +109,13 @@ void K_TimerInit(void)
boolean domodeattack = ((modeattacking != ATTACKING_NONE) boolean domodeattack = ((modeattacking != ATTACKING_NONE)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)); || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
if (specialStage.active == true) if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT))
{ {
K_InitSpecialStage(); K_InitSpecialStage();
} }
else if (bossinfo.boss == false) else if (K_CheckBossIntro() == true)
;
else
{ {
if (!domodeattack) if (!domodeattack)
{ {
@ -152,7 +154,7 @@ void K_TimerInit(void)
K_BattleInit(domodeattack); K_BattleInit(domodeattack);
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss && !modeattacking) if ((gametyperules & GTR_TIMELIMIT) && !modeattacking)
{ {
if (!K_CanChangeRules(true)) if (!K_CanChangeRules(true))
{ {
@ -343,7 +345,7 @@ boolean K_IsPlayerLosing(player_t *player)
if (battlecapsules && numtargets == 0) if (battlecapsules && numtargets == 0)
return true; // Didn't even TRY? return true; // Didn't even TRY?
if (battlecapsules || bossinfo.boss) if (battlecapsules || (gametyperules & GTR_BOSS))
return (player->bumpers <= 0); // anything short of DNF is COOL return (player->bumpers <= 0); // anything short of DNF is COOL
if (player->position == 1) if (player->position == 1)
@ -513,7 +515,7 @@ boolean K_TimeAttackRules(void)
UINT8 playing = 0; UINT8 playing = 0;
UINT8 i; UINT8 i;
if (specialStage.active == true) if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT))
{ {
// Kind of a hack -- Special Stages // Kind of a hack -- Special Stages
// are expected to be 1-player, so // are expected to be 1-player, so
@ -1301,8 +1303,8 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed
*/ */
static void K_UpdateDraft(player_t *player) static void K_UpdateDraft(player_t *player)
{ {
const boolean addUfo = ((specialStage.active == true) const boolean addUfo = ((specialstageinfo.valid == true)
&& (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)); && (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false));
fixed_t topspd = K_GetKartSpeed(player, false, false); fixed_t topspd = K_GetKartSpeed(player, false, false);
fixed_t draftdistance; fixed_t draftdistance;
@ -1345,7 +1347,7 @@ static void K_UpdateDraft(player_t *player)
if (addUfo == true) if (addUfo == true)
{ {
// Tether off of the UFO! // Tether off of the UFO!
if (K_TryDraft(player, specialStage.ufo, minDist, draftdistance, leniency) == true) if (K_TryDraft(player, specialstageinfo.ufo, minDist, draftdistance, leniency) == true)
{ {
return; // Finished doing our draft. return; // Finished doing our draft.
} }
@ -1402,8 +1404,8 @@ static void K_UpdateDraft(player_t *player)
else if (addUfo == true) else if (addUfo == true)
{ {
// kind of a hack to not have to mess with how lastdraft works // kind of a hack to not have to mess with how lastdraft works
fixed_t dist = P_AproxDistance(P_AproxDistance(specialStage.ufo->x - player->mo->x, specialStage.ufo->y - player->mo->y), specialStage.ufo->z - player->mo->z); fixed_t dist = P_AproxDistance(P_AproxDistance(specialstageinfo.ufo->x - player->mo->x, specialstageinfo.ufo->y - player->mo->y), specialstageinfo.ufo->z - player->mo->z);
K_DrawDraftCombiring(player, specialStage.ufo, dist, draftdistance, true); K_DrawDraftCombiring(player, specialstageinfo.ufo, dist, draftdistance, true);
} }
} }
else // Remove draft speed boost. else // Remove draft speed boost.
@ -4128,7 +4130,7 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
player->karmadelay = comebacktime; player->karmadelay = comebacktime;
if (bossinfo.boss) if (gametyperules & GTR_BOSS)
{ {
P_DoTimeOver(player); P_DoTimeOver(player);
} }
@ -6789,10 +6791,10 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
mobj_t *wtarg = NULL; mobj_t *wtarg = NULL;
INT32 i; INT32 i;
if (specialStage.active == true) if (specialstageinfo.valid == true)
{ {
// Always target the UFO. // Always target the UFO.
return specialStage.ufo; return specialstageinfo.ufo;
} }
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -8028,10 +8030,10 @@ void K_KartPlayerAfterThink(player_t *player)
mobj_t *ret = NULL; mobj_t *ret = NULL;
if (specialStage.active == true && lastTargID == MAXPLAYERS) if (specialstageinfo.valid == true && lastTargID == MAXPLAYERS)
{ {
// Aiming at the UFO. // Aiming at the UFO.
lastTarg = specialStage.ufo; lastTarg = specialstageinfo.ufo;
} }
else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS) else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS)
&& playeringame[lastTargID] == true) && playeringame[lastTargID] == true)

View file

@ -16,7 +16,6 @@
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems #include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
#include "p_tick.h" // leveltime #include "p_tick.h" // leveltime
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h"
#include "k_profiles.h" #include "k_profiles.h"
// Client-sided calculations done for Power Levels. // Client-sided calculations done for Power Levels.
@ -38,7 +37,7 @@ SINT8 K_UsingPowerLevels(void)
{ {
SINT8 pt = PWRLV_DISABLED; SINT8 pt = PWRLV_DISABLED;
if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true || bossinfo.boss == true) if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true)
{ {
return PWRLV_DISABLED; return PWRLV_DISABLED;
} }

View file

@ -359,7 +359,7 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers
return 0; return 0;
} }
if (specialStage.active == true) if (specialstageinfo.valid == true)
{ {
UINT32 ufoDis = K_GetSpecialUFODistance(); UINT32 ufoDis = K_GetSpecialUFODistance();
@ -506,7 +506,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table
newOdds = K_KartItemOddsBattle[item-1][pos]; newOdds = K_KartItemOddsBattle[item-1][pos];
} }
else if (specialStage.active == true) else if (specialstageinfo.valid == true)
{ {
I_Assert(pos < 4); // Ditto I_Assert(pos < 4); // Ditto
newOdds = K_KartItemOddsSpecial[item-1][pos]; newOdds = K_KartItemOddsSpecial[item-1][pos];
@ -573,7 +573,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
return 0; return 0;
} }
if (specialStage.active == false) if (specialstageinfo.valid == false)
{ {
if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done
|| position == 1) // No SPB for 1st ever || position == 1) // No SPB for 1st ever
@ -705,7 +705,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
oddsValid[i] = false; oddsValid[i] = false;
continue; continue;
} }
else if (specialStage.active == true && i > 3) else if (specialstageinfo.valid == true && i > 3)
{ {
oddsValid[i] = false; oddsValid[i] = false;
continue; continue;
@ -734,7 +734,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
} }
else else
{ {
if (specialStage.active == true) // Special Stages if (specialstageinfo.valid == true) // Special Stages
{ {
SETUPDISTTABLE(0,2); SETUPDISTTABLE(0,2);
SETUPDISTTABLE(1,2); SETUPDISTTABLE(1,2);
@ -808,7 +808,7 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett
return false; return false;
} }
if (specialStage.active == true) if (specialstageinfo.valid == true)
{ {
return false; return false;
} }
@ -904,7 +904,7 @@ static void K_InitRoulette(itemroulette_t *const roulette)
roulette->exiting++; roulette->exiting++;
} }
if (specialStage.active == true) if (specialstageinfo.valid == true)
{ {
UINT32 dis = K_UndoMapScaling(players[i].distancetofinish); UINT32 dis = K_UndoMapScaling(players[i].distancetofinish);
if (dis < roulette->secondDist) if (dis < roulette->secondDist)
@ -926,7 +926,7 @@ static void K_InitRoulette(itemroulette_t *const roulette)
} }
} }
if (specialStage.active == true) if (specialstageinfo.valid == true)
{ {
roulette->firstDist = K_UndoMapScaling(K_GetSpecialUFODistance()); roulette->firstDist = K_UndoMapScaling(K_GetSpecialUFODistance());
} }
@ -1114,7 +1114,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
// SPECIAL CASE No. 2: // SPECIAL CASE No. 2:
// Use a special, pre-determined item reel for Time Attack / Free Play // Use a special, pre-determined item reel for Time Attack / Free Play
if (bossinfo.boss == true) if (gametyperules & GTR_BOSS)
{ {
for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++) for (i = 0; K_KartItemReelBoss[i] != KITEM_NONE; i++)
{ {

View file

@ -22,7 +22,7 @@
#include "k_waypoint.h" #include "k_waypoint.h"
#include "k_objects.h" #include "k_objects.h"
struct specialStage specialStage; struct specialstageinfo specialstageinfo;
/*-------------------------------------------------- /*--------------------------------------------------
void K_ResetSpecialStage(void) void K_ResetSpecialStage(void)
@ -31,7 +31,8 @@ struct specialStage specialStage;
--------------------------------------------------*/ --------------------------------------------------*/
void K_ResetSpecialStage(void) void K_ResetSpecialStage(void)
{ {
memset(&specialStage, 0, sizeof(struct specialStage)); memset(&specialstageinfo, 0, sizeof(struct specialstageinfo));
specialstageinfo.beamDist = UINT32_MAX;
} }
/*-------------------------------------------------- /*--------------------------------------------------
@ -43,8 +44,15 @@ void K_InitSpecialStage(void)
{ {
INT32 i; INT32 i;
specialStage.beamDist = UINT32_MAX; // TODO: make proper value if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) != (GTR_CATCHER|GTR_CIRCUIT))
P_SetTarget(&specialStage.ufo, Obj_CreateSpecialUFO()); {
return;
}
specialstageinfo.valid = true;
specialstageinfo.beamDist = UINT32_MAX; // TODO: make proper value
P_SetTarget(&specialstageinfo.ufo, Obj_CreateSpecialUFO());
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -88,15 +96,15 @@ static void K_MoveExitBeam(void)
moveDist = (8 * mapobjectscale) / FRACUNIT; moveDist = (8 * mapobjectscale) / FRACUNIT;
if (specialStage.beamDist <= moveDist) if (specialstageinfo.beamDist <= moveDist)
{ {
specialStage.beamDist = 0; specialstageinfo.beamDist = 0;
// TODO: Fail Special Stage // TODO: Fail Special Stage
} }
else else
{ {
specialStage.beamDist -= moveDist; specialstageinfo.beamDist -= moveDist;
} }
// Find players who are now outside of the level. // Find players who are now outside of the level.
@ -118,7 +126,7 @@ static void K_MoveExitBeam(void)
continue; continue;
} }
if (player->distancetofinish > specialStage.beamDist) if (player->distancetofinish > specialstageinfo.beamDist)
{ {
P_DoTimeOver(player); P_DoTimeOver(player);
} }
@ -132,7 +140,7 @@ static void K_MoveExitBeam(void)
--------------------------------------------------*/ --------------------------------------------------*/
void K_TickSpecialStage(void) void K_TickSpecialStage(void)
{ {
if (specialStage.active == false) if (specialstageinfo.valid == false)
{ {
return; return;
} }

View file

@ -16,14 +16,13 @@
#include "doomdef.h" #include "doomdef.h"
#include "doomstat.h" #include "doomstat.h"
extern struct specialStage extern struct specialstageinfo
{ {
boolean active; ///< If true, then we are in a special stage boolean valid; ///< If true, then data in this struct is valid
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
UINT32 beamDist; ///< Where the exit beam is. UINT32 beamDist; ///< Where the exit beam is.
mobj_t *ufo; ///< The Chaos Emerald capsule. mobj_t *ufo; ///< The Chaos Emerald capsule.
} specialStage; } specialstageinfo;
/*-------------------------------------------------- /*--------------------------------------------------
void K_ResetSpecialStage(void); void K_ResetSpecialStage(void);

View file

@ -549,7 +549,7 @@ static void SPBSeek(mobj_t *spb, mobj_t *bestMobj)
SetSPBSpeed(spb, xySpeed, zSpeed); SetSPBSpeed(spb, xySpeed, zSpeed);
if (specialStage.active == false) if (specialstageinfo.valid == false)
{ {
// see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh!
steerDist = 1536 * mapobjectscale; steerDist = 1536 * mapobjectscale;
@ -874,12 +874,12 @@ void Obj_SPBThink(mobj_t *spb)
} }
else else
{ {
if (specialStage.active == true) if (specialstageinfo.valid == true)
{ {
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false) if (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false)
{ {
bestRank = 1; bestRank = 1;
bestMobj = specialStage.ufo; bestMobj = specialstageinfo.ufo;
} }
} }

View file

@ -239,9 +239,9 @@ static void UFOUpdateAngle(mobj_t *ufo)
waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo) waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo)
{ {
if ((ufo == NULL) && (specialStage.active == true)) if ((ufo == NULL) && (specialstageinfo.valid == true))
{ {
ufo = specialStage.ufo; ufo = specialstageinfo.ufo;
} }
if (ufo != NULL && P_MobjWasRemoved(ufo) == false if (ufo != NULL && P_MobjWasRemoved(ufo) == false
@ -820,11 +820,11 @@ mobj_t *Obj_CreateSpecialUFO(void)
UINT32 K_GetSpecialUFODistance(void) UINT32 K_GetSpecialUFODistance(void)
{ {
if (specialStage.active == true) if (specialstageinfo.valid == true)
{ {
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false) if (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false)
{ {
return (UINT32)ufo_distancetofinish(specialStage.ufo); return (UINT32)ufo_distancetofinish(specialstageinfo.ufo);
} }
} }

View file

@ -34,7 +34,6 @@
#include "k_battle.h" #include "k_battle.h"
#include "k_pwrlv.h" #include "k_pwrlv.h"
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h"
#include "k_respawn.h" #include "k_respawn.h"
#include "p_spec.h" #include "p_spec.h"
#include "k_objects.h" #include "k_objects.h"

View file

@ -39,7 +39,6 @@
// SRB2kart // SRB2kart
#include "k_kart.h" #include "k_kart.h"
#include "k_battle.h" #include "k_battle.h"
#include "k_boss.h"
#include "k_color.h" #include "k_color.h"
#include "k_respawn.h" #include "k_respawn.h"
#include "k_bot.h" #include "k_bot.h"
@ -9366,7 +9365,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
{ {
if (gametyperules & GTR_PAPERITEMS) if (gametyperules & GTR_PAPERITEMS)
{ {
if (battlecapsules == true || bossinfo.boss == true) if (battlecapsules == true)
{ {
; ;
} }
@ -12075,7 +12074,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
// No bosses outside of a combat situation. // No bosses outside of a combat situation.
// (just in case we want boss arenas to do double duty as battle maps) // (just in case we want boss arenas to do double duty as battle maps)
if (!bossinfo.boss && (mobjinfo[i].flags & MF_BOSS)) if (!(gametyperules & GTR_BOSS) && (mobjinfo[i].flags & MF_BOSS))
{ {
return false; return false;
} }
@ -12096,7 +12095,7 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i)
if ((i == MT_RING) && (gametyperules & GTR_SPHERES)) if ((i == MT_RING) && (gametyperules & GTR_SPHERES))
return MT_BLUESPHERE; return MT_BLUESPHERE;
if ((i == MT_RANDOMITEM) && (gametyperules & (GTR_PAPERITEMS|GTR_CIRCUIT)) == (GTR_PAPERITEMS|GTR_CIRCUIT) && !bossinfo.boss) if ((i == MT_RANDOMITEM) && (gametyperules & (GTR_PAPERITEMS|GTR_CIRCUIT)) == (GTR_PAPERITEMS|GTR_CIRCUIT))
return MT_PAPERITEMSPOT; return MT_PAPERITEMSPOT;
return i; return i;

View file

@ -6844,7 +6844,7 @@ static void P_InitLevelSettings(void)
if (playeringame[i] && !players[i].spectator) if (playeringame[i] && !players[i].spectator)
p++; p++;
if (grandprixinfo.gp == false && bossinfo.boss == false) if (grandprixinfo.gp == false)
players[i].lives = 3; players[i].lives = 3;
G_PlayerReborn(i, true); G_PlayerReborn(i, true);
@ -6854,38 +6854,27 @@ static void P_InitLevelSettings(void)
racecountdown = exitcountdown = exitfadestarted = 0; racecountdown = exitcountdown = exitfadestarted = 0;
curlap = bestlap = 0; // SRB2Kart curlap = bestlap = 0; // SRB2Kart
// SRB2Kart: map load variables // Gamespeed and frantic items
gamespeed = KARTSPEED_EASY;
franticitems = false;
if (grandprixinfo.gp == true) if (grandprixinfo.gp == true)
{ {
if (!(gametyperules & GTR_CIRCUIT)) if (gametyperules & GTR_CIRCUIT)
{
gamespeed = KARTSPEED_EASY;
}
else
{ {
gamespeed = grandprixinfo.gamespeed; gamespeed = grandprixinfo.gamespeed;
} }
franticitems = false;
}
else if (bossinfo.boss)
{
gamespeed = KARTSPEED_EASY;
franticitems = false;
} }
else if (modeattacking) else if (modeattacking)
{ {
if (!(gametyperules & GTR_CIRCUIT)) if (gametyperules & GTR_CIRCUIT)
gamespeed = KARTSPEED_EASY; {
else
gamespeed = KARTSPEED_HARD; gamespeed = KARTSPEED_HARD;
franticitems = false; }
} }
else else
{ {
if (!(gametyperules & GTR_CIRCUIT)) if (gametyperules & GTR_CIRCUIT)
gamespeed = KARTSPEED_EASY;
else
{ {
if (cv_kartspeed.value == KARTSPEED_AUTO) if (cv_kartspeed.value == KARTSPEED_AUTO)
gamespeed = ((speedscramble == -1) ? KARTSPEED_NORMAL : (UINT8)speedscramble); gamespeed = ((speedscramble == -1) ? KARTSPEED_NORMAL : (UINT8)speedscramble);
@ -6900,6 +6889,9 @@ static void P_InitLevelSettings(void)
memset(&battleovertime, 0, sizeof(struct battleovertime)); memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1; speedscramble = encorescramble = -1;
K_ResetSpecialStage();
K_ResetBossInfo();
} }
#if 0 #if 0
@ -7611,19 +7603,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
K_UpdateMatchRaceBots(); K_UpdateMatchRaceBots();
} }
if (bossinfo.boss)
{
// Reset some pesky boss state that can't be handled elsewhere.
bossinfo.barlen = BOSSHEALTHBARLEN;
bossinfo.visualbar = 0;
Z_Free(bossinfo.enemyname);
Z_Free(bossinfo.subtitle);
bossinfo.enemyname = bossinfo.subtitle = NULL;
bossinfo.titleshow = 0;
bossinfo.titlesound = sfx_typri1;
memset(&(bossinfo.weakspots), 0, sizeof(weakspot_t)*NUMWEAKSPOTS);
}
if (!fromnetsave) // uglier hack if (!fromnetsave) // uglier hack
{ // to make a newly loaded level start on the second frame. { // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS; INT32 buf = gametic % BACKUPTICS;

View file

@ -646,7 +646,7 @@ void P_Ticker(boolean run)
P_PlayerAfterThink(&players[i]); P_PlayerAfterThink(&players[i]);
// Bosses have a punchy start, so no position. // Bosses have a punchy start, so no position.
if (bossinfo.boss == true) if (K_CheckBossIntro() == true)
{ {
if (leveltime == 3) if (leveltime == 3)
{ {

View file

@ -828,7 +828,8 @@ void P_RestoreMusic(player_t *player)
return; return;
// Event - Level Start // Event - Level Start
if (bossinfo.boss == false && (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled if ((K_CheckBossIntro() == false)
&& (leveltime < (starttime + (TICRATE/2)))) // see also where time overs are handled
return; return;
{ {
@ -3612,7 +3613,7 @@ void P_DoTimeOver(player_t *player)
legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p
} }
if (netgame && !player->bot && !bossinfo.boss) if (netgame && !player->bot && !(gametyperules & GTR_BOSS))
{ {
CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players])); CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players]));
} }

View file

@ -30,7 +30,6 @@
#include "m_misc.h" // for tunes command #include "m_misc.h" // for tunes command
#include "m_cond.h" // for conditionsets #include "m_cond.h" // for conditionsets
#include "lua_hook.h" // MusicChange hook #include "lua_hook.h" // MusicChange hook
#include "k_boss.h" // bossinfo
#include "byteptr.h" #include "byteptr.h"
#ifdef HW3SOUND #ifdef HW3SOUND

View file

@ -597,7 +597,7 @@ void ST_runTitleCard(void)
// SRB2KART // SRB2KART
// side Zig-Zag positions... // side Zig-Zag positions...
if (bossinfo.boss == true) if (K_CheckBossIntro() == true)
{ {
// Handle name info... // Handle name info...
if (bossinfo.enemyname) if (bossinfo.enemyname)
@ -792,7 +792,7 @@ void ST_drawTitleCard(void)
if (lt_ticker < TTANIMSTART) if (lt_ticker < TTANIMSTART)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
if (bossinfo.boss == true) if (K_CheckBossIntro() == true)
{ {
// WARNING! // WARNING!
// https://twitter.com/matthewseiji/status/1485003284196716544 // https://twitter.com/matthewseiji/status/1485003284196716544

View file

@ -589,7 +589,7 @@ void V_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du
{ {
const tic_t length = TICRATE/4; const tic_t length = TICRATE/4;
tic_t timer = lt_exitticker; tic_t timer = lt_exitticker;
if (bossinfo.boss == true) if (K_CheckBossIntro() == true)
{ {
if (leveltime <= 3) if (leveltime <= 3)
timer = 0; timer = 0;

View file

@ -209,7 +209,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
else else
{ {
// set up the levelstring // set up the levelstring
if (bossinfo.boss == true && bossinfo.enemyname) if (bossinfo.valid == true && bossinfo.enemyname)
{ {
snprintf(data.levelstring, snprintf(data.levelstring,
sizeof data.levelstring, sizeof data.levelstring,
@ -574,7 +574,7 @@ skiptallydrawer:
if (!LUA_HudEnabled(hud_intermissionmessages)) if (!LUA_HudEnabled(hud_intermissionmessages))
return; return;
if (timer && grandprixinfo.gp == false && bossinfo.boss == false && !modeattacking) if (timer && grandprixinfo.gp == false && !modeattacking)
{ {
char *string; char *string;
INT32 tickdown = (timer+1)/TICRATE; INT32 tickdown = (timer+1)/TICRATE;