132 lines
4.2 KiB
C++
132 lines
4.2 KiB
C++
#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;
|
|
}
|