Files

251 lines
7.3 KiB
C++

#ifndef CITY_H
#define CITY_H
#include <QString>
#include <QVector3D>
#include <vector>
#include <utility>
#include <map>
#include <cmath>
#include "terrain.h"
struct CityYieldSummary {
int food = 0;
int labor = 0;
int science = 0;
int culture = 0;
int faith = 0;
int gold = 0;
};
enum class CityProjectType {
None,
Unit,
Building
};
struct CityProject {
CityProjectType type = CityProjectType::None;
QString id = "";
QString name = "";
int laborCost = 0;
int currentLabor = 0;
QString district = "center";
bool isValid() const { return type != CityProjectType::None && !id.isEmpty(); }
float getProgress() const {
return laborCost > 0 ? std::max(0.0f, std::min(1.0f, (float)currentLabor / (float)laborCost)) : 0.0f;
}
int getRemainingLabor() const {
return std::max(0, laborCost - currentLabor);
}
};
class City {
public:
int id;
int playerId;
QString name;
int tileX;
int tileY;
QVector3D worldPos;
int inhabitants;
int inhabitantsPerPop;
int accumulatedLabor = 0; // Local resource (Arbeid)
CityProject currentProject;
std::vector<CityProject> projectQueue;
std::vector<QString> completedBuildings; // IDs of completed buildings in the city
std::vector<std::pair<int, int>> territory; // Owned hex coordinates
std::map<std::pair<int, int>, QString> workedTiles; // (x, y) -> resource name
std::map<std::pair<int, int>, QString> tilePreferredResources; // (x, y) -> user chosen resource
City(int cId, int pId, const QString &cName, int tx, int ty, const QVector3D &pos,
int initInhabitants = 1000, int initInhabitantsPerPop = 100000, int initRadius = 1);
bool hasBuilding(const QString &buildingId) const {
for (const auto &b : completedBuildings) {
if (b == buildingId) return true;
}
return false;
}
bool addBuilding(const QString &buildingId) {
if (hasBuilding(buildingId)) return false;
completedBuildings.push_back(buildingId);
return true;
}
bool isBuildingQueuedOrBuilt(const QString &buildingId) const {
if (hasBuilding(buildingId)) return true;
if (currentProject.isValid() && currentProject.type == CityProjectType::Building && currentProject.id == buildingId) return true;
for (const auto &q : projectQueue) {
if (q.type == CityProjectType::Building && q.id == buildingId) return true;
}
return false;
}
int getQueuedBuildingPosition(const QString &buildingId) const {
for (size_t i = 0; i < projectQueue.size(); ++i) {
if (projectQueue[i].type == CityProjectType::Building && projectQueue[i].id == buildingId) {
return (int)i + 1;
}
}
return 0;
}
int getQueuedUnitCount(const QString &unitId) const {
int count = 0;
for (const auto &q : projectQueue) {
if (q.type == CityProjectType::Unit && q.id == unitId) {
count++;
}
}
return count;
}
bool hasActiveProject() const {
return currentProject.isValid();
}
void startProject(CityProjectType type, const QString &projId, const QString &projName, int cost, const QString &district = "center") {
currentProject.type = type;
currentProject.id = projId;
currentProject.name = projName;
currentProject.laborCost = std::max(1, cost);
currentProject.currentLabor = 0;
currentProject.district = district.isEmpty() ? "center" : district;
}
bool queueProject(CityProjectType type, const QString &projId, const QString &projName, int cost, const QString &district = "center", int maxQueueSize = 6) {
if (!hasActiveProject()) {
startProject(type, projId, projName, cost, district);
return true;
}
if ((int)projectQueue.size() >= maxQueueSize) {
return false;
}
CityProject q;
q.type = type;
q.id = projId;
q.name = projName;
q.laborCost = std::max(1, cost);
q.currentLabor = 0;
q.district = district.isEmpty() ? "center" : district;
projectQueue.push_back(q);
return true;
}
void cancelActiveProject() {
if (!projectQueue.empty()) {
currentProject = projectQueue.front();
projectQueue.erase(projectQueue.begin());
} else {
currentProject = CityProject();
}
}
void cancelProject() {
cancelActiveProject();
}
bool removeQueuedProject(int index) {
if (index >= 0 && index < (int)projectQueue.size()) {
projectQueue.erase(projectQueue.begin() + index);
return true;
}
return false;
}
void advanceQueue() {
if (!projectQueue.empty()) {
currentProject = projectQueue.front();
projectQueue.erase(projectQueue.begin());
} else {
currentProject = CityProject();
}
}
bool addTerritory(int tx, int ty) {
if (ownsTile(tx, ty)) return false;
territory.emplace_back(tx, ty);
return true;
}
int getTotalPop() const {
return (int)std::ceil((float)inhabitants / (float)std::max(1, inhabitantsPerPop));
}
int getAssignedPop() const {
return (int)workedTiles.size();
}
int getAvailablePop() const {
return std::max(0, getTotalPop() - getAssignedPop());
}
bool ownsTile(int tx, int ty) const {
for (const auto &p : territory) {
if (p.first == tx && p.second == ty) return true;
}
return false;
}
bool isTileWorked(int tx, int ty) const {
return workedTiles.find({tx, ty}) != workedTiles.end();
}
QString getWorkedResource(int tx, int ty) const {
auto it = workedTiles.find({tx, ty});
return it != workedTiles.end() ? it->second : "";
}
QString getEffectiveResource(int tx, int ty, const QString &defaultBestRes) const {
auto itW = workedTiles.find({tx, ty});
if (itW != workedTiles.end()) return itW->second;
auto itP = tilePreferredResources.find({tx, ty});
if (itP != tilePreferredResources.end()) return itP->second;
return defaultBestRes;
}
void setTilePreferredResource(int tx, int ty, const QString &resource) {
tilePreferredResources[{tx, ty}] = resource;
if (isTileWorked(tx, ty)) {
workedTiles[{tx, ty}] = resource;
}
}
bool assignWorker(int tx, int ty, const QString &resource) {
if (!ownsTile(tx, ty)) return false;
tilePreferredResources[{tx, ty}] = resource;
if (isTileWorked(tx, ty)) {
workedTiles[{tx, ty}] = resource;
return true;
}
if (getAvailablePop() <= 0) return false;
workedTiles[{tx, ty}] = resource;
return true;
}
bool unassignWorker(int tx, int ty) {
auto it = workedTiles.find({tx, ty});
if (it != workedTiles.end()) {
workedTiles.erase(it);
return true;
}
return false;
}
void toggleWorker(int tx, int ty, const QString &defaultRes) {
if (isTileWorked(tx, ty)) {
unassignWorker(tx, ty);
} else {
assignWorker(tx, ty, defaultRes);
}
}
};
#endif // CITY_H