game/data+design: rename member 'requires' -> requires_tech (C++20 keyword, -Werror under design tests)
This commit is contained in:
parent
ba9ec951d2
commit
587e4ab9fb
18 changed files with 56 additions and 56 deletions
|
|
@ -297,10 +297,10 @@ CrossCheck Catalog::cross_check() const {
|
|||
return t;
|
||||
};
|
||||
|
||||
// weapon requires -> tech
|
||||
// weapon requires_tech -> tech
|
||||
for (const WeaponDef& w : weapons) {
|
||||
if (w.requires.empty()) x.weapons_without_requires.push_back(w.file);
|
||||
for (const std::string& r : w.requires) {
|
||||
if (w.requires_tech.empty()) x.weapons_without_requires.push_back(w.file);
|
||||
for (const std::string& r : w.requires_tech) {
|
||||
const TechNode* exact = nullptr;
|
||||
const TechNode* t = tech_ref(r, &exact);
|
||||
if (!t)
|
||||
|
|
@ -314,9 +314,9 @@ CrossCheck Catalog::cross_check() const {
|
|||
x.unresolved_weapon_names.push_back({w.file, w.name, 0});
|
||||
}
|
||||
|
||||
// sections: requires, options, bank weapons, turret pairs
|
||||
// sections: requires_tech, options, bank weapons, turret pairs
|
||||
for (const ShipSectionDef& s : sections) {
|
||||
for (const std::string& r : s.requires) {
|
||||
for (const std::string& r : s.requires_tech) {
|
||||
if (is_group_ref(r)) {
|
||||
if (!tech.requirement_exists(r)) x.section_requires_dangling.push_back({s.file, r, 0});
|
||||
continue;
|
||||
|
|
@ -342,7 +342,7 @@ CrossCheck Catalog::cross_check() const {
|
|||
}
|
||||
}
|
||||
|
||||
// tech: ship sections, weapon files, requires, allows
|
||||
// tech: ship sections, weapon files, requires_tech, allows
|
||||
std::set<std::string> weapon_files;
|
||||
for (const WeaponDef& w : weapons) weapon_files.insert(fold(w.file));
|
||||
for (const TechNode& t : tech.nodes) {
|
||||
|
|
@ -353,7 +353,7 @@ CrossCheck Catalog::cross_check() const {
|
|||
std::replace(k.begin(), k.end(), '\\', '/');
|
||||
if (!weapon_files.count(k)) x.tech_weapon_file_dangling.push_back({t.name, wf, t.line});
|
||||
}
|
||||
for (const std::string& r : t.requires)
|
||||
for (const std::string& r : t.requires_tech)
|
||||
if (!tech.requirement_exists(r)) x.tech_requires_dangling.push_back({t.name, r, t.line});
|
||||
for (std::size_t ei : t.allows) {
|
||||
const AllowsEdge& e = tech.edges[ei];
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Species parse_species(std::string_view name); // case-insensitive; "NPC
|
|||
struct Problem {
|
||||
enum class Kind {
|
||||
MissingBlock, // file has no top-level block of the expected name
|
||||
MissingKey, // a key the schema requires is absent
|
||||
MissingKey, // a key the schema requires_tech is absent
|
||||
BadValue, // a value does not have the expected shape (kept in raw)
|
||||
Unparsed, // a structured value (allows string, turret row) could not be parsed
|
||||
Duplicate, // an id / name occurs twice
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ Loaded<ShipSectionDef> load_shipsection(const mars::parse::Document& doc, std::s
|
|||
|
||||
s.model = f.required_str("model");
|
||||
s.dam_model = f.str("dam_model");
|
||||
s.requires = f.strs("requires");
|
||||
s.requires_tech = f.strs("requires");
|
||||
s.section_type_text = f.str("section_type");
|
||||
s.section_type = parse_section_type(s.section_type_text);
|
||||
s.section_class_text = f.str("section_class");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// game::data -- ShipSectionDef: one `Species/<Race>/sections/*.shipsection`.
|
||||
//
|
||||
// shipsection {
|
||||
// model PATH requires TECH... section_type command|mission|engine
|
||||
// model PATH requires_tech TECH... section_type command|mission|engine
|
||||
// section_class destroyer|cruiser|dreadnought
|
||||
// health mass cost cpoints crew socket_fore/socket_aft NODE
|
||||
// option TECH -- one-member option group
|
||||
|
|
@ -83,7 +83,7 @@ struct ShipSectionDef {
|
|||
|
||||
// body
|
||||
std::string model, dam_model;
|
||||
std::vector<std::string> requires; // AND; may name GRP_<group>
|
||||
std::vector<std::string> requires_tech; // AND; may name GRP_<group>
|
||||
std::string section_type_text;
|
||||
SectionType section_type = SectionType::None;
|
||||
std::string section_class_text;
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ Loaded<TechTree> load_tech_tree(const mars::parse::Document& doc, std::string fi
|
|||
t.group = f.str("group");
|
||||
t.option_cost = f.opt_double("option_cost");
|
||||
t.unlock_explicitly = f.opt_bool("unlock_explicitly");
|
||||
t.requires = f.strs("requires");
|
||||
t.requires_tech = f.strs("requires");
|
||||
for (const Block* s : t.raw.blocks_named("strategy")) {
|
||||
for (const std::string& v : s->strs("inc")) t.benefits_inc.push_back(v);
|
||||
for (const std::string& v : s->strs("dec")) t.benefits_dec.push_back(v);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// tech {
|
||||
// name "IND_Waldo" family "IND" type P threat N group TORPS option_cost 1.2
|
||||
// requires "TECH" | "GRP_<group>" -- prerequisite beyond the allows edge
|
||||
// requires_tech "TECH" | "GRP_<group>" -- prerequisite beyond the allows edge
|
||||
// allows "CHILD RP:cost Human:% Zuul:% Hiver:% Tarkas:% Liir:% Morrigi:%"
|
||||
// strategy { inc TECHBEN_X dec TECHBEN_Y }
|
||||
// ship { section STEM ... } -- "new section" notice (not a build gate)
|
||||
|
|
@ -59,7 +59,7 @@ struct TechNode {
|
|||
std::string group; // as written; group membership is matched upper-cased
|
||||
std::optional<double> option_cost;
|
||||
std::optional<bool> unlock_explicitly;
|
||||
std::vector<std::string> requires;
|
||||
std::vector<std::string> requires_tech;
|
||||
std::vector<std::string> benefits_inc, benefits_dec; // TECHBEN_* tokens, kept as data
|
||||
std::vector<std::string> sections; // ship{section} stems
|
||||
std::vector<std::string> weapon_files; // weapon{filename}
|
||||
|
|
@ -87,7 +87,7 @@ public:
|
|||
std::vector<const AllowsEdge*> edges_from(std::string_view name) const;
|
||||
std::vector<const TechNode*> roots() const; // nodes no edge points at
|
||||
|
||||
// A `requires` token is satisfiable if it names a tech or a non-empty group.
|
||||
// A `requires_tech` token is satisfiable if it names a tech or a non-empty group.
|
||||
bool requirement_exists(std::string_view token) const;
|
||||
|
||||
void rebuild_index();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ Loaded<WeaponDef> load_weapon(const mars::parse::Document& doc, std::string file
|
|||
w.weapon_class = f.required_str("weaponclass");
|
||||
w.weapon_family = f.str("weaponfamily");
|
||||
w.weapon_damage_type = f.str("weapondamagetype");
|
||||
w.requires = f.strs("requires");
|
||||
w.requires_tech = f.strs("requires");
|
||||
w.compatible_section = f.strs("compatible_section");
|
||||
w.exclusive_species = f.str("exclusive_species");
|
||||
w.cost = f.opt_int("cost");
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// weapon {
|
||||
// name @WEAPON_X weaponclass bullet weaponfamily gauss
|
||||
// requires TECH [requires TECH2] cost N
|
||||
// requires_tech TECH [requires_tech TECH2] cost N
|
||||
// turretsize small turretclass standard
|
||||
// burst_volleys N recharge_time T range R range_planet RP
|
||||
// fc_* true/false rating_* N
|
||||
|
|
@ -72,7 +72,7 @@ struct WeaponDef {
|
|||
std::string weapon_class; // drives engine behaviour (bullet, beam, missile, ...)
|
||||
std::string weapon_family; // AI grouping (gauss, laser, torpedo, ...)
|
||||
std::string weapon_damage_type;
|
||||
std::vector<std::string> requires; // AND of techs; may be empty
|
||||
std::vector<std::string> requires_tech; // AND of techs; may be empty
|
||||
std::vector<std::string> compatible_section; // rider weapons: section stems they carry
|
||||
std::string exclusive_species;
|
||||
std::optional<std::int64_t> cost;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ std::vector<const data::OptionGroup*> option_groups(const ShipSectionDef& s) {
|
|||
std::vector<std::string> applied_techs(const ShipSectionDef& s, const std::vector<std::string>& chosen) {
|
||||
std::vector<std::string> out;
|
||||
if (auto hct = hull_class_tech(s)) out.push_back(*hct);
|
||||
out.insert(out.end(), s.requires.begin(), s.requires.end());
|
||||
out.insert(out.end(), s.requires_tech.begin(), s.requires_tech.end());
|
||||
for (const data::OptionGroup* g : option_groups(s))
|
||||
for (const std::string& member : g->members)
|
||||
for (const std::string& c : chosen)
|
||||
|
|
@ -64,7 +64,7 @@ std::vector<std::string> options_from_applied(const ShipSectionDef& s, const std
|
|||
auto hct = hull_class_tech(s);
|
||||
for (const std::string& t : applied) {
|
||||
if (hct && iequals(t, *hct)) continue;
|
||||
if (contains_fold(s.requires, t)) continue;
|
||||
if (contains_fold(s.requires_tech, t)) continue;
|
||||
out.push_back(t);
|
||||
}
|
||||
return out;
|
||||
|
|
@ -248,7 +248,7 @@ std::vector<Violation> Ruleset::validate(const Design& d) const {
|
|||
if (want->empty()) continue;
|
||||
if (!part->def || !iequals(part->def->stem, *want))
|
||||
add(v, "A8", Level::Error,
|
||||
m.stem + " requires " + slot_str(part->slot) + " section " + *want + ", got " +
|
||||
m.stem + " requires_tech " + slot_str(part->slot) + " section " + *want + ", got " +
|
||||
(part->def ? part->def->stem : std::string("none")),
|
||||
part->slot);
|
||||
}
|
||||
|
|
@ -365,8 +365,8 @@ std::vector<Violation> Ruleset::validate(const Design& d) const {
|
|||
}
|
||||
}
|
||||
if (known && bank.weapon.empty())
|
||||
for (const std::string& t : w->requires)
|
||||
if (!known->satisfies(cat.tech, t)) add(v, "C2", Level::Error, w->stem + " requires " + t, p->slot, bi);
|
||||
for (const std::string& t : w->requires_tech)
|
||||
if (!known->satisfies(cat.tech, t)) add(v, "C2", Level::Error, w->stem + " requires_tech " + t, p->slot, bi);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -394,18 +394,18 @@ std::vector<Violation> Ruleset::validate(const Design& d) const {
|
|||
// -- tech gating ----------------------------------------------------------
|
||||
// Engine-generated hidden designs (the per-race "Default Assault Shuttle")
|
||||
// exist in every save regardless of research: the Tarkas _AssaultShuttle
|
||||
// requires DRN_AdvFrm and the design is there on turn 1. Gating on hidden
|
||||
// requires_tech DRN_AdvFrm and the design is there on turn 1. Gating on hidden
|
||||
// designs is therefore reported at warn level.
|
||||
const Level gate = hidden ? Level::Warn : Level::Error;
|
||||
for (const Filled* p : present) {
|
||||
const ShipSectionDef& s = *p->def;
|
||||
for (const std::string& t : s.requires) {
|
||||
for (const std::string& t : s.requires_tech) {
|
||||
if (!cat.tech.find(t) && !data::is_group_ref(t))
|
||||
add(v, "C1", Level::Warn, s.stem + " requires unknown tech " + t, p->slot);
|
||||
add(v, "C1", Level::Warn, s.stem + " requires_tech unknown tech " + t, p->slot);
|
||||
else if (known && !known->satisfies(cat.tech, t))
|
||||
add(v, "C1", gate, s.stem + " requires " + t, p->slot);
|
||||
add(v, "C1", gate, s.stem + " requires_tech " + t, p->slot);
|
||||
if (unobtainable(t, race))
|
||||
add(v, "C5", Level::Error, s.stem + " requires " + t + ", which " + std::string(rk) + " can never research", p->slot);
|
||||
add(v, "C5", Level::Error, s.stem + " requires_tech " + t + ", which " + std::string(rk) + " can never research", p->slot);
|
||||
}
|
||||
if (auto hct = hull_class_tech(s); hct && known && !known->satisfies(cat.tech, *hct))
|
||||
add(v, "C4", gate, s.stem + " is a " + fold(s.section_class_text) + " section; " + *hct + " not researched", p->slot);
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@
|
|||
// B* banks: one weapon per bank{}, none empty, fixed NPC banks bound to
|
||||
// their file, fit = class-accept table + a _turrets.txt row, weapon
|
||||
// scope / exclusive_species / rider carriers / hidden weapons.
|
||||
// C* tech gating: section and weapon `requires` (GRP_ = any of the group),
|
||||
// C* tech gating: section and weapon `requires_tech` (GRP_ = any of the group),
|
||||
// chosen options, the implicit hull-class tech (IND_CruisCon /
|
||||
// IND_DreadCon, stations exempt), race availability. Hidden default
|
||||
// designs are gated at warn level (code rule the data does not show).
|
||||
// D* options: at most one per group, members only; applied_techs =
|
||||
// [hull-class tech] + requires + chosen options in group order (D2).
|
||||
// [hull-class tech] + requires_tech + chosen options in group order (D2).
|
||||
//
|
||||
// Nothing here throws; every finding is a Violation.
|
||||
#pragma once
|
||||
|
|
@ -44,12 +44,12 @@ inline bool is_station(const data::ShipSectionDef& s) { return data::iequals(s.d
|
|||
std::vector<const data::OptionGroup*> option_groups(const data::ShipSectionDef& s);
|
||||
|
||||
// D2: the techs a section instance is built with -- exactly the save's DOpts
|
||||
// list: hull-class tech, `requires` in file order, then the chosen option of
|
||||
// list: hull-class tech, `requires_tech` in file order, then the chosen option of
|
||||
// each group in group order (spelled as chosen).
|
||||
std::vector<std::string> applied_techs(const data::ShipSectionDef& s, const std::vector<std::string>& chosen_options);
|
||||
|
||||
// Inverse of applied_techs for a save record: drops the hull-class tech and
|
||||
// the `requires` from a DOpts list, leaving the chosen options.
|
||||
// the `requires_tech` from a DOpts list, leaving the chosen options.
|
||||
std::vector<std::string> options_from_applied(const data::ShipSectionDef& s, const std::vector<std::string>& applied);
|
||||
|
||||
// B4: which weapon turret classes a bank turret class takes besides an exact
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ J weapon_json(const WeaponDef& w) {
|
|||
.set("weaponclass", jstr(w.weapon_class))
|
||||
.set("weaponfamily", jstr(w.weapon_family))
|
||||
.set("weapondamagetype", jstr(w.weapon_damage_type))
|
||||
.set("requires", jstrs(w.requires))
|
||||
.set("requires", jstrs(w.requires_tech))
|
||||
.set("compatible_section", jstrs(w.compatible_section))
|
||||
.set("exclusive_species", jstr(w.exclusive_species))
|
||||
.set("cost", jopt(w.cost))
|
||||
|
|
@ -270,7 +270,7 @@ J section_json(const ShipSectionDef& s) {
|
|||
JObj t;
|
||||
t.set("model", jstr(s.model))
|
||||
.set("dam_model", jstr(s.dam_model))
|
||||
.set("requires", jstrs(s.requires))
|
||||
.set("requires", jstrs(s.requires_tech))
|
||||
.set("section_type", jstr(s.section_type_text))
|
||||
.set("section_type_enum", jstr(section_type_name(s.section_type)))
|
||||
.set("section_class", jstr(s.section_class_text))
|
||||
|
|
@ -385,7 +385,7 @@ J tech_json(const TechTree& t) {
|
|||
.set("group", n.group.empty() ? jnull() : jstr(n.group))
|
||||
.set("option_cost", jopt(n.option_cost))
|
||||
.set("unlock_explicitly", jopt(n.unlock_explicitly))
|
||||
.set("requires", jstrs(n.requires))
|
||||
.set("requires", jstrs(n.requires_tech))
|
||||
.set("benefits_inc", jstrs(n.benefits_inc))
|
||||
.set("benefits_dec", jstrs(n.benefits_dec))
|
||||
.set("sections", jstrs(n.sections))
|
||||
|
|
|
|||
|
|
@ -57,17 +57,17 @@ int main() {
|
|||
const WeaponDef* g = cat.weapon("bal_gauss");
|
||||
EXPECT(g && g->id && *g->id == 8);
|
||||
EXPECT(g && g->cost && *g->cost == 50 && g->turret_size == "small" && g->turret_class == "standard");
|
||||
EXPECT(g && g->requires.size() == 1 && g->requires[0] == "WEP_GsDrvr");
|
||||
EXPECT(g && g->requires_tech.size() == 1 && g->requires_tech[0] == "WEP_GsDrvr");
|
||||
EXPECT(g && g->bolt && g->bolt->rangetable.maximum.range && *g->bolt->rangetable.maximum.range == 455);
|
||||
EXPECT(g && g->bolt && g->bolt->planet.pop && *g->bolt->planet.pop == 3500);
|
||||
EXPECT(g && g->fc.requires_los && *g->fc.requires_los && g->fc.manual_target && !*g->fc.manual_target);
|
||||
EXPECT(g && g->display_name && *g->display_name == "Gauss Cannon");
|
||||
EXPECT(cat.weapon_by_id(8) == g);
|
||||
EXPECT(cat.weapon("mis") && cat.weapon("mis")->requires.empty());
|
||||
EXPECT(cat.weapon("mis") && cat.weapon("mis")->requires_tech.empty());
|
||||
|
||||
const ShipSectionDef* fis = cat.section("Human", "DEFission");
|
||||
EXPECT(fis && fis->id && *fis->id == 47);
|
||||
EXPECT(fis && fis->requires.size() == 2 && fis->requires[1] == "DRV_Node");
|
||||
EXPECT(fis && fis->requires_tech.size() == 2 && fis->requires_tech[1] == "DRV_Node");
|
||||
EXPECT(fis && fis->section_type == SectionType::Engine && fis->section_class == SectionClass::Destroyer);
|
||||
EXPECT(fis && fis->options.size() == 3 && fis->options[0].members.size() == 5 && fis->options[2].scalar &&
|
||||
fis->options[2].members[0] == "DRV_RecFiss");
|
||||
|
|
@ -131,7 +131,7 @@ int main() {
|
|||
// ---- cross-check --------------------------------------------------
|
||||
CrossCheck x = cat.cross_check();
|
||||
std::printf("cross-check: dangling %zu (manifest gaps %zu, unresolved names %zu); case mismatches %zu/%zu; "
|
||||
"no-requires %zu; missing sectiondesc %zu; roots %zu\n",
|
||||
"no-requires_tech %zu; missing sectiondesc %zu; roots %zu\n",
|
||||
x.dangling_count(), x.manifest_ids_without_file.size(), x.unresolved_weapon_names.size(),
|
||||
x.weapon_requires_case_mismatch.size(), x.section_requires_case_mismatch.size(),
|
||||
x.weapons_without_requires.size(), x.missing_sectiondesc.size(), x.tech_roots.size());
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ fs::path make_root() {
|
|||
fs::path root = fs::temp_directory_path() / ("game_data_test_" + std::to_string(static_cast<long long>(stamp)));
|
||||
fs::remove_all(root);
|
||||
put(root / "Weapons/bal_gauss.weapon",
|
||||
"weapon { name @WEAPON_BAL_GAUSS weaponclass bullet requires WEP_GsDrvr cost 50 turretsize small turretclass standard\n"
|
||||
"weapon { name @WEAPON_BAL_GAUSS weaponclass bullet requires_tech WEP_GsDrvr cost 50 turretsize small turretclass standard\n"
|
||||
" bolt { rangetable { max_range 455 } dam_pop 3500 } }\n");
|
||||
put(root / "Weapons/las_odd.weapon",
|
||||
"weapon { name @WEAPON_LAS_ODD weaponclass beam requires WEP_NOPE requires wep_gsdrvr turretsize Large turretclass Beam }\n");
|
||||
"weapon { name @WEAPON_LAS_ODD weaponclass beam requires_tech WEP_NOPE requires_tech wep_gsdrvr turretsize Large turretclass Beam }\n");
|
||||
put(root / "Weapons/mis.weapon",
|
||||
"weapon { name @WEAPON_MIS weaponclass missile turretsize medium turretclass missile }\n");
|
||||
put(root / "Weapons/_weapons.txt", "// ids\n8 bal_gauss.weapon\n21 mis.weapon\n// DELETED - 36\n40 ghost.weapon\n");
|
||||
|
|
@ -38,7 +38,7 @@ fs::path make_root() {
|
|||
" bank { turretclass standard turretsize small mount { node N1 } } }\n");
|
||||
put(root / "Species/Human/sections/DEFission.shipsection",
|
||||
"shipsection { model b.X section_type engine section_class destroyer health 1 mass 2 socket_fore EngineNode\n"
|
||||
" requires DRV_Fissn requires grp_torps requires GRP_Empty\n"
|
||||
" requires_tech DRV_Fissn requires_tech grp_torps requires_tech GRP_Empty\n"
|
||||
" bank { turretclass strafe turretsize small mount { node N1 } } }\n");
|
||||
put(root / "Species/Human/sections/_shipsections.txt", "1 DECommand.shipsection\n47 defission.SHIPSECTION\n98 DEWar.shipsection\n");
|
||||
put(root / "Species/_NPC/sections/_Herald.shipsection",
|
||||
|
|
@ -52,7 +52,7 @@ fs::path make_root() {
|
|||
"tech { name \"IND_ROOT\" family \"IND\" allows \"IND_PlyAlloy RP:5000\" allows \"WEP_Missing RP:1\" allows \"BAD\" }\n"
|
||||
"tech { name \"IND_PlyAlloy\" ship { section DECommand section DENowhere } weapon { filename \"Weapons/bal_gauss.weapon\" }\n"
|
||||
" weapon { filename \"Weapons/nope.weapon\" } }\n"
|
||||
"tech { name \"WEP_GsDrvr\" group TORPS requires \"GRP_TORPS\" requires \"GRP_Nothing\" requires \"IND_Gone\" }\n"
|
||||
"tech { name \"WEP_GsDrvr\" group TORPS requires_tech \"GRP_TORPS\" requires_tech \"GRP_Nothing\" requires_tech \"IND_Gone\" }\n"
|
||||
"tech { name \"DRV_Fissn\" }\n");
|
||||
put(root / "Locale/EN/Strings.csv",
|
||||
"#Key+A955,String,Size,Notes\n"
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ TEST(section_typed_fields) {
|
|||
CHECK(s.section_type == SectionType::Engine);
|
||||
CHECK_EQ(s.section_class_text, "Destroyer");
|
||||
CHECK(s.section_class == SectionClass::Destroyer);
|
||||
CHECK_EQ(s.requires.size(), std::size_t{2});
|
||||
CHECK_EQ(s.requires[1], "DRV_Node");
|
||||
CHECK_EQ(s.requires_tech.size(), std::size_t{2});
|
||||
CHECK_EQ(s.requires_tech[1], "DRV_Node");
|
||||
CHECK_EQ(s.engine_techera, "fission");
|
||||
CHECK(s.health && *s.health == 450);
|
||||
CHECK(s.mass && *s.mass == 2500);
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ TEST(tech_tree_nodes_edges_groups) {
|
|||
|
||||
const TechNode* alloy = t.find("IND_PlyAlloy");
|
||||
CHECK(alloy->option_cost && *alloy->option_cost == 1.2); // OPTION_COST key case
|
||||
CHECK_EQ(alloy->requires.size(), std::size_t{1});
|
||||
CHECK_EQ(alloy->requires_tech.size(), std::size_t{1});
|
||||
CHECK(t.find("IND_StlthArm")->unlock_explicitly.value_or(false));
|
||||
|
||||
const TechNode* dsr = t.find("WEP_Dsrptr");
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ TEST(weapon_typed_fields) {
|
|||
CHECK_EQ(w.name, "@WEAPON_TEST_GUN");
|
||||
CHECK_EQ(w.weapon_class, "bullet");
|
||||
CHECK_EQ(w.weapon_family, "gauss");
|
||||
CHECK_EQ(w.requires.size(), std::size_t{2});
|
||||
CHECK_EQ(w.requires[0], "WEP_A");
|
||||
CHECK_EQ(w.requires[1], "WEP_B");
|
||||
CHECK_EQ(w.requires_tech.size(), std::size_t{2});
|
||||
CHECK_EQ(w.requires_tech[0], "WEP_A");
|
||||
CHECK_EQ(w.requires_tech[1], "WEP_B");
|
||||
CHECK(w.cost && *w.cost == 50);
|
||||
CHECK_EQ(w.turret_size, "small");
|
||||
CHECK_EQ(w.turret_class, "Standard"); // kept as written
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ std::string de_armor(const char* extra = "") {
|
|||
|
||||
std::string de_fission(const char* drive) {
|
||||
return "shipsection { model c.X Section_Type Engine section_class Destroyer socket_fore EngineNode\n"
|
||||
" requires DRV_Fissn requires " + std::string(drive) +
|
||||
" requires_tech DRV_Fissn requires_tech " + std::string(drive) +
|
||||
"\n health 450 mass 2500 cost 5000 cpoints 1380 engine_techera fission ftlspeed .2 nodespeed 4 range 9\n" +
|
||||
std::string(kArmorOptions) + " option DRV_RecFiss\n"
|
||||
" netforcelimits { force_forward 23000 force_right 23000 force_up 23000 torque_yaw 5 torque_pitch 5 torque_roll 5 speed 40 rotspeed 10 }\n" +
|
||||
|
|
@ -59,7 +59,7 @@ std::string de_platform() {
|
|||
|
||||
const char* kRider = "shipsection { model r.X section_class destroyer design_class rider entity_class rider health 50 mass 100 cost 100 }\n";
|
||||
const char* kRiderGated =
|
||||
"shipsection { model r.X section_class destroyer design_class rider entity_class rider requires DRN_AdvFrm health 50 mass 100 cost 100 }\n";
|
||||
"shipsection { model r.X section_class destroyer design_class rider entity_class rider requires_tech DRN_AdvFrm health 50 mass 100 cost 100 }\n";
|
||||
|
||||
void write_human(const fs::path& r) {
|
||||
fs::path s = r / "Species/Human/sections";
|
||||
|
|
@ -75,10 +75,10 @@ void write_human(const fs::path& r) {
|
|||
bank("strafe", "small", 4) + bank("standard", "large", 1) + "}\n");
|
||||
put(s / "DETorpedo.shipsection",
|
||||
"shipsection { model e.X section_type mission section_class destroyer socket_fore F socket_aft A health 1 mass 1 cost 1\n"
|
||||
" requires GRP_Torps\n" + bank("torpedo", "large", 1) + bank("missile", "medium", 1) + "}\n");
|
||||
" requires_tech GRP_Torps\n" + bank("torpedo", "large", 1) + bank("missile", "medium", 1) + "}\n");
|
||||
put(s / "DEAssaultShuttle.shipsection",
|
||||
"shipsection { model e.X section_type mission section_class destroyer socket_fore F socket_aft A health 1 mass 1 cost 1\n"
|
||||
" requires DRN_AdvFrm\n" + bank("assaultshuttlerider", "large", 1) + bank("tarkahunter", "large", 1) + "}\n");
|
||||
" requires_tech DRN_AdvFrm\n" + bank("assaultshuttlerider", "large", 1) + bank("tarkahunter", "large", 1) + "}\n");
|
||||
put(s / "DEPlanetMissile.shipsection",
|
||||
"shipsection { model e.X section_type mission section_class destroyer socket_fore F socket_aft A health 1 mass 1 cost 1\n" +
|
||||
bank("planetmissile", "large", 1) + "}\n");
|
||||
|
|
@ -90,12 +90,12 @@ void write_human(const fs::path& r) {
|
|||
bank("standard", "large", 2) + "}\n");
|
||||
put(s / "CRFission.shipsection",
|
||||
"shipsection { model f.X section_type engine section_class cruiser socket_fore EngineNode health 1 mass 30 cost 300 crew 15\n"
|
||||
" requires DRV_Fissn requires DRV_Node engine_techera fission ftlspeed .2 nodespeed 4\n" +
|
||||
" requires_tech DRV_Fissn requires_tech DRV_Node engine_techera fission ftlspeed .2 nodespeed 4\n" +
|
||||
bank("standard", "medium", 1) + "}\n");
|
||||
// the station triple (design_class station: no hull-class tech; partners pinned)
|
||||
put(s / "DNStation.shipsection",
|
||||
"shipsection { model g.X section_type mission section_class dreadnought socket_fore Fore socket_aft Aft\n"
|
||||
" requires IND_OrbCom health 9000 mass 100000 cost 700000 crew 100 entity_class \"Station\" design_class station command_quota 46\n"
|
||||
" requires_tech IND_OrbCom health 9000 mass 100000 cost 700000 crew 100 entity_class \"Station\" design_class station command_quota 46\n"
|
||||
" explicit_command_section \"DNStation_Fore\" explicit_engine_section \"DNStation_Aft\"\n" +
|
||||
bank("standard", "large", 2) + "}\n");
|
||||
put(s / "DNStation_Fore.shipsection",
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ TEST(C1_group_requirements) {
|
|||
TEST(C1_unknown_tech_is_a_warning) {
|
||||
Design d = armor();
|
||||
d.engine() = use("DEOddTech", {"bal_gauss"});
|
||||
// DEOddTech requires DRV_Fissn (known) and XXX_Nowhere (not in the tree):
|
||||
// DEOddTech requires_tech DRV_Fissn (known) and XXX_Nowhere (not in the tree):
|
||||
// a token the tree cannot satisfy is reported, never an error.
|
||||
std::vector<Violation> v = mini_rules().validate(d);
|
||||
CHECK_EQ(join(rules_at(v, Level::Error)), "");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue