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 Player::StaticUpdate(const float timeStep) { vector3d v; matrix4x4d m; Ship::StaticUpdate(timeStep); // also calls autopilot AI if (GetFlightState() == Ship::FLYING) { switch (m_flightControlState) { case CONTROL_FIXSPEED: if (Pi::GetView() == Pi::worldView) PollControls(timeStep); if (IsAnyThrusterKeyDown()) break; GetRotMatrix(m); v = m * vector3d(0, 0, -m_setSpeed); if (m_setSpeedTarget) { v += m_setSpeedTarget->GetVelocityRelTo(GetFrame()); } AIMatchVel(v); break; case CONTROL_MANUAL: if (Pi::GetView() == Pi::worldView) PollControls(timeStep); break; case CONTROL_AUTOPILOT: if (AIIsActive()) break; Pi::game->RequestTimeAccel(Game::TIMEACCEL_1X); // AIMatchVel(vector3d(0.0)); // just in case autopilot doesn't... // actually this breaks last timestep slightly in non-relative target cases AIMatchAngVelObjSpace(vector3d(0.0)); if (GetFrame()->IsRotatingFrame()) SetFlightControlState(CONTROL_FIXSPEED); else SetFlightControlState(CONTROL_MANUAL); m_setSpeed = 0.0; break; } } else SetFlightControlState(CONTROL_MANUAL); /* This wank probably shouldn't be in Player... */ /* Ship engine noise. less loud inside */ float v_env = (Pi::worldView->GetCamType() == WorldView::CAM_EXTERNAL ? 1.0f : 0.5f) * Sound::GetSfxVolume(); static Sound::Event sndev; float volBoth = 0.0f; volBoth += 0.5f*fabs(GetThrusterState().y); volBoth += 0.5f*fabs(GetThrusterState().z); float targetVol[2] = { volBoth, volBoth }; if (GetThrusterState().x > 0.0) targetVol[0] += 0.5f*float(GetThrusterState().x); else targetVol[1] += -0.5f*float(GetThrusterState().x); targetVol[0] = v_env * Clamp(targetVol[0], 0.0f, 1.0f); targetVol[1] = v_env * Clamp(targetVol[1], 0.0f, 1.0f); float dv_dt[2] = { 4.0f, 4.0f }; if (!sndev.VolumeAnimate(targetVol, dv_dt)) { sndev.Play("Thruster_large", 0.0f, 0.0f, Sound::OP_REPEAT); sndev.VolumeAnimate(targetVol, dv_dt); } float angthrust = 0.1f * v_env * float(Pi::player->GetAngThrusterState().Length()); static Sound::Event angThrustSnd; if (!angThrustSnd.VolumeAnimate(angthrust, angthrust, 5.0f, 5.0f)) { angThrustSnd.Play("Thruster_Small", 0.0f, 0.0f, Sound::OP_REPEAT); angThrustSnd.VolumeAnimate(angthrust, angthrust, 5.0f, 5.0f); } }
void Player::PollControls(const float timeStep) { static bool stickySpeedKey = false; if (Pi::game->GetTimeAccel() == Game::TIMEACCEL_PAUSED || Pi::player->IsDead() || GetFlightState() != FLYING) return; // if flying { ClearThrusterState(); SetGunState(0,0); SetGunState(1,0); vector3d wantAngVel(0.0); double angThrustSoftness = 50.0; // have to use this function. SDL mouse position event is bugged in windows int mouseMotion[2]; SDL_GetRelativeMouseState (mouseMotion+0, mouseMotion+1); // call to flush if (Pi::MouseButtonState(SDL_BUTTON_RIGHT)) { matrix4x4d rot; GetRotMatrix(rot); if (!m_mouseActive) { m_mouseDir = vector3d(-rot[8],-rot[9],-rot[10]); // in world space m_mouseX = m_mouseY = 0; m_mouseActive = true; } vector3d objDir = m_mouseDir * rot; const double radiansPerPixel = 0.002; m_mouseX += mouseMotion[0] * radiansPerPixel; double modx = clipmouse(objDir.x, m_mouseX); m_mouseX -= modx; const bool invertY = (Pi::IsMouseYInvert() ? !m_invertMouse : m_invertMouse); m_mouseY += mouseMotion[1] * radiansPerPixel * (invertY ? -1 : 1); double mody = clipmouse(objDir.y, m_mouseY); m_mouseY -= mody; if(!float_is_zero_general(modx) || !float_is_zero_general(mody)) { matrix4x4d mrot = matrix4x4d::RotateYMatrix(modx); mrot.RotateX(mody); m_mouseDir = (rot * (mrot * objDir)).Normalized(); } } else m_mouseActive = false; // disable all keyboard controls while the console is active if (!Pi::IsConsoleActive()) { if (m_flightControlState == CONTROL_FIXSPEED) { double oldSpeed = m_setSpeed; if (stickySpeedKey) { if (!(KeyBindings::increaseSpeed.IsActive() || KeyBindings::decreaseSpeed.IsActive())) { stickySpeedKey = false; } } if (!stickySpeedKey) { if (KeyBindings::increaseSpeed.IsActive()) m_setSpeed += std::max(fabs(m_setSpeed)*0.05, 1.0); if (KeyBindings::decreaseSpeed.IsActive()) m_setSpeed -= std::max(fabs(m_setSpeed)*0.05, 1.0); if ( ((oldSpeed < 0.0) && (m_setSpeed >= 0.0)) || ((oldSpeed > 0.0) && (m_setSpeed <= 0.0)) ) { // flipped from going forward to backwards. make the speed 'stick' at zero // until the player lets go of the key and presses it again stickySpeedKey = true; m_setSpeed = 0; } } } if (KeyBindings::thrustForward.IsActive()) SetThrusterState(2, -1.0); if (KeyBindings::thrustBackwards.IsActive()) SetThrusterState(2, 1.0); if (KeyBindings::thrustUp.IsActive()) SetThrusterState(1, 1.0); if (KeyBindings::thrustDown.IsActive()) SetThrusterState(1, -1.0); if (KeyBindings::thrustLeft.IsActive()) SetThrusterState(0, -1.0); if (KeyBindings::thrustRight.IsActive()) SetThrusterState(0, 1.0); if (KeyBindings::fireLaser.IsActive() || (Pi::MouseButtonState(SDL_BUTTON_LEFT) && Pi::MouseButtonState(SDL_BUTTON_RIGHT))) { SetGunState(Pi::worldView->GetActiveWeapon(), 1); } if (KeyBindings::yawLeft.IsActive()) wantAngVel.y += 1.0; if (KeyBindings::yawRight.IsActive()) wantAngVel.y += -1.0; if (KeyBindings::pitchDown.IsActive()) wantAngVel.x += -1.0; if (KeyBindings::pitchUp.IsActive()) wantAngVel.x += 1.0; if (KeyBindings::rollLeft.IsActive()) wantAngVel.z += 1.0; if (KeyBindings::rollRight.IsActive()) wantAngVel.z -= 1.0; if (KeyBindings::fastRotate.IsActive()) angThrustSoftness = 10.0; } vector3d changeVec; changeVec.x = KeyBindings::pitchAxis.GetValue(); changeVec.y = KeyBindings::yawAxis.GetValue(); changeVec.z = KeyBindings::rollAxis.GetValue(); // Deadzone if(changeVec.LengthSqr() < m_joystickDeadzone) changeVec = vector3d(0.0); changeVec *= 2.0; wantAngVel += changeVec; double invTimeAccelRate = 1.0 / Pi::game->GetTimeAccelRate(); for (int axis=0; axis<3; axis++) wantAngVel[axis] = Clamp(wantAngVel[axis], -invTimeAccelRate, invTimeAccelRate); if (m_mouseActive) AIFaceDirection(m_mouseDir); else AIModelCoordsMatchAngVel(wantAngVel, angThrustSoftness); } }
void Player::PollControls(const float timeStep) { double time_accel = Pi::GetTimeAccel(); double invTimeAccel = 1.0 / time_accel; static bool stickySpeedKey = false; if ((time_accel == 0) || GetDockedWith() || Pi::player->IsDead() || (GetFlightState() != FLYING)) { return; } // if flying { ClearThrusterState(); vector3d wantAngVel(0.0); // have to use this function. SDL mouse position event is bugged in windows int mouseMotion[2]; SDL_GetRelativeMouseState (mouseMotion+0, mouseMotion+1); // call to flush if (Pi::MouseButtonState(3)) { matrix4x4d rot; GetRotMatrix(rot); if (!m_mouseActive) { m_mouseDir = vector3d(-rot[8],-rot[9],-rot[10]); // in world space m_mouseX = m_mouseY = 0; m_mouseActive = true; } vector3d objDir = m_mouseDir * rot; m_mouseX += mouseMotion[0] * 0.002; double modx = clipmouse(objDir.x, m_mouseX); m_mouseX -= modx; m_mouseY += mouseMotion[1] * 0.002; // factor pixels => radians double mody = clipmouse(objDir.y, m_mouseY); m_mouseY -= mody; if(modx != 0.0 || mody != 0.0) { matrix4x4d mrot = matrix4x4d::RotateYMatrix(modx); mrot.RotateX(mody); m_mouseDir = (rot * (mrot * objDir)).Normalized(); } } else m_mouseActive = false; if (m_flightControlState == CONTROL_FIXSPEED) { double oldSpeed = m_setSpeed; if (stickySpeedKey) { if (!(KeyBindings::increaseSpeed.IsActive() || KeyBindings::decreaseSpeed.IsActive())) { stickySpeedKey = false; } } if (!stickySpeedKey) { if (KeyBindings::increaseSpeed.IsActive()) m_setSpeed += std::max(m_setSpeed*0.05, 1.0); if (KeyBindings::decreaseSpeed.IsActive()) m_setSpeed -= std::max(m_setSpeed*0.05, 1.0); if ( ((oldSpeed < 0.0) && (m_setSpeed >= 0.0)) || ((oldSpeed > 0.0) && (m_setSpeed <= 0.0)) ) { // flipped from going forward to backwards. make the speed 'stick' at zero // until the player lets go of the key and presses it again stickySpeedKey = true; m_setSpeed = 0; } } } if (KeyBindings::thrustForward.IsActive()) SetThrusterState(2, -1.0); if (KeyBindings::thrustBackwards.IsActive()) SetThrusterState(2, 1.0); if (KeyBindings::thrustUp.IsActive()) SetThrusterState(1, 1.0); if (KeyBindings::thrustDown.IsActive()) SetThrusterState(1, -1.0); if (KeyBindings::thrustLeft.IsActive()) SetThrusterState(0, -1.0); if (KeyBindings::thrustRight.IsActive()) SetThrusterState(0, 1.0); SetGunState(0,0); SetGunState(1,0); if (KeyBindings::fireLaser.IsActive() || (Pi::MouseButtonState(1) && Pi::MouseButtonState(3))) { SetGunState(Pi::worldView->GetActiveWeapon(), 1); } if (KeyBindings::yawLeft.IsActive()) wantAngVel.y += 1.0; if (KeyBindings::yawRight.IsActive()) wantAngVel.y += -1.0; if (KeyBindings::pitchDown.IsActive()) wantAngVel.x += -1.0; if (KeyBindings::pitchUp.IsActive()) wantAngVel.x += 1.0; if (KeyBindings::rollLeft.IsActive()) wantAngVel.z += 1.0; if (KeyBindings::rollRight.IsActive()) wantAngVel.z -= 1.0; wantAngVel.x += 2 * KeyBindings::pitchAxis.GetValue(); wantAngVel.y += 2 * KeyBindings::yawAxis.GetValue(); wantAngVel.z += 2 * KeyBindings::rollAxis.GetValue(); for (int axis=0; axis<3; axis++) wantAngVel[axis] = Clamp(wantAngVel[axis], -invTimeAccel, invTimeAccel); // matrix4x4d rot; GetRotMatrix(rot); const double angThrustSoftness = KeyBindings::fastRotate.IsActive() ? 10.0 : 50.0; if (m_mouseActive) AIFaceDirection(m_mouseDir); else AIModelCoordsMatchAngVel(wantAngVel, angThrustSoftness); } }
void Ship::AIModelCoordsMatchSpeedRelTo(const vector3d v, const Ship *other) { matrix4x4d m; GetRotMatrix(m); vector3d relToVel = m.InverseOf() * other->GetVelocity() + v; AIAccelToModelRelativeVelocity(relToVel); }
// vel is desired velocity in ship's frame // returns true if this can be attained in a single timestep // todo: check space station rotating frame case bool Ship::AIMatchVel(const vector3d &vel) { matrix4x4d rot; GetRotMatrix(rot); vector3d diffvel = (vel - GetVelocityRelTo(GetFrame())) * rot; // convert to object space return AIChangeVelBy(diffvel); }
bool SpaceStation::OnCollision(Object *b, Uint32 flags, double relVel) { if ((flags & 0x10) && (b->IsType(Object::SHIP))) { Ship *s = static_cast<Ship*>(b); matrix4x4d rot; GetRotMatrix(rot); bool canDock = true; int port = -1; for (int i=0; i<MAX_DOCKING_PORTS; i++) { if (m_shipDocking[i].ship == s) { port = i; break; } } if (m_type->dockOneAtATimePlease) { for (int i=0; i<m_type->numDockingPorts; i++) { if (m_shipDocking[i].ship && m_shipDocking[i].stage != 1 && (m_shipDocking[i].stage != m_type->numDockingStages+1)) { canDock = false; break; } } } else { // for non-dockOneAtATimePlease, the ship is expected // to hit the right docking trigger surface for that port if (m_shipDocking[flags&0xf].ship != s) canDock = false; } if (port == -1) canDock = false; // hitting docking area of a station if (canDock) { SpaceStationType::positionOrient_t dport; // why stage 2? Because stage 1 is permission to dock // granted, stage 2 is start of docking animation. PiVerify(m_type->GetDockAnimPositionOrient(port, 2, 0.0f, vector3d(0.0), dport, s)); double speed = s->GetVelocity().Length(); // must be oriented sensibly and have wheels down if (IsGroundStation()) { matrix4x4d shiprot; s->GetRotMatrix(shiprot); matrix4x4d invShipRot = shiprot.InverseOf(); vector3d dockingNormal = rot*dport.yaxis; // check player is sortof sensibly oriented for landing const double dot = vector3d(invShipRot[1], invShipRot[5], invShipRot[9]).Dot(dockingNormal); if ((dot < 0.99) || (s->GetWheelState() < 1.0)) return false; } if ((speed < MAX_LANDING_SPEED) && (!s->GetDockedWith()) && (m_shipDocking[port].stage == 1)) { // if there is more docking port anim to do, // don't set docked yet if (m_type->numDockingStages >= 2) { shipDocking_t &sd = m_shipDocking[port]; sd.ship = s; sd.stage = 2; sd.stagePos = 0; sd.fromPos = rot.InverseOf() * (s->GetPosition() - GetPosition()); matrix4x4d temp; s->GetRotMatrix(temp); sd.fromRot = Quaterniond::FromMatrix4x4(temp); s->Disable(); s->ClearThrusterState(); s->SetFlightState(Ship::DOCKING); } else { s->SetDockedWith(this, port); CreateBB(); Pi::luaOnShipDocked->Queue(s, this); } } } return false; } else { return true; } }
void SpaceStation::DoDockingAnimation(const double timeStep) { matrix4x4d rot, wantRot; vector3d p1, p2, zaxis; for (int i=0; i<MAX_DOCKING_PORTS; i++) { shipDocking_t &dt = m_shipDocking[i]; if (!dt.ship) continue; if (!dt.stage) continue; // docked stage is m_type->numDockingPorts + 1 if (dt.stage > m_type->numDockingStages) continue; GetRotMatrix(rot); double stageDuration = (dt.stage > 0 ? m_type->dockAnimStageDuration[dt.stage-1] : m_type->undockAnimStageDuration[abs(dt.stage)-1]); dt.stagePos += timeStep / stageDuration; if (dt.stage == 1) { // SPECIAL stage! Docking granted but waiting for ship // to dock m_openAnimState[i] += 0.3*timeStep; m_dockAnimState[i] -= 0.3*timeStep; if (dt.stagePos >= 1.0) { if (dt.ship == static_cast<Ship*>(Pi::player)) Pi::onDockingClearanceExpired.emit(this); dt.ship = 0; dt.stage = 0; } continue; } if (dt.stagePos > 1.0) { dt.stagePos = 0; if (dt.stage >= 0) dt.stage++; else dt.stage--; dt.fromPos = rot.InverseOf() * (dt.ship->GetPosition() - GetPosition()); matrix4x4d temp; dt.ship->GetRotMatrix(temp); dt.fromRot = Quaterniond::FromMatrix4x4(temp); } SpaceStationType::positionOrient_t shipOrient; bool onRails = m_type->GetDockAnimPositionOrient(i, dt.stage, dt.stagePos, dt.fromPos, shipOrient, dt.ship); if (onRails) { dt.ship->SetPosition(GetPosition() + rot*shipOrient.pos); wantRot = matrix4x4d::MakeRotMatrix( shipOrient.xaxis, shipOrient.yaxis, shipOrient.xaxis.Cross(shipOrient.yaxis)) * rot; // use quaternion spherical linear interpolation to do // rotation smoothly Quaterniond wantQuat = Quaterniond::FromMatrix4x4(wantRot); Quaterniond q = Quaterniond::Nlerp(dt.fromRot, wantQuat, dt.stagePos); wantRot = q.ToMatrix4x4<double>(); // wantRot.Renormalize(); dt.ship->SetRotMatrix(wantRot); } else { if (dt.stage >= 0) { // set docked dt.ship->SetDockedWith(this, i); CreateBB(); Pi::luaOnShipDocked->Queue(dt.ship, this); } else { if (!dt.ship->IsEnabled()) { // launch ship dt.ship->Enable(); dt.ship->SetFlightState(Ship::FLYING); dt.ship->SetAngVelocity(GetFrame()->GetAngVelocity()); dt.ship->SetForce(vector3d(0,0,0)); dt.ship->SetTorque(vector3d(0,0,0)); if (m_type->dockMethod == SpaceStationType::SURFACE) { dt.ship->SetThrusterState(1, 1.0); // up } else { dt.ship->SetVelocity(GetFrame()->GetStasisVelocityAtPosition(dt.ship->GetPosition())); dt.ship->SetThrusterState(2, -1.0); // forward } Pi::luaOnShipUndocked->Queue(dt.ship, this); } } } if ((dt.stage < 0) && ((-dt.stage) > m_type->numUndockStages)) { dt.stage = 0; dt.ship = 0; } } for (int i=0; i<MAX_DOCKING_PORTS; i++) { m_openAnimState[i] = Clamp(m_openAnimState[i], 0.0, 1.0); m_dockAnimState[i] = Clamp(m_dockAnimState[i], 0.0, 1.0); } }