sots-engine/src/game/data/weapon.cpp

150 lines
5.5 KiB
C++

#include "game/data/weapon.h"
#include "game/data/fields.h"
namespace game::data {
using detail::Fields;
const std::vector<std::string_view>& weapon_behavior_blocks() {
static const std::vector<std::string_view> names = {
"bolt", "beam", "torpedo", "rider", "missile", "chainlightning", "col", "mine",
"disintegrator", "grapple", "projectedshield", "mirv", "nodecannon", "siege",
"mesonprojector", "spyship", "wraith"};
return names;
}
namespace {
RangeTable read_rangetable(Fields& f) {
RangeTable t;
auto band = [&](RangeTable::Band& b, const char* prefix) {
std::string p(prefix);
b.range = f.opt_double(p + "_range");
b.deviation = f.opt_double(p + "_range_dev");
b.damage = f.opt_double(p + "_range_dam");
};
band(t.point_blank, "pb");
band(t.effective, "eff");
band(t.maximum, "max");
return t;
}
PlanetDamage read_planet_damage(Fields& f) {
PlanetDamage d;
d.pop = f.opt_double("dam_pop");
d.infra = f.opt_double("dam_infra");
d.terra = f.opt_double("dam_terra");
return d;
}
} // namespace
Loaded<WeaponDef> load_weapon(const mars::parse::Document& doc, std::string file) {
Loaded<WeaponDef> out;
const mars::parse::Node* node = doc.root.first_block("weapon");
if (!node) {
out.problems.push_back({Problem::Kind::MissingBlock, file, 0, "weapon", "no top-level weapon{} block"});
return out;
}
WeaponDef w;
w.file = file;
w.raw = snapshot(*node);
Fields f(w.raw, file, out.problems);
w.name = f.required_str("name");
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.compatible_section = f.strs("compatible_section");
w.exclusive_species = f.str("exclusive_species");
w.cost = f.opt_int("cost");
w.turret_size = f.required_str("turretsize");
w.turret_class = f.required_str("turretclass");
w.track_speed_mod = f.opt_double("trackspeed_mod");
w.burst_volleys = f.opt_int("burst_volleys");
w.recharge_time = f.opt_double("recharge_time");
w.volley_period = f.opt_double("volley_period");
w.volley_duration = f.opt_double("volley_duration");
w.buildup_delay = f.opt_double("buildup_delay");
w.solution_tolerance = f.opt_double("solution_tolerance");
w.range = f.opt_int("range");
w.range_planet = f.opt_int("range_planet");
w.muzzle_speed = f.opt_int("muzzle_speed");
w.hpbonus = f.opt_int("hpbonus");
w.dam_est = f.opt_int("dam_est");
w.hidden = f.opt_bool("hidden");
w.pinpoint = f.opt_bool("pinpoint");
w.blindfire = f.opt_bool("blindfire");
w.secondary_pd = f.opt_bool("secondary_pd");
w.model1 = f.str("model1");
w.model2 = f.str("model2");
w.model3 = f.str("model3");
w.muzzle_effect = f.str("muzzle_effect");
w.muzzle_sound = f.str("muzzle_sound");
w.icon_file = f.str("icon_file");
w.icon_rect = f.str("icon_rect");
w.fc.requires_los = f.opt_bool("fc_requires_los");
w.fc.requires_inrange = f.opt_bool("fc_requires_inrange");
w.fc.requires_enemycolony = f.opt_bool("fc_requires_enemycolony");
w.fc.manual_target = f.opt_bool("fc_manual_target");
w.fc.manual_toggle = f.opt_bool("fc_manual_toggle");
w.fc.manual_launch = f.opt_bool("fc_manual_launch");
w.fc.controllable = f.opt_bool("fc_controllable");
w.fc.holdsfire = f.opt_bool("fc_holdsfire");
w.fc.explicit_target = f.opt_bool("fc_explicit_target");
w.fc.exclusive_launch = f.opt_bool("fc_exclusive_launch");
w.fc.targets_expire = f.opt_bool("fc_targets_expire");
w.ratings.fire_rate = f.opt_double("rating_frate");
w.ratings.damage = f.opt_double("rating_dam");
w.ratings.accuracy = f.opt_double("rating_acc");
w.ratings.range = f.opt_double("rating_range");
for (std::string_view name : weapon_behavior_blocks()) {
const Block* b = w.raw.block(name);
if (!b) continue;
w.behavior_kind = std::string(name);
Fields bf = f.sub(*b);
w.planet_damage = read_planet_damage(bf);
if (const Block* rt = b->block("rangetable")) {
Fields rf = f.sub(*rt);
w.rangetable = read_rangetable(rf);
}
if (iequals(name, "bolt")) {
BoltDef bolt;
if (w.rangetable) bolt.rangetable = *w.rangetable;
bolt.planet = w.planet_damage;
bolt.mass = bf.opt_double("mass");
bolt.beam_origin = bf.opt_double("beam_origin");
bolt.beam_length = bf.opt_double("beam_length");
bolt.ricochet_mod = bf.opt_double("ricochet_mod");
bolt.effect = bf.str("effect");
bolt.impact_effect = bf.str("impact_effect");
bolt.expire_effect = bf.str("expire_effect");
w.bolt = std::move(bolt);
}
break;
}
out.value = std::move(w);
return out;
}
Loaded<WeaponDef> parse_weapon(std::string_view text, std::string file) {
auto parsed = mars::parse::parse_blocks(text);
if (!parsed.ok()) {
Loaded<WeaponDef> out;
out.problems.push_back({Problem::Kind::Syntax, file, parsed.error().line, "", parsed.error().message});
return out;
}
Loaded<WeaponDef> out = load_weapon(parsed.value(), std::move(file));
for (const auto& d : parsed.value().warnings)
out.problems.push_back({Problem::Kind::Syntax, out.value ? out.value->file : std::string(), d.line, "",
"recovered: " + d.message});
return out;
}
} // namespace game::data