Implement technology tree, research queue by least remaining work, and unit/building tech availability & obsolescence
This commit is contained in:
+131
@@ -0,0 +1,131 @@
|
||||
#include "techconfig.h"
|
||||
#include "yamlparser.h"
|
||||
#include <QDebug>
|
||||
#include <algorithm>
|
||||
|
||||
TechTreeManager::TechTreeManager()
|
||||
: m_maxColumn(0), m_maxRow(0)
|
||||
{
|
||||
}
|
||||
|
||||
int TechTreeManager::computeTechDepth(const QString &techId, QMap<QString, int> &visited) const
|
||||
{
|
||||
if (visited.contains(techId)) {
|
||||
return visited.value(techId);
|
||||
}
|
||||
visited[techId] = 0; // Guard against cycles
|
||||
|
||||
auto it = m_techs.find(techId);
|
||||
if (it == m_techs.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxPrereqDepth = -1;
|
||||
for (const QString &pre : it.value().prerequisites) {
|
||||
if (m_techs.contains(pre)) {
|
||||
int d = computeTechDepth(pre, visited);
|
||||
if (d > maxPrereqDepth) {
|
||||
maxPrereqDepth = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int depth = maxPrereqDepth + 1;
|
||||
visited[techId] = depth;
|
||||
return depth;
|
||||
}
|
||||
|
||||
bool TechTreeManager::loadConfigFile(const QString &filePath)
|
||||
{
|
||||
YamlNode root = YamlParser::parseFile(filePath);
|
||||
if (root.children.isEmpty()) {
|
||||
qWarning() << "Failed to parse technologies config YAML:" << filePath;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_techs.clear();
|
||||
m_orderedTechIds.clear();
|
||||
m_maxColumn = 0;
|
||||
m_maxRow = 0;
|
||||
|
||||
const YamlNode &techsNode = root.hasChild("technologies") ? root["technologies"] : root;
|
||||
QMap<QString, bool> hasExplicitColumn;
|
||||
QMap<QString, bool> hasExplicitRow;
|
||||
|
||||
for (auto it = techsNode.children.begin(); it != techsNode.children.end(); ++it) {
|
||||
const YamlNode &node = it.value();
|
||||
TechConfig cfg;
|
||||
cfg.id = node.hasChild("id") ? node["id"].asString() : it.key();
|
||||
cfg.name = node.hasChild("name") ? node["name"].asString() : cfg.id;
|
||||
cfg.scienceCost = node.hasChild("science_cost") ? node["science_cost"].asInt(1000) : 1000;
|
||||
if (node.hasChild("cost") && !node.hasChild("science_cost")) {
|
||||
cfg.scienceCost = node["cost"].asInt(1000);
|
||||
}
|
||||
cfg.scienceCost = std::max(1, cfg.scienceCost);
|
||||
|
||||
if (node.hasChild("prerequisites")) {
|
||||
cfg.prerequisites = node["prerequisites"].listValues;
|
||||
}
|
||||
if (node.hasChild("icon")) {
|
||||
cfg.iconPath = node["icon"].asString("images/resources/kennis.png");
|
||||
}
|
||||
if (node.hasChild("description")) {
|
||||
cfg.description = node["description"].asString("");
|
||||
}
|
||||
|
||||
if (node.hasChild("column")) {
|
||||
cfg.column = node["column"].asInt(0);
|
||||
hasExplicitColumn[cfg.id] = true;
|
||||
}
|
||||
if (node.hasChild("row")) {
|
||||
cfg.row = node["row"].asInt(0);
|
||||
hasExplicitRow[cfg.id] = true;
|
||||
}
|
||||
|
||||
m_techs[cfg.id] = cfg;
|
||||
}
|
||||
|
||||
// Auto-calculate column and row for any tech without explicit coordinates
|
||||
QMap<QString, int> depthCache;
|
||||
QMap<int, int> nextRowInCol;
|
||||
|
||||
for (auto it = m_techs.begin(); it != m_techs.end(); ++it) {
|
||||
if (!hasExplicitColumn.value(it.key(), false)) {
|
||||
it.value().column = computeTechDepth(it.key(), depthCache);
|
||||
}
|
||||
if (hasExplicitRow.value(it.key(), false)) {
|
||||
nextRowInCol[it.value().column] = std::max(nextRowInCol.value(it.value().column, 0), it.value().row + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = m_techs.begin(); it != m_techs.end(); ++it) {
|
||||
if (!hasExplicitRow.value(it.key(), false)) {
|
||||
int col = it.value().column;
|
||||
it.value().row = nextRowInCol.value(col, 0);
|
||||
nextRowInCol[col] = it.value().row + 1;
|
||||
}
|
||||
m_maxColumn = std::max(m_maxColumn, it.value().column);
|
||||
m_maxRow = std::max(m_maxRow, it.value().row);
|
||||
m_orderedTechIds.push_back(it.key());
|
||||
}
|
||||
|
||||
std::sort(m_orderedTechIds.begin(), m_orderedTechIds.end(), [&](const QString &a, const QString &b) {
|
||||
const TechConfig &ta = m_techs[a];
|
||||
const TechConfig &tb = m_techs[b];
|
||||
if (ta.column != tb.column) return ta.column < tb.column;
|
||||
if (ta.row != tb.row) return ta.row < tb.row;
|
||||
return ta.id < tb.id;
|
||||
});
|
||||
|
||||
qDebug() << "TechTreeManager loaded" << m_techs.size() << "technologies from" << filePath;
|
||||
return !m_techs.isEmpty();
|
||||
}
|
||||
|
||||
const TechConfig* TechTreeManager::getTech(const QString &id) const
|
||||
{
|
||||
auto it = m_techs.find(id);
|
||||
if (it != m_techs.end()) {
|
||||
return &it.value();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
Reference in New Issue
Block a user