Implement city production projects for units and buildings
This commit is contained in:
+133
-8
@@ -42,6 +42,10 @@ void GameManager::init(const QString &baseDir, Matrix2D<TerrainPoint> *mapPoints
|
||||
QString unitsConfigDir = baseDir + "/config/units";
|
||||
m_unitConfigManager.loadFromDirectory(unitsConfigDir);
|
||||
|
||||
// Load building & district YAML configurations
|
||||
QString buildingsConfigPath = baseDir + "/config/buildings.yaml";
|
||||
m_buildingConfigManager.loadConfigFile(buildingsConfigPath);
|
||||
|
||||
const CityConfig &cCfg = TerrainYieldManager::instance().getCityConfig();
|
||||
|
||||
// Initialize player national resources from configuration
|
||||
@@ -272,6 +276,29 @@ CityYieldSummary GameManager::calculateCityYields(const City &city, Matrix2D<Ter
|
||||
else if (assignedRes == "geloof") summary.faith += amount;
|
||||
else if (assignedRes == "goud") summary.gold += amount;
|
||||
}
|
||||
|
||||
// Additional yields and multipliers from completed city buildings
|
||||
float totalFoodMultiplier = 1.0f;
|
||||
for (const QString &bId : city.completedBuildings) {
|
||||
const BuildingConfig *bCfg = m_buildingConfigManager.getBuilding(bId);
|
||||
if (!bCfg) continue;
|
||||
|
||||
summary.food += bCfg->foodBonus;
|
||||
summary.labor += bCfg->laborBonus;
|
||||
summary.science += bCfg->scienceBonus;
|
||||
summary.culture += bCfg->cultureBonus;
|
||||
summary.faith += bCfg->faithBonus;
|
||||
summary.gold += bCfg->goldBonus;
|
||||
|
||||
if (bCfg->foodModifier > 0.0f) {
|
||||
totalFoodMultiplier += bCfg->foodModifier;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalFoodMultiplier != 1.0f) {
|
||||
summary.food = (int)std::round(summary.food * totalFoodMultiplier);
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
@@ -286,8 +313,15 @@ void GameManager::processTickYields(Matrix2D<TerrainPoint> *mapPoints, int numPo
|
||||
int foodGrown = (int)std::round(yields.food * cfg.foodToInhabitantsRatio);
|
||||
city.inhabitants += foodGrown;
|
||||
|
||||
// 2. Labor is accumulated locally in the city:
|
||||
city.accumulatedLabor += yields.labor;
|
||||
// 2. Labor contributes to active project or accumulates locally:
|
||||
if (city.hasActiveProject()) {
|
||||
city.currentProject.currentLabor += yields.labor;
|
||||
if (city.currentProject.currentLabor >= city.currentProject.laborCost) {
|
||||
completeCityProject(city, mapPoints, numPoints05);
|
||||
}
|
||||
} else {
|
||||
city.accumulatedLabor += yields.labor;
|
||||
}
|
||||
|
||||
// 3. National resources go to the owner player:
|
||||
Player *owner = m_playerManager.getPlayer(city.playerId);
|
||||
@@ -300,8 +334,10 @@ void GameManager::processTickYields(Matrix2D<TerrainPoint> *mapPoints, int numPo
|
||||
|
||||
// Floating growth text over city
|
||||
QVector3D ftPos = city.worldPos + QVector3D(0.0f, 0.0f, 1.1f);
|
||||
addFloatingText(QString("+%1 Inwoners | +%2 Cultuur").arg(foodGrown).arg(yields.culture),
|
||||
ftPos, QColor(100, 255, 140), 2.5f);
|
||||
QString growthMsg = Localization::instance().get(
|
||||
"ui.city.growth_float", "+%1 Inhabitants | +%2 Culture")
|
||||
.arg(foodGrown).arg(yields.culture);
|
||||
addFloatingText(growthMsg, ftPos, QColor(100, 255, 140), 2.5f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,8 +378,8 @@ bool GameManager::buyTileForCity(int cityId, int tx, int ty)
|
||||
int cost = cfg.tileCultureCost;
|
||||
if (player->culture < cost) {
|
||||
QVector2D w = Pathfinder::hexToWorld(tx, ty);
|
||||
addFloatingText(QString("Niet genoeg Cultuur! (%1 nodig)").arg(cost),
|
||||
QVector3D(w.x(), w.y(), 0.8f), QColor(255, 90, 90), 2.0f);
|
||||
QString notEnough = Localization::instance().get("ui.city.not_enough_culture", "Not enough Culture! (%1 needed)").arg(cost);
|
||||
addFloatingText(notEnough, QVector3D(w.x(), w.y(), 0.8f), QColor(255, 90, 90), 2.0f);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -352,11 +388,100 @@ bool GameManager::buyTileForCity(int cityId, int tx, int ty)
|
||||
city->addTerritory(tx, ty);
|
||||
|
||||
QVector2D w = Pathfinder::hexToWorld(tx, ty);
|
||||
addFloatingText(QString("Tegel gekocht! (-%1 Cultuur)").arg(cost),
|
||||
QVector3D(w.x(), w.y(), 0.8f), QColor(210, 120, 255), 2.5f);
|
||||
QString boughtMsg = Localization::instance().get("ui.unit.tile_purchased", "Tile purchased! (-%1 Culture)").arg(cost);
|
||||
addFloatingText(boughtMsg, QVector3D(w.x(), w.y(), 0.8f), QColor(210, 120, 255), 2.5f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameManager::setCityProject(int cityId, CityProjectType type, const QString &id)
|
||||
{
|
||||
City *city = getCityById(cityId);
|
||||
if (!city) return;
|
||||
|
||||
if (type == CityProjectType::Unit) {
|
||||
const UnitConfig *uCfg = m_unitConfigManager.getConfig(id);
|
||||
if (!uCfg) return;
|
||||
city->startProject(CityProjectType::Unit, id, uCfg->name, uCfg->laborCost);
|
||||
QString msg = Localization::instance().get("ui.city.project_started", "%1: Started %2")
|
||||
.arg(city->name).arg(uCfg->name);
|
||||
addFloatingText(msg, city->worldPos + QVector3D(0.0f, 0.0f, 1.2f), QColor(255, 230, 80), 2.5f);
|
||||
} else if (type == CityProjectType::Building) {
|
||||
const BuildingConfig *bCfg = m_buildingConfigManager.getBuilding(id);
|
||||
if (!bCfg) return;
|
||||
if (city->hasBuilding(id)) {
|
||||
QString msg = Localization::instance().get("ui.city.already_built", "%1 already built!").arg(bCfg->name);
|
||||
addFloatingText(msg, city->worldPos + QVector3D(0.0f, 0.0f, 1.2f), QColor(255, 100, 100), 2.2f);
|
||||
return;
|
||||
}
|
||||
city->startProject(CityProjectType::Building, id, bCfg->name, bCfg->laborCost, bCfg->district);
|
||||
QString msg = Localization::instance().get("ui.city.project_started", "%1: Started %2")
|
||||
.arg(city->name).arg(bCfg->name);
|
||||
addFloatingText(msg, city->worldPos + QVector3D(0.0f, 0.0f, 1.2f), QColor(255, 230, 80), 2.5f);
|
||||
}
|
||||
}
|
||||
|
||||
void GameManager::cancelCityProject(int cityId)
|
||||
{
|
||||
City *city = getCityById(cityId);
|
||||
if (!city || !city->hasActiveProject()) return;
|
||||
|
||||
QString msg = Localization::instance().get("ui.city.project_cancelled", "%1: Project cancelled").arg(city->name);
|
||||
addFloatingText(msg, city->worldPos + QVector3D(0.0f, 0.0f, 1.2f), QColor(255, 180, 80), 2.0f);
|
||||
city->cancelProject();
|
||||
}
|
||||
|
||||
void GameManager::completeCityProject(City &city, Matrix2D<TerrainPoint> *mapPoints, int numPoints05)
|
||||
{
|
||||
if (!city.hasActiveProject()) return;
|
||||
|
||||
if (city.currentProject.type == CityProjectType::Unit) {
|
||||
QString unitId = city.currentProject.id;
|
||||
const UnitConfig *uCfg = m_unitConfigManager.getConfig(unitId);
|
||||
|
||||
// Find spawn location: city center or adjacent empty hex
|
||||
int spawnX = city.tileX;
|
||||
int spawnY = city.tileY;
|
||||
|
||||
if (getUnitAt(spawnX, spawnY) != nullptr) {
|
||||
for (int e = 0; e < 6; ++e) {
|
||||
int nx, ny;
|
||||
getHexNeighbor(city.tileX, city.tileY, e, nx, ny);
|
||||
if (nx >= 0 && nx < MAP_WIDTH && ny >= 0 && ny < MAP_HEIGHT) {
|
||||
if (getUnitAt(nx, ny) == nullptr &&
|
||||
TerrainMovementManager::instance().isHexPassable(nx, ny, uCfg ? uCfg->movementDomain : MovementDomain::Land, mapPoints, numPoints05, uCfg)) {
|
||||
spawnX = nx;
|
||||
spawnY = ny;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Unit *spawned = spawnUnit(unitId, city.playerId, spawnX, spawnY, mapPoints, numPoints05);
|
||||
QString unitName = spawned ? spawned->config.name : (uCfg ? uCfg->name : unitId);
|
||||
QString msg = Localization::instance().get("ui.city.unit_trained", "%1: %2 trained!")
|
||||
.arg(city.name).arg(unitName);
|
||||
addFloatingText(msg, city.worldPos + QVector3D(0.0f, 0.0f, 1.3f), QColor(80, 255, 140), 3.0f);
|
||||
|
||||
} else if (city.currentProject.type == CityProjectType::Building) {
|
||||
QString bId = city.currentProject.id;
|
||||
city.addBuilding(bId);
|
||||
|
||||
const BuildingConfig *bCfg = m_buildingConfigManager.getBuilding(bId);
|
||||
QString bName = bCfg ? bCfg->name : bId;
|
||||
const DistrictConfig *dCfg = m_buildingConfigManager.getDistrict(bCfg ? bCfg->district : "center");
|
||||
QString dName = dCfg ? dCfg->name : "City Center";
|
||||
|
||||
QString msg = Localization::instance().get("ui.city.building_completed", "%1: %2 completed in %3!")
|
||||
.arg(city.name).arg(bName).arg(dName);
|
||||
addFloatingText(msg, city.worldPos + QVector3D(0.0f, 0.0f, 1.3f), QColor(80, 240, 255), 3.5f);
|
||||
}
|
||||
|
||||
int excess = city.currentProject.currentLabor - city.currentProject.laborCost;
|
||||
city.accumulatedLabor += std::max(0, excess);
|
||||
city.cancelProject();
|
||||
}
|
||||
|
||||
void GameManager::commandMoveSelected(int targetX, int targetY, Matrix2D<TerrainPoint> *mapPoints, int numPoints05)
|
||||
{
|
||||
Unit *selected = getSelectedUnit();
|
||||
|
||||
Reference in New Issue
Block a user