diff --git a/src/deh_soc.c b/src/deh_soc.c index 53da34d0b..53ad24ae6 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2965,7 +2965,8 @@ static void readcondition(UINT16 set, UINT32 id, char *word2) || (++offset && fastcmp(params[0], "REPLAY")) || (++offset && fastcmp(params[0], "CRASH")) || (++offset && fastcmp(params[0], "TUTORIALSKIP")) - || (++offset && fastcmp(params[0], "TUTORIALDONE"))) + || (++offset && fastcmp(params[0], "TUTORIALDONE")) + || (++offset && fastcmp(params[0], "PLAYGROUNDROUTE"))) { //PARAMCHECK(1); ty = UC_ADDON + offset; @@ -3633,6 +3634,11 @@ void readmaincfg(MYFILE *f, boolean mainfile) titlemap = Z_StrDup(word2); titlechanged = true; } + else if (fastcmp(word, "TUTORIALPLAYGROUNDMAP")) + { + Z_Free(tutorialplaygroundmap); + tutorialplaygroundmap = Z_StrDup(word2); + } else if (fastcmp(word, "TUTORIALCHALLENGEMAP")) { Z_Free(tutorialchallengemap); diff --git a/src/doomstat.h b/src/doomstat.h index 748b0773e..9a8efea18 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -280,6 +280,7 @@ extern boolean looptitle; extern char * bootmap; //bootmap for loading a map on startup extern char * podiummap; // map to load for podium +extern char * tutorialplaygroundmap; // map to load for playground extern char * tutorialchallengemap; // map to load for tutorial skip extern UINT8 tutorialchallenge; #define TUTORIALSKIP_NONE 0 diff --git a/src/f_finale.c b/src/f_finale.c index fcb5821a8..411ddc621 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -504,7 +504,7 @@ static void F_IntroDrawScene(void) } // Joyeaux Anniversaire - V_DrawCenteredMenuString(BASEVIDWIDTH/2, 174 - (textoffs/FRACUNIT), (trans<gonerlevel < GDGONER_DONE) { // Tutorial was finished gamedata->tutorialdone = true; @@ -5129,6 +5130,13 @@ void G_EndGame(void) return; } + if (gametype == GT_TUTORIAL && M_GameAboutToStart() && restoreMenu == NULL) + { + // Playground Hack + F_StartIntro(); + return; + } + // Time to return to the menu. Command_ExitGame_f(); } diff --git a/src/g_gamedata.cpp b/src/g_gamedata.cpp index 077eb0b81..2695dffc9 100644 --- a/src/g_gamedata.cpp +++ b/src/g_gamedata.cpp @@ -83,6 +83,7 @@ void srb2::save_ng_gamedata() ng.milestones.enteredtutorialchallenge = gamedata->enteredtutorialchallenge; ng.milestones.sealedswapalerted = gamedata->sealedswapalerted; ng.milestones.tutorialdone = gamedata->tutorialdone; + ng.milestones.playgroundroute = gamedata->playgroundroute; ng.milestones.gonerlevel = gamedata->gonerlevel; ng.prisons.thisprisoneggpickup = gamedata->thisprisoneggpickup; ng.prisons.prisoneggstothispickup = gamedata->prisoneggstothispickup; @@ -471,6 +472,7 @@ void srb2::load_ng_gamedata() gamedata->enteredtutorialchallenge = js.milestones.enteredtutorialchallenge; gamedata->sealedswapalerted = js.milestones.sealedswapalerted; gamedata->tutorialdone = js.milestones.tutorialdone; + gamedata->playgroundroute = js.milestones.playgroundroute; gamedata->gonerlevel = js.milestones.gonerlevel; gamedata->thisprisoneggpickup = js.prisons.thisprisoneggpickup; diff --git a/src/g_gamedata.h b/src/g_gamedata.h index 0027ef582..9209ecb2b 100644 --- a/src/g_gamedata.h +++ b/src/g_gamedata.h @@ -96,6 +96,7 @@ struct GamedataMilestonesJson final bool enteredtutorialchallenge; bool sealedswapalerted; bool tutorialdone; + bool playgroundroute; SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( GamedataMilestonesJson, @@ -109,7 +110,8 @@ struct GamedataMilestonesJson final finishedtutorialchallenge, enteredtutorialchallenge, sealedswapalerted, - tutorialdone + tutorialdone, + playgroundroute ) }; diff --git a/src/k_menu.h b/src/k_menu.h index 28db4810e..3b1151aae 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -140,6 +140,7 @@ typedef enum MBF_SOUNDLESS = 1<<1, // do not play base menu sounds MBF_NOLOOPENTRIES = 1<<2, // do not loop M_NextOpt/M_PrevOpt MBF_DRAWBGWHILEPLAYING = 1<<3, // run backroutine() outside of GS_MENU + MBF_CANTRESTORE = 1<<4, // Do not use in restoreMenu } menubehaviourflags_t; struct menuitem_t @@ -221,7 +222,7 @@ typedef enum quitkart } main_e; -extern menuitem_t MAIN_Goner[]; +extern menu_t MAIN_GonerAccessibilityDef; extern menu_t MAIN_GonerDef; void M_GonerTick(void); @@ -229,9 +230,12 @@ void M_GonerBGTick(void); void M_GonerBGImplyPassageOfTime(void); void M_DrawGonerBack(void); void M_GonerProfile(INT32 choice); +void M_GonerChoice(INT32 choice); void M_GonerTutorial(INT32 choice); +void M_GonerPlayground(INT32 choice); void M_GonerResetLooking(int type); void M_GonerCheckLooking(void); +void M_GonerResetText(boolean completely); void M_GonerGDQ(boolean opinion); boolean M_GonerMusicPlayable(void); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 64da0fecd..9c0ff5039 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -662,7 +662,7 @@ boolean M_ConsiderSealedSwapAlert(void) void M_ValidateRestoreMenu(void) { - if (restoreMenu == NULL || restoreMenu == &MAIN_GonerDef) + if (restoreMenu == NULL || (restoreMenu->behaviourflags & MBF_CANTRESTORE)) restoreMenu = &MainDef; } @@ -829,17 +829,10 @@ void M_StartControlPanel(void) if (gamedata != NULL && gamedata->gonerlevel < GDGONER_OUTRO - && gamestartchallenge < MAXUNLOCKABLES) + && M_GameAboutToStart()) { - // See M_GameTrulyStarted - if ( - gamedata->unlockpending[gamestartchallenge] - || gamedata->unlocked[gamestartchallenge] - ) - { - gamedata->gonerlevel = GDGONER_OUTRO; - M_GonerBGImplyPassageOfTime(); - } + gamedata->gonerlevel = GDGONER_OUTRO; + M_GonerBGImplyPassageOfTime(); } if (M_GameTrulyStarted() == false) @@ -847,7 +840,7 @@ void M_StartControlPanel(void) // Are you ready for the First Boot Experience? M_ResetOptions(); - currentMenu = &MAIN_GonerDef; + currentMenu = &MAIN_GonerAccessibilityDef; restoreMenu = NULL; M_PlayMenuJam(); diff --git a/src/lua_script.c b/src/lua_script.c index 1f01010cb..c7e9745f3 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -216,6 +216,9 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"podiummap")) { lua_pushstring(L, podiummap); return 1; + } else if (fastcmp(word,"tutorialplaygroundmap")) { + lua_pushstring(L, tutorialplaygroundmap); + return 1; } else if (fastcmp(word,"tutorialchallengemap")) { lua_pushstring(L, tutorialchallengemap); return 1; diff --git a/src/m_cond.c b/src/m_cond.c index 026dace10..c94a115ff 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -660,9 +660,7 @@ void M_ClearStats(void) gamedata->chaokeytutorial = false; gamedata->majorkeyskipattempted = false; gamedata->enteredtutorialchallenge = false; - gamedata->finishedtutorialchallenge = false; gamedata->sealedswapalerted = false; - gamedata->tutorialdone = false; gamedata->musicstate = GDMUSIC_NONE; gamedata->importprofilewins = false; @@ -777,6 +775,10 @@ void M_ClearSecrets(void) gamedata->chaokeys = GDINIT_CHAOKEYS; gamedata->prisoneggstothispickup = GDINIT_PRISONSTOPRIZE; + gamedata->tutorialdone = false; + gamedata->playgroundroute = false; + gamedata->finishedtutorialchallenge = false; + gamedata->gonerlevel = GDGONER_INIT; } @@ -1755,6 +1757,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) return (gamedata->finishedtutorialchallenge == true); case UC_TUTORIALDONE: return (gamedata->tutorialdone == true); + case UC_PLAYGROUND: + return (gamedata->playgroundroute == true); case UC_PASSWORD: return (cn->stringvar == NULL); @@ -2641,6 +2645,8 @@ static const char *M_GetConditionString(condition_t *cn) return "successfully skip the Tutorial"; case UC_TUTORIALDONE: return "complete the Tutorial"; + case UC_PLAYGROUND: + return "pick the Playground"; case UC_PASSWORD: return "enter a secret password"; @@ -3470,6 +3476,27 @@ boolean M_GameTrulyStarted(void) return (gamedata->gonerlevel == GDGONER_DONE); } +boolean M_GameAboutToStart(void) +{ + // Fail safe + if (gamedata == NULL) + return false; + + // Not set + if (gamestartchallenge >= MAXUNLOCKABLES) + return true; + + // An unfortunate sidestep, but sync is important. + if (netgame) + return true; + + // Pending unlocked, but not unlocked + return ( + gamedata->unlockpending[gamestartchallenge] + && !gamedata->unlocked[gamestartchallenge] + ); +} + boolean M_CheckNetUnlockByID(UINT16 unlockid) { if (unlockid >= MAXUNLOCKABLES diff --git a/src/m_cond.h b/src/m_cond.h index 10b9e4883..09175e8b3 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -68,6 +68,7 @@ typedef enum UC_CRASH, // Hee ho ! UC_TUTORIALSKIP, // Complete the Tutorial Challenge UC_TUTORIALDONE, // Complete the Tutorial at all + UC_PLAYGROUND, // Go to the playground instead..? UC_PASSWORD, // Type in something funny @@ -301,7 +302,7 @@ typedef enum { #define GDCONVERT_ROUNDSTOKEY 5 -#define GDINIT_CHAOKEYS 10 // Start with 10 Chao Keys !! +#define GDINIT_CHAOKEYS 0 // Start with ZERO Chao Keys. You get NONE. fizzy lifting dink #define GDINIT_PRISONSTOPRIZE 15 // 15 Prison Eggs to your [Wild Prize] !! typedef enum { @@ -395,6 +396,7 @@ struct gamedata_t boolean finishedtutorialchallenge; boolean sealedswapalerted; boolean tutorialdone; + boolean playgroundroute; gdmusic_t musicstate; UINT8 gonerlevel; @@ -470,6 +472,7 @@ extern UINT16 gamestartchallenge; boolean M_CheckNetUnlockByID(UINT16 unlockid); boolean M_SecretUnlocked(INT32 type, boolean local); boolean M_GameTrulyStarted(void); +boolean M_GameAboutToStart(void); boolean M_CupLocked(cupheader_t *cup); boolean M_CupSecondRowLocked(void); boolean M_MapLocked(UINT16 mapnum); diff --git a/src/m_pw.cpp b/src/m_pw.cpp index 0808ff28c..eb8b7706a 100644 --- a/src/m_pw.cpp +++ b/src/m_pw.cpp @@ -28,6 +28,7 @@ #include "doomdef.h" #include "doomstat.h" #include "doomtype.h" +#include "f_finale.h" #include "g_game.h" #include "k_menu.h" #include "m_cheat.h" @@ -613,6 +614,7 @@ void f_devmode() void f_proceed() { +#if 0 gamedata->gonerlevel = GDGONER_DONE; gamedata->finishedtutorialchallenge = true; M_UpdateUnlockablesAndExtraEmblems(true, true); @@ -621,6 +623,11 @@ void f_proceed() S_StartSound(0, sfx_kc42); G_SaveGameData(); +#else + F_StartIntro(); + M_ClearMenus(true); + M_GonerResetText(true); +#endif } }; // namespace diff --git a/src/menus/main-1.c b/src/menus/main-1.c index f055358dc..3a913427e 100644 --- a/src/menus/main-1.c +++ b/src/menus/main-1.c @@ -101,7 +101,7 @@ void M_QuitSRB2(INT32 choice) (void)choice; - if (M_GameTrulyStarted()) + if (!M_GameAboutToStart() && M_GameTrulyStarted()) { INT32 mrand = M_RandomKey(sizeof(quitsounds) / sizeof(INT32)); if (quitsounds[mrand]) diff --git a/src/menus/main-goner.cpp b/src/menus/main-goner.cpp index 75b41d172..0dd64e811 100644 --- a/src/menus/main-goner.cpp +++ b/src/menus/main-goner.cpp @@ -27,17 +27,109 @@ #include "../m_pw.h" #include "../z_zone.h" -#include +#include #include "../core/string.h" static void M_GonerDrawer(void); +static void M_GonerChoiceDrawer(void); static void M_GonerConclude(INT32 choice); static boolean M_GonerInputs(INT32 ch); -menuitem_t MAIN_Goner[] = +static menuitem_t MAIN_GonerAccessibility[] = { - {IT_STRING | IT_CALL, NULL, NULL, NULL, {.routine = M_QuitSRB2}, 0, 0}, // will be replaced + {IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, +}; + +static UINT32 goneraccessibilitytick = 0; + +//#define HANDSTRAIN + +#ifdef HANDSTRAIN +static void M_GonerHandStrain(INT32 ch) +{ + if (ch != MA_YES) + return; + + CV_StealthSet(&cv_kickstartaccel[0], "On"); +} +#endif + +static void M_GonerPhotosensitivity(INT32 ch) +{ + if (ch == MA_YES) + { + CV_StealthSet(&cv_reducevfx, "Yes"); + CV_StealthSet(&cv_screenshake, "Off"); + CV_StealthSet(&cv_tilting, "Off"); + } + +#ifdef HANDSTRAIN + M_StartMessage("Hand strain warning", + "You may be required to press many buttons\n" + "at once in order to control your Ring Racer.\n" + "\n" + "There is an option for your Accel input\n" + "to \"lock\" on after being held for 1 second.\n" + "Would you like to turn it on?\n" + , &M_GonerHandStrain, MM_YESNO, "Yes, I want Accel to \"lock\"", "No thanks"); +#endif +} + +static void M_GonerAccessibilityTick(void) +{ + if (goneraccessibilitytick) + { + if (!menumessage.active && !menutransition.dest) + { + M_SetupNextMenu(&MAIN_GonerDef, true); + M_GonerTick(); // tick once, for safety + } + + return; + } + + goneraccessibilitytick++; + + M_StartMessage("Photosensitivity warning", + "This game has ""\x87""flashing lights and high-contrast\n" + "patterns.""\x80"" Listen to your body, and\n" + "stop playing if you feel unwell.\n" + "\n" + "There is a ""\x88""special mode""\x80"" to reduce some\n" + "visual effects. Would you like to turn it on?\n" + , &M_GonerPhotosensitivity, MM_YESNO, "Yes, reduce effects", "No thanks"); + return; +} + +static void M_GonerAccessibilityDrawer(void) +{ + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); +} + +menu_t MAIN_GonerAccessibilityDef = { + sizeof (MAIN_GonerAccessibility) / sizeof (menuitem_t), + NULL, + 0, + MAIN_GonerAccessibility, + 26, 160, + 0, 0, + MBF_CANTRESTORE, + "_GONER", + 0, 0, + M_GonerAccessibilityDrawer, + NULL, + M_GonerAccessibilityTick, + NULL, + NULL, + NULL, +}; + +static menuitem_t MAIN_Goner[] = +{ + {IT_STRING | IT_CVAR | IT_CV_STRING, "PASSWORD", + "ATTEMPT ADMINISTRATOR ACCESS.", NULL, + {.cvar = &cv_dummyextraspassword}, 0, 0}, {IT_STRING | IT_CALL, "EXIT PROGRAM", "CONCLUDE OBSERVATIONS NOW.", NULL, @@ -55,9 +147,9 @@ menuitem_t MAIN_Goner[] = "ASSIGN VEHICLE INPUTS.", NULL, {.routine = M_GonerProfile}, 0, 0}, - {IT_STRING | IT_CALL, "BEGIN TUTORIAL", - "PREPARE FOR INTEGRATION.", NULL, - {.routine = M_GonerTutorial}, 0, 0}, + {IT_STRING | IT_CALL, "MAKE CHOICE", + "PREPARE FOR INTEGRATION?", NULL, + {.routine = M_GonerChoice}, 0, 0}, {IT_STRING | IT_CALL, "START GAME", "I WILL SUCCEED.", NULL, @@ -71,7 +163,7 @@ menu_t MAIN_GonerDef = { MAIN_Goner, 26, 160, 0, sizeof (MAIN_Goner) / sizeof (menuitem_t), // extra2 is final width - MBF_UD_LR_FLIPPED, + MBF_CANTRESTORE|MBF_UD_LR_FLIPPED, "_GONER", 0, 0, M_GonerDrawer, @@ -82,9 +174,57 @@ menu_t MAIN_GonerDef = { M_GonerInputs, }; +static menuitem_t MAIN_GonerChoice[] = +{ + {IT_STRING | IT_CALL, "Tails' way", + "As a child scientist, Tails has recorded bits\n" + "and pieces of an adventure he and Eggman went\n" + "on while trying out their new Ring Racers.\n" + "\n" + "This is a structured, back-to-basics tutorial\n" + "that will likely take ""\x88""10-20 minutes""\x80"" of your time.", + NULL, {.routine = M_GonerTutorial}, 0, 0}, + + //{IT_STRING, NULL, NULL, NULL, {.routine = M_QuitSRB2}, 0, 0}, // will be replaced + + {IT_STRING | IT_CALL, "Eggman's way", + "As a childlike scientist, Eggman has turned the\n" + "wrecked Egg Carrier into a giant skatepark,\n" + "dotted with fun collectables to test drivers.\n" + "\n" + "You can ""\x88""exit immediately""\x80"" and get to racing...\n" + "or spend ""\x88""as long as you want""\x80"" in the playground!", + NULL, {.routine = M_GonerPlayground}, 0, 0}, +}; + +static menu_t MAIN_GonerChoiceDef = { + sizeof (MAIN_GonerChoice) / sizeof (menuitem_t), + &MAIN_GonerDef, + 0, + MAIN_GonerChoice, + 26, 160, + 0, 0, + MBF_CANTRESTORE|MBF_UD_LR_FLIPPED|MBF_NOLOOPENTRIES, + "_GONER", + 0, 0, + M_GonerChoiceDrawer, + M_DrawGonerBack, + NULL, + NULL, + NULL, + NULL, +}; + namespace { +typedef enum +{ + GONERCHOICE_TAILS = 0, + //GONERCHOICE_NONEBINEY, + GONERCHOICE_EGGMAN +} gonerchoices_t; + typedef enum { GONERSPEAKER_EGGMAN = 0, @@ -200,8 +340,8 @@ public: }; }; -std::forward_list LinesToDigest; -std::forward_list LinesOutput; +std::deque LinesToDigest; +std::deque LinesOutput; class GonerBGData { @@ -429,16 +569,6 @@ void Miles_Electric_Lower() int goner_levelworking = GDGONER_INIT; bool goner_gdq = false; -void M_GonerResetText(void) -{ - goner_typewriter.ClearText(); - LinesToDigest.clear(); - LinesOutput.clear(); - - goner_scroll = 0; - goner_scrollend = -1; -} - static void Initial_Control_Info(void) { if (cv_currprofile.value != -1) @@ -453,24 +583,13 @@ static void Initial_Control_Info(void) ) ); - if (LinesToDigest.empty()) - { - LinesToDigest.emplace_front(line); - return; - } - - LinesToDigest.emplace_after( - LinesToDigest.begin(), - line - ); + LinesToDigest.push_back(line); } void M_AddGonerLines(void) { SRB2_ASSERT(LinesToDigest.empty()); - auto _ = srb2::finally([]() { LinesToDigest.reverse(); }); - static bool leftoff = false; goner_delay = TICRATE; @@ -480,7 +599,7 @@ void M_AddGonerLines(void) { if (!MAIN_Goner[0].mvar2) { - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Metal Sonic. Are you online?"); } @@ -488,10 +607,10 @@ void M_AddGonerLines(void) if (leftoff) { - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "It must have run into some sort of error..."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Don't worry, your settings so far are saved. "\ "Let's pick up where we left off."); @@ -506,40 +625,40 @@ void M_AddGonerLines(void) { case GDGONER_VIDEO: { - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/2, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/2, "Take a close look, Miles. Moments ago he was at my throat! "\ "Now he's docile as can be on that operating table."); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "I don't feel very safe!"); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/4, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/4, "But its programming is definitely locked down..."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "You've given me quite the headache, Metal. "\ "Thankfully, Tails caught you in the act."); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/5, + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/5, "Wait, I'm getting weird readings over the network."); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "Metal Sonic is the unit labeled \"MS-1\", right?"); - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE, "The ""\x87""viewport""\x80"" and ""\x87""audio""\x80"" "\ "config looks like it got messed up."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "So you're right. I wonder if it has anything to do with that outburst."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Alright, Metal! I don't remember your specifications offhand. "\ "First things first, go ahead and set up your "\ "\x87""Video Options""\x80"" yourself."); - LinesToDigest.emplace_front(0, Initial_Control_Info); + LinesToDigest.emplace_back(0, Initial_Control_Info); break; } @@ -547,101 +666,101 @@ void M_AddGonerLines(void) { if (!leftoff) { - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Ah, you can see us now. Good."); } - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Now, calibrate your ""\x87""Sound Options""\x80""."); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "You always make your stuff so loud by default, Eggman. It might need a moment."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Not Metal! He always needed to be stealthy. But go on, set your sliders."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); + LinesToDigest.emplace_back(0, Miles_Look_Electric); break; } case GDGONER_PROFILE: { if (!leftoff) { - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "Oh! Let's tell Metal about our project!"); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Of course. I and my lab assista-"); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "Lab PARTNER."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Irrelevant!"); } - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/4, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/4, "We made a machine together, Tails and I. "\ "It's called a \"""\x82""Ring Racer""\x80""\"."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE, "At its core, it is designed to utilise the boundless potential "\ "of the ""\x83""High Voltage Ring""\x80""."); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE, "We made this special ""\x83""Ring""\x80"" by combining the power of tens of "\ "thousands of ordinary ""\x82""Rings""\x80""."); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "We recorded some of our testing for you, MS-1. Maybe your neural "\ "network could train on some less violent data for once."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/4, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/4, "While that's uploading, why don't you set up your ""\x87""Profile Card""\x80""?"); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "Yes! That's one of my contributions."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "(I'm too used to my systems being designed for me alone...)"); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "Every racer carries one, to contain their personal settings."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "It helps get your ""\x87""controls""\x80"" set up nice and quickly, "\ "when starting your vehicle and navigating the menu."); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "And it helps track your wins, too."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); + LinesToDigest.emplace_back(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/5, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/5, "Bragging rights. My idea!"); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "You can make the ID and player tag on there anything you want."); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "Mine says \"Nine Tails\". That's the name of my original character! "\ "He's like me if I never met my ""\x84""brother""\x80"". He'd use cool "\ "robotics, but be kind of mean to protect himself..."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/5, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/5, "Mine says \"Robotnik\". You can't beat a classic."); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "And I'm not sure if you'll need it, but we always tell new drivers to "\ "look at the ""\x87""Accessibility""\x80"" settings. Often there's some "\ "feature they're not expecting. Maybe you'd be surprised too?"); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "So go on, do your ""\x87""Profile Setup""\x80""!"); break; @@ -650,31 +769,42 @@ void M_AddGonerLines(void) { if (!leftoff) { - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/2, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/2, "Now that that's been set up, you can use your ""\x87""Profile controls""\x80"" on menus from here on out, too."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/5, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/5, "Miles. How's the upload going?"); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "Just finished."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Perfect."); } - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "Now, Metal... it's important you pay attention."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/5, - "It's time to ""\x87""begin your Tutorial""\x80""!"); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/5, + "We have a ""\x88""choice""\x80"" ready for you."); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, - "Remember, MS-1. Even when you move on from this setup, you "\ - "can always change your ""\x87""Options""\x80"" at any time from the menu."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, + "You can play back our testing data as a sort of ""\x82""tutorial""\x80"\ + " and learn the core parts of driving in a safe environment..."); + + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/5, + "...or if you're too headstrong and want to figure things out"\ + " for yourself, we can let you loose in our ""\x85""playground""\x80""!"); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/2, + "If you do run into trouble, the ""\x82""tutorial""\x80"" can"\ + " always be found in the ""\x87""Extras""\x80"" menu later on."); + + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, + "Either way, MS-1. Even when you move on from this setup,"\ + " you can always change your ""\x87""Options""\x80"" at any time."); + LinesToDigest.emplace_back(0, Miles_Look_Electric); break; } @@ -682,37 +812,35 @@ void M_AddGonerLines(void) { if (!leftoff) { - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/3, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/3, "And... the training data is completed."); } - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "It's kind of funny, actually."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/3, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/3, "Oh? Care to elucidate, Prower?"); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "No matter how much time we took getting here, a machine like "\ "Metal can play it back in minutes."); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "It could have been five days or five years of development on "\ "our ""\x82""Ring Racers""\x80"", and that would barely matter to it."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/4, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/4, "Ha! As if. I'd like to think our partnership hasn't felt "\ "particularly protracted."); - LinesToDigest.emplace_front(0, Miles_Look_Electric); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Look_Electric); + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/2, "But yes. Perhaps now you have a better appreciation of what "\ "we're building here, Metal."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/2, - "If you need to learn more, you can always come back to the Tutorial later in the ""\x87""Extras""\x80"" menu."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/5, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/5, "Now, I'm willing to let bygones be bygones."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/2, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/2, "As long as you keep your violence to the track, I'll be "\ "giving you your autonomy back in a moment."); - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "We've kept the keys from you long enough!"); break; } @@ -720,7 +848,7 @@ void M_AddGonerLines(void) break; default: - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "I am error"); } @@ -839,22 +967,25 @@ void M_GonerTick(void) first = true; // a lie, but only slightly... // Handle rewinding if you clear your gamedata. - M_GonerResetText(); - goner_background = GonerBGData(); - - goner_levelworking = GDGONER_INIT; + M_GonerResetText(true); } M_GonerResetLooking(GDGONER_INIT); if (first) { + goner_background = GonerBGData(); + first = goner_gdq = false; +#if 0 MAIN_Goner[0] = {IT_STRING | IT_CVAR | IT_CV_STRING, "PASSWORD", "ATTEMPT ADMINISTRATOR ACCESS.", NULL, {.cvar = &cv_dummyextraspassword}, 0, 0}; +#endif + + MAIN_Goner[0].mvar2 = 0; if (gamedata->gonerlevel < GDGONER_INTRO) gamedata->gonerlevel = GDGONER_INTRO; @@ -1218,6 +1349,130 @@ static void M_GonerDrawer(void) M_DrawHorizontalMenu(); } +static void M_GonerChoiceDrawer(void) +{ + srb2::Draw drawer = srb2::Draw(); + + const INT32 lex = (24 + BASEVIDWIDTH/2)/2; + + if (itemOn == GONERCHOICE_TAILS) + { + drawer + .size((BASEVIDWIDTH/2) + 25, BASEVIDHEIGHT) + .fill(60); + + drawer + .xy((BASEVIDWIDTH/2) + 40 + 1, 28+3) + .colormap(SKINCOLOR_ORANGE) + .flags(V_FLIP) + .patch("MENUPLTR"); + + drawer + .xy(lex, 28) + .font(srb2::Draw::Font::kGamemode) + .align(srb2::Draw::Align::kCenter) + .text(currentMenu->menuitems[itemOn].text); + + drawer + .xy(8, 72) + .font(srb2::Draw::Font::kThin) + .align(srb2::Draw::Align::kLeft) + .text(currentMenu->menuitems[itemOn].tooltip); + + drawer + .xy(lex, 154) + .font(srb2::Draw::Font::kFreeplay) + .align(srb2::Draw::Align::kCenter) + .text("(unlocks 20 )"); + + drawer + .xy(lex, 154+14) + .font(srb2::Draw::Font::kThin) + .align(srb2::Draw::Align::kCenter) + .flags(V_TRANSLUCENT) + .text("+ more surprises to find"); + + drawer + .xy(lex + 26, 154-4) + .patch("UN_CHA00"); + } + else if (itemOn == GONERCHOICE_EGGMAN) + { + drawer + .x((BASEVIDWIDTH/2) - 24) + .size((BASEVIDWIDTH/2) + 24, BASEVIDHEIGHT) + .fill(44); + + drawer + .xy((BASEVIDWIDTH/2) - 40, 28+3) + .colormap(SKINCOLOR_RED) + .patch("MENUPLTR"); + + drawer + .xy(BASEVIDWIDTH - lex, 28) + .font(srb2::Draw::Font::kGamemode) + .align(srb2::Draw::Align::kCenter) + .text(currentMenu->menuitems[itemOn].text); + + drawer + .xy(BASEVIDWIDTH - 8, 72) + .font(srb2::Draw::Font::kThin) + .align(srb2::Draw::Align::kRight) + .text(currentMenu->menuitems[itemOn].tooltip); + + drawer + .xy(BASEVIDWIDTH - lex, 154) + .font(srb2::Draw::Font::kFreeplay) + .align(srb2::Draw::Align::kCenter) + .text("(unlocks Addons/Online)"); + + drawer + .xy(BASEVIDWIDTH - lex, 154+14) + .font(srb2::Draw::Font::kThin) + .align(srb2::Draw::Align::kCenter) + .flags(V_TRANSLUCENT) + .text("the other way has these too, just later"); + } + + // Un-highlighteds done this weird way because of GONERCHOICE_NONEBINEY + + if (itemOn != GONERCHOICE_TAILS) + { + drawer + .size(20, BASEVIDHEIGHT) + .fill(60); + + drawer + .xy(25, 39) + .font(srb2::Draw::Font::kFreeplay) + .align(srb2::Draw::Align::kLeft) + .text(currentMenu->menuitems[GONERCHOICE_TAILS].text); + + drawer + .xy(20 - 3 - (skullAnimCounter/5), 39+6) + .patch("CUPARROW"); + } + + if (itemOn != GONERCHOICE_EGGMAN) + { + drawer + .x(BASEVIDWIDTH - 20) + .size(20, BASEVIDHEIGHT) + .fill(44); + + drawer + .xy(BASEVIDWIDTH - 25, 39) + .font(srb2::Draw::Font::kFreeplay) + .align(srb2::Draw::Align::kRight) + .text(currentMenu->menuitems[GONERCHOICE_EGGMAN].text); + + drawer + .xy((BASEVIDWIDTH - 20 + 3) + (skullAnimCounter/5), 39+6) + .flags(V_FLIP) + .patch("CUPARROW"); + } +} + // --- void M_GonerProfile(INT32 choice) @@ -1245,19 +1500,16 @@ void M_GonerProfile(INT32 choice) M_GonerResetLooking(GDGONER_PROFILE); } -static void M_GonerSurveyResponse(INT32 ch) +static void M_GonerTutorialResponse(INT32 ch) { if (ch != MA_YES) return; - if (gamedata->gonerlevel < GDGONER_OUTRO) - gamedata->gonerlevel = GDGONER_OUTRO; + M_GonerTutorial(0); } -void M_GonerTutorial(INT32 choice) +void M_GonerChoice(INT32 choice) { - (void)choice; - if (cv_currprofile.value == -1) { const INT32 maxp = PR_GetNumProfiles(); @@ -1270,6 +1522,57 @@ void M_GonerTutorial(INT32 choice) PR_ApplyProfile(profilen, 0); } + if (gamedata->gonerlevel >= GDGONER_OUTRO) + { + M_StartMessage("First Boot Tutorial", + "You've already played the Tutorial!\n" + "Do you want to see it again?", + &M_GonerTutorialResponse, MM_YESNO, "I'd love to", "Not right now"); + return; + } + + M_SetupNextMenu(&MAIN_GonerChoiceDef, false); +} + +static void M_GonerSurveyResponse(INT32 ch) +{ + if (ch != MA_YES) + return; + + if (gamedata->gonerlevel < GDGONER_OUTRO) + gamedata->gonerlevel = GDGONER_OUTRO; + + if (currentMenu == &MAIN_GonerChoiceDef) + { + if (itemOn == GONERCHOICE_EGGMAN) + { + gamedata->playgroundroute = true; + gamedata->gonerlevel = GDGONER_DONE; + + F_StartIntro(); + M_ClearMenus(true); + M_GonerResetText(false); + return; + } + + M_GoBack(0); + } +} + +static void M_GonerSurvey(INT32 choice) +{ + (void)choice; + + // The game is incapable of progression, but I can't bring myself to put an I_Error here. + M_StartMessage("First Boot Error", + "YOU ACCEPT EVERYTHING THAT\nWILL HAPPEN FROM NOW ON.", + &M_GonerSurveyResponse, MM_YESNO, "I agree", "Cancel"); +} + +void M_GonerTutorial(INT32 choice) +{ + (void)choice; + // Please also see M_LevelSelectInit as called in extras-1.c levellist.netgame = false; levellist.canqueue = false; @@ -1279,22 +1582,61 @@ void M_GonerTutorial(INT32 choice) if (!M_LevelListFromGametype(GT_TUTORIAL) && gamedata->gonerlevel < GDGONER_OUTRO) { - // The game is incapable of progression, but I can't bring myself to put an I_Error here. - M_StartMessage("Agreement", - "YOU ACCEPT EVERYTHING THAT WILL HAPPEN FROM NOW ON.", - &M_GonerSurveyResponse, MM_YESNO, "I agree", "Cancel"); + M_GonerSurvey(0); + return; } } +void M_GonerPlayground(INT32 choice) +{ + (void)choice; + + UINT16 playgroundmap = NEXTMAP_INVALID; + if (tutorialplaygroundmap) + playgroundmap = G_MapNumber(tutorialplaygroundmap); + + if (playgroundmap >= nummapheaders) + { + M_GonerSurvey(0); + return; + } + + multiplayer = true; + + M_MenuToLevelPreamble(0, false); + + D_MapChange( + playgroundmap+1, + GT_TUTORIAL, + false, + true, + 0, + false, + false + ); + + M_ClearMenus(true); + restoreMenu = NULL; // Playground Hack + + // need to do all this here because it will skip returning to goner and there are circumstances (game close) where DoCompleted won't be called + gamedata->gonerlevel = GDGONER_DONE; + gamedata->playgroundroute = true; + gamedata->deferredsave = true; + M_GonerResetText(true); +} + static void M_GonerConclude(INT32 choice) { (void)choice; gamedata->gonerlevel = GDGONER_DONE; + if (gamedata->chaokeys < 20) + gamedata->chaokeys = 20; + F_StartIntro(); M_ClearMenus(true); - M_GonerResetText(); + M_GonerResetText(true); } void M_GonerGDQ(boolean opinion) @@ -1311,24 +1653,23 @@ void M_GonerGDQ(boolean opinion) if (opinion) // Save The Animals { - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE/2, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, TICRATE/2, "Why wouldn't you save the frames..?"); - LinesToDigest.emplace_front(0, Miles_Look_Camera); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + LinesToDigest.emplace_back(0, Miles_Look_Camera); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, 0, "Don't mind him. Good luck on the run!"); - LinesToDigest.emplace_front(0, Miles_Look_Electric); + LinesToDigest.emplace_back(0, Miles_Look_Electric); } else // Save The Frames { - LinesToDigest.emplace_front(0, Miles_Electric_Lower); - LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/2, + LinesToDigest.emplace_back(0, Miles_Electric_Lower); + LinesToDigest.emplace_back(GONERSPEAKER_TAILS, TICRATE/2, "But what about all the little animals..."); - LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + LinesToDigest.emplace_back(GONERSPEAKER_EGGMAN, 0, "It's just logical. I know you'll conquer this run."); } - LinesToDigest.reverse(); if (gamedata->gonerlevel <= GDGONER_TUTORIAL) { @@ -1380,3 +1721,18 @@ static boolean M_GonerInputs(INT32 ch) return false; } + +void M_GonerResetText(boolean completely) +{ + goner_typewriter.ClearText(); + LinesToDigest.clear(); + LinesOutput.clear(); + + goner_scroll = 0; + goner_scrollend = -1; + + if (!completely) + return; + + goner_levelworking = GDGONER_INIT; +} diff --git a/src/menus/options-profiles-1.c b/src/menus/options-profiles-1.c index 8f61f01cd..85e1fd73d 100644 --- a/src/menus/options-profiles-1.c +++ b/src/menus/options-profiles-1.c @@ -83,6 +83,12 @@ void M_StartEditProfile(INT32 c) PR_InitNewProfile(); // initialize the new profile. optionsmenu.profile = PR_GetProfile(optionsmenu.profilen); + if (cv_kickstartaccel[0].value) + { + // Primarily for Goner but should help with standard set-up too + optionsmenu.profile->kickstartaccel = true; + } + // copy this profile's controls into optionsmenu so that we can edit controls without changing them directly. // we do this so that we don't edit a profile's controls in real-time and end up doing really weird shit. memcpy(&optionsmenu.tempcontrols, optionsmenu.profile->controls, sizeof(gamecontroldefault)); diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index 00884cb4f..c94be64a1 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -561,7 +561,8 @@ void M_EndGame(INT32 choice) if (!Playing()) return; - if (M_GameTrulyStarted() == false) + if (M_GameTrulyStarted() == false + || M_GameAboutToStart() == true) // Playground Hack { // No returning to the title screen. M_QuitSRB2(-1); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 9111107ec..a200652d2 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -120,6 +120,7 @@ #include "k_credits.h" #include "k_objects.h" #include "p_deepcopy.h" +#include "k_color.h" // K_ColorUsable // Replay names have time #if !defined (UNDER_CE) @@ -8046,7 +8047,7 @@ static void P_ShuffleTeams(void) static void P_InitPlayers(void) { - INT32 i, skin = -1, follower = -1; + INT32 i, skin = -1, follower = -1, col = SKINCOLOR_NONE; // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); @@ -8060,7 +8061,28 @@ static void P_InitPlayers(void) // Get skin from name. if (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->relevantskin[0]) { - skin = R_SkinAvailable(mapheaderinfo[gamemap-1]->relevantskin); + if (strcmp(mapheaderinfo[gamemap-1]->relevantskin, "_PROFILE") == 0) + { + profile_t *p = PR_GetProfile(cv_ttlprofilen.value); + if (p && !netgame) + { + skin = R_SkinAvailable(p->skinname); + + if (!R_SkinUsable(g_localplayers[0], skin, false)) + { + skin = GetSkinNumClosestToStats(skins[skin].kartspeed, skins[skin].kartweight, skins[skin].flags, false); + } + + if (K_ColorUsable(static_cast(p->color), false, true) == true) + { + col = p->color; + } + } + } + else + { + skin = R_SkinAvailable(mapheaderinfo[gamemap-1]->relevantskin); + } } else { @@ -8097,7 +8119,7 @@ static void P_InitPlayers(void) if (skin != -1) { SetPlayerSkinByNum(i, skin); - players[i].skincolor = skins[skin].prefcolor; + players[i].skincolor = (col != SKINCOLOR_NONE) ? col : skins[skin].prefcolor; players[i].followerskin = follower; if (follower != -1)