From 2939f63418fe14c752bb9de0e1d4b8f03d5831f3 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 14 Oct 2022 18:34:34 +0100 Subject: [PATCH] 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. --- src/d_netcmd.c | 4 +++ src/g_game.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++--- src/k_battle.c | 2 +- src/k_hud.c | 10 +++++- src/p_enemy.c | 7 ++-- src/p_inter.c | 64 +++++++++++++++++++++++++++++------ src/p_setup.c | 16 --------- 7 files changed, 161 insertions(+), 34 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9740bdff0..88b49cd74 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2891,6 +2891,10 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r { pencoremode = bossinfo.encore; } + else if (specialStage.active == true) + { + pencoremode = specialStage.encore; + } else if (grandprixinfo.gp == true) { pencoremode = grandprixinfo.encore; diff --git a/src/g_game.c b/src/g_game.c index 2559cb707..72ad99903 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -59,6 +59,7 @@ #include "k_color.h" #include "k_grandprix.h" #include "k_boss.h" +#include "k_specialstage.h" #include "k_bot.h" #include "k_odds.h" #include "doomstat.h" @@ -2837,7 +2838,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) starposttime = 0; prevcheck = 0; nextcheck = 0; - xtralife = 0; for (i = 0; i < LAP__MAX; i++) { @@ -4397,20 +4397,104 @@ static INT16 G_GetNextMap(boolean advancemap) } else if (grandprixinfo.gp == true) { - if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session + // G: oh dear, this whole GP block has loads of side effects... + // for now, just repeat the same map for "map +" + // you're not supposed to use that in GP anyways + if (!advancemap || // ...right? + grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session { newmap = curmap; // Same map } 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; + newmap = 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; + newmap = cupLevelNum; + } + } + } + + if (grandprixinfo.eventmode != GPEVENT_NONE) + { + // nextmap is set above + const INT32 newtol = mapheaderinfo[newmap]->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 { newmap = NEXTMAP_CEREMONY; // ceremonymap } else { // Proceed to next map - const INT32 cupLevelNum =grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum]; + const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum]; if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]) { diff --git a/src/k_battle.c b/src/k_battle.c index f0a8a4e07..c95455925 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -493,7 +493,7 @@ void K_BattleInit(UINT8 numPlayers) { if (modeattacking != ATTACKING_ITEMBREAK) { - if (!cv_kartitembreaker.value) + if (K_CanChangeRules() && !cv_kartitembreaker.value) goto afteritembreaker; if (numPlayers > 1) diff --git a/src/k_hud.c b/src/k_hud.c index ab7456dd1..6ab76b78c 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1642,7 +1642,15 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI else { 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; if (drawtime & 2) diff --git a/src/p_enemy.c b/src/p_enemy.c index 36ce44d69..5f83e1748 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3284,9 +3284,12 @@ void A_AttractChase(mobj_t *actor) // Set attraction flag actor->cusval = 1; - if ((actor->tracer->player->itemtype == KITEM_THUNDERSHIELD + if ( + actor->tracer->player && actor->tracer->health + && ((gametyperules & GTR_BUMPERS) + || (actor->tracer->player->itemtype == KITEM_THUNDERSHIELD && RINGTOTAL(actor->tracer->player) < actor->tracer->player->ringmax - && !(actor->tracer->player->pflags & PF_RINGLOCK)) + && !(actor->tracer->player->pflags & PF_RINGLOCK))) //&& P_CheckSight(actor, actor->tracer) ) { diff --git a/src/p_inter.c b/src/p_inter.c index 3663e4449..30d535eb8 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1004,17 +1004,37 @@ void P_CheckTimeLimit(void) { INT32 i, k; + if (exitcountdown) + return; + if (!timelimitintics) return; - if (!(multiplayer || netgame)) - return; - - if (!(gametyperules & GTR_TIMELIMIT)) - return; - - if (itembreaker) - return; + if (secretextratime) + { + secretextratime--; + timelimitintics++; + } + else if (extratimeintics) + { + timelimitintics++; + if (leveltime & 1) + ; + else + { + if (extratimeintics > 20) + { + extratimeintics -= 20; + timelimitintics += 20; + } + else + { + timelimitintics += extratimeintics; + extratimeintics = 0; + } + S_StartSound(NULL, sfx_ptally); + } + } if (leveltime < (timelimitintics + starttime)) return; @@ -1408,15 +1428,39 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (itembreaker) { + // G: uncommented this for funsies + mobj_t * ring; + angle_t dir = 0; + if (inflictor) + dir = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y); + else + dir = R_PointToAngle2(source->x, source->y, target->x, target->y); + for (UINT8 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++; + } + target->flags2 |= MF2_BOSSFLEE; target->flags2 |= MF2_DONTRESPAWN; K_SpawnBattlePoints(source->player, NULL, 1); + // All targets busted! if (++numtargets >= nummapboxes) { - P_DoAllPlayersExit(0, true); + P_DoAllPlayersExit(0, (grandprixinfo.gp == true)); + } + else if (timelimitintics) + { + S_StartSound(NULL, sfx_s221); + extratimeintics += 10*TICRATE; + secretextratime = TICRATE/2; } - } if (cv_itemrespawn.value && modeattacking == ATTACKING_NONE && !itembreaker) diff --git a/src/p_setup.c b/src/p_setup.c index d1ebd6ae9..e55e9a6b0 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8390,22 +8390,6 @@ static void P_InitGametype(void) numlaps = K_RaceLapCount(gamemap - 1); - if ((gametyperules & GTR_TIMELIMIT) && !bossinfo.boss) - { - if (K_CanChangeRules() == false) - { - timelimitintics = timelimits[gametype] * (60*TICRATE); - } - else - { - timelimitintics = cv_timelimit.value * (60*TICRATE); - } - } - else - { - timelimitintics = 0; - } - wantedcalcdelay = wantedfrequency*2; indirectitemcooldown = 0;