136 lines
5.0 KiB
C++
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
|