114 lines
5.3 KiB
C++
114 lines
5.3 KiB
C++
// derive_stats and the default-weapon table on the mini catalog.
|
|
#include "game/design/defaults.h"
|
|
#include "game/design/stats.h"
|
|
#include "mini_catalog.h"
|
|
#include "test_main.h"
|
|
|
|
using namespace game::data;
|
|
using namespace game::design;
|
|
|
|
TEST(stats_sums_and_counts) {
|
|
DesignStats st = derive_stats(mini_rules(), armor());
|
|
CHECK_EQ(st.race, "Human");
|
|
CHECK_EQ(st.hull_class, "destroyer");
|
|
CHECK_EQ(st.sections.size(), std::size_t(3));
|
|
CHECK_EQ(st.mass, 800.0 + 2000 + 2500);
|
|
CHECK_EQ(st.section_cost, 2000.0 + 4000 + 5000);
|
|
CHECK_EQ(st.section_cost_with_options, st.section_cost);
|
|
CHECK_EQ(st.health_total, 300.0 + 600 + 450);
|
|
CHECK_EQ(st.cpoints, 500 + 1000 + 1380);
|
|
CHECK_EQ(st.crew, 0);
|
|
CHECK_EQ(st.banks, 5);
|
|
CHECK_EQ(st.turrets, 1 + 3 + 3 + 1 + 1);
|
|
CHECK_EQ(st.weapon_cost_per_bank, 50.0 * 4 + 3000);
|
|
CHECK_EQ(st.weapon_cost_per_mount, 50.0 * (1 + 3 + 3 + 1) + 3000);
|
|
CHECK_EQ(st.total_cost_estimate, 11000.0 + 3200);
|
|
CHECK_EQ(st.command_cost, 1);
|
|
CHECK(!st.command_quota);
|
|
CHECK(st.ftlspeed && *st.ftlspeed == 0.2);
|
|
CHECK(st.nodespeed && *st.nodespeed == 4);
|
|
CHECK_EQ(st.engine_techera, "fission");
|
|
CHECK(st.netforcelimits && st.netforcelimits->speed == 40);
|
|
CHECK_EQ(join(st.sections[2].applied_techs), "DRV_Fissn,DRV_Node");
|
|
CHECK(st.sections[1].banks[2].turret != nullptr);
|
|
if (st.sections[1].banks[2].turret) CHECK_EQ(st.sections[1].banks[2].turret->model, "turret_m1missile.x");
|
|
CHECK(st.capacity("range") != nullptr);
|
|
if (st.capacity("range")) CHECK_EQ(*st.capacity("range"), "9");
|
|
}
|
|
|
|
TEST(stats_option_cost_multiplier) {
|
|
Design d = armor();
|
|
d.known_techs.reset();
|
|
d.mission().options = {"IND_MagLat", "IND_RefCoat"}; // 1.4 x 1.2
|
|
DesignStats st = derive_stats(mini_rules(), d);
|
|
CHECK(st.sections[1].option_cost_multiplier > 1.679 && st.sections[1].option_cost_multiplier < 1.681);
|
|
CHECK(st.section_cost_with_options > 2000 + 4000 * 1.68 + 5000 - 0.01 && st.section_cost_with_options < 2000 + 4000 * 1.68 + 5000 + 0.01);
|
|
CHECK_EQ(join(st.sections[1].applied_techs), "IND_MagLat,IND_RefCoat");
|
|
}
|
|
|
|
TEST(stats_standalone_and_missing) {
|
|
Design d;
|
|
d.race = Species::Human;
|
|
d.mission() = use("DEDefencePlatform", {"bal_gauss", "bal_gauss", "mis"});
|
|
DesignStats st = derive_stats(mini_rules(), d);
|
|
CHECK_EQ(st.sections.size(), std::size_t(1));
|
|
CHECK(st.ftlspeed && *st.ftlspeed == 0); // a standalone mission section supplies the drive figures
|
|
CHECK(st.command_quota && *st.command_quota == 20);
|
|
CHECK(st.capacity("defence_platform") && *st.capacity("defence_platform") == "true");
|
|
CHECK(st.capacity("scanrange") && *st.capacity("scanrange") == "12");
|
|
// an unresolvable section contributes nothing; empty banks count as banks
|
|
d.mission() = use("DENowhere");
|
|
d.engine() = use("DEFission", {""});
|
|
st = derive_stats(mini_rules(), d);
|
|
CHECK_EQ(st.sections.size(), std::size_t(1));
|
|
CHECK_EQ(st.banks, 1);
|
|
CHECK_EQ(st.weapon_cost_per_bank, 0.0);
|
|
// NPC fixed weapons are costed
|
|
Design n;
|
|
n.race = Species::NPC;
|
|
n.mission() = use("_Herald", {"", "", ""});
|
|
st = derive_stats(mini_rules(), n);
|
|
CHECK_EQ(st.weapon_cost_per_bank, 7.0 + 9 + 7);
|
|
CHECK_EQ(st.weapon_cost_per_mount, 7.0 * 2 + 9 + 7);
|
|
CHECK_EQ(st.crew, 0); // `crew false` is a BadValue -> 0
|
|
}
|
|
|
|
TEST(default_weapons_table) {
|
|
Loaded<DefaultWeaponTable> t = parse_default_weapons(
|
|
"// comment\nstandard small \"las_red.weapon\"\nMissile medium \"mis.weapon\"\nbad row\n");
|
|
CHECK(t.ok());
|
|
CHECK_EQ(t.value->size(), std::size_t(2));
|
|
CHECK_EQ(t.problems.size(), std::size_t(1));
|
|
CHECK(t.value->find("standard", "Small") != nullptr);
|
|
CHECK(t.value->find("missile", "medium") != nullptr);
|
|
CHECK(t.value->find("standard", "large") == nullptr);
|
|
CHECK_EQ(t.value->find("standard", "small")->weapon_file, "las_red.weapon");
|
|
Loaded<DefaultWeaponTable> missing = load_default_weapons("/nonexistent/root");
|
|
CHECK(missing.ok());
|
|
CHECK_EQ(missing.value->size(), std::size_t(0));
|
|
CHECK_EQ(missing.problems.size(), std::size_t(1));
|
|
}
|
|
|
|
TEST(fill_empty_banks_from_defaults) {
|
|
Loaded<DefaultWeaponTable> t = parse_default_weapons(
|
|
"standard small \"las_red.weapon\"\nstandard medium \"las_red.weapon\"\ntorpedo large \"trp_photon.weapon\"\n");
|
|
Design d = armor();
|
|
d.known_techs.reset();
|
|
d.mission() = use("DETorpedo", {"", ""}); // large torpedo + medium missile (no default row for missile)
|
|
d.engine().weapons.clear(); // short list gets extended
|
|
int n = fill_empty_banks(d, mini_catalog(), *t.value);
|
|
CHECK_EQ(n, 2);
|
|
CHECK_EQ(d.mission().weapons[0].text, "Weapons/trp_photon.weapon");
|
|
CHECK(d.mission().weapons[1].empty());
|
|
CHECK_EQ(d.engine().weapons.size(), std::size_t(1));
|
|
CHECK_EQ(d.engine().weapons[0].text, "Weapons/las_red.weapon");
|
|
CHECK_EQ(d.command().weapons[0].text, "bal_gauss"); // untouched
|
|
std::vector<Violation> v = mini_rules().validate(d);
|
|
CHECK_EQ(join(rules_at(v, Level::Error)), "B2"); // only the missile bank is still empty
|
|
// NPC fixed banks are filled with their own weapon
|
|
Design h;
|
|
h.race = Species::NPC;
|
|
h.mission() = use("_Herald");
|
|
CHECK_EQ(fill_empty_banks(h, mini_catalog(), *t.value), 3);
|
|
CHECK_EQ(h.mission().weapons[1].text, "Species/_NPC/weapons/Herald_beam.weapon");
|
|
}
|