diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/consts.h | 3 | ||||
| -rw-r--r-- | src/game.cpp | 33 | ||||
| -rw-r--r-- | src/game.h | 15 | ||||
| -rw-r--r-- | src/main.cpp | 2 | ||||
| -rw-r--r-- | src/muxer.cpp | 12 | ||||
| -rw-r--r-- | src/muxer.h | 2 | ||||
| -rw-r--r-- | src/renderer.cpp | 96 | ||||
| -rw-r--r-- | src/renderer.h | 57 | ||||
| -rw-r--r-- | src/sign.cpp | 130 | ||||
| -rw-r--r-- | src/sign.h | 45 |
10 files changed, 381 insertions, 14 deletions
| diff --git a/src/consts.h b/src/consts.h index 78312c0..ecb2306 100644 --- a/src/consts.h +++ b/src/consts.h | |||
| @@ -12,5 +12,8 @@ constexpr int CHUNK_WIDTH = 80; | |||
| 12 | constexpr int CHUNK_HEIGHT = 80; | 12 | constexpr int CHUNK_HEIGHT = 80; |
| 13 | constexpr int RADIUS = 8; | 13 | constexpr int RADIUS = 8; |
| 14 | constexpr int NUM_TITLES = 1; | 14 | constexpr int NUM_TITLES = 1; |
| 15 | constexpr int MESSAGE_MARGIN = 64; | ||
| 16 | constexpr int MESSAGE_TEXT_WIDTH = GAME_WIDTH - MESSAGE_MARGIN * 2; | ||
| 17 | constexpr int CHARS_PER_BEEP = 8; | ||
| 15 | 18 | ||
| 16 | #endif /* end of include guard: CONSTS_H_152EBF56 */ | 19 | #endif /* end of include guard: CONSTS_H_152EBF56 */ |
| diff --git a/src/game.cpp b/src/game.cpp index 301447f..5838528 100644 --- a/src/game.cpp +++ b/src/game.cpp | |||
| @@ -7,9 +7,10 @@ | |||
| 7 | #include "renderer.h" | 7 | #include "renderer.h" |
| 8 | #include "consts.h" | 8 | #include "consts.h" |
| 9 | 9 | ||
| 10 | Game::Game(std::mt19937& rng, Muxer& muxer) : | 10 | Game::Game(std::mt19937& rng, Muxer& muxer, Renderer& renderer) : |
| 11 | rng(rng), | 11 | rng(rng), |
| 12 | muxer(muxer) | 12 | muxer(muxer), |
| 13 | sign(renderer.getFont()) | ||
| 13 | { | 14 | { |
| 14 | losePopLampTimer.accumulate(losePopLampTimer.getDt()); | 15 | losePopLampTimer.accumulate(losePopLampTimer.getDt()); |
| 15 | 16 | ||
| @@ -631,7 +632,7 @@ void Game::performDash() { | |||
| 631 | } | 632 | } |
| 632 | } | 633 | } |
| 633 | 634 | ||
| 634 | void Game::update(size_t frameTime) { | 635 | void Game::updatePlaying(size_t frameTime) { |
| 635 | SDL_Event e; | 636 | SDL_Event e; |
| 636 | 637 | ||
| 637 | while (SDL_PollEvent(&e)) | 638 | while (SDL_PollEvent(&e)) |
| @@ -666,8 +667,22 @@ void Game::update(size_t frameTime) { | |||
| 666 | { | 667 | { |
| 667 | if (losing == LoseState::None) | 668 | if (losing == LoseState::None) |
| 668 | { | 669 | { |
| 670 | auto [lookX, lookY] = coordInDirection(player_x, player_y, playerAnim.getDirection()); | ||
| 671 | MapData& lookTile = map.at(lookX, lookY); | ||
| 669 | if (moving) { | 672 | if (moving) { |
| 670 | queueDash = true; | 673 | if (!lookTile.sign) { |
| 674 | queueDash = true; | ||
| 675 | } | ||
| 676 | } else if (lookTile.sign) { | ||
| 677 | if (lookTile.text.empty()) { | ||
| 678 | int lineToRead = nextSignIndex++; | ||
| 679 | if (nextSignIndex >= signTexts.size()) { | ||
| 680 | nextSignIndex = 0; | ||
| 681 | } | ||
| 682 | lookTile.text = signTexts[lineToRead]; | ||
| 683 | } | ||
| 684 | |||
| 685 | sign.displayMessage(lookTile.text); | ||
| 671 | } else { | 686 | } else { |
| 672 | performDash(); | 687 | performDash(); |
| 673 | } | 688 | } |
| @@ -815,7 +830,7 @@ void Game::update(size_t frameTime) { | |||
| 815 | switch (signInstructionState) { | 830 | switch (signInstructionState) { |
| 816 | case SignInstructionState::Hidden: { | 831 | case SignInstructionState::Hidden: { |
| 817 | auto [lookX, lookY] = coordInDirection(player_x, player_y, playerAnim.getDirection()); | 832 | auto [lookX, lookY] = coordInDirection(player_x, player_y, playerAnim.getDirection()); |
| 818 | if (map.at(lookX, lookY).sign) { | 833 | if (losing == LoseState::None && map.at(lookX, lookY).sign) { |
| 819 | signInstructionState = SignInstructionState::FadingIn; | 834 | signInstructionState = SignInstructionState::FadingIn; |
| 820 | signFade.start(1000); | 835 | signFade.start(1000); |
| 821 | } | 836 | } |
| @@ -913,3 +928,11 @@ void Game::update(size_t frameTime) { | |||
| 913 | 928 | ||
| 914 | playerAnim.update(frameTime); | 929 | playerAnim.update(frameTime); |
| 915 | } | 930 | } |
| 931 | |||
| 932 | void Game::update(size_t frameTime) { | ||
| 933 | if (sign.signDisplayState != SignInstructionState::Hidden) { | ||
| 934 | sign.update(frameTime, *this); | ||
| 935 | } else { | ||
| 936 | updatePlaying(frameTime); | ||
| 937 | } | ||
| 938 | } | ||
| diff --git a/src/game.h b/src/game.h index 71685e6..637a033 100644 --- a/src/game.h +++ b/src/game.h | |||
| @@ -11,6 +11,9 @@ | |||
| 11 | #include "animation.h" | 11 | #include "animation.h" |
| 12 | #include "interpolation.h" | 12 | #include "interpolation.h" |
| 13 | #include "consts.h" | 13 | #include "consts.h" |
| 14 | #include "sign.h" | ||
| 15 | |||
| 16 | class Renderer; | ||
| 14 | 17 | ||
| 15 | constexpr int TilesetIndex(int x, int y) { | 18 | constexpr int TilesetIndex(int x, int y) { |
| 16 | return x + y * 25; | 19 | return x + y * 25; |
| @@ -23,13 +26,6 @@ enum class LoseState { | |||
| 23 | Outro | 26 | Outro |
| 24 | }; | 27 | }; |
| 25 | 28 | ||
| 26 | enum class SignInstructionState { | ||
| 27 | Hidden, | ||
| 28 | FadingIn, | ||
| 29 | Visible, | ||
| 30 | FadingOut | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct Input { | 29 | struct Input { |
| 34 | bool left = false; | 30 | bool left = false; |
| 35 | bool right = false; | 31 | bool right = false; |
| @@ -58,7 +54,7 @@ struct Kickup { | |||
| 58 | class Game { | 54 | class Game { |
| 59 | public: | 55 | public: |
| 60 | 56 | ||
| 61 | Game(std::mt19937& rng, Muxer& muxer); | 57 | Game(std::mt19937& rng, Muxer& muxer, Renderer& render); |
| 62 | 58 | ||
| 63 | void update(size_t dt); | 59 | void update(size_t dt); |
| 64 | 60 | ||
| @@ -111,6 +107,7 @@ public: | |||
| 111 | int nextSignIndex = 0; | 107 | int nextSignIndex = 0; |
| 112 | SignInstructionState signInstructionState = SignInstructionState::Hidden; | 108 | SignInstructionState signInstructionState = SignInstructionState::Hidden; |
| 113 | Interpolation signFade; | 109 | Interpolation signFade; |
| 110 | Sign sign; | ||
| 114 | 111 | ||
| 115 | private: | 112 | private: |
| 116 | 113 | ||
| @@ -144,6 +141,8 @@ private: | |||
| 144 | 141 | ||
| 145 | void performDash(); | 142 | void performDash(); |
| 146 | 143 | ||
| 144 | void updatePlaying(size_t frameTime); | ||
| 145 | |||
| 147 | }; | 146 | }; |
| 148 | 147 | ||
| 149 | #endif /* end of include guard: GAME_H_7D2B65AE */ | 148 | #endif /* end of include guard: GAME_H_7D2B65AE */ |
| diff --git a/src/main.cpp b/src/main.cpp index 49f5ff2..2bd8f3c 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -21,7 +21,7 @@ int main(int, char**) | |||
| 21 | Renderer renderer; | 21 | Renderer renderer; |
| 22 | Muxer muxer; | 22 | Muxer muxer; |
| 23 | 23 | ||
| 24 | Game game(rng, muxer); | 24 | Game game(rng, muxer, renderer); |
| 25 | 25 | ||
| 26 | constexpr int titleFadeLen = 2000; | 26 | constexpr int titleFadeLen = 2000; |
| 27 | bool doneTitles = false; | 27 | bool doneTitles = false; |
| diff --git a/src/muxer.cpp b/src/muxer.cpp index 3450187..c93c45a 100644 --- a/src/muxer.cpp +++ b/src/muxer.cpp | |||
| @@ -42,6 +42,18 @@ void Muxer::setPlayerLoc(int x, int y) { | |||
| 42 | ERRCHECK(system_->setListenerAttributes(0, &attributes)); | 42 | ERRCHECK(system_->setListenerAttributes(0, &attributes)); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | void Muxer::playSound(std::string name) { | ||
| 46 | std::string eventPath = std::string("event:/") + name; | ||
| 47 | |||
| 48 | FMOD::Studio::EventDescription* eventDescription = nullptr; | ||
| 49 | ERRCHECK(system_->getEvent(eventPath.c_str(), &eventDescription)); | ||
| 50 | |||
| 51 | FMOD::Studio::EventInstance* eventInstance = nullptr; | ||
| 52 | ERRCHECK(eventDescription->createInstance(&eventInstance)); | ||
| 53 | ERRCHECK(eventInstance->start()); | ||
| 54 | ERRCHECK(eventInstance->release()); | ||
| 55 | } | ||
| 56 | |||
| 45 | void Muxer::playSoundAtPosition(std::string name, float x, float y) { | 57 | void Muxer::playSoundAtPosition(std::string name, float x, float y) { |
| 46 | std::string eventPath = std::string("event:/") + name; | 58 | std::string eventPath = std::string("event:/") + name; |
| 47 | 59 | ||
| diff --git a/src/muxer.h b/src/muxer.h index b1a5b26..9750808 100644 --- a/src/muxer.h +++ b/src/muxer.h | |||
| @@ -28,6 +28,8 @@ public: | |||
| 28 | 28 | ||
| 29 | void setPlayerLoc(int x, int y); | 29 | void setPlayerLoc(int x, int y); |
| 30 | 30 | ||
| 31 | void playSound(std::string name); | ||
| 32 | |||
| 31 | void playSoundAtPosition(std::string name, float x, float y); | 33 | void playSoundAtPosition(std::string name, float x, float y); |
| 32 | 34 | ||
| 33 | void setMusicLevel(int level); | 35 | void setMusicLevel(int level); |
| diff --git a/src/renderer.cpp b/src/renderer.cpp index 2be36ae..c8c1746 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #include "renderer.h" | 1 | #include "renderer.h" |
| 2 | #include <iostream> | ||
| 2 | #include "game.h" | 3 | #include "game.h" |
| 3 | 4 | ||
| 4 | Renderer::Renderer() | 5 | Renderer::Renderer() |
| @@ -113,6 +114,11 @@ Renderer::Renderer() | |||
| 113 | 114 | ||
| 114 | loadTextureFromFile("../res/title0.png", titles_[0]); | 115 | loadTextureFromFile("../res/title0.png", titles_[0]); |
| 115 | SDL_QueryTexture(titles_[0].get(), nullptr, nullptr, &titleWidths_[0], &titleHeights_[0]); | 116 | SDL_QueryTexture(titles_[0].get(), nullptr, nullptr, &titleWidths_[0], &titleHeights_[0]); |
| 117 | |||
| 118 | font_ = font_ptr(TTF_OpenFont("../res/softsquare.ttf", 45)); | ||
| 119 | if (!font_) { | ||
| 120 | throw ttf_error(); | ||
| 121 | } | ||
| 116 | } | 122 | } |
| 117 | 123 | ||
| 118 | void Renderer::renderGame( | 124 | void Renderer::renderGame( |
| @@ -389,6 +395,55 @@ void Renderer::renderGame( | |||
| 389 | SDL_RenderCopy(ren_.get(), readInstruction_.get(), nullptr, nullptr); | 395 | SDL_RenderCopy(ren_.get(), readInstruction_.get(), nullptr, nullptr); |
| 390 | } | 396 | } |
| 391 | 397 | ||
| 398 | if (game.sign.signDisplayState != SignInstructionState::Hidden) { | ||
| 399 | int opacity = 255; | ||
| 400 | if (game.sign.signDisplayState == SignInstructionState::FadingIn) { | ||
| 401 | opacity = game.sign.signDisplayFade.getProgress(0, 255); | ||
| 402 | } else if (game.sign.signDisplayState == SignInstructionState::FadingOut) { | ||
| 403 | opacity = game.sign.signDisplayFade.getProgress(255, 0); | ||
| 404 | } | ||
| 405 | |||
| 406 | SDL_Rect signRect { | ||
| 407 | 0, | ||
| 408 | GAME_HEIGHT / 2 - (45 * 2 + 1 + MESSAGE_MARGIN * 2) / 2, | ||
| 409 | GAME_WIDTH, | ||
| 410 | 45 * 2 + 1 + MESSAGE_MARGIN * 2 | ||
| 411 | }; | ||
| 412 | |||
| 413 | SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_BLEND); | ||
| 414 | SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, opacity); | ||
| 415 | SDL_RenderFillRect(ren_.get(), &signRect); | ||
| 416 | |||
| 417 | if (game.sign.signDisplayState == SignInstructionState::Visible) { | ||
| 418 | int lineIndex = 0; | ||
| 419 | for (const SignLine& line : game.sign.linesToShow) { | ||
| 420 | if (messageLines_[lineIndex].line != line.text) { | ||
| 421 | renderMessageLine(messageLines_[lineIndex], line.text, game); | ||
| 422 | } | ||
| 423 | |||
| 424 | if (line.charsRevealed > 0) { | ||
| 425 | { | ||
| 426 | SDL_Rect srcRect { | ||
| 427 | 0, 0, messageLines_[lineIndex].charIndexToWidth[line.charsRevealed], | ||
| 428 | TTF_FontHeight(font_.get()) | ||
| 429 | }; | ||
| 430 | SDL_Rect destRect { | ||
| 431 | MESSAGE_MARGIN, | ||
| 432 | GAME_HEIGHT / 2 - (45 * 2 + 1 + MESSAGE_MARGIN * 2) / 2 + MESSAGE_MARGIN + (45 + 1) * lineIndex, | ||
| 433 | srcRect.w, | ||
| 434 | srcRect.h }; | ||
| 435 | |||
| 436 | SDL_SetRenderTarget(ren_.get(), nullptr); | ||
| 437 | SDL_RenderCopy(ren_.get(), messageLines_[lineIndex].renderedTex.get(), &srcRect, &destRect); | ||
| 438 | //std::cout << line.charsRevealed << " (" << messageLines_[lineIndex].charIndexToWidth[line.charsRevealed] << "): " << messageLines_[lineIndex].line.substr(0,line.charsRevealed) << std::endl; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | lineIndex++; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 392 | SDL_RenderPresent(ren_.get()); | 447 | SDL_RenderPresent(ren_.get()); |
| 393 | } | 448 | } |
| 394 | 449 | ||
| @@ -443,3 +498,44 @@ void Renderer::loadTextureFromFile(std::string_view path, texture_ptr& texture) | |||
| 443 | texture = texture_ptr(SDL_CreateTextureFromSurface(ren_.get(), pfs.get())); | 498 | texture = texture_ptr(SDL_CreateTextureFromSurface(ren_.get(), pfs.get())); |
| 444 | SDL_SetTextureBlendMode(texture.get(), SDL_BLENDMODE_BLEND); | 499 | SDL_SetTextureBlendMode(texture.get(), SDL_BLENDMODE_BLEND); |
| 445 | } | 500 | } |
| 501 | |||
| 502 | void Renderer::renderMessageLine(MessageCache& line, const std::string& text, const Game& game) { | ||
| 503 | line.line = text; | ||
| 504 | |||
| 505 | line.renderedTex.reset( | ||
| 506 | SDL_CreateTexture( | ||
| 507 | ren_.get(), | ||
| 508 | SDL_PIXELFORMAT_RGBA8888, | ||
| 509 | SDL_TEXTUREACCESS_TARGET, | ||
| 510 | MESSAGE_TEXT_WIDTH, | ||
| 511 | TTF_FontHeight(font_.get()))); | ||
| 512 | |||
| 513 | SDL_SetTextureBlendMode(line.renderedTex.get(), SDL_BLENDMODE_BLEND); | ||
| 514 | |||
| 515 | SDL_SetRenderTarget(ren_.get(), line.renderedTex.get()); | ||
| 516 | SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_BLEND); | ||
| 517 | SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0); | ||
| 518 | SDL_RenderClear(ren_.get()); | ||
| 519 | |||
| 520 | line.charIndexToWidth.clear(); | ||
| 521 | line.charIndexToWidth.push_back(0); | ||
| 522 | |||
| 523 | for (int i=0; i<line.line.size(); i++) { | ||
| 524 | if (line.line[i] != ' ') { | ||
| 525 | int width = 0; | ||
| 526 | std::string substr = line.line.substr(0, i+1); | ||
| 527 | TTF_SizeText(font_.get(), substr.c_str(), &width, nullptr); | ||
| 528 | line.charIndexToWidth.push_back(width); | ||
| 529 | } else { | ||
| 530 | line.charIndexToWidth.push_back(line.charIndexToWidth.back()); | ||
| 531 | } | ||
| 532 | } | ||
| 533 | |||
| 534 | SDL_Color fgColor {.r = 255, .g = 255, .b = 255, .a = 255}; | ||
| 535 | SDL_Color bgColor {.r = 0, .g = 0, .b = 0, .a = 255}; | ||
| 536 | SDL_Rect rect {0, 0, line.charIndexToWidth.back(), TTF_FontHeight(font_.get())}; | ||
| 537 | surface_ptr lineSurf = surface_ptr(TTF_RenderText_Shaded(font_.get(), line.line.c_str(), fgColor, bgColor)); | ||
| 538 | texture_ptr lineTex = texture_ptr(SDL_CreateTextureFromSurface(ren_.get(), lineSurf.get())); | ||
| 539 | SDL_RenderCopy(ren_.get(), lineTex.get(), nullptr, &rect); | ||
| 540 | } | ||
| 541 | |||
| diff --git a/src/renderer.h b/src/renderer.h index de1e125..ce2e7e1 100644 --- a/src/renderer.h +++ b/src/renderer.h | |||
| @@ -3,10 +3,13 @@ | |||
| 3 | 3 | ||
| 4 | #include <SDL.h> | 4 | #include <SDL.h> |
| 5 | #include <SDL_image.h> | 5 | #include <SDL_image.h> |
| 6 | #include <SDL_ttf.h> | ||
| 6 | #include <stdexcept> | 7 | #include <stdexcept> |
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <array> | 9 | #include <array> |
| 9 | #include <string_view> | 10 | #include <string_view> |
| 11 | #include <vector> | ||
| 12 | #include <string> | ||
| 10 | #include "consts.h" | 13 | #include "consts.h" |
| 11 | 14 | ||
| 12 | class Game; | 15 | class Game; |
| @@ -27,6 +30,14 @@ public: | |||
| 27 | } | 30 | } |
| 28 | }; | 31 | }; |
| 29 | 32 | ||
| 33 | class ttf_error : public std::logic_error { | ||
| 34 | public: | ||
| 35 | |||
| 36 | ttf_error() : std::logic_error(TTF_GetError()) | ||
| 37 | { | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | |||
| 30 | class sdl_wrapper { | 41 | class sdl_wrapper { |
| 31 | public: | 42 | public: |
| 32 | 43 | ||
| @@ -67,6 +78,26 @@ public: | |||
| 67 | } | 78 | } |
| 68 | }; | 79 | }; |
| 69 | 80 | ||
| 81 | class ttf_wrapper { | ||
| 82 | public: | ||
| 83 | |||
| 84 | ttf_wrapper() | ||
| 85 | { | ||
| 86 | if (TTF_Init() != 0) | ||
| 87 | { | ||
| 88 | ttf_error ex; | ||
| 89 | TTF_Quit(); | ||
| 90 | |||
| 91 | throw ex; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | ~ttf_wrapper() | ||
| 96 | { | ||
| 97 | TTF_Quit(); | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | |||
| 70 | class window_deleter { | 101 | class window_deleter { |
| 71 | public: | 102 | public: |
| 72 | 103 | ||
| @@ -111,6 +142,16 @@ public: | |||
| 111 | 142 | ||
| 112 | using texture_ptr = std::unique_ptr<SDL_Texture, texture_deleter>; | 143 | using texture_ptr = std::unique_ptr<SDL_Texture, texture_deleter>; |
| 113 | 144 | ||
| 145 | class font_deleter { | ||
| 146 | public: | ||
| 147 | |||
| 148 | void operator()(TTF_Font* ptr) { | ||
| 149 | TTF_CloseFont(ptr); | ||
| 150 | } | ||
| 151 | }; | ||
| 152 | |||
| 153 | using font_ptr = std::unique_ptr<TTF_Font, font_deleter>; | ||
| 154 | |||
| 114 | class Renderer { | 155 | class Renderer { |
| 115 | public: | 156 | public: |
| 116 | 157 | ||
| @@ -122,14 +163,18 @@ public: | |||
| 122 | 163 | ||
| 123 | void renderTitle(int num, double fade); | 164 | void renderTitle(int num, double fade); |
| 124 | 165 | ||
| 166 | TTF_Font* getFont() { return font_.get(); } | ||
| 167 | |||
| 125 | private: | 168 | private: |
| 126 | 169 | ||
| 127 | void loadTextureFromFile(std::string_view path, texture_ptr& texture); | 170 | void loadTextureFromFile(std::string_view path, texture_ptr& texture); |
| 128 | 171 | ||
| 129 | sdl_wrapper sdl_; | 172 | sdl_wrapper sdl_; |
| 130 | img_wrapper img_; | 173 | img_wrapper img_; |
| 174 | ttf_wrapper ttf_; | ||
| 131 | window_ptr win_; | 175 | window_ptr win_; |
| 132 | renderer_ptr ren_; | 176 | renderer_ptr ren_; |
| 177 | font_ptr font_; | ||
| 133 | 178 | ||
| 134 | texture_ptr playerFade_; | 179 | texture_ptr playerFade_; |
| 135 | texture_ptr lampFade_; | 180 | texture_ptr lampFade_; |
| @@ -142,6 +187,18 @@ private: | |||
| 142 | std::array<texture_ptr, NUM_TITLES> titles_; | 187 | std::array<texture_ptr, NUM_TITLES> titles_; |
| 143 | std::array<int, NUM_TITLES> titleWidths_; | 188 | std::array<int, NUM_TITLES> titleWidths_; |
| 144 | std::array<int, NUM_TITLES> titleHeights_; | 189 | std::array<int, NUM_TITLES> titleHeights_; |
| 190 | |||
| 191 | // Text rendering | ||
| 192 | struct MessageCache { | ||
| 193 | texture_ptr renderedTex; | ||
| 194 | std::vector<int> charIndexToWidth; | ||
| 195 | std::string line; | ||
| 196 | std::string overflow; | ||
| 197 | }; | ||
| 198 | |||
| 199 | void renderMessageLine(MessageCache& line, const std::string& text, const Game& game); | ||
| 200 | |||
| 201 | MessageCache messageLines_[2]; | ||
| 145 | }; | 202 | }; |
| 146 | 203 | ||
| 147 | #endif /* end of include guard: RENDERER_H_6A58EC30 */ | 204 | #endif /* end of include guard: RENDERER_H_6A58EC30 */ |
| diff --git a/src/sign.cpp b/src/sign.cpp new file mode 100644 index 0000000..992ac3d --- /dev/null +++ b/src/sign.cpp | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | #include "sign.h" | ||
| 2 | #include <list> | ||
| 3 | #include "util.h" | ||
| 4 | #include "consts.h" | ||
| 5 | #include "game.h" | ||
| 6 | |||
| 7 | void Sign::displayMessage(std::string text) { | ||
| 8 | signDisplayState = SignInstructionState::FadingIn; | ||
| 9 | lines.clear(); | ||
| 10 | |||
| 11 | auto lineChunks = splitStr<std::list<std::string>>(text, "\\n"); | ||
| 12 | for (std::string lineChunk : lineChunks) { | ||
| 13 | auto words = splitStr<std::list<std::string>>(lineChunk, " "); | ||
| 14 | std::string prev = words.front(); | ||
| 15 | words.pop_front(); | ||
| 16 | std::string cur = prev; | ||
| 17 | bool pauseLine = false; | ||
| 18 | |||
| 19 | while (!words.empty()) { | ||
| 20 | cur = prev + " " + words.front(); | ||
| 21 | |||
| 22 | int width = 0; | ||
| 23 | TTF_SizeText(font_, cur.c_str(), &width, nullptr); | ||
| 24 | if (width > MESSAGE_TEXT_WIDTH) { | ||
| 25 | lines.push_back({.text = prev, .pause = pauseLine}); | ||
| 26 | pauseLine = !pauseLine; | ||
| 27 | cur = words.front(); | ||
| 28 | } else if (words.size() == 1) { | ||
| 29 | lines.push_back({.text = cur, .pause = true}); | ||
| 30 | } | ||
| 31 | |||
| 32 | prev = cur; | ||
| 33 | words.pop_front(); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | lines.back().pause = true; | ||
| 38 | linesToShow.push_back(lines.front()); | ||
| 39 | lines.pop_front(); | ||
| 40 | |||
| 41 | if (!linesToShow.back().pause) { | ||
| 42 | linesToShow.push_back(lines.front()); | ||
| 43 | lines.pop_front(); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | void Sign::update(size_t dt, Game& game) { | ||
| 48 | SDL_Event e; | ||
| 49 | |||
| 50 | while (SDL_PollEvent(&e)) { | ||
| 51 | if (e.type == SDL_QUIT) { | ||
| 52 | game.quit = true; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | switch (signDisplayState) { | ||
| 57 | case SignInstructionState::Hidden: { | ||
| 58 | // Shouldn't happen. | ||
| 59 | break; | ||
| 60 | } | ||
| 61 | case SignInstructionState::FadingIn: { | ||
| 62 | signDisplayFade.tick(dt); | ||
| 63 | if (signDisplayFade.isComplete()) { | ||
| 64 | signDisplayState = SignInstructionState::Visible; | ||
| 65 | } | ||
| 66 | |||
| 67 | break; | ||
| 68 | } | ||
| 69 | case SignInstructionState::FadingOut: { | ||
| 70 | signDisplayFade.tick(dt); | ||
| 71 | if (signDisplayFade.isComplete()) { | ||
| 72 | signDisplayState = SignInstructionState::Hidden; | ||
| 73 | } | ||
| 74 | |||
| 75 | break; | ||
| 76 | } | ||
| 77 | case SignInstructionState::Visible: { | ||
| 78 | const Uint8* state = SDL_GetKeyboardState(NULL); | ||
| 79 | if (state[SDL_SCANCODE_SPACE]) { | ||
| 80 | bool fullyRevealed = true; | ||
| 81 | for (const SignLine& line : linesToShow) { | ||
| 82 | if (line.charsRevealed != line.text.size()) { | ||
| 83 | fullyRevealed = false; | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | if (fullyRevealed) { | ||
| 89 | if (linesToShow.back().pause) { | ||
| 90 | linesToShow.back().pause = false; | ||
| 91 | // Play a sound | ||
| 92 | } | ||
| 93 | if (lines.empty()) { | ||
| 94 | linesToShow.clear(); | ||
| 95 | signDisplayState = SignInstructionState::FadingOut; | ||
| 96 | signDisplayFade.start(1000); | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | textAdvTimer_.accumulate(dt); | ||
| 103 | while (textAdvTimer_.step()) { | ||
| 104 | bool advancedChars = false; | ||
| 105 | for (SignLine& line : linesToShow) { | ||
| 106 | if (line.charsRevealed < line.text.size()) { | ||
| 107 | if (line.charsRevealed % CHARS_PER_BEEP == 0) { | ||
| 108 | // Play a sound | ||
| 109 | game.muxer.playSound("textbeep"); | ||
| 110 | } | ||
| 111 | line.charsRevealed++; | ||
| 112 | advancedChars = true; | ||
| 113 | break; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | if (!advancedChars) { | ||
| 117 | if (!lines.empty() && !linesToShow.back().pause) { | ||
| 118 | if (linesToShow.size() == 2) { | ||
| 119 | linesToShow.pop_front(); | ||
| 120 | } | ||
| 121 | linesToShow.push_back(lines.front()); | ||
| 122 | lines.pop_front(); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | break; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| diff --git a/src/sign.h b/src/sign.h new file mode 100644 index 0000000..c90a8fd --- /dev/null +++ b/src/sign.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | #ifndef SIGN_H_B0491849 | ||
| 2 | #define SIGN_H_B0491849 | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | #include <list> | ||
| 6 | #include <SDL_ttf.h> | ||
| 7 | #include "interpolation.h" | ||
| 8 | #include "timer.h" | ||
| 9 | |||
| 10 | class Game; | ||
| 11 | |||
| 12 | enum class SignInstructionState { | ||
| 13 | Hidden, | ||
| 14 | FadingIn, | ||
| 15 | Visible, | ||
| 16 | FadingOut | ||
| 17 | }; | ||
| 18 | |||
| 19 | struct SignLine { | ||
| 20 | std::string text; | ||
| 21 | bool pause = false; | ||
| 22 | int charsRevealed = 0; | ||
| 23 | }; | ||
| 24 | |||
| 25 | class Sign { | ||
| 26 | public: | ||
| 27 | |||
| 28 | explicit Sign(TTF_Font* font) : font_(font) {} | ||
| 29 | |||
| 30 | void displayMessage(std::string text); | ||
| 31 | |||
| 32 | void update(size_t dt, Game& game); | ||
| 33 | |||
| 34 | SignInstructionState signDisplayState = SignInstructionState::Hidden; | ||
| 35 | Interpolation signDisplayFade; | ||
| 36 | std::list<SignLine> lines; | ||
| 37 | std::list<SignLine> linesToShow; | ||
| 38 | |||
| 39 | private: | ||
| 40 | |||
| 41 | TTF_Font* font_; | ||
| 42 | Timer textAdvTimer_ { 15 }; | ||
| 43 | }; | ||
| 44 | |||
| 45 | #endif /* end of include guard: SIGN_H_B0491849 */ | ||
