Example #1
0
void Player::InitCockpit()
{
	m_cockpit.release();
	if (!Pi::config->Int("EnableCockpit"))
		return;

	// XXX select a cockpit model. this is all quite skanky because we want a
	// fallback if the name is not found, which means having to actually try to
	// load the model. but ModelBody (on which ShipCockpit is currently based)
	// requires a model name, not a model object. it won't hurt much because it
	// all stays in the model cache anyway, its just awkward. the fix is to fix
	// ShipCockpit so its not a ModelBody and thus does its model work
	// directly, but we're not there yet
	std::string cockpitModelName;
	if (!GetShipType()->cockpitName.empty()) {
		if (Pi::FindModel(GetShipType()->cockpitName, false))
			cockpitModelName = GetShipType()->cockpitName;
	}
	if (cockpitModelName.empty()) {
		if (Pi::FindModel("default_cockpit", false))
			cockpitModelName = "default_cockpit";
	}
	if (!cockpitModelName.empty())
		m_cockpit.reset(new ShipCockpit(cockpitModelName));
}
Example #2
0
void Ship::UpdateFuel(const float timeStep, const vector3d &thrust)
{
	const double fuelUseRate = GetShipType()->GetFuelUseRate() * 0.01;
	double totalThrust = (fabs(thrust.x) + fabs(thrust.y) + fabs(thrust.z))
		/ -GetShipType()->linThrust[ShipType::THRUSTER_FORWARD];

	FuelState lastState = GetFuelState();
	SetFuel(GetFuel() - timeStep * (totalThrust * fuelUseRate));
	FuelState currentState = GetFuelState();

	UpdateFuelStats();

	if (currentState != lastState)
		LuaEvent::Queue("onShipFuelChanged", this, EnumStrings::GetString("ShipFuelStatus", currentState));
}
Example #3
0
double Ship::AIFaceOrient(const vector3d &dir, const vector3d &updir)
{
	double timeStep = Pi::GetTimeStep();
	matrix4x4d rot; GetRotMatrix(rot);
	double maxAccel = GetShipType().angThrust / GetAngularInertia();		// should probably be in stats anyway
	double frameAccel = maxAccel * timeStep;
	
	if (dir.Dot(vector3d(rot[8], rot[9], rot[10])) > -0.999999)
		{ AIFaceDirection(dir); return false; }
	
	vector3d uphead = (updir * rot).Normalized();		// create desired object-space updir
	vector3d dav(0.0, 0.0, 0.0);			// desired angular velocity
	double ang = 0.0;
	if (uphead.y < 0.999999)
	{
		ang = acos(Clamp(uphead.y, -1.0, 1.0));		// scalar angle from head to curhead
		double iangvel = sqrt(2.0 * maxAccel * ang);	// ideal angvel at current time

		double frameEndAV = iangvel - frameAccel;
		if (frameEndAV <= 0.0) iangvel = ang / timeStep;	// last frame discrete correction
		else iangvel = (iangvel + frameEndAV) * 0.5;		// discrete overshoot correction
		dav.z = -iangvel;
	}
	vector3d cav = (GetAngVelocity() - GetFrame()->GetAngVelocity()) * rot;				// current obj-rel angvel
//	vector3d cav = GetAngVelocity() * rot;				// current obj-rel angvel
	vector3d diff = (dav - cav) / frameAccel;			// find diff between current & desired angvel

	SetAngThrusterState(diff);
	return ang;
//	if (diff.x*diff.x > 1.0 || diff.y*diff.y > 1.0 || diff.z*diff.z > 1.0) return false;
//	else return true;
}
Example #4
0
void Ship::Init()
{
	// XXX the animation namespace must match that in LuaConstants
	// note: this must be set before generating the collision mesh
	// (which happens in SetModel()) and before rendering
	GetLmrObjParams().animationNamespace = "ShipAnimation";
	GetLmrObjParams().equipment = &m_equipment;

	const ShipType &stype = GetShipType();

	// Dirty hack: Always use gear-down mesh for collision detection
	// necessary because some ships have non-docking meshes too large for docking
	float temp = GetLmrObjParams().animValues[ANIM_WHEEL_STATE];
	GetLmrObjParams().animValues[ANIM_WHEEL_STATE] = 1.0f;
	SetModel(stype.lmrModelName.c_str());
	GetLmrObjParams().animValues[ANIM_WHEEL_STATE] = temp;

	SetMassDistributionFromModel();
	UpdateStats();
	m_stats.hull_mass_left = float(stype.hullMass);
	m_stats.shield_mass_left = 0;
	m_hyperspace.now = false;			// TODO: move this on next savegame change, maybe
	m_hyperspaceCloud = 0;

	m_landingGearAnimation = 0;
	SceneGraph::Model *nmodel = dynamic_cast<SceneGraph::Model*>(GetModel());
	if (nmodel) {
		m_landingGearAnimation = nmodel->FindAnimation("gear_down");
	}
}
Example #5
0
/* Assumed to be at model coords */
void Ship::RenderLaserfire()
{
	const ShipType &stype = GetShipType();
	glDisable(GL_LIGHTING);
	
	for (int i=0; i<ShipType::GUNMOUNT_MAX; i++) {
		if (!m_gunState[i]) continue;
		glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT);
		switch (m_equipment.Get(Equip::SLOT_LASER, i)) {
			case Equip::LASER_2MW_BEAM:
				glColor3f(1,.5,0); break;
			case Equip::LASER_4MW_BEAM:
				glColor3f(1,1,0); break;
			default:
			case Equip::LASER_1MW_BEAM:
				glColor3f(1,0,0); break;
		}
		glLineWidth(2.0f);
		glBegin(GL_LINES);
		vector3f pos = stype.gunMount[i].pos;
		glVertex3f(pos.x, pos.y, pos.z);
		glVertex3fv(&((10000)*stype.gunMount[i].dir)[0]);
		glEnd();
		glPopAttrib();
	}
	glEnable(GL_LIGHTING);
}
Example #6
0
double Ship::GetAccelMin() const
{
	const ShipType &stype = GetShipType();
	float val = stype.linThrust[ShipType::THRUSTER_UP];
	val = std::min(val, stype.linThrust[ShipType::THRUSTER_RIGHT]);
	val = std::min(val, -stype.linThrust[ShipType::THRUSTER_LEFT]);
	return val / GetMass();
}
Example #7
0
float Ship::GetWeakestThrustersForce() const
{
	const ShipType &type = GetShipType();
	float val = FLT_MAX;
	for (int i=0; i<ShipType::THRUSTER_MAX; i++) {
		val = std::min(val, fabsf(type.linThrust[i]));
	}
	return val;
}
Example #8
0
void Ship::Init()
{
	const ShipType &stype = GetShipType();
	SetModel(stype.lmrModelName.c_str());
	SetMassDistributionFromModel();
	UpdateMass();
	m_stats.hull_mass_left = float(stype.hullMass);
	m_stats.shield_mass_left = 0;
}
Example #9
0
// Input in object space
void Ship::AIMatchAngVelObjSpace(const vector3d &angvel)
{
	double maxAccel = GetShipType().angThrust / GetAngularInertia();
	double invFrameAccel = 1.0 / (maxAccel * Pi::GetTimeStep());

	matrix4x4d rot; GetRotMatrix(rot);
	vector3d diff = angvel - GetAngVelocity() * rot;		// find diff between current & desired angvel
	SetAngThrusterState(diff * invFrameAccel);
}
Example #10
0
void Ship::UpdateFuelStats()
{
	const ShipType &stype = GetShipType();

	m_stats.fuel_tank_mass = stype.fuelTankMass;
	m_stats.fuel_use = GetFuelUseRate();
	m_stats.fuel_tank_mass_left = m_stats.fuel_tank_mass * GetFuel();

	UpdateMass();
}
Example #11
0
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);
}
Example #12
0
vector3d Ship::GetMaxThrust(const vector3d &dir)
{
	const ShipType &stype = GetShipType();
	vector3d maxThrust;
	maxThrust.x = (dir.x > 0) ? stype.linThrust[ShipType::THRUSTER_RIGHT]
		: -stype.linThrust[ShipType::THRUSTER_LEFT];
	maxThrust.y = (dir.y > 0) ? stype.linThrust[ShipType::THRUSTER_UP]
		: -stype.linThrust[ShipType::THRUSTER_DOWN];
	maxThrust.z = (dir.z > 0) ? stype.linThrust[ShipType::THRUSTER_REVERSE]
		: -stype.linThrust[ShipType::THRUSTER_FORWARD];
	return maxThrust;
}
Example #13
0
bool Ship::OnDamage(Object *attacker, float kgDamage, const CollisionContact& contactData)
{
	if (m_invulnerable) {
		Sound::BodyMakeNoise(this, "Hull_hit_Small", 0.5f);
		return true;
	}

	if (!IsDead()) {
		float dam = kgDamage*0.001f;
		if (m_stats.shield_mass_left > 0.0f) {
			if (m_stats.shield_mass_left > dam) {
				m_stats.shield_mass_left -= dam;
				dam = 0;
			} else {
				dam -= m_stats.shield_mass_left;
				m_stats.shield_mass_left = 0;
			}
			Properties().Set("shieldMassLeft", m_stats.shield_mass_left);
		}

		m_shieldCooldown = DEFAULT_SHIELD_COOLDOWN_TIME;
		// transform the collision location into the models local space (from world space) and add it as a hit.
		matrix4x4d mtx = GetOrient();
		mtx.SetTranslate( GetPosition() );
		const matrix4x4d invmtx = mtx.Inverse();
		const vector3d localPos = invmtx * contactData.pos;
		GetShields()->AddHit(localPos);

		m_stats.hull_mass_left -= dam;
		Properties().Set("hullMassLeft", m_stats.hull_mass_left);
		Properties().Set("hullPercent", 100.0f * (m_stats.hull_mass_left / float(m_type->hullMass)));
		if (m_stats.hull_mass_left < 0) {
			if (attacker) {
				if (attacker->IsType(Object::BODY))
					LuaEvent::Queue("onShipDestroyed", this, dynamic_cast<Body*>(attacker));
			}

			Explode();
		} else {
			if (Pi::rng.Double() < kgDamage)
				SfxManager::Add(this, TYPE_DAMAGE);

			if (dam < 0.01 * float(GetShipType()->hullMass))
				Sound::BodyMakeNoise(this, "Hull_hit_Small", 1.0f);
			else
				Sound::BodyMakeNoise(this, "Hull_Hit_Medium", 1.0f);
		}
	}

	//Output("Ouch! %s took %.1f kilos of damage from %s! (%.1f t hull left)\n", GetLabel().c_str(), kgDamage, attacker->GetLabel().c_str(), m_stats.hull_mass_left);
	return true;
}
Example #14
0
void Ship::TimeStepUpdate(const float timeStep)
{
	// XXX why not just f*****g do this rather than track down the
	// reason that ship geoms are being collision tested during launch
	if (m_flightState == FLYING) Enable();
	else Disable();

	vector3d maxThrust = GetMaxThrust(m_thrusters);
	AddRelForce(vector3d(maxThrust.x*m_thrusters.x, maxThrust.y*m_thrusters.y,
		maxThrust.z*m_thrusters.z));
	AddRelTorque(GetShipType().angThrust * m_angThrusters);

	DynamicBody::TimeStepUpdate(timeStep);
}
Example #15
0
void Ship::Load(Serializer::Reader &rd, Space *space)
{
	DynamicBody::Load(rd, space);
	// needs fixups
	m_angThrusters = rd.Vector3d();
	m_thrusters = rd.Vector3d();
	m_wheelTransition = rd.Int32();
	m_wheelState = rd.Float();
	m_launchLockTimeout = rd.Float();
	m_testLanded = rd.Bool();
	m_flightState = FlightState(rd.Int32());
	m_alertState = AlertState(rd.Int32());
	m_lastFiringAlert = rd.Double();

	m_hyperspace.dest = SystemPath::Unserialize(rd);
	m_hyperspace.countdown = rd.Float();

	for (int i=0; i<ShipType::GUNMOUNT_MAX; i++) {
		m_gunState[i] = rd.Int32();
		m_gunRecharge[i] = rd.Float();
		m_gunTemperature[i] = rd.Float();
	}
	m_ecmRecharge = rd.Float();
	m_shipFlavour.Load(rd);
	m_type = &ShipType::types[m_shipFlavour.id];
	m_dockedWithPort = rd.Int32();
	m_dockedWithIndex = rd.Int32();
	m_equipment.InitSlotSizes(m_shipFlavour.id);
	m_equipment.Load(rd);
	Init();
	m_stats.hull_mass_left = rd.Float(); // must be after Init()...
	m_stats.shield_mass_left = rd.Float();
	if(rd.Int32()) m_curAICmd = AICommand::Load(rd);
	else m_curAICmd = 0;
	m_aiMessage = AIError(rd.Int32());
	SetFuel(rd.Double());
	m_stats.fuel_tank_mass_left = GetShipType().fuelTankMass * GetFuel();
	m_reserveFuel = rd.Double();
	UpdateStats(); // this is necessary, UpdateStats() in Ship::Init has wrong values of m_thrusterFuel after Load

	m_controller = 0;
	const ShipController::Type ctype = static_cast<ShipController::Type>(rd.Int32());
	if (ctype == ShipController::PLAYER)
		SetController(new PlayerShipController());
	else
		SetController(new ShipController());
	m_controller->Load(rd);

	m_equipment.onChange.connect(sigc::mem_fun(this, &Ship::OnEquipmentChange));
}
Example #16
0
bool Ship::OnDamage(Object *attacker, float kgDamage)
{
	if (!IsDead()) {
		float dam = kgDamage*0.001f;
		if (m_stats.shield_mass_left > 0.0f) {
			if (m_stats.shield_mass_left > dam) {
				m_stats.shield_mass_left -= dam;
				dam = 0;
			} else {
				dam -= m_stats.shield_mass_left;
				m_stats.shield_mass_left = 0;
			}
		}

		m_stats.hull_mass_left -= dam;
		if (m_stats.hull_mass_left < 0) {
			if (attacker) {
				if (attacker->IsType(Object::BODY)) {
					// XXX remove this call. kill stuff (including elite rating) should be in a script
					static_cast<Body*>(attacker)->OnHaveKilled(this);
					Pi::luaOnShipDestroyed->Queue(this, dynamic_cast<Body*>(attacker));
				}

				if (attacker->IsType(Object::SHIP))
					Polit::NotifyOfCrime(static_cast<Ship*>(attacker), Polit::CRIME_MURDER);
			}

			Space::KillBody(this);
			Sfx::Add(this, Sfx::TYPE_EXPLOSION);
			Sound::BodyMakeNoise(this, "Explosion_1", 1.0f);
		}
		
		else {
			if (attacker && attacker->IsType(Object::SHIP))
				Polit::NotifyOfCrime(static_cast<Ship*>(attacker), Polit::CRIME_PIRACY);

			if (Pi::rng.Double() < kgDamage)
				Sfx::Add(this, Sfx::TYPE_DAMAGE);
			
			if (dam < 0.01 * float(GetShipType().hullMass))
				Sound::BodyMakeNoise(this, "Hull_hit_Small", 1.0f);
			else
				Sound::BodyMakeNoise(this, "Hull_Hit_Medium", 1.0f);
		}
	}

	//printf("Ouch! %s took %.1f kilos of damage from %s! (%.1f t hull left)\n", GetLabel().c_str(), kgDamage, attacker->GetLabel().c_str(), m_stats.hull_mass_left);
	return true;
}
Example #17
0
void Ship::Init()
{
	const ShipType &stype = GetShipType();

	SetModel(stype.modelName.c_str());

	SetMassDistributionFromModel();
	UpdateStats();
	m_stats.hull_mass_left = float(stype.hullMass);
	m_stats.shield_mass_left = 0;
	m_hyperspace.now = false;			// TODO: move this on next savegame change, maybe
	m_hyperspaceCloud = 0;

	m_landingGearAnimation = GetModel()->FindAnimation("gear_down");
}
Example #18
0
bool Ship::OnDamage(Object *attacker, float kgDamage)
{
	if (!IsDead()) {
		float dam = kgDamage*0.001f;
		if (m_stats.shield_mass_left > 0.0f) {
			if (m_stats.shield_mass_left > dam) {
				m_stats.shield_mass_left -= dam;
				dam = 0;
			} else {
				dam -= m_stats.shield_mass_left;
				m_stats.shield_mass_left = 0;
			}
		}

		m_stats.hull_mass_left -= dam;
		if (m_stats.hull_mass_left < 0) {
			if (attacker) {
				if (attacker->IsType(Object::BODY))
					LuaEvent::Queue("onShipDestroyed", this, dynamic_cast<Body*>(attacker));

				if (attacker->IsType(Object::SHIP))
					Polit::NotifyOfCrime(static_cast<Ship*>(attacker), Polit::CRIME_MURDER);
			}

			Explode();
		}

		else {
			if (attacker && attacker->IsType(Object::SHIP))
				Polit::NotifyOfCrime(static_cast<Ship*>(attacker), Polit::CRIME_PIRACY);

			if (Pi::rng.Double() < kgDamage)
				Sfx::Add(this, Sfx::TYPE_DAMAGE);

			if (dam < 0.01 * float(GetShipType().hullMass))
				Sound::BodyMakeNoise(this, "Hull_hit_Small", 1.0f);
			else
				Sound::BodyMakeNoise(this, "Hull_Hit_Medium", 1.0f);
		}
	}

	//printf("Ouch! %s took %.1f kilos of damage from %s! (%.1f t hull left)\n", GetLabel().c_str(), kgDamage, attacker->GetLabel().c_str(), m_stats.hull_mass_left);
	return true;
}
Example #19
0
void Ship::FireWeapon(int num)
{
	const ShipType &stype = GetShipType();
	if (m_flightState != FLYING) return;

	const matrix3x3d &m = GetOrient();
	const vector3d dir = m * vector3d(stype.gunMount[num].dir);
	const vector3d pos = m * vector3d(stype.gunMount[num].pos) + GetPosition();

	m_gunTemperature[num] += 0.01f;

	Equip::Type t = m_equipment.Get(Equip::SLOT_LASER, num);
	const LaserType &lt = Equip::lasers[Equip::types[t].tableIndex];
	m_gunRecharge[num] = lt.rechargeTime;
	vector3d baseVel = GetVelocity();
	vector3d dirVel = lt.speed * dir.Normalized();

	if (lt.flags & Equip::LASER_DUAL)
	{
		const ShipType::DualLaserOrientation orient = stype.gunMount[num].orient;
		const vector3d orient_norm =
				(orient == ShipType::DUAL_LASERS_VERTICAL) ? m.VectorX() : m.VectorY();
		const vector3d sep = stype.gunMount[num].sep * dir.Cross(orient_norm).NormalizedSafe();

		Projectile::Add(this, t, pos + sep, baseVel, dirVel);
		Projectile::Add(this, t, pos - sep, baseVel, dirVel);
	}
	else
		Projectile::Add(this, t, pos, baseVel, dirVel);

	/*
			// trace laser beam through frame to see who it hits
			CollisionContact c;
			GetFrame()->GetCollisionSpace()->TraceRay(pos, dir, 10000.0, &c, this->GetGeom());
			if (c.userData1) {
				Body *hit = static_cast<Body*>(c.userData1);
				hit->OnDamage(this, damage);
			}
	*/

	Polit::NotifyOfCrime(this, Polit::CRIME_WEAPON_DISCHARGE);
	Sound::BodyMakeNoise(this, "Pulse_Laser", 1.0f);
}
Example #20
0
void Ship::TimeStepUpdate(const float timeStep)
{
	// If docked, station is responsible for updating position/orient of ship
	// but we call this crap anyway and hope it doesn't do anything bad

	vector3d maxThrust = GetMaxThrust(m_thrusters);
	vector3d thrust = vector3d(maxThrust.x*m_thrusters.x, maxThrust.y*m_thrusters.y,
		maxThrust.z*m_thrusters.z);
	AddRelForce(thrust);
	AddRelTorque(GetShipType().angThrust * m_angThrusters);

	DynamicBody::TimeStepUpdate(timeStep);

	// fuel use decreases mass, so do this as the last thing in the frame
	UpdateFuel(timeStep, thrust);

	if (m_landingGearAnimation)
		static_cast<SceneGraph::Model*>(GetModel())->UpdateAnimations();
}
Example #21
0
void Ship::FireWeapon(int num)
{
	const ShipType &stype = GetShipType();
	if (m_flightState != FLYING) return;
	matrix4x4d m;
	GetRotMatrix(m);

	vector3d dir = vector3d(stype.gunMount[num].dir);
	vector3d pos = vector3d(stype.gunMount[num].pos);
	m_gunTemperature[num] += 0.01f;

	dir = m.ApplyRotationOnly(dir);
	pos = m.ApplyRotationOnly(pos);
	pos += GetPosition();
	
	Equip::Type t = m_equipment.Get(Equip::SLOT_LASER, num);
	const LaserType &lt = Equip::lasers[Equip::types[t].tableIndex];
	m_gunRecharge[num] = lt.rechargeTime;
	vector3d baseVel = GetVelocity();
	vector3d dirVel = lt.speed * dir.Normalized();
	
	if (lt.flags & Equip::LASER_DUAL)
	{
		vector3d sep = dir.Cross(vector3d(m[4],m[5],m[6])).Normalized();
		Projectile::Add(this, t, pos+5.0*sep, baseVel, dirVel);
		Projectile::Add(this, t, pos-5.0*sep, baseVel, dirVel);
	}
	else Projectile::Add(this, t, pos, baseVel, dirVel);

	/*
			// trace laser beam through frame to see who it hits
			CollisionContact c;
			GetFrame()->GetCollisionSpace()->TraceRay(pos, dir, 10000.0, &c, this->GetGeom());
			if (c.userData1) {
				Body *hit = static_cast<Body*>(c.userData1);
				hit->OnDamage(this, damage);
			}
	*/

	Polit::NotifyOfCrime(this, Polit::CRIME_WEAPON_DISCHARGE);
	Sound::BodyMakeNoise(this, "Pulse_Laser", 1.0f);
}
Example #22
0
// Input: direction in ship's frame, doesn't need to be normalized
// Approximate positive angular velocity at match point
// Applies thrust directly
// old: returns whether it can reach that direction in this frame
// returns angle to target
double Ship::AIFaceDirection(const vector3d &dir, double av)
{
	double timeStep = Pi::game->GetTimeStep();
	double maxAccel = GetShipType().angThrust / GetAngularInertia();		// should probably be in stats anyway
	if (maxAccel <= 0.0) return 0.0;
	double frameAccel = maxAccel * timeStep;

	matrix4x4d rot; GetRotMatrix(rot);
	vector3d head = (dir * rot).Normalized();		// create desired object-space heading
	vector3d dav(0.0, 0.0, 0.0);	// desired angular velocity

	double ang = 0.0;
	if (head.z > -0.99999999)			
	{
		ang = acos (Clamp(-head.z, -1.0, 1.0));		// scalar angle from head to curhead
		double iangvel = av + sqrt (2.0 * maxAccel * ang);	// ideal angvel at current time

		double frameEndAV = iangvel - frameAccel;
		if (frameEndAV <= 0.0) iangvel = ang / timeStep;	// last frame discrete correction
		else iangvel = (iangvel + frameEndAV) * 0.5;		// discrete overshoot correction

		// Normalize (head.x, head.y) to give desired angvel direction
		if (head.z > 0.999999) head.x = 1.0;
		double head2dnorm = 1.0 / sqrt(head.x*head.x + head.y*head.y);		// NAN fix shouldn't be necessary if inputs are normalized
		dav.x = head.y * head2dnorm * iangvel;
		dav.y = -head.x * head2dnorm * iangvel;
	}
	vector3d cav = (GetAngVelocity() - GetFrame()->GetAngVelocity()) * rot;		// current obj-rel angvel
//	vector3d cav = GetAngVelocity() * rot;				// current obj-rel angvel
	vector3d diff = (dav - cav) / frameAccel;					// find diff between current & desired angvel

	// If the player is pressing a roll key, don't override roll.
	// XXX this really shouldn't be here. a better way would be to have a
	// field in Ship describing the wanted angvel adjustment from input. the
	// baseclass version in Ship would always be 0. the version in Player
	// would be constructed from user input. that adjustment could then be
	// considered by this method when computing the required change
	if (IsType(Object::PLAYER) && (KeyBindings::rollLeft.IsActive() || KeyBindings::rollRight.IsActive()))
		diff.z = m_angThrusters.z;
	SetAngThrusterState(diff);
	return ang;
}
Example #23
0
void Ship::AIModelCoordsMatchAngVel(vector3d desiredAngVel, double softness)
{
	const ShipType &stype = GetShipType();
	double angAccel = stype.angThrust / GetAngularInertia();
	const double softTimeStep = Pi::GetTimeStep() * softness;

	matrix4x4d rot;
	GetRotMatrix(rot);
	vector3d angVel = desiredAngVel - rot.InverseOf() * GetAngVelocity();

	vector3d thrust; 
	for (int axis=0; axis<3; axis++) {
		if (angAccel * softTimeStep >= fabs(angVel[axis])) {
			thrust[axis] = angVel[axis] / (softTimeStep * angAccel);
		} else {
			thrust[axis] = (angVel[axis] > 0.0 ? 1.0 : -1.0);
		}
	}
	SetAngThrusterState(thrust);
}
Example #24
0
void Ship::TimeStepUpdate(const float timeStep)
{
	// If docked, station is responsible for updating position/orient of ship
	// but we call this crap anyway and hope it doesn't do anything bad

	const vector3d thrust(m_thrusters * GetMaxThrust(m_thrusters));
	AddRelForce(thrust);
	AddRelTorque(GetShipType()->angThrust * m_angThrusters);

	if (m_landingGearAnimation)
		m_landingGearAnimation->SetProgress(m_wheelState);
	m_dragCoeff = DynamicBody::DEFAULT_DRAG_COEFF * (1.0 + 0.25 * m_wheelState);
	DynamicBody::TimeStepUpdate(timeStep);

	// fuel use decreases mass, so do this as the last thing in the frame
	UpdateFuel(timeStep, thrust);

	m_navLights->SetEnabled(m_wheelState > 0.01f);
	m_navLights->Update(timeStep);
	if (m_sensors.get()) m_sensors->Update(timeStep);
}
Example #25
0
// Input: direction in ship's frame, doesn't need to be normalized
// Approximate positive angular velocity at match point
// Applies thrust directly
// old: returns whether it can reach that direction in this frame
// returns angle to target
double Ship::AIFaceDirection(const vector3d &dir, double av)
{
	double timeStep = Pi::GetTimeStep();

	double maxAccel = GetShipType().angThrust / GetAngularInertia();		// should probably be in stats anyway
	if (maxAccel <= 0.0)
		// happens if no angular thrust is set for the model eg MISSILE_UNGUIDED
		return 0.0;
	double frameAccel = maxAccel * timeStep;

	matrix4x4d rot; GetRotMatrix(rot);

	vector3d head = (dir * rot).Normalized();		// create desired object-space heading
	vector3d dav(0.0, 0.0, 0.0);	// desired angular velocity

	double ang = 0.0;
	if (head.z > -0.99999999)			
	{
		ang = acos (Clamp(-head.z, -1.0, 1.0));		// scalar angle from head to curhead
		double iangvel = av + sqrt (2.0 * maxAccel * ang);	// ideal angvel at current time

		double frameEndAV = iangvel - frameAccel;
		if (frameEndAV <= 0.0) iangvel = ang / timeStep;	// last frame discrete correction
		else iangvel = (iangvel + frameEndAV) * 0.5;		// discrete overshoot correction

		// Normalize (head.x, head.y) to give desired angvel direction
		if (head.z > 0.9999) head.x = 1.0;
		double head2dnorm = 1.0 / sqrt(head.x*head.x + head.y*head.y);		// NAN fix shouldn't be necessary if inputs are normalized
		dav.x = head.y * head2dnorm * iangvel;
		dav.y = -head.x * head2dnorm * iangvel;
	}
	vector3d cav = (GetAngVelocity() - GetFrame()->GetAngVelocity()) * rot;		// current obj-rel angvel
//	vector3d cav = GetAngVelocity() * rot;				// current obj-rel angvel
	vector3d diff = (dav - cav) / frameAccel;					// find diff between current & desired angvel

	SetAngThrusterState(diff);
	return ang;
//if (diff.x*diff.x > 1.0 || diff.y*diff.y > 1.0 || diff.z*diff.z > 1.0) return false;
//	else return true;
}
Example #26
0
void Ship::UpdateEquipStats()
{
	const ShipType &stype = GetShipType();

	m_stats.max_capacity = stype.capacity;
	m_stats.used_capacity = 0;
	m_stats.used_cargo = 0;

	for (int i=0; i<Equip::SLOT_MAX; i++) {
		for (int j=0; j<stype.equipSlotCapacity[i]; j++) {
			Equip::Type t = m_equipment.Get(Equip::Slot(i), j);
			if (t) m_stats.used_capacity += Equip::types[t].mass;
			if (Equip::Slot(i) == Equip::SLOT_CARGO) m_stats.used_cargo += Equip::types[t].mass;
		}
	}
	m_stats.free_capacity = m_stats.max_capacity - m_stats.used_capacity;
	m_stats.total_mass = m_stats.used_capacity + stype.hullMass;

	m_stats.shield_mass = TONS_HULL_PER_SHIELD * float(m_equipment.Count(Equip::SLOT_SHIELD, Equip::SHIELD_GENERATOR));

	UpdateMass();
	UpdateFuelStats();

	Equip::Type fuelType = GetHyperdriveFuelType();

	if (stype.equipSlotCapacity[Equip::SLOT_ENGINE]) {
		Equip::Type t = m_equipment.Get(Equip::SLOT_ENGINE);
		int hyperclass = Equip::types[t].pval;
		if (!hyperclass) { // no drive
			m_stats.hyperspace_range = m_stats.hyperspace_range_max = 0;
		} else {
			m_stats.hyperspace_range_max = Pi::CalcHyperspaceRangeMax(hyperclass, GetMass()/1000);
			m_stats.hyperspace_range = Pi::CalcHyperspaceRange(hyperclass, GetMass()/1000, m_equipment.Count(Equip::SLOT_CARGO, fuelType));
		}
	} else {
		m_stats.hyperspace_range = m_stats.hyperspace_range_max = 0;
	}
}
Example #27
0
const shipstats_t *Ship::CalcStats()
{
	const ShipType &stype = GetShipType();
	m_stats.max_capacity = stype.capacity;
	m_stats.used_capacity = 0;
	m_stats.used_cargo = 0;
	Equip::Type fuelType = GetHyperdriveFuelType();

	for (int i=0; i<Equip::SLOT_MAX; i++) {
		for (int j=0; j<stype.equipSlotCapacity[i]; j++) {
			Equip::Type t = m_equipment.Get(Equip::Slot(i), j);
			if (t) m_stats.used_capacity += EquipType::types[t].mass;
			if (Equip::Slot(i) == Equip::SLOT_CARGO) m_stats.used_cargo += EquipType::types[t].mass;
		}
	}
	m_stats.free_capacity = m_stats.max_capacity - m_stats.used_capacity;
	m_stats.total_mass = m_stats.used_capacity + stype.hullMass;

	m_stats.shield_mass = TONS_HULL_PER_SHIELD * float(m_equipment.Count(Equip::SLOT_SHIELD, Equip::SHIELD_GENERATOR));

	if (stype.equipSlotCapacity[Equip::SLOT_ENGINE]) {
		Equip::Type t = m_equipment.Get(Equip::SLOT_ENGINE);
		int hyperclass = EquipType::types[t].pval;
		if (!hyperclass) { // no drive
			m_stats.hyperspace_range = m_stats.hyperspace_range_max = 0;
		} else {
			// for the sake of hyperspace range, we count ships mass as 60% of original.
			m_stats.hyperspace_range_max = Pi::CalcHyperspaceRange(hyperclass, m_stats.total_mass); 
			m_stats.hyperspace_range = std::min(m_stats.hyperspace_range_max, m_stats.hyperspace_range_max * m_equipment.Count(Equip::SLOT_CARGO, fuelType) /
				(hyperclass * hyperclass));
		}
	} else {
		m_stats.hyperspace_range = m_stats.hyperspace_range_max = 0;
	}
	return &m_stats;
}
Example #28
0
void Ship::LoadFromJson(const Json::Value &jsonObj, Space *space)
{
	DynamicBody::LoadFromJson(jsonObj, space);

	if (!jsonObj.isMember("ship")) throw SavedGameCorruptException();
	Json::Value shipObj = jsonObj["ship"];

	if (!shipObj.isMember("ang_thrusters")) throw SavedGameCorruptException();
	if (!shipObj.isMember("thrusters")) throw SavedGameCorruptException();
	if (!shipObj.isMember("wheel_transition")) throw SavedGameCorruptException();
	if (!shipObj.isMember("wheel_state")) throw SavedGameCorruptException();
	if (!shipObj.isMember("launch_lock_timeout")) throw SavedGameCorruptException();
	if (!shipObj.isMember("test_landed")) throw SavedGameCorruptException();
	if (!shipObj.isMember("flight_state")) throw SavedGameCorruptException();
	if (!shipObj.isMember("alert_state")) throw SavedGameCorruptException();
	if (!shipObj.isMember("last_firing_alert")) throw SavedGameCorruptException();
	if (!shipObj.isMember("hyperspace_destination")) throw SavedGameCorruptException();
	if (!shipObj.isMember("hyperspace_countdown")) throw SavedGameCorruptException();
	if (!shipObj.isMember("guns")) throw SavedGameCorruptException();
	if (!shipObj.isMember("ecm_recharge")) throw SavedGameCorruptException();
	if (!shipObj.isMember("ship_type_id")) throw SavedGameCorruptException();
	if (!shipObj.isMember("docked_with_port")) throw SavedGameCorruptException();
	if (!shipObj.isMember("index_for_body_docked_with")) throw SavedGameCorruptException();
	if (!shipObj.isMember("hull_mass_left")) throw SavedGameCorruptException();
	if (!shipObj.isMember("shield_mass_left")) throw SavedGameCorruptException();
	if (!shipObj.isMember("shield_cooldown")) throw SavedGameCorruptException();
	if (!shipObj.isMember("ai_message")) throw SavedGameCorruptException();
	if (!shipObj.isMember("thruster_fuel")) throw SavedGameCorruptException();
	if (!shipObj.isMember("reserve_fuel")) throw SavedGameCorruptException();
	if (!shipObj.isMember("controller_type")) throw SavedGameCorruptException();
	if (!shipObj.isMember("name")) throw SavedGameCorruptException();

	m_skin.LoadFromJson(shipObj);
	m_skin.Apply(GetModel());
	// needs fixups
	JsonToVector(&m_angThrusters, shipObj, "ang_thrusters");
	JsonToVector(&m_thrusters, shipObj, "thrusters");
	m_wheelTransition = shipObj["wheel_transition"].asInt();
	m_wheelState = StrToFloat(shipObj["wheel_state"].asString());
	m_launchLockTimeout = StrToFloat(shipObj["launch_lock_timeout"].asString());
	m_testLanded = shipObj["test_landed"].asBool();
	m_flightState = static_cast<FlightState>(shipObj["flight_state"].asInt());
	m_alertState = static_cast<AlertState>(shipObj["alert_state"].asInt());
	Properties().Set("flightState", EnumStrings::GetString("ShipFlightState", m_flightState));
	Properties().Set("alertStatus", EnumStrings::GetString("ShipAlertStatus", m_alertState));
	m_lastFiringAlert = StrToDouble(shipObj["last_firing_alert"].asString());

	Json::Value hyperspaceDestObj = shipObj["hyperspace_destination"];
	m_hyperspace.dest = SystemPath::FromJson(hyperspaceDestObj);
	m_hyperspace.countdown = StrToFloat(shipObj["hyperspace_countdown"].asString());
	m_hyperspace.duration = 0;

	Json::Value gunArray = shipObj["guns"];
	if (!gunArray.isArray()) throw SavedGameCorruptException();
	assert(ShipType::GUNMOUNT_MAX == gunArray.size());
	for (unsigned int i = 0; i < ShipType::GUNMOUNT_MAX; i++)
	{
		Json::Value gunArrayEl = gunArray[i];
		if (!gunArrayEl.isMember("state")) throw SavedGameCorruptException();
		if (!gunArrayEl.isMember("recharge")) throw SavedGameCorruptException();
		if (!gunArrayEl.isMember("temperature")) throw SavedGameCorruptException();

		m_gun[i].state = gunArrayEl["state"].asUInt();
		m_gun[i].recharge = StrToFloat(gunArrayEl["recharge"].asString());
		m_gun[i].temperature = StrToFloat(gunArrayEl["temperature"].asString());
	}
	m_ecmRecharge = StrToFloat(shipObj["ecm_recharge"].asString());
	SetShipId(shipObj["ship_type_id"].asString()); // XXX handle missing thirdparty ship
	m_dockedWithPort = shipObj["docked_with_port"].asInt();
	m_dockedWithIndex = shipObj["index_for_body_docked_with"].asUInt();
	Init();
	m_stats.hull_mass_left = StrToFloat(shipObj["hull_mass_left"].asString()); // must be after Init()...
	m_stats.shield_mass_left = StrToFloat(shipObj["shield_mass_left"].asString());
	m_shieldCooldown = StrToFloat(shipObj["shield_cooldown"].asString());
	m_curAICmd = 0;
	m_curAICmd = AICommand::LoadFromJson(shipObj);
	m_aiMessage = AIError(shipObj["ai_message"].asInt());
	SetFuel(StrToDouble(shipObj["thruster_fuel"].asString()));
	m_stats.fuel_tank_mass_left = GetShipType()->fuelTankMass * GetFuel();
	m_reserveFuel = StrToDouble(shipObj["reserve_fuel"].asString());

	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.PushLuaTable();
	lua_State *l = Lua::manager->GetLuaState();
	lua_getfield(l, -1, "equipSet");
	m_equipSet = LuaRef(l, -1);
	lua_pop(l, 2);

	UpdateLuaStats();

	m_controller = 0;
	const ShipController::Type ctype = static_cast<ShipController::Type>(shipObj["controller_type"].asInt());
	if (ctype == ShipController::PLAYER)
		SetController(new PlayerShipController());
	else
		SetController(new ShipController());
	m_controller->LoadFromJson(shipObj);

	m_navLights->LoadFromJson(shipObj);

	m_shipName = shipObj["name"].asString();
	Properties().Set("shipName", m_shipName);
}
Example #29
0
// returns speed that can be reached using fuel minus reserve according to the Tsiolkovsky equation
double Ship::GetSpeedReachedWithFuel() const
{
	const double fuelmass = 1000*GetShipType()->fuelTankMass * (m_thrusterFuel - m_reserveFuel);
	if (fuelmass < 0) return 0.0;
	return GetShipType()->effectiveExhaustVelocity * log(GetMass()/(GetMass()-fuelmass));
}
Example #30
0
void Ship::UpdateMass()
{
	SetMass((m_stats.static_mass + GetFuel()*GetShipType()->fuelTankMass)*1000);
}