diff options
-rw-r--r-- | res/scripts/common.lua | 8 | ||||
-rw-r--r-- | res/scripts/script0001.lua | 10 | ||||
-rw-r--r-- | res/sfx/horizontal_menu.wav | bin | 0 -> 52864 bytes | |||
-rw-r--r-- | src/input_system.cpp | 8 | ||||
-rw-r--r-- | src/message_system.cpp | 82 | ||||
-rw-r--r-- | src/message_system.h | 26 | ||||
-rw-r--r-- | src/renderer.cpp | 28 | ||||
-rw-r--r-- | src/script_system.cpp | 4 |
8 files changed, 127 insertions, 39 deletions
diff --git a/res/scripts/common.lua b/res/scripts/common.lua index b540548..2023e63 100644 --- a/res/scripts/common.lua +++ b/res/scripts/common.lua | |||
@@ -36,6 +36,14 @@ function DisplayMessage(msg, name, type) | |||
36 | message():displayMessage(msg, name, type) | 36 | message():displayMessage(msg, name, type) |
37 | end | 37 | end |
38 | 38 | ||
39 | function ShowChoice(one, two) | ||
40 | message():showChoice(one, two) | ||
41 | end | ||
42 | |||
43 | function GetChoiceSelection() | ||
44 | return message():getChoiceSelection() | ||
45 | end | ||
46 | |||
39 | function WaitForEndOfMessage() | 47 | function WaitForEndOfMessage() |
40 | while (message().isMessageActive) do | 48 | while (message().isMessageActive) do |
41 | coroutine.yield() | 49 | coroutine.yield() |
diff --git a/res/scripts/script0001.lua b/res/scripts/script0001.lua index e5078f2..077e809 100644 --- a/res/scripts/script0001.lua +++ b/res/scripts/script0001.lua | |||
@@ -3,11 +3,19 @@ function script0001() | |||
3 | SetAnimation("boney", "barking") | 3 | SetAnimation("boney", "barking") |
4 | local barkingNoise = LoopSound("barking_at_hallucination.wav") | 4 | local barkingNoise = LoopSound("barking_at_hallucination.wav") |
5 | 5 | ||
6 | DisplayMessage("Lucas. It's me, Flint. Your father.\n\fI found Claus. He's here. After three years I've finally found your brother.\n\fLook at me when I'm talking to you, Lucas.\n\fWhen Claus gets home, we won't need you anymore.\nYou're nothing compared to him.", "Flint", SpeakerType.MAN) | 6 | DisplayMessage("Lucas. It's me, Flint. Your father.\n\fI found Claus. He's here. After three years I've finally found your brother.\n\fAre you ready to see him again?", "Flint", SpeakerType.MAN) |
7 | ShowChoice("Yes", "No") | ||
7 | WaitForEndOfMessage() | 8 | WaitForEndOfMessage() |
8 | 9 | ||
9 | SetAnimation("boney", "crouch") | 10 | SetAnimation("boney", "crouch") |
10 | StopSound(barkingNoise) | 11 | StopSound(barkingNoise) |
11 | PlaySound("boney_growl.wav") | 12 | PlaySound("boney_growl.wav") |
13 | |||
14 | if GetChoiceSelection() == 0 then | ||
15 | DisplayMessage("I can hear the tremor in your voice, Lucas.\n\fLies are little games a mouth plays while the person attached withers away slowly.\n\fClaus is coming, whether you like it or not.\nWill you be able to look him in the eye?", "Flint", SpeakerType.MAN) | ||
16 | else | ||
17 | DisplayMessage("Look at me when I'm talking to you, Lucas.\n\fWhen Claus gets home, we won't need you anymore.\nYou're nothing compared to him.", "Flint", SpeakerType.MAN) | ||
18 | end | ||
19 | |||
12 | HideCutsceneBars() | 20 | HideCutsceneBars() |
13 | end | 21 | end |
diff --git a/res/sfx/horizontal_menu.wav b/res/sfx/horizontal_menu.wav new file mode 100644 index 0000000..cf9595d --- /dev/null +++ b/res/sfx/horizontal_menu.wav | |||
Binary files differ | |||
diff --git a/src/input_system.cpp b/src/input_system.cpp index 5158724..6ee442a 100644 --- a/src/input_system.cpp +++ b/src/input_system.cpp | |||
@@ -73,6 +73,14 @@ void InputSystem::tick(double dt) { | |||
73 | game_.getSystem<ScriptSystem>().runScript("default"); | 73 | game_.getSystem<ScriptSystem>().runScript("default"); |
74 | } | 74 | } |
75 | } | 75 | } |
76 | } else if (e.key.keysym.sym == SDLK_LEFT) { | ||
77 | if (game_.getSystem<MessageSystem>().isChoiceActive()) { | ||
78 | game_.getSystem<MessageSystem>().selectFirstChoice(); | ||
79 | } | ||
80 | } else if (e.key.keysym.sym == SDLK_RIGHT) { | ||
81 | if (game_.getSystem<MessageSystem>().isChoiceActive()) { | ||
82 | game_.getSystem<MessageSystem>().selectSecondChoice(); | ||
83 | } | ||
76 | } | 84 | } |
77 | } else if (e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_LSHIFT || e.key.keysym.sym == SDLK_RSHIFT)) { | 85 | } else if (e.type == SDL_KEYUP && (e.key.keysym.sym == SDLK_LSHIFT || e.key.keysym.sym == SDLK_RSHIFT)) { |
78 | for (int spriteId : game_.getSprites()) { | 86 | for (int spriteId : game_.getSprites()) { |
diff --git a/src/message_system.cpp b/src/message_system.cpp index a200abc..a969427 100644 --- a/src/message_system.cpp +++ b/src/message_system.cpp | |||
@@ -39,10 +39,16 @@ void MessageSystem::tick(double dt) { | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | 41 | ||
42 | line.charsRevealed += CHARS_TO_REVEAL; | 42 | if (line.isChoice) { |
43 | if (line.charsRevealed > line.text.size()) { | ||
44 | line.charsRevealed = line.text.size(); | 43 | line.charsRevealed = line.text.size(); |
44 | choiceSelection_ = 0; | ||
45 | } else { | ||
46 | line.charsRevealed += CHARS_TO_REVEAL; | ||
47 | if (line.charsRevealed > line.text.size()) { | ||
48 | line.charsRevealed = line.text.size(); | ||
49 | } | ||
45 | } | 50 | } |
51 | |||
46 | advancedChars = true; | 52 | advancedChars = true; |
47 | break; | 53 | break; |
48 | } | 54 | } |
@@ -50,14 +56,14 @@ void MessageSystem::tick(double dt) { | |||
50 | 56 | ||
51 | if (!advancedChars) { | 57 | if (!advancedChars) { |
52 | // If both lines are totally revealed, see if we can scroll up a line. | 58 | // If both lines are totally revealed, see if we can scroll up a line. |
53 | // This is doable as long as the next line isn't the sentinel value that | 59 | // This is doable as long as the last currently visible line doesn't |
54 | // means an A press is required. | 60 | // have the flag that means an A press is required. |
55 | if (!lines_.empty() && lines_.front() != "\f") { | 61 | if (!lines_.empty() && !linesToShow_.back().pause) { |
56 | if (linesToShow_.size() == 2) { | 62 | if (linesToShow_.size() == 2) { |
57 | linesToShow_.pop_front(); | 63 | linesToShow_.pop_front(); |
58 | } | 64 | } |
59 | 65 | ||
60 | linesToShow_.push_back(MessageLine { .text = lines_.front() }); | 66 | linesToShow_.push_back(lines_.front()); |
61 | lines_.pop_front(); | 67 | lines_.pop_front(); |
62 | } else { | 68 | } else { |
63 | showNextArrow_ = true; | 69 | showNextArrow_ = true; |
@@ -113,8 +119,8 @@ void MessageSystem::displayMessage(std::string_view msg, std::string speakerName | |||
113 | text.erase(0, 1); | 119 | text.erase(0, 1); |
114 | shouldAddBlank = false; | 120 | shouldAddBlank = false; |
115 | 121 | ||
116 | if (lines_.empty() || lines_.back() != "\f") { | 122 | if (!lines_.empty()) { |
117 | lines_.push_back("\f"); | 123 | lines_.back().pause = true; |
118 | } | 124 | } |
119 | } | 125 | } |
120 | 126 | ||
@@ -139,13 +145,13 @@ void MessageSystem::displayMessage(std::string_view msg, std::string speakerName | |||
139 | } | 145 | } |
140 | 146 | ||
141 | if (nextWidth > MESSAGE_TEXT_WIDTH) { | 147 | if (nextWidth > MESSAGE_TEXT_WIDTH) { |
142 | lines_.push_back(curLine); | 148 | lines_.push_back({.text = curLine}); |
143 | curLine = word; | 149 | curLine = word; |
144 | curWidth = wordWidth + game_.getFont().getCharacterWidth(' '); | 150 | curWidth = wordWidth + game_.getFont().getCharacterWidth(' '); |
145 | 151 | ||
146 | if (shouldAddBlank) { | 152 | if (shouldAddBlank) { |
147 | shouldAddBlank = false; | 153 | shouldAddBlank = false; |
148 | lines_.push_back("\f"); | 154 | lines_.back().pause = true; |
149 | } else { | 155 | } else { |
150 | shouldAddBlank = true; | 156 | shouldAddBlank = true; |
151 | } | 157 | } |
@@ -160,52 +166,66 @@ void MessageSystem::displayMessage(std::string_view msg, std::string speakerName | |||
160 | firstWord = false; | 166 | firstWord = false; |
161 | } | 167 | } |
162 | 168 | ||
163 | lines_.push_back(curLine); | 169 | lines_.push_back({.text = curLine}); |
164 | 170 | ||
165 | if (shouldAddBlank) { | 171 | if (shouldAddBlank) { |
166 | shouldAddBlank = false; | 172 | shouldAddBlank = false; |
167 | lines_.push_back("\f"); | 173 | lines_.back().pause = true; |
168 | } else { | 174 | } else { |
169 | shouldAddBlank = true; | 175 | shouldAddBlank = true; |
170 | } | 176 | } |
171 | } | 177 | } |
172 | 178 | ||
173 | if (lines_.empty() || lines_.back() != "\f") { | 179 | if (!lines_.empty()) { |
174 | lines_.push_back("\f"); | 180 | lines_.back().pause = true; |
175 | } | 181 | } |
176 | 182 | ||
177 | if (linesToShow_.empty()) { | 183 | if (linesToShow_.empty()) { |
178 | linesToShow_.push_back(MessageLine { .text = lines_.front() }); | 184 | linesToShow_.push_back(lines_.front()); |
179 | lines_.pop_front(); | 185 | lines_.pop_front(); |
180 | 186 | ||
181 | if (lines_.front() != "\f") { | 187 | if (!linesToShow_.back().pause) { |
182 | linesToShow_.push_back(MessageLine { .text = lines_.front() }); | 188 | linesToShow_.push_back(lines_.front()); |
183 | lines_.pop_front(); | 189 | lines_.pop_front(); |
184 | } | 190 | } |
185 | } | 191 | } |
186 | } | 192 | } |
187 | 193 | ||
194 | void MessageSystem::showChoice(std::string choice1, std::string choice2) { | ||
195 | MessageLine line; | ||
196 | line.text = std::string(8, ' ') + choice1 + std::string(11, ' ') + choice2; | ||
197 | line.pause = true; | ||
198 | line.isChoice = true; | ||
199 | line.choicePos[0] = 8; | ||
200 | line.choicePos[1] = 8 + 11 + choice1.size(); | ||
201 | |||
202 | lines_.push_back(line); | ||
203 | } | ||
204 | |||
188 | void MessageSystem::advanceText() { | 205 | void MessageSystem::advanceText() { |
206 | // Cutscene must be active. | ||
189 | if (barsState_ != BarsState::Open) { | 207 | if (barsState_ != BarsState::Open) { |
190 | return; | 208 | return; |
191 | } | 209 | } |
210 | if (linesToShow_.empty()) { | ||
211 | return; | ||
212 | } | ||
192 | 213 | ||
214 | // We can only advance if all visible lines are fully revealed. | ||
193 | for (const MessageLine& line : linesToShow_) { | 215 | for (const MessageLine& line : linesToShow_) { |
194 | if (line.charsRevealed != line.text.size()) { | 216 | if (line.charsRevealed != line.text.size()) { |
195 | return; | 217 | return; |
196 | } | 218 | } |
197 | } | 219 | } |
198 | 220 | ||
199 | if (lines_.empty()) { | 221 | // If the last visible line doesn't have a pause, then the cutscene |
200 | linesToShow_.clear(); | 222 | // will automatically progress without us doing anything. |
223 | if (!linesToShow_.back().pause) { | ||
201 | return; | 224 | return; |
202 | } | 225 | } |
203 | 226 | ||
204 | if (lines_.front() != "\f") { | 227 | // Clear the current pause. |
205 | return; | 228 | linesToShow_.back().pause = false; |
206 | } | ||
207 | |||
208 | lines_.pop_front(); | ||
209 | showNextArrow_ = false; | 229 | showNextArrow_ = false; |
210 | 230 | ||
211 | if (lines_.empty()) { | 231 | if (lines_.empty()) { |
@@ -215,6 +235,20 @@ void MessageSystem::advanceText() { | |||
215 | } | 235 | } |
216 | } | 236 | } |
217 | 237 | ||
238 | void MessageSystem::selectFirstChoice() { | ||
239 | if (isChoiceActive() && choiceSelection_ != 0) { | ||
240 | choiceSelection_ = 0; | ||
241 | game_.getMixer().playSound("../res/sfx/horizontal_menu.wav"); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | void MessageSystem::selectSecondChoice() { | ||
246 | if (isChoiceActive() && choiceSelection_ != 1) { | ||
247 | choiceSelection_ = 1; | ||
248 | game_.getMixer().playSound("../res/sfx/horizontal_menu.wav"); | ||
249 | } | ||
250 | } | ||
251 | |||
218 | double MessageSystem::getCutsceneBarsProgress() const { | 252 | double MessageSystem::getCutsceneBarsProgress() const { |
219 | switch (barsState_) { | 253 | switch (barsState_) { |
220 | case BarsState::Closed: return 0.0; | 254 | case BarsState::Closed: return 0.0; |
diff --git a/src/message_system.h b/src/message_system.h index 155f9c6..ea27f14 100644 --- a/src/message_system.h +++ b/src/message_system.h | |||
@@ -18,6 +18,14 @@ enum class SpeakerType { | |||
18 | Nonhuman | 18 | Nonhuman |
19 | }; | 19 | }; |
20 | 20 | ||
21 | struct MessageLine { | ||
22 | std::string text; | ||
23 | int charsRevealed = 0; | ||
24 | bool pause = false; | ||
25 | bool isChoice = false; | ||
26 | int choicePos[2] = {-1, -1}; | ||
27 | }; | ||
28 | |||
21 | class MessageSystem : public System { | 29 | class MessageSystem : public System { |
22 | public: | 30 | public: |
23 | 31 | ||
@@ -41,17 +49,18 @@ public: | |||
41 | std::string speakerName = "", | 49 | std::string speakerName = "", |
42 | SpeakerType speakerType = SpeakerType::None); | 50 | SpeakerType speakerType = SpeakerType::None); |
43 | 51 | ||
52 | void showChoice(std::string choice1, std::string choice2); | ||
53 | |||
44 | void advanceText(); | 54 | void advanceText(); |
45 | 55 | ||
56 | void selectFirstChoice(); | ||
57 | |||
58 | void selectSecondChoice(); | ||
59 | |||
46 | // Info | 60 | // Info |
47 | 61 | ||
48 | double getCutsceneBarsProgress() const; | 62 | double getCutsceneBarsProgress() const; |
49 | 63 | ||
50 | struct MessageLine { | ||
51 | std::string text; | ||
52 | int charsRevealed = 0; | ||
53 | }; | ||
54 | |||
55 | const std::list<MessageLine>& getLines() const { return linesToShow_; } | 64 | const std::list<MessageLine>& getLines() const { return linesToShow_; } |
56 | 65 | ||
57 | bool isMessageActive() const { return !linesToShow_.empty(); } | 66 | bool isMessageActive() const { return !linesToShow_.empty(); } |
@@ -62,6 +71,10 @@ public: | |||
62 | 71 | ||
63 | int getNextArrowBob() const { return nextArrowBobPos_; } | 72 | int getNextArrowBob() const { return nextArrowBobPos_; } |
64 | 73 | ||
74 | bool isChoiceActive() const { return isMessageActive() && linesToShow_.back().isChoice; } | ||
75 | |||
76 | int getChoiceSelection() const { return choiceSelection_; } | ||
77 | |||
65 | private: | 78 | private: |
66 | 79 | ||
67 | enum class BarsState { | 80 | enum class BarsState { |
@@ -77,13 +90,14 @@ private: | |||
77 | double length_ = 1000.0/8; | 90 | double length_ = 1000.0/8; |
78 | SpeakerType speaker_ = SpeakerType::None; | 91 | SpeakerType speaker_ = SpeakerType::None; |
79 | std::string speakerName_; | 92 | std::string speakerName_; |
80 | std::list<std::string> lines_; | 93 | std::list<MessageLine> lines_; |
81 | std::list<MessageLine> linesToShow_; | 94 | std::list<MessageLine> linesToShow_; |
82 | Timer textAdvTimer_ { 15 }; | 95 | Timer textAdvTimer_ { 15 }; |
83 | bool showNextArrow_ = false; | 96 | bool showNextArrow_ = false; |
84 | int nextArrowBobPos_ = 0; | 97 | int nextArrowBobPos_ = 0; |
85 | bool nextArrowBobDown_ = true; | 98 | bool nextArrowBobDown_ = true; |
86 | Timer nextArrowBobTimer_ { 125 }; | 99 | Timer nextArrowBobTimer_ { 125 }; |
100 | int choiceSelection_ = 0; | ||
87 | }; | 101 | }; |
88 | 102 | ||
89 | #endif /* end of include guard: MESSAGE_SYSTEM_H_DE10D011 */ | 103 | #endif /* end of include guard: MESSAGE_SYSTEM_H_DE10D011 */ |
diff --git a/src/renderer.cpp b/src/renderer.cpp index db5daed..0c70ef5 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp | |||
@@ -194,7 +194,7 @@ void Renderer::render(Game& game) { | |||
194 | } | 194 | } |
195 | 195 | ||
196 | int lineIndex = 0; | 196 | int lineIndex = 0; |
197 | for (const MessageSystem::MessageLine& line : game.getSystem<MessageSystem>().getLines()) { | 197 | for (const MessageLine& line : game.getSystem<MessageSystem>().getLines()) { |
198 | if (messageLines_[lineIndex].line != line.text) { | 198 | if (messageLines_[lineIndex].line != line.text) { |
199 | renderMessageLine(messageLines_[lineIndex], line.text, game); | 199 | renderMessageLine(messageLines_[lineIndex], line.text, game); |
200 | } | 200 | } |
@@ -223,13 +223,27 @@ void Renderer::render(Game& game) { | |||
223 | advMsgArrowTex_ = loadImageFromFile("../res/advance_text_arrow.png"); | 223 | advMsgArrowTex_ = loadImageFromFile("../res/advance_text_arrow.png"); |
224 | } | 224 | } |
225 | 225 | ||
226 | SDL_Rect destRect { | 226 | if (game.getSystem<MessageSystem>().isChoiceActive()) { |
227 | 216, | 227 | int selection = game.getSystem<MessageSystem>().getChoiceSelection(); |
228 | 138 + game.getSystem<MessageSystem>().getNextArrowBob(), | 228 | int charIndex = game.getSystem<MessageSystem>().getLines().back().choicePos[selection]; |
229 | 16, | 229 | int baseX = messageLines_[1].charIndexToWidth[charIndex]; |
230 | 16 }; | ||
231 | 230 | ||
232 | SDL_RenderCopy(ren_.get(), textures_.at(advMsgArrowTex_).get(), nullptr, &destRect); | 231 | SDL_Rect destRect { |
232 | 18 + baseX - game.getSystem<MessageSystem>().getNextArrowBob()-16+3, | ||
233 | 141, | ||
234 | 16, | ||
235 | 16}; | ||
236 | |||
237 | SDL_RenderCopyEx(ren_.get(), textures_.at(advMsgArrowTex_).get(), nullptr, &destRect, -90, nullptr, SDL_FLIP_NONE); | ||
238 | } else { | ||
239 | SDL_Rect destRect { | ||
240 | 216, | ||
241 | 138 + game.getSystem<MessageSystem>().getNextArrowBob(), | ||
242 | 16, | ||
243 | 16 }; | ||
244 | |||
245 | SDL_RenderCopy(ren_.get(), textures_.at(advMsgArrowTex_).get(), nullptr, &destRect); | ||
246 | } | ||
233 | } | 247 | } |
234 | } | 248 | } |
235 | } | 249 | } |
diff --git a/src/script_system.cpp b/src/script_system.cpp index 0fa0c1b..21d4090 100644 --- a/src/script_system.cpp +++ b/src/script_system.cpp | |||
@@ -21,8 +21,10 @@ ScriptSystem::ScriptSystem(Game& game) : game_(game) { | |||
21 | engine_.new_usertype<MessageSystem>( | 21 | engine_.new_usertype<MessageSystem>( |
22 | "message", | 22 | "message", |
23 | "displayMessage", &MessageSystem::displayMessage, | 23 | "displayMessage", &MessageSystem::displayMessage, |
24 | "showChoice", &MessageSystem::showChoice, | ||
24 | "hideCutsceneBars", &MessageSystem::hideCutsceneBars, | 25 | "hideCutsceneBars", &MessageSystem::hideCutsceneBars, |
25 | "isMessageActive", sol::property(&MessageSystem::isMessageActive)); | 26 | "isMessageActive", sol::property(&MessageSystem::isMessageActive), |
27 | "getChoiceSelection", &MessageSystem::getChoiceSelection); | ||
26 | 28 | ||
27 | engine_.new_usertype<AnimationSystem>( | 29 | engine_.new_usertype<AnimationSystem>( |
28 | "animation", | 30 | "animation", |