#include "ship.h" #include "frame.h" #include "l3d.h" #include "world_view.h" #include "space.h" #include "model_coll_mesh_data.h" #include "space_station.h" static ObjParams params = { { 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, /* pColor[3] */ { { { 1.0f, 0.0f, 1.0f }, { 0, 0, 0 }, { 0, 0, 0 }, 0 }, { { 0.8f, 0.6f, 0.5f }, { 0, 0, 0 }, { 0, 0, 0 }, 0 }, { { 0.5f, 0.5f, 0.5f }, { 0, 0, 0 }, { 0, 0, 0 }, 0 } }, /* pText[3][256] */ { "IR-L33T", "ME TOO" }, }; Ship::Ship(ShipType::Type shipType) : DynamicBody() { m_flightState = FLYING; m_testLanded = false; m_launchLockTimeout = 0; m_wheelTransition = 0; m_wheelState = 0; m_dockedWith = 0; m_dockingTimer = 0; m_navTarget = 0; m_combatTarget = 0; m_shipType = shipType; m_angThrusters[0] = m_angThrusters[1] = m_angThrusters[2] = 0; m_laserCollisionObj.owner = this; m_equipment = EquipSet(shipType); for(int i = 0; i < ShipType::GUNMOUNT_MAX; i++) { m_tempLaserGeom[i] = 0; m_gunState[i] = 0; } memset(m_thrusters, 0, sizeof(m_thrusters)); const ShipType& stype = GetShipType(); SetModel(stype.sbreModel); SetMassDistributionFromCollMesh(GetModelSBRECollMesh(stype.sbreModel)); GeomsSetBody(m_body); UpdateMass(); } void Ship::UpdateMass(void) { shipstats_t s; CalcStats(&s); dMassAdjust(&m_mass, s.total_mass*1000); dBodySetMass(m_body, &m_mass); } bool Ship::OnCollision(Body* b, Uint32 flags) { if(b->GetType() == Object::PLANET) { /* Geoms still enabled when landed. */ if(m_flightState != FLYING) return false; else m_testLanded = true; } return true; } vector3d Ship::CalcRotDamping(void) { /* Rotation damping. */ const dReal* _av = dBodyGetAngularVel(m_body); vector3d angVel(_av[0], _av[1], _av[2]); matrix4x4d rot; GetRotMatrix(rot); angVel = rot.InverseOf() * angVel; return angVel * 0.6; } void Ship::SetThrusterState(enum ShipType::Thruster t, float level) { m_thrusters[t] = level; } void Ship::ClearThrusterState(void) { SetAngThrusterState(0, 0.0f); SetAngThrusterState(1, 0.0f); SetAngThrusterState(2, 0.0f); if(m_launchLockTimeout == 0) { for(int i = 0; i < ShipType::THRUSTER_MAX; i++) m_thrusters[i] = 0; } } /* Hyperspace range is: * (200 * hyperspace_class^2) / total_mass (in tonnes) */ void Ship::CalcStats(shipstats_t* stats) { const ShipType& stype = GetShipType(); stats->max_capacity = stype.capacity; stats->used_capacity = 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) stats->used_capacity += EquipType::types[t].mass; } } stats->free_capacity = stats->max_capacity - stats->used_capacity; stats->total_mass = stats->used_capacity + stype.hullMass; Equip::Type t = m_equipment.Get(Equip::SLOT_ENGINE); float hyperclass = EquipType::types[t].pval; stats->hyperspace_range = 200 * hyperclass * hyperclass / stats->total_mass; } void Ship::Blastoff(void) { if(m_flightState != LANDED) return; ClearThrusterState(); m_flightState = FLYING; m_testLanded = false; m_dockedWith = 0; m_launchLockTimeout = 1.0; /* One second of applying thrusters. */ Enable(); const double planetRadius = GetFrame()->m_astroBody->GetRadius(); vector3d up = vector3d::Normalize(GetPosition()); dBodySetLinearVel(m_body, 0, 0, 0); dBodySetAngularVel(m_body, 0, 0, 0); dBodySetForce(m_body, 0, 0, 0); dBodySetTorque(m_body, 0, 0, 0); SetPosition(up*planetRadius+10.0*up); SetThrusterState(ShipType::THRUSTER_TOP, 1.0f); } void Ship::TestLanded(void) { if(m_launchLockTimeout != 0) return; if(GetFrame()->m_astroBody) { const dReal* vel = dBodyGetLinearVel(m_body); double speed = vector3d(vel[0], vel[1], vel[2]).Length(); const double planetRadius = GetFrame()->m_astroBody->GetRadius(); if(speed < 20) { /* Orient the damn thing right! * Why is the inverse of the body rot matrix being used?!?! * *shrugs* it just works this way. */ matrix4x4d rot; GetRotMatrix(rot); matrix4x4d invRot = rot.InverseOf(); vector3d up = vector3d::Normalize(GetPosition()); /* Check player is sortof sensibly oriented for landing. */ const double dot = vector3d::Dot(vector3d::Normalize(vector3d(invRot[1], invRot[5], invRot[9])), up); if(dot > 0.99) { /* Position at zero altitude. */ SetPosition(up * planetRadius); vector3d forward = rot * vector3d(0,0,1); vector3d other = vector3d::Normalize(vector3d::Cross(up, forward)); forward = vector3d::Cross(other, up); rot = matrix4x4d::MakeRotMatrix(other, up, forward); rot = rot.InverseOf(); SetRotMatrix(rot); dBodySetLinearVel(m_body, 0,0,0); dBodySetAngularVel(m_body,0,0,0); dBodySetForce(m_body,0,0,0); dBodySetTorque(m_body,0,0,0); /* * We don't use DynamicBody::Disable because that also disables * the geom, and that must still get collisions. */ dBodyDisable(m_body); ClearThrusterState(); m_flightState = LANDED; m_testLanded = false; } } } } void Ship::TimeStepUpdate(const float timeStep) { m_dockingTimer = (m_dockingTimer-timeStep > 0 ? m_dockingTimer-timeStep : 0); /* ODE tri mesh likes to know our old position. */ TriMeshUpdateLastPos(); m_launchLockTimeout -= timeStep; if(m_launchLockTimeout < 0) m_launchLockTimeout = 0; const ShipType& stype = GetShipType(); for(int i = 0; i < ShipType::THRUSTER_MAX; i++) { float force = timeStep * stype.linThrust[i] * m_thrusters[i]; switch(i) { case ShipType::THRUSTER_REAR: case ShipType::THRUSTER_FRONT: dBodyAddRelForce(m_body, 0, 0, force); break; case ShipType::THRUSTER_TOP: case ShipType::THRUSTER_BOTTOM: dBodyAddRelForce(m_body, 0, force, 0); break; case ShipType::THRUSTER_LEFT: case ShipType::THRUSTER_RIGHT: dBodyAddRelForce(m_body, force, 0, 0); break; } } dBodyAddRelTorque(m_body, stype.angThrust*m_angThrusters[0], stype.angThrust*m_angThrusters[1], stype.angThrust*m_angThrusters[2]); /* LASERZ!! */ for(int i = 0; i < ShipType::GUNMOUNT_MAX; i++) { /* Free old temp laser geoms. */ if(m_tempLaserGeom[i]) dGeomDestroy(m_tempLaserGeom[i]); m_tempLaserGeom[i] = 0; if(!m_gunState[i]) continue; dGeomID ray = dCreateRay(GetFrame()->GetSpaceID(), 10000); const vector3d pos = GetPosition(); const vector3f _dir = stype.gunMount[i].dir; vector3d dir = vector3d(_dir.x, _dir.y, _dir.z); matrix4x4d m; GetRotMatrix(m); dir = m.ApplyRotationOnly(dir); dGeomRaySet(ray, pos.x, pos.y, pos.z, dir.x, dir.y, dir.z); dGeomSetData(ray, static_cast(&m_laserCollisionObj)); m_tempLaserGeom[i] = ray; } if(m_wheelTransition != 0.0f) { m_wheelState += m_wheelTransition*timeStep; m_wheelState = CLAMP(m_wheelState, 0, 1); if((m_wheelState == 0) || (m_wheelState == 1)) m_wheelTransition = 0; } if(m_testLanded) TestLanded(); } void Ship::NotifyDeath(const Body* const dyingBody) { if(GetNavTarget() == dyingBody) SetNavTarget(0); if(GetCombatTarget() == dyingBody) SetCombatTarget(0); } const ShipType& Ship::GetShipType(void) { return ShipType::types[m_shipType]; } void Ship::SetDockedWith(SpaceStation* s) { if(m_dockedWith && !s) { /* * Position player in middle of docking bay, pointing out of it. * TODO Need to do forced thrusting thingy. * TODO ang vel not zeroed for some reason.. */ matrix4x4d stationRot; m_dockedWith->GetRotMatrix(stationRot); vector3d port_y = vector3d::Cross(-m_dockedWith->port.horiz, m_dockedWith->port.normal); matrix4x4d rot = stationRot * matrix4x4d::MakeRotMatrix(m_dockedWith->port.horiz, port_y, m_dockedWith->port.normal); vector3d pos = m_dockedWith->GetPosition() + stationRot*m_dockedWith->port.center; SetPosition(pos); SetRotMatrix(rot); SetVelocity(vector3d(0, 0, 0)); SetAngVelocity(vector3d(0, 0, 0)); Enable(); m_dockedWith = 0; } else { m_dockedWith = s; m_dockingTimer = 0.0f; SetVelocity(vector3d(0, 0, 0)); SetAngVelocity(vector3d(0, 0, 0)); Disable(); } } void Ship::SetGunState(int idx, int state) { m_gunState[idx] = state; } bool Ship::SetWheelState(bool down) { if(m_flightState != FLYING) return false; if(down) m_wheelTransition = 1; else m_wheelTransition = -1; return true; } void Ship::SetNavTarget(Body* const target) { m_navTarget = target; L3D::worldView->UpdateCommsOptions(); } void Ship::SetCombatTarget(Body* const target) { m_combatTarget = target; L3D::worldView->UpdateCommsOptions(); } bool Ship::IsFiringLasers(void) { for(int i = 0; i < ShipType::GUNMOUNT_MAX; i++) { if(m_gunState[i]) return true; } return false; } /* Assumed to be at model coords. */ void Ship::RenderLaserfire(void) { 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); glColor3f(1, 0, 0); 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); } static void render_coll_mesh(const CollMesh* m) { glDisable(GL_LIGHTING); glColor3f(1, 0, 1); glBegin(GL_TRIANGLES); for(int i = 0; i < m->ni; i += 3) { glVertex3fv(&m->pVertex[3*m->pIndex[i]]); glVertex3fv(&m->pVertex[3*m->pIndex[i+1]]); glVertex3fv(&m->pVertex[3*m->pIndex[i+2]]); } glEnd(); glColor3f(1,1,1); glDepthRange(0, 1.0f-0.0002f); for(int i = 0; i < m->ni; i += 3) { glBegin(GL_LINE_LOOP); glVertex3fv(&m->pVertex[3*m->pIndex[i]]); glVertex3fv(&m->pVertex[3*m->pIndex[i+1]]); glVertex3fv(&m->pVertex[3*m->pIndex[i+2]]); glEnd(); } glDepthRange(0.0, 1.0); glEnable(GL_LIGHTING); } void Ship::Render(const Frame* camFrame) { if((!dBodyIsEnabled(m_body)) && !m_flightState) return; const ShipType& stype = GetShipType(); params.angthrust[0] = m_angThrusters[0]; params.angthrust[1] = m_angThrusters[1]; params.angthrust[2] = m_angThrusters[2]; params.linthrust[0] = m_thrusters[ShipType::THRUSTER_RIGHT] - m_thrusters[ShipType::THRUSTER_LEFT]; params.linthrust[1] = m_thrusters[ShipType::THRUSTER_TOP] - m_thrusters[ShipType::THRUSTER_BOTTOM]; params.linthrust[2] = m_thrusters[ShipType::THRUSTER_REAR] - m_thrusters[ShipType::THRUSTER_FRONT]; params.pAnim[ASRC_SECFRAC] = L3D::GetGameTime(); params.pAnim[ASRC_MINFRAC] = L3D::GetGameTime() / 60; params.pAnim[ASRC_HOURFRAC] = L3D::GetGameTime() / 3600.0f; params.pAnim[ASRC_DAYFRAC] = L3D::GetGameTime() / (24*3600.0f); params.pAnim[ASRC_GEAR] = m_wheelState; params.pFlag[AFLAG_GEAR] = m_wheelState != 0.0f; strncpy(params.pText[0], GetLabel().c_str(), sizeof(params.pText)); RenderSbreModel(camFrame, stype.sbreModel, ¶ms); if(IsFiringLasers()) { glPushMatrix(); TransformToModelCoords(camFrame); RenderLaserfire(); glPopMatrix(); } }