207 lines
7.5 KiB
C++
207 lines
7.5 KiB
C++
#include "BalancingConfig.h"
|
|
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#include "toml.hpp"
|
|
|
|
#include "Rotation.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
std::runtime_error makeError(const std::string& path, const std::string& why)
|
|
{
|
|
return std::runtime_error("balancing.toml: '" + path + "' " + why);
|
|
}
|
|
|
|
template <typename NodeView>
|
|
int64_t requireInt(NodeView node, const std::string& path)
|
|
{
|
|
const std::optional<int64_t> value = node.template value<int64_t>();
|
|
if (!value)
|
|
{
|
|
throw makeError(path, "missing or not an integer");
|
|
}
|
|
return *value;
|
|
}
|
|
|
|
template <typename NodeView>
|
|
std::string requireString(NodeView node, const std::string& path)
|
|
{
|
|
const std::optional<std::string> value = node.template value<std::string>();
|
|
if (!value)
|
|
{
|
|
throw makeError(path, "missing or not a string");
|
|
}
|
|
return *value;
|
|
}
|
|
|
|
Rotation parseRotation(const std::string& s)
|
|
{
|
|
if (s == "east") { return Rotation::East; }
|
|
if (s == "south") { return Rotation::South; }
|
|
if (s == "west") { return Rotation::West; }
|
|
return Rotation::North;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
BalancingConfig loadBalancingConfig(const std::string& path)
|
|
{
|
|
toml::table root;
|
|
try
|
|
{
|
|
root = toml::parse_file(path);
|
|
}
|
|
catch (const toml::parse_error& e)
|
|
{
|
|
throw std::runtime_error(std::string("balancing.toml: parse error: ") + e.what());
|
|
}
|
|
|
|
const toml::array* arenaArray = root["arena"].as_array();
|
|
if (!arenaArray)
|
|
{
|
|
throw makeError("arena", "missing or not an array of tables");
|
|
}
|
|
|
|
BalancingConfig config;
|
|
|
|
for (std::size_t ai = 0; ai < arenaArray->size(); ++ai)
|
|
{
|
|
const toml::table* arenaTbl = (*arenaArray)[ai].as_table();
|
|
if (!arenaTbl)
|
|
{
|
|
throw makeError("arena[" + std::to_string(ai) + "]", "not a table");
|
|
}
|
|
|
|
const std::string prefix = "arena[" + std::to_string(ai) + "]";
|
|
|
|
ArenaConfig arena;
|
|
arena.name = requireString((*arenaTbl)["name"], prefix + ".name");
|
|
arena.heightTiles = static_cast<int>(
|
|
requireInt((*arenaTbl)["height_tiles"], prefix + ".height_tiles"));
|
|
arena.playerBufferWidth = static_cast<int>(
|
|
requireInt((*arenaTbl)["player_buffer_width"], prefix + ".player_buffer_width"));
|
|
arena.contestZoneWidth = static_cast<int>(
|
|
requireInt((*arenaTbl)["contest_zone_width"], prefix + ".contest_zone_width"));
|
|
arena.enemyBufferWidth = static_cast<int>(
|
|
requireInt((*arenaTbl)["enemy_buffer_width"], prefix + ".enemy_buffer_width"));
|
|
|
|
const toml::array* teamArray = (*arenaTbl)["team"].as_array();
|
|
if (!teamArray || teamArray->size() != 2)
|
|
{
|
|
throw makeError(prefix + ".team", "must contain exactly 2 teams");
|
|
}
|
|
|
|
for (int ti = 0; ti < 2; ++ti)
|
|
{
|
|
const toml::table* teamTbl = (*teamArray)[static_cast<std::size_t>(ti)].as_table();
|
|
if (!teamTbl)
|
|
{
|
|
throw makeError(prefix + ".team[" + std::to_string(ti) + "]", "not a table");
|
|
}
|
|
|
|
const std::string tPrefix = prefix + ".team[" + std::to_string(ti) + "]";
|
|
|
|
ArenaTeamConfig& team = arena.teams[ti];
|
|
team.name = requireString((*teamTbl)["name"], tPrefix + ".name");
|
|
|
|
const toml::array* shipArray = (*teamTbl)["ship"].as_array();
|
|
if (shipArray)
|
|
{
|
|
for (std::size_t si = 0; si < shipArray->size(); ++si)
|
|
{
|
|
const toml::table* shipTbl = (*shipArray)[si].as_table();
|
|
if (!shipTbl)
|
|
{
|
|
throw makeError(tPrefix + ".ship[" + std::to_string(si) + "]",
|
|
"not a table");
|
|
}
|
|
|
|
const std::string sPrefix =
|
|
tPrefix + ".ship[" + std::to_string(si) + "]";
|
|
|
|
ArenaShipEntry entry;
|
|
entry.schematicId = requireString((*shipTbl)["schematic"],
|
|
sPrefix + ".schematic");
|
|
entry.level = static_cast<int>(
|
|
requireInt((*shipTbl)["level"], sPrefix + ".level"));
|
|
entry.count = static_cast<int>(
|
|
requireInt((*shipTbl)["count"], sPrefix + ".count"));
|
|
|
|
const toml::array* modArray = (*shipTbl)["modules"].as_array();
|
|
if (modArray && !modArray->empty())
|
|
{
|
|
ShipLayoutConfig layout;
|
|
for (std::size_t mi = 0; mi < modArray->size(); ++mi)
|
|
{
|
|
const toml::table* modTbl = (*modArray)[mi].as_table();
|
|
if (!modTbl) { continue; }
|
|
|
|
const std::optional<std::string> type =
|
|
(*modTbl)["type"].value<std::string>();
|
|
const std::optional<int64_t> x =
|
|
(*modTbl)["x"].value<int64_t>();
|
|
const std::optional<int64_t> y =
|
|
(*modTbl)["y"].value<int64_t>();
|
|
const std::optional<std::string> rotStr =
|
|
(*modTbl)["rotation"].value<std::string>();
|
|
if (!type || !x || !y || !rotStr) { continue; }
|
|
|
|
PlacedModule pm;
|
|
pm.moduleId = *type;
|
|
pm.position = QPoint(static_cast<int>(*x),
|
|
static_cast<int>(*y));
|
|
pm.rotation = parseRotation(*rotStr);
|
|
layout.placedModules.push_back(std::move(pm));
|
|
}
|
|
entry.layout = std::move(layout);
|
|
}
|
|
|
|
team.ships.push_back(entry);
|
|
}
|
|
}
|
|
|
|
const toml::array* stationArray = (*teamTbl)["station"].as_array();
|
|
if (stationArray)
|
|
{
|
|
for (std::size_t si = 0; si < stationArray->size(); ++si)
|
|
{
|
|
const toml::table* stationTbl = (*stationArray)[si].as_table();
|
|
if (!stationTbl)
|
|
{
|
|
throw makeError(tPrefix + ".station[" + std::to_string(si) + "]",
|
|
"not a table");
|
|
}
|
|
|
|
const std::string sPrefix =
|
|
tPrefix + ".station[" + std::to_string(si) + "]";
|
|
|
|
ArenaStationEntry entry;
|
|
entry.stationType = requireString((*stationTbl)["type"],
|
|
sPrefix + ".type");
|
|
entry.level = static_cast<int>(
|
|
requireInt((*stationTbl)["level"], sPrefix + ".level"));
|
|
entry.position = QPoint(
|
|
static_cast<int>(requireInt((*stationTbl)["x"], sPrefix + ".x")),
|
|
static_cast<int>(requireInt((*stationTbl)["y"], sPrefix + ".y")));
|
|
|
|
if (entry.stationType != "player_station"
|
|
&& entry.stationType != "enemy_station")
|
|
{
|
|
throw makeError(sPrefix + ".type",
|
|
"must be 'player_station' or 'enemy_station'");
|
|
}
|
|
|
|
team.stations.push_back(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
config.arenas.push_back(arena);
|
|
}
|
|
|
|
return config;
|
|
}
|