#include "message_system.h" #include "game.h" #include "util.h" const int CHARS_TO_REVEAL = 1; const int CHARS_PER_BEEP = 8; void MessageSystem::tick(double dt) { if (barsState_ == BarsState::Opening || barsState_ == BarsState::Closing) { accum_ += dt; if (accum_ >= length_) { if (barsState_ == BarsState::Opening) { barsState_ = BarsState::Open; } else { barsState_ = BarsState::Closed; } } } else if (barsState_ == BarsState::Open) { if (!linesToShow_.empty()) { textAdvTimer_.accumulate(dt); while (textAdvTimer_.step()) { // Try to advance text on the first line that isn't totally revealed yet. bool advancedChars = false; for (MessageLine& line : linesToShow_) { if (line.charsRevealed < line.text.size()) { // Every so often play a beep. if (line.charsRevealed % CHARS_PER_BEEP == 0) { if (speaker_ == SpeakerType::Man) { game_.getMixer().playSound("../res/sfx/speaking_man.wav"); } else if (speaker_ == SpeakerType::Woman) { game_.getMixer().playSound("../res/sfx/speaking_woman.wav"); } else if (speaker_ == SpeakerType::Boy) { game_.getMixer().playSound("../res/sfx/speaking_boy.wav"); } else if (speaker_ == SpeakerType::Girl) { game_.getMixer().playSound("../res/sfx/speaking_girl.wav"); } else if (speaker_ == SpeakerType::Nonhuman) { game_.getMixer().playSound("../res/sfx/speaking_nonhuman.wav"); } } line.charsRevealed += CHARS_TO_REVEAL; if (line.charsRevealed > line.text.size()) { line.charsRevealed = line.text.size(); } advancedChars = true; break; } } if (!advancedChars) { // If both lines are totally revealed, see if we can scroll up a line. // This is doable as long as the next line isn't the sentinel value that // means an A press is required. if (!lines_.empty() && lines_.front() != "\n") { if (linesToShow_.size() == 2) { linesToShow_.pop_front(); } linesToShow_.push_back(MessageLine { .text = lines_.front() }); lines_.pop_front(); } else { showNextArrow_ = true; } } } if (showNextArrow_) { nextArrowBobTimer_.accumulate(dt); while (nextArrowBobTimer_.step()) { if (nextArrowBobDown_) { nextArrowBobPos_++; if (nextArrowBobPos_ >= 4) { nextArrowBobDown_ = false; } } else { nextArrowBobPos_--; if (nextArrowBobPos_ <= 0) { nextArrowBobDown_ = true; } } } } } } } void MessageSystem::displayCutsceneBars() { accum_ = 0.0; barsState_ = BarsState::Opening; } void MessageSystem::hideCutsceneBars() { accum_ = 0.0; barsState_ = BarsState::Closing; } void MessageSystem::displayMessage(std::string_view msg, std::string speakerName, SpeakerType speakerType) { if (!(barsState_ == BarsState::Opening || barsState_ == BarsState::Open)) { displayCutsceneBars(); } speaker_ = speakerType; speakerName_ = speakerName; auto lineChunks = splitStr>(std::string(msg), "\n"); for (const std::string& text : lineChunks) { auto words = splitStr>(text, " "); std::string curLine; int curWidth = 0; bool firstWord = true; bool shouldAddBlank = false; // I'm gonna be frank and admit it: I'm not gonna take hyphenation into // consideration. Please don't write any words that are wider than the // textbox. for (const std::string& word : words) { int wordWidth = 0; bool firstChar = true; for (int i=0; i MESSAGE_TEXT_WIDTH) { lines_.push_back(curLine); curLine = word; curWidth = wordWidth + game_.getFont().getCharacterWidth(' '); if (shouldAddBlank) { shouldAddBlank = false; lines_.push_back("\n"); } else { shouldAddBlank = true; } } else { curWidth = nextWidth; if (!firstWord) { curLine.append(" "); } curLine.append(word); } firstWord = false; } lines_.push_back(curLine); lines_.push_back("\n"); } if (linesToShow_.empty()) { linesToShow_.push_back(MessageLine { .text = lines_.front() }); lines_.pop_front(); if (lines_.front() != "\n") { linesToShow_.push_back(MessageLine { .text = lines_.front() }); lines_.pop_front(); } } } void MessageSystem::advanceText() { if (barsState_ != BarsState::Open) { return; } for (const MessageLine& line : linesToShow_) { if (line.charsRevealed != line.text.size()) { return; } } if (lines_.empty()) { linesToShow_.clear(); return; } if (lines_.front() != "\n") { return; } lines_.pop_front(); showNextArrow_ = false; if (lines_.empty()) { linesToShow_.clear(); } else { game_.getMixer().playSound("../res/sfx/pageflip.wav"); } } double MessageSystem::getCutsceneBarsProgress() const { switch (barsState_) { case BarsState::Closed: return 0.0; case BarsState::Opening: return accum_ / length_; case BarsState::Open: return 1.0; case BarsState::Closing: return 1.0 - (accum_ / length_); } }