void Ship::EnterHyperspace() { assert(GetFlightState() != Ship::HYPERSPACE); const SystemPath dest = GetHyperspaceDest(); int fuel_cost; Ship::HyperjumpStatus status = CheckHyperspaceTo(dest, fuel_cost, m_hyperspace.duration); if (status != HYPERJUMP_OK) { // XXX something has changed (fuel loss, mass change, whatever). // could report it to the player but better would be to cancel the // countdown before this is reached. either way do something return; } Equip::Type fuelType = GetHyperdriveFuelType(); m_equipment.Remove(fuelType, fuel_cost); if (fuelType == Equip::MILITARY_FUEL) { m_equipment.Add(Equip::RADIOACTIVES, fuel_cost); } UpdateEquipStats(); LuaEvent::Queue("onLeaveSystem", this); SetFlightState(Ship::HYPERSPACE); // virtual call, do class-specific things OnEnterHyperspace(); }
bool Ship::FireMissile(int idx, Ship *target) { assert(target); if (GetFlightState() != FLYING) return false; const Equip::Type t = m_equipment.Get(Equip::SLOT_MISSILE, idx); if (t == Equip::NONE) { return false; } m_equipment.Set(Equip::SLOT_MISSILE, idx, Equip::NONE); UpdateEquipStats(); ShipType::Id mtype; switch (t) { case Equip::MISSILE_SMART: mtype = ShipType::MISSILE_SMART; break; case Equip::MISSILE_NAVAL: mtype = ShipType::MISSILE_NAVAL; break; case Equip::MISSILE_UNGUIDED: mtype = ShipType::MISSILE_UNGUIDED; break; default: case Equip::MISSILE_GUIDED: mtype = ShipType::MISSILE_GUIDED; break; } Missile *missile = new Missile(mtype, this, target); missile->SetOrient(GetOrient()); missile->SetFrame(GetFrame()); // XXX DODGY! need to put it in a sensible location vector3d dir = -GetOrient().VectorZ(); missile->SetPosition(GetPosition()+50.0*dir); missile->SetVelocity(GetVelocity()); Pi::game->GetSpace()->AddBody(missile); return true; }
bool Ship::OnCollision(Object *b, Uint32 flags, double relVel) { // hitting space station docking surfaces shouldn't do damage if (b->IsType(Object::SPACESTATION) && (flags & 0x10)) { return true; } // hitting cargo scoop surface shouldn't do damage int cargoscoop_cap = 0; Properties().Get("cargo_scoop_cap", cargoscoop_cap); if (cargoscoop_cap > 0 && b->IsType(Object::CARGOBODY) && !dynamic_cast<Body*>(b)->IsDead()) { LuaRef item = dynamic_cast<CargoBody*>(b)->GetCargoType(); if (LuaObject<Ship>::CallMethod<int>(this, "AddEquip", item) > 0) { // try to add it to the ship cargo. Pi::game->GetSpace()->KillBody(dynamic_cast<Body*>(b)); if (this->IsType(Object::PLAYER)) Pi::game->log->Add(stringf(Lang::CARGO_SCOOP_ACTIVE_1_TONNE_X_COLLECTED, formatarg("item", ScopedTable(item).CallMethod<std::string>("GetName")))); // XXX SfxManager::Add(this, TYPE_SCOOP); UpdateEquipStats(); return true; } if (this->IsType(Object::PLAYER)) Pi::game->log->Add(Lang::CARGO_SCOOP_ATTEMPTED); } if (b->IsType(Object::PLANET)) { // geoms still enabled when landed if (m_flightState != FLYING) return false; else { if (GetVelocity().Length() < MAX_LANDING_SPEED) { m_testLanded = true; return true; } } } if ( b->IsType(Object::CITYONPLANET) || b->IsType(Object::SHIP) || b->IsType(Object::PLAYER) || b->IsType(Object::SPACESTATION) || b->IsType(Object::PLANET) || b->IsType(Object::STAR) || b->IsType(Object::CARGOBODY)) { LuaEvent::Queue("onShipCollided", this, b->IsType(Object::CITYONPLANET) ? dynamic_cast<CityOnPlanet*>(b)->GetPlanet() : dynamic_cast<Body*>(b)); } return DynamicBody::OnCollision(b, flags, relVel); }
bool Ship::OnCollision(Object *b, Uint32 flags, double relVel) { // hitting space station docking surfaces shouldn't do damage if (b->IsType(Object::SPACESTATION) && (flags & 0x10)) { return true; } // hitting cargo scoop surface shouldn't do damage if ((m_equipment.Get(Equip::SLOT_CARGOSCOOP) != Equip::NONE) && b->IsType(Object::CARGOBODY) && m_stats.free_capacity) { Equip::Type item = dynamic_cast<CargoBody*>(b)->GetCargoType(); Pi::game->GetSpace()->KillBody(dynamic_cast<Body*>(b)); m_equipment.Add(item); UpdateEquipStats(); if (this->IsType(Object::PLAYER)) Pi::Message(stringf(Lang::CARGO_SCOOP_ACTIVE_1_TONNE_X_COLLECTED, formatarg("item", Equip::types[item].name))); // XXX Sfx::Add(this, Sfx::TYPE_SCOOP); return true; } if (b->IsType(Object::PLANET)) { // geoms still enabled when landed if (m_flightState != FLYING) return false; else { if (GetVelocity().Length() < MAX_LANDING_SPEED) { m_testLanded = true; return true; } } } if ( b->IsType(Object::CITYONPLANET) || b->IsType(Object::SHIP) || b->IsType(Object::PLAYER) || b->IsType(Object::SPACESTATION) || b->IsType(Object::PLANET) || b->IsType(Object::STAR) || b->IsType(Object::CARGOBODY)) { LuaEvent::Queue("onShipCollided", this, b->IsType(Object::CITYONPLANET) ? dynamic_cast<CityOnPlanet*>(b)->GetPlanet() : dynamic_cast<Body*>(b)); } return DynamicBody::OnCollision(b, flags, relVel); }
void Ship::UpdateLuaStats() { // This code cannot be in UpdateEquipStats itself because *Equip* needs to be // called in Init(), which is itself called in the constructor, but we absolutely // cannot use LuaObject<Ship>::* in a constructor, or else we'd fix the type of the // object to Ship forever, even though it could very well be a Player. UpdateEquipStats(); PropertyMap& p = Properties(); m_stats.hyperspace_range = m_stats.hyperspace_range_max = 0; int hyperclass = 0; p.Get<int>("hyperclass_cap", hyperclass); if (hyperclass) { auto ranges = LuaObject<Ship>::CallMethod<double, double>(this, "GetHyperspaceRange"); m_stats.hyperspace_range_max = std::get<1>(ranges); m_stats.hyperspace_range = std::get<0>(ranges); } p.Set("hyperspaceRange", m_stats.hyperspace_range); p.Set("maxHyperspaceRange", m_stats.hyperspace_range_max); }
void Ship::InitEquipSet() { lua_State * l = Lua::manager->GetLuaState(); PropertyMap & p = Properties(); LUA_DEBUG_START(l); pi_lua_import(l, "EquipSet"); LuaTable es_class(l, -1); LuaTable slots = LuaTable(l).LoadMap(GetShipType()->slots.begin(), GetShipType()->slots.end()); m_equipSet = es_class.Call<LuaRef>("New", slots); p.Set("equipSet", ScopedTable(m_equipSet)); UpdateEquipStats(); { ScopedTable es(m_equipSet); int usedCargo = es.CallMethod<int>("OccupiedSpace", "cargo"); int totalCargo = std::min(m_stats.free_capacity + usedCargo, es.CallMethod<int>("SlotSize", "cargo")); p.Set("usedCargo", usedCargo); p.Set("totalCargo", totalCargo); } lua_pop(l, 2); LUA_DEBUG_END(l, 0); }
void Ship::Init() { m_invulnerable = false; m_sensors.reset(new Sensors(this)); m_navLights.reset(new NavLights(GetModel())); m_navLights->SetEnabled(true); SetMassDistributionFromModel(); UpdateEquipStats(); m_stats.hull_mass_left = float(m_type->hullMass); m_stats.shield_mass_left = 0; PropertyMap &p = Properties(); p.Set("hullMassLeft", m_stats.hull_mass_left); p.Set("hullPercent", 100.0f * (m_stats.hull_mass_left / float(m_type->hullMass))); p.Set("shieldMassLeft", m_stats.shield_mass_left); p.Set("fuelMassLeft", m_stats.fuel_tank_mass_left); p.Set("shipName", m_shipName); m_hyperspace.now = false; // TODO: move this on next savegame change, maybe m_hyperspaceCloud = 0; m_landingGearAnimation = GetModel()->FindAnimation("gear_down"); InitGun("tag_gunmount_0", 0); InitGun("tag_gunmount_1", 1); // If we've got the tag_landing set then use it for an offset otherwise grab the AABB const SceneGraph::MatrixTransform *mt = GetModel()->FindTagByName("tag_landing"); if( mt ) { m_landingMinOffset = mt->GetTransform().GetTranslate().y; } else { m_landingMinOffset = GetAabb().min.y; } InitMaterials(); }
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 Player::Sold(Equip::Type t) { m_equipment.Remove(t, 1); UpdateEquipStats(); }
//XXX move to Player character .cpp void Player::Bought(Equip::Type t) { m_equipment.Add(t); UpdateEquipStats(); }
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(); } }
void Ship::UpdateStats() { UpdateEquipStats(); UpdateFuelStats(); }