From 2c75d95ddf849996bfc18267a9eecb4d0f4e1916 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 16 Mar 2022 15:30:37 -0400 Subject: signs can be read now! --- CMakeLists.txt | 4 + cmake/FindSDL2_TTF.cmake | 98 ++++++++++++++++ res/childoflight.txt | 34 ++---- res/fmod/.cache/fsbcache/Desktop/71C77FCD.fobj | Bin 0 -> 2312 bytes .../{9eeec59c-fe90-499a-a390-6175f2fa58e8}.pdc | Bin 0 -> 1136 bytes ...{68372a81-e8b2-4b54-b8a4-93de302bcc94}.user.xml | 34 ++++++ res/fmod/Assets/Attack02.wav | Bin 0 -> 27208 bytes .../{9eeec59c-fe90-499a-a390-6175f2fa58e8}.xml | 20 ++++ .../{68372a81-e8b2-4b54-b8a4-93de302bcc94}.xml | 128 ++++++++++++++++++++ res/softsquare.ttf | Bin 0 -> 14056 bytes src/consts.h | 3 + src/game.cpp | 33 +++++- src/game.h | 15 ++- src/main.cpp | 2 +- src/muxer.cpp | 12 ++ src/muxer.h | 2 + src/renderer.cpp | 96 +++++++++++++++ src/renderer.h | 57 +++++++++ src/sign.cpp | 130 +++++++++++++++++++++ src/sign.h | 45 +++++++ 20 files changed, 675 insertions(+), 38 deletions(-) create mode 100644 cmake/FindSDL2_TTF.cmake create mode 100644 res/fmod/.cache/fsbcache/Desktop/71C77FCD.fobj create mode 100644 res/fmod/.cache/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.pdc create mode 100644 res/fmod/.user/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.user.xml create mode 100644 res/fmod/Assets/Attack02.wav create mode 100644 res/fmod/Metadata/AudioFile/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.xml create mode 100644 res/fmod/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.xml create mode 100644 res/softsquare.ttf create mode 100644 src/sign.cpp create mode 100644 src/sign.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f07e97..a40e14f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,12 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${Ether_SOURCE_DIR}/cmake") find_package(SDL2 REQUIRED) find_package(SDL2_Image REQUIRED) +find_package(SDL2_TTF REQUIRED) set(ALL_LIBS ${SDL2_LIBRARY} ${SDL2_IMAGE_LIBRARIES} + ${SDL2_TTF_LIBRARIES} fmodstudio fmod ) @@ -19,6 +21,7 @@ set(ALL_LIBS include_directories( ${SDL2_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIRS} + ${SDL2_TTF_INCLUDE_DIRS} src vendor vendor/fmod/inc @@ -34,6 +37,7 @@ add_executable(Ether src/muxer.cpp src/animation.cpp src/game.cpp + src/sign.cpp vendor/fov.c ) diff --git a/cmake/FindSDL2_TTF.cmake b/cmake/FindSDL2_TTF.cmake new file mode 100644 index 0000000..8b2edcb --- /dev/null +++ b/cmake/FindSDL2_TTF.cmake @@ -0,0 +1,98 @@ +# Locate SDL_ttf library +# +# This module defines: +# +# :: +# +# SDL2_TTF_LIBRARIES, the name of the library to link against +# SDL2_TTF_INCLUDE_DIRS, where to find the headers +# SDL2_TTF_FOUND, if false, do not try to link against +# SDL2_TTF_VERSION_STRING - human-readable string containing the version of SDL_ttf +# +# +# +# For backward compatibility the following variables are also set: +# +# :: +# +# SDLTTF_LIBRARY (same value as SDL2_TTF_LIBRARIES) +# SDLTTF_INCLUDE_DIR (same value as SDL2_TTF_INCLUDE_DIRS) +# SDLTTF_FOUND (same value as SDL2_TTF_FOUND) +# +# +# +# $SDLDIR is an environment variable that would correspond to the +# ./configure --prefix=$SDLDIR used in building SDL. +# +# Created by Eric Wing. This was influenced by the FindSDL.cmake +# module, but with modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). + +#============================================================================= +# Copyright 2005-2009 Kitware, Inc. +# Copyright 2012 Benjamin Eikel +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +find_path(SDL2_TTF_INCLUDE_DIR SDL_ttf.h + HINTS + ENV SDL2TTFDIR + ENV SDL2DIR + PATH_SUFFIXES SDL2 + # path suffixes to search inside ENV{SDLDIR} + include/SDL2 include + PATHS ${SDL2_TTF_PATH} + ) + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(VC_LIB_PATH_SUFFIX lib/x64) +else () + set(VC_LIB_PATH_SUFFIX lib/x86) +endif () + +find_library(SDL2_TTF_LIBRARY + NAMES SDL2_ttf + HINTS + ENV SDL2TTFDIR + ENV SDL2DIR + PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} + PATHS ${SDL2_TTF_PATH} + ) + +if (SDL2_TTF_INCLUDE_DIR AND EXISTS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h") + file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h" SDL2_TTF_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_TTF_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h" SDL2_TTF_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_TTF_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h" SDL2_TTF_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_TTF_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_TTF_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_MAJOR "${SDL2_TTF_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_TTF_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_MINOR "${SDL2_TTF_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_TTF_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_PATCH "${SDL2_TTF_VERSION_PATCH_LINE}") + set(SDL2_TTF_VERSION_STRING ${SDL2_TTF_VERSION_MAJOR}.${SDL2_TTF_VERSION_MINOR}.${SDL2_TTF_VERSION_PATCH}) + unset(SDL2_TTF_VERSION_MAJOR_LINE) + unset(SDL2_TTF_VERSION_MINOR_LINE) + unset(SDL2_TTF_VERSION_PATCH_LINE) + unset(SDL2_TTF_VERSION_MAJOR) + unset(SDL2_TTF_VERSION_MINOR) + unset(SDL2_TTF_VERSION_PATCH) +endif () + +set(SDL2_TTF_LIBRARIES ${SDL2_TTF_LIBRARY}) +set(SDL2_TTF_INCLUDE_DIRS ${SDL2_TTF_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_ttf + REQUIRED_VARS SDL2_TTF_LIBRARIES SDL2_TTF_INCLUDE_DIRS + VERSION_VAR SDL2_TTF_VERSION_STRING) + +# for backward compatibility +set(SDLTTF_LIBRARY ${SDL2_TTF_LIBRARIES}) +set(SDLTTF_INCLUDE_DIR ${SDL2_TTF_INCLUDE_DIRS}) +set(SDLTTF_FOUND ${SDL2_TTF_FOUND}) diff --git a/res/childoflight.txt b/res/childoflight.txt index d65796d..72cc037 100644 --- a/res/childoflight.txt +++ b/res/childoflight.txt @@ -1,26 +1,12 @@ -"Your life, and the lives of those who believe in you. Are you willing to put it all on the line?" -Those words echoed in the child's head more than any other as he put his hands on the lamp. Through it, he could feel the sun's enormous presence far below him. Its power was palpable; a gentle thrumming that tickled his fingers. It was strong -- stronger and nearer than it had been with the other temples. Up close, it felt like all there was. +"Your life, and the lives of those who believe in you. Are you willing to put it all on the line?"\nThose words echoed in the child's head more than any other as he put his hands on the lamp. Through it, he could feel the sun's enormous presence far below him. Its power was palpable; a gentle thrumming that tickled his fingers. It was strong -- stronger and nearer than it had been with the other lamps. Up close, it felt like all there was. The lamp was light in his hands. Without a word he pulled, and light poured out. He closed his eyes and all he saw was light. -The child was nowhere when he awoke. Bright, white emptiness stretched out in all directions. He gulped as he looked around -- felt that emptiness resonate in his chest. -"Hello?" he called out. No one answered. -Trembling, he pushed himself to his feet. He'd been through countless battles, been spat on by people he used to trust, and even took down the immortal ruler of the world. He told himself he could handle whatever this was. -He hadn't had to do those things by himself, though. -The child took a step. His bare foot thudded against an invisible ground. When nothing further happened, he kept going. Choose a direction and just walk, he thought. There was nothing else to do. -"Is anyone out there?" -His steps turned inside out. The ground flipped over and curved in on itself. He saw figures in the distance but when he approached he realized it was himself he was seeing. He felt a headache start to come on, and it only grew when something finally did appear. -A black spiral, twisting out from beneath him. He climbed and climbed, and his feet hurt and his chest ached and his arms twisted but it wasn't enough to take him down. Somehow he felt like nothing would ever be able to stop him again. One foot after another he climbed and watched the matter splay out before him. This thing he'd found, it was immense. It was infinite. -It was the Sun. -"Pure hearted one," it said without saying, not a roaring voice but a subtle understanding planted in the child's mind. "We meet at last." -The child gulped. Throughout all that time searching for and sanctifying the lamps, he hadn't considered what would actually happen if he won. But now here he was, facing infinity itself, and it dawned on him the Sun had probably been sealed away for a good reason. -Would saving the world be worth the consequences? -"H-hello?" he stammered. It came out as nothing more than a squeak, but a shift in the Sun's surface -- starry as the night sky -- told him that it heard. -"For thousands of years I have slept here, ever since the world grew past the need for my power." -An eye -- incomprehensibly large -- regarded the boy who'd raised it from its slumber. An eye which had seen countless things: births, deaths, loves, wars. A being who'd caused so many of them, and had to be shut away to contain its limitless power. A power that was once again free. -The child took a deep breath. "I'm sorry I woke you up," he said, voice wobbling like a teeter totter. "I had to light the lamps to stop someone else from doing it. He wanted to end the world. But I want to save it." -"I've seen visions in my dreams of your world crumbling. I've felt your despair. I've heard your cries for help. I've seen your strength, clear as day, in the pieces of your heart that you shared." -The child saw himself reflected in that eye. Saw his feet lift off the ground, pulled by some invisible force. Saw the sparks of magic flying around his hands, felt his being churning out of control as something dark, something horrible, something unfathomably ancient poured into his mind, squeezing and pulling it like taffy, tearing it apart and putting it back together. -"Now my power is needed once again. As you have shared with me, I will share it in return." -"Wh-" he tried to ask, but his throat squeezed tight and choked the words to death. Sweat poured from his brow as the worst fever he'd ever felt took hold. His head was being thrust beneath the surface of the water, his torso was being struck by lightning from a machine. -The Sun watched him float there, writhing and sweating and sparking with violent electricity. It closed its eye. -"I promise you that I will not miss it." +The child was nowhere when he awoke. Bright, white emptiness stretched out in all directions. He gulped as he looked around, and felt that emptiness resonate in his chest.\n"Hello?" he called out. No one answered. +Trembling, the child pushed himself to his feet. He'd been through countless battles, been spat on by people he used to trust, and even took down the immortal ruler of the world. He told himself he could handle whatever this was.\nHe hadn't had to do those things by himself, though. +The child took a step. His bare foot thudded against an invisible ground. When nothing further happened, he kept going. "Choose a direction and just walk," he thought. There was nothing else to do.\n"Is anyone out there?" +The child's steps turned inside out. The ground flipped over and curved in on itself. He saw figures in the distance but when he approached he realized it was himself he was seeing. He felt a headache start to come on, and it only grew when something finally did appear.\nA black spiral, twisting out from beneath him. He climbed and climbed, and his feet hurt and his chest ached and his arms twisted but it wasn't enough to take him down. Somehow he felt like nothing would ever be able to stop him again. One foot after another he climbed and watched the matter splay out before him. This thing he'd found, it was immense. It was infinite.\nIt was the Sun. +"Pure hearted one," the Sun said without saying, not a roaring voice but a subtle understanding planted in the child's mind. "We meet at last."\nThe child gulped. Throughout all that time searching for and lighting the lamps, he hadn't considered what would actually happen if he won. But now here he was, facing infinity itself, and it dawned on him the Sun had probably been sealed away for a good reason.\nWould saving the world be worth the consequences? +"H-hello?" the child stammered. It came out as nothing more than a squeak, but a shift in the Sun's surface -- starry as the night sky -- told him that it heard.\nIt spoke: "For thousands of years I have slept here, ever since the world grew past the need for my power."\nAn eye -- incomprehensibly large -- regarded the boy who'd raised it from its slumber. An eye which had seen countless things: births, deaths, loves, wars. A being who'd caused so many of them, and had to be shut away to contain its limitless power. A power that was once again free. +The child took a deep breath. "I'm sorry I woke you up," he said, voice wobbling like a teeter totter. "I had to light the lamps to stop someone else from doing it. He wanted to end the world. But I want to save it."\nThat deep, immaterial voice continued: "I've seen visions in my dreams of your world crumbling. I've felt your despair. I've heard your cries for help. I've seen your strength, clear as day, in the pieces of your heart that you shared." +The child saw himself reflected in that eye. Saw his feet lift off the ground, pulled by some invisible force. Saw the sparks of magic flying around his hands, felt his being churning out of control as something dark, something horrible, something unfathomably ancient poured into his mind, squeezing and pulling it like taffy, tearing it apart and putting it back together.\n"Now my power is needed once again," the Sun said. "As you have shared with me, I will share it in return." +The child tried to ask what that meant, but his throat squeezed tight and choked the words to death. Sweat poured from his brow as the worst fever he'd ever felt took hold. His head was being thrust beneath the surface of the water, his torso was being struck by lightning from a machine.\nThe Sun watched him float there, writhing and sweating and sparking with violent electricity. It closed its eye.\n"I promise you that I will not miss it." The thrumming from before was inside of him now. The Sun's heartbeat was gone -- it was his own. The child opened his mouth to scream, and what came out was light. \ No newline at end of file diff --git a/res/fmod/.cache/fsbcache/Desktop/71C77FCD.fobj b/res/fmod/.cache/fsbcache/Desktop/71C77FCD.fobj new file mode 100644 index 0000000..ee4f169 Binary files /dev/null and b/res/fmod/.cache/fsbcache/Desktop/71C77FCD.fobj differ diff --git a/res/fmod/.cache/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.pdc b/res/fmod/.cache/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.pdc new file mode 100644 index 0000000..3fa7826 Binary files /dev/null and b/res/fmod/.cache/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.pdc differ diff --git a/res/fmod/.user/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.user.xml b/res/fmod/.user/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.user.xml new file mode 100644 index 0000000..8deeed0 --- /dev/null +++ b/res/fmod/.user/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.user.xml @@ -0,0 +1,34 @@ + + + + + {8d0e479a-a12b-473f-93aa-de133b6c288c} + + + + + + + + + + 0.016137708445400752 + + + + + {cfc8b7fd-e863-413e-89e8-c6f0bc5db7a8} + + + + + + + + + + + + + + diff --git a/res/fmod/Assets/Attack02.wav b/res/fmod/Assets/Attack02.wav new file mode 100644 index 0000000..ddd9127 Binary files /dev/null and b/res/fmod/Assets/Attack02.wav differ diff --git a/res/fmod/Metadata/AudioFile/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.xml b/res/fmod/Metadata/AudioFile/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.xml new file mode 100644 index 0000000..7330e45 --- /dev/null +++ b/res/fmod/Metadata/AudioFile/{9eeec59c-fe90-499a-a390-6175f2fa58e8}.xml @@ -0,0 +1,20 @@ + + + + + Attack02.wav + + + 44.0999985 + + + 1 + + + 0.30798185941043082 + + + {4c4e21c6-cc51-4a2c-b05e-fb67da4f7ed1} + + + diff --git a/res/fmod/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.xml b/res/fmod/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.xml new file mode 100644 index 0000000..18db19f --- /dev/null +++ b/res/fmod/Metadata/Event/{68372a81-e8b2-4b54-b8a4-93de302bcc94}.xml @@ -0,0 +1,128 @@ + + + + + textbeep + + + 0 + + + {b10a4c1b-72f0-4676-bbef-b450aaa46cb6} + + + {4928b913-a336-4aff-b192-a452d9e20bdd} + + + {4073a972-7eb2-4ce6-a745-f479d895c198} + + + {ece72a71-313f-4e2e-8cde-1b45205bc439} + + + {c0b89761-f8b7-4fff-a63e-7acc097ef1a9} + + + {afc86c14-4856-4b21-b002-e2c236e1c00a} + + + {f93105f9-338b-4be2-bb3b-38b7a54a95da} + + + {8d0e479a-a12b-473f-93aa-de133b6c288c} + + + {a23d2b1c-914e-48d4-8df9-80f6edad6c28} + + + + + {d6928c43-7ad8-4ac8-afde-404be9c3c3c9} + + + + + {d6928c43-7ad8-4ac8-afde-404be9c3c3c9} + + + + + {a7a25c5a-074d-490a-a50a-8b3b839f6a14} + + + {b33c4429-f286-4505-a998-52b4c752c162} + + + {a028aeaa-4166-48ea-881f-bb967c81e6f1} + + + + + + + {905db4d4-3f09-4a71-a981-772aa96e7ff4} + + + {cfc8b7fd-e863-413e-89e8-c6f0bc5db7a8} + + + + + {905db4d4-3f09-4a71-a981-772aa96e7ff4} + + + + + {df7fd3ea-e7fd-4672-ac38-319471cf4416} + + + {105a5683-47eb-4d70-a553-adc2b2040fb7} + + + {4928b913-a336-4aff-b192-a452d9e20bdd} + + + + + {bf2399fd-38f7-45cf-8404-ce25844a23da} + + + + + + 0.30798185941043082 + + + {9eeec59c-fe90-499a-a390-6175f2fa58e8} + + + + + Audio 1 + + + {fe3c2b3a-f423-48c0-b918-ea3064331c9c} + + + {100a8e0f-32d5-424a-8453-f03c784c8e0f} + + + {d6928c43-7ad8-4ac8-afde-404be9c3c3c9} + + + + + {ffb4df86-9426-45b8-b3c2-b12e3b5ff447} + + + + + + + {cc649c59-cd4c-4d44-9941-66792be28cb9} + + + + + + diff --git a/res/softsquare.ttf b/res/softsquare.ttf new file mode 100644 index 0000000..fb0ee06 Binary files /dev/null and b/res/softsquare.ttf differ 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; constexpr int CHUNK_HEIGHT = 80; constexpr int RADIUS = 8; constexpr int NUM_TITLES = 1; +constexpr int MESSAGE_MARGIN = 64; +constexpr int MESSAGE_TEXT_WIDTH = GAME_WIDTH - MESSAGE_MARGIN * 2; +constexpr int CHARS_PER_BEEP = 8; #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 @@ #include "renderer.h" #include "consts.h" -Game::Game(std::mt19937& rng, Muxer& muxer) : +Game::Game(std::mt19937& rng, Muxer& muxer, Renderer& renderer) : rng(rng), - muxer(muxer) + muxer(muxer), + sign(renderer.getFont()) { losePopLampTimer.accumulate(losePopLampTimer.getDt()); @@ -631,7 +632,7 @@ void Game::performDash() { } } -void Game::update(size_t frameTime) { +void Game::updatePlaying(size_t frameTime) { SDL_Event e; while (SDL_PollEvent(&e)) @@ -666,8 +667,22 @@ void Game::update(size_t frameTime) { { if (losing == LoseState::None) { + auto [lookX, lookY] = coordInDirection(player_x, player_y, playerAnim.getDirection()); + MapData& lookTile = map.at(lookX, lookY); if (moving) { - queueDash = true; + if (!lookTile.sign) { + queueDash = true; + } + } else if (lookTile.sign) { + if (lookTile.text.empty()) { + int lineToRead = nextSignIndex++; + if (nextSignIndex >= signTexts.size()) { + nextSignIndex = 0; + } + lookTile.text = signTexts[lineToRead]; + } + + sign.displayMessage(lookTile.text); } else { performDash(); } @@ -815,7 +830,7 @@ void Game::update(size_t frameTime) { switch (signInstructionState) { case SignInstructionState::Hidden: { auto [lookX, lookY] = coordInDirection(player_x, player_y, playerAnim.getDirection()); - if (map.at(lookX, lookY).sign) { + if (losing == LoseState::None && map.at(lookX, lookY).sign) { signInstructionState = SignInstructionState::FadingIn; signFade.start(1000); } @@ -913,3 +928,11 @@ void Game::update(size_t frameTime) { playerAnim.update(frameTime); } + +void Game::update(size_t frameTime) { + if (sign.signDisplayState != SignInstructionState::Hidden) { + sign.update(frameTime, *this); + } else { + updatePlaying(frameTime); + } +} 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 @@ #include "animation.h" #include "interpolation.h" #include "consts.h" +#include "sign.h" + +class Renderer; constexpr int TilesetIndex(int x, int y) { return x + y * 25; @@ -23,13 +26,6 @@ enum class LoseState { Outro }; -enum class SignInstructionState { - Hidden, - FadingIn, - Visible, - FadingOut -}; - struct Input { bool left = false; bool right = false; @@ -58,7 +54,7 @@ struct Kickup { class Game { public: - Game(std::mt19937& rng, Muxer& muxer); + Game(std::mt19937& rng, Muxer& muxer, Renderer& render); void update(size_t dt); @@ -111,6 +107,7 @@ public: int nextSignIndex = 0; SignInstructionState signInstructionState = SignInstructionState::Hidden; Interpolation signFade; + Sign sign; private: @@ -144,6 +141,8 @@ private: void performDash(); + void updatePlaying(size_t frameTime); + }; #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**) Renderer renderer; Muxer muxer; - Game game(rng, muxer); + Game game(rng, muxer, renderer); constexpr int titleFadeLen = 2000; 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) { ERRCHECK(system_->setListenerAttributes(0, &attributes)); } +void Muxer::playSound(std::string name) { + std::string eventPath = std::string("event:/") + name; + + FMOD::Studio::EventDescription* eventDescription = nullptr; + ERRCHECK(system_->getEvent(eventPath.c_str(), &eventDescription)); + + FMOD::Studio::EventInstance* eventInstance = nullptr; + ERRCHECK(eventDescription->createInstance(&eventInstance)); + ERRCHECK(eventInstance->start()); + ERRCHECK(eventInstance->release()); +} + void Muxer::playSoundAtPosition(std::string name, float x, float y) { std::string eventPath = std::string("event:/") + name; 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: void setPlayerLoc(int x, int y); + void playSound(std::string name); + void playSoundAtPosition(std::string name, float x, float y); 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 @@ #include "renderer.h" +#include #include "game.h" Renderer::Renderer() @@ -113,6 +114,11 @@ Renderer::Renderer() loadTextureFromFile("../res/title0.png", titles_[0]); SDL_QueryTexture(titles_[0].get(), nullptr, nullptr, &titleWidths_[0], &titleHeights_[0]); + + font_ = font_ptr(TTF_OpenFont("../res/softsquare.ttf", 45)); + if (!font_) { + throw ttf_error(); + } } void Renderer::renderGame( @@ -389,6 +395,55 @@ void Renderer::renderGame( SDL_RenderCopy(ren_.get(), readInstruction_.get(), nullptr, nullptr); } + if (game.sign.signDisplayState != SignInstructionState::Hidden) { + int opacity = 255; + if (game.sign.signDisplayState == SignInstructionState::FadingIn) { + opacity = game.sign.signDisplayFade.getProgress(0, 255); + } else if (game.sign.signDisplayState == SignInstructionState::FadingOut) { + opacity = game.sign.signDisplayFade.getProgress(255, 0); + } + + SDL_Rect signRect { + 0, + GAME_HEIGHT / 2 - (45 * 2 + 1 + MESSAGE_MARGIN * 2) / 2, + GAME_WIDTH, + 45 * 2 + 1 + MESSAGE_MARGIN * 2 + }; + + SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, opacity); + SDL_RenderFillRect(ren_.get(), &signRect); + + if (game.sign.signDisplayState == SignInstructionState::Visible) { + int lineIndex = 0; + for (const SignLine& line : game.sign.linesToShow) { + if (messageLines_[lineIndex].line != line.text) { + renderMessageLine(messageLines_[lineIndex], line.text, game); + } + + if (line.charsRevealed > 0) { + { + SDL_Rect srcRect { + 0, 0, messageLines_[lineIndex].charIndexToWidth[line.charsRevealed], + TTF_FontHeight(font_.get()) + }; + SDL_Rect destRect { + MESSAGE_MARGIN, + GAME_HEIGHT / 2 - (45 * 2 + 1 + MESSAGE_MARGIN * 2) / 2 + MESSAGE_MARGIN + (45 + 1) * lineIndex, + srcRect.w, + srcRect.h }; + + SDL_SetRenderTarget(ren_.get(), nullptr); + SDL_RenderCopy(ren_.get(), messageLines_[lineIndex].renderedTex.get(), &srcRect, &destRect); + //std::cout << line.charsRevealed << " (" << messageLines_[lineIndex].charIndexToWidth[line.charsRevealed] << "): " << messageLines_[lineIndex].line.substr(0,line.charsRevealed) << std::endl; + } + } + + lineIndex++; + } + } + } + SDL_RenderPresent(ren_.get()); } @@ -443,3 +498,44 @@ void Renderer::loadTextureFromFile(std::string_view path, texture_ptr& texture) texture = texture_ptr(SDL_CreateTextureFromSurface(ren_.get(), pfs.get())); SDL_SetTextureBlendMode(texture.get(), SDL_BLENDMODE_BLEND); } + +void Renderer::renderMessageLine(MessageCache& line, const std::string& text, const Game& game) { + line.line = text; + + line.renderedTex.reset( + SDL_CreateTexture( + ren_.get(), + SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, + MESSAGE_TEXT_WIDTH, + TTF_FontHeight(font_.get()))); + + SDL_SetTextureBlendMode(line.renderedTex.get(), SDL_BLENDMODE_BLEND); + + SDL_SetRenderTarget(ren_.get(), line.renderedTex.get()); + SDL_SetRenderDrawBlendMode(ren_.get(), SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(ren_.get(), 0, 0, 0, 0); + SDL_RenderClear(ren_.get()); + + line.charIndexToWidth.clear(); + line.charIndexToWidth.push_back(0); + + for (int i=0; i #include +#include #include #include #include #include +#include +#include #include "consts.h" class Game; @@ -27,6 +30,14 @@ public: } }; +class ttf_error : public std::logic_error { +public: + + ttf_error() : std::logic_error(TTF_GetError()) + { + } +}; + class sdl_wrapper { public: @@ -67,6 +78,26 @@ public: } }; +class ttf_wrapper { +public: + + ttf_wrapper() + { + if (TTF_Init() != 0) + { + ttf_error ex; + TTF_Quit(); + + throw ex; + } + } + + ~ttf_wrapper() + { + TTF_Quit(); + } +}; + class window_deleter { public: @@ -111,6 +142,16 @@ public: using texture_ptr = std::unique_ptr; +class font_deleter { +public: + + void operator()(TTF_Font* ptr) { + TTF_CloseFont(ptr); + } +}; + +using font_ptr = std::unique_ptr; + class Renderer { public: @@ -122,14 +163,18 @@ public: void renderTitle(int num, double fade); + TTF_Font* getFont() { return font_.get(); } + private: void loadTextureFromFile(std::string_view path, texture_ptr& texture); sdl_wrapper sdl_; img_wrapper img_; + ttf_wrapper ttf_; window_ptr win_; renderer_ptr ren_; + font_ptr font_; texture_ptr playerFade_; texture_ptr lampFade_; @@ -142,6 +187,18 @@ private: std::array titles_; std::array titleWidths_; std::array titleHeights_; + + // Text rendering + struct MessageCache { + texture_ptr renderedTex; + std::vector charIndexToWidth; + std::string line; + std::string overflow; + }; + + void renderMessageLine(MessageCache& line, const std::string& text, const Game& game); + + MessageCache messageLines_[2]; }; #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 @@ +#include "sign.h" +#include +#include "util.h" +#include "consts.h" +#include "game.h" + +void Sign::displayMessage(std::string text) { + signDisplayState = SignInstructionState::FadingIn; + lines.clear(); + + auto lineChunks = splitStr>(text, "\\n"); + for (std::string lineChunk : lineChunks) { + auto words = splitStr>(lineChunk, " "); + std::string prev = words.front(); + words.pop_front(); + std::string cur = prev; + bool pauseLine = false; + + while (!words.empty()) { + cur = prev + " " + words.front(); + + int width = 0; + TTF_SizeText(font_, cur.c_str(), &width, nullptr); + if (width > MESSAGE_TEXT_WIDTH) { + lines.push_back({.text = prev, .pause = pauseLine}); + pauseLine = !pauseLine; + cur = words.front(); + } else if (words.size() == 1) { + lines.push_back({.text = cur, .pause = true}); + } + + prev = cur; + words.pop_front(); + } + } + + lines.back().pause = true; + linesToShow.push_back(lines.front()); + lines.pop_front(); + + if (!linesToShow.back().pause) { + linesToShow.push_back(lines.front()); + lines.pop_front(); + } +} + +void Sign::update(size_t dt, Game& game) { + SDL_Event e; + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.quit = true; + } + } + + switch (signDisplayState) { + case SignInstructionState::Hidden: { + // Shouldn't happen. + break; + } + case SignInstructionState::FadingIn: { + signDisplayFade.tick(dt); + if (signDisplayFade.isComplete()) { + signDisplayState = SignInstructionState::Visible; + } + + break; + } + case SignInstructionState::FadingOut: { + signDisplayFade.tick(dt); + if (signDisplayFade.isComplete()) { + signDisplayState = SignInstructionState::Hidden; + } + + break; + } + case SignInstructionState::Visible: { + const Uint8* state = SDL_GetKeyboardState(NULL); + if (state[SDL_SCANCODE_SPACE]) { + bool fullyRevealed = true; + for (const SignLine& line : linesToShow) { + if (line.charsRevealed != line.text.size()) { + fullyRevealed = false; + break; + } + } + + if (fullyRevealed) { + if (linesToShow.back().pause) { + linesToShow.back().pause = false; + // Play a sound + } + if (lines.empty()) { + linesToShow.clear(); + signDisplayState = SignInstructionState::FadingOut; + signDisplayFade.start(1000); + break; + } + } + } + + textAdvTimer_.accumulate(dt); + while (textAdvTimer_.step()) { + bool advancedChars = false; + for (SignLine& line : linesToShow) { + if (line.charsRevealed < line.text.size()) { + if (line.charsRevealed % CHARS_PER_BEEP == 0) { + // Play a sound + game.muxer.playSound("textbeep"); + } + line.charsRevealed++; + advancedChars = true; + break; + } + } + if (!advancedChars) { + if (!lines.empty() && !linesToShow.back().pause) { + if (linesToShow.size() == 2) { + linesToShow.pop_front(); + } + linesToShow.push_back(lines.front()); + lines.pop_front(); + } + } + } + + break; + } + } +} 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 @@ +#ifndef SIGN_H_B0491849 +#define SIGN_H_B0491849 + +#include +#include +#include +#include "interpolation.h" +#include "timer.h" + +class Game; + +enum class SignInstructionState { + Hidden, + FadingIn, + Visible, + FadingOut +}; + +struct SignLine { + std::string text; + bool pause = false; + int charsRevealed = 0; +}; + +class Sign { +public: + + explicit Sign(TTF_Font* font) : font_(font) {} + + void displayMessage(std::string text); + + void update(size_t dt, Game& game); + + SignInstructionState signDisplayState = SignInstructionState::Hidden; + Interpolation signDisplayFade; + std::list lines; + std::list linesToShow; + +private: + + TTF_Font* font_; + Timer textAdvTimer_ { 15 }; +}; + +#endif /* end of include guard: SIGN_H_B0491849 */ -- cgit 1.4.1