summary refs log tree commit diff stats
path: root/src/message_system.cpp
blob: 1447f1ea3ffb09e3dec6b85e1a12d3d57e3cffb3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?php
/*
       444444444  
      4::::::::4  
     4:::::::::4  
    4::::44::::4  
   4::::4 4::::4   Four Island
  4::::4  4::::4  
 4::::4   4::::4   Written and maintained by Starla Insigna
4::::444444::::444
4::::::::::::::::4  pages/polloftheweek.php
4444444444:::::444
          4::::4   Please do not use, reproduce or steal the
          4::::4   contents of this file without explicit
          4::::4   permission from Hatkirby.
        44::::::44
        4::::::::4
        4444444444
*/

if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}

require('headerproc.php');

if (!isset($_GET['potw']))
{
	if (!isset(<
#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 (game_.isGameplayPaused()) return;

  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");
              }
            }

            if (line.isChoice) {
              line.charsRevealed = line.text.size();
              choiceSelection_ = 0;
            } else {
              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 last currently visible line doesn't
          // have the flag that means an A press is required.
          if (!lines_.empty() && !linesToShow_.back().pause) {
            if (linesToShow_.size() == 2) {
              linesToShow_.pop_front();
            }

            linesToShow_.push_back(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;

  bool shouldAddBlank = false;

  auto lineChunks = splitStr<std::list<std::string>>(std::string(msg), "\n");
  for (std::string text : lineChunks) {
    if (text.substr(0, 1) == "\f") {
      text.erase(0, 1);
      shouldAddBlank = false;

      if (!lines_.empty()) {
        lines_.back().pause = true;
      }
    }

    bool bulleted = false;
    if (text.substr(0, 2) == "* ") {
      text.erase(0, 2);
      bulleted = true;
    }

    auto words = splitStr<std::list<std::string>>(text, " ");

    std::string curLine;
    int curWidth = 0;
    bool firstWord = true;
    // 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;
      for (int i=0; i<word.size(); i++) {
        wordWidth += game_.getFont().getCharacterWidth(word[i]);
        wordWidth++;
      }

      int nextWidth = curWidth + wordWidth;
      if (!firstWord) {
        nextWidth += game_.getFont().getCharacterWidth(' ');
      }

      if (nextWidth > MESSAGE_TEXT_WIDTH) {
        lines_.push_back({.text = curLine, .bulleted = bulleted});
        curLine = word;
        curWidth = wordWidth + game_.getFont().getCharacterWidth(' ');
        bulleted = false;

        if (shouldAddBlank) {
          shouldAddBlank = false;
          lines_.back().pause = true;
        } else {
          shouldAddBlank = true;
        }
      } else {
        curWidth = nextWidth;
        if (!firstWord) {
          curLine.append(" ");
        }
        curLine.append(word);
      }

      firstWord = false;
    }

    lines_.push_back({.text = curLine, .bulleted = bulleted});

    if (shouldAddBlank) {
      shouldAddBlank = false;
      lines_.back().pause = true;
    } else {
      shouldAddBlank = true;
    }
  }

  if (!lines_.empty()) {
    lines_.back().pause = true;
  }

  if (linesToShow_.empty()) {
    linesToShow_.push_back(lines_.front());
    lines_.pop_front();

    if (!linesToShow_.back().pause) {
      linesToShow_.push_back(lines_.front());
      lines_.pop_front();
    }
  }
}

void MessageSystem::showChoice(std::string choice1, std::string choice2) {
  MessageLine line;
  line.text = std::string(8, ' ') + choice1 + std::string(11, ' ') + choice2;
  line.pause = true;
  line.isChoice = true;
  line.choicePos[0] = 8;
  line.choicePos[1] = 8 + 11 + choice1.size();

  lines_.push_back(line);
}

void MessageSystem::advanceText() {
  // Cutscene must be active.
  if (barsState_ != BarsState::Open) {
    return;
  }
  if (linesToShow_.empty()) {
    return;
  }

  // We can only advance if all visible lines are fully revealed.
  for (const MessageLine& line : linesToShow_) {
    if (line.charsRevealed != line.text.size()) {
      return;
    }
  }

  // If the last visible line doesn't have a pause, then the cutscene
  // will automatically progress without us doing anything.
  if (!linesToShow_.back().pause) {
    return;
  }

  // Clear the current pause.
  linesToShow_.back().pause = false;
  showNextArrow_ = false;

  if (lines_.empty()) {
    if (isChoiceActive()) {
      if (choiceSelection_ == 0) {
        game_.getMixer().playSound("../res/sfx/left_selection.wav");
      } else {
        game_.getMixer().playSound("../res/sfx/right_selection.wav");
      }
    }

    linesToShow_.clear();
  } else {
    game_.getMixer().playSound("../res/sfx/pageflip.wav");
  }
}

void MessageSystem::selectFirstChoice() {
  if (isChoiceActive() && choiceSelection_ != 0) {
    choiceSelection_ = 0;
    game_.getMixer().playSound("../res/sfx/horizontal_menu.wav");
  }
}

void MessageSystem::selectSecondChoice() {
  if (isChoiceActive() && choiceSelection_ != 1) {
    choiceSelection_ = 1;
    game_.getMixer().playSound("../res/sfx/horizontal_menu.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_);
  }
}