summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/consts.h3
-rw-r--r--src/game.cpp33
-rw-r--r--src/game.h15
-rw-r--r--src/main.cpp2
-rw-r--r--src/muxer.cpp12
-rw-r--r--src/muxer.h2
-rw-r--r--src/renderer.cpp96
-rw-r--r--src/renderer.h57
-rw-r--r--src/sign.cpp130
-rw-r--r--src/sign.h45
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;
12constexpr int CHUNK_HEIGHT = 80; 12constexpr int CHUNK_HEIGHT = 80;
13constexpr int RADIUS = 8; 13constexpr int RADIUS = 8;
14constexpr int NUM_TITLES = 1; 14constexpr int NUM_TITLES = 1;
15constexpr int MESSAGE_MARGIN = 64;
16constexpr int MESSAGE_TEXT_WIDTH = GAME_WIDTH - MESSAGE_MARGIN * 2;
17constexpr 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
10Game::Game(std::mt19937& rng, Muxer& muxer) : 10Game::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
634void Game::update(size_t frameTime) { 635void 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
932void 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
16class Renderer;
14 17
15constexpr int TilesetIndex(int x, int y) { 18constexpr 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
26enum class SignInstructionState {
27 Hidden,
28 FadingIn,
29 Visible,
30 FadingOut
31};
32
33struct Input { 29struct Input {
34 bool left = false; 30 bool left = false;
35 bool right = false; 31 bool right = false;
@@ -58,7 +54,7 @@ struct Kickup {
58class Game { 54class Game {
59public: 55public:
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
115private: 112private:
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
45void 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
45void Muxer::playSoundAtPosition(std::string name, float x, float y) { 57void 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
4Renderer::Renderer() 5Renderer::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
118void Renderer::renderGame( 124void 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
502void 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
12class Game; 15class Game;
@@ -27,6 +30,14 @@ public:
27 } 30 }
28}; 31};
29 32
33class ttf_error : public std::logic_error {
34public:
35
36 ttf_error() : std::logic_error(TTF_GetError())
37 {
38 }
39};
40
30class sdl_wrapper { 41class sdl_wrapper {
31public: 42public:
32 43
@@ -67,6 +78,26 @@ public:
67 } 78 }
68}; 79};
69 80
81class ttf_wrapper {
82public:
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
70class window_deleter { 101class window_deleter {
71public: 102public:
72 103
@@ -111,6 +142,16 @@ public:
111 142
112using texture_ptr = std::unique_ptr<SDL_Texture, texture_deleter>; 143using texture_ptr = std::unique_ptr<SDL_Texture, texture_deleter>;
113 144
145class font_deleter {
146public:
147
148 void operator()(TTF_Font* ptr) {
149 TTF_CloseFont(ptr);
150 }
151};
152
153using font_ptr = std::unique_ptr<TTF_Font, font_deleter>;
154
114class Renderer { 155class Renderer {
115public: 156public:
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
125private: 168private:
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
7void 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
47void 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
10class Game;
11
12enum class SignInstructionState {
13 Hidden,
14 FadingIn,
15 Visible,
16 FadingOut
17};
18
19struct SignLine {
20 std::string text;
21 bool pause = false;
22 int charsRevealed = 0;
23};
24
25class Sign {
26public:
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
39private:
40
41 TTF_Font* font_;
42 Timer textAdvTimer_ { 15 };
43};
44
45#endif /* end of include guard: SIGN_H_B0491849 */