150 lines
5.5 KiB
C++
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
|