ShipType::ShipType(const Id &_id, const std::string &path) { Json::Reader reader; Json::Value data; isGlobalColorDefined = false; auto fd = FileSystem::gameDataFiles.ReadFile(path); if (!fd) { Output("couldn't open ship def '%s'\n", path.c_str()); return; } if (!reader.parse(fd->GetData(), fd->GetData()+fd->GetSize(), data)) { Output("couldn't read ship def '%s': %s\n", path.c_str(), reader.getFormattedErrorMessages().c_str()); return; } // determine what kind (tag) of ship this is. const std::string tagStr = data.get("tag", "").asString(); if( tagStr.empty() || strcasecmp(tagStr.c_str(), "ship")==0 ) { tag = TAG_SHIP; } else if( strcasecmp(tagStr.c_str(), "static")==0 ) { tag = TAG_STATIC_SHIP; } else if( strcasecmp(tagStr.c_str(), "missile")==0 ) { tag = TAG_MISSILE; } id = _id; name = data.get("name", "").asString(); shipClass = data.get("ship_class", "").asString(); manufacturer = data.get("manufacturer", "").asString(); modelName = data.get("model", "").asString(); cockpitName = data.get("cockpit", "").asString(); linThrust[THRUSTER_REVERSE] = data.get("reverse_thrust", 0.0f).asFloat(); linThrust[THRUSTER_FORWARD] = data.get("forward_thrust", 0.0f).asFloat(); linThrust[THRUSTER_UP] = data.get("up_thrust", 0.0f).asFloat(); linThrust[THRUSTER_DOWN] = data.get("down_thrust", 0.0f).asFloat(); linThrust[THRUSTER_LEFT] = data.get("left_thrust", 0.0f).asFloat(); linThrust[THRUSTER_RIGHT] = data.get("right_thrust", 0.0f).asFloat(); angThrust = data.get("angular_thrust", 0.0f).asFloat(); // Parse global thrusters color bool error = false; int parse = 0; for( Json::Value::iterator thruster_color = data["thruster_global_color"].begin() ; thruster_color != data["thruster_global_color"].end() ; ++thruster_color ) { const std::string colorchannel = thruster_color.key().asString(); if (colorchannel.length()!=1) { error = true; break; } if (colorchannel.at(0) == 'r') { globalThrusterColor.r = data["thruster_global_color"].get(colorchannel, 0).asInt(); parse++; continue; } else if (colorchannel.at(0) == 'g') { globalThrusterColor.g = data["thruster_global_color"].get(colorchannel, 0).asInt(); parse++; continue; } else if (colorchannel.at(0) == 'b') { globalThrusterColor.b = data["thruster_global_color"].get(colorchannel, 0).asInt(); parse++; continue; } else { // No 'r', no 'g', no 'b', no good :/ error = true; break; } } if (error==true) { Output("In file \"%s.json\" global thrusters custom color must be \"r\",\"g\" and \"b\"\n", modelName.c_str()); } else if (parse>0 && parse<3) { Output("In file \"%s.json\" global thrusters custom color is malformed\n", modelName.c_str()); } else if (parse==3) { globalThrusterColor.a = 255; isGlobalColorDefined = true; } // Parse direction thrusters color for (int i=0; i<THRUSTER_MAX; i++) isDirectionColorDefined[i]=false; error = false; for( Json::Value::iterator thruster_color = data["thruster_direction_color"].begin() ; thruster_color != data["thruster_direction_color"].end() ; ++thruster_color ) { const std::string th_color_dir = thruster_color.key().asString(); Json::Value dir_color = data["thruster_direction_color"].get(th_color_dir, 0); Color color; if (!dir_color.isMember("r")||!dir_color.isMember("g")||!dir_color.isMember("b")) { error = true; continue /* for */; } else { color.r = dir_color["r"].asInt(); color.g = dir_color["g"].asInt(); color.b = dir_color["b"].asInt(); color.a = 255; } if (th_color_dir.find("forward")!=std::string::npos) { isDirectionColorDefined[THRUSTER_FORWARD]=true; directionThrusterColor[THRUSTER_FORWARD]= color; } if (th_color_dir.find("retro")!=std::string::npos) { isDirectionColorDefined[THRUSTER_REVERSE]=true; directionThrusterColor[THRUSTER_REVERSE]= color; } if (th_color_dir.find("left")!=std::string::npos) { isDirectionColorDefined[THRUSTER_LEFT]=true; directionThrusterColor[THRUSTER_LEFT]= color; } if (th_color_dir.find("right")!=std::string::npos) { isDirectionColorDefined[THRUSTER_RIGHT]=true; directionThrusterColor[THRUSTER_RIGHT]= color; } if (th_color_dir.find("up")!=std::string::npos) { isDirectionColorDefined[THRUSTER_UP]=true; directionThrusterColor[THRUSTER_UP]= color; } if (th_color_dir.find("down")!=std::string::npos) { isDirectionColorDefined[THRUSTER_DOWN]=true; directionThrusterColor[THRUSTER_DOWN]= color; } } if (error==true) { for (int i=0; i<THRUSTER_MAX; i++) isDirectionColorDefined[i]=false; Output("In file \"%s.json\" directional thrusters custom color must be \"r\",\"g\" and \"b\"\n", modelName.c_str()); } // invert values where necessary linThrust[THRUSTER_FORWARD] *= -1.f; linThrust[THRUSTER_LEFT] *= -1.f; linThrust[THRUSTER_DOWN] *= -1.f; // angthrust fudge (XXX: why?) angThrust = angThrust * 0.5f; hullMass = data.get("hull_mass", 100).asInt(); capacity = data.get("capacity", 0).asInt(); fuelTankMass = data.get("fuel_tank_mass", 5).asInt(); for( Json::Value::iterator slot = data["slots"].begin() ; slot != data["slots"].end() ; ++slot ) { const std::string slotname = slot.key().asString(); slots[slotname] = data["slots"].get(slotname, 0).asInt(); } for( Json::Value::iterator role = data["roles"].begin(); role != data["roles"].end(); ++role ) { const std::string rolename = role.key().asString(); roles[rolename] = data["roles"].get(rolename, 0).asBool(); } for(int it=0;it<4;it++) thrusterUpgrades[it] = 1.0 + (double(it)/10.0); for( Json::Value::iterator slot = data["thrust_upgrades"].begin() ; slot != data["thrust_upgrades"].end() ; ++slot ) { const std::string slotname = slot.key().asString(); const int index = Clamp(atoi(&slotname.c_str()[9]), 1, 3); thrusterUpgrades[index] = data["thrust_upgrades"].get(slotname, 0).asDouble(); } atmosphericPressureLimit = data.get("atmospheric_pressure_limit", 10.0).asDouble(); // 10 atmosphere is about 90 metres underwater (on Earth) { const auto it = slots.find("engine"); if (it != slots.end()) { it->second = Clamp(it->second, 0, 1); } } effectiveExhaustVelocity = data.get("effective_exhaust_velocity", -1.0f).asFloat(); const float thruster_fuel_use = data.get("thruster_fuel_use", -1.0f).asFloat(); if(effectiveExhaustVelocity < 0 && thruster_fuel_use < 0) { // default value of v_c is used effectiveExhaustVelocity = 55000000; } else if(effectiveExhaustVelocity < 0 && thruster_fuel_use >= 0) { // v_c undefined and thruster fuel use defined -- use it! effectiveExhaustVelocity = GetEffectiveExhaustVelocity(fuelTankMass, thruster_fuel_use, linThrust[Thruster::THRUSTER_FORWARD]); } else { if(thruster_fuel_use >= 0) { Output("Warning: Both thruster_fuel_use and effective_exhaust_velocity defined for %s, using effective_exhaust_velocity.\n", modelName.c_str()); } } baseprice = data.get("price", 0.0).asDouble(); minCrew = data.get("min_crew", 1).asInt(); maxCrew = data.get("max_crew", 1).asInt(); hyperdriveClass = data.get("hyperdrive_class", 1).asInt(); }
int _define_ship(lua_State *L, ShipType::Tag tag, std::vector<ShipType::Id> *list) { if (s_currentShipFile.empty()) return luaL_error(L, "ship file contains multiple ship definitions"); Json::Value data; ShipType s; s.tag = tag; s.id = s_currentShipFile; LUA_DEBUG_START(L); LuaTable t(L, -1); s.name = t.Get("name", ""); s.shipClass = t.Get("ship_class", "unknown"); s.manufacturer = t.Get("manufacturer", "unknown"); s.modelName = t.Get("model", ""); data["name"] = s.name; data["ship_class"] = s.shipClass; data["manufacturer"] = s.manufacturer; data["model"] = s.modelName; s.cockpitName = t.Get("cockpit", ""); s.linThrust[ShipType::THRUSTER_REVERSE] = t.Get("reverse_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_FORWARD] = t.Get("forward_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_UP] = t.Get("up_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_DOWN] = t.Get("down_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_LEFT] = t.Get("left_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_RIGHT] = t.Get("right_thrust", 0.0f); s.angThrust = t.Get("angular_thrust", 0.0f); data["cockpit"] = s.cockpitName; data["reverse_thrust"] = s.linThrust[ShipType::THRUSTER_REVERSE]; data["forward_thrust"] = s.linThrust[ShipType::THRUSTER_FORWARD]; data["up_thrust"] = s.linThrust[ShipType::THRUSTER_UP]; data["down_thrust"] = s.linThrust[ShipType::THRUSTER_DOWN]; data["left_thrust"] = s.linThrust[ShipType::THRUSTER_LEFT]; data["right_thrust"] = s.linThrust[ShipType::THRUSTER_RIGHT]; data["angular_thrust"] = s.angThrust; // invert values where necessary s.linThrust[ShipType::THRUSTER_FORWARD] *= -1.f; s.linThrust[ShipType::THRUSTER_LEFT] *= -1.f; s.linThrust[ShipType::THRUSTER_DOWN] *= -1.f; // angthrust fudge (XXX: why?) s.angThrust = s.angThrust / 2; s.capacity = t.Get("capacity", 0); s.hullMass = t.Get("hull_mass", 100); s.fuelTankMass = t.Get("fuel_tank_mass", 5); data["capacity"] = s.capacity; data["hull_mass"] = s.hullMass; data["fuel_tank_mass"] = s.fuelTankMass; LuaTable slot_table = t.Sub("slots"); if (slot_table.GetLua()) { s.slots = slot_table.GetMap<std::string, int>(); } lua_pop(L, 1); { const auto it = s.slots.find("engine"); if (it != s.slots.end()) { it->second = Clamp(it->second, 0, 1); } } for( auto slot : s.slots ) { data["slots"][slot.first] = slot.second; } // fuel_use_rate can be given in two ways float thruster_fuel_use = 0; s.effectiveExhaustVelocity = t.Get("effective_exhaust_velocity", -1.0f); thruster_fuel_use = t.Get("thruster_fuel_use", -1.0f); data["effective_exhaust_velocity"] = s.effectiveExhaustVelocity; data["thruster_fuel_use"] = thruster_fuel_use; if(s.effectiveExhaustVelocity < 0 && thruster_fuel_use < 0) { // default value of v_c is used s.effectiveExhaustVelocity = 55000000; } else if(s.effectiveExhaustVelocity < 0 && thruster_fuel_use >= 0) { // v_c undefined and thruster fuel use defined -- use it! s.effectiveExhaustVelocity = GetEffectiveExhaustVelocity(s.fuelTankMass, thruster_fuel_use, s.linThrust[ShipType::THRUSTER_FORWARD]); } else { if(thruster_fuel_use >= 0) Output("Warning: Both thruster_fuel_use and effective_exhaust_velocity defined for %s, using effective_exhaust_velocity.\n", s.modelName.c_str()); } s.baseprice = t.Get("price", 0.0); s.minCrew = t.Get("min_crew", 1); s.maxCrew = t.Get("max_crew", 1); s.hyperdriveClass = t.Get("hyperdrive_class", 1); data["price"] = s.baseprice; data["min_crew"] = s.minCrew; data["max_crew"] = s.maxCrew; data["hyperdrive_class"] = s.hyperdriveClass; Json::StyledWriter writer; const std::string saveMe = writer.write( data ); const std::string path("ships/" + s_currentShipFile + ".json"); FileSystem::FileSourceFS newFS(FileSystem::GetDataDir()); FILE *f = newFS.OpenWriteStream(path); if (!f) { Output("couldn't open file for writing '%s'\n", path.c_str()); abort(); } fwrite(saveMe.data(), saveMe.length(), 1, f); fclose(f); lua_pop(L, 1); LUA_DEBUG_END(L, 0); //sanity check if (s.name.empty()) return luaL_error(L, "Ship has no name"); if (s.modelName.empty()) return luaL_error(L, "Missing model name in ship"); if (s.minCrew < 1 || s.maxCrew < 1 || s.minCrew > s.maxCrew) return luaL_error(L, "Invalid values for min_crew and max_crew"); const std::string& id = s_currentShipFile; typedef std::map<std::string, const ShipType>::iterator iter; std::pair<iter, bool> result = ShipType::types.insert(std::make_pair(id, s)); if (result.second) list->push_back(s_currentShipFile); else return luaL_error(L, "Ship '%s' was already defined by a different file", id.c_str()); s_currentShipFile.clear(); return 0; }
ShipType::ShipType(const Id &_id, const std::string &path) { Json::Reader reader; Json::Value data; auto fd = FileSystem::gameDataFiles.ReadFile(path); if (!fd) { Output("couldn't open ship def '%s'\n", path.c_str()); return; } if (!reader.parse(fd->GetData(), fd->GetData()+fd->GetSize(), data)) { Output("couldn't read ship def '%s': %s\n", path.c_str(), reader.getFormattedErrorMessages().c_str()); return; } // determine what kind (tag) of ship this is. const std::string tagStr = data.get("tag", "").asString(); if( tagStr.empty() || strcasecmp(tagStr.c_str(), "ship")==0 ) { tag = TAG_SHIP; } else if( strcasecmp(tagStr.c_str(), "static")==0 ) { tag = TAG_STATIC_SHIP; } else if( strcasecmp(tagStr.c_str(), "missile")==0 ) { tag = TAG_MISSILE; } id = _id; name = data.get("name", "").asString(); shipClass = data.get("ship_class", "").asString(); manufacturer = data.get("manufacturer", "").asString(); modelName = data.get("model", "").asString(); cockpitName = data.get("cockpit", "").asString(); linThrust[THRUSTER_REVERSE] = data.get("reverse_thrust", 0.0f).asFloat(); linThrust[THRUSTER_FORWARD] = data.get("forward_thrust", 0.0f).asFloat(); linThrust[THRUSTER_UP] = data.get("up_thrust", 0.0f).asFloat(); linThrust[THRUSTER_DOWN] = data.get("down_thrust", 0.0f).asFloat(); linThrust[THRUSTER_LEFT] = data.get("left_thrust", 0.0f).asFloat(); linThrust[THRUSTER_RIGHT] = data.get("right_thrust", 0.0f).asFloat(); angThrust = data.get("angular_thrust", 0.0f).asFloat(); // invert values where necessary linThrust[THRUSTER_FORWARD] *= -1.f; linThrust[THRUSTER_LEFT] *= -1.f; linThrust[THRUSTER_DOWN] *= -1.f; // angthrust fudge (XXX: why?) angThrust = angThrust * 0.5f; hullMass = data.get("hull_mass", 100).asInt(); capacity = data.get("capacity", 0).asInt(); fuelTankMass = data.get("fuel_tank_mass", 5).asInt(); for( Json::Value::iterator slot = data["slots"].begin() ; slot != data["slots"].end() ; ++slot ) { const std::string slotname = slot.key().asString(); slots[slotname] = data["slots"].get(slotname, 0).asInt(); } { const auto it = slots.find("engine"); if (it != slots.end()) { it->second = Clamp(it->second, 0, 1); } } effectiveExhaustVelocity = data.get("effective_exhaust_velocity", -1.0f).asFloat(); const float thruster_fuel_use = data.get("thruster_fuel_use", -1.0f).asFloat(); if(effectiveExhaustVelocity < 0 && thruster_fuel_use < 0) { // default value of v_c is used effectiveExhaustVelocity = 55000000; } else if(effectiveExhaustVelocity < 0 && thruster_fuel_use >= 0) { // v_c undefined and thruster fuel use defined -- use it! effectiveExhaustVelocity = GetEffectiveExhaustVelocity(fuelTankMass, thruster_fuel_use, linThrust[ShipType::THRUSTER_FORWARD]); } else { if(thruster_fuel_use >= 0) { Output("Warning: Both thruster_fuel_use and effective_exhaust_velocity defined for %s, using effective_exhaust_velocity.\n", modelName.c_str()); } } baseprice = data.get("price", 0.0).asDouble(); minCrew = data.get("min_crew", 1).asInt(); maxCrew = data.get("max_crew", 1).asInt(); hyperdriveClass = data.get("hyperdrive_class", 1).asInt(); }
int _define_ship(lua_State *L, ShipType::Tag tag, std::vector<ShipType::Id> *list) { if (s_currentShipFile.empty()) return luaL_error(L, "ship file contains multiple ship definitions"); ShipType s; s.tag = tag; s.id = s_currentShipFile; LUA_DEBUG_START(L); LuaTable t(L, -1); s.name = t.Get("name", ""); s.shipClass = t.Get("ship_class", "unknown"); s.manufacturer = t.Get("manufacturer", "unknown"); s.modelName = t.Get("model", ""); s.cockpitName = t.Get("cockpit", ""); s.linThrust[ShipType::THRUSTER_REVERSE] = t.Get("reverse_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_FORWARD] = t.Get("forward_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_UP] = t.Get("up_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_DOWN] = t.Get("down_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_LEFT] = t.Get("left_thrust", 0.0f); s.linThrust[ShipType::THRUSTER_RIGHT] = t.Get("right_thrust", 0.0f); s.angThrust = t.Get("angular_thrust", 0.0f); // invert values where necessary s.linThrust[ShipType::THRUSTER_FORWARD] *= -1.f; s.linThrust[ShipType::THRUSTER_LEFT] *= -1.f; s.linThrust[ShipType::THRUSTER_DOWN] *= -1.f; // angthrust fudge (XXX: why?) s.angThrust = s.angThrust / 2; lua_pushstring(L, "camera_offset"); lua_gettable(L, -2); if (!lua_isnil(L, -1)) Output("ship definition for '%s' has deprecated 'camera_offset' field\n", s.id.c_str()); lua_pop(L, 1); s.cameraOffset = t.Get("camera_offset", vector3d(0.0)); for (int i=0; i<Equip::SLOT_MAX; i++) s.equipSlotCapacity[i] = 0; s.equipSlotCapacity[Equip::SLOT_CARGO] = t.Get("max_cargo", 0); s.equipSlotCapacity[Equip::SLOT_ENGINE] = t.Get("max_engine", 1); s.equipSlotCapacity[Equip::SLOT_LASER] = t.Get("max_laser", 1); s.equipSlotCapacity[Equip::SLOT_MISSILE] = t.Get("max_missile", 0); s.equipSlotCapacity[Equip::SLOT_ECM] = t.Get("max_ecm", 1); s.equipSlotCapacity[Equip::SLOT_SCANNER] = t.Get("max_scanner", 1); s.equipSlotCapacity[Equip::SLOT_RADARMAPPER] = t.Get("max_radarmapper", 1); s.equipSlotCapacity[Equip::SLOT_HYPERCLOUD] = t.Get("max_hypercloud", 1); s.equipSlotCapacity[Equip::SLOT_HULLAUTOREPAIR] = t.Get("max_hullautorepair", 1); s.equipSlotCapacity[Equip::SLOT_ENERGYBOOSTER] = t.Get("max_energybooster", 1); s.equipSlotCapacity[Equip::SLOT_ATMOSHIELD] = t.Get("max_atmoshield", 1); s.equipSlotCapacity[Equip::SLOT_CABIN] = t.Get("max_cabin", 50); s.equipSlotCapacity[Equip::SLOT_SHIELD] = t.Get("max_shield", 9999); s.equipSlotCapacity[Equip::SLOT_FUELSCOOP] = t.Get("max_fuelscoop", 1); s.equipSlotCapacity[Equip::SLOT_CARGOSCOOP] = t.Get("max_cargoscoop", 1); s.equipSlotCapacity[Equip::SLOT_LASERCOOLER] = t.Get("max_lasercooler", 1); s.equipSlotCapacity[Equip::SLOT_CARGOLIFESUPPORT] = t.Get("max_cargolifesupport", 1); s.equipSlotCapacity[Equip::SLOT_AUTOPILOT] = t.Get("max_autopilot", 1); s.capacity = t.Get("capacity", 0); s.hullMass = t.Get("hull_mass", 100); s.fuelTankMass = t.Get("fuel_tank_mass", 5); LuaTable slot_table = t.Sub("slots"); if (slot_table.GetLua()) { s.slots = slot_table.GetMap<std::string, int>(); } lua_pop(L, 1); // fuel_use_rate can be given in two ways float thruster_fuel_use = 0; s.effectiveExhaustVelocity = t.Get("effective_exhaust_velocity", -1.0f); thruster_fuel_use = t.Get("thruster_fuel_use", -1.0f); if(s.effectiveExhaustVelocity < 0 && thruster_fuel_use < 0) { // default value of v_c is used s.effectiveExhaustVelocity = 55000000; } else if(s.effectiveExhaustVelocity < 0 && thruster_fuel_use >= 0) { // v_c undefined and thruster fuel use defined -- use it! s.effectiveExhaustVelocity = GetEffectiveExhaustVelocity(s.fuelTankMass, thruster_fuel_use, s.linThrust[ShipType::THRUSTER_FORWARD]); } else { if(thruster_fuel_use >= 0) Output("Warning: Both thruster_fuel_use and effective_exhaust_velocity defined for %s, using effective_exhaust_velocity.\n", s.modelName.c_str()); } s.baseprice = t.Get("price", 0); s.baseprice *= 100; // in hundredths of credits s.minCrew = t.Get("min_crew", 1); s.maxCrew = t.Get("max_crew", 1); s.equipSlotCapacity[Equip::SLOT_ENGINE] = Clamp(s.equipSlotCapacity[Equip::SLOT_ENGINE], 0, 1); s.hyperdriveClass = t.Get("hyperdrive_class", 1); for (int i = 0; i < ShipType::GUNMOUNT_MAX; i++) { s.gunMount[i].pos = vector3f(0,0,0); s.gunMount[i].dir = vector3f(0,0,1); s.gunMount[i].sep = 5; s.gunMount[i].orient = ShipType::DUAL_LASERS_HORIZONTAL; } lua_pushstring(L, "gun_mounts"); lua_gettable(L, -2); if (lua_istable(L, -1)) { Output("ship definition for '%s' has deprecated 'gun_mounts' field\n", s.id.c_str()); for (unsigned int i=0; i<lua_rawlen(L,-1); i++) { lua_pushinteger(L, i+1); lua_gettable(L, -2); if (lua_istable(L, -1) && lua_rawlen(L,-1) == 4) { lua_pushinteger(L, 1); lua_gettable(L, -2); s.gunMount[i].pos = LuaVector::CheckFromLuaF(L, -1); lua_pop(L, 1); lua_pushinteger(L, 2); lua_gettable(L, -2); s.gunMount[i].dir = LuaVector::CheckFromLuaF(L, -1); lua_pop(L, 1); lua_pushinteger(L, 3); lua_gettable(L, -2); s.gunMount[i].sep = lua_tonumber(L,-1); lua_pop(L, 1); lua_pushinteger(L, 4); lua_gettable(L, -2); s.gunMount[i].orient = static_cast<ShipType::DualLaserOrientation>( LuaConstants::GetConstantFromArg(L, "DualLaserOrientation", -1)); lua_pop(L, 1); } lua_pop(L, 1); } } lua_pop(L, 1); LUA_DEBUG_END(L, 0); //sanity check if (s.name.empty()) return luaL_error(L, "Ship has no name"); if (s.modelName.empty()) return luaL_error(L, "Missing model name in ship"); if (s.minCrew < 1 || s.maxCrew < 1 || s.minCrew > s.maxCrew) return luaL_error(L, "Invalid values for min_crew and max_crew"); const std::string& id = s_currentShipFile; typedef std::map<ShipType::Id, ShipType>::iterator iter; std::pair<iter, bool> result = ShipType::types.insert(std::make_pair(id, s)); if (result.second) list->push_back(s_currentShipFile); else return luaL_error(L, "Ship '%s' was already defined by a different file", id.c_str()); s_currentShipFile.clear(); return 0; }
// returns speed that can be reached using fuel minus reserve according to the Tsiolkovsky equation double Ship::GetSpeedReachedWithFuel() { double fuelmass = 1000*GetShipType().fuelTankMass * (m_thrusterFuel - m_reserveFuel); if (fuelmass < 0) return 0.0; return GetEffectiveExhaustVelocity() * log(GetMass()/(GetMass()-fuelmass)); }