summaryrefslogtreecommitdiffhomepage
path: root/StarsEx/WeaponDesign.cpp
diff options
context:
space:
mode:
authorAki <please@ignore.pl>2022-04-01 21:23:39 +0200
committerAki <please@ignore.pl>2022-04-01 21:23:39 +0200
commit3c487c5cd69c53d6fea948643c0a76df03516605 (patch)
tree72730c7b8b26a5ef8fc9a987ec4c16129efd5aac /StarsEx/WeaponDesign.cpp
parent8f353abd0bfe18baddd8a8250ab7c4f2d1c83a6e (diff)
downloadstarshatter-3c487c5cd69c53d6fea948643c0a76df03516605.zip
starshatter-3c487c5cd69c53d6fea948643c0a76df03516605.tar.gz
starshatter-3c487c5cd69c53d6fea948643c0a76df03516605.tar.bz2
Moved Stars45 to StarsEx
Diffstat (limited to 'StarsEx/WeaponDesign.cpp')
-rw-r--r--StarsEx/WeaponDesign.cpp725
1 files changed, 725 insertions, 0 deletions
diff --git a/StarsEx/WeaponDesign.cpp b/StarsEx/WeaponDesign.cpp
new file mode 100644
index 0000000..aa116b0
--- /dev/null
+++ b/StarsEx/WeaponDesign.cpp
@@ -0,0 +1,725 @@
+/* Starshatter: The Open Source Project
+ Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors
+ Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors
+ Copyright (c) 1997-2006, Destroyer Studios LLC.
+
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon Design parameters class
+*/
+
+#include "WeaponDesign.h"
+#include "ShipDesign.h"
+#include "Weapon.h"
+
+#include "Game.h"
+#include "ContentBundle.h"
+#include "Sprite.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "Solid.h"
+#include "Sound.h"
+#include "DataLoader.h"
+
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+
+static List<WeaponDesign> catalog;
+static List<WeaponDesign> mod_catalog;
+static bool degrees;
+
+#define GET_DEF_BOOL(x) if(defname==(#x))GetDefBool(design->x,pdef,filename)
+#define GET_DEF_TEXT(x) if(defname==(#x))GetDefText(design->x,pdef,filename)
+#define GET_DEF_NUM(x) if(defname==(#x))GetDefNumber(design->x,pdef,filename)
+
+// +--------------------------------------------------------------------+
+
+WeaponDesign::WeaponDesign()
+{
+ type = 0;
+ secret = 0;
+ drone = 0;
+ primary = 0;
+ beam = 0;
+ flak = 0;
+ guided = 0;
+ self_aiming = 0;
+ syncro = 0;
+ value = 0;
+ decoy_type = 0;
+ probe = 0;
+ target_type = 0;
+
+ visible_stores = 0;
+ nstores = 0;
+ nbarrels = 0;
+
+ recharge_rate = 0.0f;
+ refire_delay = 0.0f;
+ salvo_delay = 0.0f;
+ charge = 0.0f;
+ min_charge = 0.0f;
+ carry_mass = 0.0f;
+ carry_resist = 0.0f;
+
+ speed = 0.0f;
+ life = 0.0f;
+ mass = 0.0f;
+ drag = 0.0f;
+ thrust = 0.0f;
+ roll_rate = 0.0f;
+ pitch_rate = 0.0f;
+ yaw_rate = 0.0f;
+ roll_drag = 0.0f;
+ pitch_drag = 0.0f;
+ yaw_drag = 0.0f;
+
+ min_range = 0.0f;
+ max_range = 0.0f;
+ max_track = 0.0f;
+
+ graphic_type = 0;
+ width = 0;
+ length = 0;
+
+ scale = 1.0f;
+ explosion_scale = 0.0f;
+ light = 0.0f;
+ light_color = Color::White;
+ flash_scale = 0.0f;
+ flare_scale = 0.0f;
+
+ spread_az = 0.0f;
+ spread_el = 0.0f;
+
+ beauty_img = 0;
+ turret_model = 0;
+ turret_base_model = 0;
+ animation = 0;
+ anim_length = 0;
+ shot_img = 0;
+ shot_model = 0;
+ trail_img = 0;
+ flash_img = 0;
+ flare_img = 0;
+ sound_resource = 0;
+
+ ammo = -1;
+ ripple_count = 0;
+ capacity = 100.0f;
+ damage = 1.0f;
+ damage_type = 0;
+ penetration = 1.0f;
+ firing_cone = 0.0f;
+ aim_az_max = 1.5f;
+ aim_az_min = -1.5f;
+ aim_az_rest = 0.0f;
+ aim_el_max = 1.5f;
+ aim_el_min = -1.5f;
+ aim_el_rest = 0.0f;
+ slew_rate = (float) (60*DEGREES);
+ turret_axis = 0;
+ lethal_radius = 500.0f;
+ integrity = 100.0f;
+
+ eject = Vec3(0.0f, -100.0f, 0.0f);
+
+ det_range = 0.0f;
+ det_count = 0;
+ det_spread = (float) (PI/8);
+
+ trail_length = 0;
+ trail_width = 0;
+ trail_dim = 0;
+
+ for (int i = 0; i < MAX_STORES; i++) {
+ muzzle_pts[i] = Vec3(0,0,0);
+ attachments[i] = Vec3(0,0,0);
+ }
+}
+
+WeaponDesign::~WeaponDesign()
+{
+ delete turret_model;
+ delete turret_base_model;
+ delete shot_model;
+ delete sound_resource;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponDesign::Initialize(const char* filename)
+{
+ Print("Loading Weapon Designs '%s'\n", filename);
+ LoadDesign("Weapons/", filename);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponDesign::Close()
+{
+ catalog.destroy();
+ mod_catalog.destroy();
+}
+
+void
+WeaponDesign::ClearModCatalog()
+{
+ mod_catalog.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponDesign::LoadDesign(const char* path, const char* filename, bool mod)
+{
+ // Load Design File:
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path);
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(filename, block, true);
+
+ Parser parser(new BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "WEAPON") {
+ Print("ERROR: invalid weapon design file '%s'\n", filename);
+ return;
+ }
+ }
+
+ int type = 1;
+ degrees = false;
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "primary" ||
+ defname == "missile" ||
+ defname == "drone" ||
+ defname == "beam") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: weapon structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ WeaponDesign* design = new WeaponDesign;
+
+ design->type = type++;
+ if (defname == "primary") {
+ design->primary = true;
+ }
+
+ else if (defname == "beam") {
+ design->primary = true;
+ design->beam = true;
+ design->guided = true;
+
+ design->spread_az = 0.15f;
+ design->spread_el = 0.15f;
+
+ }
+
+ else if (defname == "drone") {
+ design->drone = true;
+ design->penetration = 5.0f;
+ }
+
+ else { // missile
+ design->penetration = 5.0f;
+ }
+
+ float sound_min_dist = 1.0f;
+ float sound_max_dist = 100.0e3f;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ GET_DEF_TEXT(name);
+ else GET_DEF_TEXT(group);
+ else GET_DEF_TEXT(description);
+ else GET_DEF_NUM (guided);
+ else GET_DEF_BOOL(self_aiming);
+ else GET_DEF_BOOL(flak);
+ else GET_DEF_BOOL(syncro);
+ else GET_DEF_BOOL(visible_stores);
+ else GET_DEF_NUM (value);
+
+ else if (defname==("degrees")) {
+ GetDefBool(degrees,pdef,filename);
+ }
+
+ else if (defname==("secret")) {
+ GetDefBool(design->secret,pdef,filename);
+ }
+
+ else if (defname==("aim_az_max")) {
+ GetDefNumber(design->aim_az_max,pdef,filename);
+ if (degrees) design->aim_az_max *= (float) DEGREES;
+ design->aim_az_min = 0.0f - design->aim_az_max;
+ }
+
+ else if (defname==("aim_el_max")) {
+ GetDefNumber(design->aim_el_max,pdef,filename);
+ if (degrees) design->aim_el_max *= (float) DEGREES;
+ design->aim_el_min = 0.0f - design->aim_el_max;
+ }
+
+ else if (defname==("aim_az_min")) {
+ GetDefNumber(design->aim_az_min,pdef,filename);
+ if (degrees) design->aim_az_min *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_el_min")) {
+ GetDefNumber(design->aim_el_min,pdef,filename);
+ if (degrees) design->aim_el_min *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_az_rest")) {
+ GetDefNumber(design->aim_az_rest,pdef,filename);
+ if (degrees) design->aim_az_rest *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_el_rest")) {
+ GetDefNumber(design->aim_el_rest,pdef,filename);
+ if (degrees) design->aim_el_rest *= (float) DEGREES;
+ }
+
+ else if (defname==("spread_az")) {
+ GetDefNumber(design->spread_az,pdef,filename);
+ if (degrees) design->spread_az *= (float) DEGREES;
+ }
+
+ else if (defname==("spread_el")) {
+ GetDefNumber(design->spread_el,pdef,filename);
+ if (degrees) design->spread_el *= (float) DEGREES;
+ }
+
+ else GET_DEF_NUM (capacity);
+ else GET_DEF_NUM (recharge_rate);
+ else GET_DEF_NUM (refire_delay);
+ else GET_DEF_NUM (salvo_delay);
+ else GET_DEF_NUM (ammo);
+ else GET_DEF_NUM (ripple_count);
+ else GET_DEF_NUM (charge);
+ else GET_DEF_NUM (min_charge);
+ else GET_DEF_NUM (carry_mass);
+ else GET_DEF_NUM (carry_resist);
+ else GET_DEF_NUM (damage);
+ else GET_DEF_NUM (penetration);
+ else GET_DEF_NUM (speed);
+ else GET_DEF_NUM (life);
+ else GET_DEF_NUM (mass);
+ else GET_DEF_NUM (drag);
+ else GET_DEF_NUM (thrust);
+ else GET_DEF_NUM (roll_rate);
+ else GET_DEF_NUM (pitch_rate);
+ else GET_DEF_NUM (yaw_rate);
+ else GET_DEF_NUM (roll_drag);
+ else GET_DEF_NUM (pitch_drag);
+ else GET_DEF_NUM (yaw_drag);
+ else GET_DEF_NUM (lethal_radius);
+ else GET_DEF_NUM (integrity);
+
+ else GET_DEF_NUM (det_range);
+ else GET_DEF_NUM (det_count);
+ else GET_DEF_NUM (det_spread);
+ else GET_DEF_TEXT(det_child);
+
+ else GET_DEF_NUM (slew_rate);
+
+ else GET_DEF_NUM (min_range);
+ else GET_DEF_NUM (max_range);
+ else GET_DEF_NUM (max_track);
+
+ else GET_DEF_NUM (graphic_type);
+ else GET_DEF_NUM (width);
+ else GET_DEF_NUM (length);
+ else GET_DEF_NUM (scale);
+ else GET_DEF_NUM (explosion_scale);
+ else GET_DEF_NUM (light);
+ else GET_DEF_NUM (flash_scale);
+ else GET_DEF_NUM (flare_scale);
+
+ else GET_DEF_NUM (trail_length);
+ else GET_DEF_NUM (trail_width);
+ else GET_DEF_NUM (trail_dim);
+
+ else GET_DEF_TEXT(beauty);
+ else GET_DEF_TEXT(bitmap);
+ else GET_DEF_TEXT(turret);
+ else GET_DEF_TEXT(turret_base);
+ else GET_DEF_TEXT(model);
+ else GET_DEF_TEXT(trail);
+ else GET_DEF_TEXT(flash);
+ else GET_DEF_TEXT(flare);
+ else GET_DEF_TEXT(sound);
+ else GET_DEF_BOOL(probe);
+ else GET_DEF_NUM (turret_axis);
+ else GET_DEF_NUM (target_type);
+
+ else if (defname == "animation") {
+ if (design->anim_length < 16) {
+ GetDefText(design->anim_frames[design->anim_length++], pdef, filename);
+ }
+ else {
+ Print("WARNING: too many animation frames for weapon '%s' in '%s'\n",
+ (const char*) design->name, filename);
+ }
+ }
+
+ else if (defname == "light_color")
+ GetDefColor(design->light_color, pdef, filename);
+
+ else if (defname == "sound_min_dist")
+ GetDefNumber(sound_min_dist,pdef,filename);
+
+ else if (defname == "sound_max_dist")
+ GetDefNumber(sound_max_dist,pdef,filename);
+
+ else if (defname == "muzzle") {
+ if (design->nbarrels < MAX_STORES) {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ design->muzzle_pts[design->nbarrels++] = a;
+ }
+ else {
+ Print("WARNING: too many muzzles for weapon '%s' in '%s'\n",
+ (const char*) design->name, filename);
+ }
+ }
+
+ else if (defname == "attachment") {
+ if (design->nstores < MAX_STORES) {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ design->attachments[design->nstores++] = a;
+ }
+ else {
+ Print("WARNING: too many attachments for weapon '%s' in '%s'\n",
+ (const char*) design->name, filename);
+ }
+ }
+
+ else if (defname == "eject")
+ GetDefVec(design->eject,pdef,filename);
+
+ else if (defname == "decoy") {
+ char typestr[32];
+ GetDefText(typestr, pdef, filename);
+ design->decoy_type = ShipDesign::ClassForName(typestr);
+ }
+
+ else if (defname == "damage_type") {
+ char typestr[32];
+ GetDefText(typestr, pdef, filename);
+
+ if (!_stricmp(typestr, "normal"))
+ design->damage_type = DMG_NORMAL;
+
+ else if (!_stricmp(typestr, "emp"))
+ design->damage_type = DMG_EMP;
+
+ else if (!_stricmp(typestr, "power"))
+ design->damage_type = DMG_POWER;
+
+ else
+ Print("WARNING: unknown weapon damage type '%s' in '%s'\n",
+ typestr, filename);
+ }
+
+
+ else {
+ Print("WARNING: parameter '%s' ignored in '%s'\n",
+ defname.data(), filename);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ val->elements()->at(i)->print();
+ }
+ }
+
+ if (design->description.length()) {
+ design->description = ContentBundle::GetInstance()->GetText(design->description);
+ }
+
+ if (design->anim_length > 0) {
+ design->animation = new Bitmap[design->anim_length];
+ for (int i = 0; i < design->anim_length; i++) {
+ Bitmap* p = design->animation + i;
+ loader->LoadBitmap(design->anim_frames[i], *p, Bitmap::BMP_TRANSLUCENT);
+ p->MakeTexture();
+ }
+ }
+
+ else if (design->bitmap.length()) {
+ loader->LoadTexture(design->bitmap, design->shot_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->beauty.length()) {
+ loader->LoadTexture(design->beauty, design->beauty_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->turret.length()) {
+ Text p;
+ Text t = design->turret;
+ const char* s = strrchr(t.data(), '/');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ else {
+ s = strrchr(t.data(), '\\');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ }
+
+ if (p.length())
+ loader->SetDataPath(p);
+
+ design->turret_model = new Model;
+ design->turret_model->Load(t, design->scale);
+
+ if (design->turret_base.length()) {
+ t = design->turret_base;
+ s = strrchr(t.data(), '/');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ else {
+ s = strrchr(t.data(), '\\');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ }
+
+ if (p.length())
+ loader->SetDataPath(p);
+
+ design->turret_base_model = new Model;
+ design->turret_base_model->Load(t, design->scale);
+ }
+
+ loader->SetDataPath(path);
+ }
+
+ if (design->model.length()) {
+ Text p;
+ Text t = design->model;
+ const char* s = strrchr(t.data(), '/');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ else {
+ s = strrchr(t.data(), '\\');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ }
+
+ if (p.length())
+ loader->SetDataPath(p);
+
+ design->shot_model = new Model;
+ design->shot_model->Load(t, design->scale);
+
+ loader->SetDataPath(path);
+ }
+
+ if (design->trail.length()) {
+ loader->LoadTexture(design->trail, design->trail_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->flash.length()) {
+ loader->LoadTexture(design->flash, design->flash_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->flare.length()) {
+ loader->LoadTexture(design->flare, design->flare_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->sound.length()) {
+ int SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D;
+
+ if (design->beam)
+ SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D | Sound::LOCKED;
+
+ if (strstr(path, "Mods") == 0)
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound(design->sound, design->sound_resource, SOUND_FLAGS);
+ loader->SetDataPath(path);
+
+ if (design->sound_resource) {
+ design->sound_resource->SetMinDistance(sound_min_dist);
+ design->sound_resource->SetMaxDistance(sound_max_dist);
+ }
+ }
+
+ if (design->max_range == 0.0f)
+ design->max_range = design->speed * design->life;
+
+ if (design->max_track == 0.0f)
+ design->max_track = 3.0f * design->max_range;
+
+ if (design->probe && design->lethal_radius < 1e3)
+ design->lethal_radius = 50e3f;
+
+ if (design->beam)
+ design->flak = false;
+
+ if (design->self_aiming) {
+ if (fabs(design->aim_az_max) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_az_max);
+
+ if (fabs(design->aim_az_min) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_az_min);
+
+ if (fabs(design->aim_el_max) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_el_max);
+
+ if (fabs(design->aim_el_min) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_el_min);
+ }
+
+ if (mod)
+ mod_catalog.append(design);
+ else
+ catalog.append(design);
+ }
+ }
+
+ else
+ Print("WARNING: unknown definition '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+}
+
+// +--------------------------------------------------------------------+
+
+WeaponDesign*
+WeaponDesign::Get(int type)
+{
+ WeaponDesign test;
+ test.type = type;
+
+ WeaponDesign* result = catalog.find(&test);
+
+ if (!result)
+ result = mod_catalog.find(&test);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+WeaponDesign*
+WeaponDesign::Find(const char* name)
+{
+ for (int i = 0; i < catalog.size(); i++) {
+ WeaponDesign* d = catalog.at(i);
+
+ if (d->name == name) {
+ return d;
+ }
+ }
+
+ for (int i = 0; i < mod_catalog.size(); i++) {
+ WeaponDesign* d = mod_catalog.at(i);
+
+ if (d->name == name) {
+ return d;
+ }
+ }
+
+ Print("WeaponDesign: no catalog entry for design '%s', checking mods...\n", name);
+ return WeaponDesign::FindModDesign(name);
+}
+
+WeaponDesign*
+WeaponDesign::FindModDesign(const char* name)
+{
+ Text fname = name;
+ fname += ".def";
+
+ LoadDesign("Mods/Weapons/", fname, true);
+
+ for (int i = 0; i < mod_catalog.size(); i++) {
+ WeaponDesign* d = mod_catalog.at(i);
+
+ if (d->name == name) {
+ Print("WeaponDesign: found mod weapon '%s'\n", d->name.data());
+ return d;
+ }
+ }
+
+ Print("WeaponDesign: no mod found for design '%s'\n", name);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+WeaponDesign::GetDesignList(List<Text>& designs)
+{
+ designs.clear();
+
+ for (int i = 0; i < catalog.size(); i++) {
+ WeaponDesign* design = catalog[i];
+ designs.append(&design->name);
+ }
+
+ for (int i = 0; i < mod_catalog.size(); i++) {
+ WeaponDesign* design = mod_catalog[i];
+ designs.append(&design->name);
+ }
+
+ return designs.size();
+}