double DynamicBody::CalcAtmosphericForce(double dragCoeff) const { Body *body = GetFrame()->GetBody(); if (!body || !GetFrame()->IsRotFrame() || !body->IsType(Object::PLANET)) return 0.0; Planet *planet = static_cast<Planet*>(body); double dist = GetPosition().Length(); double speed = m_vel.Length(); double pressure, density; planet->GetAtmosphericState(dist, &pressure, &density); const double radius = GetClipRadius(); // bogus, preserving behaviour const double area = radius; // ^^^ yes that is as stupid as it looks return 0.5*density*speed*speed*area*dragCoeff; }
void DynamicBody::CalcExternalForce() { // gravity if (!GetFrame()) return; // no external force if not in a frame Body *body = GetFrame()->GetBody(); if (body && !body->IsType(Object::SPACESTATION)) { // they ought to have mass though... vector3d b1b2 = GetPosition(); double m1m2 = GetMass() * body->GetMass(); double invrsqr = 1.0 / b1b2.LengthSqr(); double force = G*m1m2 * invrsqr; m_externalForce = -b1b2 * sqrt(invrsqr) * force; } else m_externalForce = vector3d(0.0); m_gravityForce = m_externalForce; // atmospheric drag if (body && GetFrame()->IsRotFrame() && body->IsType(Object::PLANET)) { Planet *planet = static_cast<Planet*>(body); double dist = GetPosition().Length(); double speed = m_vel.Length(); double pressure, density; planet->GetAtmosphericState(dist, &pressure, &density); const double radius = GetClipRadius(); // bogus, preserving behaviour const double AREA = radius; // ^^^ yes that is as stupid as it looks const double DRAG_COEFF = 0.1; // 'smooth sphere' vector3d dragDir = -m_vel.NormalizedSafe(); vector3d fDrag = 0.5*density*speed*speed*AREA*DRAG_COEFF*dragDir; // make this a bit less daft at high time accel // only allow atmosForce to increase by .1g per frame vector3d f1g = m_atmosForce + dragDir * GetMass(); if (fDrag.LengthSqr() > f1g.LengthSqr()) m_atmosForce = f1g; else m_atmosForce = fDrag; m_externalForce += m_atmosForce; } else m_atmosForce = vector3d(0.0); // centrifugal and coriolis forces for rotating frames if (GetFrame()->IsRotFrame()) { vector3d angRot(0, GetFrame()->GetAngSpeed(), 0); m_externalForce -= m_mass * angRot.Cross(angRot.Cross(GetPosition())); // centrifugal m_externalForce -= 2 * m_mass * angRot.Cross(GetVelocity()); // coriolis } }
// Calculates the ambiently and directly lit portions of the lighting model taking into account the atmosphere and sun positions at a given location // 1. Calculates the amount of direct illumination available taking into account // * multiple suns // * sun positions relative to up direction i.e. light is dimmed as suns set // * Thickness of the atmosphere overhead i.e. as atmospheres get thicker light starts dimming earlier as sun sets, without atmosphere the light switches off at point of sunset // 2. Calculates the split between ambient and directly lit portions taking into account // * Atmosphere density (optical thickness) of the sky dome overhead // as optical thickness increases the fraction of ambient light increases // this takes altitude into account automatically // * As suns set the split is biased towards ambient void ModelBody::CalcLighting(double &ambient, double &direct, const Camera *camera) { const double minAmbient = 0.05; ambient = minAmbient; direct = 1.0; Body *astro = GetFrame()->GetBody(); if ( ! (astro && astro->IsType(Object::PLANET)) ) return; Planet *planet = static_cast<Planet*>(astro); // position relative to the rotating frame of the planet vector3d upDir = GetInterpPositionRelTo(planet->GetFrame()); const double planetRadius = planet->GetSystemBody()->GetRadius(); const double dist = std::max(planetRadius, upDir.Length()); upDir = upDir.Normalized(); double pressure, density; planet->GetAtmosphericState(dist, &pressure, &density); double surfaceDensity; Color cl; planet->GetSystemBody()->GetAtmosphereFlavor(&cl, &surfaceDensity); // approximate optical thickness fraction as fraction of density remaining relative to earths double opticalThicknessFraction = density/EARTH_ATMOSPHERE_SURFACE_DENSITY; // tweak optical thickness curve - lower exponent ==> higher altitude before ambient level drops // Commenting this out, since it leads to a sharp transition at // atmosphereRadius, where density is suddenly 0 //opticalThicknessFraction = pow(std::max(0.00001,opticalThicknessFraction),0.15); //max needed to avoid 0^power if (opticalThicknessFraction < 0.0001) return; //step through all the lights and calculate contributions taking into account sun position double light = 0.0; double light_clamped = 0.0; const std::vector<Camera::LightSource> &lightSources = camera->GetLightSources(); for(std::vector<Camera::LightSource>::const_iterator l = lightSources.begin(); l != lightSources.end(); ++l) { double sunAngle; // calculate the extent the sun is towards zenith if (l->GetBody()){ // relative to the rotating frame of the planet const vector3d lightDir = (l->GetBody()->GetInterpPositionRelTo(planet->GetFrame()).Normalized()); sunAngle = lightDir.Dot(upDir); } else { // light is the default light for systems without lights sunAngle = 1.0; } const double critAngle = -sqrt(dist*dist-planetRadius*planetRadius)/dist; //0 to 1 as sunangle goes from critAngle to 1.0 double sunAngle2 = (Clamp(sunAngle, critAngle, 1.0)-critAngle)/(1.0-critAngle); // angle at which light begins to fade on Earth const double surfaceStartAngle = 0.3; // angle at which sun set completes, which should be after sun has dipped below the horizon on Earth const double surfaceEndAngle = -0.18; const double start = std::min((surfaceStartAngle*opticalThicknessFraction),1.0); const double end = std::max((surfaceEndAngle*opticalThicknessFraction),-0.2); sunAngle = (Clamp(sunAngle-critAngle, end, start)-end)/(start-end); light += sunAngle; light_clamped += sunAngle2; } light_clamped /= lightSources.size(); light /= lightSources.size(); // brightness depends on optical depth and intensity of light from all the stars direct = 1.0 - Clamp((1.0 - light),0.0,1.0) * Clamp(opticalThicknessFraction,0.0,1.0); // ambient light fraction // alter ratio between directly and ambiently lit portions towards ambiently lit as sun sets const double fraction = ( 0.2 + 0.8 * (1.0-light_clamped) ) * Clamp(opticalThicknessFraction,0.0,1.0); // fraction of light left over to be lit directly direct = (1.0-fraction)*direct; // scale ambient by amount of light ambient = fraction*(Clamp((light),0.0,1.0))*0.25; ambient = std::max(minAmbient, ambient); }
void Camera::Draw(Renderer *renderer, const Body *excludeBody) { if (!m_camFrame) return; if (!renderer) return; m_renderer = renderer; glPushAttrib(GL_ALL_ATTRIB_BITS & (~GL_POINT_BIT)); m_renderer->SetPerspectiveProjection(m_fovAng, m_width/m_height, m_zNear, m_zFar); m_renderer->SetTransform(matrix4x4f::Identity()); m_renderer->ClearScreen(); matrix4x4d trans2bg; Frame::GetFrameRenderTransform(Pi::game->GetSpace()->GetRootFrame(), m_camFrame, trans2bg); trans2bg.ClearToRotOnly(); // Pick up to four suitable system light sources (stars) m_lightSources.clear(); m_lightSources.reserve(4); position_system_lights(m_camFrame, Pi::game->GetSpace()->GetRootFrame(), m_lightSources); if (m_lightSources.empty()) { // no lights means we're somewhere weird (eg hyperspace). fake one const Color col(1.f); m_lightSources.push_back(LightSource(0, Graphics::Light(Graphics::Light::LIGHT_DIRECTIONAL, vector3f(0.f), col, col))); } //fade space background based on atmosphere thickness and light angle float bgIntensity = 1.f; if (m_camFrame->GetParent() && m_camFrame->GetParent()->IsRotFrame()) { //check if camera is near a planet Body *camParentBody = m_camFrame->GetParent()->GetBody(); if (camParentBody && camParentBody->IsType(Object::PLANET)) { Planet *planet = static_cast<Planet*>(camParentBody); const vector3f relpos(planet->GetInterpPositionRelTo(m_camFrame)); double altitude(relpos.Length()); double pressure, density; planet->GetAtmosphericState(altitude, &pressure, &density); if (pressure >= 0.001) { //go through all lights to calculate something resembling light intensity float angle = 0.f; for(std::vector<LightSource>::const_iterator it = m_lightSources.begin(); it != m_lightSources.end(); ++it) { const vector3f lightDir(it->GetLight().GetPosition().Normalized()); angle += std::max(0.f, lightDir.Dot(-relpos.Normalized())) * it->GetLight().GetDiffuse().GetLuminance(); } //calculate background intensity with some hand-tweaked fuzz applied bgIntensity = Clamp(1.f - std::min(1.f, powf(density, 0.25f)) * (0.3f + powf(angle, 0.25f)), 0.f, 1.f); } } } Pi::game->GetSpace()->GetBackground().SetIntensity(bgIntensity); Pi::game->GetSpace()->GetBackground().Draw(renderer, trans2bg); { std::vector<Graphics::Light> rendererLights; for (size_t i = 0; i < m_lightSources.size(); i++) rendererLights.push_back(m_lightSources[i].GetLight()); renderer->SetLights(rendererLights.size(), &rendererLights[0]); } for (std::list<BodyAttrs>::iterator i = m_sortedBodies.begin(); i != m_sortedBodies.end(); ++i) { BodyAttrs *attrs = &(*i); // explicitly exclude a single body if specified (eg player) if (attrs->body == excludeBody) continue; double rad = attrs->body->GetClipRadius(); if (!m_frustum.TestPointInfinite((*i).viewCoords, rad)) continue; // draw spikes for far objects double screenrad = 500 * rad / attrs->camDist; // approximate pixel size if (attrs->body->IsType(Object::PLANET) && screenrad < 2) { // absolute bullshit double spikerad = (7 + 1.5*log10(screenrad)) * rad / screenrad; DrawSpike(spikerad, attrs->viewCoords, attrs->viewTransform); } else if (screenrad >= 2 || attrs->body->IsType(Object::STAR) || (attrs->body->IsType(Object::PROJECTILE) && screenrad > 0.25)) attrs->body->Render(renderer, this, attrs->viewCoords, attrs->viewTransform); } Sfx::RenderAll(renderer, Pi::game->GetSpace()->GetRootFrame(), m_camFrame); m_frame->RemoveChild(m_camFrame); delete m_camFrame; m_camFrame = 0; glPopAttrib(); }
void Ship::StaticUpdate(const float timeStep) { // do player sounds before dead check, so they also turn off if (IsType(Object::PLAYER)) DoThrusterSounds(); if (IsDead()) return; if (m_controller) m_controller->StaticUpdate(timeStep); if (GetHullTemperature() > 1.0) Explode(); UpdateAlertState(); /* FUEL SCOOPING!!!!!!!!! */ int capacity = 0; Properties().Get("fuel_scoop_cap", capacity); if (m_flightState == FLYING && capacity > 0) { Body *astro = GetFrame()->GetBody(); if (astro && astro->IsType(Object::PLANET)) { Planet *p = static_cast<Planet*>(astro); if (p->GetSystemBody()->IsScoopable()) { double dist = GetPosition().Length(); double pressure, density; p->GetAtmosphericState(dist, &pressure, &density); double speed = GetVelocity().Length(); vector3d vdir = GetVelocity().Normalized(); vector3d pdir = -GetOrient().VectorZ(); double dot = vdir.Dot(pdir); if ((m_stats.free_capacity) && (dot > 0.95) && (speed > 2000.0) && (density > 1.0)) { double rate = speed*density*0.00000333f*double(capacity); if (Pi::rng.Double() < rate) { lua_State *l = Lua::manager->GetLuaState(); pi_lua_import(l, "Equipment"); LuaTable hydrogen = LuaTable(l, -1).Sub("cargo").Sub("hydrogen"); LuaObject<Ship>::CallMethod(this, "AddEquip", hydrogen); UpdateEquipStats(); if (this->IsType(Object::PLAYER)) { Pi::game->log->Add(stringf(Lang::FUEL_SCOOP_ACTIVE_N_TONNES_H_COLLECTED, formatarg("quantity", LuaObject<Ship>::CallMethod<int>(this, "CountEquip", hydrogen)))); } lua_pop(l, 3); } } } } } // Cargo bay life support capacity = 0; Properties().Get("cargo_life_support_cap", capacity); if (!capacity) { // Hull is pressure-sealed, it just doesn't provide // temperature regulation and breathable atmosphere // kill stuff roughly every 5 seconds if ((!m_dockedWith) && (5.0*Pi::rng.Double() < timeStep)) { std::string t(Pi::rng.Int32(2) ? "live_animals" : "slaves"); lua_State *l = Lua::manager->GetLuaState(); pi_lua_import(l, "Equipment"); LuaTable cargo = LuaTable(l, -1).Sub("cargo"); if (LuaObject<Ship>::CallMethod<int>(this, "RemoveEquip", cargo.Sub(t))) { LuaObject<Ship>::CallMethod<int>(this, "AddEquip", cargo.Sub("fertilizer")); if (this->IsType(Object::PLAYER)) { Pi::game->log->Add(Lang::CARGO_BAY_LIFE_SUPPORT_LOST); } lua_pop(l, 4); } else lua_pop(l, 3); } } if (m_flightState == FLYING) m_launchLockTimeout -= timeStep; if (m_launchLockTimeout < 0) m_launchLockTimeout = 0; if (m_flightState == JUMPING || m_flightState == HYPERSPACE) m_launchLockTimeout = 0; // lasers for (int i=0; i<ShipType::GUNMOUNT_MAX; i++) { m_gun[i].recharge -= timeStep; float rateCooling = 0.01f; float cooler = 1.0f; Properties().Get("laser_cooler_cap", cooler); rateCooling *= cooler; m_gun[i].temperature -= rateCooling*timeStep; if (m_gun[i].temperature < 0.0f) m_gun[i].temperature = 0; if (m_gun[i].recharge < 0.0f) m_gun[i].recharge = 0; if (!m_gun[i].state) continue; if (m_gun[i].recharge > 0.0f) continue; if (m_gun[i].temperature > 1.0) continue; FireWeapon(i); } if (m_ecmRecharge > 0.0f) { m_ecmRecharge = std::max(0.0f, m_ecmRecharge - timeStep); } if (m_shieldCooldown > 0.0f) { m_shieldCooldown = std::max(0.0f, m_shieldCooldown - timeStep); } if (m_stats.shield_mass_left < m_stats.shield_mass) { // 250 second recharge float recharge_rate = 0.004f; float booster = 1.0f; Properties().Get("shield_energy_booster_cap", booster); recharge_rate *= booster; m_stats.shield_mass_left = Clamp(m_stats.shield_mass_left + m_stats.shield_mass * recharge_rate * timeStep, 0.0f, m_stats.shield_mass); Properties().Set("shieldMassLeft", m_stats.shield_mass_left); } if (m_wheelTransition) { m_wheelState += m_wheelTransition*0.3f*timeStep; m_wheelState = Clamp(m_wheelState, 0.0f, 1.0f); if (is_equal_exact(m_wheelState, 0.0f) || is_equal_exact(m_wheelState, 1.0f)) m_wheelTransition = 0; } if (m_testLanded) TestLanded(); capacity = 0; Properties().Get("hull_autorepair_cap", capacity); if (capacity) { m_stats.hull_mass_left = std::min(m_stats.hull_mass_left + 0.1f*timeStep, float(m_type->hullMass)); Properties().Set("hullMassLeft", m_stats.hull_mass_left); Properties().Set("hullPercent", 100.0f * (m_stats.hull_mass_left / float(m_type->hullMass))); } // After calling StartHyperspaceTo this Ship must not spawn objects // holding references to it (eg missiles), as StartHyperspaceTo // removes the ship from Space::bodies and so the missile will not // have references to this cleared by NotifyRemoved() if (m_hyperspace.now) { m_hyperspace.now = false; EnterHyperspace(); } if (m_hyperspace.countdown > 0.0f) { // Check the Lua function bool abort = false; lua_State * l = m_hyperspace.checks.GetLua(); if (l) { m_hyperspace.checks.PushCopyToStack(); if (lua_isfunction(l, -1)) { lua_call(l, 0, 1); abort = !lua_toboolean(l, -1); lua_pop(l, 1); } } if (abort) { AbortHyperjump(); } else { m_hyperspace.countdown = m_hyperspace.countdown - timeStep; if (!abort && m_hyperspace.countdown <= 0.0f) { m_hyperspace.countdown = 0; m_hyperspace.now = true; SetFlightState(JUMPING); // We have to fire it here, because the event isn't actually fired until // after the whole physics update, which means the flight state on next // step would be HYPERSPACE, thus breaking quite a few things. LuaEvent::Queue("onLeaveSystem", this); } } } //Add smoke trails for missiles on thruster state static double s_timeAccum = 0.0; s_timeAccum += timeStep; if (m_type->tag == ShipType::TAG_MISSILE && !is_equal_exact(m_thrusters.LengthSqr(), 0.0) && (s_timeAccum > 4 || 0.1*Pi::rng.Double() < timeStep)) { s_timeAccum = 0.0; const vector3d pos = GetOrient() * vector3d(0, 0 , 5); const float speed = std::min(10.0*GetVelocity().Length()*std::max(1.0,fabs(m_thrusters.z)),100.0); SfxManager::AddThrustSmoke(this, speed, pos); } }
void Ship::StaticUpdate(const float timeStep) { AITimeStep(timeStep); // moved to correct place, maybe if (GetHullTemperature() > 1.0) { Space::KillBody(this); } UpdateAlertState(); /* FUEL SCOOPING!!!!!!!!! */ if (m_equipment.Get(Equip::SLOT_FUELSCOOP) != Equip::NONE) { Body *astro = GetFrame()->m_astroBody; if (astro && astro->IsType(Object::PLANET)) { Planet *p = static_cast<Planet*>(astro); if (p->IsSuperType(SBody::SUPERTYPE_GAS_GIANT)) { double dist = GetPosition().Length(); double pressure, density; p->GetAtmosphericState(dist, &pressure, &density); double speed = GetVelocity().Length(); vector3d vdir = GetVelocity().Normalized(); matrix4x4d rot; GetRotMatrix(rot); vector3d pdir = -vector3d(rot[8], rot[9], rot[10]).Normalized(); double dot = vdir.Dot(pdir); if ((m_stats.free_capacity) && (dot > 0.95) && (speed > 2000.0) && (density > 1.0)) { double rate = speed*density*0.00001f; if (Pi::rng.Double() < rate) { m_equipment.Add(Equip::HYDROGEN); if (this == reinterpret_cast<Ship*>(Pi::player)) { Pi::Message(stringf(Lang::FUEL_SCOOP_ACTIVE_N_TONNES_H_COLLECTED, formatarg("quantity", m_equipment.Count(Equip::SLOT_CARGO, Equip::HYDROGEN)))); } UpdateMass(); } } } } } // Cargo bay life support if (m_equipment.Get(Equip::SLOT_CARGOLIFESUPPORT) != Equip::CARGO_LIFE_SUPPORT) { // Hull is pressure-sealed, it just doesn't provide // temperature regulation and breathable atmosphere // kill stuff roughly every 5 seconds if ((!m_dockedWith) && (5.0*Pi::rng.Double() < timeStep)) { Equip::Type t = (Pi::rng.Int32(2) ? Equip::LIVE_ANIMALS : Equip::SLAVES); if (m_equipment.Remove(t, 1)) { m_equipment.Add(Equip::FERTILIZER); if (this == reinterpret_cast<Ship*>(Pi::player)) { Pi::Message(Lang::CARGO_BAY_LIFE_SUPPORT_LOST); } } } } if (m_flightState == FLYING) m_launchLockTimeout -= timeStep; if (m_launchLockTimeout < 0) m_launchLockTimeout = 0; /* can't orient ships in SetDockedWith() because it gets * called from collision handler, and collision system gets a bit * weirded out if bodies are moved in the middle of collision detection */ if (m_dockedWith) m_dockedWith->OrientDockedShip(this, m_dockedWithPort); // lasers for (int i=0; i<ShipType::GUNMOUNT_MAX; i++) { m_gunRecharge[i] -= timeStep; float rateCooling = 0.01f; if (m_equipment.Get(Equip::SLOT_LASERCOOLER) != Equip::NONE) { rateCooling *= float(EquipType::types[ m_equipment.Get(Equip::SLOT_LASERCOOLER) ].pval); } m_gunTemperature[i] -= rateCooling*timeStep; if (m_gunTemperature[i] < 0.0f) m_gunTemperature[i] = 0; if (m_gunRecharge[i] < 0.0f) m_gunRecharge[i] = 0; if (!m_gunState[i]) continue; if (m_gunRecharge[i] > 0.0f) continue; if (m_gunTemperature[i] > 1.0) continue; FireWeapon(i); } if (m_ecmRecharge > 0.0f) { m_ecmRecharge = std::max(0.0f, m_ecmRecharge - timeStep); } if (m_stats.shield_mass_left < m_stats.shield_mass) { // 250 second recharge float recharge_rate = 0.004f; if (m_equipment.Get(Equip::SLOT_ENERGYBOOSTER) != Equip::NONE) { recharge_rate *= float(EquipType::types[ m_equipment.Get(Equip::SLOT_ENERGYBOOSTER) ].pval); } m_stats.shield_mass_left += m_stats.shield_mass * recharge_rate * timeStep; } m_stats.shield_mass_left = Clamp(m_stats.shield_mass_left, 0.0f, m_stats.shield_mass); if (m_wheelTransition) { m_wheelState += m_wheelTransition*0.3f*timeStep; m_wheelState = Clamp(m_wheelState, 0.0f, 1.0f); if (float_equal_exact(m_wheelState, 0.0f) || float_equal_exact(m_wheelState, 1.0f)) m_wheelTransition = 0; } if (m_testLanded) TestLanded(); if (m_equipment.Get(Equip::SLOT_HULLAUTOREPAIR) == Equip::HULL_AUTOREPAIR) { const ShipType &stype = GetShipType(); m_stats.hull_mass_left = std::min(m_stats.hull_mass_left + 0.1f*timeStep, float(stype.hullMass)); } // After calling StartHyperspaceTo this Ship must not spawn objects // holding references to it (eg missiles), as StartHyperspaceTo // removes the ship from Space::bodies and so the missile will not // have references to this cleared by NotifyDeleted() if (m_hyperspace.countdown > 0.0f) { m_hyperspace.countdown = m_hyperspace.countdown - timeStep; if (m_hyperspace.countdown <= 0.0f) { m_hyperspace.countdown = 0; m_hyperspace.now = true; } } if (m_hyperspace.now) { m_hyperspace.now = false; Space::StartHyperspaceTo(this, &m_hyperspace.dest); } }
void Ship::StaticUpdate(const float timeStep) { // do player sounds before dead check, so they also turn off if (IsType(Object::PLAYER)) DoThrusterSounds(); if (IsDead()) return; if (m_controller) m_controller->StaticUpdate(timeStep); if (GetHullTemperature() > 1.0) Explode(); UpdateAlertState(); /* FUEL SCOOPING!!!!!!!!! */ if ((m_flightState == FLYING) && (m_equipment.Get(Equip::SLOT_FUELSCOOP) != Equip::NONE)) { Body *astro = GetFrame()->GetBody(); if (astro && astro->IsType(Object::PLANET)) { Planet *p = static_cast<Planet*>(astro); if (p->GetSystemBody()->IsScoopable()) { double dist = GetPosition().Length(); double pressure, density; p->GetAtmosphericState(dist, &pressure, &density); double speed = GetVelocity().Length(); vector3d vdir = GetVelocity().Normalized(); vector3d pdir = -GetOrient().VectorZ(); double dot = vdir.Dot(pdir); if ((m_stats.free_capacity) && (dot > 0.95) && (speed > 2000.0) && (density > 1.0)) { double rate = speed*density*0.00001f; if (Pi::rng.Double() < rate) { m_equipment.Add(Equip::HYDROGEN); UpdateEquipStats(); if (this->IsType(Object::PLAYER)) { Pi::Message(stringf(Lang::FUEL_SCOOP_ACTIVE_N_TONNES_H_COLLECTED, formatarg("quantity", m_equipment.Count(Equip::SLOT_CARGO, Equip::HYDROGEN)))); } } } } } } // Cargo bay life support if (m_equipment.Get(Equip::SLOT_CARGOLIFESUPPORT) != Equip::CARGO_LIFE_SUPPORT) { // Hull is pressure-sealed, it just doesn't provide // temperature regulation and breathable atmosphere // kill stuff roughly every 5 seconds if ((!m_dockedWith) && (5.0*Pi::rng.Double() < timeStep)) { Equip::Type t = (Pi::rng.Int32(2) ? Equip::LIVE_ANIMALS : Equip::SLAVES); if (m_equipment.Remove(t, 1)) { m_equipment.Add(Equip::FERTILIZER); if (this->IsType(Object::PLAYER)) { Pi::Message(Lang::CARGO_BAY_LIFE_SUPPORT_LOST); } } } } if (m_flightState == FLYING) m_launchLockTimeout -= timeStep; if (m_launchLockTimeout < 0) m_launchLockTimeout = 0; // lasers for (int i=0; i<ShipType::GUNMOUNT_MAX; i++) { m_gunRecharge[i] -= timeStep; float rateCooling = 0.01f; if (m_equipment.Get(Equip::SLOT_LASERCOOLER) != Equip::NONE) { rateCooling *= float(Equip::types[ m_equipment.Get(Equip::SLOT_LASERCOOLER) ].pval); } m_gunTemperature[i] -= rateCooling*timeStep; if (m_gunTemperature[i] < 0.0f) m_gunTemperature[i] = 0; if (m_gunRecharge[i] < 0.0f) m_gunRecharge[i] = 0; if (!m_gunState[i]) continue; if (m_gunRecharge[i] > 0.0f) continue; if (m_gunTemperature[i] > 1.0) continue; FireWeapon(i); } if (m_ecmRecharge > 0.0f) { m_ecmRecharge = std::max(0.0f, m_ecmRecharge - timeStep); } if (m_stats.shield_mass_left < m_stats.shield_mass) { // 250 second recharge float recharge_rate = 0.004f; if (m_equipment.Get(Equip::SLOT_ENERGYBOOSTER) != Equip::NONE) { recharge_rate *= float(Equip::types[ m_equipment.Get(Equip::SLOT_ENERGYBOOSTER) ].pval); } m_stats.shield_mass_left += m_stats.shield_mass * recharge_rate * timeStep; } m_stats.shield_mass_left = Clamp(m_stats.shield_mass_left, 0.0f, m_stats.shield_mass); if (m_wheelTransition) { m_wheelState += m_wheelTransition*0.3f*timeStep; m_wheelState = Clamp(m_wheelState, 0.0f, 1.0f); if (is_equal_exact(m_wheelState, 0.0f) || is_equal_exact(m_wheelState, 1.0f)) m_wheelTransition = 0; } if (m_testLanded) TestLanded(); if (m_equipment.Get(Equip::SLOT_HULLAUTOREPAIR) == Equip::HULL_AUTOREPAIR) { const ShipType &stype = GetShipType(); m_stats.hull_mass_left = std::min(m_stats.hull_mass_left + 0.1f*timeStep, float(stype.hullMass)); } // After calling StartHyperspaceTo this Ship must not spawn objects // holding references to it (eg missiles), as StartHyperspaceTo // removes the ship from Space::bodies and so the missile will not // have references to this cleared by NotifyRemoved() if (m_hyperspace.countdown > 0.0f) { m_hyperspace.countdown = m_hyperspace.countdown - timeStep; if (m_hyperspace.countdown <= 0.0f) { m_hyperspace.countdown = 0; m_hyperspace.now = true; } } if (m_hyperspace.now) { m_hyperspace.now = false; EnterHyperspace(); } }