Support alternate gameplay events during GP

- Implementation details:
    - grandprixinfo.eventmode is the reference point
    - All bots have spectator applied and removed at map start depending on eventmode, and I've done my best to guard against side effects of not removing them entirely
    - You shouldn't turn off grandprixinfo.gp when turning on things like specialStage.active or bossinfo.boss when pursuing eventmode behaviour
    - Probably needs to be integrated into XD_MAP for any future netplay support, is currently disabled.
    - You technically don't have to assign a Capsules map to be the bonus and a Special Stage to be the special. A Capsules map can be assigned to a Special Stage too, and a Boss can be assigned to either of them.
    - Special Stages are still just as incomplete as they were before.
- Break the Capsules has special behaviour.
   - Timelimit starts at 20 seconds.
   - Earn 10 seconds (plus a little extra cheaty time) every capsule you destroy.
   - WIN + extra life if you bust all the capsules, COOL if you get some but run out of time, LOSE if you lose your bumper or run out of time without breaking a single capsule.
   - Supposed to also give you rings, but ran into a LOT of difficulty with this and didn't want to commit half-baked stuff, so it'll be a later project.
Also:
- Fix a long standing bug where totalring was reset between maps, preventing the sum from adding up across GP rounds and depriving you of extra lives you were owed.
- Fix an issue where Break the Capsules record attack was KARTSPEED_HARD.
- Send timelimitintics in savegames, since it's handled seperately now.
This commit is contained in:
toaster 2022-10-14 18:34:34 +01:00
parent f926842b30
commit f281f47c6f
16 changed files with 398 additions and 177 deletions

View file

@ -2572,15 +2572,19 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypedefaultrules[newgametype] & GTR_CAMPAIGN))) if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypedefaultrules[newgametype] & GTR_CAMPAIGN)))
FLS = false; FLS = false;
if (grandprixinfo.gp == true) // Too lazy to change the input value for every instance of this function.......
{ if (bossinfo.boss == true)
// Too lazy to change the input value for every instance of this function.......
pencoremode = grandprixinfo.encore;
}
else if (bossinfo.boss == true)
{ {
pencoremode = bossinfo.encore; pencoremode = bossinfo.encore;
} }
else if (specialStage.active == true)
{
pencoremode = specialStage.encore;
}
else if (grandprixinfo.gp == true)
{
pencoremode = grandprixinfo.encore;
}
if (delay != 2) if (delay != 2)
{ {
@ -4946,6 +4950,8 @@ Lagless_OnChange (void)
} }
UINT32 timelimitintics = 0; UINT32 timelimitintics = 0;
UINT32 extratimeintics = 0;
UINT32 secretextratime = 0;
/** Deals with a timelimit change by printing the change to the console. /** Deals with a timelimit change by printing the change to the console.
* If the gametype is single player, cooperative, or race, the timelimit is * If the gametype is single player, cooperative, or race, the timelimit is
@ -5752,6 +5758,10 @@ void Command_Retry_f(void)
{ {
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)
{
CONS_Printf(M_GetText("You can't retry right now!\n"));
}
else else
{ {
M_ClearMenus(true); M_ClearMenus(true);

View file

@ -55,7 +55,7 @@ extern consvar_t cv_itemrespawn;
extern consvar_t cv_pointlimit; extern consvar_t cv_pointlimit;
extern consvar_t cv_timelimit; extern consvar_t cv_timelimit;
extern consvar_t cv_numlaps; extern consvar_t cv_numlaps;
extern UINT32 timelimitintics; extern UINT32 timelimitintics, extratimeintics, secretextratime;
extern consvar_t cv_allowexitlevel; extern consvar_t cv_allowexitlevel;
extern consvar_t cv_autobalance; extern consvar_t cv_autobalance;

View file

@ -58,6 +58,7 @@
#include "k_respawn.h" #include "k_respawn.h"
#include "k_grandprix.h" #include "k_grandprix.h"
#include "k_boss.h" #include "k_boss.h"
#include "k_specialstage.h"
#include "k_bot.h" #include "k_bot.h"
#include "doomstat.h" #include "doomstat.h"
@ -2297,6 +2298,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
botdiffincrease = players[player].botvars.diffincrease; botdiffincrease = players[player].botvars.diffincrease;
botrival = players[player].botvars.rival; 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)); pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE));
// SRB2kart // SRB2kart
@ -2315,13 +2319,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
nocontrol = 0; nocontrol = 0;
laps = 0; laps = 0;
latestlap = 0; latestlap = 0;
totalring = 0;
roundscore = 0; roundscore = 0;
exiting = 0; exiting = 0;
khudfinish = 0; khudfinish = 0;
khudcardanimation = 0; khudcardanimation = 0;
starpostnum = 0; starpostnum = 0;
xtralife = 0;
follower = NULL; follower = NULL;
} }
@ -2358,7 +2360,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
laps = players[player].laps; laps = players[player].laps;
latestlap = players[player].latestlap; latestlap = players[player].latestlap;
totalring = players[player].totalring;
roundscore = players[player].roundscore; roundscore = players[player].roundscore;
exiting = players[player].exiting; exiting = players[player].exiting;
@ -2375,8 +2376,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
starpostnum = players[player].starpostnum; starpostnum = players[player].starpostnum;
xtralife = players[player].xtralife;
follower = players[player].follower; follower = players[player].follower;
pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_FAULT|PF_LOSTLIFE)); pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_FAULT|PF_LOSTLIFE));
@ -2919,7 +2918,7 @@ void G_ExitLevel(void)
} }
} }
} }
else if (grandprixinfo.gp == true) else if (grandprixinfo.gp == true && grandprixinfo.eventmode == GPEVENT_NONE)
{ {
youlost = (grandprixinfo.wonround != true); youlost = (grandprixinfo.wonround != true);
} }
@ -3233,14 +3232,14 @@ boolean G_GametypeUsesLives(void)
if (modeattacking || metalrecording) // NOT in Record Attack if (modeattacking || metalrecording) // NOT in Record Attack
return false; 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; return true;
} }
if ((grandprixinfo.gp == true) // In Grand Prix if (bossinfo.boss == true) // Fighting a boss?
&& (gametype == GT_RACE) // NOT in bonus round
&& !G_IsSpecialStage(gamemap)) // NOT in special stage
{ {
return true; return true;
} }
@ -3723,14 +3722,94 @@ static void G_GetNextMap(void)
} }
else 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 nextmap = NEXTMAP_CEREMONY; // ceremonymap
} }
else else
{ {
// Proceed to next map // 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]) if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{ {

View file

@ -182,7 +182,13 @@ void K_CheckBumpers(void)
K_KartUpdatePosition(&players[i]); 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... 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]); P_DoPlayerExit(&players[i]);
}
} }
void K_CheckEmeralds(player_t *player) void K_CheckEmeralds(player_t *player)

View file

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

View file

@ -332,6 +332,16 @@ void K_UpdateGrandPrixBots(void)
UINT16 newrivalscore = 0; UINT16 newrivalscore = 0;
UINT8 i; 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. // Find the rival.
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -518,9 +528,11 @@ void K_RetireBots(void)
UINT8 i; 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; return;
} }
@ -576,18 +588,13 @@ void K_RetireBots(void)
{ {
player_t *bot = NULL; player_t *bot = NULL;
if (!playeringame[i] || !players[i].bot) if (!playeringame[i] || !players[i].bot || players[i].spectator)
{ {
continue; continue;
} }
bot = &players[i]; bot = &players[i];
if (bot->spectator)
{
continue;
}
if (bot->pflags & PF_NOCONTEST) if (bot->pflags & PF_NOCONTEST)
{ {
UINT8 skinnum = defaultbotskin; UINT8 skinnum = defaultbotskin;

View file

@ -16,6 +16,10 @@
#include "doomdef.h" #include "doomdef.h"
#include "doomstat.h" #include "doomstat.h"
#define GPEVENT_NONE 0
#define GPEVENT_BONUS 1
#define GPEVENT_SPECIAL 2
extern struct grandprixinfo extern struct grandprixinfo
{ {
boolean gp; ///< If true, then we are in a Grand Prix. 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 masterbots; ///< If true, all bots should be max difficulty (Master Mode)
boolean initalize; ///< If true, we need to initialize a new session. 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. boolean wonround; ///< If false, then we retry the map instead of going to the next.
UINT8 eventmode; ///< See GPEVENT_ constants
} grandprixinfo; } grandprixinfo;

View file

@ -12,6 +12,7 @@
#include "k_hud.h" #include "k_hud.h"
#include "k_kart.h" #include "k_kart.h"
#include "k_battle.h" #include "k_battle.h"
#include "k_grandprix.h"
#include "k_boss.h" #include "k_boss.h"
#include "k_color.h" #include "k_color.h"
#include "k_director.h" #include "k_director.h"
@ -1406,7 +1407,15 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI
else else
{ {
drawtime = timelimitintics - drawtime; drawtime = timelimitintics - drawtime;
if (drawtime < 5*TICRATE) if (secretextratime)
;
else if (extratimeintics)
{
jitter = 2;
if (leveltime & 1)
jitter = -jitter;
}
else if (drawtime < 5*TICRATE)
{ {
jitter = 1; jitter = 1;
if (drawtime & 2) if (drawtime & 2)
@ -3904,7 +3913,7 @@ static void K_drawBattleFullscreen(void)
if (K_IsPlayerLosing(stplyr)) if (K_IsPlayerLosing(stplyr))
p = kp_battlelose; p = kp_battlelose;
else if (stplyr->position == 1) else if (stplyr->position == 1 && (!battlecapsules || numtargets >= maptargets))
p = kp_battlewin; p = kp_battlewin;
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, scale, splitflags, p, NULL); V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, scale, splitflags, p, NULL);
@ -3952,7 +3961,7 @@ static void K_drawBattleFullscreen(void)
} }
} }
if (netgame && !stplyr->spectator) // FREE PLAY? // FREE PLAY?
{ {
UINT8 i; UINT8 i;
@ -3961,11 +3970,11 @@ static void K_drawBattleFullscreen(void)
{ {
if (i == displayplayers[0]) if (i == displayplayers[0])
continue; continue;
if (playeringame[i] && !stplyr->spectator) if (playeringame[i] && !players[i].spectator)
return; break;
} }
if (LUA_HudEnabled(hud_freeplay)) if (i != MAXPLAYERS)
K_drawKartFreePlay(); K_drawKartFreePlay();
} }
} }
@ -4359,8 +4368,13 @@ static void K_drawTrickCool(void)
void K_drawKartFreePlay(void) void K_drawKartFreePlay(void)
{ {
// no splitscreen support because it's not FREE PLAY if you have more than one player in-game // Doesn't support splitscreens higher than 2 for real estate reasons.
// (you fool, you can take splitscreen online. :V)
if (!LUA_HudEnabled(hud_freeplay))
return;
if (modeattacking || grandprixinfo.gp || bossinfo.boss || stplyr->spectator)
return;
if (lt_exitticker < TICRATE/2) if (lt_exitticker < TICRATE/2)
return; return;
@ -4369,7 +4383,7 @@ void K_drawKartFreePlay(void)
return; return;
V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - 72, // mirror the laps thingy 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 static void
@ -4733,11 +4747,8 @@ void K_drawKartHUD(void)
V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
// Draw FREE PLAY. // Draw FREE PLAY.
if (islonesome && !modeattacking && !bossinfo.boss && !stplyr->spectator) if (islonesome)
{ K_drawKartFreePlay();
if (LUA_HudEnabled(hud_freeplay))
K_drawKartFreePlay();
}
if (r_splitscreen == 0 && (stplyr->pflags & PF_WRONGWAY) && ((leveltime / 8) & 1)) if (r_splitscreen == 0 && (stplyr->pflags & PF_WRONGWAY) && ((leveltime / 8) & 1))
{ {

View file

@ -40,6 +40,7 @@
#include "k_collide.h" #include "k_collide.h"
#include "k_follower.h" #include "k_follower.h"
#include "k_objects.h" #include "k_objects.h"
#include "k_grandprix.h"
#include "k_specialstage.h" #include "k_specialstage.h"
// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
@ -158,6 +159,29 @@ void K_TimerInit(void)
// NOW you can try to spawn in the Battle capsules, if there's not enough players for a match // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match
K_BattleInit(); K_BattleInit();
timelimitintics = extratimeintics = secretextratime = 0;
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss)
{
if (!K_CanChangeRules())
{
if (grandprixinfo.gp)
{
timelimitintics = (20*TICRATE);
}
else
{
timelimitintics = timelimits[gametype] * (60*TICRATE);
}
}
else
#ifndef TESTOVERTIMEINFREEPLAY
if (!battlecapsules)
#endif
{
timelimitintics = cv_timelimit.value * (60*TICRATE);
}
}
if (inDuel == true) if (inDuel == true)
{ {
K_SpawnDuelOnlyItems(); K_SpawnDuelOnlyItems();
@ -331,10 +355,10 @@ boolean K_IsPlayerLosing(player_t *player)
INT32 winningpos = 1; INT32 winningpos = 1;
UINT8 i, pcount = 0; UINT8 i, pcount = 0;
if (battlecapsules && player->bumpers <= 0) if (battlecapsules && numtargets == 0)
return true; // DNF in break the capsules return true; // Didn't even TRY?
if (bossinfo.boss) if (battlecapsules || bossinfo.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)

View file

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

View file

@ -603,19 +603,37 @@ void P_CheckTimeLimit(void)
{ {
INT32 i; INT32 i;
if (exitcountdown)
return;
if (!timelimitintics) if (!timelimitintics)
return; return;
#ifndef TESTOVERTIMEINFREEPLAY if (secretextratime)
if (battlecapsules && (grandprixinfo.gp == false)) {
return; secretextratime--;
#endif timelimitintics++;
}
if (!(gametyperules & GTR_TIMELIMIT)) else if (extratimeintics)
return; {
timelimitintics++;
if (bossinfo.boss == true) if (leveltime & 1)
return; ;
else
{
if (extratimeintics > 20)
{
extratimeintics -= 20;
timelimitintics += 20;
}
else
{
timelimitintics += extratimeintics;
extratimeintics = 0;
}
S_StartSound(NULL, sfx_ptally);
}
}
if (leveltime < (timelimitintics + starttime)) if (leveltime < (timelimitintics + starttime))
return; return;
@ -705,16 +723,19 @@ void P_CheckPointLimit(void)
{ {
INT32 i; INT32 i;
if (!cv_pointlimit.value) if (exitcountdown)
return; return;
if (!(multiplayer || netgame)) if (!K_CanChangeRules())
return;
if (!cv_pointlimit.value)
return; return;
if (!(gametyperules & GTR_POINTLIMIT)) if (!(gametyperules & GTR_POINTLIMIT))
return; return;
if (bossinfo.boss == true) if (battlecapsules)
return; return;
// pointlimit is nonzero, check if it's been reached by this player // pointlimit is nonzero, check if it's been reached by this player
@ -1422,19 +1443,21 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
case MT_BATTLECAPSULE: case MT_BATTLECAPSULE:
{ {
UINT8 i;
mobj_t *cur; mobj_t *cur;
angle_t dir = 0;
numtargets++;
target->fuse = 16; target->fuse = 16;
target->flags |= MF_NOCLIP|MF_NOCLIPTHING; target->flags |= MF_NOCLIP|MF_NOCLIPTHING;
if (inflictor) if (inflictor)
{ {
P_Thrust(target, dir = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y);
R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y), P_Thrust(target, dir, P_AproxDistance(inflictor->momx, inflictor->momy)/12);
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->momz += 8 * target->scale * P_MobjFlip(target);
target->flags &= ~MF_NOGRAVITY; target->flags &= ~MF_NOGRAVITY;
@ -1467,12 +1490,47 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
S_StartSound(target, sfx_mbs60); S_StartSound(target, sfx_mbs60);
// All targets busted! if ((gametyperules & GTR_POINTLIMIT) && (source && source->player))
if (numtargets >= maptargets)
{ {
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++) for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
P_DoPlayerExit(&players[i]); 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; break;

View file

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

View file

@ -3565,7 +3565,7 @@ static void P_InitLevelSettings(void)
// SRB2Kart: map load variables // SRB2Kart: map load variables
if (grandprixinfo.gp == true) if (grandprixinfo.gp == true)
{ {
if (gametype == GT_BATTLE) if ((gametyperules & GTR_BUMPERS))
{ {
gamespeed = KARTSPEED_EASY; gamespeed = KARTSPEED_EASY;
} }
@ -3584,7 +3584,10 @@ static void P_InitLevelSettings(void)
else if (modeattacking) else if (modeattacking)
{ {
// Just play it safe and set everything // Just play it safe and set everything
gamespeed = KARTSPEED_HARD; if ((gametyperules & GTR_BUMPERS))
gamespeed = KARTSPEED_EASY;
else
gamespeed = KARTSPEED_HARD;
franticitems = false; franticitems = false;
} }
else else
@ -3843,7 +3846,6 @@ static void P_InitPlayers(void)
static void P_InitGametype(void) static void P_InitGametype(void)
{ {
size_t i; size_t i;
boolean canchangerules = K_CanChangeRules();
spectateGriefed = 0; spectateGriefed = 0;
K_CashInPowerLevels(); // Pushes power level changes even if intermission was skipped K_CashInPowerLevels(); // Pushes power level changes even if intermission was skipped
@ -3855,7 +3857,7 @@ static void P_InitGametype(void)
if (gametyperules & GTR_CIRCUIT) if (gametyperules & GTR_CIRCUIT)
{ {
if (canchangerules && cv_numlaps.value if (K_CanChangeRules() && cv_numlaps.value
&& (!(mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) && (!(mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
|| (mapheaderinfo[gamemap - 1]->numlaps > cv_numlaps.value))) || (mapheaderinfo[gamemap - 1]->numlaps > cv_numlaps.value)))
{ {
@ -3875,22 +3877,6 @@ static void P_InitGametype(void)
numlaps = 0; numlaps = 0;
} }
if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss)
{
if (!canchangerules)
{
timelimitintics = timelimits[gametype] * (60*TICRATE);
}
else
{
timelimitintics = cv_timelimit.value * (60*TICRATE);
}
}
else
{
timelimitintics = 0;
}
wantedcalcdelay = wantedfrequency*2; wantedcalcdelay = wantedfrequency*2;
for (i = 0; i < NUMKARTITEMS-1; i++) for (i = 0; i < NUMKARTITEMS-1; i++)

View file

@ -505,7 +505,6 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
num_rings -= (test+20); num_rings -= (test+20);
player->rings += num_rings; 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; return num_rings;
} }
@ -1263,75 +1262,78 @@ void P_DoPlayerExit(player_t *player)
player->exiting = 1; player->exiting = 1;
if ((gametyperules & GTR_CIRCUIT)) // If in Race Mode, allow if (!player->spectator)
{ {
K_KartUpdatePosition(player); if ((gametyperules & GTR_CIRCUIT)) // If in Race Mode, allow
if (cv_kartvoices.value)
{ {
if (P_IsDisplayPlayer(player)) K_KartUpdatePosition(player);
if (cv_kartvoices.value)
{ {
sfxenum_t sfx_id; if (P_IsDisplayPlayer(player))
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 (!K_CanChangeRules() || 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); sfxenum_t sfx_id;
S_StartSound(NULL, sfx_cdfm73); if (losing)
player->xtralife = extra; 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 (!K_CanChangeRules() || 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;
}
} }
} }
} }

View file

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

View file

@ -753,21 +753,26 @@ void Y_DetermineIntermissionType(void)
// set to int_none initially // set to int_none initially
intertype = int_none; intertype = int_none;
if (intermissiontypes[gametype] != int_none) if (gametype == GT_RACE)
intertype = intermissiontypes[gametype];
else if (gametype == GT_RACE)
intertype = int_race; intertype = int_race;
else if (gametype == GT_BATTLE) else if (gametype == GT_BATTLE)
{ {
UINT8 i = 0, nump = 0; if (grandprixinfo.gp == true && bossinfo.boss == false)
for (i = 0; i < MAXPLAYERS; i++) intertype = int_none;
else
{ {
if (!playeringame[i] || players[i].spectator) UINT8 i = 0, nump = 0;
continue; for (i = 0; i < MAXPLAYERS; i++)
nump++; {
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];
} }
// //