"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_bot.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "doomstat.h"
#include "s_sound.h" // sfx_syfail
#include "m_cond.h" // netUnlocked

View file

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

View file

@ -57,7 +57,6 @@
#include "k_color.h"
#include "k_respawn.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_follower.h"
#include "doomstat.h"
#include "deh_tables.h"
@ -2525,15 +2524,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
FLS = false;
// 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)
if (grandprixinfo.gp == true)
{
pencoremode = grandprixinfo.encore;
}
@ -2843,7 +2834,15 @@ static void Command_Map_f(void)
if (mapheaderinfo[newmapnum-1])
{
// 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)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
@ -2875,7 +2876,7 @@ static void Command_Map_f(void)
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(mapname);
return;
@ -2940,35 +2941,18 @@ static void Command_Map_f(void)
grandprixinfo.eventmode = GPEVENT_NONE;
if (newgametype == GT_BATTLE)
if (gametypes[newgametype]->rules & (GTR_BOSS|GTR_CATCHER))
{
grandprixinfo.eventmode = GPEVENT_SPECIAL;
}
else if (newgametype != GT_RACE)
{
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 (!Playing())
{
if (mapheaderinfo[newmapnum-1] &&
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
{
specialStage.active = true;
specialStage.encore = newencoremode;
grandprixinfo.eventmode = GPEVENT_SPECIAL;
}
else
{
specialStage.active = false;
}
multiplayer = true;
}
}
@ -3022,7 +3006,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!(gametyperules & GTR_ENCORE) && !bossinfo.boss)
if (!(gametyperules & GTR_ENCORE))
pencoremode = false;
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"));
}
else if (grandprixinfo.gp == false && bossinfo.boss == false)
else if (grandprixinfo.gp == false)
{
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"));
}

View file

@ -455,6 +455,8 @@ enum GameType
{
GT_RACE = 0,
GT_BATTLE,
GT_SPECIAL,
GT_VERSUS,
GT_FIRSTFREESLOT,
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
// 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_ENCORE = 1<<22, // Alternate Encore mirroring, scripting, and texture remapping
// free: to and including 1<<31
};
// Remember to update GAMETYPERULE_LIST in deh_soc.c
// TODO: replace every instance
#define gametyperules (gametypes[gametype]->rules)
@ -531,6 +537,7 @@ enum TypeOfLevel
// Modifiers
TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD
};
// Make sure to update TYPEOFLEVEL too
#define MAXTOL (1<<31)
#define NUMBASETOLNAMES (5)

View file

@ -1425,9 +1425,9 @@ void G_StartTitleCard(void)
// play the sound
{
sfxenum_t kstart = sfx_kstart;
if (bossinfo.boss)
if (K_CheckBossIntro() == true)
kstart = sfx_ssa021;
else if (encoremode)
else if (encoremode == true)
kstart = sfx_ruby2;
S_StartSound(NULL, kstart);
}
@ -2790,22 +2790,9 @@ mapthing_t *G_FindMapStart(INT32 playernum)
if (!playeringame[playernum])
return NULL;
// -- Spectators --
// 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 --
// -- Time Attack --
// Order: Race->DM->CTF
else if (grandprixinfo.gp || modeattacking)
if (K_TimeAttackRules() == true)
spawnpoint = G_FindRaceStartOrFallback(playernum);
// -- CTF --
@ -2911,7 +2898,7 @@ void G_ExitLevel(void)
{
UINT8 i;
boolean youlost = false;
if (bossinfo.boss == true)
if (gametyperules & GTR_BOSS)
{
youlost = true;
for (i = 0; i < MAXPLAYERS; i++)
@ -3021,12 +3008,35 @@ static gametype_t defaultgametypes[] =
0,
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] =
{
&defaultgametypes[GT_RACE],
&defaultgametypes[GT_BATTLE],
&defaultgametypes[GT_SPECIAL],
&defaultgametypes[GT_VERSUS],
};
//
@ -3048,6 +3058,25 @@ INT32 G_GetGametypeByName(const char *gametypestr)
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
//
@ -3651,7 +3680,7 @@ static void G_GetNextMap(void)
}
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)
UINT8 bonusmodulo = (grandprixinfo.cup->numlevels+1)/(grandprixinfo.cup->numbonus+1);
UINT8 bonusindex = (grandprixinfo.roundnum / bonusmodulo) - 1;
@ -3668,9 +3697,6 @@ static void G_GetNextMap(void)
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)
@ -3691,11 +3717,11 @@ static void G_GetNextMap(void)
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))
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
grandprixinfo.eventmode = GPEVENT_SPECIAL;
nextmap = cupLevelNum;
newgametype = G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel);
}
}
}
@ -3706,37 +3732,27 @@ static void G_GetNextMap(void)
// todo any other condition?
{
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS + bonusindex];
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_BOSS|TOL_BATTLE))
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
grandprixinfo.eventmode = GPEVENT_BONUS;
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)
{
// 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;
}
}
G_SetGametype(newgametype);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype);
}
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
{
UINT32 tolflag = G_TOLFlag(gametype);

View file

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

View file

@ -55,7 +55,6 @@
// SRB2Kart
#include "s_sound.h" // song credits
#include "k_kart.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_hud.h"
#include "r_fps.h"
@ -2409,7 +2408,7 @@ static void HU_DrawRankings(void)
else if (gametype >= 0 && gametype < numgametypes)
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)
{

View file

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

View file

@ -23,7 +23,7 @@
struct bossinfo bossinfo;
/*--------------------------------------------------
void K_ClearBossInfo(void)
void K_ResetBossInfo(void)
See header file for description.
--------------------------------------------------*/
@ -32,6 +32,8 @@ void K_ResetBossInfo(void)
Z_Free(bossinfo.enemyname);
Z_Free(bossinfo.subtitle);
memset(&bossinfo, 0, sizeof(struct bossinfo));
bossinfo.barlen = BOSSHEALTHBARLEN;
bossinfo.titlesound = sfx_typri1;
}
/*--------------------------------------------------
@ -43,7 +45,7 @@ void K_BossInfoTicker(void)
{
UINT8 i;
if (bossinfo.boss == false)
if (bossinfo.valid == false)
return;
// Update healthbar data. (only if the hud is visible)
@ -55,7 +57,7 @@ void K_BossInfoTicker(void)
bossinfo.visualbar--;
// If the boss is dying, start shrinking the healthbar.
if (bossinfo.visualbar == 0)
bossinfo.barlen-= 2;
bossinfo.barlen -= 2;
}
// Less than the actual health?
else if (bossinfo.visualbar < bossinfo.healthbar)
@ -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)
{
if (!(gametyperules & GTR_BOSS))
{
return;
}
bossinfo.valid = true;
if (!leveltime)
{
bossinfo.coolintro = true;
}
if (enemyname && enemyname[0])
{
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)
{
if (bossinfo.valid == false)
return;
if (magnitude > FRACUNIT)
magnitude = FRACUNIT;
else if (magnitude < 0)
@ -177,6 +194,9 @@ void K_DeclareWeakspot(mobj_t *spot, spottype_t spottype, UINT16 color, boolean
{
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).
for (i = 0; i < NUMWEAKSPOTS; i++)
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;
}
}
/*--------------------------------------------------
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
{
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 visualbar; ///< Tracks above, but with delay
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)
char *enemyname; ///< The name next to the bar
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
tic_t titleshow; ///< Show this many letters on the titlecard
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);
/*--------------------------------------------------
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

View file

@ -27,7 +27,6 @@
#include "m_random.h"
#include "r_things.h" // numskins
#include "k_race.h" // finishBeamLine
#include "k_boss.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;
}

View file

@ -11,7 +11,6 @@
/// \brief Grand Prix mode game logic & bot behaviors
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "doomdef.h"
#include "d_player.h"
@ -529,7 +528,7 @@ void K_RetireBots(void)
UINT8 i;
if (grandprixinfo.gp == true
&& ((grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
&& ((grandprixinfo.cup != NULL && grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
|| grandprixinfo.eventmode != GPEVENT_NONE))
{
// No replacement.
@ -703,24 +702,12 @@ void K_PlayerLoseLife(player_t *player)
--------------------------------------------------*/
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!
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)
{
// Don't cheat the endurance challenge!

View file

@ -3360,7 +3360,7 @@ static void K_drawKartNameTags(void)
c.z = viewz;
// 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];
UINT8 numdraw = 0;
@ -3923,7 +3923,7 @@ static void K_drawKartMinimap(void)
// Target reticule
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);
}
@ -3981,7 +3981,7 @@ static void K_drawKartMinimap(void)
}
// ...but first, any boss targets.
if (bossinfo.boss)
if (bossinfo.valid == true)
{
for (i = 0; i < NUMWEAKSPOTS; i++)
{
@ -4050,7 +4050,7 @@ static void K_drawKartMinimap(void)
// Target reticule
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);
}
@ -4838,7 +4838,7 @@ void K_drawKartFreePlay(void)
if (!LUA_HudEnabled(hud_freeplay))
return;
if (modeattacking || grandprixinfo.gp || bossinfo.boss || stplyr->spectator)
if (modeattacking || grandprixinfo.gp || bossinfo.valid || stplyr->spectator)
return;
if (lt_exitticker < TICRATE/2)
@ -5098,7 +5098,7 @@ void K_drawKartHUD(void)
{
if (LUA_HudEnabled(hud_position))
{
if (bossinfo.boss)
if (bossinfo.valid)
{
K_drawBossHealthBar();
}
@ -5140,7 +5140,7 @@ void K_drawKartHUD(void)
K_drawBlueSphereMeter();
}
if (modeattacking && !bossinfo.boss)
if (modeattacking && !bossinfo.valid)
{
// Draw the input UI
if (LUA_HudEnabled(hud_position))

View file

@ -6,7 +6,6 @@
#include "k_kart.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_pwrlv.h"
#include "k_color.h"
#include "k_respawn.h"
@ -41,6 +40,7 @@
#include "k_follower.h"
#include "k_objects.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_specialstage.h"
#include "k_roulette.h"
@ -109,11 +109,13 @@ void K_TimerInit(void)
boolean domodeattack = ((modeattacking != ATTACKING_NONE)
|| (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE));
if (specialStage.active == true)
if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT))
{
K_InitSpecialStage();
}
else if (bossinfo.boss == false)
else if (K_CheckBossIntro() == true)
;
else
{
if (!domodeattack)
{
@ -152,7 +154,7 @@ void K_TimerInit(void)
K_BattleInit(domodeattack);
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss && !modeattacking)
if ((gametyperules & GTR_TIMELIMIT) && !modeattacking)
{
if (!K_CanChangeRules(true))
{
@ -343,7 +345,7 @@ boolean K_IsPlayerLosing(player_t *player)
if (battlecapsules && numtargets == 0)
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
if (player->position == 1)
@ -513,7 +515,7 @@ boolean K_TimeAttackRules(void)
UINT8 playing = 0;
UINT8 i;
if (specialStage.active == true)
if ((gametyperules & (GTR_CATCHER|GTR_CIRCUIT)) == (GTR_CATCHER|GTR_CIRCUIT))
{
// Kind of a hack -- Special Stages
// 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)
{
const boolean addUfo = ((specialStage.active == true)
&& (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false));
const boolean addUfo = ((specialstageinfo.valid == true)
&& (specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false));
fixed_t topspd = K_GetKartSpeed(player, false, false);
fixed_t draftdistance;
@ -1345,7 +1347,7 @@ static void K_UpdateDraft(player_t *player)
if (addUfo == true)
{
// 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.
}
@ -1402,8 +1404,8 @@ static void K_UpdateDraft(player_t *player)
else if (addUfo == true)
{
// 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);
K_DrawDraftCombiring(player, specialStage.ufo, dist, draftdistance, true);
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, specialstageinfo.ufo, dist, draftdistance, true);
}
}
else // Remove draft speed boost.
@ -4128,7 +4130,7 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
player->karmadelay = comebacktime;
if (bossinfo.boss)
if (gametyperules & GTR_BOSS)
{
P_DoTimeOver(player);
}
@ -6789,10 +6791,10 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
mobj_t *wtarg = NULL;
INT32 i;
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
// Always target the UFO.
return specialStage.ufo;
return specialstageinfo.ufo;
}
for (i = 0; i < MAXPLAYERS; i++)
@ -8028,10 +8030,10 @@ void K_KartPlayerAfterThink(player_t *player)
mobj_t *ret = NULL;
if (specialStage.active == true && lastTargID == MAXPLAYERS)
if (specialstageinfo.valid == true && lastTargID == MAXPLAYERS)
{
// Aiming at the UFO.
lastTarg = specialStage.ufo;
lastTarg = specialstageinfo.ufo;
}
else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS)
&& playeringame[lastTargID] == true)

View file

@ -16,7 +16,6 @@
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
#include "p_tick.h" // leveltime
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_profiles.h"
// Client-sided calculations done for Power Levels.
@ -38,7 +37,7 @@ SINT8 K_UsingPowerLevels(void)
{
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;
}

View file

@ -359,7 +359,7 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers
return 0;
}
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
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
newOdds = K_KartItemOddsBattle[item-1][pos];
}
else if (specialStage.active == true)
else if (specialstageinfo.valid == true)
{
I_Assert(pos < 4); // Ditto
newOdds = K_KartItemOddsSpecial[item-1][pos];
@ -573,7 +573,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
return 0;
}
if (specialStage.active == false)
if (specialstageinfo.valid == false)
{
if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done
|| 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;
continue;
}
else if (specialStage.active == true && i > 3)
else if (specialstageinfo.valid == true && i > 3)
{
oddsValid[i] = false;
continue;
@ -734,7 +734,7 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
}
else
{
if (specialStage.active == true) // Special Stages
if (specialstageinfo.valid == true) // Special Stages
{
SETUPDISTTABLE(0,2);
SETUPDISTTABLE(1,2);
@ -808,7 +808,7 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett
return false;
}
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
return false;
}
@ -904,7 +904,7 @@ static void K_InitRoulette(itemroulette_t *const roulette)
roulette->exiting++;
}
if (specialStage.active == true)
if (specialstageinfo.valid == true)
{
UINT32 dis = K_UndoMapScaling(players[i].distancetofinish);
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());
}
@ -1114,7 +1114,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
// SPECIAL CASE No. 2:
// 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++)
{

View file

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

View file

@ -16,14 +16,13 @@
#include "doomdef.h"
#include "doomstat.h"
extern struct specialStage
extern struct specialstageinfo
{
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
boolean valid; ///< If true, then data in this struct is valid
UINT32 beamDist; ///< Where the exit beam is.
mobj_t *ufo; ///< The Chaos Emerald capsule.
} specialStage;
} specialstageinfo;
/*--------------------------------------------------
void K_ResetSpecialStage(void);

View file

@ -549,7 +549,7 @@ static void SPBSeek(mobj_t *spb, mobj_t *bestMobj)
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!
steerDist = 1536 * mapobjectscale;
@ -874,12 +874,12 @@ void Obj_SPBThink(mobj_t *spb)
}
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;
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)
{
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
@ -820,11 +820,11 @@ mobj_t *Obj_CreateSpecialUFO(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_pwrlv.h"
#include "k_grandprix.h"
#include "k_boss.h"
#include "k_respawn.h"
#include "p_spec.h"
#include "k_objects.h"

View file

@ -39,7 +39,6 @@
// SRB2kart
#include "k_kart.h"
#include "k_battle.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_respawn.h"
#include "k_bot.h"
@ -9366,7 +9365,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
{
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.
// (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;
}
@ -12096,7 +12095,7 @@ static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i)
if ((i == MT_RING) && (gametyperules & GTR_SPHERES))
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 i;

View file

@ -6844,7 +6844,7 @@ static void P_InitLevelSettings(void)
if (playeringame[i] && !players[i].spectator)
p++;
if (grandprixinfo.gp == false && bossinfo.boss == false)
if (grandprixinfo.gp == false)
players[i].lives = 3;
G_PlayerReborn(i, true);
@ -6854,38 +6854,27 @@ static void P_InitLevelSettings(void)
racecountdown = exitcountdown = exitfadestarted = 0;
curlap = bestlap = 0; // SRB2Kart
// SRB2Kart: map load variables
// Gamespeed and frantic items
gamespeed = KARTSPEED_EASY;
franticitems = false;
if (grandprixinfo.gp == true)
{
if (!(gametyperules & GTR_CIRCUIT))
{
gamespeed = KARTSPEED_EASY;
}
else
if (gametyperules & GTR_CIRCUIT)
{
gamespeed = grandprixinfo.gamespeed;
}
franticitems = false;
}
else if (bossinfo.boss)
{
gamespeed = KARTSPEED_EASY;
franticitems = false;
}
else if (modeattacking)
{
if (!(gametyperules & GTR_CIRCUIT))
gamespeed = KARTSPEED_EASY;
else
if (gametyperules & GTR_CIRCUIT)
{
gamespeed = KARTSPEED_HARD;
franticitems = false;
}
}
else
{
if (!(gametyperules & GTR_CIRCUIT))
gamespeed = KARTSPEED_EASY;
else
if (gametyperules & GTR_CIRCUIT)
{
if (cv_kartspeed.value == KARTSPEED_AUTO)
gamespeed = ((speedscramble == -1) ? KARTSPEED_NORMAL : (UINT8)speedscramble);
@ -6900,6 +6889,9 @@ static void P_InitLevelSettings(void)
memset(&battleovertime, 0, sizeof(struct battleovertime));
speedscramble = encorescramble = -1;
K_ResetSpecialStage();
K_ResetBossInfo();
}
#if 0
@ -7611,19 +7603,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
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
{ // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS;

View file

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

View file

@ -828,7 +828,8 @@ void P_RestoreMusic(player_t *player)
return;
// 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;
{
@ -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
}
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]));
}

View file

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

View file

@ -597,7 +597,7 @@ void ST_runTitleCard(void)
// SRB2KART
// side Zig-Zag positions...
if (bossinfo.boss == true)
if (K_CheckBossIntro() == true)
{
// Handle name info...
if (bossinfo.enemyname)
@ -792,7 +792,7 @@ void ST_drawTitleCard(void)
if (lt_ticker < TTANIMSTART)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
if (bossinfo.boss == true)
if (K_CheckBossIntro() == true)
{
// WARNING!
// 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;
tic_t timer = lt_exitticker;
if (bossinfo.boss == true)
if (K_CheckBossIntro() == true)
{
if (leveltime <= 3)
timer = 0;

View file

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