The-Simpsons-Hit-and-Run/game/code/worldsim/redbrick/vehicle.cpp

6483 lines
186 KiB
C++

//=============================================================================
// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
//
// File: vehicle.cpp
//
// Description: the car
//
// History: Nov 16, 2001 + Created -- gmayer
//
//=============================================================================
//========================================
// System Includes
//========================================
#include <p3d/anim/skeleton.hpp>
#include <p3d/matrixstack.hpp>
#include <simcommon/simstate.hpp>
#include <simcommon/simstatearticulated.hpp>
#include <simcommon/simulatedobject.hpp>
#include <simcommon/simenvironment.hpp>
#include <simcollision/collisionobject.hpp>
#include <simcollision/collisionvolume.hpp>
#include <simcollision/collisionmanager.hpp>
#include <simphysics/articulatedphysicsobject.hpp>
#include <simphysics/physicsobject.hpp>
#include <mission/charactersheet/charactersheetmanager.h>
#include <simcollision/collisiondisplay.hpp>
#include <render/DSG/StatePropDSG.h>
#include <worldsim/character/characterrenderable.h>
#include <mission/statepropcollectible.h>
#include <mission/objectives/missionobjective.h>
#include <raddebug.hpp>
#include <raddebugwatch.hpp>
#include <string.h>
//========================================
// Project Includes
//========================================
#include <worldsim/redbrick/vehicle.h>
#include <worldsim/worldphysicsmanager.h>
#include <worldsim/redbrick/geometryvehicle.h>
#include <worldsim/redbrick/wheel.h>
#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
#include <worldsim/redbrick/physicslocomotion.h>
#include <worldsim/redbrick/trafficlocomotion.h>
#include <worldsim/traffic/trafficmanager.h>
#include <worldsim/avatarmanager.h>
#include <worldsim/avatar.h>
#include <worldsim/character/character.h>
#include <choreo/puppet.hpp>
#include <events/eventmanager.h>
#include <events/eventdata.h>
#include <roads/roadsegment.h>
#include <roads/geometry.h>
#include <memory/srrmemory.h>
#include <mission/gameplaymanager.h>
#include <meta/carstartlocator.h>
#include <debug/debuginfo.h>
#include <ai/actionbuttonhandler.h>
#include <ai/actionbuttonmanager.h>
#include <meta/eventlocator.h>
#include <meta/spheretriggervolume.h>
#include <meta/recttriggervolume.h>
#include <render/RenderManager/RenderManager.h>
#include <render/Culling/WorldScene.h>
#include <render/IntersectManager/IntersectManager.h>
#include <render/breakables/breakablesmanager.h>
#include <sound/soundcollisiondata.h>
#include <radmath/radmath.hpp>
#include <worldsim/coins/sparkle.h>
#include <cheats/cheatinputsystem.h>
#include <mission/gameplaymanager.h>
#include <supersprint/supersprintmanager.h>
#include <p3d/billboardobject.hpp>
#include <meta/triggervolumetracker.h>
#include <camera/supercammanager.h>
#include <camera/supercamcentral.h>
#include <presentation/gui/guisystem.h>
using namespace sim;
// note - methods that are only called once at initialization, moved to vehicleinit
// CONSTANTS
// In what radius the vehicle explosion will affect objects
const float EXPLOSION_EFFECT_RADIUS = 20.0f;
// How much force to apply to objects within this radius
const float EXPLOSION_FORCE = 20000.0f;
// Set the center of the explosion so be below that car position so that objects get hurled upwards
const float EXPLOSION_Y_OFFSET = -5.0f;
const float HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION = 0.5;
const float HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION_PLAYER = 0.5;
static const float REST_LINEAR_TOL = 0.05f; // linear velocity tolerance
static const float REST_ANGULAR_TOL = 0.3f; // angular velocity tolerance
// Initialize static variables
float Vehicle::s_DamageFromExplosion = HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION;
float Vehicle::s_DamageFromExplosionPlayer = HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION_PLAYER;
bool Vehicle::sDoBounce = false;
void Vehicle::ActivateTriggers( bool activate )
{
if(activate == mTriggerActive)
{
return;
}
if( mVehicleType == VT_AI && activate )
{
// NOT OK to add triggers for AI car doors, but OK to remove...
// AI triggers remain as they are (so you can't get into the cars)
//rAssert( false, "FALSE, Chuck, FALSE!" );
return;
}
if(mVehicleDestroyed && activate)
{
return;
}
if(!GetVehicleCentral()->GetVehicleTriggersActive() && activate)
{
return;
}
if( mpEventLocator == NULL )
{
return;
}
mTriggerActive = activate;
if( activate )
{
for( unsigned j = 0; j < mpEventLocator->GetNumTriggers(); j++ )
{
GetTriggerVolumeTracker()->AddTrigger( mpEventLocator->GetTriggerVolume( j ) );
}
}
else
{
for( unsigned j = 0; j < mpEventLocator->GetNumTriggers(); j++ )
{
GetTriggerVolumeTracker()->RemoveTrigger( mpEventLocator->GetTriggerVolume( j ) );
}
}
}
void Vehicle::SetUserDrivingCar( bool b )
{
// chooka set's this when a character get's in or out.
mUserDrivingCar = b;
if( mVehicleType != VT_AI && mUserDrivingCar )
{
mVehicleType = VT_USER;
}
}
void Vehicle::TransitToAI()
{
mVehicleType = VT_AI;
ActivateTriggers( false );
GetEventManager()->TriggerEvent( EVENT_USER_VEHICLE_REMOVED_FROM_WORLD, this );
}
//=============================================================================
//
//=============================================================================
// Description: Comment
//
// Parameters:
//
// Return: void
//
//=============================================================================
void Vehicle::EnteringJumpBoostVolume()
{
mDoingJumpBoost = true;
}
/*
void Vehicle::AddRef()
{
if( mVehicleType == VT_TRAFFIC && TrafficManager::GetInstance()->IsVehicleTrafficVehicle(this) )
{
rDebugPrintf( "Booya!\n" );
}
tRefCounted::AddRef();
}
void Vehicle::Release()
{
if( mVehicleType == VT_TRAFFIC && TrafficManager::GetInstance()->IsVehicleTrafficVehicle(this) )
{
rDebugPrintf( "Yaaaboo!\n" );
}
tRefCounted::Release();
}
*/
//=============================================================================
// Vehicle::
//=============================================================================
// Description: Comment
//
// Parameters:
//
// Return: void
//
//=============================================================================
void Vehicle::ExitingJumpBoostVolume()
{
if(mDoingJumpBoost)
{
// if we were doing one, now might be a good time for this event:
if(mVehicleType == VT_USER)
{
GetEventManager()->TriggerEvent(EVENT_BIG_AIR, (void*)(this->mpDriver));
}
}
mDoingJumpBoost = false;
}
int Vehicle::CastsShadow()
{
int retVal;
// Casts a shadow in the 2nd pass, if this vehicle has one (witch and ship don't)
if ( m_IsSimpleShadow )
{
retVal = 989;
}
else
{
retVal = 0;
}
return retVal;
}
//=============================================================================
// Vehicle::DisplayShadow
//=============================================================================
// Description: Comment
//
// Parameters: ( )
//
// Return: void
//
//=============================================================================
void Vehicle::DisplayShadow()
{
if( !mOkToDrawSelf || !mDrawVehicle )
{
return;
}
BEGIN_PROFILE("Vehicle::DisplayShadow")
if( !IsSimpleShadow() )
{
p3d::stack->Push();
p3d::stack->Multiply(mTransform);
mGeometryVehicle->DisplayShadow();
p3d::stack->Pop();
}
END_PROFILE("Vehicle::DisplayShadow")
}
void Vehicle::DisplaySimpleShadow( void )
{
if( !mOkToDrawSelf || !mDrawVehicle )
{
return;
}
BEGIN_PROFILE("Vehicle::DisplaySimpleShadow")
p3d::pddi->SetZWrite(false);
if( IsSimpleShadow() )
{
const float HeightRatio = 1.0f / 4.0f;
rmt::Vector pos = mTransform.Row( 3 );
float carY = pos.y;
pos.y = GetGroundY();
rmt::Vector norm;
rmt::Vector forward;
forward = mTransform.Row( 2 );
if( GetLocomotionType() == VL_TRAFFIC )
{
// We'll assume the traffic doesn't go jumping through the air...although they
//could theorically get knocked through the air I guess. When that happens however
//they are under physics control and Greg says they aren't VL_TRAFFIC any more.
norm = mTransform.Row( 1 );
}
else
{
//const rmt::Matrix& groundTrans = mGroundPlaneSimState->GetTransform();
//norm = groundTrans.Row( 2 );
norm = this->mGroundPlaneWallVolume->mNormal;
}
BlobShadowParams p( pos, norm, forward );
p.ShadowScale = rmt::Clamp( 1.0f - ( ( carY - ( pos.y + GetRestHeightAboveGround() ) ) * HeightRatio ), 0.0f, 1.0f );
p.ShadowAlpha = p.ShadowScale * ( mInterior ? 0.5f : 1.0f );
mGeometryVehicle->DisplayShadow( &p );
}
p3d::pddi->SetZWrite(true);
END_PROFILE("Vehicle::DisplaySimpleShadow")
}
//=============================================================================
// Vehicle::SetInCarSimState
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::SetInCarSimState()
{
if(mSimStateArticulatedOutOfCar)
{
if(!mUsingInCarPhysics)
{
if(mCollisionAreaIndex != -1)
{
GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(this);
RemoveSelfFromCollisionManager(); // deals with our own index
GetWorldPhysicsManager()->EmptyCollisionAreaIndex(this->mCollisionAreaIndex);
}
mSimStateArticulatedOutOfCar->ResetVelocities();
mSimStateArticulatedInCar->ResetVelocities();
rmt::Matrix transform = mSimStateArticulatedOutOfCar->GetTransform();
mSimStateArticulatedInCar->SetControl(sim::simAICtrl);
mSimStateArticulatedInCar->SetTransform(transform);
mSimStateArticulatedInCar->SetControl(sim::simSimulationCtrl);
mSimStateArticulated = mSimStateArticulatedInCar;
mUsingInCarPhysics = true;
if(mCollisionAreaIndex != -1)
{
AddSelfToCollisionManager();
}
}
}
else
{
// please God let this be the last fucking hack in this game...
mSimStateArticulated->ResetVelocities();
rmt::Matrix transform = mSimStateArticulated->GetTransform();
mSimStateArticulated->SetControl(sim::simAICtrl);
mSimStateArticulated->SetTransform(transform);
mSimStateArticulated->SetControl(sim::simSimulationCtrl);
CalculateSuspensionLocationAndVelocity(); // just in case
}
}
//=============================================================================
// Vehicle::SetOutOfCarSimState
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::SetOutOfCarSimState()
{
if(mSimStateArticulatedOutOfCar)
{
if(mUsingInCarPhysics)
{
if(mCollisionAreaIndex != -1)
{
GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(this);
RemoveSelfFromCollisionManager(); // deals with our own index
GetWorldPhysicsManager()->EmptyCollisionAreaIndex(this->mCollisionAreaIndex);
// hmmm
// this is probably at init.
//
// we need to do something to try and make the normal physics update loop run once
// so that the cars settles into place with suspensionYOffset set by designers...
// try this:
//this->PreSubstepUpdate();
//this->PreCollisionPrep(0.016f, true);
//this->Update(0.016f);
//this->PostSubstepUpdate();
}
mSimStateArticulatedOutOfCar->ResetVelocities();
mSimStateArticulatedInCar->ResetVelocities();
rmt::Matrix transform = mSimStateArticulatedInCar->GetTransform();
mSimStateArticulatedOutOfCar->SetControl(sim::simAICtrl);
mSimStateArticulatedOutOfCar->SetTransform(transform);
mSimStateArticulatedOutOfCar->SetControl(sim::simSimulationCtrl);
mSimStateArticulated = mSimStateArticulatedOutOfCar;
CalculateSuspensionLocationAndVelocity(); // just in case
mUsingInCarPhysics = false;
if(mCollisionAreaIndex != -1)
{
AddSelfToCollisionManager();
}
}
}
else
{
// please God let this be the last fucking hack in this game...
mSimStateArticulated->ResetVelocities();
rmt::Matrix transform = mSimStateArticulated->GetTransform();
mSimStateArticulated->SetControl(sim::simAICtrl);
mSimStateArticulated->SetTransform(transform);
mSimStateArticulated->SetControl(sim::simSimulationCtrl);
CalculateSuspensionLocationAndVelocity(); // just in case
}
}
//=============================================================================
// Vehicle::SetLocomotion
//=============================================================================
// Description: Comment
//
// Parameters: ( VehicleLocomotionType loco )
//
// Return: void
//
//=============================================================================
void Vehicle::SetLocomotion( VehicleLocomotionType loco )
{
switch(loco)
{
case VL_PHYSICS:
if(mLoco == VL_TRAFFIC)
{
// this is a traffic car that has just been hit
// deactivate the AI so that when we get to a rest state
// and return to AI control the car won't move
::GetEventManager()->TriggerEvent( EVENT_TRAFFIC_GOT_HIT, this );
TrafficManager::GetInstance()->Deactivate( this );
}
mVehicleLocomotion = mPhysicsLocomotion;
//mSimStateArticulated->ResetVelocities(); //hmmmmmmmmmmmmmmmmmmmmmmmmmm
// looks like we have to do this
//mSimStateArticulated->StoreJointState(0.016f);
mSimStateArticulated->SetControl(simSimulationCtrl);
mLocoSwitchedToPhysicsThisFrame = true;
// hmm....
// safe to set velocity of vehicle to mVelocityCM?
//mSimStateArticulated->ResetVelocities();
break;
case VL_TRAFFIC:
mVehicleLocomotion = mTrafficLocomotion;
mSimStateArticulated->SetControl(simAICtrl);
mSimStateArticulated->ResetVelocities();
break;
default:
rAssert(0);
}
mLoco = loco;
}
//=============================================================================
// Vehicle::IsAFlappingJoint
//=============================================================================
// Description: Comment
//
// Parameters: (int index)
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsAFlappingJoint(int index)
{
if( index == mDoorDJoint ||
index == mDoorPJoint ||
index == mHoodJoint ||
index == mTrunkJoint)
{
return true;
}
return false;
}
//=============================================================================
// Vehicle::CalculateDragCoeffBasedOnTopSpeed
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::CalculateDragCoeffBasedOnTopSpeed()
{
// this will also have to be called again whenever the desginer params are reloaded
// dragforce = 0.5 * coeff * speed * speed;
//
// so, we need a coeff that causes dragforce to match engine force at that speed
//
// for now we know the max engine force - 2 * 1.0f * mDpGasScale
float topspeedmps = mDesignerParams.mDpTopSpeedKmh / 3.6f;
// * 2 because this is what we apply at each wheel
// also need to subtract rolling friction for this to be accurate
//float terminalForce = 2.0f * mDesignerParams.mDpGasScale * mDesignerParams.mDpMass - mRollingFrictionForce;
float terminalForce = 0.0f;
if(mDesignerParams.mDpGasScaleSpeedThreshold == 1.0f)
{
terminalForce = 2.0f * mDesignerParams.mDpGasScale * mDesignerParams.mDpMass; //) - (mRollingFrictionForce * mDesignerParams.mDpMass);
}
else
{
// else threhold is < 1.0 so it is used
terminalForce = 2.0f * mDesignerParams.mDpHighSpeedGasScale * mDesignerParams.mDpMass; //) - (mRollingFrictionForce * mDesignerParams.mDpMass);
}
mDragCoeff = 2.0f* terminalForce / (topspeedmps * topspeedmps);
}
//=============================================================================
// Vehicle::IsJointAWheel
//=============================================================================
// Description: Comment
//
// Parameters: (int jointIndex)
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsJointAWheel(int jointIndex)
{
if(mJointIndexToWheelMapping[jointIndex] >= 0)
{
return true;
}
else
{
return false;
}
}
//=============================================================================
// Vehicle::SetWheelCorrectionOffset
//=============================================================================
// Description: Comment
//
// Parameters: (int jointNum, float objectSpaceYOffsetFromCurrentPosition)
//
// Return: bool
//
//=============================================================================
bool Vehicle::SetWheelCorrectionOffset(int jointNum, float objectSpaceYOffsetFromCurrentPosition, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint)
{
//bool bottomedOut = mWheels[mJointIndexToWheelMapping[jointNum]]->SetYOffsetFromCurrentPosition(objectSpaceYOffsetFromCurrentPosition);
float bottomedOut = mWheels[mJointIndexToWheelMapping[jointNum]]->SetYOffsetFromCurrentPosition(objectSpaceYOffsetFromCurrentPosition);
if(mLoco == VL_PHYSICS) // is this method even called if it's not?
{
mVehicleLocomotion->CompareNormalAndHeight(mJointIndexToWheelMapping[jointNum], normalPointingAtCar, groundContactPoint);
}
if(bottomedOut > 0.0f)
{
// if any wheel bottoms out we want to set this
// currently this is just used by the parent Vehicle to do some debug rendering
mBottomedOutThisFrame = true;
// TODO ?
// what to do with this value
return true;
}
//return bottomedOut;
return false;
}
//=============================================================================
// Vehicle::SetVehicleSimEnvironment
//=============================================================================
// Description: Comment
//
// Parameters: (sim::SimEnvironment* se)
//
// Return: void
//
//=============================================================================
void Vehicle::SetVehicleSimEnvironment(sim::SimEnvironment* se)
{
mPhObj->SetSimEnvironment(se);
}
//=============================================================================
// Vehicle::GetCollisionAreaIndexAndAddSelf
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::GetCollisionAreaIndexAndAddSelf()
{
mCollisionAreaIndex = GetWorldPhysicsManager()->GetVehicleCollisionAreaIndex();
rAssert(mCollisionAreaIndex != -1);
AddSelfToCollisionManager();
}
//=============================================================================
// Vehicle::RemoveSelfAndFreeCollisionAreaIndex
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::RemoveSelfAndFreeCollisionAreaIndex()
{
RemoveSelfFromCollisionManager();
GetWorldPhysicsManager()->FreeCollisionAreaIndex(mCollisionAreaIndex);
mCollisionAreaIndex = -1;
}
//=============================================================================
// Vehicle::AddSelfToCollisionManager
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::AddSelfToCollisionManager()
{
GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(mSimStateArticulated->GetCollisionObject(), mCollisionAreaIndex);
GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(mGroundPlaneSimState->GetCollisionObject(), mCollisionAreaIndex);
GetWorldPhysicsManager()->mCollisionManager->AddPair(mGroundPlaneSimState->GetCollisionObject(), mSimStateArticulated->GetCollisionObject(), mCollisionAreaIndex);
}
//=============================================================================
// Vehicle::RemoveSelfFromCollisionManager
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::RemoveSelfFromCollisionManager()
{
rAssert(mCollisionAreaIndex != -1);
GetWorldPhysicsManager()->mCollisionManager->RemoveCollisionObject(mSimStateArticulated->GetCollisionObject(), mCollisionAreaIndex);
GetWorldPhysicsManager()->mCollisionManager->RemoveCollisionObject(mGroundPlaneSimState->GetCollisionObject(), mCollisionAreaIndex);
}
//=============================================================================
// Vehicle::AddToOtherCollisionArea
//=============================================================================
// Description: Comment
//
// Parameters: (int index)
//
// Return: void
//
//=============================================================================
void Vehicle::AddToOtherCollisionArea(int index)
{
GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(mSimStateArticulated->GetCollisionObject(), index);
}
//=============================================================================
// Vehicle::ReLoadDesignerParams
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::CalculateValuesBasedOnDesignerParams()
{
// in the future, reload from file or something?
//
// right now, this method can just be called when a gamepad button is hit, to
// pass down new settings (using the Marting-method in msdev) and force
// recalculation in the physicsvehicle and wheel as necessary
int stophere = 1; // break, and change values in msdev, then continue
//
//mPhysicsVehicle->SetDesignerParams(&mDesignerParams);
if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_HIGH_ACCELERATION))
{
//mDesignerParams.mDpGasScale *= 3.0f;
}
// TODO - reimplement as necessary here
// account for new mass
SetupPhysicsProperties();
CalculateDragCoeffBasedOnTopSpeed();
int i;
for(i = 0; i < 4; i++)
{
mSuspensionRestPoints[i] = mSuspensionRestPointsFromFile[i];
mSuspensionRestPoints[i].y += mDesignerParams.mDpSuspensionYOffset;
mWheels[i]->SetDesignerParams(&mDesignerParams);
}
// reset hitpoints to full
mHitPoints = mDesignerParams.mHitPoints;
float health = GetCharacterSheetManager()->GetCarHealth( mCharacterSheetCarIndex );
if( ( health >= 0.0f ) && ( health < 1.0f ) )
{
mHitPoints *= health;
SyncVisualDamage( health );
}
}
//=============================================================================
// Vehicle::TrafficSetTransform
//=============================================================================
// Description: Comment
//
// Parameters: (rmt::Matrix &m)
//
// Return: void
//
//=============================================================================
void Vehicle::TrafficSetTransform(rmt::Matrix &m)
{
// should already be under simAICtrl
rAssert(mSimStateArticulated->GetControl() == simAICtrl);
// the position in the incoming matrix is on the road surface
//
// need to figure out how much to bump this up along vup
// ( I think the wheels settling a bit on the suspension will have to be
// handled elsewhere.. eg. suspensionjointdriver)
float restHeightAboveGround = GetRestHeightAboveGround();
//restHeightAboveGround = 2.0f;
rmt::Vector fudge = mVehicleUp;
const float hacktest = 0.1f;
fudge.Scale(restHeightAboveGround - hacktest);
rmt::Vector currentTrans = m.Row(3);
currentTrans.Add(fudge);
m.FillTranslate(currentTrans);
mSimStateArticulated->SetTransform(m);
mTransform = m;
}
//=============================================================================
// Vehicle::GetRestHeightAboveGround
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::GetRestHeightAboveGround()
{
// ie. height above ground for skeleton in rest pose
//
// assume all the wheels have same radius and same suspension point y
// TODO - is this correct? adequate?
float suspensionPointToCenter = -1.0f * mSuspensionRestPoints[0].y;
float wheelRadius = mWheels[0]->mRadius;
return suspensionPointToCenter + wheelRadius;
}
//=============================================================================
// Vehicle::SetTransform
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Matrix* m )
//
// Return: void
//
//=============================================================================
void Vehicle::SetTransform( rmt::Matrix &m )
{
if(mSimStateArticulated->GetControl() == simAICtrl)
{
mSimStateArticulated->SetTransform(m);
// TODO
// MS7 HACK
//mSimStateArticulated->SetControl(simSimulationCtrl);
}
else
{
mSimStateArticulated->SetControl(simAICtrl);
mSimStateArticulated->SetTransform(m);
mSimStateArticulated->SetControl(simSimulationCtrl);
}
mSimStateArticulated->ResetVelocities();
mTransform = mSimStateArticulated->GetTransform(-1);
mPoseEngine->Begin(true); // TODO - should this be true or false
int i;
for (i = 0; i < mPoseEngine->GetPassCount(); i++)
{
mPoseEngine->Advance(i, 0);
mPoseEngine->Update(i);
}
mPoseEngine->End();
mSimStateArticulated->UpdateJointState(0);
// TODO - reset other state variables
// TODO - need to touch pose engine here?
mVelocityCM.Set(0.0f, 0.0f, 0.0f);
mSpeed = 0.0f;
mSpeedKmh = 0.0f;
mPercentOfTopSpeed = 0.0f;
mBrakeTimer = 0.0f;
mBrakeActingAsReverse = false;
mVehicleFacing = mTransform.Row(2);
mVehicleUp = mTransform.Row(1);
mVehicleTransverse = mTransform.Row(0);
mGear = 0; // neutral?
// redundant, but just in case
for(i = 0; i < 4; i++)
{
mWheels[i]->Reset();
}
CalculateSuspensionLocationAndVelocity();
mPhysicsLocomotion->SetTerrainIntersectCachePointsForNewTransform();
//Only do this if the car is actually IN the DSG
if ( mVehicleCentralIndex > -1 )
{
DSGUpdateAndMove();
}
}
//=============================================================================
// Vehicle::SetPosition
//=============================================================================
// Description: Comment
//
// Parameters: (rmt::Vector* newPos)
//
// Return: void
//
//=============================================================================
void Vehicle::SetPosition(rmt::Vector* newPos)
{
rmt::Matrix m;
m.Identity();
m.FillTranslate(*newPos);
SetTransform( m );
}
//
// Dumbledore here,
// This method is to be used for vehicles that get spawned at a locator position that
// has been SNAPPED TO GROUND. Since Vehicle will assume initial position belongs
// to the car's center of mass, the car will start half-sunken into the ground..
// that is unless we can do the auto adjustment here...
//
void Vehicle::SetInitialPositionGroundOffsetAutoAdjust( rmt::Vector* newPos )
{
mInitialPosition = *newPos;
mInitialPosition.y += GetRestHeightAboveGround();
}
//=============================================================================
// Vehicle::SetInitialPosition
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Vector* newPos )
//
// Return: void
//
//=============================================================================
void Vehicle::SetInitialPosition( rmt::Vector* newPos )
{
mInitialPosition = *newPos;
}
//=============================================================================
// Vehicle::SetResetFacingInRadians
//=============================================================================
// Description: Comment
//
// Parameters: ( float rotation )
//
// Return: void
//
//=============================================================================
void Vehicle::SetResetFacingInRadians( float rotation )
{
mResetFacingRadians = rotation;
}
//=============================================================================
// Vehicle::Reset
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::Reset( bool resetDamage, bool clearTraffic )
{
//SetPosition(&mInitialPosition);
rmt::Matrix m;
m.Identity();
m.FillRotateXYZ( 0.0f, mResetFacingRadians, 0.0f );
m.FillTranslate( mInitialPosition );
int i;
for(i = 0; i < 4; i++)
{
mWheels[i]->Reset();
}
SetTransform( m );
/*
// FORCE UPDATE THE AVATAR'S LAST PATH INFO
Avatar* avatar = GetAvatarManager()->GetAvatarForVehicle( this );
if( avatar )
{
RoadManager::PathElement elem;
RoadSegment* seg;
float segT, roadT;
avatar->GetLastPathInfo( elem, seg, segT, roadT );
}
*/
// if clearTraffic is set to true, we want to use the functionality of ResetOnSpot
// but use the position that was filled into mInitialPosition
if(clearTraffic)
{
this->ResetOnSpot(resetDamage, false);
}
else
{
ResetFlagsOnly( resetDamage );
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForVehicle( this );
if ( playerAvatar )
{
GetSuperCamManager()->GetSCC( playerAvatar->GetPlayerId() )->DoCameraCut();
}
}
}
//=============================================================================
// Vehicle::ResetFlagsOnly
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::ResetFlagsOnly(bool resetDamage)
{
mOkToDrawSelf = true;
mDoingBurnout = false;
//mOkToCrashLand = false;
mDoingRockford = false;
mSpeedBurstTimer = 0.0f;
mEBrakeTimer = 0.0f;
mBuildingUpSpeedBurst = false;
mDoSpeedBurst = false;
mDoingJumpBoost = false;
mBrakeLightsOn = false;
mReverseLightsOn = false;
mCMOffsetSetToOriginal = false;
mStuckOnSideTimer = 0.0f;
if( resetDamage )
{
mVehicleDestroyed = false;
ResetDamageState();
mAlreadyPlayedExplosion = false;
}
mDesiredDoorPosition[0] = mDesiredDoorPosition[1] = 0.0f;
mDesiredDoorAction[0] = mDesiredDoorAction[1] = DOORACTION_NONE;
mDrawWireFrame = false;
mWasAirborn = false;
mWasAirbornTimer = 0.0f;
mBottomOutSpeedMaintenance = 0.0f;
// reset the AI pathfinding information, if we're an AI vehicle
if( mVehicleType == VT_AI && mVehicleCentralIndex != -1 )
{
// if we're an AI car, dump our pathfinding data
VehicleAI* vAI = dynamic_cast<VehicleAI*>( GetVehicleCentral()->GetVehicleController( mVehicleCentralIndex ) );
if( vAI )
{
vAI->Reset();
}
}
}
//=============================================================================
// Vehicle::ResetOnSpot
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::ResetOnSpot( bool resetDamage /*=true*/ , bool moveCarOntoRoad)
{
// find road segment and put them on it
// one branch or the other will fill these up
rmt::Vector newVehiclePosition(0.0f, 0.0f, 0.0f);
float ang = 0.0f;
rmt::Vector currentVehiclePosition = this->rPosition();
float radius = 100.0f; // this is the radius of query for nearest road
RoadSegment* roadSeg = NULL;
float dummy;
bool useIntersection = false;
Intersection* in = NULL;
// We need to do something special for street races because they have race props,
// meaning that resetting onto the "nearest road" can take you outside the race
// bounds. For normal race missions, we just find the closest road.
//
if( GetGameplayManager()->GetCurrentMission() != NULL &&
GetGameplayManager()->GetCurrentMission()->IsRaceMission() )
{
RoadManager::PathElement elem;
RoadSegment* seg = 0;
float segT;
float roadT;
if( mVehicleType == VT_USER )
{
//// Use my UBER-search algorithm only when desperate times call for it.
//RoadManager::PathfindingOptions options = RoadManager::PO_SEARCH_INTERSECTIONS | RoadManager::PO_SEARCH_ROADS;
//RoadManager::GetInstance()->FindClosestPathElement( currentVehiclePosition, radius, options, elem, roadSeg, segT, roadT );
GetAvatarManager()->GetAvatarForPlayer(0)->GetLastPathInfo(elem, roadSeg, segT, roadT);
if( elem.type == RoadManager::ET_INTERSECTION )
{
useIntersection = true;
in = (Intersection*) elem.elem;
}
}
else
{
rAssert( mVehicleType == VT_AI );
VehicleAI* vAI = GetVehicleCentral()->GetVehicleAI( this );
rAssert( vAI );
vAI->GetLastPathInfo( elem, roadSeg, segT, roadT );
}
}
else
{
GetIntersectManager()->FindClosestRoad( currentVehiclePosition, radius, roadSeg, dummy );
}
if( useIntersection )
{
rAssert( in );
in->GetLocation( newVehiclePosition );
// bump up position
float h = this->GetRestHeightAboveGround();
h += 1.0f;
newVehiclePosition.y += h;
ang = FacingIntoRad(mVehicleFacing);
}
else
{
if(roadSeg)
{
// from the road
// want two points - centre of baseline and center of top
rmt::Vector corner0, corner1, corner2, corner3;
roadSeg->GetCorner(0, corner0);
roadSeg->GetCorner(1, corner1);
roadSeg->GetCorner(2, corner2);
roadSeg->GetCorner(3, corner3);
rmt::Vector base = corner0;
base.Add(corner3);
base.Scale(0.5f);
rmt::Vector top = corner1;
top.Add(corner2);
top.Scale(0.5f);
rmt::Vector centerline = top;
centerline.Sub(base);
// we want the vehicle's new orientation to be along this vector, and the position
// to be the projection (shortest distance) of vehicle's position onto this line
rmt::Vector centerlineDir = centerline;
rAssert(centerline.Magnitude() > 0.0f);
centerlineDir.NormalizeSafe();
rmt::Vector newVehicleFacing = centerlineDir;
//If this is NOT SuperSprint, do this test. Otherwise the car will
//always face along the road.
if ( !GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
{
if(mVehicleFacing.DotProduct(centerlineDir) > 0.0f)
{
// leave as is
}
else
{
newVehicleFacing.Scale(-1.0f);
}
}
ang = FacingIntoRad(newVehicleFacing);
// for position, we just use Dusit's magical function
// the line segment at which this closest point occurs
//float FindClosestPointOnLine( const rmt::Vector& start,
// const rmt::Vector& end,
// const rmt::Vector& p,
// rmt::Vector& closestPt );
FindClosestPointOnLine(base, top, currentVehiclePosition, newVehiclePosition);
// make sure there's no traffic in the way at that point
rmt::Sphere s;
s.centre = newVehiclePosition;
//s.radius = 10.0f;
s.radius = this->mWheelBase * 2.0f;
TrafficManager::GetInstance()->ClearTrafficInSphere(s);
// bump up position
float h = this->GetRestHeightAboveGround();
h += 1.0f;
newVehiclePosition.y += h;
//GetVehicleCentral()->ClearSpot(newVehiclePosition, this->mWheelBase * 2.0f, this);
}
else
{
// this is the old debug version
//
// literally resets on spot.
newVehiclePosition = currentVehiclePosition;
newVehiclePosition.y += 1.0f;
ang = FacingIntoRad(mVehicleFacing);
}
}
if(moveCarOntoRoad)
{
rmt::Matrix m;
m.Identity();
if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
{
if ( GetSSM()->IsTrackReversed() )
{
ang += rmt::PI; //Turn, turn around
}
}
//m.FillRotateXYZ( 0.0f, mResetFacingRadians, 0.0f );
m.FillRotateXYZ( 0.0f, ang, 0.0f );
m.FillTranslate(newVehiclePosition);
int i;
for(i = 0; i < 4; i++)
{
mWheels[i]->Reset();
}
SetTransform( m );
}
ResetFlagsOnly(resetDamage);
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForVehicle( this );
if ( playerAvatar )
{
GetSuperCamManager()->GetSCC( playerAvatar->GetPlayerId() )->DoCameraCut();
}
mAlreadyCalledAutoResetOnSpot = false;
}
//=============================================================================
// Vehicle::GetFacingInRadians
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::GetFacingInRadians()
{
return FacingIntoRad(mVehicleFacing);
}
//=============================================================================
// Vehicle::FacingIntoRad
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::FacingIntoRad(rmt::Vector facing)
{
float test = rmt::ATan2(facing.x, facing.z);
if(rmt::IsNan(test))
{
return 0.0f;
}
return test;
//return 0.0f;
}
//=============================================================================
// Vehicle::SetGas
//=============================================================================
// Description: Comment
//
// Parameters: (float gas)
//
// Return: void
//
//=============================================================================
void Vehicle::SetGas(float gas)
{
if(!mVehicleDestroyed && !mGasBrakeDisabled)
{
mGas = gas;
//if(mGas > 0.8f)
//{
// mGas = 1.0f;
//}
}
else
{
mGas = 0.0f;
}
}
//=============================================================================
// Vehicle::SetBrake
//=============================================================================
// Description: Comment
//
// Parameters: (float brake)
//
// Return: void
//
//=============================================================================
void Vehicle::SetBrake(float brake)
{
if(!mVehicleDestroyed && !mGasBrakeDisabled)
{
// normal setting of value
// the max we will set brake to is inverse of percentage of top speed
float proj = mVelocityCM.DotProduct(mVehicleFacing);
if(proj > 0.0f)
{
// only do all this crap if going forward, and using brake to slow down, not go in reverse
float maxbrake = 1.0f - this->mPercentOfTopSpeed;
if(maxbrake < 0.0f)
{
maxbrake = 0.0f;
}
if(brake > maxbrake)
{
brake = maxbrake;
}
if(mGas > 0.1f)
{
if(brake > mGas)
{
brake = mGas - 0.1f;
}
}
if(brake < 0.0f)
{
brake = 0.0f;
}
}
mBrake = brake;
}
else
{
mBrake = 0.0f;
}
/*
if(mBrake > 0.0f && !mBrakeLightsOn)
{
mGeometryVehicle->ShowBrakeLights();
mBrakeLightsOn = true;
}
if(mBrake == 0.0f && mBrakeLightsOn)
{
mGeometryVehicle->HideBrakeLights();
mBrakeLightsOn = false;
}
*/
if(mBrake > 0.0f && !mDontShowBrakeLights)
{
if(IsInReverse())
{
if(!mReverseLightsOn)
{
mGeometryVehicle->ShowReverseLights();
mReverseLightsOn = true;
}
if(mBrakeLightsOn)
{
mGeometryVehicle->HideBrakeLights();
mBrakeLightsOn = false;
}
}
else
{
if(mReverseLightsOn)
{
mGeometryVehicle->HideReverseLights();
mReverseLightsOn = false;
}
if(!mBrakeLightsOn)
{
mGeometryVehicle->ShowBrakeLights();
mBrakeLightsOn = true;
}
}
}
if(mBrake == 0.0f || mDontShowBrakeLights)
{
if(mBrakeLightsOn)
{
mGeometryVehicle->HideBrakeLights();
mBrakeLightsOn = false;
}
if(mReverseLightsOn)
{
mGeometryVehicle->HideReverseLights();
mReverseLightsOn = false;
}
}
}
//=============================================================================
// Vehicle::SetEBrake
//=============================================================================
// Description: Comment
//
// Parameters: (float ebrake)
//
// Return: void
//
//=============================================================================
void Vehicle::SetEBrake(float ebrake, float dt)
{
const float magicEBrakeTimerLimit = 0.5f; // seconds
if(ebrake > 0.0f)
{
//mEBrake = ebrake;
if(mEBrake == 0.0f)
{
// ie. the button just went down, reset the timer
mEBrakeTimer = 0.0f;
}
// hack for digital
mEBrake = 1.0f;
}
else if (mEBrakeTimer < magicEBrakeTimerLimit)
{
// leave EBrake at last set value?
}
else
{
mEBrake = 0.0f;
mEBrakeTimer = 0.0f;
}
mEBrakeTimer += dt;
// mEBrake = ebrake; old implementation of this method
}
//=============================================================================
// Vehicle::SetWheelTurnAngle
//=============================================================================
// Description: Comment
//
// Parameters: (float wheelTurnAngle, bool doNotModifyInputValue)
//
// Return: void
//
//=============================================================================
void Vehicle::SetWheelTurnAngle(float wheelTurnAngle, bool doNotModifyInputValue, float dt)
{
mUnmodifiedInputWheelTurnAngle = wheelTurnAngle; // just store for later use - things like doughnuts...
// modify the h/w input to be nicer
//char dbinfo[64];
//sprintf(dbinfo, "input wheel turn angle: %.2f\n", wheelTurnAngle);
//DEBUGINFO_ADDSCREENTEXT( dbinfo );
//doNotModifyInputValue = true; //debug test
if(doNotModifyInputValue) // please ignore the name of the param :)
{
// this is a wheel
// scale up input value
//const float magicWheelScaleUp = 1.2f;
//const float magicWheelScaleUp = 10.2f;
const float magicWheelScaleUp = 2.75f;
//const float magicWheelScaleUp = 1.5f;
wheelTurnAngle *= magicWheelScaleUp;
if(wheelTurnAngle < -1.0f)
{
wheelTurnAngle = -1.0f;
}
if(wheelTurnAngle > 1.0f)
{
wheelTurnAngle = 1.0f;
}
}
if (1) // plum wants high speed steering drop always
{
/*
if(rmt::Fabs(wheelTurnAngle) < mSteeringInputThreshold)
{
wheelTurnAngle *= mSteeringPreSlope;
}
else
{
float steeringPostSlope = (1.0f - (mSteeringInputThreshold * mSteeringPreSlope)) / (1.0f - mSteeringInputThreshold);
float pieceBelow = mSteeringInputThreshold * mSteeringPreSlope;
float pieceAbove = (rmt::Fabs(wheelTurnAngle) - mSteeringInputThreshold) * steeringPostSlope;
float wheelTurnAngleMag = pieceBelow + pieceAbove;
wheelTurnAngle = wheelTurnAngleMag * rmt::Sign(wheelTurnAngle);
}
*/
// factor in sensitivity drop as speed increases
//
// simple slope-intercept formula
float slope = mDesignerParams.mDpHighSpeedSteeringDrop - 1.0f; // divided by 1.0 implied
float yintercept = 1.0f;
// input x is mPercentOfTopSpeed
float value = slope * mPercentOfTopSpeed + yintercept;
// this should still be a positive number
// use to scale the angle (ie. formulat initialy setup for input value of 1.0)
if(value < 0.0f)
{
value = 0.0f; // should never hit this
}
wheelTurnAngle *= value;
}
//else
//{
// float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle);
// mWheelTurnAngle = wheelTurnAngle * maxWheelTurnInRadians;
//}
// if we're below some low speed.....??? 60 kmh... have a time lag to reach the desired angle to reduce twichyness
const float thresholdLowSpeed = 90.0f; // kmh
const float timeAtZero = 0.3f;
if(/*this->mSpeedKmh < thresholdLowSpeed && */ this->mVehicleType == VT_USER && !doNotModifyInputValue)
{
// closer to zero, the longer the lag.
// at 60 it would take no time.
// standard linear drop:
//float secondsToChangeOneUnitAtCurrentSpeed = (1.0f - mSpeedKmh / thresholdLowSpeed) * timeAtZero;
// parabolic drop:
float secondsToChangeOneUnitAtCurrentSpeed = ((1.0f - rmt::Sqr(mSpeedKmh / thresholdLowSpeed)) * timeAtZero);
// new cap for plum
if(secondsToChangeOneUnitAtCurrentSpeed < 0.15f)
{
secondsToChangeOneUnitAtCurrentSpeed = 0.15f;
}
rAssert(secondsToChangeOneUnitAtCurrentSpeed > 0.0f);
if(secondsToChangeOneUnitAtCurrentSpeed > 0.0f) // 2 lines of defense
{
float unitsPerSecond = 1.0f / secondsToChangeOneUnitAtCurrentSpeed;
// this is the max amount we can change in this frame
float unitsInThisFrame = unitsPerSecond * dt;
// at this point wheelTurnAngle is still our 'target', between 0 and 1
// we can move a maximum of 'unitsInThisFrame' from mWheelTurnAngleInputValue (last value) towards our target, wheelTurnAngle
if(wheelTurnAngle > mWheelTurnAngleInputValue)
{
if(wheelTurnAngle > (mWheelTurnAngleInputValue + unitsInThisFrame))
{
mWheelTurnAngleInputValue += unitsInThisFrame;
}
else
{
mWheelTurnAngleInputValue = wheelTurnAngle;
}
}
else if(wheelTurnAngle < mWheelTurnAngleInputValue)
{
if(wheelTurnAngle < (mWheelTurnAngleInputValue - unitsInThisFrame))
{
mWheelTurnAngleInputValue -= unitsInThisFrame;
}
else
{
mWheelTurnAngleInputValue = wheelTurnAngle;
}
}
// else if they're == do nothing
}
}
else
{
mWheelTurnAngleInputValue = wheelTurnAngle;
}
// low speed uturn thingy
if(this->mSpeedKmh < 50.0f && this->mVehicleType == VT_USER)
{
// allow the max wheel turn angel to be higher
// ?? try current + 10?
// normal case - use limit set by designers
float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle + 10.0f);
//mWheelTurnAngle = wheelTurnAngle * maxWheelTurnInRadians;
mWheelTurnAngle = mWheelTurnAngleInputValue * maxWheelTurnInRadians;
}
else
{
// normal case - use limit set by designers
float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle);
//mWheelTurnAngle = wheelTurnAngle * maxWheelTurnInRadians;
mWheelTurnAngle = mWheelTurnAngleInputValue * maxWheelTurnInRadians;
}
int i;
for(i = 0; i < 4; i++)
{
if(mWheels[i]->mSteerWheel)
{
mWheels[i]->mWheelTurnAngle = mWheelTurnAngle;
}
}
//sprintf(dbinfo, "modified wheel turn angle: %.2f\n", mWheelTurnAngle);
//DEBUGINFO_ADDSCREENTEXT( dbinfo );
}
//=============================================================================
// Vehicle::SetWheelTurnAngleDirectlyInRadiansForDusitOnly
//=============================================================================
// Description: Comment
//
// Parameters: (float rad)
//
// Return: void
//
//=============================================================================
void Vehicle::SetWheelTurnAngleDirectlyInRadiansForDusitOnly(float rad)
{
float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle);
mWheelTurnAngle = rad;
if(rad > maxWheelTurnInRadians)
{
mWheelTurnAngle = maxWheelTurnInRadians;
}
if(rad < -maxWheelTurnInRadians)
{
mWheelTurnAngle = -maxWheelTurnInRadians;
}
int i;
for(i = 0; i < 4; i++)
{
if(mWheels[i]->mSteerWheel)
{
mWheels[i]->mWheelTurnAngle = mWheelTurnAngle;
}
}
}
//=============================================================================
// Vehicle::SetReverse
//=============================================================================
// Description: Comment
//
// Parameters: (float reverse)
//
// Return: void
//
//=============================================================================
void Vehicle::SetReverse(float reverse)
{
mReverse = reverse;
if(mReverse > 0.0f)
{
//rAssertMsg(0, "see greg");
//int stophere = 1;
}
}
//=============================================================================
// Vehicle::PreSubstepUpdate
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::PreSubstepUpdate(float dt)
{
float dirSpeedKmh = mSpeedKmh * ( (mVelocityCM.DotProduct(mVehicleFacing) > 0.0f) ? 1.0f : -1.0f);
float deltaKmh = dirSpeedKmh - mLastSpeedKmh;
mAccelMss = (deltaKmh / 3600.0f) / (dt / 1000.0f);
mLastSpeedKmh = mSpeedKmh;
mBottomedOutThisFrame = false;
/*
if(mWaitingToSwitchToOutOfCar && mCollisionAreaIndex != -1)
{
mOutOfCarSwitchTimer += dt;
if(mOutOfCarSwitchTimer > 2.0f)
{
mOutOfCarSwitchTimer = 0.0f;
mWaitingToSwitchToOutOfCar = false;
SetOutOfCarSimState();
}
}
*/
mVelocityCMLag = mVelocityCM;
mPositionCMLag = this->rPosition();
mVehicleLocomotion->PreSubstepUpdate();
}
//=============================================================================
// Vehicle::PreCollisionPrep
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::PreCollisionPrep(float dt, bool firstSubstep)
{
BEGIN_PROFILE("mGeometryVehicle->Update")
mGeometryVehicle->Update(dt); // seems to work fine before
END_PROFILE("mGeometryVehicle->Update")
// new
// TODO - how to improve the interfaces
BEGIN_PROFILE("mVehicleLocomotion->PreCollisionPrep")
mVehicleLocomotion->PreCollisionPrep(firstSubstep);
END_PROFILE("mVehicleLocomotion->PreCollisionPrep")
// used by physicslocomotion in suspension force calculation
// but set by redbrickcollisionsolveragent - yuck
mDamperShouldNotPullDown[0] = false;
mDamperShouldNotPullDown[1] = false;
mDamperShouldNotPullDown[2] = false;
mDamperShouldNotPullDown[3] = false;
// reset this flag
//mHitJoint = -1;
}
//=============================================================================
// Vehicle::SetNoDamperDownFlagOnWheel
//=============================================================================
// Description: Comment
//
// Parameters: (int wheelIndex)
//
// Return: void
//
//=============================================================================
void Vehicle::SetNoDamperDownFlagOnWheel(int wheelIndex)
{
mDamperShouldNotPullDown[wheelIndex] = true; // just for this frame
}
//=============================================================================
// Vehicle::CalculateSuspensionLocationAndVelocity
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::CalculateSuspensionLocationAndVelocity()
{
// TODO - is this ok to actually change what we call world space suspension point for this frame if we're airborn???
// do airborn calculation here?
// only look at wheels 2,3
// recall - right now this is getting called from physicslocomotion::update, after collision detection
//if( !(mWheels[2]->mWheelInCollision) && !(mWheels[3]->mWheelInCollision))
if( !(mWheels[2]->mWheelInCollision) || !(mWheels[3]->mWheelInCollision))
{
// consider this airbor
// even if we're just cresting hill.
mSteeringWheelsOutOfContact = true;
}
else
{
mSteeringWheelsOutOfContact = false;
}
// air born test
int i;
int count = 0;
for(i = 0; i < 4; i++)
{
if(!(mWheels[i]->mWheelInCollision))
{
count++;
}
}
if(count > 3)
{
mAirBorn = true;
mWasAirborn = true;
mWasAirbornTimer = 0.0f;
}
else
{
mAirBorn = false;
}
// TODO - how 'bout flipped?
for(i = 0; i < 4; i++)
{
mSuspensionWorldSpacePoints[i] = mSuspensionRestPoints[i];
//if(1)//mSteeringWheelsOutOfContact)
if(mSteeringWheelsOutOfContact)// || mVehicleState == VS_NORMAL)
{
// TODO - revisit this - I don't like it
// ok, this was a serious fuck up
// revisit during the tipping likelihood thing
//mSuspensionWorldSpacePoints[i].y = mCMOffset.y;
}
mSuspensionWorldSpacePoints[i].Transform(mTransform);
mSimStateArticulated->GetVelocity(mSuspensionWorldSpacePoints[i], mSuspensionPointVelocities[i]);
}
// note:
// mSteeringWheelsOutOfContact is a bit of a misnomer
// do a test here for real airborn, all 4 wheels off, and vehicle upright
//
// this is the only place where mOkToCrashLand will get set to true
//
/*
if( !(mWheels[0]->mWheelInCollision) && !(mWheels[1]->mWheelInCollision) &&
!(mWheels[2]->mWheelInCollision) && !(mWheels[3]->mWheelInCollision) &&
mVehicleUp.y > 0.0f)
{
mOkToCrashLand = true;
}
*/
// TODO - around here somewhere is the place to calculate and set a big-air event
}
//=============================================================================
// Vehicle::JumpOnHorn
//=============================================================================
// Description: Comment
//
// Parameters: (float test)
//
// Return: void
//
//=============================================================================
void Vehicle::JumpOnHorn(float test)
{
if(!(this->mAirBorn))
{
if(GetSimState()->GetControl() == sim::simAICtrl)
{
GetSimState()->SetControl(sim::simSimulationCtrl);
}
rmt::Vector boost = mVehicleFacing;
boost.y += 2.0f;
static float hack = 5.0f;
//const float hack = 2.0f;
boost.Scale(test * hack);
rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
linearVel.Add(boost);
if(GetSimState()->GetControl() == sim::simAICtrl)
{
GetSimState()->SetControl(sim::simSimulationCtrl);
}
}
}
void Vehicle::TurboOnHorn()
{
/*static*/ const float SECONDS_TURBO_RECHARGE = 0.5f;
/*static*/ const float TURBO_SPEED_MPS = 10.0f;
if( mSecondsTillNextTurbo <= 0.0f )
{
if( mNumTurbos > 0 )
{
// apply turbo speed
if(GetSimState()->GetControl() == sim::simAICtrl)
{
GetSimState()->SetControl(sim::simSimulationCtrl);
}
rmt::Vector turbo = mVehicleFacing * TURBO_SPEED_MPS;
rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
linearVel.Add( turbo );
/*
// TODO:
// Maybe play a sound effect? Pass in the vehicle pointer? Ask Esan
::GetEventManager()->TriggerEvent( EVENT_USE_NITRO, this );
*/
// decrement number of turbos
mNumTurbos--;
// reset mSecondsTillNextTurbo
mSecondsTillNextTurbo = SECONDS_TURBO_RECHARGE;
}
}
}
//=============================================================================
// Vehicle::Update
//=============================================================================
// Description: Comment
//
// Parameters: (float dt)
//
// Return: void
//
//=============================================================================
void Vehicle::Update(float dt)
{
// Update gas, if the change in gas is significant
if(mLocoSwitchedToPhysicsThisFrame)
{
this->PreCollisionPrep(dt, true);
}
mLocoSwitchedToPhysicsThisFrame = false;
mDeltaGas = (mGas - mLastGas) / dt;
mLastGas = mGas;
// update turbo wind down time
if( mSecondsTillNextTurbo > 0.0f )
{
mSecondsTillNextTurbo -= dt;
}
mNoDamageTimer -= dt;
if(mNoDamageTimer < 0.0f)
{
mNoDamageTimer = 0.0f;
}
// TODO - before or after physics?
//mGeometryVehicle->Update(dt); // seems to work fine before
// don't need this if traffic!
//
// actually, need a different one if this is traffic
//CalculateSuspensionLocationAndVelocity(); // TODO - ? shouldn't this also be done before FetchingWheelTerrainCollisionInfo
// moved to PhysicsLocomotion::PreUpdate
// break up of work into pre and post update, outside of update, is a bit arbitrary
// I suppose
mSimStateArticulated->StoreJointState(dt);
mVehicleLocomotion->PreUpdate();
if(mVehicleType != VT_AI && GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT)
{
if(mCollisionLateralResistanceDropFactor < 1.0f)
{
mCollisionLateralResistanceDropFactor += dt;
if(mCollisionLateralResistanceDropFactor > 1.0f)
{
mCollisionLateralResistanceDropFactor = 1.0f;
}
}
}
else
{
mCollisionLateralResistanceDropFactor = 1.0f;
}
mVehicleLocomotion->Update(dt);
// basically - the only thing that is different after this is the mTransform,
// whether traffic or physics
//
// ?? maybe traffic should just set mTransform directly
// also set mSpeed???
mVehicleLocomotion->PostUpdate();
// TODO - trust these are normalized?
mVehicleFacing = mTransform.Row(2);
mVehicleUp = mTransform.Row(1);
mVehicleTransverse = mTransform.Row(0);
// is this gonna hurt framrate?
mVehicleFacing.Normalize();
mVehicleUp.Normalize();
mVehicleTransverse.Normalize();
// this is good
// can leave as is for both traffic and physics
//
// just have to make sure that mSuspensionPointVelocities are correct
// and it will use mSteeringWheelAngle if set.
UpdateWheelRenderingInfo(dt);
SetGeometryVehicleWheelSmokeLevel();
if(mBurnoutLevel > 0.0f)
{
if ( !mDoingBurnout )
{
GetEventManager()->TriggerEvent( EVENT_BURNOUT );
mDoingBurnout = true;
}
}
else
{
if ( mDoingBurnout )
{
GetEventManager()->TriggerEvent( EVENT_BURNOUT_END );
mDoingBurnout = false;
}
}
mSpeedKmh = mSpeed * 3.6f; // for watcher...
int i;
for (i = 0; i < mPoseEngine->GetPassCount(); i++)
{
mPoseEngine->Advance(i, dt);
mPoseEngine->Update(i);
}
// calculate forced door opening as a result of character getting into/out of car
// TODO : this should probably be doen in the poseengine , but I don't yet understand
// enough about what poseengine/posedriver are actually doing to do that
CalcDoors();
// cap on huge impulses
const float hugeSpeed = 200.0f; // that's about 720 kmh
if(this->mSpeed > hugeSpeed)
{
// this is really fucking bad
//rAssertMsg(0,"tell greg how you did this");
mSimStateArticulated->ResetVelocities();
mSimStateArticulated->GetSimulatedObject()->ResetCache();
this->ResetOnSpot(false);
// mVehicleLocomotion->PostUpdate();
}
if(1)//mLoco == VL_PHYSICS)
{
mSimStateArticulated->UpdateJointState(dt);
}
// for debugging:
// we want the collision objects to draw in the correct places
// TODO - want to call this here! every frame!!
CollisionObject* collObj = mSimStateArticulated->GetCollisionObject();
collObj->Update();
DSGUpdateAndMove();
//--------------------------
// do some gear and rpm shit
//--------------------------
UpdateGearAndRPM();
// Update the trigger volume.
mpTriggerVolume->SetTransform( mTransform);
mWasAirbornTimer += dt;
if(mWasAirbornTimer > 1.0f)
{
mWasAirborn = false;
}
if(mBottomedOutThisFrame && mWasAirborn)
{
// try this here for Esan
mWasAirborn = false;
if( mTerrainType == TT_Road || mTerrainType == TT_Metal || mTerrainType == TT_Gravel )
{
GetSparkleManager()->AddBottomOut( GetPosition() );
}
GetEventManager()->TriggerEvent(EVENT_VEHICLE_SUSPENSION_BOTTOMED_OUT, (void*)this);
//rDebugPrintf("vehicle bottomed out this frame \n");
}
}
//=============================================================================
// Vehicle::GetGroundY
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::GetGroundY()
{
// for James, for the shadows...
if(this->mLoco == VL_PHYSICS)
{
// need this value for either case
float avg = 0.0f;
avg = mPhysicsLocomotion->mTerrainIntersectCache[0].planePosn.y +
mPhysicsLocomotion->mTerrainIntersectCache[1].planePosn.y +
mPhysicsLocomotion->mTerrainIntersectCache[2].planePosn.y +
mPhysicsLocomotion->mTerrainIntersectCache[3].planePosn.y;
avg *= 0.25;
if(mAirBorn) // all four wheels out of contact
{
// in this situation, give the average of the terrain intersect caches?
return avg;
}
else
{
// here at least one wheel is in collision
// so... give the average y of all the wheels that are in collision
float wheelavg = 0.0f;
float count = 0.0f;
int i;
for(i = 0; i < 4; i++)
{
if(mWheels[i]->mWheelInCollision)
{
count = count + 1.0f;
poser::Pose* pose = mPoseEngine->GetPose();
poser::Joint* joint = pose->GetJoint(mWheelToJointIndexMapping[i]);
rmt::Vector trans = joint->GetWorldTranslation();
wheelavg += ( trans.y - mWheels[ i ]->mRadius );
}
}
if(count > 0.0f)
{
return wheelavg / count;
}
return avg;
}
}
else
{
float y = mTransform.m[3][1];
float diff = GetRestHeightAboveGround();
y -= diff;
return y;
}
// should never get here
return 0.0f;
}
// I'm hiding. I don't have a comment header. I'm a fugitive. I am hunted.
//
// that's ok Dusit, here you go:
//=============================================================================
// Vehicle::PostSubstepUpdate
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::PostSubstepUpdate(float dt)
{
//-------------------
// reset input values
//-------------------
// mGas = 0.0f; // for tony, for hud
// mBrake = 0.0f; ? safe to do this - want the IsInReverse method to work afterall...
//mWheelTurnAngle = 0.0f;
mReverse = 0.0f;
//mEBrake = 0.0f;
// skid setting
if( mVehicleID == VehicleEnum::HUSKA )
{
rmt::Vector vel = mVelocityCM;
if( ( rmt::Abs( vel.x ) + rmt::Abs( vel.z ) > 0.5f ) && ( mPhysicsLocomotion ) )
{
vel.y = 0.0f;
vel.Normalize();
// Make a sound effects call here.
for( int wi = 0; wi < 2; ++wi )
{
eTerrainType tt = mPhysicsLocomotion->mTerrainIntersectCache[ wi ].mTerrainType;
if( tt == TT_Road || tt == TT_Metal || tt == TT_Gravel )
{
rmt::Vector pos = mSuspensionRestPoints[ wi ];
pos.y -= mWheels[ wi ]->mRadius;
pos.y += mWheels[ wi ]->mYOffset;
GetTransform().Transform( pos, &pos );
GetSparkleManager()->AddSparks( pos, vel, 0.1f );
}
}
}
}
if(!mNoSkid)
{
//if(mSkidLevel > 0.0f)// && mVehicleType == VT_USER)
if(mLoco == VL_PHYSICS)
{
int i;
if(mBurnoutLevel > 0.0f)
{
for(i = 0; i < 2; i++)
{
if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
{ // need to fetch ground plane normal also
rAssert(mPhysicsLocomotion);
rmt::Vector normal = mPhysicsLocomotion->mIntersectNormalUsed[i];
mGeometryVehicle->SetSkidValues(i, mSkidLevel, normal, mPhysicsLocomotion->mTerrainIntersectCache[i].mTerrainType );
}
}
}
else
{
int numWheels = mNoFrontSkid ? 2 : 4; // We want to determine how many wheels
// generate skids, taking advantage of the fact that wheels 0 and 1 are always the
// back wheels.
for(i = 0; i < numWheels; i++)
{
if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
{
rAssert(mPhysicsLocomotion);
rmt::Vector normal = mPhysicsLocomotion->mIntersectNormalUsed[i];
mGeometryVehicle->SetSkidValues(i, mSkidLevel, normal, mPhysicsLocomotion->mTerrainIntersectCache[i].mTerrainType );
}
}
}
}
mGeometryVehicle->UpdateSkids();
}
int i;
int count = 0;
for(i = 0; i < 4; i++)
{
if(mWheels[i]->mWheelInCollision)
{
count++;
}
}
// only want to do the following two tests if we are the avatar's car
//
// unless, we are supersprint, then we want to do it for all cars, with no fade to black...
if(GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() == this ||
GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT)
{
if(count == 0 && GetWorldPhysicsManager()->mWorldUp.DotProduct(mVehicleUp) < 0.0f)
{
float linear = mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude());
float angular = mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude());
// flipped
if(rmt::Fabs(linear) < REST_LINEAR_TOL * 3.0f && rmt::Fabs(angular) < REST_ANGULAR_TOL && !mAlreadyCalledAutoResetOnSpot)
{
// put it at rest
// reset
if(GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT)
{
this->ResetOnSpot(false);
}
else
{
GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >(this) );
}
mAlreadyCalledAutoResetOnSpot = true;
}
}
// other on side reset test:
const float lowspeed = 1.0f;
if(mSpeed < lowspeed)
{
rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
float tip = mVehicleUp.DotProduct(up);
//float cos85 = 0.0872f;
float test = 0.2f;
if(tip < test)
{
// anytime we're in here we should incrememnt the timer
mStuckOnSideTimer += dt;
if(mStuckOnSideTimer > 1.0f && !mAlreadyCalledAutoResetOnSpot) // yeah for magic numbers!
{
mStuckOnSideTimer = 0.0f; /// this is done in the reset anyway, but easier to see like this
if(GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT)
{
this->ResetOnSpot(false);
}
else
{
GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >(this) );
}
mAlreadyCalledAutoResetOnSpot = true;
}
}
else
{
// just in case
mStuckOnSideTimer = 0.0f;
}
}
else
{
// not satisfied the on-side test so reset stuckonsidetimer
mStuckOnSideTimer = 0.0f;
}
}
// revisit this
//
// with new husk system all you have to do here for all types of cars is play an explosion
//
// that should probably move to where the event is fired - so that we don't swap out the car and replace with husk before this?
if(mVehicleID != VehicleEnum::HUSKA)
{
if( mVehicleDestroyed && !mAlreadyPlayedExplosion )
{
// Lets detach any objects from this vehicle on explosion
DetachCollectible( rmt::Vector( 0, 0, 0 ), true );
if( this->mVehicleType == VT_USER || GetGameplayManager()->mIsDemo )
{
mDamageOutResetTimer += dt;
if(mDamageOutResetTimer > 3.0f)
{
// kaboom
PlayExplosionEffect();
mAlreadyPlayedExplosion = true; // this will get reset immediately in ResetOnSpot
if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
{
ResetOnSpot( true );
}
else
{
// appropriate manager should catch this and swap in husk
GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED, (void*)this);
}
}
}
else
{
mDamageOutResetTimer += dt;
if( GetGameplayManager()->GetGameType() == GameplayManager::GT_NORMAL ||
mDamageOutResetTimer > 3.0f )
{
// kaboom
PlayExplosionEffect();
mAlreadyPlayedExplosion = true; // this will get reset immediately in ResetOnSpot
if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
{
ResetOnSpot( true );
}
else
{
// appropriate manager should catch this and swap in husk
GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED, (void*)this);
if(mWasHitByVehicleType != VT_AI)
{
GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED_BY_USER, (void*)this);
}
}
}
}
}
}
/*
if(mDamageType == VDT_AI && mVehicleDestroyed && !mAlreadyPlayedExplosion)
{
// just kaboom and it sits there dead...... for now
PlayExplosionEffect();
mAlreadyPlayedExplosion = true;
}
if(mDamageType == VDT_TRAFFIC && mVehicleDestroyed && !mAlreadyPlayedExplosion && this->mVehicleID != VehicleEnum::HUSKA)
{
PlayExplosionEffect();
mAlreadyPlayedExplosion = true;
// Dusit here...
// It seems that the way we set mDamageType to VDT_TRAFFIC doesn't necessarily
// imply that this is a traffic car. SwapInTrafficHusk is only safe to call if
// the car is actually a traffic car (owned and initialized by TrafficManager)
// So... if it's not a traffic car, we won't call SwapInTrafficHusk... It will
// just sit there after explosions... for now... just like what's done for VDT_AI
//
if( this->mVehicleType == VT_TRAFFIC )
{
TrafficManager::GetInstance()->SwapInTrafficHusk(this);
}
}
*/
// hmm... this might be the place to try moving the characters?
BounceCharacters(dt);
}
//=============================================================================
// Vehicle::BounceCharacters
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::BounceCharacters(float dt)
{
if(mVehicleType == VT_USER)
{
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
if(sDoBounce && (playerAvatar->GetCharacter()->GetStateManager()->GetState() == CharacterAi::INCAR) && playerAvatar->GetVehicle() == this)
{
mYAccelForSeatingOffset = (mVelocityCM.y - mVelocityCMLag.y) / dt;
// seems to hover around -1.0f to 1.0f a lot - goes up to 8 or 10 occassionally
if(rmt::Fabs(mYAccelForSeatingOffset) > mBounceAccelThreshold)
{
// want to displace it opposite the accel dir, proportional to the amount?
//static float magicShitScale = 0.25f;
const float magicShitScale = 0.003f;
float amountToDisplace = mYAccelForSeatingOffset * magicShitScale * -1.0f;
if(rmt::Fabs(amountToDisplace) > mMaxBounceDisplacementPerSecond * dt)
{
amountToDisplace = mMaxBounceDisplacementPerSecond * dt * rmt::Sign(amountToDisplace);
}
ApplyDisplacementToCharacters(amountToDisplace);
}
else
{
// want to move back to the middle
MoveCharactersTowardsRestPosition(dt);
}
}
}
}
//=============================================================================
// Vehicle::MoveCharactersTowardsRestPosition
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::MoveCharactersTowardsRestPosition(float dt)
{
// let's say, for first try, move at half max diplacment speed
float rate = 0.5f;
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
if( mpDriver && mpDriver != playerAvatar->GetCharacter() )
{
// there is both a driver and us
//
// sort of clunky to set every frame but whatever...
mNPCRestSeatingPosition = mDriverLocation;
mOurRestSeatingPosition = mPassengerLocation;
// hackey, hackey - need to slide lisa up a little because of dress poking through car
if(mpDriver->IsLisa())
{
mNPCRestSeatingPosition.y += 0.12f;
}
if(playerAvatar->GetCharacter()->IsLisa())
{
mOurRestSeatingPosition.y += 0.12f;
}
// us
Character* us = playerAvatar->GetCharacter();
choreo::Puppet* ourPuppet = us->GetPuppet();
const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
rmt::Vector newOurPuppetPosition = ourPuppetPosition;
// compare rest pos
float diff = newOurPuppetPosition.y - mOurRestSeatingPosition.y;
float maxchange = rate * dt * mMaxBounceDisplacementPerSecond;
if(rmt::Fabs(diff) <= maxchange)
{
// easy - back at rest
newOurPuppetPosition.y = mOurRestSeatingPosition.y;
ourPuppet->SetPosition(newOurPuppetPosition);
}
else if(diff < 0.0f)
{
// add to it
newOurPuppetPosition.y += maxchange;
ourPuppet->SetPosition(newOurPuppetPosition);
}
else
{
// subtract
newOurPuppetPosition.y -= maxchange;
ourPuppet->SetPosition(newOurPuppetPosition);
}
// driver
choreo::Puppet* npcPuppet = mpDriver->GetPuppet();
const rmt::Vector& npcPuppetPosition = npcPuppet->GetPosition();
rmt::Vector newNPCPuppetPosition = npcPuppetPosition;
diff = newNPCPuppetPosition.y - mNPCRestSeatingPosition.y;
//maxchange = rate * dt * mMaxBounceDisplacementPerSecond;
if(rmt::Fabs(diff) <= maxchange)
{
// easy - back at rest
newNPCPuppetPosition.y = mNPCRestSeatingPosition.y;
npcPuppet->SetPosition(newNPCPuppetPosition);
}
else if(diff < 0.0f)
{
// add to it
newNPCPuppetPosition.y += maxchange;
npcPuppet->SetPosition(newNPCPuppetPosition);
}
else
{
// subtract
newNPCPuppetPosition.y -= maxchange;
npcPuppet->SetPosition(newNPCPuppetPosition);
}
}
else
{
// if we're the driver... or if no driver (so we must be the driver)
mOurRestSeatingPosition = mDriverLocation;
if(playerAvatar->GetCharacter()->IsLisa())
{
mOurRestSeatingPosition.y += 0.12f;
}
// just us drivign?
Character* us = playerAvatar->GetCharacter();
choreo::Puppet* ourPuppet = us->GetPuppet();
const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
rmt::Vector newOurPuppetPosition = ourPuppetPosition;
// compare rest pos
float diff = newOurPuppetPosition.y - mOurRestSeatingPosition.y;
float maxchange = rate * dt * mMaxBounceDisplacementPerSecond;
if(rmt::Fabs(diff) <= maxchange)
{
// easy - back at rest
newOurPuppetPosition.y = mOurRestSeatingPosition.y;
ourPuppet->SetPosition(newOurPuppetPosition);
}
else if(diff < 0.0f)
{
// add to it
newOurPuppetPosition.y += maxchange;
ourPuppet->SetPosition(newOurPuppetPosition);
}
else
{
// subtract
newOurPuppetPosition.y -= maxchange;
ourPuppet->SetPosition(newOurPuppetPosition);
}
}
}
//=============================================================================
// Vehicle::ApplyDisplacementToCharacters
//=============================================================================
// Description: Comment
//
// Parameters: (float displacement)
//
// Return: void
//
//=============================================================================
void Vehicle::ApplyDisplacementToCharacters(float displacement)
{
// now either we're driving alone or we're a passenger and there's a driver
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
if( mpDriver && mpDriver != playerAvatar->GetCharacter() )
{
// sort of clunky to set every frame but whatever...
mNPCRestSeatingPosition = mDriverLocation;
mOurRestSeatingPosition = mPassengerLocation;
// there is both a driver and us
Character* us = playerAvatar->GetCharacter();
choreo::Puppet* ourPuppet = us->GetPuppet();
const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
rmt::Vector newOurPuppetPosition = ourPuppetPosition;
//newOurPuppetPosition.y += 0.001f;
newOurPuppetPosition.y += displacement;
// test against limit
if(newOurPuppetPosition.y > mOurRestSeatingPosition.y + mBounceLimit)
{
newOurPuppetPosition.y = mOurRestSeatingPosition.y + mBounceLimit;
}
if(newOurPuppetPosition.y < mOurRestSeatingPosition.y - mBounceLimit)
{
newOurPuppetPosition.y = mOurRestSeatingPosition.y - mBounceLimit;
}
ourPuppet->SetPosition(newOurPuppetPosition);
choreo::Puppet* npcPuppet = mpDriver->GetPuppet();
const rmt::Vector& npcPuppetPosition = npcPuppet->GetPosition();
rmt::Vector newNPCPuppetPosition = npcPuppetPosition;
newNPCPuppetPosition.y += displacement;
// test against limit
if(newNPCPuppetPosition.y > mNPCRestSeatingPosition.y + mBounceLimit)
{
newNPCPuppetPosition.y = mNPCRestSeatingPosition.y + mBounceLimit;
}
if(newNPCPuppetPosition.y < mNPCRestSeatingPosition.y - mBounceLimit)
{
newNPCPuppetPosition.y = mNPCRestSeatingPosition.y - mBounceLimit;
}
npcPuppet->SetPosition(newNPCPuppetPosition);
}
else
{
// just us drivign?
mOurRestSeatingPosition = mDriverLocation;
Character* us = playerAvatar->GetCharacter();
choreo::Puppet* ourPuppet = us->GetPuppet();
const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
rmt::Vector newOurPuppetPosition = ourPuppetPosition;
//newOurPuppetPosition.y += 0.001f;
newOurPuppetPosition.y += displacement;
if(newOurPuppetPosition.y > mOurRestSeatingPosition.y + mBounceLimit)
{
newOurPuppetPosition.y = mOurRestSeatingPosition.y + mBounceLimit;
}
if(newOurPuppetPosition.y < mOurRestSeatingPosition.y - mBounceLimit)
{
newOurPuppetPosition.y = mOurRestSeatingPosition.y - mBounceLimit;
}
ourPuppet->SetPosition(newOurPuppetPosition);
}
}
//=============================================================================
// Vehicle::RecordRestSeatingPositionsOnEntry
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::RecordRestSeatingPositionsOnEntry()
{
// same logic as BounceCharacters to decide which of these we need
return;
if(mVehicleType == VT_USER)
{
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
if(playerAvatar->IsInCar() && playerAvatar->GetVehicle() == this)
{
// now either we're driving alone or we're a passenger and there's a driver
if(this->mpDriver)
{
// there is a driver
if(this->mpDriver == playerAvatar->GetCharacter())
{
// don't think we should ever hit this - the mpDriver is an NPC driver
int stophere = 1;
}
else
{
// there is both a driver and us
Character* us = playerAvatar->GetCharacter();
choreo::Puppet* ourPuppet = us->GetPuppet();
const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
mOurRestSeatingPosition = ourPuppetPosition;
choreo::Puppet* npcPuppet = mpDriver->GetPuppet();
const rmt::Vector& npcPuppetPosition = npcPuppet->GetPosition();
mNPCRestSeatingPosition = npcPuppetPosition;
}
}
else
{
// just us drivign?
Character* us = playerAvatar->GetCharacter();
choreo::Puppet* ourPuppet = us->GetPuppet();
const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
mOurRestSeatingPosition = ourPuppetPosition;
}
}
}
}
//=============================================================================
// Vehicle::RestTest
//=============================================================================
// Description: Comment
//
// Parameters: (void)
//
// Return: void
//
//=============================================================================
void Vehicle::RestTest(void)
{
// only traffic rests
if(mVehicleType != VT_TRAFFIC)
{
return;
}
if(!GetSimState() || GetSimState()->GetControl() == sim::simAICtrl)
{
// already at rest
return;
}
// check if smoothed velocities are below tolerance
if((mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude()) < REST_LINEAR_TOL) &&
(mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude()) < REST_ANGULAR_TOL))
{
// put it at rest
// this is done inside the loco-switch
//GetSimState()->SetControl(sim::simAICtrl);
//GetSimState()->ResetVelocities();
}
}
//=============================================================================
// Vehicle::SelfRestTest
//=============================================================================
// Description: Comment
//
// Parameters: (void)
//
// Return: void
//
//=============================================================================
bool Vehicle::SelfRestTest(void)
{
// new version of this for user and ai cars to call on themselves
// during physicslocomotion updates so that at very low speeds they come to a complete stop
if(!GetSimState())
{
rAssert(0);
return false;
}
int i;
int count = 0;
for(i = 0; i < 4; i++)
{
if(mWheels[i]->mWheelInCollision)
{
count++;
}
}
/*
if(count == 0 && GetWorldPhysicsManager()->mWorldUp.DotProduct(mVehicleUp) < 0.0f)
{
// flipped
if((mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude()) < REST_LINEAR_TOL) &&
(mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude()) < REST_ANGULAR_TOL))
{
// put it at rest
// reset
this->ResetOnSpot(false);
}
}
*/
Vehicle* playerVehicle = GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
//Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();
if(mGas < 0.1f && mBrake < 0.2f && count > 2)
{
float dot = this->mVehicleUp.DotProduct(GetWorldPhysicsManager()->mWorldUp);
float linear = mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude());
float angular = mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude());
float ebrakefactor = 1.0f;
if(this->mEBrake == 1.0f)
{
//ebrakefactor = 2.0f;
}
if(mUsingInCarPhysics)
{
if(1)//dot < 0.995f)
{
// steep, so more aggresive test
//if( linear < 2.0f * REST_LINEAR_TOL * ebrakefactor && angular < 2.0f * REST_ANGULAR_TOL * ebrakefactor)
if( linear < 3.0f * REST_LINEAR_TOL * ebrakefactor && angular < 3.0f * REST_ANGULAR_TOL * ebrakefactor)
{
//GetSimState()->SetControl(sim::simAICtrl);
//GetSimState()->ResetVelocities();
if(this->mVehicleType == VT_TRAFFIC && this != playerVehicle && !mCreatedByParkedCarManager)
{
GetSimState()->SetControl(sim::simAICtrl);
GetSimState()->ResetVelocities();
}
else
{
this->ZeroOutXZVelocity();
}
mAtRestAsFarAsTriggersAreConcerned = true;
return true;
}
}
else if( linear < REST_LINEAR_TOL * ebrakefactor && angular < REST_ANGULAR_TOL * ebrakefactor)
{
// put it at rest
//GetSimState()->SetControl(sim::simAICtrl);
//GetSimState()->ResetVelocities();
//this->ZeroOutXZVelocity();
if(this->mVehicleType == VT_TRAFFIC && this != playerVehicle && !mCreatedByParkedCarManager)
{
GetSimState()->SetControl(sim::simAICtrl);
GetSimState()->ResetVelocities();
}
else
{
this->ZeroOutXZVelocity();
}
mAtRestAsFarAsTriggersAreConcerned = true;
return true;
}
}
else
{
// more aggressive test
if( linear < 10.0f * REST_LINEAR_TOL * ebrakefactor && angular < 10.0f * REST_ANGULAR_TOL * ebrakefactor)
{
//GetSimState()->SetControl(sim::simAICtrl);
//GetSimState()->ResetVelocities();
//this->ZeroOutXZVelocity();
if(this->mVehicleType == VT_TRAFFIC && this != playerVehicle && !mCreatedByParkedCarManager)
{
GetSimState()->SetControl(sim::simAICtrl);
GetSimState()->ResetVelocities();
}
else
{
this->ZeroOutXZVelocity();
}
mAtRestAsFarAsTriggersAreConcerned = true;
return true;
}
}
}
else
{
if(0)//this->mVehicleType == VT_TRAFFIC)
{
GetSimState()->SetControl(sim::simSimulationCtrl);
}
}
mAtRestAsFarAsTriggersAreConcerned = false;
return false;
}
//=============================================================================
// Vehicle::ZeroOutXZVelocity
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::ZeroOutXZVelocity()
{
rmt::Vector& linear = mSimStateArticulated->GetLinearVelocity();
rmt::Vector& angular = mSimStateArticulated->GetAngularVelocity();
rmt::Vector newlinear = this->mVehicleUp;
float linearProj = linear.DotProduct(newlinear);
newlinear.Scale(linearProj);
linear = newlinear;
angular.Set(0.0f, 0.0f, 0.0f);
}
//=============================================================================
// Vehicle::SetGeometryVehicleWheelSmokeLevel
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::SetGeometryVehicleWheelSmokeLevel()
{
// just based on burnout level for now
if(mBurnoutLevel > 0.0f)
{
if(this->mSpeedBurstTimer >= 2.5f)
{
// TODO
// replace this with wheel flame
// MKR - replaced. Greg, does this work ok?
// -1 indicates apply to all wheel
mGeometryVehicle->SetWheelSmoke( -1, ParticleEnum::eFireSpray, mBurnoutLevel);
}
else
{
// TODO - if anyone notices or complains, switch this to
// set on a wheel by wheel basis
// for now, just base on James's overall value
//TT_Road, // Default road terrain. Also used for sidewalk. This is default. If not set, it's this.
//TT_Grass, // Grass type terrain most everything else which isn't road or sidewalk.
//TT_Sand, // Sand type terrain.
//TT_Gravel, // Loose gravel type terrain.
//TT_Water, // Water on surface type terrain.
//TT_Wood, // Boardwalks, docks type terrain.
//TT_Metal, // Powerplant and other structures.
//TT_Dirt, // Dirt type terrain.
//TT_NumTerrainTypes
for ( int i = 0 ; i < 2 ; i++)
{
switch( mPhysicsLocomotion->mTerrainIntersectCache[i].mTerrainType )
{
case TT_Grass:
mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eGrassSpray, mBurnoutLevel);
break;
case TT_Gravel:
case TT_Dirt:
case TT_Sand:
mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eDirtSpray, mBurnoutLevel);
break;
case TT_Water:
mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eWaterSpray, mBurnoutLevel);
break;
default:
mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eSmokeSpray, mBurnoutLevel);
break;
}
}
}
}
else
{
mGeometryVehicle->SetWheelSmoke( -1, ParticleEnum::eNull, 0.0f);
}
}
//=============================================================================
// Vehicle::DSGUpdateAndMove
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::DSGUpdateAndMove()
{
/*
rmt::Box3D mBBox;
rmt::Sphere mSphere;
rmt::Vector mPosn;
sim::SimState* mpSimStateObj; // TODO - ever care about this?
*/
rmt::Box3D oldBox = mBBox;
mPosn = *((rmt::Vector*)mTransform.m[3]);
mBBox.low = mPosn;
mBBox.high = mPosn;
// ?
// I think the same code should work for vehicle...
mBBox.high += mSimStateArticulated->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
mBBox.low -= mSimStateArticulated->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
mSphere.centre.Sub(mBBox.high,mBBox.low);
mSphere.centre *= 0.5f;
mSphere.centre.Add(mBBox.low);
mSphere.radius = mSimStateArticulated->GetCollisionObject()->GetCollisionVolume()->mSphereRadius;
// now move!
GetRenderManager()->pWorldScene()->Move(oldBox, (IEntityDSG*)this);
}
//=============================================================================
// Vehicle::UpdateGearAndRPM
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::UpdateGearAndRPM()
{
// make sure this method can work on both
// physics and traffic updated vehicles.
float speedAlongFacing;
speedAlongFacing = mVehicleFacing.DotProduct(mVelocityCM);
// just use wheel 0
float linearDistancePerRev = rmt::PI * mWheels[0]->mRadius * 2.0f;
float revPerTime = speedAlongFacing / linearDistancePerRev;
revPerTime = rmt::Fabs(revPerTime);
// now we go backwards from this to figure out what the rpm is,
// and if necessary, shift gears
// !!! TODO - deal with reverse!
// first see if we're in neutral and just sitting there
//static float smallRevPerTime = 0.1f;
float smallRevPerTime = 0.1f;
if(mGas == 0.0f && revPerTime < smallRevPerTime)
{
mGear = 0;
mRPM = mBaseRPM;
return;
}
// otherwise let's move!
if(mGear == 0)
{
mGear = 1;
}
if((mVehicleState == VS_NORMAL || mGas == 0) && mBurnoutLevel == 0.0f && !mDoSpeedBurst)
{
//--------------------------
// determine tentative value
//--------------------------
float targetRPM = mGearRatios[mGear - 1] * mFinalDriveRatio * revPerTime * 60.0f; // 60.0f to get to minutes instead of seconds
//------------------------
// only change by set rate
//------------------------
if(targetRPM > mRPM)
{
// shouldn't be able to make arbitrary changes in rpm
mRPM += mRPMUpRate;
if(mRPM > targetRPM)
{
mRPM = targetRPM;
}
}
if(targetRPM < mRPM)
{
// shouldn't be able to make arbitrary changes in rpm
mRPM -= mRPMDownRate;
if(mRPM < targetRPM)
{
mRPM = targetRPM;
}
}
//--------------------------
// change gears if necessary
//--------------------------
if(mRPM > mShiftPointHigh)
{
// change gears and recalculate
mGear++;
if(mGear > mNumGears)
{
mGear = mNumGears;
}
}
else if(mRPM < mShiftPointLow)
{
mGear--;
if(mGear < 1)
{
mGear = 1; // TODO - again - deal with reverse!
}
}
//-----------------------------------------
// recalc target due to gear change - maybe
//-----------------------------------------
targetRPM = mGearRatios[mGear - 1] * mFinalDriveRatio * revPerTime * 60.0f; // 60.0f to get to minutes instead of seconds
//------------------------
// only change by set rate
//------------------------
if(targetRPM > mRPM)
{
// shouldn't be able to make arbitrary changes in rpm
mRPM += mRPMUpRate;
if(mRPM > targetRPM)
{
mRPM = targetRPM;
}
}
if(targetRPM < mRPM)
{
// shouldn't be able to make arbitrary changes in rpm
mRPM -= mRPMDownRate;
if(mRPM < targetRPM)
{
mRPM = targetRPM;
}
}
//------------------------
// lock no lower than base
//------------------------
if(mRPM < mBaseRPM)
{
mRPM = mBaseRPM;
}
}
else // VS_SLIP
{
if(mGas > 0.0f)
{
const float hack = 0.1f;
//mRPM *= 1.0f + (hack * mGas);
const float hack2 = 3.1f;
if(mBurnoutLevel > 0.0f)
{
mRPM += mGas * mRPMUpRate * hack2;
}
else
{
mRPM += mGas * mRPMUpRate * hack;
}
// gear same
}
if(mRPM > mShiftPointHigh)
{
mRPM = mShiftPointHigh;
}
}
}
//=============================================================================
// Vehicle::UpdateWheelRenderingInfo
//=============================================================================
// Description: Comment
//
// Parameters: (float dt)
//
// Return: void
//
//=============================================================================
void Vehicle::UpdateWheelRenderingInfo(float dt)
{
int i;
for(i = 0; i < 4; i++)
{
// setup this method such that it can be called to get the right info
// cahced into the Wheel after either a physics or traffic update
//
// maybe this can be the single calculation-type method in
// Wheel.
mWheels[i]->CalculateRotAngle(dt);
}
}
//=============================================================================
// Vehicle::GetTransform
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: const
//
//=============================================================================
const rmt::Matrix& Vehicle::GetTransform()
{
return mTransform;
}
//========================================================================
// vehicle::
//========================================================================
//
// Description:
//
// Parameters: None.
//
// Return: None.
//
// Constraints: None.
//
//========================================================================
rmt::Vector* Vehicle::pPosition()
{
return (rmt::Vector*)mTransform.m[3];
}
//========================================================================
// vehicle::
//========================================================================
//
// Description:
//
// Parameters: None.
//
// Return: None.
//
// Constraints: None.
//
//========================================================================
const rmt::Vector& Vehicle::rPosition()
{
// rAssert(false);
// return NULL;
return *((rmt::Vector*)mTransform.m[3]);
}
//------------------------------------------------------------------------
//=============================================================================
// Vehicle::Display
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::Display()
{
ActivateTriggers(!(IsUnstable() || IsAirborn()) || (GetLocomotionType() == VL_TRAFFIC) || mAtRestAsFarAsTriggersAreConcerned);
if(IS_DRAW_LONG) return;
// not sure if these belong here?
//p3d::stack->Push();
//p3d::stack->Multiply(mTransform);
//return;
//DebugDisplay();
//return;
if( !mOkToDrawSelf || !mDrawVehicle )
{
return;
}
DSG_BEGIN_PROFILE(" Vehicle::Display")
BillboardQuadManager::Enable();
//DisplaySimpleShadow();
// TODO - how to set this up cleaner?
// little debug test
//
//if(!(mWheels[2]->mWheelInCollision) || !(mWheels[3]->mWheelInCollision))
if(0)//mWeebleOn)
//if(mDrawWireFrame)
//if(this->mAirBorn)
{
p3d::pddi->SetFillMode(PDDI_FILL_WIRE);
}
// temp!!
//p3d::pddi->SetFillMode(PDDI_FILL_WIRE);
mPoseEngine->End(); // copy over what we're gonna render
mGeometryVehicle->Display();
//p3d::stack->Pop();
//if(!(mWheels[2]->mWheelInCollision) || !(mWheels[3]->mWheelInCollision))
if(0)//mWeebleOn)
//if(mDrawWireFrame)
//if(this->mAirBorn)
{
p3d::pddi->SetFillMode(PDDI_FILL_SOLID);
}
// ugly, but just for debugging shit
mDrawWireFrame = false;
this->mLosingTractionDueToAccel = false;
BillboardQuadManager::Disable();
DSG_END_PROFILE(" Vehicle::Display")
}
//=============================================================================
// Vehicle::DebugDisplay
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::DebugDisplay()
{
mSimStateArticulated->DebugDisplay(2); // this one draws some sort of virtual centre of mass or something
sim::DrawCollisionObject(mSimStateArticulated->GetCollisionObject());
}
//=============================================================================
// Vehicle::CarDisplay
//=============================================================================
// Description: Comment
//
// Parameters: (bool doit)
//
// Return: void
//
//=============================================================================
void Vehicle::CarDisplay(bool doit)
{
mOkToDrawSelf = doit;
}
//=============================================================================
// Vehicle::GetSpeedKmh
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::GetSpeedKmh()
{
//return mSpeed * 3.6f;
return mSpeedKmh;
}
float Vehicle::GetAccelMss()
{
return mAccelMss;
}
//=============================================================================
// Vehicle::GetRPM
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::GetRPM()
{
return mRPM;
}
//=============================================================================
// Vehicle::GetSkidLevel
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::GetSkidLevel()
{
return mSkidLevel;
}
//=============================================================================
// Vehicle::GetGear
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: int
//
//=============================================================================
int Vehicle::GetGear()
{
// TODO - how to do the gearshift interface?
return mGear;
}
//----------------------
// camera inteface stuff
//----------------------
//=============================================================================
// Vehicle::GetPosition
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Vector* position )
//
// Return: void
//
//=============================================================================
void Vehicle::GetPosition( rmt::Vector* position )
{
*position = *((rmt::Vector*)mTransform.m[3]);
}
//=============================================================================
// Vehicle::GetHeading
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Vector* heading )
//
// Return: void
//
//=============================================================================
void Vehicle::GetHeading( rmt::Vector* heading )
{
*heading = mVehicleFacing;
}
//=============================================================================
// Vehicle::GetVUP
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Vector* vup )
//
// Return: void
//
//=============================================================================
void Vehicle::GetVUP( rmt::Vector* vup )
{
*vup = mVehicleUp;
}
//=============================================================================
// Vehicle::GetVelocity
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Vector* velocity )
//
// Return: void
//
//=============================================================================
void Vehicle::GetVelocity( rmt::Vector* velocity )
{
// TODO - make sure this holds the right thing when
// we are being traffic locomoted
*velocity = mVelocityCM;
}
//=============================================================================
// Vehicle::GetID
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: unsigned
//
//=============================================================================
unsigned int Vehicle::GetID()
{
return mVehicleID;
}
//=============================================================================
// Vehicle::IsCar
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsCar() const
{
return true;
}
//=============================================================================
// Vehicle::IsAirborn
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsAirborn()
{
//return mSteeringWheelsOutOfContact;
return mAirBorn;
}
//=============================================================================
// Vehicle::IsUnstable
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsUnstable()
{
// see how much it's tipped...
rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
float tip = mVehicleUp.DotProduct(up);
//float cos10 = 0.9848f;
float cos30 = 0.866f;
if(tip < cos30)
{
return true;
}
if(mAirBorn)
{
return true;
}
return false;
// quick test
//return mWeebleOn;
}
//=============================================================================
// Vehicle::IsSafeToUpShift
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsSafeToUpShift()
{
// let's say if at least 1 wheel in contact with ground (ie. !airborn)
// and the tip angle is less than 15 degrees, then ok.
if(!mAirBorn)
{
rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
float tip = mVehicleUp.DotProduct(up);
//float cos15 = 0.9659f;
float cos10 = 0.9848f;
if( (tip > cos10) || (mVehicleFacing.DotProduct(up) <= 0.0f) )
{
return true;
}
}
return false;
}
//=============================================================================
// Vehicle::IsQuickTurn
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsQuickTurn()
{
// TODO
return false;
}
//=============================================================================
// Vehicle::IsInReverse
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsInReverse()
{
//return false;
// first heuristic
//
// if the brake button is down and we're travelling backwards
float proj = mVelocityCM.DotProduct(mVehicleFacing);
// don't want just any slight backwards motion to trigger
const float cos120 = -0.5f;
if(mBrake > 0.1f && proj < cos120)
{
return true;
}
return false;
}
//=============================================================================
// Vehicle::GetTerrainIntersect
//=============================================================================
// Description: Comment
//
// Parameters: ( rmt::Vector& pos, rmt::Vector& normal )
//
// Return: void
//
//=============================================================================
void Vehicle::GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const
{
//THIS IS A LIE!
pos = mTransform.Row(3);
normal.Set( 0.0f, 1.0f, 0.0f );
}
//=============================================================================
// Vehicle::IsMovingBackward
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: bool
//
//=============================================================================
bool Vehicle::IsMovingBackward()
{
// this one does not require the brake input to be held down
if(this->mPercentOfTopSpeed > 0.05f)
{
float proj = mVelocityCM.DotProduct(mVehicleFacing);
const float cos120 = -0.5f;
if(proj < cos120)
{
return true;
}
}
return false;
}
//=============================================================================
// Vehicle::GetName
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: const
//
//=============================================================================
const char* const Vehicle::GetName()
{
return (const char*)mName;
}
const rmt::Vector& Vehicle::GetPassengerLocation( void ) const
{
return mPassengerLocation;
}
const rmt::Vector& Vehicle::GetDriverLocation( void ) const
{
return mDriverLocation;
}
//=============================================================================
// Vehicle::PreReactToCollision
//=============================================================================
// Description: Comment
//
// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
//
// Return: bool
//
//=============================================================================
sim::Solving_Answer Vehicle::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
{
// this is called from the generic worldcollisionsolveragentmanager
// don't abort
return sim::Solving_Continue;
}
//=============================================================================
// Vehicle::SetHitJoint
//=============================================================================
// Description: Comment
//
// Parameters: (int hj)
//
// Return: void
//
//=============================================================================
/*
void Vehicle::SetHitJoint(int hj)
{
// want to try and use the most "interesting" value
if(IsAFlappingJoint(hj))
{
// set for sure
mHitJoint = hj;
return;
}
if(IsAFlappingJoint(mHitJoint))
{
// we already have a flapping joint set and the incoming one is not
return;
}
// ok we'll use it.
mHitJoint = hj;
}
*/
//=============================================================================
// Vehicle::ResetDamageState
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::ResetDamageState()
{
mVehicleDestroyed = false;
mDontShowBrakeLights = false;
mGeometryVehicle->SetLightsOffDueToDamage(false);
mDamageOutResetTimer = 0.0f;
mHitPoints = mDesignerParams.mHitPoints;
mDesiredDoorPosition[0] = mDesiredDoorPosition[1] = 0.0f;
mDesiredDoorAction[0] = mDesiredDoorAction[1] = DOORACTION_NONE;
ActivateTriggers(true);
if (mbPlayerCar ==true)
{
GetCharacterSheetManager()->UpdateCarHealth( mCharacterSheetCarIndex, 1.0f );
}
switch(mDamageType)
{
//case 1:
case VDT_USER:
{
// reset flapping joints
if(mHoodJoint != -1)
{
int index = mJointIndexToInertialJointDriverMapping[mHoodJoint];
if(index != -1)
{
mPhObj->GetJoint(mHoodJoint)->SetInvStiffness(0.0f);
mPhObj->GetJoint(mHoodJoint)->ResetDeformation();
mInertialJointDrivers[index]->SetIsEnabled(false);
}
}
if(mTrunkJoint != -1)
{
int index = mJointIndexToInertialJointDriverMapping[mTrunkJoint];
if(index != -1)
{
mPhObj->GetJoint(mTrunkJoint)->SetInvStiffness(0.0f);
mPhObj->GetJoint(mTrunkJoint)->ResetDeformation();
mInertialJointDrivers[index]->SetIsEnabled(false);
}
}
if(mDoorDJoint != -1)
{
int index = mJointIndexToInertialJointDriverMapping[mDoorDJoint];
if(index != -1)
{
mPhObj->GetJoint(mDoorDJoint)->SetInvStiffness(0.0f);
mPhObj->GetJoint(mDoorDJoint)->ResetDeformation();
mInertialJointDrivers[index]->SetIsEnabled(false);
}
}
if(mDoorPJoint != -1)
{
int index = mJointIndexToInertialJointDriverMapping[mDoorPJoint];
if(index != -1)
{
mPhObj->GetJoint(mDoorPJoint)->SetInvStiffness(0.0f);
mPhObj->GetJoint(mDoorPJoint)->ResetDeformation();
mInertialJointDrivers[index]->SetIsEnabled(false);
}
}
mGeometryVehicle->DamageTextureHood(false);
mGeometryVehicle->DamageTextureTrunk(false);
mGeometryVehicle->DamageTextureDoorD(false);
mGeometryVehicle->DamageTextureDoorP(false);
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
mGeometryVehicle->HideFlappingPiece(mHoodJoint, false);
}
break;
//case 2:
case VDT_AI:
{
mGeometryVehicle->DamageTextureHood(false);
mGeometryVehicle->DamageTextureTrunk(false);
mGeometryVehicle->DamageTextureDoorD(false);
mGeometryVehicle->DamageTextureDoorP(false);
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
}
break;
//case 3:
case VDT_TRAFFIC:
{
//mGeometryVehicle->DamageTextureChassis(false);
//mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
}
//=============================================================================
// Vehicle::DebugInflictDamageHood
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::DebugInflictDamageHood()
{
//int stophere = 1;
//float perc = GetVehicleLifePercentage();
float perc = TriggerDamage(0.15f);
switch(mDamageType)
{
//case 1: // user level - flaping joints etc...
case VDT_USER:
{
VisualDamageType1(1.0f - perc, dl_hood);
}
break;
//case 2: // ai - localized damage textures but no flapping
case VDT_AI:
{
VisualDamageType2(1.0f - perc, dl_hood);
}
break;
//case 3: // traffic - one big poof and texture
case VDT_TRAFFIC:
{
VisualDamageType3(1.0f - perc);
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
}
//=============================================================================
// Vehicle::DebugInflictDamageBack
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::DebugInflictDamageBack()
{
//TriggerDamage(0.15f);
//float perc = GetVehicleLifePercentage();
//float perc = GetVehicleLifePercentage();
float perc = TriggerDamage(0.15f);
switch(mDamageType)
{
//case 1: // user level - flaping joints etc...
case VDT_USER:
{
VisualDamageType1(1.0f - perc, dl_trunk);
}
break;
//case 2: // ai - localized damage textures but no flapping
case VDT_AI:
{
VisualDamageType2(1.0f - perc, dl_trunk);
}
break;
//case 3: // traffic - one big poof and texture
case VDT_TRAFFIC:
{
VisualDamageType3(1.0f - perc);
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
}
//=============================================================================
// Vehicle::DebugInflictDamageDriverSide
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::DebugInflictDamageDriverSide()
{
//TriggerDamage(0.15f);
//float perc = GetVehicleLifePercentage();
//float perc = GetVehicleLifePercentage();
float perc = TriggerDamage(0.15f);
switch(mDamageType)
{
//Case 1: // user level - flaping joints etc...
case VDT_USER:
{
VisualDamageType1(1.0f - perc, dl_driverside);
}
break;
//case 2: // ai - localized damage textures but no flapping
case VDT_AI:
{
VisualDamageType2(1.0f - perc, dl_driverside);
}
break;
//case 3: // traffic - one big poof and texture
case VDT_TRAFFIC:
{
VisualDamageType3(1.0f - perc);
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
}
//=============================================================================
// Vehicle::DebugInflictDamagePassengerSide
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::DebugInflictDamagePassengerSide()
{
//TriggerDamage(0.15f);
//float perc = GetVehicleLifePercentage();
//float perc = GetVehicleLifePercentage();
float perc = TriggerDamage(0.15f);
switch(mDamageType)
{
//case 1: // user level - flaping joints etc...
case VDT_USER:
{
VisualDamageType1(1.0f - perc, dl_passengerside);
}
break;
//case 2: // ai - localized damage textures but no flapping
case VDT_AI:
{
VisualDamageType2(1.0f - perc, dl_passengerside);
}
break;
//case 3: // traffic - one big poof and texture
case VDT_TRAFFIC:
{
VisualDamageType3(1.0f - perc);
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
}
//=============================================================================
// Vehicle::PostReactToCollision
//=============================================================================
// Description: Comment
//
// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
//
// Return: sim
//
//=============================================================================
sim::Solving_Answer Vehicle::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
{
// this is called from the generic worldcollisionsolveragentmanager
// don't abort
sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
sim::SimState* simStateA = collObjA->GetSimState();
sim::SimState* simStateB = collObjB->GetSimState();
float impulseMagnitude = impulse.Magnitude();
const float maxIntensity = 100000.0f; // empircally determined
// Check to see if the impact was strong enough to detach any attached collectibles.
if ( impulseMagnitude > mForceToDetachCollectible )
{
rmt::Vector velocity = GetSimState()->GetLinearVelocity();
DetachCollectible( velocity );
}
float normalizedMagnitude = impulseMagnitude / maxIntensity;
if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
{
// stricter test
if(normalizedMagnitude > 0.8f)
{
return sim::Solving_Aborted;
}
else if( ( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleID == VehicleEnum::BART_V && ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleID == VehicleEnum::CKLIMO) ||
( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleID == VehicleEnum::CKLIMO && ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleID == VehicleEnum::BART_V) )
{
if(normalizedMagnitude > 0.5f)
{
return sim::Solving_Aborted;
}
}
}
if(normalizedMagnitude > 1.0f)
{
rDebugPrintf("Yikes!!! Enormous impulse!\n");
//impulse.x /= normalizedMagnitude * 2;
//impulse.y /= normalizedMagnitude * 2;
//impulse.z /= normalizedMagnitude * 2;
if(normalizedMagnitude > 2.0f)
{
return sim::Solving_Aborted;
}
normalizedMagnitude = 1.0f;
}
if(normalizedMagnitude < 0.0f)
{
rAssert(0);
}
if(mVehicleID == VehicleEnum::HUSKA)
{
normalizedMagnitude = 0.2f;
}
if(this->mVehicleID == VehicleEnum::DUNE_V)
{
if( simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizFence ||
simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizFence)
{
// r/c car hitting a fence
//? scale up impulse?
impulse.Scale(2.0f);
}
}
//Rumble the controller
RumbleCollision rc;
rc.normalizedForce = normalizedMagnitude;
rc.vehicle = this;
rc.point = inCollision.GetPositionA(); //This could be either.
GetEventManager()->TriggerEvent( EVENT_RUMBLE_COLLISION, &rc );
if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
{
TestWhoHitWhom( simStateA, simStateB, normalizedMagnitude, inCollision );
if(mVehicleType != VT_USER)
{
DusitsStunTest(normalizedMagnitude);
}
// Vehicle - vehicle collision
// we want to emit paint flicks of the appropriate colour
if ( mGeometryVehicle->HasVehicleColour() )
{
tColour vehicleColour = mGeometryVehicle->GetVehicleColour();
float strength = impulseMagnitude * ( 1.0f / 5000.0f );
float velocityScale = strength;
const rmt::Vector& position = inCollision.GetPositionA();
rmt::Vector velocity = mVelocityCM;
if ( velocity.MagnitudeSqr() > 0.01f )
{
velocity.y = 0.0f;
velocity.Normalize();
velocity.Scale( velocityScale );
}
else
{
velocity = rmt::Vector(0,0,0);
}
GetSparkleManager()->AddPaintChips( position, velocity, vehicleColour, strength );
}
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
if(playerAvatar->GetVehicle() == this)
{
CarOnCarCollisionEventData data;
//data.vehicle shall be the other vehicle.
if ( simStateA->mAIRefPointer == this )
{
data.vehicle = ((Vehicle*)(simStateB->mAIRefPointer));
}
else
{
data.vehicle = ((Vehicle*)(simStateA->mAIRefPointer));
}
data.force = normalizedMagnitude;
data.collision = &inCollision;
GetEventManager()->TriggerEvent( EVENT_VEHICLE_VEHICLE_COLLISION, &data );
}
}
if(mVehicleType == VT_USER)
{
SparksTest(impulseMagnitude, inCollision); // should this also be wrapped in mUserDrivingCar??
}
//which one are we?
bool thisIsA = true;
if(simStateA->mAIRefPointer == this)
{
// ok
}
else
{
thisIsA = false;
}
// try movign this down so that husks hitting cars and statics will still make sound
//if(mVehicleID == VehicleEnum::HUSKA)
//{
// return sim::Solving_Continue;
//}
// new debug test just for traffic collision
bool oneTapTrafficDeath = false;
if( GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_ONE_TAP_TRAFFIC_DEATH) )
{
if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
{
if(thisIsA)
{
if( (((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_TRAFFIC || ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_AI) &&
((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_USER)
{
int stophere = 1;
oneTapTrafficDeath = true;
}
}
else
{
if( (((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_TRAFFIC || ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_AI) &&
((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_USER)
{
int stophere = 1;
oneTapTrafficDeath = true;
}
}
}
}
// This is the "minimum damage threshold" for applying ANY damage
// - think of the number as a percentage, i.e., any hit less than .1 (10 &) intensity
// won't cause any damage at all
const float minDamageThreshold = 0.06f;
if(!oneTapTrafficDeath)
{
if(normalizedMagnitude < minDamageThreshold)
{
//#ifdef RAD_DEBUG
//char buffy[128];
//sprintf(buffy, "normalizedMagnitude %.4f\n", normalizedMagnitude);
//rDebugPrintf(buffy);
//#endif
return sim::Solving_Continue;
}
}
//Greg says to do this. Won't work in 2-player.
if (mUserDrivingCar)
{
CameraShakeTest(impulseMagnitude, inCollision); // pass struct, by reference, for convenience
// pass in impulseMagnitude because we don't want to compute it twice.
}
SoundCollisionData soundData( normalizedMagnitude,
static_cast<CollisionEntityDSG*>(simStateA->mAIRefPointer),
static_cast<CollisionEntityDSG*>(simStateB->mAIRefPointer) );
GetEventManager()->TriggerEvent( EVENT_COLLISION, &soundData );
if(mVehicleID == VehicleEnum::HUSKA)
{
return sim::Solving_Continue;
}
// before further testing, see if this is a car that hit it's own ground plane
// if so, abort
Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
if(GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT && playerAvatar->GetVehicle() == this)
{
const float percentageOfSpeedToMaintain = 0.85f; // plum, change this number
if(thisIsA)
{
if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane && this->mGas > 0.0f)
{
if(mBottomOutSpeedMaintenance == 0.0f)
{
// not set
mBottomOutSpeedMaintenance = this->mSpeed * percentageOfSpeedToMaintain;
}
else
{
// it is set
//
// so.... maintain it!
if(mBottomOutSpeedMaintenance > (this->mDesignerParams.mDpTopSpeedKmh / 3.6f) * 0.6f)
{
rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
float proj = linearVel.DotProduct(this->mVehicleFacing);
if(proj < mBottomOutSpeedMaintenance)
{
float diff = mBottomOutSpeedMaintenance - proj;
rmt::Vector boost = mVehicleFacing;
boost.Scale(diff);
linearVel.Add(boost);
}
}
}
return sim::Solving_Continue;
}
else
{
// didn't hit our ground plane this frame so make sure the maintenance speed is reset
mBottomOutSpeedMaintenance = 0.0f;
}
}
else
{
if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane && this->mGas > 0.0f)
{
if(mBottomOutSpeedMaintenance == 0.0f)
{
// not set
mBottomOutSpeedMaintenance = this->mSpeed * percentageOfSpeedToMaintain;
}
else
{
// it is set
//
// so.... maintain it!
if(mBottomOutSpeedMaintenance > (this->mDesignerParams.mDpTopSpeedKmh / 3.6f) * 0.6f)
{
rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
float proj = linearVel.DotProduct(this->mVehicleFacing);
if(proj < mBottomOutSpeedMaintenance)
{
float diff = mBottomOutSpeedMaintenance - proj;
rmt::Vector boost = mVehicleFacing;
boost.Scale(diff);
linearVel.Add(boost);
}
}
}
return sim::Solving_Continue;
}
else
{
// didn't hit our ground plane this frame so make sure the maintenance speed is reset
mBottomOutSpeedMaintenance = 0.0f;
}
}
}
bool inflictDamage = true;
if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
{
// lateral resistance drop
if(mVehicleType != VT_USER)
{
rmt::Vector otherFacing;
if(thisIsA)
{
otherFacing = ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleFacing;
}
else
{
otherFacing = ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleFacing;
}
float dp = mVehicleFacing.DotProduct(otherFacing);
if(dp < 0.1f)
{
dp = 0.1f;
}
mCollisionLateralResistanceDropFactor = dp;
// exaggerated hit results - note the test for ! VT_USER ain't gonna help here
/*
rmt::Vector funImpulse = impulse;
funImpulse.NormalizeSafe();
funImpulse.y += 0.7f;
funImpulse.Scale(impulseMagnitude);
impulse = funImpulse;
*/
}
bool carOnCarDamage = CarOnCarDamageLogic(thisIsA, simStateA, simStateB);
inflictDamage &= carOnCarDamage;
}
if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_INVINCIBLE_CAR))
{
if(GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() == this)
{
return sim::Solving_Continue;
}
}
// time for damage inc.
// debug cheat for the testers...
if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_FULL_DAMAGE_TO_CAR) && mVehicleType == VT_USER)
{
// debug cheat
// note - don't actually decrememtn hitpoints, just draw all damaged.
switch(mDamageType)
{
//case 1: // user level - flaping joints etc...
case VDT_USER:
{
DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
VisualDamageType1(0.995f, dl); // any point in this actually taking in a parameter
}
break;
//case 2: // ai - localized damage textures but no flapping
case VDT_AI:
{
DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
VisualDamageType2(0.995f, dl); // any point in this actually taking in a parameter
}
break;
//case 3: // traffic - one big poof and texture
case VDT_TRAFFIC:
{
VisualDamageType3(0.995f); // any point in this actually taking in a parameter
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
}
// normal case
else if((mVehicleCanSustainDamage && inflictDamage) || oneTapTrafficDeath)// && this->mVehicleType != VT_TRAFFIC) // todo - definately need to reassess the traffic test here
{
if(oneTapTrafficDeath)
{
this->TriggerDamage(100.0f);
}
else if(mNoDamageTimer == 0.0f)
{
if( simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle &&
simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
{
// The hitter sustains less damage, the hit sustains more
if( !mWasHitByVehicle )
{
normalizedMagnitude *= 0.5f; // I was the hitter! Yes!
}
}
SwitchOnDamageTypeAndApply(normalizedMagnitude, inCollision);
}
}
// test
//static float fun = 2.0f;
//impulse.Scale(fun);
return sim::Solving_Continue;
}
//=============================================================================
// Vehicle::DusitsStunTest
//=============================================================================
// Description: Comment
//
// Parameters: (float normalizedMagnitude)
//
// Return: void
//
//=============================================================================
void Vehicle::DusitsStunTest(float normalizedMagnitude)
{
mCollidedWithVehicle = true;
/*
const float AI_VEHICLE_STUNNED_IMPACT_THRESHOLD = 0.01f;
if( normalizedMagnitude > AI_VEHICLE_STUNNED_IMPACT_THRESHOLD )
{
mCollidedWithVehicle = true;
}
*/
}
void Vehicle::TestWhoHitWhom( sim::SimState* simA, sim::SimState* simB, float normalizedMagnitude, const sim::Collision& inCollision )
{
rAssert( simA->mAIRefIndex == PhysicsAIRef::redBrickVehicle &&
simB->mAIRefIndex == PhysicsAIRef::redBrickVehicle );
sim::Collision simCollision = inCollision;
rAssert( rmt::Epsilon( simCollision.mNormal.MagnitudeSqr(),1.0f,0.0005f ) );
// Want to determine which one I am.
// We also want collision normal to point from him to me..
// But since collision normal for all sim::Collision always points from B to A,
// if he is simState A, then invert the collision normal.
Vehicle* him = NULL;
if( simA->mAIRefPointer == this )
{
him = (Vehicle*)simB->mAIRefPointer;
}
else
{
him = (Vehicle*)simA->mAIRefPointer;
simCollision.mNormal.Scale( -1.0f );
}
rmt::Vector myVel;
GetVelocity( &myVel );
bool gotHitByHim = false;
float velThreshold = 0.005f;
// If one of the speeds is near zero, it's obvious who hit whom...
if( mSpeed < velThreshold )
{
gotHitByHim = true;
}
else if( him->mSpeed < velThreshold )
{
gotHitByHim = false;
}
else
{
// Deal with the ambiguous case of who hit whom, when
// both velocities are > zero. Sooo...
// The collision normal is always pointing from him to me and is
// at this point NORMALIZED. We steal this and invert the vector
// to get the heading vector from US to HIM.
rmt::Vector toHim = simCollision.mNormal * -1.0f;
rAssert( rmt::Epsilon( toHim.MagnitudeSqr(), 1.0f, 0.001f ) );
// Now... if my velocity vector is NOT pointing in "more or less"
// the same direction as my vector to him, then I'm not the hitter,
// so I got hit by him...
rmt::Vector myNormalizedVel = myVel / mSpeed;
rAssert( rmt::Epsilon( myNormalizedVel.MagnitudeSqr(), 1.0f, 0.001f ) );
const float cosAlphaTest = 0.8660254f; //approx cos30 (in either directions)
if( myNormalizedVel.Dot( toHim ) < cosAlphaTest ) // angle greater than cosalpha
{
gotHitByHim = true; // we're not the hitter, so we're the hit
}
else
{
// ok, we're the hitter... BUT there's a special case for when
// we're in a head-on collision with the other car. If we detect
// this case we don't want to set both as the hitter... we want
// each party to take full damage....
rmt::Vector hisVel;
him->GetVelocity( &hisVel );
rmt::Vector hisNormalizedVel = hisVel / him->mSpeed;
rAssert( rmt::Epsilon( hisNormalizedVel.MagnitudeSqr(), 1.0f, 0.001f ) );
rmt::Vector toMe = toHim * -1.0f;
rAssert( rmt::Epsilon( toMe.MagnitudeSqr(), 1.0f, 0.001f ) );
if( hisNormalizedVel.Dot( toMe ) < cosAlphaTest )
{
// angle greater than tolerance, so he's not headed at me
// therefore not a head-on collision. So do the setting
// normally.
gotHitByHim = false; // we're the hitter
}
else
{
// we're the hit (because he's coming straight at us with some
// tangible velocity.)
gotHitByHim = true;
}
}
}
// ok we figured out that ya we got hit, so store the appropriate data
if( gotHitByHim )
{
// Do special transit to slip for all vehicles
// NOTE: If don't want player vehicle to transit to slip from impact,
// just gotta put in a check here that we're not player vehicle
if( normalizedMagnitude > 0.035f )
{
mVehicleState = VS_SLIP;
}
mWasHitByVehicle = true;
mWasHitByVehicleType = him->mVehicleType;
mNormalizedMagnitudeOfVehicleHit = normalizedMagnitude;
mSwerveNormal = simCollision.mNormal;
}
}
//=============================================================================
// Vehicle::SwitchOnDamageTypeAndApply
//=============================================================================
// Description: Comment
//
// Parameters: (float normalizedMagnitude)
//
// Return: void
//
//=============================================================================
void Vehicle::SwitchOnDamageTypeAndApply(float normalizedMagnitude, sim::Collision& inCollision)
{
// first just decrement the hit points
//TriggerDamage(normalizedMagnitude);
// make sure appropriate effects level is playing
//float perc = GetVehicleLifePercentage();
float perc = TriggerDamage(normalizedMagnitude);
switch(mDamageType)
{
//case 1: // user level - flaping joints etc...
case VDT_USER:
{
DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
VisualDamageType1(1.0f - perc, dl); // any point in this actually taking in a parameter
}
break;
//case 2: // ai - localized damage textures but no flapping
case VDT_AI:
{
DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
VisualDamageType2(1.0f - perc, dl); // any point in this actually taking in a parameter
}
break;
case VDT_TRAFFIC:
{
VisualDamageType3(1.0f - perc); // any point in this actually taking in a parameter
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
}
//=============================================================================
// Vehicle::CarOnCarDamageLogic
//=============================================================================
// Description: Comment
//
// Parameters: ( sim::SimState* simStateA, sim::SimState* simStateB)
//
// Return: bool
//
//=============================================================================
bool Vehicle::CarOnCarDamageLogic(bool thisIsA, sim::SimState* simStateA, sim::SimState* simStateB)
{
if(this->mIsADestroyObjective)
{
if(thisIsA)
{
if( ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_USER)
{
// destroy objective hit by user car so it should take damage!
return true;
}
else
{
//inflictDamage = false;
return false;
}
}
else
{
if( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_USER)
{
// destroy objective hit by user car so it should take damage
return true;
}
else
{
//inflictDamage = false;
return false;
}
}
}
else if(this->mVehicleType == VT_USER)
{
// I want to know if this is a dump mission - if so, I shouldn't take damage....
if(thisIsA)
{
if( ((Vehicle*)(simStateB->mAIRefPointer))->mIsADestroyObjective)
{
// user car hitting a destroy objective so it should not take damage
//inflictDamage = false;
//return false;
// test - April 8, 2003
// even during a destroy mission, the user will take damage when hitting the destory objective
// makes stronger cars more important
return true;
}
}
else
{
if( ((Vehicle*)(simStateA->mAIRefPointer))->mIsADestroyObjective)
{
// destroy objective hit by user car so it should take damage
//inflictDamage = false;
//return false;
// see note above
return true;
}
}
}
return true;
}
//=============================================================================
// Vehicle::CameraShakeTest
//=============================================================================
// Description: Comment
//
// Parameters: (float impulseMagnitude, sim::Collision& inCollision)
//
// Return: void
//
//=============================================================================
void Vehicle::CameraShakeTest(float impulseMagnitude, sim::Collision& inCollision)
{
//TODO: Greg can you make this more reliable? Should I take into account
//the mass of the vehicle?
//Fudge some magnitude calc to send to the shaker.
// greg responds: the mass is taken into account in the size of the impulse - I think...?
const float cameraShakeThreshold = 19000.0f;
const float maxIntensity = 100000.0f; // copied from PostReactToCollision, but doesn't have to be the same
if(impulseMagnitude > cameraShakeThreshold)
{
ShakeEventData shakeData;
shakeData.playerID = 0; //Hack...
shakeData.force = impulseMagnitude / maxIntensity;
rmt::Vector pointOnOtherObject;
sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
sim::SimState* simStateA = collObjA->GetSimState();
sim::SimState* simStateB = collObjB->GetSimState();
if(simStateA == mSimStateArticulated)
{
pointOnOtherObject = inCollision.GetPositionB();
}
else if(simStateB == mSimStateArticulated)
{
pointOnOtherObject = inCollision.GetPositionA();
}
else
{
rAssertMsg(0, "Ask Greg what went wrong!");
}
rmt::Vector contactPointToCenter;
contactPointToCenter.Sub(this->rPosition(), pointOnOtherObject);
contactPointToCenter.NormalizeSafe();
shakeData.direction = contactPointToCenter;
GetEventManager()->TriggerEvent( EVENT_CAMERA_SHAKE, (void*)(&shakeData) );
}
}
//=============================================================================
// Vehicle::SparksTest
//=============================================================================
// Description: Comment
//
// Parameters: (float impulseMagnitude, sim::Collision& inCollision)
//
// Return: void
//
//=============================================================================
void Vehicle::SparksTest(float impulseMagnitude, sim::Collision& inCollision) // should this also be wrapped in mUserDrivingCar??
{
sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
sim::SimState* simStateA = collObjA->GetSimState(); // A is the vehicle.
sim::SimState* simStateB;
if( simStateA->mAIRefPointer != this )
{
simStateB = simStateA;
simStateA = collObjB->GetSimState();
}
else
{
simStateB = collObjB->GetSimState();
}
// Lets add some vehicle specific particle effects upon collision
// If the other collision object also a vehicle, lets create some sparks!
// Better place for this constant?
const float MIN_IMPULSE_VEHICLE_VEHICLE_SPARKS = 0.01f;
float velocityScale = 0.0f;
bool doStars = false;
switch( simStateB->mAIRefIndex )
{
case PhysicsAIRef::redBrickPhizFence:
impulseMagnitude *= ( 1.0f / 25000.0f );
doStars = true;
break;
case PhysicsAIRef::redBrickVehicle:
impulseMagnitude *= ( 1.0f / 5000.0f );
velocityScale = impulseMagnitude;
break;
case PhysicsAIRef::redBrickPhizMoveable:
impulseMagnitude *= ( 1.0f / 5000.0f );
velocityScale = impulseMagnitude * 0.5f;
break;
case PhysicsAIRef::redBrickPhizStatic:
impulseMagnitude *= ( 1.0f / 25000.0f );
impulseMagnitude = rmt::Min( 1.0f, impulseMagnitude * 2.0f );
doStars = true;
default:
return;
}
if( impulseMagnitude > 10.0f )
{
impulseMagnitude *= 0.1f;
}
if ( impulseMagnitude > MIN_IMPULSE_VEHICLE_VEHICLE_SPARKS )
{
const rmt::Vector& pos = inCollision.GetPositionA();
if( doStars )
{
GetSparkleManager()->AddStars( pos, impulseMagnitude );
}
else
{
rmt::Vector vel = mVelocityCM;
vel.y = 0.0f;
vel.Normalize();
vel.Scale( velocityScale );
GetSparkleManager()->AddSparks( pos, vel, impulseMagnitude );
}
}
}
//=============================================================================
// Vehicle::VisualDamageType1
//=============================================================================
// Description: Comment
//
// Parameters: (float percentageDamage)
//
// Return: void
//
//=============================================================================
void Vehicle::VisualDamageType1(float percentageDamage, DamageLocation dl)
{
/*
// new design, new range
0 normal
10 flap
30 texture
60 white smoke
99 grey smoke (the one-hit indicator) - this will have been clamped to exactly this value
100 black smoke and flame - car will blow in X seconds
*/
// get the joint in question:
int joint = -1;
switch(dl)
{
case dl_hood:
joint = mHoodJoint;
break;
case dl_trunk:
joint = mTrunkJoint;
break;
case dl_driverside:
joint = mDoorDJoint;
break;
case dl_passengerside:
joint = mDoorPJoint;
break;
default:
rAssert(0);
}
if(percentageDamage > 0.1f)
{
// only join that got hit should flap
if(joint != -1)
{
int index = mJointIndexToInertialJointDriverMapping[joint];
if(index != -1)
{
mPhObj->GetJoint(joint)->SetInvStiffness(1.0f);
mInertialJointDrivers[index]->SetIsEnabled(true);
}
}
}
if(percentageDamage > 0.3f)
{
// only the joint that got hit should have texture on it.
switch(dl)
{
case dl_hood:
mGeometryVehicle->DamageTextureHood(true);
break;
case dl_trunk:
mGeometryVehicle->DamageTextureTrunk(true);
mDontShowBrakeLights = true;
break;
case dl_driverside:
mGeometryVehicle->DamageTextureDoorD(true);
break;
case dl_passengerside:
mGeometryVehicle->DamageTextureDoorP(true);
break;
default:
rAssert(0);
}
// but all should flap
// all sides flapping ?
int index;
if(mHoodJoint != -1)
{
index = mJointIndexToInertialJointDriverMapping[mHoodJoint];
if(index != -1)
{
mPhObj->GetJoint(mHoodJoint)->SetInvStiffness(1.0f);
mInertialJointDrivers[index]->SetIsEnabled(true);
}
}
if(mTrunkJoint != -1)
{
index = mJointIndexToInertialJointDriverMapping[mTrunkJoint];
if(index != -1)
{
mPhObj->GetJoint(mTrunkJoint)->SetInvStiffness(1.0f);
mInertialJointDrivers[index]->SetIsEnabled(true);
}
}
if(mDoorDJoint != -1)
{
index = mJointIndexToInertialJointDriverMapping[mDoorDJoint];
if(index != -1)
{
mPhObj->GetJoint(mDoorDJoint)->SetInvStiffness(1.0f);
mInertialJointDrivers[index]->SetIsEnabled(true);
}
}
if(mDoorPJoint != -1)
{
index = mJointIndexToInertialJointDriverMapping[mDoorPJoint];
if(index != -1)
{
mPhObj->GetJoint(mDoorPJoint)->SetInvStiffness(1.0f);
mInertialJointDrivers[index]->SetIsEnabled(true);
}
}
}
if(percentageDamage > 0.6f)
{
// for now, just try taking off the hood before we make smoke pour out there.
//int index;
if(mHoodJoint != -1)
{
mGeometryVehicle->HideFlappingPiece(mHoodJoint, true);
}
// all texture
mGeometryVehicle->DamageTextureHood(true);
mGeometryVehicle->DamageTextureTrunk(true);
mGeometryVehicle->DamageTextureDoorD(true);
mGeometryVehicle->DamageTextureDoorP(true);
// white smoke
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
mDontShowBrakeLights = true;
mGeometryVehicle->SetLightsOffDueToDamage(true);
// at this point lights and brake lights should stop working
}
if(percentageDamage >= 0.98f) // this is the one-more-hit warning
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
}
if(percentageDamage > 0.99f) // about to blow
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
}
}
//=============================================================================
// Vehicle::VisualDamageType2
//=============================================================================
// Description: Comment
//
// Parameters: (float percentageDamage, DamageLocation dl)
//
// Return: void
//
//=============================================================================
void Vehicle::VisualDamageType2(float percentageDamage, DamageLocation dl)
{
/*
try this range
0 normal
10 texture damage (localized front, back, driver side, passenger side)
40 smoke level 1
60 smoke level 2
80 smoke level 3
100 disabled
*/
// note:
// joint might still be -1, if the is a type 1 car but it doesn't have all four
// flapping joints?
// this is bulky, but fuck it, gotta get this shit working like yesterday
if(percentageDamage > 0.1f)
{
// only the joint that got hit should have texture on it.
switch(dl)
{
case dl_hood:
mGeometryVehicle->DamageTextureHood(true);
break;
case dl_trunk:
mGeometryVehicle->DamageTextureTrunk(true);
mDontShowBrakeLights = true;
break;
case dl_driverside:
mGeometryVehicle->DamageTextureDoorD(true);
break;
case dl_passengerside:
mGeometryVehicle->DamageTextureDoorP(true);
break;
default:
rAssert(0);
}
}
if(percentageDamage > 0.4f)
{
// smoke 1
//
// ? all sides textured?
mGeometryVehicle->DamageTextureHood(true);
mGeometryVehicle->DamageTextureTrunk(true);
mGeometryVehicle->DamageTextureDoorD(true);
mGeometryVehicle->DamageTextureDoorP(true);
//mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
mGeometryVehicle->SetLightsOffDueToDamage(true);
}
if(percentageDamage > 0.6f)
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
//mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
}
if(percentageDamage >= 0.98f)
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
}
if(percentageDamage > 0.99f)
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
}
}
//=============================================================================
// Vehicle::VisualDamageType3
//=============================================================================
// Description: Comment
//
// Parameters: (float percentageDamage)
//
// Return: void
//
//=============================================================================
void Vehicle::VisualDamageType3(float percentageDamage)
{
if(percentageDamage > 0.6f)
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
//mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
}
if(percentageDamage >= 0.98f)
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
}
if(percentageDamage > 0.99f)
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
}
}
//=============================================================================
// Vehicle::SyncVisualDamage
//=============================================================================
// Description: Comment
//
// Parameters: ( float Health )
//
// Return: void
//
//=============================================================================
void Vehicle::SyncVisualDamage( float Health )
{
if( Health >= 1.0f )
{
return;
}
else if( Health <= 0.0f )
{
// Husk.
mHitPoints = 0.0f;
mVehicleDestroyed = true;
ActivateTriggers(false);
}
else
{
// this should use the same visual logic functions as the normal damage system
// just need to make up a DamageLocation
// how about the hood? :)
switch(mDamageType)
{
//case 1: // user level - flaping joints etc...
case VDT_USER:
{
VisualDamageType1(1.0f - Health, dl_hood); // any point in this actually taking in a parameter
}
break;
//case 2: // ai - localized damage textures but no flapping
case VDT_AI:
{
VisualDamageType2(1.0f - Health, dl_hood); // any point in this actually taking in a parameter
}
break;
case VDT_TRAFFIC:
{
VisualDamageType3(1.0f - Health); // any point in this actually taking in a parameter
}
break;
default:
rAssertMsg(0, "what are you doing here?");
}
/*
if( Health <= 0.8f)
{
mGeometryVehicle->DamageTextureHood(true);
mGeometryVehicle->DamageTextureTrunk(true);
mGeometryVehicle->DamageTextureDoorD(true);
mGeometryVehicle->DamageTextureDoorP(true);
}
if( Health <= 0.6f )
{
mGeometryVehicle->SetLightsOffDueToDamage(true);
if( Health <= 0.01f )
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
}
else if( Health <= 0.4f )
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
}
else
{
mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
}
}
*/
}
}
//=============================================================================
// Vehicle::TranslateCollisionIntoLocation
//=============================================================================
// Description: Comment
//
// Parameters: (sim::Collision& inCollision)
//
// Return:
//
//=============================================================================
Vehicle::DamageLocation Vehicle::TranslateCollisionIntoLocation(sim::Collision& inCollision)
{
sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
sim::SimState* simStateA = collObjA->GetSimState();
sim::SimState* simStateB = collObjB->GetSimState();
rmt::Vector pointOnOtherCar;
if(simStateA == mSimStateArticulated)
{
pointOnOtherCar = inCollision.GetPositionB();
}
else if(simStateB == mSimStateArticulated)
{
pointOnOtherCar = inCollision.GetPositionA();
}
else
{
rAssertMsg(0, "problem in Vehicle::TranslateCollisionIntoLocation");
}
rmt::Vector centreToContactPoint;
centreToContactPoint.Sub(pointOnOtherCar, this->rPosition());
centreToContactPoint.NormalizeSafe();
float dot = mVehicleFacing.DotProduct(centreToContactPoint);
// first pass:
// if no more than 45 degrees from facing, use trunk or hood
//
// otherwise, project on transverse and see
const float cos45 = 0.7071f;
int joint = 0;
if(dot > cos45)
{
// hood
//return mHoodJoint;
//joint = mHoodJoint;
return dl_hood;
}
else if(dot < -cos45)
{
// trunk
//return mTrunkJoint;
//joint = mTrunkJoint;
return dl_trunk;
}
else
{
float dot2 = mVehicleTransverse.DotProduct(centreToContactPoint);
if(dot2 > cos45)
{
// passenger side
//return mDoorPJoint;
//joint = mDoorPJoint;
return dl_driverside;
}
else
{
// driver side
//return mDoorDJoint;
//joint = mDoorDJoint;
return dl_passengerside;
}
}
rAssert(0); // shouldn't really ever get here.
return dl_hood;
}
//=============================================================================
// Vehicle::BeefUpHitPointsOnTrafficCarsWhenUserDriving
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void Vehicle::BeefUpHitPointsOnTrafficCarsWhenUserDriving()
{
// can't just use VT_TRAFFIC since we want the effect if we've gone to a phone booth
if( this->mVehicleID == VehicleEnum::TAXIA || // plus all others Jeff gives me
this->mVehicleID == VehicleEnum::COMPACTA ||
this->mVehicleID == VehicleEnum::MINIVANA ||
this->mVehicleID == VehicleEnum::PICKUPA ||
this->mVehicleID == VehicleEnum::SEDANA ||
this->mVehicleID == VehicleEnum::SEDANB ||
this->mVehicleID == VehicleEnum::SPORTSA ||
this->mVehicleID == VehicleEnum::SPORTSB ||
this->mVehicleID == VehicleEnum::SUVA ||
this->mVehicleID == VehicleEnum::WAGONA ||
this->mVehicleID == VehicleEnum::COFFIN ||
this->mVehicleID == VehicleEnum::HALLO ||
this->mVehicleID == VehicleEnum::SHIP ||
this->mVehicleID == VehicleEnum::WITCHCAR ||
this->mVehicleID == VehicleEnum::AMBUL ||
this->mVehicleID == VehicleEnum::BURNSARM ||
this->mVehicleID == VehicleEnum::FISHTRUC ||
this->mVehicleID == VehicleEnum::GARBAGE ||
this->mVehicleID == VehicleEnum::GLASTRUC ||
this->mVehicleID == VehicleEnum::ICECREAM ||
this->mVehicleID == VehicleEnum::ISTRUCK ||
this->mVehicleID == VehicleEnum::NUCTRUCK ||
this->mVehicleID == VehicleEnum::PIZZA ||
this->mVehicleID == VehicleEnum::SCHOOLBU ||
this->mVehicleID == VehicleEnum::VOTETRUC ||
this->mVehicleID == VehicleEnum::CBONE )
{
// only add to an undamaged car, so that you can't get in and out to repair
if(this->GetVehicleLifePercentage(this->mHitPoints) == 1.0f)
{
if(this->mDesignerParams.mHitPoints <= 1.0f)
{
this->mDesignerParams.mHitPoints += 1.0f;
this->mHitPoints = mDesignerParams.mHitPoints;
}
}
}
}
//=============================================================================
// Vehicle::GetVehicleDamagePercentage
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: float
//
//=============================================================================
float Vehicle::GetVehicleLifePercentage(float testvalue)
{
float temp = testvalue / mDesignerParams.mHitPoints;
if(temp < 0.0f)
{
// shouldn't ever really be here
// going below 0 should be caught somewhere else
temp = 0.0f;
}
rAssert(temp <= 1.0f);
rAssert(temp >= 0.0f);
return temp;
}
//=============================================================================
// Vehicle::TriggerDamage
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
float Vehicle::TriggerDamage(float amount, bool clamp)
{
// TODO - disable some updating stuff - like locomotive shit, if
// we are destroyed
if(mVehicleDestroyed)
{
// already destroyed
// do nothing
return 0.0f;
}
// try the clamp to 99% for last hit on all car types
if(clamp)//mDamageType == VDT_USER || mDamageType == VDT_AI)
{
float currentPerc = GetVehicleLifePercentage(mHitPoints);
if(currentPerc > 0.02f)
{
// we want to make sure we clamp this hit at 1% life left
float testvalue = mHitPoints;
testvalue -= amount;
if(testvalue < 0.0f)
{
testvalue = 0.0f;
}
float perc = GetVehicleLifePercentage(testvalue);
if(perc < 0.02f)
{
// the clamp case:
mHitPoints = 0.001f; // just some token amount
//only update the charactersheet if this car belongs to the player
if (mbPlayerCar == true)
{
GetCharacterSheetManager()->UpdateCarHealth( mCharacterSheetCarIndex, perc);
}
GetEventManager()->TriggerEvent(EVENT_VEHICLE_DAMAGED, (void*)this);
mNoDamageTimer = 1.0f; // set countdown
return 0.02f; // return fixed amount
}
else
{
// just normal hit
mHitPoints = testvalue; // fall through and continue
}
}
else
{
// this is the hit that takes us out
mHitPoints = 0.0f;
}
}
else
{
mHitPoints -= amount;
}
if(mHitPoints <= 0.0f)
{
// we've been destroyed
mHitPoints = 0.0f;
mVehicleDestroyed = true;
ActivateTriggers(false);
//GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED_BY_USER, (void*)this);
// move the triggering of this event to PostSubstepUpdate
//
// user car will pause for timer, other cars will send out immediately
mDamageOutResetTimer = 0.0f;
/*
// if this is a traffic, then just give'r right here
if(mDamageType == VDT_TRAFFIC)
{
ParticleAttributes pa;
pa.mType = ParticleEnum::eCarExplosion;
rmt::Vector pos = this->rPosition();
GetParticleManager()->Add(pa, pos);
TrafficManager::GetInstance()->SwapInTrafficHusk(this);
}
*/
}
// make sure the damaged event fired off either way...
GetEventManager()->TriggerEvent(EVENT_VEHICLE_DAMAGED, (void*)this);
float health = GetVehicleLifePercentage(mHitPoints);
//only update the charactersheet if this car belongs to the player
if (mbPlayerCar == true)
{
GetCharacterSheetManager()->UpdateCarHealth( mCharacterSheetCarIndex, health );
}
return health;
}
//=============================================================================
// Vehicle door handling
//=============================================================================
//
// Figure out the final position of the doors, based on current state,
// and whether someone is opening/closing them
//
bool Vehicle::NeedToOpenDoor(Door door)
{
if(!mHasDoors)
{
return false;
}
if(mDesiredDoorPosition[door] < 0.8f)
{
return true;
}
if(!HasActiveDoor(door))
{
return false;
}
return false;
}
bool Vehicle::NeedToCloseDoor(Door door)
{
// TODO : there are cases where we might want to not close it
// (physics, missing door, door left open, etc)
if(!mHasDoors)
{
return false;
}
// check if door was left open when we exited
if(mDesiredDoorPosition[door] > 0.0f)
{
return true;
}
if(!HasActiveDoor(door))
{
return false;
}
return false;
}
bool Vehicle::HasActiveDoor(Door door)
{
if(!mHasDoors)
{
return false;
}
switch(door)
{
case DOOR_DRIVER:
if(mDoorDJoint != -1)
{
int inertialJointIndex = mJointIndexToInertialJointDriverMapping[ mDoorDJoint ];
if( inertialJointIndex == -1 )
{
rWarningMsg( false, "why is there no inertial joint? doesn't this car have a door?" );
return false;
}
sim::PhysicsJointInertialEffector* inertialJoint = mInertialJointDrivers[ inertialJointIndex ];
if( inertialJoint == NULL )
{
return false;
}
if( inertialJoint->IsEnabled() )
{
return false;
}
}
break;
case DOOR_PASSENGER:
if(mDoorPJoint != -1)
{
if(mInertialJointDrivers[mJointIndexToInertialJointDriverMapping[mDoorPJoint]]->IsEnabled())
return false;
}
break;
}
return true;
}
void Vehicle::UpdateDoorState(void)
{
if(mDesiredDoorPosition[DOOR_DRIVER] == 0.0f)
{
if((mDoorDJoint != -1) && (mPhObj->GetJoint(mDoorDJoint)))
{
mDesiredDoorPosition[DOOR_DRIVER] = mPhObj->GetJoint(mDoorDJoint)->Deformation();
}
}
if(mDesiredDoorPosition[DOOR_PASSENGER] == 0.0f)
{
if((mDoorPJoint != -1) && (mPhObj->GetJoint(mDoorPJoint)))
{
mDesiredDoorPosition[DOOR_PASSENGER] = mPhObj->GetJoint(mDoorPJoint)->Deformation();
}
}
}
void Vehicle::ReleaseDoors(void)
{
mDesiredDoorPosition[DOOR_DRIVER] = 0.0f;
mDesiredDoorPosition[DOOR_PASSENGER] = 0.0f;
}
void Vehicle::PlayExplosionEffect()
{
//rmt::Vector explosionCenter = rPosition();
// explosionCenter.y += EXPLOSION_Y_OFFSET;
GetWorldPhysicsManager()->ApplyForceToDynamicsSpherical( mCollisionAreaIndex, rPosition(), EXPLOSION_EFFECT_RADIUS, EXPLOSION_FORCE );
// Lets get the explosion position as the center of the wheels
rmt::Vector explosionCenter(0,0,0);
for ( int i = 0 ; i < 4 ; i++ )
{
explosionCenter += mSuspensionWorldSpacePoints[i];
}
explosionCenter *= 0.25f;
rmt::Matrix explosionTransform = GetTransform();
explosionTransform.FillTranslate( explosionCenter );
GetBreakablesManager()->Play( BreakablesEnum::eCarExplosion, explosionTransform );
GetEventManager()->TriggerEvent( EVENT_BIG_BOOM_SOUND, this );
}
void Vehicle::AddToSimulation()
{
//DynaPhysDSG::AddToSimulation(); // greg
// jan 31, 2003
// I don't think any vehicle should do this - just use it's own ground plane
SetLocomotion( VL_PHYSICS );
}
void Vehicle::ApplyForce( const rmt::Vector& direction, float force )
{
SetLocomotion( VL_PHYSICS );
rmt::Vector& rVelocity = GetSimState()->GetLinearVelocity();
float deltaV = force / GetMass();
// Apply delta velocity
rVelocity += (direction * deltaV);
// Make it interact with the world
AddToSimulation();
// Damage the vehicle
if ( mUserDrivingCar )
{
TriggerDamage( force * s_DamageFromExplosionPlayer / EXPLOSION_FORCE, false );
}
else
{
TriggerDamage( force * s_DamageFromExplosion / EXPLOSION_FORCE, false );
}
}
bool Vehicle::AttachCollectible( StatePropCollectible* drawable )
{
if ( drawable->GetState() != 0 )
return false;
bool wasAttached = mGeometryVehicle->AttachCollectible( drawable );
if ( wasAttached )
{
GetEventManager()->TriggerEvent( EVENT_VEHICLE_COLLECTED_PROP, this );
}
return wasAttached;
}
StatePropCollectible* Vehicle::GetAttachedCollectible()
{
return mGeometryVehicle->GetAttachedCollectible();
}
void Vehicle::DetachCollectible( const rmt::Vector& velocity, bool explode )
{
mGeometryVehicle->DetachCollectible(velocity, explode);
}
// move the door to the specified location (called by the character AI during get in/out of car)
void Vehicle::MoveDoor(Door door, DoorAction action, float position)
{
rAssert(door < 2);
mDesiredDoorPosition[door] = position;
mDesiredDoorAction[door] = action;
}
// calculate the position of a single door
void Vehicle::CalcDoor(unsigned doorIndex, unsigned joint, float doorOpen)
{
// clamp desired door position to 0 -> 1
if(mDesiredDoorPosition[doorIndex] > rmt::Abs(doorOpen))
mDesiredDoorPosition[doorIndex] = rmt::Abs(doorOpen);
if(mDesiredDoorPosition[doorIndex] < 0.0f)
mDesiredDoorPosition[doorIndex] = 0.0f;
// grab the rest pose
poser::Pose* pose = mPoseEngine->GetPose();
rmt::Matrix matrix = pose->GetSkeleton()->GetJoint(joint)->restPose;
rmt::Matrix tmp;
// rotate by the scaled desired position
tmp.Identity();
tmp.FillRotateY(mDesiredDoorPosition[doorIndex] * doorOpen);
tmp.Mult(matrix);
// set the new joint position
pose->GetJoint(joint)->SetObjectMatrix(tmp);
// if we just closed the door all the way, turn off future door activity
if((mDesiredDoorAction[doorIndex] == DOORACTION_CLOSE) && (mDesiredDoorPosition[doorIndex] == 0.0f))
{
// if neccesary, reset physics state
int index = mJointIndexToInertialJointDriverMapping[joint];
if( index != -1 )
{
if(mInertialJointDrivers[index]->IsEnabled())
{
mPhObj->GetJoint(joint)->ResetDeformation();
}
}
mDesiredDoorAction[doorIndex] = DOORACTION_NONE;
}
}
void Vehicle::CalcDoors(void)
{
// angle in radians that represents a fully open door
// TODO : could be tweakable
const float doorOpen = 1.0f;
// update each door, if neccesary
if((mDesiredDoorAction[0] != DOORACTION_NONE) || (mDesiredDoorAction[1] != DOORACTION_NONE))
{
if((mDesiredDoorAction[0] != DOORACTION_NONE) && mDoorDJoint != -1)
{
CalcDoor(0, mDoorDJoint, doorOpen);
}
if((mDesiredDoorAction[1] != DOORACTION_NONE) && mDoorPJoint != -1)
{
// passenger door is backwards, so we do negative rotation
CalcDoor(1, mDoorPJoint, -1.0f * doorOpen);
}
}
}
void Vehicle::SetDriverName(const char* name)
{
if(!name)
{
mDriverName[0] = 0;
return;
}
rAssert(strlen(name) < 32);
strcpy(mDriverName, name);
}
const char* Vehicle::GetDriverName(void)
{
return mDriverName;
}
void Vehicle::SetDriver(Character* d)
{
tRefCounted::Assign(mpDriver, d);
}
void Vehicle::SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] )
{
mGeometryVehicle->SetShadowAdjustments( Adjustments );
}
void Vehicle::SetShininess( unsigned char EnvRef )
{
mGeometryVehicle->SetShininess( EnvRef );
}
//=============================================================================
// Vehicle::GetTopSpeedKmh
//=============================================================================
// Description: returns the top speed of which this vehicle is capable in world
// units, not KPH
//
// Parameters: NONE
//
// Return: float
//
//=============================================================================
float Vehicle::GetTopSpeed() const
{
return mDesignerParams.mDpTopSpeedKmh * ( 1000.0f / 3600.0f );
}