diff --git a/audio/bishkek fight calm.ogg b/audio/bishkek fight calm.ogg new file mode 100644 index 0000000..359f9c7 --- /dev/null +++ b/audio/bishkek fight calm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f3f51bbd965dae892a22122d29cb469798d50a723f80ff4dbb2425fbe960137 +size 3888436 diff --git a/audio/bishkek fight.ogg b/audio/bishkek fight.ogg new file mode 100644 index 0000000..e0bf8a8 --- /dev/null +++ b/audio/bishkek fight.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:684877cb31d792870d9552c07b7bdc8827e98101439006e60e9af3c29c294966 +size 4868335 diff --git a/audio/bishkek univer day.ogg b/audio/bishkek univer day.ogg new file mode 100644 index 0000000..0bd8ee2 --- /dev/null +++ b/audio/bishkek univer day.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b286bbd8d0e216f0034c6d59b37d1746b4f69048eb90597bb9c838f145dabbc4 +size 5317400 diff --git a/audio/main menu final.ogg b/audio/main menu final.ogg new file mode 100644 index 0000000..d5deeb0 --- /dev/null +++ b/audio/main menu final.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c56cd872468619fea85b89b23b4d175ada3d62eeab3f33d78bf931ac179a5df7 +size 4265537 diff --git a/audio/obshaga.ogg b/audio/obshaga.ogg new file mode 100644 index 0000000..c2061e7 --- /dev/null +++ b/audio/obshaga.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbb3f0f1d6765f472cf9aa36a3e4b25b8dcd478fc9b25f8d68fd5e3944fcc423 +size 4329675 diff --git a/src/AudioPlayerAsync.cpp b/src/AudioPlayerAsync.cpp index d928f28..b5a6b39 100644 --- a/src/AudioPlayerAsync.cpp +++ b/src/AudioPlayerAsync.cpp @@ -99,9 +99,20 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in } void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) { + std::cout << "AudioPlayerAsync::playMusicAsync called step 1" << std::endl; if (!initialized) return; + std::cout << "AudioPlayerAsync::playMusicAsync called step 1" << std::endl; + + if (filePath == currentTrack) + { + return; + } + + currentTrack = filePath; auto task = [filePath, loops]() { + std::cout << "AudioPlayerAsync::playMusicAsync called step 3" << std::endl; + Mix_Music* music = Mix_LoadMUS(filePath.c_str()); if (!music) { std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl; @@ -124,6 +135,9 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) { void AudioPlayerAsync::stopMusicAsync() { if (!initialized) return; + + currentTrack = ""; + #ifdef __EMSCRIPTEN__ Mix_HaltMusic(); #else diff --git a/src/AudioPlayerAsync.h b/src/AudioPlayerAsync.h index ab699b5..4ae1fd5 100644 --- a/src/AudioPlayerAsync.h +++ b/src/AudioPlayerAsync.h @@ -46,5 +46,7 @@ private: std::unordered_map soundCache; std::mutex soundCacheMutex; + std::string currentTrack; + bool initialized = false; }; \ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index 6337b46..96695de 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -74,8 +74,8 @@ namespace ZL Game::Game() : newTickCount(0) , lastTickCount(0) - , menuManager(renderer, gameState) , audioPlayer(std::make_unique()) + , menuManager(renderer, gameState, *audioPlayer) { } @@ -327,14 +327,13 @@ namespace ZL menuManager.startGameFunc = [this]() { gameState.currentLocationName = "location_dorm"; currentLocation()->scriptEngine.callLocationEnterCallback(); + this->audioPlayer->playMusicAsync("audio/obshaga.ogg"); }; menuManager.onSaveGame = [this](int slot) { saveGame(slot); }; menuManager.onLoadGame = [this](int slot) { loadGame(slot); }; menuManager.getSlotInfoFunc = [this](int slot) { return readSlotInfo(slot); }; - loadingCompleted = true; - if (audioPlayer->init()) { audioPlayer->setMusicVolume(100); audioPlayer->setSoundVolume(80); @@ -344,6 +343,11 @@ namespace ZL std::cout << "Audio initialization failed" << std::endl; } + audioPlayer->playMusicAsync("audio/main menu final.ogg"); + + loadingCompleted = true; + + } void Game::createLocations() @@ -448,6 +452,12 @@ namespace ZL } } + gameState.locations["uni_interior"]->requestDarklandsPlayBattleMusic = [this]() { + audioPlayer->playMusicAsync("audio/bishkek fight.ogg"); + }; + gameState.locations["uni_interior"]->requestDarklandsPlayNormalMusic = [this]() { + audioPlayer->playMusicAsync("audio/bishkek fight calm.ogg"); + }; LocationSetup uniExteriorParams = uniInteriorParams; uniExteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects_uni_exterior_x.json"; uniExteriorParams.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_exterior_x.json"; diff --git a/src/Game.h b/src/Game.h index c165d55..ece5782 100644 --- a/src/Game.h +++ b/src/Game.h @@ -57,6 +57,8 @@ namespace ZL { bool startDarklandsTransition(); bool startNightTransition(); + std::unique_ptr audioPlayer; + MenuManager menuManager; void activateSlowMoEffect(); @@ -91,8 +93,7 @@ namespace ZL { float dragStartAzimuth = 0.0f; float dragStartInclination = 0.0f; - std::unique_ptr audioPlayer; - + Location* currentLocation() const; void saveGame(int slot); diff --git a/src/Location.cpp b/src/Location.cpp index fa21917..e018c3a 100644 --- a/src/Location.cpp +++ b/src/Location.cpp @@ -1516,9 +1516,32 @@ namespace ZL dialogueSystem.update(static_cast(delta)); updateTriggerZones(player->state.position); } + + bool npcAttacking = false; + for (auto& npc : npcs) { npc->update(delta, resolver); + + if (npc->state.enabled && npc->state.canAttack && (!npc->state.pathWaypoints.empty() || npc->state.battle_state != 0) && npc->getHp() > 0.f) + { + //std::cout << "[NPC] NPC '" << npc->state.npcId << "' is attacking target index " << npc->state.attackTargetIndex << std::endl; + npcAttacking = true; + } + } + + if (state.isDarklands) + { + if (npcAttacking && !playBattleMusic) + { + playBattleMusic = true; + if (requestDarklandsPlayBattleMusic) requestDarklandsPlayBattleMusic(); + } + else if (!npcAttacking && playBattleMusic) + { + playBattleMusic = false; + if (requestDarklandsPlayNormalMusic) requestDarklandsPlayNormalMusic(); + } } resolveCharacterCollisions(); diff --git a/src/Location.h b/src/Location.h index 8de3287..a9b570f 100644 --- a/src/Location.h +++ b/src/Location.h @@ -114,6 +114,10 @@ namespace ZL std::function requestReturnToMainMenu; std::function requestSetChatUnread; + std::function requestDarklandsPlayBattleMusic; + std::function requestDarklandsPlayNormalMusic; + + // Navigation editor — toggle with 'N', save with 'B', right-click to finalize polygon EditorMode editorMode = EditorMode::None; LocationEditor editor; @@ -126,6 +130,8 @@ namespace ZL int lastMouseX = 0; int lastMouseY = 0; + bool playBattleMusic = false; + void setup(const LocationSetup& params, Quest::QuestJournal* journal); void setupNavigation(const std::vector& paths); diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp index c04ab09..d90c46b 100644 --- a/src/MenuManager.cpp +++ b/src/MenuManager.cpp @@ -92,9 +92,10 @@ namespace ZL { } } - MenuManager::MenuManager(Renderer& iRenderer, GameState& gameState) : + MenuManager::MenuManager(Renderer& iRenderer, GameState& gameState, AudioPlayerAsync& audioPlayer) : renderer(iRenderer), - gameState_(gameState) + gameState_(gameState), + audioPlayer_(audioPlayer) { } @@ -250,6 +251,9 @@ namespace ZL { void MenuManager::showMainMenu() { if (onResetGame) onResetGame(); + + audioPlayer_.playMusicAsync("audio/main menu final.ogg"); + uiState_ = GameUiState::MainMenu; uiManager.clearMenuStack(); topUiManager.replaceRoot(nullptr); @@ -900,13 +904,46 @@ namespace ZL { uiManager.replaceRoot(gameState_.isDarklands ? hudUniExtDarkRoot : hudUniExtRoot); applyCurrentHealthBar(); setupGameplayHudCallbacks(); + if (gameState_.isDarklands) + { + audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg"); + } + else if (gameState_.isNight) + { + audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg"); + } + else + { + audioPlayer_.playMusicAsync("audio/bishkek univer day.ogg"); + } } else if (locationName == "uni_interior") { applyUniIntHud(); + if (gameState_.isDarklands) + { + audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg"); + } + else if (gameState_.isNight) + { + audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg"); + } + else + { + audioPlayer_.playMusicAsync("audio/bishkek univer day.ogg"); + } } else { // Returning to dorm: reuse step5ab, suppress already-completed hints uiManager.replaceRoot(hudStep5abRoot); applyCurrentHealthBar(); setupStep5Callbacks(); + + if (gameState_.isNight) + { + audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg"); + } + else + { + audioPlayer_.playMusicAsync("audio/obshaga.ogg"); + } } } diff --git a/src/MenuManager.h b/src/MenuManager.h index c3a712b..e2ccedf 100644 --- a/src/MenuManager.h +++ b/src/MenuManager.h @@ -9,6 +9,7 @@ #include #include #include +#include "AudioPlayerAsync.h" // Forward-declared here to avoid pulling Game.h into MenuManager.h. namespace ZL { struct SaveSlotInfo; } @@ -31,7 +32,7 @@ namespace ZL { UiManager uiManager; UiManager topUiManager; - MenuManager(Renderer& iRenderer, GameState& gameState); + MenuManager(Renderer& iRenderer, GameState& gameState, AudioPlayerAsync& audioPlayer); void setup(Inventory& inv, const std::string& zipFile); @@ -101,6 +102,7 @@ namespace ZL { private: GameState& gameState_; + AudioPlayerAsync& audioPlayer_; void enterGameplay(bool isFromLoad = false); void applyHudForCurrentState();