Files
shadow-over-bishkek001/src/Campaign.cpp
T

136 lines
5.0 KiB
C++

#include "Campaign.h"
#include "utils/Utils.h"
#include "external/nlohmann/json.hpp"
#include <iostream>
namespace FRG {
using json = nlohmann::json;
static bool parseJsonFile(const std::string& path, const std::string& zipFile, json& out) {
const std::string content = readLocalizedConfigFile(path, zipFile);
if (content.empty()) {
std::cerr << "[Campaign] Failed to read " << path << "\n";
return false;
}
try {
out = json::parse(content);
} catch (const std::exception& e) {
std::cerr << "[Campaign] JSON parse error in " << path << ": " << e.what() << "\n";
return false;
}
return true;
}
static Eigen::Vector3f readVec3(const json& j) {
if (!j.is_array() || j.size() != 3) return Eigen::Vector3f::Zero();
return Eigen::Vector3f(j[0].get<float>(), j[1].get<float>(), j[2].get<float>());
}
bool LocationConfig::fromFile(const std::string& path, const std::string& zipFile, LocationConfig& out) {
json j;
if (!parseJsonFile(path, zipFile, j)) return false;
out = LocationConfig{};
out.id = j.value("id", "");
out.gameObjectsJsonPath = j.value("gameObjects", "");
out.interactiveObjectsJsonPath = j.value("interactiveObjects", "");
out.npcsJsonPath = j.value("npcs", "");
out.dialoguesJsonPath = j.value("dialogues", "");
out.navigationJsonPaths = j.value("navigation", std::vector<std::string>{});
out.scriptPath = j.value("script", "");
out.teleportsJsonPath = j.value("teleports", "");
out.triggerZonesJsonPath = j.value("triggerZones", "");
out.lightsJsonPath = j.value("lights", "");
if (j.contains("playerPosition")) out.playerPosition = readVec3(j["playerPosition"]);
out.lockInteractiveObjectsDuringTutorial = j.value("lockInteractiveObjectsDuringTutorial", false);
out.loadingScreenPath = j.value("loadingScreen", "");
if (j.contains("taxi") && j["taxi"].is_object()) {
const json& t = j["taxi"];
Taxi taxi;
if (t.contains("triggerPosition")) taxi.triggerPosition = readVec3(t["triggerPosition"]);
if (t.contains("initialPosition")) taxi.initialPosition = readVec3(t["initialPosition"]);
if (t.contains("waypoints") && t["waypoints"].is_array()) {
for (const auto& w : t["waypoints"]) taxi.waypoints.push_back(readVec3(w));
}
taxi.initialRotation = t.value("initialRotation", taxi.initialRotation);
taxi.waypointReachRadius = t.value("waypointReachRadius", taxi.waypointReachRadius);
out.taxi = taxi;
}
if (out.id.empty()) {
std::cerr << "[Campaign] Location config without an id: " << path << "\n";
return false;
}
return true;
}
std::string CampaignConfig::title(Language lang) const {
auto it = titles.find(languageToCode(lang));
if (it != titles.end()) return it->second;
return titles.empty() ? id : titles.begin()->second;
}
const LocationConfig* CampaignConfig::findLocation(const std::string& locationId) const {
for (const auto& loc : locations) {
if (loc.id == locationId) return &loc;
}
return nullptr;
}
std::string CampaignConfig::loadingScreenFor(const std::string& locationId) const {
const LocationConfig* loc = findLocation(locationId);
return (loc && !loc->loadingScreenPath.empty()) ? loc->loadingScreenPath : loadingScreenPath;
}
CampaignRegistry& CampaignRegistry::instance() {
static CampaignRegistry reg;
return reg;
}
void CampaignRegistry::loadFromJson(const std::string& jsonPath, const std::string& zipFile) {
json j;
if (!parseJsonFile(jsonPath, zipFile, j)) return;
if (!j.contains("campaigns") || !j["campaigns"].is_array()) {
std::cerr << "[Campaign] No 'campaigns' array in " << jsonPath << "\n";
return;
}
campaigns_.clear();
for (const auto& entry : j["campaigns"]) {
CampaignConfig c;
c.id = entry.value("id", "");
if (c.id.empty()) continue;
c.titles = entry.value("title", std::map<std::string, std::string>{});
c.locationConfigPaths = entry.value("locations", std::vector<std::string>{});
c.startLocation = entry.value("startLocation", "");
c.startMusic = entry.value("startMusic", "");
c.startStatePath = entry.value("startState", "");
c.questsPath = entry.value("quests", "");
c.tutorial = entry.value("tutorial", false);
c.networkPlayerSlots =
entry.value("networkPlayerSlots", std::vector<std::string>{});
c.networkCompatibility =
entry.value("networkCompatibility", std::string());
c.loadingScreenPath = entry.value("loadingScreen", std::string(CampaignConfig::kDefaultLoadingScreen));
for (const std::string& path : c.locationConfigPaths) {
LocationConfig loc;
if (LocationConfig::fromFile(path, zipFile, loc)) c.locations.push_back(std::move(loc));
}
campaigns_.push_back(std::move(c));
}
logger() << "[Campaign] Loaded " << campaigns_.size() << " campaigns from " << jsonPath << "\n";
}
const CampaignConfig* CampaignRegistry::find(const std::string& id) const {
for (const auto& c : campaigns_) {
if (c.id == id) return &c;
}
return nullptr;
}
} // namespace FRG