/* Starshatter OpenSource Distribution Copyright (c) 1997-2004, Destroyer Studios LLC. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "Destroyer Studios" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SUBSYSTEM: Stars.exe FILE: ShipDesign.cpp AUTHOR: John DiCamillo OVERVIEW ======== Starship Design parameters class */ #include "MemDebug.h" #include "ShipDesign.h" #include "Ship.h" #include "Shot.h" #include "Power.h" #include "HardPoint.h" #include "Weapon.h" #include "WeaponDesign.h" #include "Shield.h" #include "Sensor.h" #include "NavLight.h" #include "NavSystem.h" #include "Drive.h" #include "QuantumDrive.h" #include "Farcaster.h" #include "Thruster.h" #include "FlightDeck.h" #include "LandingGear.h" #include "Computer.h" #include "SystemDesign.h" #include "Component.h" #include "Game.h" #include "Solid.h" #include "Skin.h" #include "Sprite.h" #include "Light.h" #include "Bitmap.h" #include "Sound.h" #include "DataLoader.h" #include "ParseUtil.h" // +--------------------------------------------------------------------+ const char* ship_design_class_name[32] = { "Drone", "Fighter", "Attack", "LCA", "Courier", "Cargo", "Corvette", "Freighter", "Frigate", "Destroyer", "Cruiser", "Battleship", "Carrier", "Dreadnaught", "Station", "Farcaster", "Mine", "DEFSAT", "COMSAT", "SWACS", "Building", "Factory", "SAM", "EWR", "C3I", "Starbase", "0x04000000", "0x08000000", "0x10000000", "0x20000000", "0x40000000", "0x80000000" }; // +--------------------------------------------------------------------+ static const int NAMELEN = 64; static bool degrees = false; struct ShipCatalogEntry { static const char* TYPENAME() { return "ShipCatalogEntry"; } ShipCatalogEntry() : hide(false), design(0) {} ShipCatalogEntry(const char* n, const char* t, const char* p, const char* f, bool h=false) : name(n), type(t), path(p), file(f), hide(h), design(0) {} ~ShipCatalogEntry() { delete design; } Text name; Text type; Text path; Text file; bool hide; ShipDesign* design; }; static List catalog; static List mod_catalog; // +--------------------------------------------------------------------+ #define GET_DEF_BOOL(n) if (defname==(#n)) GetDefBool((n), def, filename) #define GET_DEF_TEXT(n) if (defname==(#n)) GetDefText((n), def, filename) #define GET_DEF_NUM(n) if (defname==(#n)) GetDefNumber((n), def, filename) #define GET_DEF_VEC(n) if (defname==(#n)) GetDefVec((n), def, filename) static char cockpit_name[80]; static List detail[4]; static List offset[4]; static char errmsg[256]; // +--------------------------------------------------------------------+ ShipLoad::ShipLoad() { ZeroMemory(name, sizeof(name)); ZeroMemory(load, sizeof(load)); mass = 0; } ShipSquadron::ShipSquadron() { name[0] = 0; design = 0; count = 4; avail = 4; } static void PrepareModel(Model& model) { bool uses_bumps = false; ListIter iter = model.GetMaterials(); while (++iter && !uses_bumps) { Material* mtl = iter.value(); if (mtl->tex_bumpmap != 0 && mtl->bump != 0) uses_bumps = true; } if (uses_bumps) model.ComputeTangents(); } // +--------------------------------------------------------------------+ ShipDesign::ShipDesign() : sensor(0), navsys(0), shield(0), type(0), decoy(0), probe(0), gear(0), valid(false), secret(false), auto_roll(1), cockpit_model(0), bolt_hit_sound_resource(0), beam_hit_sound_resource(0), lod_levels(0) { ZeroMemory(filename, sizeof(filename)); ZeroMemory(path_name, sizeof(path_name)); ZeroMemory(name, sizeof(name)); ZeroMemory(display_name, sizeof(display_name)); ZeroMemory(abrv, sizeof(abrv)); for (int i = 0; i < 4; i++) feature_size[i] = 0.0f; } // +--------------------------------------------------------------------+ ShipDesign::ShipDesign(const char* n, const char* p, const char* fname, bool s) : sensor(0), navsys(0), shield(0), type(0), quantum_drive(0), farcaster(0), thruster(0), shield_model(0), decoy(0), probe(0), gear(0), valid(false), secret(s), auto_roll(1), cockpit_model(0), bolt_hit_sound_resource(0), beam_hit_sound_resource(0), lod_levels(0) { ZeroMemory(filename, sizeof(filename)); ZeroMemory(path_name, sizeof(path_name)); ZeroMemory(name, sizeof(name)); ZeroMemory(display_name, sizeof(display_name)); ZeroMemory(abrv, sizeof(abrv)); strcpy_s(name, n); if (!strstr(fname, ".def")) sprintf_s(filename, "%s.def", fname); else strcpy_s(filename, fname); for (int i = 0; i < 4; i++) feature_size[i] = 0.0f; scale = 1.0f; agility = 2e2f; air_factor = 0.1f; vlimit = 8e3f; drag = 2.5e-5f; arcade_drag = 1.0f; roll_drag = 5.0f; pitch_drag = 5.0f; yaw_drag = 5.0f; roll_rate = 0.0f; pitch_rate = 0.0f; yaw_rate = 0.0f; trans_x = 0.0f; trans_y = 0.0f; trans_z = 0.0f; turn_bank = (float) (PI/8); CL = 0.0f; CD = 0.0f; stall = 0.0f; prep_time = 30.0f; avoid_time = 0.0f; avoid_fighter = 0.0f; avoid_strike = 0.0f; avoid_target = 0.0f; commit_range = 0.0f; splash_radius = -1.0f; scuttle = 5e3f; repair_speed = 1.0f; repair_teams = 2; repair_auto = true; repair_screen = true; wep_screen = true; chase_vec = Vec3(0, -100, 20); bridge_vec = Vec3(0, 0, 0); beauty_cam = Vec3(0, 0, 0); cockpit_scale = 1.0f; radius = 1.0f; integrity = 500.0f; primary = 0; secondary = 1; main_drive = -1; pcs = 3.0f; acs = 1.0f; detet = 250.0e3f; e_factor[0] = 0.1f; e_factor[1] = 0.3f; e_factor[2] = 1.0f; explosion_scale = 0.0f; death_spiral_time = 3.0f; if (!secret) Print("Loading ShipDesign '%s'\n", name); strcpy_s(path_name, p); if (path_name[strlen(path_name)-1] != '/') strcat_s(path_name, "/"); // Load Design File: DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath(path_name); BYTE* block; int blocklen = loader->LoadBuffer(filename, block, true); // file not found: if (blocklen <= 4) { valid = false; return; } Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen)); Term* term = parser.ParseTerm(); if (!term) { Print("ERROR: could not parse '%s'\n", filename); valid = false; return; } else { TermText* file_type = term->isText(); if (!file_type || file_type->value() != "SHIP") { Print("ERROR: invalid ship design file '%s'\n", filename); valid = false; return; } } cockpit_name[0] = 0; valid = true; degrees = false; do { delete term; term = parser.ParseTerm(); if (term) { TermDef* def = term->isDef(); if (def) { ParseShip(def); } else { Print("WARNING: term ignored in '%s'\n", filename); term->print(); } } } while (term); for (int i = 0; i < 4; i++) { int n = 0; ListIter iter = detail[i]; while (++iter) { const char* model_name = iter.value()->data(); Model* model = new(__FILE__,__LINE__) Model; if (!model->Load(model_name, scale)) { Print("ERROR: Could not load detail %d, model '%s'\n", i, model_name); delete model; model = 0; valid = false; } else { lod_levels = i+1; if (model->Radius() > radius) radius = (float) model->Radius(); models[i].append(model); PrepareModel(*model); if (offset[i].size()) { *offset[i].at(n) *= scale; offsets[i].append(offset[i].at(n)); // transfer ownership } else offsets[i].append(new(__FILE__,__LINE__) Point); n++; } } detail[i].destroy(); } if (!secret) Print(" Ship Design Radius = %f\n", radius); if (cockpit_name[0]) { const char* model_name = cockpit_name; cockpit_model = new(__FILE__,__LINE__) Model; if (!cockpit_model->Load(model_name, cockpit_scale)) { Print("ERROR: Could not load cockpit model '%s'\n", model_name); delete cockpit_model; cockpit_model = 0; } else { if (!secret) Print(" Loaded cockpit model '%s', preparing tangents\n", model_name); PrepareModel(*cockpit_model); } } if (beauty.Width() < 1 && loader->FindFile("beauty.pcx")) loader->LoadBitmap("beauty.pcx", beauty); if (hud_icon.Width() < 1 && loader->FindFile("hud_icon.pcx")) loader->LoadBitmap("hud_icon.pcx", hud_icon); loader->ReleaseBuffer(block); loader->SetDataPath(0); if (abrv[0] == 0) { switch (type) { case Ship::DRONE: strcpy_s(abrv, "DR"); break; case Ship::FIGHTER: strcpy_s(abrv, "F"); break; case Ship::ATTACK: strcpy_s(abrv, "F/A"); break; case Ship::LCA: strcpy_s(abrv, "LCA"); break; case Ship::CORVETTE: strcpy_s(abrv, "FC"); break; case Ship::COURIER: case Ship::CARGO: case Ship::FREIGHTER: strcpy_s(abrv, "MV"); break; case Ship::FRIGATE: strcpy_s(abrv, "FF"); break; case Ship::DESTROYER: strcpy_s(abrv, "DD"); break; case Ship::CRUISER: strcpy_s(abrv, "CA"); break; case Ship::BATTLESHIP: strcpy_s(abrv, "BB"); break; case Ship::CARRIER: strcpy_s(abrv, "CV"); break; case Ship::DREADNAUGHT: strcpy_s(abrv, "DN"); break; case Ship::MINE: strcpy_s(abrv, "MINE"); break; case Ship::COMSAT: strcpy_s(abrv, "COMS"); break; case Ship::DEFSAT: strcpy_s(abrv, "DEFS"); break; case Ship::SWACS: strcpy_s(abrv, "SWAC"); break; default: break; } } if (scuttle < 1) scuttle = 1; if (splash_radius < 0) splash_radius = radius * 12.0f; if (repair_speed <= 1e-6) repair_speed = 1.0e-6f; if (commit_range <= 0) { if (type <= Ship::LCA) commit_range = 80.0e3f; else commit_range = 200.0e3f; } // calc standard loadout weights: ListIter sl = loadouts; while (++sl) { for (int i = 0; i < hard_points.size(); i++) { HardPoint* hp = hard_points[i]; sl->mass += hp->GetCarryMass(sl->load[i]); } } } // +--------------------------------------------------------------------+ ShipDesign::~ShipDesign() { delete bolt_hit_sound_resource; delete beam_hit_sound_resource; delete cockpit_model; delete navsys; delete sensor; delete shield; delete thruster; delete farcaster; delete quantum_drive; delete decoy; delete probe; delete gear; navlights.destroy(); flight_decks.destroy(); hard_points.destroy(); computers.destroy(); weapons.destroy(); drives.destroy(); reactors.destroy(); loadouts.destroy(); map_sprites.destroy(); delete shield_model; for (int i = 0; i < 4; i++) { models[i].destroy(); offsets[i].destroy(); } spin_rates.destroy(); for (int i = 0; i < 10; i++) { delete debris[i].model; } } const char* ShipDesign::DisplayName() const { if (display_name[0]) return display_name; return name; } // +--------------------------------------------------------------------+ void AddModCatalogEntry(const char* design_name, const char* design_path) { if (!design_name || !*design_name) return; ShipCatalogEntry* entry = 0; for (int i = 0; i < catalog.size(); i++) { ShipCatalogEntry* e = catalog[i]; if (e->name == design_name) { if (design_path && *design_path && e->path != design_path) continue; entry = e; return; } } for (int i = 0; i < mod_catalog.size(); i++) { ShipCatalogEntry* e = mod_catalog[i]; if (e->name == design_name) { if (design_path && *design_path) { Text full_path = "Mods/Ships/"; full_path += design_path; if (e->path != full_path) continue; } entry = e; return; } } // still here? not found yet: Text file = Text(design_name) + ".def"; Text path = Text("Mods/Ships/"); Text name; Text type; bool valid = false; if (design_path && *design_path) path += design_path; else path += design_name; path += "/"; DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath(path); BYTE* block; int blocklen = loader->LoadBuffer(file, block, true); // file not found: if (blocklen <= 4) { return; } Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen)); Term* term = parser.ParseTerm(); if (!term) { Print("ERROR: could not parse '%s'\n", file.data()); delete block; return; } else { TermText* file_type = term->isText(); if (!file_type || file_type->value() != "SHIP") { Print("ERROR: invalid ship design file '%s'\n", file.data()); delete block; return; } } valid = true; do { delete term; term = parser.ParseTerm(); if (term) { TermDef* def = term->isDef(); if (def) { Text defname = def->name()->value(); defname.setSensitive(false); if (defname == "class") { if (!GetDefText(type, def, file)) { Print("WARNING: invalid or missing ship class in '%s'\n", file.data()); valid = false; } } else if (defname == "name") { if (!GetDefText(name, def, file)) { Print("WARNING: invalid or missing ship name in '%s'\n", file.data()); valid = false; } } } else { Print("WARNING: term ignored in '%s'\n", file.data()); term->print(); } } } while (term && valid && (name.length() < 1 || type.length() < 1)); delete block; if (valid && name.length() && type.length()) { Print("Add Mod Catalog Entry '%s' Class '%s'\n", name.data(), type.data()); ShipCatalogEntry* entry = new(__FILE__,__LINE__) ShipCatalogEntry(name, type, path, file); mod_catalog.append(entry); } } void ShipDesign::Initialize() { if (catalog.size()) return; LoadCatalog("Ships/", "catalog.def"); LoadSkins("Mods/Skins/"); List mod_designs; DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath("Mods/Ships/"); loader->ListFiles("*.def", mod_designs, true); for (int i = 0; i < mod_designs.size(); i++) { Text full_name = *mod_designs[i]; full_name.setSensitive(false); if (full_name.contains('/') && !full_name.contains("catalog")) { char path[1024]; strcpy_s(path, full_name.data()); char* name = path + full_name.length(); while (*name != '/') name--; *name++ = 0; char* p = strrchr(name, '.'); if (p && strlen(p) > 3) { if ((p[1] == 'd' || p[1] == 'D') && (p[2] == 'e' || p[2] == 'E') && (p[3] == 'f' || p[3] == 'F')) { *p = 0; } } // Just do a quick parse of the def file and add the // info to the catalog. DON'T preload all of the models, // textures, and weapons at this time. That takes way // too long with some of the larger user mods. AddModCatalogEntry(name, path); } } mod_designs.destroy(); loader->SetDataPath(0); } void ShipDesign::Close() { mod_catalog.destroy(); catalog.destroy(); } // +--------------------------------------------------------------------+ int ShipDesign::LoadCatalog(const char* path, const char* fname, bool mod) { int result = 0; // Load Design Catalog File: DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath(path); char filename[NAMELEN]; ZeroMemory(filename, NAMELEN); strncpy(filename, fname, NAMELEN-1); Print("Loading ship design catalog: %s%s\n", path, filename); BYTE* block; int blocklen = loader->LoadBuffer(filename, block, true); Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen)); Term* term = parser.ParseTerm(); if (!term) { Print("ERROR: could not parse '%s'\n", filename); loader->ReleaseBuffer(block); loader->SetDataPath(0); return result; } else { TermText* file_type = term->isText(); if (!file_type || file_type->value() != "SHIPCATALOG") { Print("ERROR: invalid ship catalog file '%s'\n", filename); loader->ReleaseBuffer(block); loader->SetDataPath(0); return result; } } do { delete term; term = parser.ParseTerm(); Text name, type, fname, path; bool hide = false; if (term) { TermDef* def = term->isDef(); if (def && def->term() && def->term()->isStruct()) { TermStruct* val = def->term()->isStruct(); for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "name") { if (!GetDefText(name, pdef, filename)) Print("WARNING: invalid or missing ship name in '%s'\n", filename); } else if (defname == "type") { if (!GetDefText(type, pdef, filename)) Print("WARNING: invalid or missing ship type in '%s'\n", filename); } else if (defname == "path") { if (!GetDefText(path, pdef, filename)) Print("WARNING: invalid or missing ship path in '%s'\n", filename); } else if (defname == "file") { if (!GetDefText(fname, pdef, filename)) Print("WARNING: invalid or missing ship file in '%s'\n", filename); } else if (defname == "hide" || defname == "secret") { GetDefBool(hide, pdef, filename); } } } ShipCatalogEntry* entry = new(__FILE__,__LINE__) ShipCatalogEntry(name, type, path, fname, hide); if (mod) mod_catalog.append(entry); else catalog.append(entry); result++; } else { Print("WARNING: term ignored in '%s'\n", filename); term->print(); } } } while (term); loader->ReleaseBuffer(block); loader->SetDataPath(0); return result; } // +--------------------------------------------------------------------+ void ShipDesign::LoadSkins(const char* path, const char* archive) { // Load MOD Skin Files: List list; DataLoader* loader = DataLoader::GetLoader(); bool oldfs = loader->IsFileSystemEnabled(); loader->UseFileSystem(true); loader->SetDataPath(path); loader->ListArchiveFiles(archive, "*.def", list); ListIter iter = list; while (++iter) { Text filename = *iter.value(); BYTE* block; int blocklen = loader->LoadBuffer(filename, block, true); // file not found: if (blocklen <= 4) { continue; } Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen)); Term* term = parser.ParseTerm(); ShipDesign* design = 0; if (!term) { Print("ERROR: could not parse '%s'\n", filename.data()); return; } else { TermText* file_type = term->isText(); if (!file_type || file_type->value() != "SKIN") { Print("ERROR: invalid skin file '%s'\n", filename.data()); return; } } do { delete term; term = parser.ParseTerm(); if (term) { TermDef* def = term->isDef(); if (def) { Text defname = def->name()->value(); defname.setSensitive(false); if (defname == "name") { Text name; GetDefText(name, def, filename); design = Get(name); } else if (defname == "skin" && design != 0) { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: skin struct missing in '%s'\n", filename.data()); } else { TermStruct* val = def->term()->isStruct(); Skin* skin = design->ParseSkin(val); if (skin) skin->SetPath(archive); } } } } } while (term); } loader->UseFileSystem(oldfs); } // +--------------------------------------------------------------------+ int ShipDesign::StandardCatalogSize() { return catalog.size(); } void ShipDesign::PreloadCatalog(int index) { if (index >= 0 && index < catalog.size()) { ShipCatalogEntry* entry = catalog[index]; if (entry->hide) return; int ship_class = ClassForName(entry->type); if (ship_class > Ship::STARSHIPS) return; if (!entry->path.contains("Alliance_")) return; if (!entry->design) { entry->design = new(__FILE__,__LINE__) ShipDesign(entry->name, entry->path, entry->file, entry->hide); } } else { ListIter iter = catalog; while (++iter) { ShipCatalogEntry* entry = iter.value(); if (!entry->design) { entry->design = new(__FILE__,__LINE__) ShipDesign(entry->name, entry->path, entry->file, entry->hide); } } } } // +--------------------------------------------------------------------+ bool ShipDesign::CheckName(const char* design_name) { ShipCatalogEntry* entry = 0; for (int i = 0; i < catalog.size(); i++) { if (catalog.at(i)->name == design_name) { entry = catalog.at(i); break; } } if (!entry) { for (int i = 0; i < mod_catalog.size(); i++) { if (mod_catalog.at(i)->name == design_name) { entry = mod_catalog.at(i); break; } } } return entry != 0; } // +--------------------------------------------------------------------+ ShipDesign* ShipDesign::Get(const char* design_name, const char* design_path) { if (!design_name || !*design_name) return 0; ShipCatalogEntry* entry = 0; for (int i = 0; i < catalog.size(); i++) { ShipCatalogEntry* e = catalog[i]; if (e->name == design_name) { if (design_path && *design_path && e->path != design_path) continue; entry = e; break; } } if (!entry) { for (int i = 0; i < mod_catalog.size(); i++) { ShipCatalogEntry* e = mod_catalog[i]; if (e->name == design_name) { if (design_path && *design_path) { Text full_path = "Mods/Ships/"; full_path += design_path; if (e->path != full_path) continue; } entry = e; break; } } } if (entry) { if (!entry->design) { entry->design = new(__FILE__,__LINE__) ShipDesign(entry->name, entry->path, entry->file, entry->hide); } return entry->design; } else { Print("ShipDesign: no catalog entry for design '%s', checking mods...\n", design_name); return ShipDesign::FindModDesign(design_name, design_path); } } ShipDesign* ShipDesign::FindModDesign(const char* design_name, const char* design_path) { Text file = Text(design_name) + ".def"; Text path = Text("Mods/Ships/"); if (design_path && *design_path) path += design_path; else path += design_name; DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath(path); ShipDesign* design = new(__FILE__,__LINE__) ShipDesign(design_name, path, file); if (design->valid) { Print("ShipDesign: found mod design '%s'\n", design->name); ShipCatalogEntry* entry = new(__FILE__,__LINE__) ShipCatalogEntry(design->name, ClassName(design->type), path, file); mod_catalog.append(entry); entry->design = design; return entry->design; } else { delete design; } return 0; } void ShipDesign::ClearModCatalog() { mod_catalog.destroy(); for (int i = 0; i < catalog.size(); i++) { ShipCatalogEntry* e = catalog[i]; if (e && e->design) { ListIter iter = e->design->skins; while (++iter) { Skin* skin = iter.value(); if (*skin->Path()) iter.removeItem(); } } } } // +--------------------------------------------------------------------+ int ShipDesign::GetDesignList(int type, List& designs) { designs.clear(); for (int i = 0; i < catalog.size(); i++) { ShipCatalogEntry* e = catalog[i]; int etype = ClassForName(e->type); if (etype & type) { if (!e->design) e->design = new(__FILE__,__LINE__) ShipDesign(e->name, e->path, e->file, e->hide); if (e->hide || !e->design || !e->design->valid || e->design->secret) continue; designs.append(&e->name); } } for (int i = 0; i < mod_catalog.size(); i++) { ShipCatalogEntry* e = mod_catalog[i]; int etype = ClassForName(e->type); if (etype & type) { designs.append(&e->name); } } return designs.size(); } // +--------------------------------------------------------------------+ int ShipDesign::ClassForName(const char* name) { if (!name || !name[0]) return 0; for (int i = 0; i < 32; i++) { if (!_stricmp(name, ship_design_class_name[i])) { return 1 << i; } } return 0; } const char* ShipDesign::ClassName(int type) { if (type != 0) { int index = 0; while (!(type & 1)) { type >>= 1; index++; } if (index >= 0 && index < 32) return ship_design_class_name[index]; } return "Unknown"; } // +--------------------------------------------------------------------+ void ShipDesign::ParseShip(TermDef* def) { char detail_name[NAMELEN]; Vec3 off_loc; Vec3 spin; Text defname = def->name()->value(); defname.setSensitive(false); if (defname == "cockpit_model") { if (!GetDefText(cockpit_name, def, filename)) Print("WARNING: invalid or missing cockpit_model in '%s'\n", filename); } else if (defname == "model" || defname == "detail_0") { if (!GetDefText(detail_name, def, filename)) Print("WARNING: invalid or missing model in '%s'\n", filename); detail[0].append(new(__FILE__,__LINE__) Text(detail_name)); } else if (defname == "detail_1") { if (!GetDefText(detail_name, def, filename)) Print("WARNING: invalid or missing detail_1 in '%s'\n", filename); detail[1].append(new(__FILE__,__LINE__) Text(detail_name)); } else if (defname == "detail_2") { if (!GetDefText(detail_name, def, filename)) Print("WARNING: invalid or missing detail_2 in '%s'\n", filename); detail[2].append(new(__FILE__,__LINE__) Text(detail_name)); } else if (defname == "detail_3") { if (!GetDefText(detail_name, def, filename)) Print("WARNING: invalid or missing detail_3 in '%s'\n", filename); detail[3].append(new(__FILE__,__LINE__) Text(detail_name)); } else if (defname == "spin") { if (!GetDefVec(spin, def, filename)) Print("WARNING: invalid or missing spin in '%s'\n", filename); spin_rates.append(new(__FILE__,__LINE__) Point(spin)); } else if (defname == "offset_0") { if (!GetDefVec(off_loc, def, filename)) Print("WARNING: invalid or missing offset_0 in '%s'\n", filename); offset[0].append(new(__FILE__,__LINE__) Point(off_loc)); } else if (defname == "offset_1") { if (!GetDefVec(off_loc, def, filename)) Print("WARNING: invalid or missing offset_1 in '%s'\n", filename); offset[1].append(new(__FILE__,__LINE__) Point(off_loc)); } else if (defname == "offset_2") { if (!GetDefVec(off_loc, def, filename)) Print("WARNING: invalid or missing offset_2 in '%s'\n", filename); offset[2].append(new(__FILE__,__LINE__) Point(off_loc)); } else if (defname == "offset_3") { if (!GetDefVec(off_loc, def, filename)) Print("WARNING: invalid or missing offset_3 in '%s'\n", filename); offset[3].append(new(__FILE__,__LINE__) Point(off_loc)); } else if (defname == "beauty") { if (def->term() && def->term()->isArray()) { GetDefVec(beauty_cam, def, filename); if (degrees) { beauty_cam.x *= (float) DEGREES; beauty_cam.y *= (float) DEGREES; } } else { char beauty_name[64]; if (!GetDefText(beauty_name, def, filename)) Print("WARNING: invalid or missing beauty in '%s'\n", filename); DataLoader* loader = DataLoader::GetLoader(); loader->LoadBitmap(beauty_name, beauty); } } else if (defname == "hud_icon") { char hud_icon_name[64]; if (!GetDefText(hud_icon_name, def, filename)) Print("WARNING: invalid or missing hud_icon in '%s'\n", filename); DataLoader* loader = DataLoader::GetLoader(); loader->LoadBitmap(hud_icon_name, hud_icon); } else if (defname == "feature_0") { if (!GetDefNumber(feature_size[0], def, filename)) Print("WARNING: invalid or missing feature_0 in '%s'\n", filename); } else if (defname == "feature_1") { if (!GetDefNumber(feature_size[1], def, filename)) Print("WARNING: invalid or missing feature_1 in '%s'\n", filename); } else if (defname == "feature_2") { if (!GetDefNumber(feature_size[2], def, filename)) Print("WARNING: invalid or missing feature_2 in '%s'\n", filename); } else if (defname == "feature_3") { if (!GetDefNumber(feature_size[3], def, filename)) Print("WARNING: invalid or missing feature_3 in '%s'\n", filename); } else if (defname == "class") { char typestr[64]; if (!GetDefText(typestr, def, filename)) Print("WARNING: invalid or missing ship class in '%s'\n", filename); type = ClassForName(typestr); if (type <= Ship::LCA) { repair_auto = false; repair_screen = false; wep_screen = false; } } else GET_DEF_TEXT(name); else GET_DEF_TEXT(description); else GET_DEF_TEXT(display_name); else GET_DEF_TEXT(abrv); else GET_DEF_NUM(pcs); else GET_DEF_NUM(acs); else GET_DEF_NUM(detet); else GET_DEF_NUM(scale); else GET_DEF_NUM(explosion_scale); else GET_DEF_NUM(mass); else GET_DEF_NUM(vlimit); else GET_DEF_NUM(agility); else GET_DEF_NUM(air_factor); else GET_DEF_NUM(roll_rate); else GET_DEF_NUM(pitch_rate); else GET_DEF_NUM(yaw_rate); else GET_DEF_NUM(integrity); else GET_DEF_NUM(drag); else GET_DEF_NUM(arcade_drag); else GET_DEF_NUM(roll_drag); else GET_DEF_NUM(pitch_drag); else GET_DEF_NUM(yaw_drag); else GET_DEF_NUM(trans_x); else GET_DEF_NUM(trans_y); else GET_DEF_NUM(trans_z); else GET_DEF_NUM(turn_bank); else GET_DEF_NUM(cockpit_scale); else GET_DEF_NUM(auto_roll); else GET_DEF_NUM(CL); else GET_DEF_NUM(CD); else GET_DEF_NUM(stall); else GET_DEF_NUM(prep_time); else GET_DEF_NUM(avoid_time); else GET_DEF_NUM(avoid_fighter); else GET_DEF_NUM(avoid_strike); else GET_DEF_NUM(avoid_target); else GET_DEF_NUM(commit_range); else GET_DEF_NUM(splash_radius); else GET_DEF_NUM(scuttle); else GET_DEF_NUM(repair_speed); else GET_DEF_NUM(repair_teams); else GET_DEF_BOOL(secret); else GET_DEF_BOOL(repair_auto); else GET_DEF_BOOL(repair_screen); else GET_DEF_BOOL(wep_screen); else GET_DEF_BOOL(degrees); else if (defname == "emcon_1") { GetDefNumber(e_factor[0], def, filename); } else if (defname == "emcon_2") { GetDefNumber(e_factor[1], def, filename); } else if (defname == "emcon_3") { GetDefNumber(e_factor[2], def, filename); } else if (defname == "chase") { if (!GetDefVec(chase_vec, def, filename)) Print("WARNING: invalid or missing chase cam loc in '%s'\n", filename); chase_vec *= (float) scale; } else if (defname == "bridge") { if (!GetDefVec(bridge_vec, def, filename)) Print("WARNING: invalid or missing bridge cam loc in '%s'\n", filename); bridge_vec *= (float) scale; } else if (defname == "power") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: power source struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParsePower(val); } } else if (defname == "main_drive" || defname == "drive") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: main drive struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseDrive(val); } } else if (defname == "quantum" || defname == "quantum_drive") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: quantum_drive struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseQuantumDrive(val); } } else if (defname == "sender" || defname == "farcaster") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: farcaster struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseFarcaster(val); } } else if (defname == "thruster") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: thruster struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseThruster(val); } } else if (defname == "navlight") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: navlight struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseNavlight(val); } } else if (defname == "flightdeck") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: flightdeck struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseFlightDeck(val); } } else if (defname == "gear") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: gear struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseLandingGear(val); } } else if (defname == "weapon") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: weapon struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseWeapon(val); } } else if (defname == "hardpoint") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: hardpoint struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseHardPoint(val); } } else if (defname == "loadout") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: loadout struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseLoadout(val); } } else if (defname == "decoy") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: decoy struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseWeapon(val); } } else if (defname == "probe") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: probe struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseWeapon(val); } } else if (defname == "sensor") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: sensor struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseSensor(val); } } else if (defname == "nav") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: nav struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseNavsys(val); } } else if (defname == "computer") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: computer struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseComputer(val); } } else if (defname == "shield") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: shield struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseShield(val); } } else if (defname == "death_spiral") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: death spiral struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseDeathSpiral(val); } } else if (defname == "map") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: map struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseMap(val); } } else if (defname == "squadron") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: squadron struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseSquadron(val); } } else if (defname == "skin") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: skin struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseSkin(val); } } else { Print("WARNING: unknown parameter '%s' in '%s'\n", defname.data(), filename); } if (description.length()) description = Game::GetText(description); } // +--------------------------------------------------------------------+ void ShipDesign::ParsePower(TermStruct* val) { int stype = 0; float output = 1000.0f; float fuel = 0.0f; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; Text design_name; Text pname; Text pabrv; int etype = 0; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "type") { TermText* tname = pdef->term()->isText(); if (tname) { if (tname->value()[0] == 'B') stype = PowerSource::BATTERY; else if (tname->value()[0] == 'A') stype = PowerSource::AUX; else if (tname->value()[0] == 'F') stype = PowerSource::FUSION; else Print("WARNING: unknown power source type '%s' in '%s'\n", tname->value().data(), filename); } } else if (defname == "name") { GetDefText(pname, pdef, filename); } else if (defname == "abrv") { GetDefText(pabrv, pdef, filename); } else if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "max_output") { GetDefNumber(output, pdef, filename); } else if (defname == "fuel_range") { GetDefNumber(fuel, pdef, filename); } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "explosion") { GetDefNumber(etype, pdef, filename); } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } } } PowerSource* source = new(__FILE__,__LINE__) PowerSource((PowerSource::SUBTYPE) stype, output); if (pname.length()) source->SetName(pname); if (pabrv.length()) source->SetName(pabrv); source->SetFuelRange(fuel); source->Mount(loc, size, hull); source->SetExplosionType(etype); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) source->SetDesign(sd); } if (emcon_1 >= 0 && emcon_1 <= 100) source->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) source->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) source->SetEMCONPower(1, emcon_3); reactors.append(source); } // +--------------------------------------------------------------------+ void ShipDesign::ParseDrive(TermStruct* val) { Text dname; Text dabrv; int dtype = 0; int etype = 0; float dthrust = 1.0f; float daug = 0.0f; float dscale = 1.0f; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; Text design_name; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; bool trail = true; Drive* drive = 0; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "type") { TermText* tname = pdef->term()->isText(); if (tname) { Text tval = tname->value(); tval.setSensitive(false); if (tval == "Plasma") dtype = Drive::PLASMA; else if (tval == "Fusion") dtype = Drive::FUSION; else if (tval == "Alien") dtype = Drive::GREEN; else if (tval == "Green") dtype = Drive::GREEN; else if (tval == "Red") dtype = Drive::RED; else if (tval == "Blue") dtype = Drive::BLUE; else if (tval == "Yellow") dtype = Drive::YELLOW; else if (tval == "Stealth") dtype = Drive::STEALTH; else Print("WARNING: unknown drive type '%s' in '%s'\n", tname->value().data(), filename); } } else if (defname == "name") { if (!GetDefText(dname, pdef, filename)) Print("WARNING: invalid or missing name for drive in '%s'\n", filename); } else if (defname == "abrv") { if (!GetDefText(dabrv, pdef, filename)) Print("WARNING: invalid or missing abrv for drive in '%s'\n", filename); } else if (defname == "design") { if (!GetDefText(design_name, pdef, filename)) Print("WARNING: invalid or missing design for drive in '%s'\n", filename); } else if (defname == "thrust") { if (!GetDefNumber(dthrust, pdef, filename)) Print("WARNING: invalid or missing thrust for drive in '%s'\n", filename); } else if (defname == "augmenter") { if (!GetDefNumber(daug, pdef, filename)) Print("WARNING: invalid or missing augmenter for drive in '%s'\n", filename); } else if (defname == "scale") { if (!GetDefNumber(dscale, pdef, filename)) Print("WARNING: invalid or missing scale for drive in '%s'\n", filename); } else if (defname == "port") { Vec3 port; float flare_scale = 0; if (pdef->term()->isArray()) { GetDefVec(port, pdef, filename); port *= scale; flare_scale = dscale; } else if (pdef->term()->isStruct()) { TermStruct* val = pdef->term()->isStruct(); for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef2 = val->elements()->at(i)->isDef(); if (pdef2) { if (pdef2->name()->value() == "loc") { GetDefVec(port, pdef2, filename); port *= scale; } else if (pdef2->name()->value() == "scale") { GetDefNumber(flare_scale, pdef2, filename); } } } if (flare_scale <= 0) flare_scale = dscale; } if (!drive) drive = new(__FILE__,__LINE__) Drive((Drive::SUBTYPE) dtype, dthrust, daug, trail); drive->AddPort(port, flare_scale); } else if (defname == "loc") { if (!GetDefVec(loc, pdef, filename)) Print("WARNING: invalid or missing loc for drive in '%s'\n", filename); loc *= (float) scale; } else if (defname == "size") { if (!GetDefNumber(size, pdef, filename)) Print("WARNING: invalid or missing size for drive in '%s'\n", filename); size *= (float) scale; } else if (defname == "hull_factor") { if (!GetDefNumber(hull, pdef, filename)) Print("WARNING: invalid or missing hull_factor for drive in '%s'\n", filename); } else if (defname == "explosion") { if (!GetDefNumber(etype, pdef, filename)) Print("WARNING: invalid or missing explosion for drive in '%s'\n", filename); } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } else if (defname == "trail" || defname == "show_trail") { GetDefBool(trail, pdef, filename); } } } if (!drive) drive = new(__FILE__,__LINE__) Drive((Drive::SUBTYPE) dtype, dthrust, daug, trail); drive->SetSourceIndex(reactors.size()-1); drive->Mount(loc, size, hull); if (dname.length()) drive->SetName(dname); if (dabrv.length()) drive->SetAbbreviation(dabrv); drive->SetExplosionType(etype); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) drive->SetDesign(sd); } if (emcon_1 >= 0 && emcon_1 <= 100) drive->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) drive->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) drive->SetEMCONPower(1, emcon_3); main_drive = drives.size(); drives.append(drive); } // +--------------------------------------------------------------------+ void ShipDesign::ParseQuantumDrive(TermStruct* val) { double capacity = 250e3; double consumption = 1e3; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; float countdown = 5.0f; Text design_name; Text type_name; Text abrv; int subtype = QuantumDrive::QUANTUM; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "abrv") { GetDefText(abrv, pdef, filename); } else if (defname == "type") { GetDefText(type_name, pdef, filename); type_name.setSensitive(false); if (type_name.contains("hyper")) { subtype = QuantumDrive::HYPER; } } else if (defname == "capacity") { GetDefNumber(capacity, pdef, filename); } else if (defname == "consumption") { GetDefNumber(consumption, pdef, filename); } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "jump_time") { GetDefNumber(countdown, pdef, filename); } else if (defname == "countdown") { GetDefNumber(countdown, pdef, filename); } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } } } QuantumDrive* drive = new(__FILE__,__LINE__) QuantumDrive((QuantumDrive::SUBTYPE) subtype, capacity, consumption); drive->SetSourceIndex(reactors.size()-1); drive->Mount(loc, size, hull); drive->SetCountdown(countdown); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) drive->SetDesign(sd); } if (abrv.length()) drive->SetAbbreviation(abrv); if (emcon_1 >= 0 && emcon_1 <= 100) drive->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) drive->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) drive->SetEMCONPower(1, emcon_3); quantum_drive = drive; } // +--------------------------------------------------------------------+ void ShipDesign::ParseFarcaster(TermStruct* val) { Text design_name; double capacity = 300e3; double consumption = 15e3; // twenty second recharge int napproach = 0; Vec3 approach[Farcaster::NUM_APPROACH_PTS]; Vec3 loc(0.0f, 0.0f, 0.0f); Vec3 start(0.0f, 0.0f, 0.0f); Vec3 end(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "capacity") { GetDefNumber(capacity, pdef, filename); } else if (defname == "consumption") { GetDefNumber(consumption, pdef, filename); } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "start") { GetDefVec(start, pdef, filename); start *= (float) scale; } else if (defname == "end") { GetDefVec(end, pdef, filename); end *= (float) scale; } else if (defname == "approach") { if (napproach < Farcaster::NUM_APPROACH_PTS) { GetDefVec(approach[napproach], pdef, filename); approach[napproach++] *= (float) scale; } else { Print("WARNING: farcaster approach point ignored in '%s' (max=%d)\n", filename, Farcaster::NUM_APPROACH_PTS); } } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } } } Farcaster* caster = new(__FILE__,__LINE__) Farcaster(capacity, consumption); caster->SetSourceIndex(reactors.size()-1); caster->Mount(loc, size, hull); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) caster->SetDesign(sd); } caster->SetStartPoint(start); caster->SetEndPoint(end); for (int i = 0; i < napproach; i++) caster->SetApproachPoint(i, approach[i]); if (emcon_1 >= 0 && emcon_1 <= 100) caster->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) caster->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) caster->SetEMCONPower(1, emcon_3); farcaster = caster; } // +--------------------------------------------------------------------+ void ShipDesign::ParseThruster(TermStruct* val) { if (thruster) { Print("WARNING: additional thruster ignored in '%s'\n", filename); return; } double thrust = 100; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; Text design_name; float tscale = 1.0f; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; int dtype = 0; Thruster* drive = 0; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "type") { TermText* tname = pdef->term()->isText(); if (tname) { Text tval = tname->value(); tval.setSensitive(false); if (tval == "Plasma") dtype = Drive::PLASMA; else if (tval == "Fusion") dtype = Drive::FUSION; else if (tval == "Alien") dtype = Drive::GREEN; else if (tval == "Green") dtype = Drive::GREEN; else if (tval == "Red") dtype = Drive::RED; else if (tval == "Blue") dtype = Drive::BLUE; else if (tval == "Yellow") dtype = Drive::YELLOW; else if (tval == "Stealth") dtype = Drive::STEALTH; else Print("WARNING: unknown thruster type '%s' in '%s'\n", tname->value().data(), filename); } } else if (defname == "thrust") { GetDefNumber(thrust, pdef, filename); } else if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "scale") { GetDefNumber(tscale, pdef, filename); } else if (defname.contains("port") && pdef->term()) { Vec3 port; float port_scale = 0; DWORD fire = 0; if (pdef->term()->isArray()) { GetDefVec(port, pdef, filename); port *= scale; port_scale = tscale; } else if (pdef->term()->isStruct()) { TermStruct* val = pdef->term()->isStruct(); for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef2 = val->elements()->at(i)->isDef(); if (pdef2) { if (pdef2->name()->value() == "loc") { GetDefVec(port, pdef2, filename); port *= scale; } else if (pdef2->name()->value() == "fire") { GetDefNumber(fire, pdef2, filename); } else if (pdef2->name()->value() == "scale") { GetDefNumber(port_scale, pdef2, filename); } } } if (port_scale <= 0) port_scale = tscale; } if (!drive) drive = new(__FILE__,__LINE__) Thruster(dtype, thrust, tscale); if (defname == "port" || defname == "port_bottom") drive->AddPort(Thruster::BOTTOM, port, fire, port_scale); else if (defname == "port_top") drive->AddPort(Thruster::TOP, port, fire, port_scale); else if (defname == "port_left") drive->AddPort(Thruster::LEFT, port, fire, port_scale); else if (defname == "port_right") drive->AddPort(Thruster::RIGHT, port, fire, port_scale); else if (defname == "port_fore") drive->AddPort(Thruster::FORE, port, fire, port_scale); else if (defname == "port_aft") drive->AddPort(Thruster::AFT, port, fire, port_scale); } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } } } if (!drive) drive = new(__FILE__,__LINE__) Thruster(dtype, thrust, tscale); drive->SetSourceIndex(reactors.size()-1); drive->Mount(loc, size, hull); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) drive->SetDesign(sd); } if (emcon_1 >= 0 && emcon_1 <= 100) drive->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) drive->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) drive->SetEMCONPower(1, emcon_3); thruster = drive; } // +--------------------------------------------------------------------+ void ShipDesign::ParseNavlight(TermStruct* val) { Text dname; Text dabrv; Text design_name; int nlights = 0; float dscale = 1.0f; float period = 10.0f; Vec3 bloc[NavLight::MAX_LIGHTS]; int btype[NavLight::MAX_LIGHTS]; DWORD pattern[NavLight::MAX_LIGHTS]; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "name") GetDefText(dname, pdef, filename); else if (defname == "abrv") GetDefText(dabrv, pdef, filename); else if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "scale") { GetDefNumber(dscale, pdef, filename); } else if (defname == "period") { GetDefNumber(period, pdef, filename); } else if (defname == "light") { if (!pdef->term() || !pdef->term()->isStruct()) { Print("WARNING: light struct missing for ship '%s' in '%s'\n", name, filename); } else { TermStruct* val = pdef->term()->isStruct(); Vec3 loc; int t = 0; DWORD ptn = 0; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "type") { GetDefNumber(t, pdef, filename); } else if (defname == "loc") { GetDefVec(loc, pdef, filename); } else if (defname == "pattern") { GetDefNumber(ptn, pdef, filename); } } } if (t < 1 || t > 4) t = 1; if (nlights < NavLight::MAX_LIGHTS) { bloc[nlights] = loc * scale; btype[nlights] = t-1; pattern[nlights] = ptn; nlights++; } else { Print("WARNING: Too many lights ship '%s' in '%s'\n", name, filename); } } } } } NavLight* nav = new(__FILE__,__LINE__) NavLight(period, dscale); if (dname.length()) nav->SetName(dname); if (dabrv.length()) nav->SetAbbreviation(dabrv); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) nav->SetDesign(sd); } for (int i = 0; i < nlights; i++) nav->AddBeacon(bloc[i], pattern[i], btype[i]); navlights.append(nav); } // +--------------------------------------------------------------------+ void ShipDesign::ParseFlightDeck(TermStruct* val) { Text dname; Text dabrv; Text design_name; float dscale = 1.0f; float az = 0.0f; int etype = 0; bool launch = false; bool recovery = false; int nslots = 0; int napproach = 0; int nrunway = 0; DWORD filters[10]; Vec3 spots[10]; Vec3 approach[FlightDeck::NUM_APPROACH_PTS]; Vec3 runway[2]; Vec3 loc(0,0,0); Vec3 start(0,0,0); Vec3 end(0,0,0); Vec3 cam(0,0,0); Vec3 box(0,0,0); float cycle_time = 0.0f; float size = 0.0f; float hull = 0.5f; float light = 0.0f; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "name") GetDefText(dname, pdef, filename); else if (defname == "abrv") GetDefText(dabrv, pdef, filename); else if (defname == "design") GetDefText(design_name, pdef, filename); else if (defname == "start") { GetDefVec(start, pdef, filename); start *= (float) scale; } else if (defname == "end") { GetDefVec(end, pdef, filename); end *= (float) scale; } else if (defname == "cam") { GetDefVec(cam, pdef, filename); cam *= (float) scale; } else if (defname == "box" || defname == "bounding_box") { GetDefVec(box, pdef, filename); box *= (float) scale; } else if (defname == "approach") { if (napproach < FlightDeck::NUM_APPROACH_PTS) { GetDefVec(approach[napproach], pdef, filename); approach[napproach++] *= (float) scale; } else { Print("WARNING: flight deck approach point ignored in '%s' (max=%d)\n", filename, FlightDeck::NUM_APPROACH_PTS); } } else if (defname == "runway") { GetDefVec(runway[nrunway], pdef, filename); runway[nrunway++] *= (float) scale; } else if (defname == "spot") { if (pdef->term()->isStruct()) { TermStruct* s = pdef->term()->isStruct(); for (int i = 0; i < s->elements()->size(); i++) { TermDef* d = s->elements()->at(i)->isDef(); if (d) { if (d->name()->value() == "loc") { GetDefVec(spots[nslots], d, filename); spots[nslots] *= (float) scale; } else if (d->name()->value() == "filter") { GetDefNumber(filters[nslots], d, filename); } } } nslots++; } else if (pdef->term()->isArray()) { GetDefVec(spots[nslots], pdef, filename); spots[nslots] *= (float) scale; filters[nslots++] = 0xf; } } else if (defname == "light") { GetDefNumber(light, pdef, filename); } else if (defname == "cycle_time") { GetDefNumber(cycle_time, pdef, filename); } else if (defname == "launch") { GetDefBool(launch, pdef, filename); } else if (defname == "recovery") { GetDefBool(recovery, pdef, filename); } else if (defname == "azimuth") { GetDefNumber(az, pdef, filename); if (degrees) az *= (float) DEGREES; } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "explosion") { GetDefNumber(etype, pdef, filename); } } } FlightDeck* deck = new(__FILE__,__LINE__) FlightDeck(); deck->Mount(loc, size, hull); if (dname.length()) deck->SetName(dname); if (dabrv.length()) deck->SetAbbreviation(dabrv); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) deck->SetDesign(sd); } if (launch) deck->SetLaunchDeck(); else if (recovery) deck->SetRecoveryDeck(); deck->SetAzimuth(az); deck->SetBoundingBox(box); deck->SetStartPoint(start); deck->SetEndPoint(end); deck->SetCamLoc(cam); deck->SetExplosionType(etype); if (light > 0) deck->SetLight(light); for (int i = 0; i < napproach; i++) deck->SetApproachPoint(i, approach[i]); for (int i = 0; i < nrunway; i++) deck->SetRunwayPoint(i, runway[i]); for (int i = 0; i < nslots; i++) deck->AddSlot(spots[i], filters[i]); if (cycle_time > 0) deck->SetCycleTime(cycle_time); flight_decks.append(deck); } // +--------------------------------------------------------------------+ void ShipDesign::ParseLandingGear(TermStruct* val) { Text dname; Text dabrv; Text design_name; int ngear = 0; Vec3 start[LandingGear::MAX_GEAR]; Vec3 end[LandingGear::MAX_GEAR]; Model* model[LandingGear::MAX_GEAR]; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "name") GetDefText(dname, pdef, filename); else if (defname == "abrv") GetDefText(dabrv, pdef, filename); else if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "gear") { if (!pdef->term() || !pdef->term()->isStruct()) { Print("WARNING: gear struct missing for ship '%s' in '%s'\n", name, filename); } else { TermStruct* val = pdef->term()->isStruct(); Vec3 v1, v2; char mod_name[256]; ZeroMemory(mod_name, sizeof(mod_name)); 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); if (defname == "model") { GetDefText(mod_name, pdef, filename); } else if (defname == "start") { GetDefVec(v1, pdef, filename); } else if (defname == "end") { GetDefVec(v2, pdef, filename); } } } if (ngear < LandingGear::MAX_GEAR) { Model* m = new(__FILE__,__LINE__) Model; if (!m->Load(mod_name, scale)) { Print("WARNING: Could not load landing gear model '%s'\n", mod_name); delete m; m = 0; } else { model[ngear] = m; start[ngear] = v1 * scale; end[ngear] = v2 * scale; ngear++; } } else { Print("WARNING: Too many landing gear ship '%s' in '%s'\n", name, filename); } } } } } gear = new(__FILE__,__LINE__) LandingGear(); if (dname.length()) gear->SetName(dname); if (dabrv.length()) gear->SetAbbreviation(dabrv); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) gear->SetDesign(sd); } for (int i = 0; i < ngear; i++) gear->AddGear(model[i], start[i], end[i]); } // +--------------------------------------------------------------------+ void ShipDesign::ParseWeapon(TermStruct* val) { Text wtype; Text wname; Text wabrv; Text design_name; Text group_name; int nmuz = 0; Vec3 muzzles[Weapon::MAX_BARRELS]; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; float az = 0.0f; float el = 0.0f; float az_max = 1e6f; float az_min = 1e6f; float el_max = 1e6f; float el_min = 1e6f; float az_rest = 1e6f; float el_rest = 1e6f; int etype = 0; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "type") GetDefText(wtype, pdef, filename); else if (defname == "name") GetDefText(wname, pdef, filename); else if (defname == "abrv") GetDefText(wabrv, pdef, filename); else if (defname == "design") GetDefText(design_name, pdef, filename); else if (defname == "group") GetDefText(group_name, pdef, filename); else if (defname == "muzzle") { if (nmuz < Weapon::MAX_BARRELS) { GetDefVec(muzzles[nmuz], pdef, filename); nmuz++; } else { Print("WARNING: too many muzzles (max=%d) for weapon in '%s'\n", filename, Weapon::MAX_BARRELS); } } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "azimuth") { GetDefNumber(az, pdef, filename); if (degrees) az *= (float) DEGREES; } else if (defname == "elevation") { GetDefNumber(el, pdef, filename); if (degrees) el *= (float) DEGREES; } else if (defname==("aim_az_max")) { GetDefNumber(az_max,pdef,filename); if (degrees) az_max *= (float) DEGREES; az_min = 0.0f - az_max; } else if (defname==("aim_el_max")) { GetDefNumber(el_max,pdef,filename); if (degrees) el_max *= (float) DEGREES; el_min = 0.0f - el_max; } else if (defname==("aim_az_min")) { GetDefNumber(az_min,pdef,filename); if (degrees) az_min *= (float) DEGREES; } else if (defname==("aim_el_min")) { GetDefNumber(el_min,pdef,filename); if (degrees) el_min *= (float) DEGREES; } else if (defname==("aim_az_rest")) { GetDefNumber(az_rest,pdef,filename); if (degrees) az_rest *= (float) DEGREES; } else if (defname==("aim_el_rest")) { GetDefNumber(el_rest,pdef,filename); if (degrees) el_rest *= (float) DEGREES; } else if (defname == "rest_azimuth") { GetDefNumber(az_rest, pdef, filename); if (degrees) az_rest *= (float) DEGREES; } else if (defname == "rest_elevation") { GetDefNumber(el_rest, pdef, filename); if (degrees) el_rest *= (float) DEGREES; } else if (defname == "explosion") { GetDefNumber(etype, pdef, filename); } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } else { Print("WARNING: unknown weapon parameter '%s' in '%s'\n", defname.data(), filename); } } } WeaponDesign* meta = WeaponDesign::Find(wtype); if (!meta) { Print("WARNING: unusual weapon name '%s' in '%s'\n", (const char*) wtype, filename); } else { // non-turret weapon muzzles are relative to ship scale: if (meta->turret_model == 0) { for (int i = 0; i < nmuz; i++) muzzles[i] *= (float) scale; } // turret weapon muzzles are relative to weapon scale: else { for (int i = 0; i < nmuz; i++) muzzles[i] *= (float) meta->scale; } Weapon* gun = new(__FILE__,__LINE__) Weapon(meta, nmuz, muzzles, az, el); gun->SetSourceIndex(reactors.size()-1); gun->Mount(loc, size, hull); if (az_max < 1e6) gun->SetAzimuthMax(az_max); if (az_min < 1e6) gun->SetAzimuthMin(az_min); if (az_rest < 1e6) gun->SetRestAzimuth(az_rest); if (el_max < 1e6) gun->SetElevationMax(el_max); if (el_min < 1e6) gun->SetElevationMin(el_min); if (el_rest < 1e6) gun->SetRestElevation(el_rest); if (emcon_1 >= 0 && emcon_1 <= 100) gun->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) gun->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) gun->SetEMCONPower(1, emcon_3); if (wname.length()) gun->SetName(wname); if (wabrv.length()) gun->SetAbbreviation(wabrv); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) gun->SetDesign(sd); } if (group_name.length()) { gun->SetGroup(group_name); } gun->SetExplosionType(etype); if (meta->decoy_type && !decoy) decoy = gun; else if (meta->probe && !probe) probe = gun; else weapons.append(gun); } DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath(path_name); } // +--------------------------------------------------------------------+ void ShipDesign::ParseHardPoint(TermStruct* val) { Text wtypes[8]; Text wname; Text wabrv; Text design; Vec3 muzzle; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; float az = 0.0f; float el = 0.0f; int ntypes = 0; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "type") GetDefText(wtypes[ntypes++], pdef, filename); else if (defname == "name") GetDefText(wname, pdef, filename); else if (defname == "abrv") GetDefText(wabrv, pdef, filename); else if (defname == "design") GetDefText(design, pdef, filename); else if (defname == "muzzle") { GetDefVec(muzzle, pdef, filename); muzzle *= (float) scale; } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "azimuth") { GetDefNumber(az, pdef, filename); if (degrees) az *= (float) DEGREES; } else if (defname == "elevation") { GetDefNumber(el, pdef, filename); if (degrees) el *= (float) DEGREES; } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } else { Print("WARNING: unknown weapon parameter '%s' in '%s'\n", defname.data(), filename); } } } HardPoint* hp = new(__FILE__,__LINE__) HardPoint(muzzle, az, el); if (hp) { for (int i = 0; i < ntypes; i++) { WeaponDesign* meta = WeaponDesign::Find(wtypes[i]); if (!meta) { Print("WARNING: unusual weapon name '%s' in '%s'\n", (const char*) wtypes[i], filename); } else { hp->AddDesign(meta); } } hp->Mount(loc, size, hull); if (wname.length()) hp->SetName(wname); if (wabrv.length()) hp->SetAbbreviation(wabrv); if (design.length()) hp->SetDesign(design); hard_points.append(hp); } DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath(path_name); } // +--------------------------------------------------------------------+ void ShipDesign::ParseLoadout(TermStruct* val) { ShipLoad* load = new(__FILE__,__LINE__) ShipLoad; if (!load) return; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "name") GetDefText(load->name, pdef, filename); else if (defname == "stations") GetDefArray(load->load, 16, pdef, filename); else Print("WARNING: unknown loadout parameter '%s' in '%s'\n", defname.data(), filename); } } loadouts.append(load); } // +--------------------------------------------------------------------+ void ShipDesign::ParseSensor(TermStruct* val) { Text design_name; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; int nranges = 0; float ranges[8]; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; ZeroMemory(ranges, sizeof(ranges)); for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "range") { GetDefNumber(ranges[nranges++], pdef, filename); } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { size *= (float) scale; GetDefNumber(size, pdef, filename); } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } } } if (!sensor) { sensor = new(__FILE__,__LINE__) Sensor(); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) sensor->SetDesign(sd); } for (int i = 0; i < nranges; i++) sensor->AddRange(ranges[i]); if (emcon_1 >= 0 && emcon_1 <= 100) sensor->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) sensor->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) sensor->SetEMCONPower(1, emcon_3); sensor->Mount(loc, size, hull); sensor->SetSourceIndex(reactors.size()-1); } else { Print("WARNING: additional sensor ignored in '%s'\n", filename); } } // +--------------------------------------------------------------------+ void ShipDesign::ParseNavsys(TermStruct* val) { Text design_name; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { size *= (float) scale; GetDefNumber(size, pdef, filename); } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } else if (defname == "design") GetDefText(design_name, pdef, filename); } } if (!navsys) { navsys = new(__FILE__,__LINE__) NavSystem; if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) navsys->SetDesign(sd); } navsys->Mount(loc, size, hull); navsys->SetSourceIndex(reactors.size()-1); } else { Print("WARNING: additional nav system ignored in '%s'\n", filename); } } // +--------------------------------------------------------------------+ void ShipDesign::ParseComputer(TermStruct* val) { Text comp_name("Computer"); Text comp_abrv("Comp"); Text design_name; int comp_type = 1; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "name") { GetDefText(comp_name, pdef, filename); } else if (defname == "abrv") { GetDefText(comp_abrv, pdef, filename); } else if (defname == "design") { GetDefText(design_name, pdef, filename); } else if (defname == "type") { GetDefNumber(comp_type, pdef, filename); } else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { size *= (float) scale; GetDefNumber(size, pdef, filename); } else if (defname == "hull_factor") { GetDefNumber(hull, pdef, filename); } } } Computer* comp = new(__FILE__,__LINE__) Computer(comp_type, comp_name); comp->Mount(loc, size, hull); comp->SetAbbreviation(comp_abrv); comp->SetSourceIndex(reactors.size()-1); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) comp->SetDesign(sd); } computers.append(comp); } // +--------------------------------------------------------------------+ void ShipDesign::ParseShield(TermStruct* val) { Text dname; Text dabrv; Text design_name; Text model_name; double factor = 0; double capacity = 0; double consumption = 0; double cutoff = 0; double curve = 0; double def_cost = 1; int shield_type = 0; Vec3 loc(0.0f, 0.0f, 0.0f); float size = 0.0f; float hull = 0.5f; int etype = 0; bool shield_capacitor = false; bool shield_bubble = false; int emcon_1 = -1; int emcon_2 = -1; int emcon_3 = -1; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "type") { GetDefNumber(shield_type, pdef, filename); } else if (defname == "name") GetDefText(dname, pdef, filename); else if (defname == "abrv") GetDefText(dabrv, pdef, filename); else if (defname == "design") GetDefText(design_name, pdef, filename); else if (defname == "model") GetDefText(model_name, pdef, filename); else if (defname == "loc") { GetDefVec(loc, pdef, filename); loc *= (float) scale; } else if (defname == "size") { GetDefNumber(size, pdef, filename); size *= (float) scale; } else if (defname == "hull_factor") GetDefNumber(hull, pdef, filename); else if (defname.contains("factor")) GetDefNumber(factor, pdef, filename); else if (defname.contains("cutoff")) GetDefNumber(cutoff, pdef, filename); else if (defname.contains("curve")) GetDefNumber(curve, pdef, filename); else if (defname.contains("capacitor")) GetDefBool(shield_capacitor, pdef, filename); else if (defname.contains("bubble")) GetDefBool(shield_bubble, pdef, filename); else if (defname == "capacity") GetDefNumber(capacity, pdef, filename); else if (defname == "consumption") GetDefNumber(consumption, pdef, filename); else if (defname == "deflection_cost") GetDefNumber(def_cost, pdef, filename); else if (defname == "explosion") GetDefNumber(etype, pdef, filename); else if (defname == "emcon_1") { GetDefNumber(emcon_1, pdef, filename); } else if (defname == "emcon_2") { GetDefNumber(emcon_2, pdef, filename); } else if (defname == "emcon_3") { GetDefNumber(emcon_3, pdef, filename); } else if (defname == "bolt_hit_sound") { GetDefText(bolt_hit_sound, pdef, filename); } else if (defname == "beam_hit_sound") { GetDefText(beam_hit_sound, pdef, filename); } } } if (!shield) { if (shield_type) { shield = new(__FILE__,__LINE__) Shield((Shield::SUBTYPE) shield_type); shield->SetSourceIndex(reactors.size()-1); shield->Mount(loc, size, hull); if (dname.length()) shield->SetName(dname); if (dabrv.length()) shield->SetAbbreviation(dabrv); if (design_name.length()) { SystemDesign* sd = SystemDesign::Find(design_name); if (sd) shield->SetDesign(sd); } shield->SetExplosionType(etype); shield->SetShieldCapacitor(shield_capacitor); shield->SetShieldBubble(shield_bubble); if (factor > 0) shield->SetShieldFactor(factor); if (capacity > 0) shield->SetCapacity(capacity); if (cutoff > 0) shield->SetShieldCutoff(cutoff); if (consumption > 0) shield->SetConsumption(consumption); if (def_cost > 0) shield->SetDeflectionCost(def_cost); if (curve > 0) shield->SetShieldCurve(curve); if (emcon_1 >= 0 && emcon_1 <= 100) shield->SetEMCONPower(1, emcon_1); if (emcon_2 >= 0 && emcon_2 <= 100) shield->SetEMCONPower(1, emcon_2); if (emcon_3 >= 0 && emcon_3 <= 100) shield->SetEMCONPower(1, emcon_3); if (model_name.length()) { shield_model = new(__FILE__,__LINE__) Model; if (!shield_model->Load(model_name, scale)) { Print("ERROR: Could not load shield model '%s'\n", model_name.data()); delete shield_model; shield_model = 0; valid = false; } else { shield_model->SetDynamic(true); shield_model->SetLuminous(true); } } DataLoader* loader = DataLoader::GetLoader(); DWORD SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D; if (bolt_hit_sound.length()) { if (!loader->LoadSound(bolt_hit_sound, bolt_hit_sound_resource, SOUND_FLAGS, true)) { loader->SetDataPath("Sounds/"); loader->LoadSound(bolt_hit_sound, bolt_hit_sound_resource, SOUND_FLAGS); loader->SetDataPath(path_name); } } if (beam_hit_sound.length()) { if (!loader->LoadSound(beam_hit_sound, beam_hit_sound_resource, SOUND_FLAGS, true)) { loader->SetDataPath("Sounds/"); loader->LoadSound(beam_hit_sound, beam_hit_sound_resource, SOUND_FLAGS); loader->SetDataPath(path_name); } } } else { Print("WARNING: invalid shield type in '%s'\n", filename); } } else { Print("WARNING: additional shield ignored in '%s'\n", filename); } } // +--------------------------------------------------------------------+ void ShipDesign::ParseDeathSpiral(TermStruct* val) { int exp_index = -1; int debris_index = -1; int fire_index = -1; for (int i = 0; i < val->elements()->size(); i++) { TermDef* def = val->elements()->at(i)->isDef(); if (def) { Text defname = def->name()->value(); defname.setSensitive(false); if (defname == "time") { GetDefNumber(death_spiral_time, def, filename); } else if (defname == "explosion") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: explosion struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseExplosion(val, ++exp_index); } } // BACKWARD COMPATIBILITY: else if (defname == "explosion_type") { GetDefNumber(explosion[++exp_index].type, def, filename); } else if (defname == "explosion_time") { GetDefNumber(explosion[exp_index].time, def, filename); } else if (defname == "explosion_loc") { GetDefVec(explosion[exp_index].loc, def, filename); explosion[exp_index].loc *= (float) scale; } else if (defname == "final_type") { GetDefNumber(explosion[++exp_index].type, def, filename); explosion[exp_index].final = true; } else if (defname == "final_loc") { GetDefVec(explosion[exp_index].loc, def, filename); explosion[exp_index].loc *= (float) scale; } else if (defname == "debris") { if (def->term() && def->term()->isText()) { Text model_name; GetDefText(model_name, def, filename); Model* model = new(__FILE__,__LINE__) Model; if (!model->Load(model_name, scale)) { Print("Could not load debris model '%s'\n", model_name.data()); delete model; return; } PrepareModel(*model); debris[++debris_index].model = model; fire_index = -1; } else if (!def->term() || !def->term()->isStruct()) { Print("WARNING: debris struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseDebris(val, ++debris_index); } } else if (defname == "debris_mass") { GetDefNumber(debris[debris_index].mass, def, filename); } else if (defname == "debris_speed") { GetDefNumber(debris[debris_index].speed, def, filename); } else if (defname == "debris_drag") { GetDefNumber(debris[debris_index].drag, def, filename); } else if (defname == "debris_loc") { GetDefVec(debris[debris_index].loc, def, filename); debris[debris_index].loc *= (float) scale; } else if (defname == "debris_count") { GetDefNumber(debris[debris_index].count, def, filename); } else if (defname == "debris_life") { GetDefNumber(debris[debris_index].life, def, filename); } else if (defname == "debris_fire") { if (++fire_index < 5) { GetDefVec(debris[debris_index].fire_loc[fire_index], def, filename); debris[debris_index].fire_loc[fire_index] *= (float) scale; } } else if (defname == "debris_fire_type") { GetDefNumber(debris[debris_index].fire_type, def, filename); } } } } // +--------------------------------------------------------------------+ void ShipDesign::ParseExplosion(TermStruct* val, int index) { ShipExplosion* exp = &explosion[index]; for (int i = 0; i < val->elements()->size(); i++) { TermDef* def = val->elements()->at(i)->isDef(); if (def) { Text defname = def->name()->value(); defname.setSensitive(false); if (defname == "time") { GetDefNumber(exp->time, def, filename); } else if (defname == "type") { GetDefNumber(exp->type, def, filename); } else if (defname == "loc") { GetDefVec(exp->loc, def, filename); exp->loc *= (float) scale; } else if (defname == "final") { GetDefBool(exp->final, def, filename); } } } } // +--------------------------------------------------------------------+ void ShipDesign::ParseDebris(TermStruct* val, int index) { char model_name[NAMELEN]; int fire_index = 0; ShipDebris* deb = &debris[index]; for (int i = 0; i < val->elements()->size(); i++) { TermDef* def = val->elements()->at(i)->isDef(); if (def) { Text defname = def->name()->value(); if (defname == "model") { GetDefText(model_name, def, filename); Model* model = new(__FILE__,__LINE__) Model; if (!model->Load(model_name, scale)) { Print("Could not load debris model '%s'\n", model_name); delete model; return; } PrepareModel(*model); deb->model = model; } else if (defname == "mass") { GetDefNumber(deb->mass, def, filename); } else if (defname == "speed") { GetDefNumber(deb->speed, def, filename); } else if (defname == "drag") { GetDefNumber(deb->drag, def, filename); } else if (defname == "loc") { GetDefVec(deb->loc, def, filename); deb->loc *= (float) scale; } else if (defname == "count") { GetDefNumber(deb->count, def, filename); } else if (defname == "life") { GetDefNumber(deb->life, def, filename); } else if (defname == "fire") { if (fire_index < 5) { GetDefVec(deb->fire_loc[fire_index], def, filename); deb->fire_loc[fire_index] *= (float) scale; fire_index++; } } else if (defname == "fire_type") { GetDefNumber(deb->fire_type, def, filename); } } } } // +--------------------------------------------------------------------+ void ShipDesign::ParseMap(TermStruct* val) { char sprite_name[NAMELEN]; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "sprite") { GetDefText(sprite_name, pdef, filename); Bitmap* sprite = new(__FILE__,__LINE__) Bitmap(); DataLoader* loader = DataLoader::GetLoader(); loader->LoadBitmap(sprite_name, *sprite, Bitmap::BMP_TRANSLUCENT); map_sprites.append(sprite); } } } } // +--------------------------------------------------------------------+ void ShipDesign::ParseSquadron(TermStruct* val) { char name[NAMELEN]; char design[NAMELEN]; int count = 4; int avail = 4; name[0] = 0; design[0] = 0; for (int i = 0; i < val->elements()->size(); i++) { TermDef* pdef = val->elements()->at(i)->isDef(); if (pdef) { Text defname = pdef->name()->value(); defname.setSensitive(false); if (defname == "name") { GetDefText(name, pdef, filename); } else if (defname == "design") { GetDefText(design, pdef, filename); } else if (defname == "count") { GetDefNumber(count, pdef, filename); } else if (defname == "avail") { GetDefNumber(avail, pdef, filename); } } } ShipSquadron* s = new(__FILE__,__LINE__) ShipSquadron; strcpy_s(s->name, name); s->design = Get(design); s->count = count; s->avail = avail; squadrons.append(s); } // +--------------------------------------------------------------------+ Skin* ShipDesign::ParseSkin(TermStruct* val) { Skin* skin = 0; char name[NAMELEN]; name[0] = 0; for (int i = 0; i < val->elements()->size(); i++) { TermDef* def = val->elements()->at(i)->isDef(); if (def) { Text defname = def->name()->value(); defname.setSensitive(false); if (defname == "name") { GetDefText(name, def, filename); skin = new(__FILE__,__LINE__) Skin(name); } else if (defname == "material" || defname == "mtl") { if (!def->term() || !def->term()->isStruct()) { Print("WARNING: skin struct missing in '%s'\n", filename); } else { TermStruct* val = def->term()->isStruct(); ParseSkinMtl(val, skin); } } } } if (skin && skin->NumCells()) { skins.append(skin); } else if (skin) { delete skin; skin = 0; } return skin; } void ShipDesign::ParseSkinMtl(TermStruct* val, Skin* skin) { Material* mtl = new(__FILE__,__LINE__) Material; if (mtl == nullptr) return; for (int i = 0; i < val->elements()->size(); i++) { TermDef* def = val->elements()->at(i)->isDef(); if (def) { Text defname = def->name()->value(); defname.setSensitive(false); if (defname == "name") { GetDefText(mtl->name, def, filename); } else if (defname == "Ka") { GetDefColor(mtl->Ka, def, filename); } else if (defname == "Kd") { GetDefColor(mtl->Kd, def, filename); } else if (defname == "Ks") { GetDefColor(mtl->Ks, def, filename); } else if (defname == "Ke") { GetDefColor(mtl->Ke, def, filename); } else if (defname == "Ns" || defname == "power") { GetDefNumber(mtl->power, def, filename); } else if (defname == "bump") { GetDefNumber(mtl->bump, def, filename); } else if (defname == "luminous") { GetDefBool(mtl->luminous, def, filename); } else if (defname == "blend") { if (def->term() && def->term()->isNumber()) GetDefNumber(mtl->blend, def, filename); else if (def->term() && def->term()->isText()) { Text val; GetDefText(val, def, filename); val.setSensitive(false); if (val == "alpha" || val == "translucent") mtl->blend = Material::MTL_TRANSLUCENT; else if (val == "additive") mtl->blend = Material::MTL_ADDITIVE; else mtl->blend = Material::MTL_SOLID; } } else if (defname.indexOf("tex_d") == 0) { char tex_name[64]; if (!GetDefText(tex_name, def, filename)) Print("WARNING: invalid or missing tex_diffuse in '%s'\n", filename); DataLoader* loader = DataLoader::GetLoader(); loader->LoadTexture(tex_name, mtl->tex_diffuse); } else if (defname.indexOf("tex_s") == 0) { char tex_name[64]; if (!GetDefText(tex_name, def, filename)) Print("WARNING: invalid or missing tex_specular in '%s'\n", filename); DataLoader* loader = DataLoader::GetLoader(); loader->LoadTexture(tex_name, mtl->tex_specular); } else if (defname.indexOf("tex_b") == 0) { char tex_name[64]; if (!GetDefText(tex_name, def, filename)) Print("WARNING: invalid or missing tex_bumpmap in '%s'\n", filename); DataLoader* loader = DataLoader::GetLoader(); loader->LoadTexture(tex_name, mtl->tex_bumpmap); } else if (defname.indexOf("tex_e") == 0) { char tex_name[64]; if (!GetDefText(tex_name, def, filename)) Print("WARNING: invalid or missing tex_emissive in '%s'\n", filename); DataLoader* loader = DataLoader::GetLoader(); loader->LoadTexture(tex_name, mtl->tex_emissive); } } } if (skin && mtl) skin->AddMaterial(mtl); } const Skin* ShipDesign::FindSkin(const char* skin_name) const { int n = skins.size(); for (int i = 0; i < n; i++) { Skin* s = skins[n-1-i]; if (!strcmp(s->Name(), skin_name)) return s; } return 0; }