Browse Source

Integrate AudioSystem and AudioEventHandler into game engine

Co-authored-by: djeada <[email protected]>
copilot-swe-agent[bot] 2 months ago
parent
commit
4a23182b53
2 changed files with 128 additions and 1 deletions
  1. 121 1
      app/core/game_engine.cpp
  2. 7 0
      app/core/game_engine.h

+ 121 - 1
app/core/game_engine.cpp

@@ -5,6 +5,7 @@
 #include "../models/cursor_manager.h"
 #include "../models/cursor_manager.h"
 #include "../models/hover_tracker.h"
 #include "../models/hover_tracker.h"
 #include "../utils/json_vec_utils.h"
 #include "../utils/json_vec_utils.h"
+#include "game/audio/AudioSystem.h"
 #include <QBuffer>
 #include <QBuffer>
 #include <QCoreApplication>
 #include <QCoreApplication>
 #include <QCursor>
 #include <QCursor>
@@ -155,6 +156,21 @@ GameEngine::GameEngine() {
   connect(m_mapCatalog.get(), &Game::Map::MapCatalog::allMapsLoaded, this,
   connect(m_mapCatalog.get(), &Game::Map::MapCatalog::allMapsLoaded, this,
           [this]() { emit availableMapsChanged(); });
           [this]() { emit availableMapsChanged(); });
 
 
+  // Initialize Audio System
+  if (AudioSystem::getInstance().initialize()) {
+    qInfo() << "AudioSystem initialized successfully";
+  } else {
+    qWarning() << "Failed to initialize AudioSystem";
+  }
+
+  // Initialize AudioEventHandler
+  m_audioEventHandler = std::make_unique<Game::Audio::AudioEventHandler>(m_world.get());
+  if (m_audioEventHandler->initialize()) {
+    qInfo() << "AudioEventHandler initialized successfully";
+  } else {
+    qWarning() << "Failed to initialize AudioEventHandler";
+  }
+
   connect(m_cursorManager.get(), &CursorManager::modeChanged, this,
   connect(m_cursorManager.get(), &CursorManager::modeChanged, this,
           &GameEngine::cursorModeChanged);
           &GameEngine::cursorModeChanged);
   connect(m_cursorManager.get(), &CursorManager::globalCursorChanged, this,
   connect(m_cursorManager.get(), &CursorManager::globalCursorChanged, this,
@@ -223,7 +239,14 @@ GameEngine::GameEngine() {
           });
           });
 }
 }
 
 
-GameEngine::~GameEngine() = default;
+GameEngine::~GameEngine() {
+  // Shutdown audio subsystem
+  if (m_audioEventHandler) {
+    m_audioEventHandler->shutdown();
+  }
+  AudioSystem::getInstance().shutdown();
+  qInfo() << "AudioSystem shut down";
+}
 
 
 void GameEngine::onMapClicked(qreal sx, qreal sy) {
 void GameEngine::onMapClicked(qreal sx, qreal sy) {
   if (!m_window)
   if (!m_window)
@@ -477,6 +500,11 @@ void GameEngine::update(float dt) {
     dt *= m_runtime.timeScale;
     dt *= m_runtime.timeScale;
   }
   }
 
 
+  // Update ambient state for audio
+  if (!m_runtime.paused && !m_runtime.loading) {
+    updateAmbientState(dt);
+  }
+
   if (m_renderer) {
   if (m_renderer) {
     m_renderer->updateAnimationTime(dt);
     m_renderer->updateAnimationTime(dt);
   }
   }
@@ -1483,3 +1511,95 @@ QVector3D GameEngine::getPatrolPreviewWaypoint() const {
     return QVector3D();
     return QVector3D();
   return m_commandController->getPatrolFirstWaypoint();
   return m_commandController->getPatrolFirstWaypoint();
 }
 }
+
+void GameEngine::updateAmbientState(float dt) {
+  // Update ambient state check every 2 seconds to avoid overhead
+  m_ambientCheckTimer += dt;
+  const float CHECK_INTERVAL = 2.0f;
+  
+  if (m_ambientCheckTimer < CHECK_INTERVAL) {
+    return;
+  }
+  m_ambientCheckTimer = 0.0f;
+
+  // Determine the new ambient state based on game conditions
+  Engine::Core::AmbientState newState = Engine::Core::AmbientState::PEACEFUL;
+
+  // Check victory/defeat first
+  if (!m_runtime.victoryState.isEmpty()) {
+    if (m_runtime.victoryState == "victory") {
+      newState = Engine::Core::AmbientState::VICTORY;
+    } else if (m_runtime.victoryState == "defeat") {
+      newState = Engine::Core::AmbientState::DEFEAT;
+    }
+  } else if (isPlayerInCombat()) {
+    // Check if player units are engaged in combat
+    newState = Engine::Core::AmbientState::COMBAT;
+  } else if (m_entityCache.enemyBarracksAlive && m_entityCache.playerBarracksAlive) {
+    // If both sides have barracks alive, it's tense
+    newState = Engine::Core::AmbientState::TENSE;
+  }
+
+  // Publish state change event if state changed
+  if (newState != m_currentAmbientState) {
+    Engine::Core::AmbientState previousState = m_currentAmbientState;
+    m_currentAmbientState = newState;
+    
+    Engine::Core::EventManager::instance().publish(
+        Engine::Core::AmbientStateChangedEvent(newState, previousState));
+    
+    qInfo() << "Ambient state changed from" << static_cast<int>(previousState) 
+            << "to" << static_cast<int>(newState);
+  }
+}
+
+bool GameEngine::isPlayerInCombat() const {
+  if (!m_world) {
+    return false;
+  }
+
+  // Check if any player unit has an attack target or is under attack
+  auto units = m_world->getEntitiesWith<Engine::Core::UnitComponent>();
+  const float COMBAT_CHECK_RADIUS = 15.0f;
+
+  for (auto *entity : units) {
+    auto *unit = entity->getComponent<Engine::Core::UnitComponent>();
+    if (!unit || unit->ownerId != m_runtime.localOwnerId || unit->health <= 0) {
+      continue;
+    }
+
+    // Check if unit has an attack target
+    if (entity->hasComponent<Engine::Core::AttackTargetComponent>()) {
+      return true;
+    }
+
+    // Check if there are enemies nearby
+    auto *transform = entity->getComponent<Engine::Core::TransformComponent>();
+    if (!transform) {
+      continue;
+    }
+
+    for (auto *otherEntity : units) {
+      auto *otherUnit = otherEntity->getComponent<Engine::Core::UnitComponent>();
+      if (!otherUnit || otherUnit->ownerId == m_runtime.localOwnerId || 
+          otherUnit->health <= 0) {
+        continue;
+      }
+
+      auto *otherTransform = otherEntity->getComponent<Engine::Core::TransformComponent>();
+      if (!otherTransform) {
+        continue;
+      }
+
+      float dx = transform->position.x - otherTransform->position.x;
+      float dz = transform->position.z - otherTransform->position.z;
+      float distSq = dx * dx + dz * dz;
+
+      if (distSq < COMBAT_CHECK_RADIUS * COMBAT_CHECK_RADIUS) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}

+ 7 - 0
app/core/game_engine.h

@@ -6,6 +6,7 @@
 #include "../utils/engine_view_helpers.h"
 #include "../utils/engine_view_helpers.h"
 #include "../utils/movement_utils.h"
 #include "../utils/movement_utils.h"
 #include "../utils/selection_utils.h"
 #include "../utils/selection_utils.h"
+#include "game/audio/AudioEventHandler.h"
 #include "game/core/event_manager.h"
 #include "game/core/event_manager.h"
 #include "game/systems/game_state_serializer.h"
 #include "game/systems/game_state_serializer.h"
 #include <QJsonObject>
 #include <QJsonObject>
@@ -276,6 +277,7 @@ private:
   std::unique_ptr<Game::Systems::SelectionController> m_selectionController;
   std::unique_ptr<Game::Systems::SelectionController> m_selectionController;
   std::unique_ptr<App::Controllers::CommandController> m_commandController;
   std::unique_ptr<App::Controllers::CommandController> m_commandController;
   std::unique_ptr<Game::Map::MapCatalog> m_mapCatalog;
   std::unique_ptr<Game::Map::MapCatalog> m_mapCatalog;
+  std::unique_ptr<Game::Audio::AudioEventHandler> m_audioEventHandler;
   QQuickWindow *m_window = nullptr;
   QQuickWindow *m_window = nullptr;
   RuntimeState m_runtime;
   RuntimeState m_runtime;
   ViewportState m_viewport;
   ViewportState m_viewport;
@@ -291,6 +293,11 @@ private:
   Engine::Core::ScopedEventSubscription<Engine::Core::UnitSpawnedEvent>
   Engine::Core::ScopedEventSubscription<Engine::Core::UnitSpawnedEvent>
       m_unitSpawnedSubscription;
       m_unitSpawnedSubscription;
   EntityCache m_entityCache;
   EntityCache m_entityCache;
+  Engine::Core::AmbientState m_currentAmbientState = Engine::Core::AmbientState::PEACEFUL;
+  float m_ambientCheckTimer = 0.0f;
+
+  void updateAmbientState(float dt);
+  bool isPlayerInCombat() const;
 signals:
 signals:
   void selectedUnitsChanged();
   void selectedUnitsChanged();
   void selectedUnitsDataChanged();
   void selectedUnitsDataChanged();