Goner setup: cook

- Build list of incoming text
    - First pass of very first section of dialogue
- Use srb2::Dialogue::Typewriter class to handle ticking through dialogue
- Store history log as incoming text is processed (possible todo: scrolling?)
This commit is contained in:
toaster 2023-12-02 23:40:02 +00:00
parent 1cf14f29dc
commit ffb34204f5
3 changed files with 241 additions and 2 deletions

View file

@ -53,6 +53,7 @@ void Dialogue::Typewriter::NewText(std::string newText)
textTimer = kTextPunctPause; textTimer = kTextPunctPause;
textSpeed = kTextSpeedDefault; textSpeed = kTextSpeedDefault;
textDone = false; textDone = false;
textLines = 1;
syllable = true; syllable = true;
} }
@ -79,6 +80,9 @@ void Dialogue::Typewriter::WriteText(void)
continue; continue;
} }
if (c == '\n')
textLines++;
if (!textDest.empty()) if (!textDest.empty())
nextc = textDest.back(); nextc = textDest.back();
@ -115,7 +119,12 @@ void Dialogue::Typewriter::CompleteText(void)
{ {
while (!textDest.empty()) while (!textDest.empty())
{ {
text.push_back( textDest.back() ); char c = textDest.back();
if (c == '\n')
textLines++;
text.push_back( c );
textDest.pop_back(); textDest.pop_back();
} }

View file

@ -61,6 +61,7 @@ public:
fixed_t textTimer; fixed_t textTimer;
fixed_t textSpeed; fixed_t textSpeed;
bool textDone; bool textDone;
int textLines;
sfxenum_t voiceSfx; sfxenum_t voiceSfx;
bool syllable; bool syllable;

View file

@ -2,6 +2,12 @@
/// \brief The Goner Setup. /// \brief The Goner Setup.
#include "../k_menu.h" #include "../k_menu.h"
#include "../r_skins.h"
#include "../st_stuff.h" // faceprefix
#include "../v_draw.hpp"
#include "../k_dialogue.hpp"
#include <forward_list>
menuitem_t MAIN_Goner[] = menuitem_t MAIN_Goner[] =
{ {
@ -31,6 +37,7 @@ menuitem_t MAIN_Goner[] =
}; };
static boolean M_GonerInputs(INT32 ch); static boolean M_GonerInputs(INT32 ch);
static void M_GonerDrawer(void);
menu_t MAIN_GonerDef = { menu_t MAIN_GonerDef = {
sizeof (MAIN_Goner) / sizeof (menuitem_t), sizeof (MAIN_Goner) / sizeof (menuitem_t),
@ -42,15 +49,156 @@ menu_t MAIN_GonerDef = {
MBF_UD_LR_FLIPPED, MBF_UD_LR_FLIPPED,
"_GONER", "_GONER",
0, 0, 0, 0,
M_DrawHorizontalMenu, M_GonerDrawer,
M_GonerTick, M_GonerTick,
NULL, NULL,
NULL, NULL,
M_GonerInputs, 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<std::optional<GonerSpeaker>, 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<GonerChatLine> LinesToDigest;
std::forward_list<GonerChatLine> LinesOutput;
// ---
void M_GonerTick(void) 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') if (menutyping.active == false && cv_dummyextraspassword.string[0] != '\0')
{ {
// Challenges are not interpreted at this stage. // Challenges are not interpreted at this stage.
@ -59,8 +207,89 @@ void M_GonerTick(void)
cht_Interpret(cv_dummyextraspassword.string); cht_Interpret(cv_dummyextraspassword.string);
CV_StealthSet(&cv_dummyextraspassword, ""); 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<skincolornum_t>(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 M_GonerProfile(INT32 choice)
{ {
(void)choice; (void)choice;