#include "gamemanager.h" #include "pathfinder.h" #include "yamlparser.h" #include #include GameManager& GameManager::instance() { static GameManager s_instance; return s_instance; } GameManager::GameManager() : m_selectedUnitId(-1), m_actionMode(ActionMode::None), m_movementTickDuration(30.0f), m_tickTimer(0.0f), m_simSpeedMultiplier(1.0f), m_nextUnitId(0), m_nextCityId(0), m_initialized(false) { } void GameManager::init(const QString &baseDir, Matrix2D *mapPoints, int numPoints05) { if (m_initialized) return; // Load game settings QString gameConfigPath = baseDir + "/config/game.yaml"; YamlNode gameRoot = YamlParser::parseFile(gameConfigPath); if (gameRoot.hasChild("movement_tick_duration")) { m_movementTickDuration = gameRoot["movement_tick_duration"].asFloat(30.0f); } // Load unit YAML configurations QString unitsConfigDir = baseDir + "/config/units"; m_unitConfigManager.loadFromDirectory(unitsConfigDir); // Spawn initial demo units near starting camera position (Cam: ~28, 16 km -> hex ~37, 18) // Player 1 (Blue & Gold) spawnUnit("settler", 1, 36, 18, mapPoints, numPoints05); spawnUnit("warrior", 1, 37, 18, mapPoints, numPoints05); spawnUnit("slinger", 1, 37, 19, mapPoints, numPoints05); // Player 2 (Red & White) - Opponent units spawnUnit("warrior", 2, 40, 18, mapPoints, numPoints05); spawnUnit("slinger", 2, 41, 19, mapPoints, numPoints05); m_initialized = true; qDebug() << "GameManager initialized with" << m_units.size() << "units."; } Unit* GameManager::spawnUnit(const QString &configId, int playerId, int tileX, int tileY, Matrix2D *mapPoints, int numPoints05) { const UnitConfig *cfg = m_unitConfigManager.getConfig(configId); if (!cfg) { qWarning() << "Unit config not found:" << configId; return nullptr; } QVector2D w = Pathfinder::hexToWorld(tileX, tileY); float gz = Unit::getTerrainHeight(w.x(), w.y(), mapPoints, numPoints05); int id = getNextUnitId(); auto unit = std::make_shared(id, playerId, *cfg, tileX, tileY, gz); m_units.push_back(unit); return unit.get(); } void GameManager::update(float deltaTime, Matrix2D *mapPoints, int numPoints05) { // Update movement tick timer m_tickTimer += deltaTime * m_simSpeedMultiplier; if (m_tickTimer >= m_movementTickDuration) { m_tickTimer = std::fmod(m_tickTimer, m_movementTickDuration); } // Update units for (auto &u : m_units) { if (!u->isDead()) { u->update(deltaTime, m_movementTickDuration, m_simSpeedMultiplier, mapPoints, numPoints05); } } // Clean up dead units for (auto it = m_units.begin(); it != m_units.end(); ) { if ((*it)->isDead()) { if ((*it)->id == m_selectedUnitId) { m_selectedUnitId = -1; m_actionMode = ActionMode::None; } it = m_units.erase(it); } else { ++it; } } // Update floating texts for (auto it = m_floatingTexts.begin(); it != m_floatingTexts.end(); ) { it->life -= deltaTime; it->worldPos.setZ(it->worldPos.z() + deltaTime * 0.4f); // Float upward if (it->life <= 0.0f) { it = m_floatingTexts.erase(it); } else { ++it; } } } Unit* GameManager::getSelectedUnit() { return getUnitById(m_selectedUnitId); } const Unit* GameManager::getSelectedUnit() const { for (const auto &u : m_units) { if (u->id == m_selectedUnitId) return u.get(); } return nullptr; } void GameManager::selectUnit(int id) { m_selectedUnitId = id; m_actionMode = ActionMode::None; } void GameManager::deselectUnit() { m_selectedUnitId = -1; m_actionMode = ActionMode::None; } Unit* GameManager::getUnitAt(int tx, int ty) { for (auto &u : m_units) { if (!u->isDead() && u->tileX == tx && u->tileY == ty) { return u.get(); } } return nullptr; } Unit* GameManager::getUnitById(int id) { for (auto &u : m_units) { if (u->id == id && !u->isDead()) return u.get(); } return nullptr; } void GameManager::commandMoveSelected(int targetX, int targetY, Matrix2D *mapPoints, int numPoints05) { Unit *selected = getSelectedUnit(); if (!selected) return; if (targetX == selected->tileX && targetY == selected->tileY) { return; } std::vector> path = Pathfinder::findPath( selected->tileX, selected->tileY, targetX, targetY, mapPoints, numPoints05 ); if (path.size() > 1) { selected->setPath(path); addFloatingText("Verplaatsen", selected->worldPos + QVector3D(0, 0, 0.8f), QColor(255, 230, 80)); } } void GameManager::commandAttack(Unit *target) { Unit *selected = getSelectedUnit(); if (!selected || !target) return; if (!selected->canAttack(*target)) { addFloatingText("Buiten bereik!", selected->worldPos + QVector3D(0, 0, 0.8f), QColor(255, 80, 80)); return; } float damage = (float)selected->config.attackDamage; selected->performAttack(*target); QString dmgText = QString("-%1 HP").arg((int)damage); addFloatingText(dmgText, target->worldPos + QVector3D(0, 0, 0.8f), QColor(255, 50, 50)); if (target->isDead()) { addFloatingText("Verslagen!", target->worldPos + QVector3D(0, 0, 1.2f), QColor(255, 200, 50), 2.2f); } m_actionMode = ActionMode::None; } void GameManager::commandBuildCitySelected(Matrix2D *mapPoints, int numPoints05) { Unit *selected = getSelectedUnit(); if (!selected || !selected->config.canBuildCity()) return; // Check if tile already has a city for (const auto &c : m_cities) { if (c.tileX == selected->tileX && c.tileY == selected->tileY) { addFloatingText("Al een stad hier!", selected->worldPos + QVector3D(0, 0, 0.8f), QColor(255, 80, 80)); return; } } int cId = getNextCityId(); QString cityName = QString("Stad %1").arg(cId); QVector2D w = Pathfinder::hexToWorld(selected->tileX, selected->tileY); float gz = Unit::getTerrainHeight(w.x(), w.y(), mapPoints, numPoints05); m_cities.emplace_back(cId, selected->playerId, cityName, selected->tileX, selected->tileY, QVector3D(w.x(), w.y(), gz)); addFloatingText(QString("%1 gesticht!").arg(cityName), QVector3D(w.x(), w.y(), gz + 1.2f), QColor(80, 255, 120), 2.5f); // Consuming settler selected->takeDamage(99999.0f); m_selectedUnitId = -1; m_actionMode = ActionMode::None; } void GameManager::addFloatingText(const QString &text, const QVector3D &worldPos, const QColor &color, float duration) { m_floatingTexts.push_back({text, worldPos, duration, duration, color}); }