diff --git a/src/k_dialogue.cpp b/src/k_dialogue.cpp index 6d2286289..43d772ad8 100644 --- a/src/k_dialogue.cpp +++ b/src/k_dialogue.cpp @@ -53,6 +53,7 @@ void Dialogue::Typewriter::NewText(std::string newText) textTimer = kTextPunctPause; textSpeed = kTextSpeedDefault; textDone = false; + textLines = 1; syllable = true; } @@ -79,6 +80,9 @@ void Dialogue::Typewriter::WriteText(void) continue; } + if (c == '\n') + textLines++; + if (!textDest.empty()) nextc = textDest.back(); @@ -115,7 +119,12 @@ void Dialogue::Typewriter::CompleteText(void) { while (!textDest.empty()) { - text.push_back( textDest.back() ); + char c = textDest.back(); + + if (c == '\n') + textLines++; + + text.push_back( c ); textDest.pop_back(); } diff --git a/src/k_dialogue.hpp b/src/k_dialogue.hpp index 1fa96de13..adce8fa84 100644 --- a/src/k_dialogue.hpp +++ b/src/k_dialogue.hpp @@ -61,6 +61,7 @@ public: fixed_t textTimer; fixed_t textSpeed; bool textDone; + int textLines; sfxenum_t voiceSfx; bool syllable; diff --git a/src/menus/main-goner.cpp b/src/menus/main-goner.cpp index 37b5aa951..eee4605ba 100644 --- a/src/menus/main-goner.cpp +++ b/src/menus/main-goner.cpp @@ -2,6 +2,12 @@ /// \brief The Goner Setup. #include "../k_menu.h" +#include "../r_skins.h" +#include "../st_stuff.h" // faceprefix +#include "../v_draw.hpp" +#include "../k_dialogue.hpp" + +#include menuitem_t MAIN_Goner[] = { @@ -31,6 +37,7 @@ menuitem_t MAIN_Goner[] = }; static boolean M_GonerInputs(INT32 ch); +static void M_GonerDrawer(void); menu_t MAIN_GonerDef = { sizeof (MAIN_Goner) / sizeof (menuitem_t), @@ -42,15 +49,156 @@ menu_t MAIN_GonerDef = { MBF_UD_LR_FLIPPED, "_GONER", 0, 0, - M_DrawHorizontalMenu, + M_GonerDrawer, M_GonerTick, NULL, NULL, M_GonerInputs, }; +// --- + +typedef enum +{ + GONERSPEAKER_EGGMAN = 0, + GONERSPEAKER_TAILS, + MAXGONERSPEAKERS +} gonerspeakers_t; + +class GonerSpeaker +{ +public: + float offset; + + GonerSpeaker(std::string skinName, float offset) + { + if (!skinName.empty()) + { + this->skinID = R_SkinAvailable(skinName.c_str()); + } + + this->offset = offset; + }; + + sfxenum_t TalkSound(void) + { + if (!ValidID()) + return sfx_ktalk; + + return skins[ skinID ] + .soundsid[ S_sfx[sfx_ktalk].skinsound ]; + }; + + int GetSkinID(void) + { + if (!ValidID()) + return -1; + + return skinID; + }; + +private: + int skinID = -1; + bool ValidID(void) + { + return (skinID >= 0 && skinID < numskins); + }; +}; + +std::array, MAXGONERSPEAKERS> goner_speakers = {}; + +srb2::Dialogue::Typewriter goner_typewriter; + +int goner_delay; + +class GonerChatLine +{ +public: + gonerspeakers_t speaker; + std::string dialogue; + int value; // Mutlipurpose. + + GonerChatLine(gonerspeakers_t speaker, int delay, std::string dialogue) + { + this->speaker = speaker; + this->dialogue = V_ScaledWordWrap( + (BASEVIDWIDTH/2 + 12) << FRACBITS, + FRACUNIT, FRACUNIT, FRACUNIT, + 0, TINY_FONT, + dialogue.c_str() + ); + this->value = delay; + }; + + // Returns true if line is text + bool Handle(void) + { + if (speaker >= MAXGONERSPEAKERS) + return false; + + goner_typewriter.voiceSfx = sfx_ktalk; + if (goner_speakers[speaker]) + { + goner_typewriter.voiceSfx = (*goner_speakers[speaker]).TalkSound(); + } + + goner_typewriter.NewText(dialogue); + + goner_delay = value; + + value = 1; // this is now repurposed as the number of lines visible + + return true; + }; +}; + +std::forward_list LinesToDigest; +std::forward_list LinesOutput; + +// --- + void M_GonerTick(void) { + static bool speakersinit = false; + if (!speakersinit) + { + goner_delay = TICRATE; + + goner_speakers[GONERSPEAKER_EGGMAN] = GonerSpeaker("eggman", 0); + goner_speakers[GONERSPEAKER_TAILS] = GonerSpeaker("tails", 12); + + LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, TICRATE, + "Metal Sonic. Are you online?"); + LinesToDigest.emplace_front(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(GONERSPEAKER_TAILS, 0, + "I don't feel very safe!"); + LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE/4, + "But its programming is definitely locked down..."); + + LinesToDigest.emplace_front(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, + "Wait, I'm getting weird readings over the network."); + LinesToDigest.emplace_front(GONERSPEAKER_TAILS, 0, + "Metal Sonic is the unit labeled \"MS1\", right?"); + LinesToDigest.emplace_front(GONERSPEAKER_TAILS, TICRATE, + "The ""\x87""viewport""\x80"" and ""\x87""audio""\x80"" config looks like it got messed up."); + + LinesToDigest.emplace_front(GONERSPEAKER_EGGMAN, 0, + "So you're right. I wonder if it has anything to do with that outburst."); + LinesToDigest.emplace_front(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.reverse(); + + speakersinit = true; + } + if (menutyping.active == false && cv_dummyextraspassword.string[0] != '\0') { // Challenges are not interpreted at this stage. @@ -59,8 +207,89 @@ void M_GonerTick(void) cht_Interpret(cv_dummyextraspassword.string); CV_StealthSet(&cv_dummyextraspassword, ""); } + + goner_typewriter.WriteText(); + + if (goner_typewriter.textDone) + { + if (goner_delay > 0) + goner_delay--; + else if (!LinesToDigest.empty()) + { + if (!LinesOutput.empty()) + LinesOutput.front().value = goner_typewriter.textLines; + + auto line = LinesToDigest.front(); + if (line.Handle()) + LinesOutput.push_front(line); + LinesToDigest.pop_front(); + } + } } +static void M_GonerDrawer(void) +{ + srb2::Draw drawer = srb2::Draw(); + + drawer + .width(BASEVIDWIDTH) + .height(BASEVIDHEIGHT) + .fill(31); + + drawer = drawer.x(BASEVIDWIDTH/4); + + float newy = BASEVIDHEIGHT/2 + (3*12); + boolean first = true; + + for (auto & element : LinesOutput) + { + INT32 flags = V_TRANSLUCENT; + std::string text; + if (first) + { + text = goner_typewriter.text; + newy -= goner_typewriter.textLines*12; + flags = 0; + first = false; + } + else + { + text = element.dialogue; + newy -= element.value*12; + } + + if (newy < 0) break; + + //if (newy > BASEVIDHEIGHT) continue; -- not needed yet + + if (!goner_speakers[element.speaker]) + continue; + + auto speaker = *goner_speakers[element.speaker]; + + srb2::Draw line = drawer + .xy(speaker.offset, newy) + .flags(flags); + + int skinID = speaker.GetSkinID(); + if (skinID != -1) + { + line + .xy(-16, -2) + .colormap(skinID, static_cast(skins[skinID].prefcolor)) + .patch(faceprefix[skinID][FACE_MINIMAP]); + } + + line + .font(srb2::Draw::Font::kThin) + .text( text.c_str() ); + } + + M_DrawHorizontalMenu(); +} + +// --- + void M_GonerProfile(INT32 choice) { (void)choice;