From 72fbf406a70b360e8ef6a491f027e1f8618acab7 Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Sun, 20 Jul 2025 13:49:53 +0300 Subject: [PATCH] Implement audio mixing --- Data/Audio/{explosion1.wav => Explosion1.wav} | Bin Data/Audio/{explosion2.wav => Explosion2.wav} | Bin Data/Audio/{explosion3.wav => Explosion3.wav} | Bin Data/Audio/{jump.wav => Jump.wav} | Bin Data/Audio/{laser.wav => Laser.wav} | Bin Data/Audio/{pickup.wav => Pickup.wav} | Bin Data/Audio/{powerUp.wav => PowerUp.wav} | Bin Data/Audio/{select.wav => Select.wav} | Bin Data/Audio/{shoot.wav => Shoot.wav} | Bin Data/Audio/Walk.wav | Bin 0 -> 748 bytes Data/Templates/LR.txt | 33 ++++ Data/Templates/LRD.txt | 16 ++ Data/Templates/LRU.txt | 17 ++ Data/Templates/LRUD.txt | 16 ++ Makefile.srcs | 7 +- src/App.cpp | 10 + src/App.h | 4 + src/AudioManager.cpp | 137 ++++++++++++++ src/AudioManager.h | 44 +++++ src/Main.cpp | 2 +- src/RoomEntity.cpp | 171 +++++++++++++++--- src/RoomEntity.h | 3 + 22 files changed, 428 insertions(+), 32 deletions(-) rename Data/Audio/{explosion1.wav => Explosion1.wav} (100%) rename Data/Audio/{explosion2.wav => Explosion2.wav} (100%) rename Data/Audio/{explosion3.wav => Explosion3.wav} (100%) rename Data/Audio/{jump.wav => Jump.wav} (100%) rename Data/Audio/{laser.wav => Laser.wav} (100%) rename Data/Audio/{pickup.wav => Pickup.wav} (100%) rename Data/Audio/{powerUp.wav => PowerUp.wav} (100%) rename Data/Audio/{select.wav => Select.wav} (100%) rename Data/Audio/{shoot.wav => Shoot.wav} (100%) create mode 100644 Data/Audio/Walk.wav create mode 100644 src/AudioManager.cpp create mode 100644 src/AudioManager.h diff --git a/Data/Audio/explosion1.wav b/Data/Audio/Explosion1.wav similarity index 100% rename from Data/Audio/explosion1.wav rename to Data/Audio/Explosion1.wav diff --git a/Data/Audio/explosion2.wav b/Data/Audio/Explosion2.wav similarity index 100% rename from Data/Audio/explosion2.wav rename to Data/Audio/Explosion2.wav diff --git a/Data/Audio/explosion3.wav b/Data/Audio/Explosion3.wav similarity index 100% rename from Data/Audio/explosion3.wav rename to Data/Audio/Explosion3.wav diff --git a/Data/Audio/jump.wav b/Data/Audio/Jump.wav similarity index 100% rename from Data/Audio/jump.wav rename to Data/Audio/Jump.wav diff --git a/Data/Audio/laser.wav b/Data/Audio/Laser.wav similarity index 100% rename from Data/Audio/laser.wav rename to Data/Audio/Laser.wav diff --git a/Data/Audio/pickup.wav b/Data/Audio/Pickup.wav similarity index 100% rename from Data/Audio/pickup.wav rename to Data/Audio/Pickup.wav diff --git a/Data/Audio/powerUp.wav b/Data/Audio/PowerUp.wav similarity index 100% rename from Data/Audio/powerUp.wav rename to Data/Audio/PowerUp.wav diff --git a/Data/Audio/select.wav b/Data/Audio/Select.wav similarity index 100% rename from Data/Audio/select.wav rename to Data/Audio/Select.wav diff --git a/Data/Audio/shoot.wav b/Data/Audio/Shoot.wav similarity index 100% rename from Data/Audio/shoot.wav rename to Data/Audio/Shoot.wav diff --git a/Data/Audio/Walk.wav b/Data/Audio/Walk.wav new file mode 100644 index 0000000000000000000000000000000000000000..525a2a824c44a3158ec347907bc6355d2404d5c2 GIT binary patch literal 748 zcmW-e-EJF29K~I(xa1KKZ-OWhNFXFcTO^vKDR%6Q~e-@tobLjX+z4?mne(c&SCLiwp3QkAc;hiNvJ!(&%|MvUv_|FC}*u31GAKwq} z$8LUealK^Uq)pR2tYy9|)Y}O%cKrR@Wq#?a698F&D4L$dJxo8IyZny=-zn0(8BZri<7pzvYYS zz&zA$I6@nKDO{mLS68&xfdczt zzIiHE7Dsx^F4N)FVj96+5VLY0-~p^^&yh||Q&=Z4YRJ5(eHCX}IdSne9p{rRjO09T zf((1NAeFTJ7CVic%VvqpU6>YX#)CVIsX4zPvWiS#^TCED79?5`yO{DSSO;s`MvD^p z*J9N#1bX#+ci1IvvC2r2Pzhm^MDkHH*0x`FwGTa9E{=2YOHA_ zY0blu0E8|D8Mrp4%Pd}4oq!Q35mYI`4acbfsDd;qlEXPtff6)Pj!>IqibxL3h(t3k WG}pkH5FkJSa7h(#ArF}YfcOv2IA*l~ literal 0 HcmV?d00001 diff --git a/Data/Templates/LR.txt b/Data/Templates/LR.txt index 17b557f..cc4ffeb 100644 --- a/Data/Templates/LR.txt +++ b/Data/Templates/LR.txt @@ -6,6 +6,15 @@ Room 0D00DD0pD0 GGGGGGGGGG +Room +5555555555 +0000000000 +0000000000 +00t0000t00 +0d00000p00 +GGGGGGGGGG + + Room 5555555555 00t00p0t00 @@ -14,6 +23,14 @@ Room 000lggggl0 GGGGggggGG +Room +5555555555 +00t00pdt00 +000lGGGGl0 +000lggggl0 +000lggggl0 +GGGGggggGG + Room 0000000000 00t00p0t00 @@ -22,10 +39,26 @@ Room 0lDDDgDDDl GGGGGGGGGG +Room +0000000000 +00t00p0t00 +0lg00g00gl +0l000g000l +0lDdDgDdDl +GGGGGGGGGG + Room 0000000000 00t0000t00 000D0pD000 00gggggg00 0gggggggg0 +GggggggggG + +Room +0000000000 +00t0000t00 +00000pd000 +00gggggg00 +0gggggggg0 GggggggggG \ No newline at end of file diff --git a/Data/Templates/LRD.txt b/Data/Templates/LRD.txt index 68951c1..5cbb967 100644 --- a/Data/Templates/LRD.txt +++ b/Data/Templates/LRD.txt @@ -6,6 +6,14 @@ Room 0tp00000t0 GGGGPPPPGG +Room +0000000000 +0000000000 +00t00000t0 +0000000000 +0dp0000000 +GGGGPPPPGG + Room 0000000000 0000000000 @@ -21,3 +29,11 @@ Room 0t000000t0 000000p000 GPPGPPGPPG + +Room +0000000000 +0000000000 +0000000000 +0t000000t0 +000d00p000 +GPPGPPGPPG diff --git a/Data/Templates/LRU.txt b/Data/Templates/LRU.txt index 7f51a30..a26131e 100644 --- a/Data/Templates/LRU.txt +++ b/Data/Templates/LRU.txt @@ -6,10 +6,27 @@ Room 0000l0p000 GGGGGGGGGG +Room +0000l00000 +0000l00000 +0000l00000 +0t00l000t0 +000dldp000 +GGGGGGGGGG + + Room 0000l00000 00t0l00t00 0lggggggl0 0l000000l0 0l00p000l0 +GGGGGGGGGG + +Room +0000l00000 +00t0l00t00 +0lggggggl0 +0l000000l0 +0ld0p000l0 GGGGGGGGGG \ No newline at end of file diff --git a/Data/Templates/LRUD.txt b/Data/Templates/LRUD.txt index e521032..a19b06f 100644 --- a/Data/Templates/LRUD.txt +++ b/Data/Templates/LRUD.txt @@ -6,6 +6,14 @@ Room 0p00l00000 GGPPlPPPGG +Room +0000l00000 +0000l00000 +0000l00000 +0t00l000t0 +0p00l0d000 +GGPPlPPPGG + Room 0000l00000 0t00l000t0 @@ -13,3 +21,11 @@ ggggggglgg 0000000l00 0000l0pl00 GGPPlGGGGG + +Room +0000l00000 +0t00l000t0 +ggggggglgg +0000000l00 +0d00l0pl00 +GGPPlGGGGG diff --git a/Makefile.srcs b/Makefile.srcs index 0bc92c9..aa79980 100644 --- a/Makefile.srcs +++ b/Makefile.srcs @@ -3,17 +3,18 @@ SRCS = \ src/Alarm.cpp \ src/App.cpp \ + src/AudioManager.cpp \ src/Common.cpp \ + src/DigitManager.cpp \ src/Entity.cpp \ src/EntityManager.cpp \ src/FileManager.cpp \ + src/FontManager.cpp \ src/Gridmap.cpp \ src/Main.cpp \ src/RenderManager.cpp \ + src/RoomEntity.cpp \ src/SpriteManager.cpp \ src/State.cpp \ src/SurfaceManager.cpp \ - src/FontManager.cpp \ - src/DigitManager.cpp \ - src/RoomEntity.cpp \ src/Variable.cpp \ No newline at end of file diff --git a/src/App.cpp b/src/App.cpp index db7028a..7613964 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -26,6 +26,7 @@ TApp::TApp() { EntityManager_ = std::make_unique(*RenderManager_, 1000 / 60); FontManager_ = std::make_unique(*SpriteManager_); DigitManager_ = std::make_unique(*SpriteManager_); + AudioManager_ = std::make_unique(*FileManager_); } TApp::~TApp() { @@ -43,6 +44,7 @@ void TApp::Process() { RenderManager_->Reset(); EntityManager_->Run(); RenderManager_->Run(); + AudioManager_->Run(); } int TApp::Run() { @@ -86,6 +88,10 @@ TDigitManager& TApp::DigitManager() { return *DigitManager_; } +TAudioManager& TApp::AudioManager() { + return *AudioManager_; +} + const TFileManager& TApp::FileManager() const { return *FileManager_; } @@ -114,4 +120,8 @@ const TDigitManager& TApp::DigitManager() const { return *DigitManager_; } +const TAudioManager& TApp::AudioManager() const { + return *AudioManager_; +} + } // namespace NGame \ No newline at end of file diff --git a/src/App.h b/src/App.h index 88566e2..65d2b6c 100644 --- a/src/App.h +++ b/src/App.h @@ -2,6 +2,7 @@ #include "Common.h" +#include "AudioManager.h" #include "DigitManager.h" #include "EntityManager.h" #include "FileManager.h" @@ -35,6 +36,7 @@ public: TSpriteManager& SpriteManager(); TFontManager& FontManager(); TDigitManager& DigitManager(); + TAudioManager& AudioManager(); const TFileManager& FileManager() const; const TRenderManager& RenderManager() const; @@ -43,6 +45,7 @@ public: const TSpriteManager& SpriteManager() const; const TFontManager& FontManager() const; const TDigitManager& DigitManager() const; + const TAudioManager& AudioManager() const; private: TApp(); @@ -57,6 +60,7 @@ private: std::unique_ptr SpriteManager_; std::unique_ptr FontManager_; std::unique_ptr DigitManager_; + std::unique_ptr AudioManager_; TState State_; }; diff --git a/src/AudioManager.cpp b/src/AudioManager.cpp new file mode 100644 index 0000000..0661273 --- /dev/null +++ b/src/AudioManager.cpp @@ -0,0 +1,137 @@ +#include "AudioManager.h" +#include + +namespace NGame { + +TAudioManager::TAudioManager(TFileManager& fileManager) + : FileManager_(fileManager) { + + SDL_AudioSpec desired; + desired.freq = 44100; + desired.format = AUDIO_F32; + desired.channels = 2; + desired.samples = 4096; + desired.callback = NULL; + + Device_ = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, 0); + if (!Device_) { + throw std::runtime_error("no audio device"); + } + + Stream_ = SDL_NewAudioStream(AUDIO_U8, 1, 22050, AUDIO_F32, 2, 44100); + if (!Stream_) { + throw std::runtime_error("can't create audio stream"); + } + + for (std::size_t i = 0; i < 16; ++i) { + Channels_[i].InUse = false; + } + SDL_PauseAudioDevice(Device_, SDL_FALSE); + + LastTick_ = SDL_GetTicks(); +} + +void TAudioManager::Run() { + std::uint32_t currentTick = SDL_GetTicks(); + + Paint(Stream_, currentTick - LastTick_); + if (SDL_GetQueuedAudioSize(Device_) < 8192) { + const int bytesRemaining = SDL_AudioStreamAvailable(Stream_); + if (bytesRemaining > 0) { + const int samplesRemaining = SDL_min(bytesRemaining, 32 * 1024); + static Uint8 convertedBuffer[32 * 1024]; + auto bytesRead = SDL_AudioStreamGet(Stream_, convertedBuffer, samplesRemaining); + SDL_QueueAudio(Device_, convertedBuffer, bytesRead); + } + } + + LastTick_ = currentTick; +} + +void TAudioManager::Play(const std::string& path) { + TSound* sound = Load(path); + + for (std::size_t i = 0; i < 16; i++) { + if (Channels_[i].InUse) { + continue; + } + + Channels_[i].InUse = true; + Channels_[i].Position = 0; + Channels_[i].Sound = sound; + break; + } +} + +void TAudioManager::Paint(SDL_AudioStream* stream, std::uint32_t ticks) { + std::uint8_t temporary[512]; + std::size_t samples = std::min((ticks * 22050 + 999) / 1000, 512u); + + // Mix + for (size_t i = 0; i < samples; ++i) { + int16_t accumulator = 0; + + for (size_t j = 0; j < 16; j++) { + auto& channel = Channels_[j]; + if (!channel.InUse) { + continue; + } + + accumulator += channel.Sound->Data[channel.Position]; + accumulator -= 128; + channel.Position++; + + if (channel.Position > channel.Sound->Size) { + channel.InUse = false; + } + } + + accumulator += 128; + if (accumulator < 0) { + accumulator = 0; + } + if (accumulator > 255) { + accumulator = 255; + } + + temporary[i] = accumulator; + } + + // Output + if (samples > 0) { + SDL_AudioStreamPut(stream, temporary, samples * sizeof (std::uint8_t)); + } +} + + + +TAudioManager::TSound* TAudioManager::Load(const std::string& path) { + auto it = Sounds_.find(path); + if (it != Sounds_.end()) { + return it->second; + } + + TSound* newSound = new TSound; + auto data = FileManager_.Get(path); + SDL_RWops* rw = SDL_RWFromConstMem(data.data(), data.size()); + if (!SDL_LoadWAV_RW(rw, 1, &newSound->Spec, &newSound->Data, &newSound->Size)) { + throw std::runtime_error("can't read WAV file " + path); + } + + if (newSound->Spec.channels != 1) { + throw std::runtime_error("not mono WAV file " + path); + } + + if (newSound->Spec.format != AUDIO_U8) { + throw std::runtime_error("not 8bit pcm WAV file " + path); + } + + if (newSound->Spec.freq != 22050) { + throw std::runtime_error("not 22050hz WAV file " + path); + } + + Sounds_[path] = newSound; + return newSound; +} + +} // namespace NGame \ No newline at end of file diff --git a/src/AudioManager.h b/src/AudioManager.h new file mode 100644 index 0000000..9a11887 --- /dev/null +++ b/src/AudioManager.h @@ -0,0 +1,44 @@ +#pragma once + +#include "Common.h" + +#include "FileManager.h" +#include + +namespace NGame { + +class TAudioManager { +public: + TAudioManager(TFileManager& fileManager); + DELETE_COPY(TAudioManager) + + void Run(); + void Play(const std::string& sound); + void Paint(SDL_AudioStream* stream, std::uint32_t ticks); + +private: + struct TSound { + SDL_AudioSpec Spec; + std::uint8_t* Data; + std::uint32_t Size; + }; + + struct TSoundChannel { + bool InUse; + TSound* Sound; + std::uint32_t Position; + }; + +private: + TSound* Load(const std::string& sound); + +private: + TFileManager& FileManager_; + TSoundChannel Channels_[16]; + std::unordered_map Sounds_; + SDL_AudioDeviceID Device_; + SDL_AudioStream* Stream_; + std::uint32_t LastTick_; +}; + +} // namespace NGame diff --git a/src/Main.cpp b/src/Main.cpp index 14d6487..b0b3838 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -53,7 +53,7 @@ static void MainLoop(void) { int main(int argc, char** argv) { - SDL_Init(SDL_INIT_VIDEO); + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); srand(time(NULL)); diff --git a/src/RoomEntity.cpp b/src/RoomEntity.cpp index 48ebae7..358b204 100644 --- a/src/RoomEntity.cpp +++ b/src/RoomEntity.cpp @@ -120,6 +120,8 @@ TExplosionEntity::TExplosionEntity(NGame::TEntity::TId id) SetSize({64, 64}); Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Light.txt"); Alarm_.Set(0, 50); + + App_->AudioManager().Play("Audio/Explosion2.wav"); } void TExplosionEntity::Update(std::uint32_t delta) { @@ -191,10 +193,15 @@ void TMineEntity::Update(std::uint32_t delta) { switch (State_) { case Dormant: if (!entityManager.IsPlaceEmpty(Position() + Size() / 2 - TriggerRadius_ / 2, TriggerRadius_, HERO_GROUP)) { - Remaining_ = rand() % 5 + 5; - if (!(Remaining_ % 2)) { - Remaining_ += 1; + if (App_->State().Variable("InstantMines").Bool()) { + Remaining_ = 1; + } else { + Remaining_ = rand() % 3 + 7; + if (!(Remaining_ % 2)) { + Remaining_ += 1; + } } + Alarm_.Set(0, 100); State_ = Active; } @@ -232,6 +239,9 @@ void TMineEntity::Alarm(NGame::TAlarm::TId id) { if (Remaining_) { --Remaining_; } + if (AlternateBlink_) { + App_->AudioManager().Play("Audio/Select.wav"); + } } } @@ -671,8 +681,14 @@ THero::THero(NGame::TEntity::TId id) SetSize(NGame::Vec2i(4, 12)); SetPosition(NGame::Vec2i(0, 0)); SetCollisionGroup(HERO_GROUP); - NGame::TApp::Instance()->RenderManager().EnableLight(true); - NGame::TApp::Instance()->RenderManager().SetDefaultLightColor(0, 0, 0); + + if (App_->State().Variable("DarkLevel").Bool()) { + NGame::TApp::Instance()->RenderManager().EnableLight(true); + NGame::TApp::Instance()->RenderManager().SetDefaultLightColor(0, 0, 0); + } else { + NGame::TApp::Instance()->RenderManager().EnableLight(false); + NGame::TApp::Instance()->RenderManager().SetDefaultLightColor(0, 0, 0); + } Alarm_.Set(0, 100); @@ -687,6 +703,9 @@ THero::THero(NGame::TEntity::TId id) break; } } + + RemainingTime_ = App_->State().Variable("Time").Double(); + Grenades_ = App_->State().Variable("Grenades").Int(); } void THero::Input(SDL_Event* event) { @@ -781,16 +800,19 @@ void THero::Update(std::uint32_t delta) { Speed_.X = -100; } if (KeysPressed_[static_cast(EKeys::X)]) { - auto entity = entityManager.MakeEntityByName("GrenadeEntity"); - auto targetEntity = dynamic_cast(entity.get()); + if (Grenades_) { + Grenades_--; + auto entity = entityManager.MakeEntityByName("GrenadeEntity"); + auto targetEntity = dynamic_cast(entity.get()); - targetEntity->SetPosition(Position()); - if (FaceLeft_) { - targetEntity->SetSpeed({-300, 0}); - } else { - targetEntity->SetSpeed({300, 0}); + targetEntity->SetPosition(Position()); + if (FaceLeft_) { + targetEntity->SetSpeed({-300, 0}); + } else { + targetEntity->SetSpeed({300, 0}); + } + entityManager.UpdateCollision(entity); } - entityManager.UpdateCollision(entity); } if (KeysPressed_[static_cast(EKeys::Up)]) { auto otherIds = entityManager.CollisionList(Position(), Size(), PASSAGE_GROUP, Id()); @@ -798,18 +820,13 @@ void THero::Update(std::uint32_t delta) { for (auto otherId : otherIds) { auto other = entityManager.Entity(otherId); if (dynamic_cast(other.get())) { + App_->State().Variable("Time").SetDouble(RemainingTime_); + App_->State().Variable("Grenades").SetInt(Grenades_); entityManager.AddToDeferred("EulaMenu"); entityManager.Reset(); } } } - if (KeysPressed_[static_cast(EKeys::C)]) { - auto entity = entityManager.MakeEntityByName("FloatingTextEntity"); - auto targetEntity = dynamic_cast(entity.get()); - targetEntity->SetText("You died, LMAO"); - targetEntity->SetColor(NGame::TFontManager::Red); - targetEntity->SetPosition(Position()); - } if (!entityManager.IsPlaceEmpty(Position(), Size(), LADDER_GROUP)) { if (KeysHeld_[static_cast(EKeys::Up)] || KeysHeld_[static_cast(EKeys::Down)]) { @@ -829,6 +846,7 @@ void THero::Update(std::uint32_t delta) { if (KeysPressed_[static_cast(EKeys::Z)]) { Speed_.Y = -std::sqrt(2 * Gravity_ * (16 + (16 - Size().Y))); State_ = EState::Jump; + App_->AudioManager().Play("Audio/jump.wav"); continue; } } @@ -972,6 +990,18 @@ void THero::Update(std::uint32_t delta) { } } + if (State_ != EState::Dead) { + RemainingTime_ -= delta; + if (RemainingTime_ < 0) { + RemainingTime_ = 0; + App_->AudioManager().Play("Audio/Laser.wav"); + State_ = EState::Dead; + if (!Alarm_.IsSet(1)) { + Alarm_.Set(1, 3000); + } + } + } + // Explictly check damage sources and flick the body around the screen { auto entityList = entityManager.CollisionList(Position(), Size(), DAMAGE_GROUP, Id()); @@ -1009,6 +1039,7 @@ void THero::Update(std::uint32_t delta) { } if (previousState != State_) { + App_->AudioManager().Play("Audio/Laser.wav"); if (!Alarm_.IsSet(1)) { Alarm_.Set(1, 3000); } @@ -1067,7 +1098,11 @@ void THero::Update(std::uint32_t delta) { if (!moveResult.second.second) { // Fall damage if (App_->State().Variable("FallDamage").Bool() && (Position().Y - LastStablePosition_.Y > 40)) { + App_->AudioManager().Play("Audio/Laser.wav"); State_ = EState::Dead; + if (!Alarm_.IsSet(1)) { + Alarm_.Set(1, 3000); + } } Speed_.Y = 0; @@ -1123,6 +1158,16 @@ void THero::Draw() const { auto light = SpriteManager_.Get("Sprites/Light.txt"); SpriteManager_.Draw(light, 0, Position() - (light->Frames[0].Size * 4 / 2), {4, 4}); + RenderManager_.SetLayer(NGame::TRenderManager::Interface); + RenderManager_.SetColor(0, 0, 0, 128); + RenderManager_.DrawRect({0, 0}, {320, 16}, true); + App_->DigitManager().Draw({320 - 48 - 2, 2}, RemainingTime_, 6, 3); + + auto itemSprite = SpriteManager_.Get("Sprites/Grenade.txt"); + for (std::size_t i = 0; i < Grenades_; i++) { + SpriteManager_.Draw(itemSprite, 0, NGame::Vec2i(200 + i * 8, 3)); + } + if (!App_->State().Variable("NoCompas").Bool()) { auto heroCenter = RenderManager_.Camera() + RenderManager_.Size() / 2; auto delta = ExitPosition_ - heroCenter; @@ -1146,22 +1191,19 @@ void THero::Draw() const { index = 3; } } - RenderManager_.SetLayer(NGame::TRenderManager::Interface); + screenPosition.X = std::min(std::max(screenPosition.X, 0), RenderManager_.Size().X - 16); screenPosition.Y = std::min(std::max(screenPosition.Y, 16), RenderManager_.Size().Y - 16); - auto arrow = SpriteManager_.Get("Sprites/Arrow.txt"); SpriteManager_.Draw(arrow, index, screenPosition); } } if (DisplayEnd_) { - RenderManager_.SetLayer(NGame::TRenderManager::Interface); - App_->FontManager().Draw(NGame::TFontManager::Red, {}, "You are dead! Press X"); + App_->FontManager().Draw(NGame::TFontManager::Red, {4, 4}, "You are dead! Press X"); } if (FadeAmount_) { - RenderManager_.SetLayer(NGame::TRenderManager::Interface); if (WhiteFade_) { RenderManager_.SetColor(255, 255, 255, FadeAmount_); } else { @@ -1174,6 +1216,9 @@ void THero::Draw() const { void THero::Alarm(NGame::TAlarm::TId id) { if (id == 0) { AlternateRun_ = !AlternateRun_; + if (AlternateRun_ && Speed_.X && State_ == EState::Normal) { + App_->AudioManager().Play("Audio/Walk.wav"); + } } else if (id == 1) { DisplayEnd_ = true; } @@ -1268,6 +1313,14 @@ void TMainMenu::Input(SDL_Event* event) { switch (event->type) { case SDL_KEYDOWN: if (event->key.keysym.sym == 'x') { + App_->AudioManager().Play("Audio/Pickup.wav"); + App_->State().Variable("Time").SetDouble(60000); + App_->State().Variable("FallDamage").SetBool(false); + App_->State().Variable("InstantMines").SetBool(false); + App_->State().Variable("LimitedGrenades").SetBool(false); + App_->State().Variable("DarkLevel").SetBool(false); + App_->State().Variable("NoCompas").SetBool(false); + App_->State().Variable("Grenades").SetInt(3); App_->EntityManager().AddToDeferred("RoomEntity"); App_->EntityManager().Reset(); } @@ -1294,12 +1347,51 @@ TEulaMenu::TEulaMenu(NGame::TEntity::TId id) , RenderManager_(App_->RenderManager()) , SpriteManager_(App_->SpriteManager()) { RenderManager_.EnableLight(false); + + std::vector toDisableTo; + + if (!App_->State().Variable("FallDamage").Bool()) { + toDisableTo.push_back("FallDamage"); + } + if (!App_->State().Variable("InstantMines").Bool()) { + toDisableTo.push_back("InstantMines"); + } + if (!App_->State().Variable("LimitedGrenades").Bool()) { + toDisableTo.push_back("LimitedGrenades"); + } + if (!App_->State().Variable("DarkLevel").Bool()) { + toDisableTo.push_back("DarkLevel"); + } + if (!App_->State().Variable("NoCompas").Bool()) { + toDisableTo.push_back("NoCompas"); + } + + if (!toDisableTo.empty()) { + Choice_ = toDisableTo[rand() % toDisableTo.size()]; + } } void TEulaMenu::Input(SDL_Event* event) { switch (event->type) { case SDL_KEYDOWN: if (event->key.keysym.sym == 'x') { + if (!Choice_.empty()) { + App_->State().Variable(Choice_).SetBool(true); + + if (!App_->State().Variable("LimitedGrenades").Bool()) { + App_->State().Variable("Grenades").SetInt(3); + } + App_->AudioManager().Play("Audio/PowerUp.wav"); + App_->State().Variable("Time").SetDouble(App_->State().Variable("Time").Double() + 30000); + App_->EntityManager().AddToDeferred("RoomEntity"); + App_->EntityManager().Reset(); + } + } + else if (event->key.keysym.sym == 'c') { + if (!App_->State().Variable("LimitedGrenades").Bool()) { + App_->State().Variable("Grenades").SetInt(3); + } + App_->AudioManager().Play("Audio/Pickup.wav"); App_->EntityManager().AddToDeferred("RoomEntity"); App_->EntityManager().Reset(); } @@ -1313,11 +1405,33 @@ void TEulaMenu::Update(std::uint32_t delta) { void TEulaMenu::Draw() const { RenderManager_.SetLayer(NGame::TRenderManager::Interface); - App_->FontManager().Draw(NGame::TFontManager::Gold, {}, "Press X to gain debuff and continue"); + + NGame::Vec2i position = {50, 50}; + std::string text; + + if (Choice_.empty()) { + text = "There is nothing that you can do"; + text += "\n\nPress C to ingore"; + App_->FontManager().Draw(NGame::TFontManager::Red, position, text); + } else { + if (Choice_ == "FallDamage") { + text = "Gain 30 seconds, but there will be\nfall damage"; + } else if (Choice_ == "InstantMines") { + text = "Gain 30 seconds, but mines will explode\ninstantly"; + } else if (Choice_ == "LimitedGrenades") { + text = "Gain 30 seconds, but grenades will\nno longer replenish"; + } else if (Choice_ == "DarkLevel") { + text = "Gain 30 seconds, but the cave is now\nshrouded in darkness"; + } else if (Choice_ == "NoCompas") { + text = "Gain 30 seconds, but you lose your compass"; + } + text += "\n\nPress X to accept or C to ingore"; + App_->FontManager().Draw(NGame::TFontManager::Gold, position, text); + } + } void TEulaMenu::Alarm(NGame::TAlarm::TId id) { - } TIntroMenu::TIntroMenu(NGame::TEntity::TId id) @@ -1332,6 +1446,7 @@ void TIntroMenu::Input(SDL_Event* event) { switch (event->type) { case SDL_KEYDOWN: if (event->key.keysym.sym == 'x') { + App_->AudioManager().Play("Audio/Pickup.wav"); App_->EntityManager().AddToDeferred("MainMenu"); App_->EntityManager().Reset(); } @@ -1419,7 +1534,7 @@ TGrenadeEntity::TGrenadeEntity(NGame::TEntity::TId id) , SpriteManager_(App_->SpriteManager()) { SetSize(9); Sprite_ = NGame::TApp::Instance()->SpriteManager().Get("Sprites/Grenade.txt"); - Alarm_.Set(0, 3000 + rand() % 1000); + Alarm_.Set(0, 1000 + rand() % 1000); Alarm_.Set(1, 100); } diff --git a/src/RoomEntity.h b/src/RoomEntity.h index d00b9b9..fd4f04b 100644 --- a/src/RoomEntity.h +++ b/src/RoomEntity.h @@ -319,6 +319,8 @@ private: bool DisplayEnd_ = false; bool WhiteFade_ = false; std::size_t FadeAmount_ = 0; + double RemainingTime_ = 0; + int Grenades_ = 0; }; class TBackgroundTiler : public NGame::TEntity { @@ -385,6 +387,7 @@ private: NGame::TApp* App_; NGame::TSpriteManager& SpriteManager_; NGame::TRenderManager& RenderManager_; + std::string Choice_; }; class TIntroMenu : public NGame::TEntity {