implement ship modules
This commit is contained in:
@@ -7,6 +7,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipsConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/StationsConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ModulesConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ConfigLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.h
|
||||
|
||||
@@ -358,6 +358,7 @@ ShipsConfig ConfigLoader::loadShips(const std::string& path)
|
||||
ShipDef def;
|
||||
def.id = requireString(mt["id"], file, elemPath + ".id");
|
||||
def.availableFromStart = requireBool(mt["available_from_start"], file, elemPath + ".available_from_start");
|
||||
def.layout = requireStringArray(mt["layout"], file, elemPath + ".layout");
|
||||
|
||||
// Schematic
|
||||
{
|
||||
@@ -498,6 +499,106 @@ StationsConfig ConfigLoader::loadStations(const std::string& path)
|
||||
return cfg;
|
||||
}
|
||||
|
||||
// Known category→stat mappings for module stat modifier discovery.
|
||||
struct StatEntry
|
||||
{
|
||||
const char* category;
|
||||
const char* stat;
|
||||
};
|
||||
|
||||
static const StatEntry kKnownStats[] = {
|
||||
{"health", "hp"},
|
||||
{"movement", "speed"},
|
||||
{"sensor", "sensor_range"},
|
||||
{"combat", "damage"},
|
||||
{"combat", "attack_range"},
|
||||
{"combat", "attack_rate"},
|
||||
{"repair", "repair_rate"},
|
||||
{"repair", "repair_range"},
|
||||
};
|
||||
|
||||
ModulesConfig ConfigLoader::loadModules(const std::string& path)
|
||||
{
|
||||
const std::string file = "modules.toml";
|
||||
toml::table tbl = parseFile(path, file);
|
||||
|
||||
ModulesConfig cfg;
|
||||
|
||||
if (!tbl.contains("module"))
|
||||
{
|
||||
return cfg;
|
||||
}
|
||||
|
||||
const toml::array& arr = requireArray(tbl["module"], file, "module");
|
||||
|
||||
for (std::size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
const std::string elemPath = "module[" + std::to_string(i) + "]";
|
||||
const toml::table* st = arr[i].as_table();
|
||||
if (st == nullptr)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a table");
|
||||
}
|
||||
toml::table& mt = const_cast<toml::table&>(*st);
|
||||
|
||||
ModuleDef def;
|
||||
def.id = requireString(mt["id"], file, elemPath + ".id");
|
||||
def.surfaceMask = requireStringArray(mt["surface_mask"], file, elemPath + ".surface_mask");
|
||||
def.playerProductionLevel = static_cast<int>(requireInt(
|
||||
mt["player_production_level"], file, elemPath + ".player_production_level"));
|
||||
def.productionTimeSeconds = requireDouble(
|
||||
mt["production_time_seconds"], file, elemPath + ".production_time_seconds");
|
||||
def.threatCost = requireDouble(mt["threat_cost"], file, elemPath + ".threat_cost");
|
||||
def.fillColor = requireString(mt["fill_color"], file, elemPath + ".fill_color");
|
||||
def.glyph = requireString(mt["glyph"], file, elemPath + ".glyph");
|
||||
|
||||
// Materials
|
||||
{
|
||||
const toml::array& materials = requireArray(mt["materials"], file, elemPath + ".materials");
|
||||
def.materials = parseIngredients(materials, file, elemPath + ".materials");
|
||||
}
|
||||
|
||||
// Stat modifiers from [module.<category>] sub-tables
|
||||
for (const StatEntry& se : kKnownStats)
|
||||
{
|
||||
if (!mt.contains(se.category))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const toml::table& catTable = requireTable(mt[se.category], file,
|
||||
elemPath + "." + se.category);
|
||||
toml::table& catMt = const_cast<toml::table&>(catTable);
|
||||
|
||||
const std::string addedKey = std::string("added_") + se.stat + "_formula";
|
||||
const std::string multipliedKey = std::string("multiplied_") + se.stat + "_formula";
|
||||
|
||||
if (catMt.contains(addedKey))
|
||||
{
|
||||
ModuleStatModifier mod;
|
||||
mod.stat = se.stat;
|
||||
mod.modifierType = "additive";
|
||||
mod.formula = requireFormula(catMt[addedKey], file,
|
||||
elemPath + "." + se.category + "." + addedKey);
|
||||
def.statModifiers.push_back(std::move(mod));
|
||||
}
|
||||
|
||||
if (catMt.contains(multipliedKey))
|
||||
{
|
||||
ModuleStatModifier mod;
|
||||
mod.stat = se.stat;
|
||||
mod.modifierType = "multiplicative";
|
||||
mod.formula = requireFormula(catMt[multipliedKey], file,
|
||||
elemPath + "." + se.category + "." + multipliedKey);
|
||||
def.statModifiers.push_back(std::move(mod));
|
||||
}
|
||||
}
|
||||
|
||||
cfg.modules.push_back(std::move(def));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
GameConfig ConfigLoader::loadFromDirectory(const std::string& configDir)
|
||||
{
|
||||
GameConfig cfg;
|
||||
@@ -506,5 +607,6 @@ GameConfig ConfigLoader::loadFromDirectory(const std::string& configDir)
|
||||
cfg.recipes = loadRecipes(configDir + "/recipes.toml");
|
||||
cfg.ships = loadShips(configDir + "/ships.toml");
|
||||
cfg.stations = loadStations(configDir + "/stations.toml");
|
||||
cfg.modules = loadModules(configDir + "/modules.toml");
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "GameConfig.h"
|
||||
|
||||
// Parses the five simulation TOML files from a directory and returns a fully
|
||||
// Parses all simulation TOML files from a directory and returns a fully
|
||||
// populated, immutable GameConfig. Throws std::runtime_error on any parse or
|
||||
// validation failure; the exception message identifies the offending file,
|
||||
// field, or formula (see architecture.md "Config Loading").
|
||||
@@ -21,4 +21,5 @@ public:
|
||||
static RecipesConfig loadRecipes(const std::string& path);
|
||||
static ShipsConfig loadShips(const std::string& path);
|
||||
static StationsConfig loadStations(const std::string& path);
|
||||
static ModulesConfig loadModules(const std::string& path);
|
||||
};
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
#include "RecipesConfig.h"
|
||||
#include "ShipsConfig.h"
|
||||
#include "StationsConfig.h"
|
||||
#include "ModulesConfig.h"
|
||||
|
||||
// Aggregate of all five simulation config files. Loaded at startup and reloaded
|
||||
// Aggregate of all simulation config files. Loaded at startup and reloaded
|
||||
// from disk on each game restart (REQ-CFG-RELOAD). See architecture.md "Config Loading".
|
||||
struct GameConfig
|
||||
{
|
||||
@@ -15,4 +16,5 @@ struct GameConfig
|
||||
RecipesConfig recipes;
|
||||
ShipsConfig ships;
|
||||
StationsConfig stations;
|
||||
ModulesConfig modules;
|
||||
};
|
||||
|
||||
34
src/lib/config/ModulesConfig.h
Normal file
34
src/lib/config/ModulesConfig.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Formula.h"
|
||||
#include "RecipesConfig.h"
|
||||
|
||||
// A single stat modifier contributed by a module instance.
|
||||
// REQ-MOD-STAT-CALC: final = base * (1 + sum(m_i - 1)) + sum(additives).
|
||||
struct ModuleStatModifier
|
||||
{
|
||||
std::string stat; // e.g. "hp", "speed", "sensor_range"
|
||||
std::string modifierType; // "additive" or "multiplicative"
|
||||
Formula formula;
|
||||
};
|
||||
|
||||
struct ModuleDef
|
||||
{
|
||||
std::string id;
|
||||
std::vector<std::string> surfaceMask;
|
||||
std::vector<RecipeIngredient> materials;
|
||||
int playerProductionLevel;
|
||||
double productionTimeSeconds;
|
||||
double threatCost;
|
||||
std::string fillColor;
|
||||
std::string glyph;
|
||||
std::vector<ModuleStatModifier> statModifiers;
|
||||
};
|
||||
|
||||
struct ModulesConfig
|
||||
{
|
||||
std::vector<ModuleDef> modules;
|
||||
};
|
||||
@@ -69,6 +69,7 @@ struct ShipDef
|
||||
{
|
||||
std::string id;
|
||||
bool availableFromStart;
|
||||
std::vector<std::string> layout;
|
||||
|
||||
ShipSchematic schematic;
|
||||
ShipThreat threat;
|
||||
|
||||
Reference in New Issue
Block a user