From 2c5caf582ba4428d60a354a027e720dde894a5e9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 25 Apr 2024 15:18:47 -0400 Subject: [PATCH 1/9] "TutorialDone" unlockable condition Replace all instances of `MapBeaten RR_SunbeamParadiseSprings` with `TutorialDone`, for the new early exits to work. --- src/deh_soc.c | 3 ++- src/g_game.c | 41 +++++++++++++++++++++++++---------------- src/g_gamedata.cpp | 2 ++ src/g_gamedata.h | 4 +++- src/m_cond.c | 5 +++++ src/m_cond.h | 2 ++ 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 849d9a0ac..b7ccb9187 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2952,7 +2952,8 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) || (++offset && fastcmp(params[0], "CREDITS")) || (++offset && fastcmp(params[0], "REPLAY")) || (++offset && fastcmp(params[0], "CRASH")) - || (++offset && fastcmp(params[0], "TUTORIALSKIP"))) + || (++offset && fastcmp(params[0], "TUTORIALSKIP")) + || (++offset && fastcmp(params[0], "TUTORIALDONE"))) { //PARAMCHECK(1); ty = UC_ADDON + offset; diff --git a/src/g_game.c b/src/g_game.c index df9da2f3a..38c4ff095 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4853,27 +4853,36 @@ void G_EndGame(void) } // Only do evaluation and credits in singleplayer contexts - if (!netgame && grandprixinfo.gp == true) + if (!netgame) { - G_HandleSaveLevel(true); - - if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony + if (gametype == GT_TUTORIAL) { - if (K_StartCeremony() == true) + // Tutorial was finished + gamedata->tutorialdone = true; + } + + if (grandprixinfo.gp == true) + { + G_HandleSaveLevel(true); + + if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony { + if (K_StartCeremony() == true) + { + return; + } + } + if (nextmap == NEXTMAP_CREDITS) // end game with credits + { + F_StartCredits(); + return; + } + if (nextmap == NEXTMAP_EVALUATION) // end game with evaluation + { + F_InitGameEvaluation(); + F_StartGameEvaluation(); return; } - } - if (nextmap == NEXTMAP_CREDITS) // end game with credits - { - F_StartCredits(); - return; - } - if (nextmap == NEXTMAP_EVALUATION) // end game with evaluation - { - F_InitGameEvaluation(); - F_StartGameEvaluation(); - return; } } diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index e080d5c5e..6fcf6e135 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -76,6 +76,7 @@ void srb2::save_ng_gamedata() ng.milestones.finishedtutorialchallenge = gamedata->finishedtutorialchallenge; ng.milestones.enteredtutorialchallenge = gamedata->enteredtutorialchallenge; ng.milestones.sealedswapalerted = gamedata->sealedswapalerted; + ng.milestones.tutorialdone = gamedata->tutorialdone; ng.milestones.gonerlevel = gamedata->gonerlevel; ng.prisons.thisprisoneggpickup = gamedata->thisprisoneggpickup; ng.prisons.prisoneggstothispickup = gamedata->prisoneggstothispickup; @@ -480,6 +481,7 @@ void srb2::load_ng_gamedata() gamedata->finishedtutorialchallenge = js.milestones.finishedtutorialchallenge; gamedata->enteredtutorialchallenge = js.milestones.enteredtutorialchallenge; gamedata->sealedswapalerted = js.milestones.sealedswapalerted; + gamedata->tutorialdone = js.milestones.tutorialdone; gamedata->gonerlevel = js.milestones.gonerlevel; gamedata->thisprisoneggpickup = js.prisons.thisprisoneggpickup; gamedata->prisoneggstothispickup = js.prisons.prisoneggstothispickup; diff --git a/src/g_gamedata.h b/src/g_gamedata.h index 3c21ea5c2..c8899c84f 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -96,6 +96,7 @@ struct GamedataMilestonesJson final bool finishedtutorialchallenge; bool enteredtutorialchallenge; bool sealedswapalerted; + bool tutorialdone; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( GamedataMilestonesJson, @@ -108,7 +109,8 @@ struct GamedataMilestonesJson final majorkeyskipattempted, finishedtutorialchallenge, enteredtutorialchallenge, - sealedswapalerted + sealedswapalerted, + tutorialdone ) }; diff --git a/src/m_cond.c b/src/m_cond.c index a3733f899..8d7c1f3b4 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -676,6 +676,7 @@ void M_ClearStats(void) gamedata->enteredtutorialchallenge = false; gamedata->finishedtutorialchallenge = false; gamedata->sealedswapalerted = false; + gamedata->tutorialdone = false; gamedata->musicstate = GDMUSIC_NONE; gamedata->importprofilewins = false; @@ -1726,6 +1727,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) return false; case UC_TUTORIALSKIP: return (gamedata->finishedtutorialchallenge == true); + case UC_TUTORIALDONE: + return (gamedata->tutorialdone == true); case UC_PASSWORD: return (cn->stringvar == NULL); @@ -2605,6 +2608,8 @@ static const char *M_GetConditionString(condition_t *cn) return NULL; case UC_TUTORIALSKIP: return "successfully skip the Tutorial"; + case UC_TUTORIALDONE: + return "complete the Tutorial"; case UC_PASSWORD: return "enter a secret password"; diff --git a/src/m_cond.h b/src/m_cond.h index 5c2142561..88905fda5 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -67,6 +67,7 @@ typedef enum UC_REPLAY, // Save a replay UC_CRASH, // Hee ho ! UC_TUTORIALSKIP, // Complete the Tutorial Challenge + UC_TUTORIALDONE, // Complete the Tutorial at all UC_PASSWORD, // Type in something funny @@ -393,6 +394,7 @@ struct gamedata_t boolean enteredtutorialchallenge; boolean finishedtutorialchallenge; boolean sealedswapalerted; + boolean tutorialdone; gdmusic_t musicstate; UINT8 gonerlevel; From f5777f1f20105ad66c7ea10a88d627ec43d3688d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 25 Apr 2024 16:28:01 -0400 Subject: [PATCH 2/9] Reduce tutorial challenge difficulty All Lv 6, harder than Robotnik Coaster but not for maniacs like before. --- src/k_bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index cf44c85ae..ad4619dd7 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -257,7 +257,7 @@ void K_UpdateMatchRaceBots(void) else if (tutorialchallenge == TUTORIALSKIP_INPROGRESS) { pmax = 8; // can you believe this is a nerf - difficulty = MAXBOTDIFFICULTY; + difficulty = 6; } else if (K_CanChangeRules(true) == false) { From f160a99ea33b803b54b79f73fc80d7ba0613b588 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 25 Apr 2024 17:04:45 -0400 Subject: [PATCH 3/9] Update unlocks immediately after tutorialdone --- src/g_game.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/g_game.c b/src/g_game.c index 38c4ff095..30e133b45 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4859,6 +4859,9 @@ void G_EndGame(void) { // Tutorial was finished gamedata->tutorialdone = true; + + M_UpdateUnlockablesAndExtraEmblems(true, true); + gamedata->deferredsave = true; } if (grandprixinfo.gp == true) From d366e9bb114964e9d89ee9e8c9407cb854741b6c Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 25 Apr 2024 17:23:27 -0700 Subject: [PATCH 4/9] Tutorial Skip: use profile character, color and follower --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 30e133b45..0218e24aa 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4152,7 +4152,7 @@ void G_GetNextMap(void) // A gamedata save will happen on successful level enter // Also set character, color, and follower from profile - + D_SendPlayerConfig(0); } } From 5561928ad478af5f1002f10b06b5007a99043f52 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 25 Apr 2024 17:45:33 -0700 Subject: [PATCH 5/9] Tutorial Skip: clear bots so Eggman bot does not carry over from Tutorial --- src/k_bot.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index ad4619dd7..48ac1557b 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -319,6 +319,27 @@ void K_UpdateMatchRaceBots(void) } } + auto clear_bots = [&numbots](UINT8 max) + { + UINT8 i = MAXPLAYERS; + while (numbots > max && i > 0) + { + i--; + + if (playeringame[i] && players[i].bot) + { + CL_RemovePlayer(i, KR_LEAVE); + numbots--; + } + } + }; + + if (tutorialchallenge == TUTORIALSKIP_INPROGRESS) + { + // Prevent Eggman bot carrying over from Tutorial + clear_bots(0); + } + if (numbots < wantedbots) { // We require MORE bots! @@ -372,17 +393,7 @@ void K_UpdateMatchRaceBots(void) } else if (numbots > wantedbots) { - i = MAXPLAYERS; - while (numbots > wantedbots && i > 0) - { - i--; - - if (playeringame[i] && players[i].bot) - { - CL_RemovePlayer(i, KR_LEAVE); - numbots--; - } - } + clear_bots(wantedbots); } // We should have enough bots now :) From 4f6870776121a5a0055179a348be14cf8ec8fe1a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 25 Apr 2024 23:56:43 -0400 Subject: [PATCH 6/9] Tutorial Skip is at Gear 2 --- src/p_setup.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 8a9598174..058f4a792 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -7696,6 +7696,10 @@ static void P_InitLevelSettings(void) // We don't touch the gamespeed, though! } + else if (tutorialchallenge == TUTORIALSKIP_INPROGRESS) + { + gamespeed = KARTSPEED_NORMAL; + } else if (grandprixinfo.gp == true) { if (multi_speed) @@ -7703,10 +7707,7 @@ static void P_InitLevelSettings(void) gamespeed = grandprixinfo.gamespeed; } } - else if ( - modeattacking != ATTACKING_NONE - || tutorialchallenge == TUTORIALSKIP_INPROGRESS - ) + else if (modeattacking != ATTACKING_NONE) { if (multi_speed) { From 2b46d49a3c91836e6b384a9562bc7bb56598e4e4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 25 Apr 2024 23:59:49 -0400 Subject: [PATCH 7/9] Nerf tutorial challenge difficulty even harder Screw it, it's not even a challenge anymore lol --- src/k_bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 48ac1557b..9fc0459e0 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -257,7 +257,7 @@ void K_UpdateMatchRaceBots(void) else if (tutorialchallenge == TUTORIALSKIP_INPROGRESS) { pmax = 8; // can you believe this is a nerf - difficulty = 6; + difficulty = 4; } else if (K_CanChangeRules(true) == false) { From a9ac7e64c600b0e88cde91bf822f6e748c8098ab Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 26 Apr 2024 14:59:48 +0100 Subject: [PATCH 8/9] Permit entering the tutorial challenge multiple times per savedata --- src/acs/call-funcs.cpp | 8 -------- src/g_game.c | 5 ----- 2 files changed, 13 deletions(-) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 662f7f1f2..196c31d01 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -1928,14 +1928,6 @@ bool CallFunc_CheckTutorialChallenge(ACSVM::Thread *thread, const ACSVM::Word *a thread->dataStk.push(~env->getString( "Failed" )->idx); return false; } - - if (gamedata != nullptr - && gamedata->enteredtutorialchallenge == true - && M_GameTrulyStarted() == false) - { - thread->dataStk.push(~env->getString( "Locked" )->idx); - return false; - } } thread->dataStk.push(0); diff --git a/src/g_game.c b/src/g_game.c index 0218e24aa..6bf730f24 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4137,11 +4137,6 @@ void G_GetNextMap(void) netgame == false && gametype == GT_TUTORIAL && nextmap == NEXTMAP_TUTORIALCHALLENGE - && ( - !gamedata - || gamedata->enteredtutorialchallenge == false - || M_GameTrulyStarted() == true - ) ) { nextmap = G_MapNumber(tutorialchallengemap); From d78dccb80f70386abb766ee18db0e9be0830b879 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 26 Apr 2024 15:46:59 +0100 Subject: [PATCH 9/9] In Tutorial gametype, permit all courses even if not visited Allows you to come back to Brakes without having to play Rings again --- src/menus/transient/level-select.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index c7e9ae12d..486104117 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -103,11 +103,15 @@ boolean M_CanShowLevelInList(INT16 mapnum, levelsearch_t *levelsearch) // Finally, the most complex check: does the map have lock conditions? if (levelsearch->checklocked) { - // Check for visitation - if (!(mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - && !(mapheaderinfo[mapnum]->records.mapvisited & MV_VISITED) - && !(cup && cup->cachedlevels[0] == mapnum)) - return false; + // All tutorial courses can be visited for the first time once the game has truly started. + if (levelsearch->tutorial == false || M_GameTrulyStarted() == false) + { + // Check for visitation + if (!(mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) + && !(mapheaderinfo[mapnum]->records.mapvisited & MV_VISITED) + && !(cup && cup->cachedlevels[0] == mapnum)) + return false; + } // Check for completion if ((mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED)