#include "gamemanager.h" #include "pathfinder.h" #include "yamlparser.h" #include "terrainyieldmanager.h" #include #include GameManager& GameManager::instance() { static GameManager s_instance; return s_instance; } GameManager::GameManager() : m_selectedUnitId(-1), m_activeCityViewId(-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 terrain yields & city configuration TerrainYieldManager::instance().loadConfigs(baseDir); // 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 an initial established city for Player 1 at (35, 17) const CityConfig &cCfg = TerrainYieldManager::instance().getCityConfig(); int cId = getNextCityId(); QVector2D w = Pathfinder::hexToWorld(35, 17); float gz = Unit::getTerrainHeight(w.x(), w.y(), mapPoints, numPoints05); City initialCity(cId, 1, "Roma", 35, 17, QVector3D(w.x(), w.y(), gz), cCfg.initialInhabitants, cCfg.inhabitantsPerPop, cCfg.initialBorderRadius); // Assign initial 1 pop to work the city center tile QString tType = TerrainYieldManager::instance().getTerrainType(35, 17, mapPoints, numPoints05); const TerrainYieldConfig *yCfg = TerrainYieldManager::instance().getYieldConfig(tType); QString defRes = yCfg ? yCfg->primaryResource : "voedsel"; initialCity.assignWorker(35, 17, defRes); m_cities.push_back(initialCity); // Spawn initial demo units near starting camera position // 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_cities.size() << "cities and" << 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); 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_activeCityViewId = -1; // Close city view when unit is selected 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; } City* GameManager::getCityAt(int tx, int ty) { for (auto &c : m_cities) { if (c.tileX == tx && c.tileY == ty) return &c; } return nullptr; } City* GameManager::getCityById(int id) { for (auto &c : m_cities) { if (c.id == id) return &c; } return nullptr; } City* GameManager::getActiveCityView() { return getCityById(m_activeCityViewId); } const City* GameManager::getActiveCityView() const { for (const auto &c : m_cities) { if (c.id == m_activeCityViewId) return &c; } return nullptr; } void GameManager::enterCityView(int cityId) { m_activeCityViewId = cityId; m_selectedUnitId = -1; m_actionMode = ActionMode::None; } void GameManager::exitCityView() { m_activeCityViewId = -1; } CityYieldSummary GameManager::calculateCityYields(const City &city, Matrix2D *mapPoints, int numPoints05) const { CityYieldSummary summary; for (const auto &pair : city.workedTiles) { int tx = pair.first.first; int ty = pair.first.second; QString assignedRes = pair.second; QString tType = TerrainYieldManager::instance().getTerrainType(tx, ty, mapPoints, numPoints05); const TerrainYieldConfig *yCfg = TerrainYieldManager::instance().getYieldConfig(tType); if (!yCfg) continue; int amount = yCfg->getYield(assignedRes); if (assignedRes == "voedsel") summary.food += amount; else if (assignedRes == "arbeid") summary.labor += amount; else if (assignedRes == "kennis") summary.science += amount; else if (assignedRes == "cultuur") summary.culture += amount; else if (assignedRes == "geloof") summary.faith += amount; else if (assignedRes == "goud") summary.gold += amount; } return summary; } 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; } } const CityConfig &cCfg = TerrainYieldManager::instance().getCityConfig(); 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); City newCity(cId, selected->playerId, cityName, selected->tileX, selected->tileY, QVector3D(w.x(), w.y(), gz), cCfg.initialInhabitants, cCfg.inhabitantsPerPop, cCfg.initialBorderRadius); // Assign initial worker to center tile QString tType = TerrainYieldManager::instance().getTerrainType(selected->tileX, selected->tileY, mapPoints, numPoints05); const TerrainYieldConfig *yCfg = TerrainYieldManager::instance().getYieldConfig(tType); QString defRes = yCfg ? yCfg->primaryResource : "voedsel"; newCity.assignWorker(selected->tileX, selected->tileY, defRes); m_cities.push_back(newCity); 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}); }