summaryrefslogtreecommitdiffstats
path: root/game/code/worldsim
diff options
context:
space:
mode:
authorSvxy <aidan61605@gmail.com>2023-05-31 23:31:32 +0200
committerSvxy <aidan61605@gmail.com>2023-05-31 23:31:32 +0200
commiteb4b3404aa00220d659e532151dab13d642c17a3 (patch)
tree7e1107c4995489a26c4007e41b53ea8d00ab2134 /game/code/worldsim
downloadThe-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.gz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.bz2
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.lz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.xz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.zst
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.zip
Diffstat (limited to 'game/code/worldsim')
-rw-r--r--game/code/worldsim/allworldsim.cpp9
-rw-r--r--game/code/worldsim/avatar.cpp1101
-rw-r--r--game/code/worldsim/avatar.h158
-rw-r--r--game/code/worldsim/avatarmanager.cpp511
-rw-r--r--game/code/worldsim/avatarmanager.h87
-rw-r--r--game/code/worldsim/character/aicharactercontroller.cpp112
-rw-r--r--game/code/worldsim/character/aicharactercontroller.h27
-rw-r--r--game/code/worldsim/character/allcharacter.cpp7
-rw-r--r--game/code/worldsim/character/character.cpp5171
-rw-r--r--game/code/worldsim/character/character.h1428
-rw-r--r--game/code/worldsim/character/charactercontroller.cpp1668
-rw-r--r--game/code/worldsim/character/charactercontroller.h308
-rw-r--r--game/code/worldsim/character/charactermanager.cpp2488
-rw-r--r--game/code/worldsim/character/charactermanager.h240
-rw-r--r--game/code/worldsim/character/charactermappable.cpp281
-rw-r--r--game/code/worldsim/character/charactermappable.h126
-rw-r--r--game/code/worldsim/character/characterrenderable.cpp581
-rw-r--r--game/code/worldsim/character/characterrenderable.h100
-rw-r--r--game/code/worldsim/character/charactertarget.cpp312
-rw-r--r--game/code/worldsim/character/charactertarget.h72
-rw-r--r--game/code/worldsim/character/controllereventhandler.h54
-rw-r--r--game/code/worldsim/character/footprint/allfootprint.cpp1
-rw-r--r--game/code/worldsim/character/footprint/footprint.cpp49
-rw-r--r--game/code/worldsim/character/footprint/footprint.h73
-rw-r--r--game/code/worldsim/character/footprint/footprintmanager.cpp252
-rw-r--r--game/code/worldsim/character/footprint/footprintmanager.h105
-rw-r--r--game/code/worldsim/coins/allcoins.cpp2
-rw-r--r--game/code/worldsim/coins/coinmanager.cpp1132
-rw-r--r--game/code/worldsim/coins/coinmanager.h168
-rw-r--r--game/code/worldsim/coins/sparkle.cpp1222
-rw-r--r--game/code/worldsim/coins/sparkle.h174
-rw-r--r--game/code/worldsim/groundplanepool.cpp326
-rw-r--r--game/code/worldsim/groundplanepool.h71
-rw-r--r--game/code/worldsim/harass/allharass.cpp1
-rw-r--r--game/code/worldsim/harass/chasemanager.cpp911
-rw-r--r--game/code/worldsim/harass/chasemanager.h188
-rw-r--r--game/code/worldsim/hitnrunmanager.cpp1118
-rw-r--r--game/code/worldsim/hitnrunmanager.h150
-rw-r--r--game/code/worldsim/huskpool.cpp309
-rw-r--r--game/code/worldsim/huskpool.h56
-rw-r--r--game/code/worldsim/parkedcars/allparkedcars.cpp1
-rw-r--r--game/code/worldsim/parkedcars/parkedcarmanager.cpp943
-rw-r--r--game/code/worldsim/parkedcars/parkedcarmanager.h127
-rw-r--r--game/code/worldsim/ped/allped.cpp2
-rw-r--r--game/code/worldsim/ped/pedestrian.cpp448
-rw-r--r--game/code/worldsim/ped/pedestrian.h128
-rw-r--r--game/code/worldsim/ped/pedestrianmanager.cpp1593
-rw-r--r--game/code/worldsim/ped/pedestrianmanager.h241
-rw-r--r--game/code/worldsim/physicsairef.h51
-rw-r--r--game/code/worldsim/redbrick/allredbrick.cpp13
-rw-r--r--game/code/worldsim/redbrick/geometryvehicle.cpp3384
-rw-r--r--game/code/worldsim/redbrick/geometryvehicle.h322
-rw-r--r--game/code/worldsim/redbrick/physicslocomotion.cpp2069
-rw-r--r--game/code/worldsim/redbrick/physicslocomotion.h135
-rw-r--r--game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp1686
-rw-r--r--game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp927
-rw-r--r--game/code/worldsim/redbrick/redbrickcollisionsolveragent.h57
-rw-r--r--game/code/worldsim/redbrick/rootmatrixdriver.cpp29
-rw-r--r--game/code/worldsim/redbrick/rootmatrixdriver.h77
-rw-r--r--game/code/worldsim/redbrick/suspensionjointdriver.cpp130
-rw-r--r--game/code/worldsim/redbrick/suspensionjointdriver.h71
-rw-r--r--game/code/worldsim/redbrick/trafficbodydrawable.cpp82
-rw-r--r--game/code/worldsim/redbrick/trafficbodydrawable.h39
-rw-r--r--game/code/worldsim/redbrick/trafficlocomotion.cpp1421
-rw-r--r--game/code/worldsim/redbrick/trafficlocomotion.h270
-rw-r--r--game/code/worldsim/redbrick/vehicle.cpp6483
-rw-r--r--game/code/worldsim/redbrick/vehicle.h989
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp167
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h40
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp4
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp807
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h78
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp16
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h49
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp263
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h60
-rw-r--r--game/code/worldsim/redbrick/vehicleeventlistener.cpp304
-rw-r--r--game/code/worldsim/redbrick/vehicleeventlistener.h45
-rw-r--r--game/code/worldsim/redbrick/vehicleinit.cpp2309
-rw-r--r--game/code/worldsim/redbrick/vehiclelocomotion.cpp46
-rw-r--r--game/code/worldsim/redbrick/vehiclelocomotion.h41
-rw-r--r--game/code/worldsim/redbrick/wheel.cpp533
-rw-r--r--game/code/worldsim/redbrick/wheel.h107
-rw-r--r--game/code/worldsim/skidmarks/SkidMarkGenerator.cpp272
-rw-r--r--game/code/worldsim/skidmarks/SkidMarkGenerator.h187
-rw-r--r--game/code/worldsim/skidmarks/allskidmarks.cpp3
-rw-r--r--game/code/worldsim/skidmarks/skidmark.cpp442
-rw-r--r--game/code/worldsim/skidmarks/skidmark.h85
-rw-r--r--game/code/worldsim/skidmarks/skidmarkmanager.cpp178
-rw-r--r--game/code/worldsim/skidmarks/skidmarkmanager.h94
-rw-r--r--game/code/worldsim/spawn/allspawn.cpp1
-rw-r--r--game/code/worldsim/spawn/spawnmanager.cpp107
-rw-r--r--game/code/worldsim/spawn/spawnmanager.h154
-rw-r--r--game/code/worldsim/traffic/alltraffic.cpp1
-rw-r--r--game/code/worldsim/traffic/trafficmanager.cpp2169
-rw-r--r--game/code/worldsim/traffic/trafficmanager.h312
-rw-r--r--game/code/worldsim/traffic/trafficmodelgroup.h145
-rw-r--r--game/code/worldsim/traffic/trafficvehicle.h135
-rw-r--r--game/code/worldsim/vehiclecentral.cpp2278
-rw-r--r--game/code/worldsim/vehiclecentral.h205
-rw-r--r--game/code/worldsim/worldcollisionsolveragent.cpp365
-rw-r--r--game/code/worldsim/worldcollisionsolveragent.h80
-rw-r--r--game/code/worldsim/worldobject.cpp125
-rw-r--r--game/code/worldsim/worldobject.h60
-rw-r--r--game/code/worldsim/worldphysicsmanager.cpp2921
-rw-r--r--game/code/worldsim/worldphysicsmanager.h296
106 files changed, 58351 insertions, 0 deletions
diff --git a/game/code/worldsim/allworldsim.cpp b/game/code/worldsim/allworldsim.cpp
new file mode 100644
index 0000000..1bc292d
--- /dev/null
+++ b/game/code/worldsim/allworldsim.cpp
@@ -0,0 +1,9 @@
+#include <worldsim/avatar.cpp>
+#include <worldsim/avatarmanager.cpp>
+#include <worldsim/groundplanepool.cpp>
+#include <worldsim/hitnrunmanager.cpp>
+#include <worldsim/vehiclecentral.cpp>
+#include <worldsim/worldcollisionsolveragent.cpp>
+#include <worldsim/worldobject.cpp>
+#include <worldsim/worldphysicsmanager.cpp>
+#include <worldsim/huskpool.cpp>
diff --git a/game/code/worldsim/avatar.cpp b/game/code/worldsim/avatar.cpp
new file mode 100644
index 0000000..69e5f44
--- /dev/null
+++ b/game/code/worldsim/avatar.cpp
@@ -0,0 +1,1101 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class Avatar
+//
+// History: 4/3/2002 + Created -- TBJ
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/avatar.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+#include <input/inputmanager.h>
+
+#include <ai/vehicle/vehicleai.h>
+
+#include <roads/geometry.h>
+#include <roads/road.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <roads/intersection.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+#include <gameflow/gameflow.h>
+#include <supersprint/supersprintmanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// If true, display avatar coordinates on screen
+//
+bool Avatar::s_displayCoordinates = false;
+
+struct AvatarInputManager
+{
+ AvatarInputManager( void )
+ :
+ mVehicleMappableHandle( -1 ),
+#ifdef RAD_PS2
+ mVehicleMappableHandleWheel0( -1 ),
+ mVehicleMappableHandleWheel1( -1 ),
+#endif
+ mCharacterMappableHandle( -1 )
+ {
+ HeapMgr()->PushHeap (GMA_LEVEL_OTHER);
+
+ mpVehicleMappable = new VehicleMappable;
+ mpVehicleMappable->AddRef();
+
+#ifdef RAD_PS2
+ mpVehicleMappableUSB0 = new VehicleMappable;
+ mpVehicleMappableUSB0->AddRef();
+
+ mpVehicleMappableUSB1 = new VehicleMappable;
+ mpVehicleMappableUSB1->AddRef();
+#endif
+
+ mpHumanVehicleController = new HumanVehicleController;
+ mpHumanVehicleController->AddRef();
+
+ mpInCarCharacterMappable = new InCarCharacterMappable;
+ mpInCarCharacterMappable->AddRef();
+
+ mpBipedCharacterMappable = new BipedCharacterMappable;
+ mpBipedCharacterMappable->AddRef();
+
+ mpCameraRelativeCharacterController = new CameraRelativeCharacterController;
+ mpCameraRelativeCharacterController->AddRef();
+
+ HeapMgr()->PopHeap (GMA_LEVEL_OTHER);
+ }
+ ~AvatarInputManager( void )
+ {
+ mpVehicleMappable->ReleaseController();
+ mpVehicleMappable->Release();
+
+#ifdef RAD_PS2
+ mpVehicleMappableUSB0->Release();
+ mpVehicleMappableUSB1->Release();
+#endif
+
+ mpHumanVehicleController->ReleaseVehicleMappable();
+ mpHumanVehicleController->Release();
+
+ mpInCarCharacterMappable->SetCharacterController( NULL );
+ mpInCarCharacterMappable->Release();
+
+ mpBipedCharacterMappable->SetCharacterController( NULL );
+ mpBipedCharacterMappable->Release();
+
+ mpCameraRelativeCharacterController->SetCamera (0);
+ mpCameraRelativeCharacterController->Release();
+
+ int x =5;
+ }
+ VehicleMappable* mpVehicleMappable;
+ VehicleMappable* mpVehicleMappableUSB0;
+ VehicleMappable* mpVehicleMappableUSB1;
+ HumanVehicleController* mpHumanVehicleController;
+ InCarCharacterMappable* mpInCarCharacterMappable;
+ BipedCharacterMappable* mpBipedCharacterMappable;
+ CameraRelativeCharacterController* mpCameraRelativeCharacterController;
+ int mVehicleMappableHandle;
+ int mVehicleMappableHandleWheel0;
+ int mVehicleMappableHandleWheel1;
+ int mCharacterMappableHandle;
+};
+
+
+void UnRegisterMappableHandle( int controllerId, int& handle )
+{
+ if ( handle > -1 )
+ {
+ // Detach the mappable.
+ //
+ InputManager::GetInstance( )->UnregisterMappable( controllerId, handle );
+ handle = -1;
+ }
+}
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Avatar::Avatar
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Avatar::Avatar()
+:
+mHasBeenUpdatedThisFrame( false ),
+mLastRoadSegment( NULL ),
+mLastRoadSegmentT( 0.0f ),
+mLastRoadT( 0.0f ),
+mControllerId( INVALID_CONTROLLER ),
+mPlayerId( 0 ),
+mpCharacter( 0 ),
+mpVehicle( 0 ),
+mpAvatarInputManager( 0 )
+{
+ mLastPathElement.elem = NULL;
+
+ mpAvatarInputManager = new AvatarInputManager;
+
+ GetCheatInputSystem()->RegisterCallback( this );
+
+ //
+ // Just in case the cheat is set before object creation
+ //
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_SHOW_AVATAR_POSITION ) )
+ {
+ s_displayCoordinates = true;
+ }
+}
+
+//==============================================================================
+// Avatar::~Avatar
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Avatar::~Avatar()
+{
+ Destroy( );
+
+ if ( mpAvatarInputManager )
+ {
+ delete mpAvatarInputManager;
+ mpAvatarInputManager = 0;
+ }
+
+ GetCheatInputSystem()->UnregisterCallback( this );
+}
+
+void Avatar::Destroy( void )
+{
+
+ if ( mpCharacter )
+ {
+ mpCharacter->SetController( NULL );
+ mpCharacter->Release( );
+ mpCharacter = 0;
+ }
+ if ( mpVehicle )
+ {
+ mpVehicle->Release( );
+ mpVehicle = 0;
+ }
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mVehicleMappableHandle );
+#ifdef RAD_PS2
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel0 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB0, mpAvatarInputManager->mVehicleMappableHandleWheel0 );
+ }
+
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel1 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB1, mpAvatarInputManager->mVehicleMappableHandleWheel1 );
+ }
+#endif
+ mControllerId = INVALID_CONTROLLER;
+}
+
+/*
+==============================================================================
+Avatar::GetControllerId
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: int
+
+=============================================================================
+*/
+int Avatar::GetControllerId( void ) const
+{
+ return mControllerId;
+}
+
+/*
+==============================================================================
+Avatar::SetControllerId
+==============================================================================
+Description: Comment
+
+Parameters: ( int id )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetControllerId( int id )
+{
+ mControllerId = id;
+}
+/*
+==============================================================================
+Avatar::GetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+Character* Avatar::GetCharacter( void ) const
+{
+ return mpCharacter;
+}
+
+/*
+==============================================================================
+Avatar::SetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetCharacter( Character* pCharacter )
+{
+ tRefCounted::Assign( mpCharacter, pCharacter );
+ if( mpCharacter )
+ {
+ tColour shadowColour = ::GetGameplayManager()->GetControllerColour( mControllerId );
+ mpCharacter->SetShadowColour( shadowColour );
+ }
+}
+
+/*
+==============================================================================
+Avatar::GetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+Vehicle* Avatar::GetVehicle( void ) const
+{
+ return mpVehicle;
+}
+
+/*
+==============================================================================
+Avatar::SetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( Vehicle* pVehicle )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetVehicle( Vehicle* pVehicle )
+{
+ tRefCounted::Assign( mpVehicle, pVehicle );
+
+ if( pVehicle )
+ {
+ int playerID = 0; // normal game defaults player to zero
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT )
+ {
+ playerID = SuperSprintManager::GetInstance()->GetOnlyHumanPlayerID();
+ if( this->mPlayerId != playerID )
+ {
+ return;
+ }
+ }
+
+ // can be -1 if in supersprint and more than 1 human is participating...
+ if( playerID == -1 )
+ {
+ return;
+ }
+
+ SuperCam* sc = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ if ( sc && sc->GetType() == SuperCam::BUMPER_CAM )
+ {
+ //Only stop rendering for bumpercams.
+ pVehicle->DrawVehicle( false );
+ }
+ else
+ {
+ pVehicle->DrawVehicle( true );
+ }
+ }
+}
+
+/*
+==============================================================================
+Avatar::GetIntoVehicle
+==============================================================================
+Description: Pseudo code.
+
+ 1. Get a "human" vehicle controller.
+ 2. Init HumanVehicleController with mControllerId.
+ 3. Register with ControllerSystem.
+ 4. Get pointer to Vehicle via VehicleManager using vehicleId.
+ 5. Set Avatar member data mpVehicle to Vehicle pointer from 4.
+ 6. Set Controller for Vehicle.
+
+
+Parameters: ( Vehicle* pVehicle)
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetInCarController( void )
+{
+ rAssert( mpVehicle );
+ mpVehicle->mGeometryVehicle->FadeRoof( true );
+ mpVehicle->mGeometryVehicle->EnableLights( true );
+ mpVehicle->SetUserDrivingCar( true );
+ mpVehicle->SetDisableGasAndBrake(false);
+
+ // in case we just got into a traffi car, make sure brake lights are off
+ mpVehicle->mGeometryVehicle->HideBrakeLights();
+ mpVehicle->mBrakeLightsOn = false;
+ mpVehicle->mGeometryVehicle->HideReverseLights();
+ mpVehicle->mReverseLightsOn = false;
+
+ int vehicleId = GetVehicleCentral( )->GetVehicleId( mpVehicle );
+ // Detach the vehicle controller.
+ //
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mVehicleMappableHandle );
+
+
+ HumanVehicleController* pVehicleController = mpAvatarInputManager->mpHumanVehicleController;
+
+ VehicleMappable* pVehicleMappable = mpAvatarInputManager->mpVehicleMappable;
+ pVehicleController->Create( mpVehicle, pVehicleMappable, mControllerId );
+ pVehicleMappable->SetController( pVehicleController );
+
+ mpAvatarInputManager->mVehicleMappableHandle = InputManager::GetInstance( )->RegisterMappable( mControllerId, pVehicleMappable );
+
+ bool wheelAttached = false;
+#ifdef RAD_PS2
+ if ( GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT )
+ {
+ if ( mControllerId == Input::USB0 ||
+ ( mControllerId != Input::USB0 &&
+ mControllerId != Input::USB1 &&
+ GetInputManager()->IsControllerInPort( Input::USB0 )
+ )
+ )
+ {
+ mpAvatarInputManager->mVehicleMappableHandleWheel0 = InputManager::GetInstance( )->RegisterMappable( Input::USB0, mpAvatarInputManager->mpVehicleMappableUSB0 );
+ pVehicleController->SetWheel( mpAvatarInputManager->mpVehicleMappableUSB0, 0 );
+ mpAvatarInputManager->mpVehicleMappableUSB0->SetController( pVehicleController );
+ GetInputManager()->SetRumbleForDevice( Input::USB0, GetInputManager()->IsRumbleEnabled() );
+ wheelAttached = true;
+ }
+ else if ( mControllerId == Input::USB1 ||
+ ( mControllerId != Input::USB0 &&
+ mControllerId != Input::USB1 &&
+ GetInputManager()->IsControllerInPort( Input::USB1 )
+ )
+ )
+ {
+ mpAvatarInputManager->mVehicleMappableHandleWheel1 = InputManager::GetInstance( )->RegisterMappable( Input::USB1, mpAvatarInputManager->mpVehicleMappableUSB1 );
+ pVehicleController->SetWheel( mpAvatarInputManager->mpVehicleMappableUSB1, 1 );
+ mpAvatarInputManager->mpVehicleMappableUSB1->SetController( pVehicleController );
+ GetInputManager()->SetRumbleForDevice( Input::USB1, GetInputManager()->IsRumbleEnabled() );
+ wheelAttached = true;
+ }
+ }
+#endif
+
+ if ( !wheelAttached )
+ {
+ GetInputManager()->SetRumbleForDevice( mControllerId, GetInputManager()->IsRumbleEnabled() );
+ }
+
+ if ( !GetGameplayManager()->mIsDemo )
+ {
+ GetVehicleCentral( )->SetVehicleController( vehicleId, pVehicleController );
+ }
+
+ // First things, detach the in car character controller.
+ //
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+
+ CharacterMappable* pCharacterMappable = mpAvatarInputManager->mpInCarCharacterMappable;
+ CameraRelativeCharacterController* pCharacterController = mpAvatarInputManager->mpCameraRelativeCharacterController;
+ pCharacterController->Create( mpCharacter, pCharacterMappable );
+
+ mpAvatarInputManager->mCharacterMappableHandle = InputManager::GetInstance()->RegisterMappable( mControllerId, pCharacterMappable );
+ mpCharacter->SetController( pCharacterController );
+
+ // new:
+ // for changing the physics/collision representation if applicabble
+ mpVehicle->SetInCarSimState();
+
+ mpVehicle->BeefUpHitPointsOnTrafficCarsWhenUserDriving();
+
+
+}
+
+void Avatar::SetCameraTargetToVehicle( bool cut )
+{
+ // Set the camera target.
+ //
+ GetSuperCamManager()->GetSCC( mPlayerId )->SetTarget( mpVehicle );
+
+ // Select the follow cam.
+ //
+ GetSuperCamManager()->GetSCC( mPlayerId )->SelectSuperCam( SuperCam::FOLLOW_CAM, cut ? (SuperCamCentral::CUT|SuperCamCentral::FORCE ) : 0, cut ? 0 : 7000 );
+}
+
+void Avatar::GetIntoVehicleStart( Vehicle* pVehicle)
+{
+ SetVehicle( pVehicle );
+
+ // check to see if the target vehicle is currently controlled by traffic
+ // if it is, we need to work a little harder to carjack it :-)
+ if(pVehicle->GetLocomotionType() == VL_TRAFFIC)
+ {
+ pVehicle->SetLocomotion(VL_PHYSICS);
+ pVehicle->mHijackedByUser = true;
+ }
+
+ if(GetSuperCamManager()->GetSCC( mPlayerId )->GetPreferredFollowCam() != SuperCam::BUMPER_CAM)
+ {
+ SetCameraTargetToVehicle( );
+ }
+
+ CGuiScreenMultiHud* currentHUD = GetCurrentHud();
+ if( currentHUD != NULL )
+ {
+ currentHUD->GetHudMap( mPlayerId )->UnregisterIcon( 0 );
+ currentHUD->GetHudMap( mPlayerId )->RegisterIcon( HudMapIcon::ICON_PLAYER,
+ rmt::Vector( 0, 0, 0 ),
+ pVehicle );
+ }
+
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+
+
+}
+
+void Avatar::GetIntoVehicleEnd( Vehicle* pVehicle)
+{
+ SetCameraTargetToVehicle( );
+
+ SetInCarController( );
+ // this call is to record the rest seating positions of the driver and passenger
+ pVehicle->RecordRestSeatingPositionsOnEntry();
+}
+
+/*
+==============================================================================
+Avatar::GetOutOfVehicle
+==============================================================================
+Description: Pseudo code
+ 2. Set Controller for Vehicle to NULL ( or AI controller )
+ 3. Set Vehicle member data to NULL.
+ 4. Unregister controller with controller system.
+
+Parameters: ( Vehicle* pVehicle)
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::GetOutOfVehicleStart( Vehicle* pVehicle)
+{
+ // First things, detach the in car character controller.
+ //
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+
+ // Detach the vehicle controller.
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mVehicleMappableHandle );
+#ifdef RAD_PS2
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel0 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB0, mpAvatarInputManager->mVehicleMappableHandleWheel0 );
+ GetInputManager()->SetRumbleForDevice( Input::USB0, false );
+ }
+
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel1 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB1, mpAvatarInputManager->mVehicleMappableHandleWheel1 );
+ GetInputManager()->SetRumbleForDevice( Input::USB1, false );
+ }
+#endif
+
+ // attach the on-foot controller
+ // we do this early (don't wait for end of get out of car) so that we can abort \
+ // the door close if there is movement)
+ SetOutOfCarController();
+
+ int vehicleId = GetVehicleCentral()->GetVehicleId( pVehicle, false );
+
+ if ( !GetGameplayManager()->mIsDemo && (vehicleId != -1))
+ {
+ GetVehicleCentral( )->SetVehicleController( vehicleId, 0 );
+ }
+
+ if(GetSuperCamManager()->GetSCC( mPlayerId )->GetPreferredFollowCam() == SuperCam::BUMPER_CAM)
+ {
+ SetCameraTargetToCharacter();
+ }
+}
+
+void Avatar::SetOutOfCarController( void )
+{
+ if( GetVehicle() )
+ {
+ GetVehicle()->mGeometryVehicle->FadeRoof( false );
+
+ if( !TrafficManager::GetInstance()->IsVehicleTrafficVehicle( GetVehicle() ) )
+ {
+ GetVehicle()->mGeometryVehicle->EnableLights( false );
+ }
+
+ GetVehicle()->SetOutOfCarSimState();
+
+ GetVehicle()->SetUserDrivingCar( false );
+ }
+
+ // Then, make a new character controller.
+ //
+ CharacterMappable* pCharacterMappable = mpAvatarInputManager->mpBipedCharacterMappable;
+ CameraRelativeCharacterController* pCharacterController = mpAvatarInputManager->mpCameraRelativeCharacterController;
+ pCharacterController->Create( mpCharacter, pCharacterMappable );
+
+ // Register the new character controller.
+ //
+ mpAvatarInputManager->mCharacterMappableHandle = InputManager::GetInstance()->RegisterMappable( mControllerId, pCharacterMappable );
+ mpCharacter->SetController( pCharacterController );
+
+ pCharacterController->SetCamera( GetSuperCamManager()->GetSCC( mPlayerId )->GetCamera() );
+
+ GetInputManager()->SetRumbleForDevice( mControllerId, false );
+}
+
+void Avatar::SetCameraTargetToCharacter( bool cut )
+{
+ // Set the camera target.
+ //
+ GetSuperCamManager()->GetSCC( mPlayerId )->SetTarget( mpCharacter->GetTarget( ) );
+ // Select the follow cam.
+ //
+#ifdef RAD_WIN32
+ GetSuperCamManager()->GetSCC( mPlayerId )->SelectSuperCam( SuperCam::ON_FOOT_CAM, cut ? SuperCamCentral::CUT : 0 );
+#else
+ GetSuperCamManager()->GetSCC( mPlayerId )->SelectSuperCam( SuperCam::WALKER_CAM, cut ? SuperCamCentral::CUT : 0 );
+#endif
+}
+
+void Avatar::GetOutOfVehicleEnd( Vehicle* pVehicle)
+{
+ pVehicle->DrawVehicle(true); // just in case it was in bumper cam
+
+ SetCameraTargetToCharacter();
+
+ SetVehicle( 0 );
+
+ CGuiScreenMultiHud* currentHUD = GetCurrentHud();
+ if( currentHUD != NULL )
+ {
+ currentHUD->GetHudMap( mPlayerId )->UnregisterIcon( 0 );
+ currentHUD->GetHudMap( mPlayerId )->RegisterIcon( HudMapIcon::ICON_PLAYER,
+ rmt::Vector( 0, 0, 0 ),
+ mpCharacter->GetTarget() );
+ }
+}
+ /*
+==============================================================================
+Avatar::IsInCar
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Avatar::IsInCar( void ) const
+{
+ rAssert( mpCharacter );
+ return mpCharacter->IsInCar( ) && (mpVehicle != NULL);
+}
+
+
+const void Avatar::GetPosition( rmt::Vector& pos ) const
+{
+ if( this->IsInCar() )
+ {
+ mpVehicle->GetPosition( &pos );
+ }
+ else
+ {
+ mpCharacter->GetPosition( pos );
+ }
+}
+
+const void Avatar::GetHeading( rmt::Vector& irHeading ) const
+{
+ if( this->IsInCar() )
+ {
+ mpVehicle->GetHeading( &irHeading );
+ }
+ else
+ {
+ mpCharacter->GetFacing( irHeading );
+ }
+}
+
+const void Avatar::GetNormalizedHeadingSafe( rmt::Vector& heading ) const
+{
+ if( this->IsInCar() )
+ {
+ assert( mpVehicle != NULL );
+ mpVehicle->GetVelocity( &heading );
+ float speedMps = mpVehicle->GetSpeedKmh() * KPH_2_MPS;
+ if( rmt::Fabs(speedMps) < 0.00001f )
+ {
+ heading.Set( 0.0f, 0.0f, 0.0f );
+ }
+ else
+ {
+ heading.Scale( 1.0f/speedMps );
+ }
+ }
+ else
+ {
+ // for characters, facing is same as heading (no skidding/sliding)
+ // I hope...
+ assert( mpCharacter != NULL );
+ mpCharacter->GetFacing( heading );
+ }
+ assert( rmt::Epsilon( heading.MagnitudeSqr(), 1.0f, 0.00001f ) ||
+ rmt::Epsilon( heading.MagnitudeSqr(), 0.0f, 0.00001f ) );
+}
+
+const void Avatar::GetVelocity( rmt::Vector& vel ) const
+{
+ if( this->IsInCar() )
+ {
+ assert( mpVehicle != NULL );
+ mpVehicle->GetVelocity( &vel );
+ }
+ else
+ {
+ // Not sure here if the Choreo puppet's velocity value
+ // (which is what Character::GetVelocity() retrieves)
+ // is a good one. So we just track our own.
+ assert( mpCharacter != NULL );
+ mpCharacter->GetVelocity( vel );
+ }
+}
+
+const float Avatar::GetSpeedMps() const
+{
+ if( this->IsInCar() )
+ {
+ assert( mpVehicle != NULL );
+ return mpVehicle->GetSpeedKmh() * KPH_2_MPS;
+ }
+ else
+ {
+ assert( mpCharacter != NULL );
+ return mpCharacter->GetSpeed();
+ }
+
+}
+
+void Avatar::OnCheatEntered( eCheatID cheatID, bool isEnabled )
+{
+ if( cheatID == CHEAT_ID_SHOW_AVATAR_POSITION )
+ {
+ s_displayCoordinates = isEnabled;
+ }
+}
+
+/*
+==============================================================================
+Avatar::Update
+==============================================================================
+Description: Comment
+
+Parameters: (float dt)
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::Update(float dt)
+{
+ mHasBeenUpdatedThisFrame = false;
+#ifndef FINAL
+ char buffy[256];
+ rmt::Vector posn;
+ const pddiColour YELLOW(255,255,0);
+
+ //
+ // Coordinate display cheat
+ //
+ if( s_displayCoordinates )
+ {
+ if( mpCharacter != NULL )
+ {
+ GetPosition( posn );
+ }
+ else
+ {
+ posn.Set( 0.0f, 0.0f, 0.0f );
+ }
+ sprintf( buffy, "Avatar %d position: %f %f %f", mPlayerId, posn.x, posn.y, posn.z );
+ p3d::pddi->DrawString( buffy, 40, 40 + ( mPlayerId * 20), YELLOW );
+ }
+#endif
+}
+
+void Avatar::GetLastPathInfo(
+ RoadManager::PathElement& oElem,
+ RoadSegment*& oSeg,
+ float& oSegT,
+ float& oRoadT )
+{
+ oElem.elem = NULL;
+ oSeg = NULL;
+ oSegT = 0.0f;
+ oRoadT = 0.0f;
+
+ // don't do it again if already done
+ if( !mHasBeenUpdatedThisFrame )
+ {
+ mHasBeenUpdatedThisFrame = true;
+ //////////////////////////////////////////////////////////////////////
+ // Update the Avatar's closest path entities in the world.
+
+ rmt::Vector myPos;
+ GetPosition( myPos );
+
+
+ RoadSegment* seg = NULL;
+ float segT = 0.0f;
+ float roadT = 0.0f;
+ RoadManager::PathElement elem;
+ elem.elem = NULL;
+
+ /*
+ RoadManager::PathfindingOptions options = RoadManager::PO_SEARCH_SHORTCUTS;
+ RoadManager::GetInstance()->FindClosestPathElement(
+ pos, 100.0f, options, tmpPathElem, tmpSeg, tmpSegT, tmpRoadT );
+ */
+
+ // Special initial case:
+ // If my last path info is completely unset (started off the road)
+ // then we fetch the closest path element using Devin's thing
+ if( mLastPathElement.elem == NULL && mLastRoadSegment == NULL )
+ {
+ RoadSegment* closestSeg = NULL;
+ float dummy;
+ GetIntersectManager()->FindClosestAnyRoad( myPos, 100.0f, closestSeg, dummy );
+
+ seg = (RoadSegment*) closestSeg;
+ segT = RoadManager::DetermineSegmentT( myPos, seg );
+ roadT = RoadManager::DetermineRoadT( seg, segT );
+ elem.elem = seg->GetRoad();
+ elem.type = RoadManager::ET_NORMALROAD;
+ }
+ else
+ {
+ VehicleAI::FindClosestPathElement( myPos, elem, seg, segT, roadT, true );
+ }
+
+ if( elem.elem == NULL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_AVATAR_OFF_ROAD );
+
+ oElem = mLastPathElement;
+ oSeg = mLastRoadSegment;
+ oSegT = mLastRoadSegmentT;
+ oRoadT = mLastRoadT;
+ return;
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_AVATAR_ON_ROAD );
+ }
+
+ rAssert( elem.elem != NULL );
+
+ mLastPathElement = elem;
+ if( seg )
+ {
+ mLastRoadSegment = seg;
+ mLastRoadSegmentT = segT;
+ mLastRoadT = roadT;
+ }
+
+ /*
+ //
+ // true means not to ignore the path element we just found
+ // default is true because we could have lastpathelement == elem
+ // but we could be on different segments...
+ //
+ bool OK = true;
+
+ if( mLastPathElement != elem )
+ {
+ if( mLastPathElement.elem == NULL )
+ {
+ OK = true;
+ }
+ else
+ {
+ Road* lastRoad = NULL;
+ Intersection* lastInt = NULL;
+ Road* currRoad = NULL;
+ Intersection* currInt = NULL;
+
+ // temporary type: 0 = intersection, 1 = road, 2 = shortcut
+
+ // Figure out what type is LAST
+ int lastElemType = 0;
+ if( mLastPathElement.type == RoadManager::ET_NORMALROAD )
+ {
+ lastRoad = (Road*) mLastPathElement.elem;
+ lastElemType++;
+
+ if( lastRoad->GetShortCut() )
+ {
+ lastElemType++;
+ }
+ }
+ else
+ {
+ lastInt = (Intersection*) mLastPathElement.elem;
+ }
+
+ // Figure out what type is CURR
+ int currElemType = 0;
+ if( elem.type == RoadManager::ET_NORMALROAD )
+ {
+ currRoad = (Road*) elem.elem;
+ currElemType++;
+
+ if( currRoad->GetShortCut() )
+ {
+ currElemType++;
+ }
+ }
+ else
+ {
+ currInt = (Intersection*) elem.elem;
+ }
+
+ //
+ // Start resolving ...
+ ///////////////
+ // LAST: Intersection
+ if( lastElemType == 0 )
+ {
+ rAssert( lastInt );
+ if( currElemType == 2 )// CURR: shortcut
+ {
+ rAssert( currRoad );
+ if( lastInt == (Intersection*) currRoad->GetSourceIntersection() ||
+ lastInt == (Intersection*) currRoad->GetDestinationIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else
+ {
+ OK = true;
+ }
+ }
+ /////////////////
+ // LAST: Normal road
+ else if( lastElemType == 1 )
+ {
+ rAssert( lastRoad );
+
+ if( currElemType == 0 ) // CURR: intersection
+ {
+ rAssert( currInt );
+ if( currInt == (Intersection*)lastRoad->GetSourceIntersection() ||
+ currInt == (Intersection*)lastRoad->GetDestinationIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else if( currElemType == 1 ) // CURR: normal road
+ {
+ rAssert( currRoad );
+ OK = true;
+ }
+ else // CURR: shortcut
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ }
+ ////////////////
+ // LAST: Shortcut
+ else
+ {
+ rAssert( lastRoad );
+
+ if( currElemType == 0 ) // CURR: intersection
+ {
+ rAssert( currInt );
+
+ // NOTE:
+ // This is the fix to Level 3 Mission 7 where the limo drives over the where the shortcuts
+ // and normal road segments merge near the squidport.... This was where its last good element
+ // was found... It then transitted onto the intersection, but rejected it because back then
+ // we only tested for the destination intersection... So it was stuck there for a long time
+ // but didn't realize it until it hit the next waypoint (wayy... over by the dam) and decided
+ // it needed to turn back.
+ if( currInt == (Intersection*)lastRoad->GetDestinationIntersection() ||
+ currInt == (Intersection*)lastRoad->GetSourceIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else if( currElemType == 1 ) // CURR: normal road
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ else // CURR: shortcut
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ }
+ }
+ }
+
+ if( OK )
+ {
+ mLastPathElement = elem;
+ if( seg )
+ {
+ if( mLastRoadSegment != seg )
+ {
+ mLastRoadSegment = seg;
+ }
+ mLastRoadSegmentT = segT;
+ mLastRoadT = roadT;
+ }
+ }
+ */
+ }
+
+ oElem = mLastPathElement;
+ oSeg = (RoadSegment*) mLastRoadSegment;
+ oSegT = mLastRoadSegmentT;
+ oRoadT = mLastRoadT;
+}
+
+void Avatar::GetRaceInfo( float& distToCurrCollectible, int& currCollectible, int& numLapsCompleted )
+{
+ distToCurrCollectible = mDistToCurrentCollectible;
+ currCollectible = mCurrentCollectible;
+ numLapsCompleted = mNumLapsCompleted;
+}
+
+void Avatar::SetRaceInfo( float distToCurrCollectible, int currCollectible, int numLapsCompleted )
+{
+ mDistToCurrentCollectible = distToCurrCollectible;
+ mCurrentCollectible = currCollectible;
+ mNumLapsCompleted = numLapsCompleted;
+}
+
+/*
+==============================================================================
+Avatar::SetCamera
+==============================================================================
+Description: Controller needs to be aware of camera changes so that
+ direction can be normalized.
+
+Parameters: ( tCamera* pCamera )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetCamera( tCamera* pCamera )
+{
+ rAssert( pCamera != 0 );
+ mpAvatarInputManager->mpCameraRelativeCharacterController->SetCamera( pCamera );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/avatar.h b/game/code/worldsim/avatar.h
new file mode 100644
index 0000000..bf3fd05
--- /dev/null
+++ b/game/code/worldsim/avatar.h
@@ -0,0 +1,158 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatar.h
+//
+// Description: Avatar Class declaration.
+//
+// History: 4/3/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef AVATAR_H
+#define AVATAR_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radmath/radmath.hpp>
+#include <cheats/cheatinputsystem.h>
+#include <roads/roadmanager.h>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+class Vehicle;
+class VehicleController;
+struct AvatarInputManager;
+class tCamera;
+
+//=============================================================================
+//
+// Synopsis: The Avatar
+//
+//=============================================================================
+
+class Avatar : public ICheatEnteredCallback
+{
+// METHODS:
+public:
+ Avatar();
+ ~Avatar();
+
+ void Destroy( void );
+
+ int GetControllerId( void ) const;
+ void SetControllerId ( int id );
+
+ void SetPlayerId( int id ) { mPlayerId = id; }
+ int GetPlayerId() const { return mPlayerId; }
+
+ Character* GetCharacter( void ) const;
+ void SetCharacter( Character* pCharacter );
+
+ Vehicle* GetVehicle( void ) const;
+ void SetVehicle( Vehicle* pVehicle );
+
+ void GetIntoVehicleStart( Vehicle* pVehicle );
+ void GetIntoVehicleEnd( Vehicle* pVehicle );
+ void GetOutOfVehicleStart( Vehicle* pVehicle );
+ void GetOutOfVehicleEnd( Vehicle* pVehicle );
+
+ void SetInCarController( void );
+ void SetOutOfCarController( void );
+ void SetCameraTargetToCharacter( bool cut = false );
+ void SetCameraTargetToVehicle( bool cut = false );
+
+ void SetCamera( tCamera* pCamera );
+
+ const void GetPosition( rmt::Vector& pos ) const;
+ const void GetHeading( rmt::Vector& irHeading ) const;
+
+ // *** //
+ const void GetNormalizedHeadingSafe( rmt::Vector& heading ) const;
+ const void GetVelocity( rmt::Vector& vel ) const;
+ const float GetSpeedMps() const;
+ // *** //
+
+ bool IsInCar( void ) const;
+
+ void PreSubstepUpdate();
+ void Update(float dt);
+ void PreCollisionPrep();
+
+ void OnCheatEntered( eCheatID cheatID, bool isEnabled );
+
+ void GetLastPathInfo(
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT );
+
+ void GetRaceInfo(
+ float& distToCurrCollectible,
+ int& currCollectible,
+ int& numLapsCompleted );
+
+ void SetRaceInfo(
+ float distToCurrCollectible,
+ int currCollectible,
+ int numLapsCompleted );
+
+//MEMBERS
+public:
+
+
+private:
+
+ ///////////////// PATH INFO /////////////////////
+ bool mHasBeenUpdatedThisFrame;
+ RoadManager::PathElement mLastPathElement;
+ RoadSegment* mLastRoadSegment;
+ float mLastRoadSegmentT;
+ float mLastRoadT;
+
+ ///////////////// RACE DATA ///////////////////
+ // we keep pieces of the race data in this class
+ // because catch-up logic will need to use them
+ float mDistToCurrentCollectible;
+ int mCurrentCollectible;
+ int mNumLapsCompleted;
+
+
+ //Prevent wasteful constructor creation.
+ //
+ Avatar( const Avatar& avatar );
+ Avatar& operator=( const Avatar& avatar );
+
+ // Data.
+ //
+ static const int INVALID_CONTROLLER = -1;
+ int mControllerId;
+
+ int mPlayerId;
+
+ Character* mpCharacter;
+
+ // NULL if not in any vehicle, when IsInCar, this is set to that car
+ Vehicle* mpVehicle;
+
+ AvatarInputManager* mpAvatarInputManager;
+
+ static bool s_displayCoordinates;
+
+ /*
+ // TRUE if neither directly on a NON-shortcut road segment nor
+ // in an intersection
+ bool mOffRoad;
+
+ // number of seconds mOffRoad has been true or false
+ float mSecondsOffOrOnRoad;
+ */
+};
+
+
+
+#endif //AVATAR_H
diff --git a/game/code/worldsim/avatarmanager.cpp b/game/code/worldsim/avatarmanager.cpp
new file mode 100644
index 0000000..3f8dcbe
--- /dev/null
+++ b/game/code/worldsim/avatarmanager.cpp
@@ -0,0 +1,511 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class AvatarManager
+//
+// History: 4/3/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <memory/srrmemory.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <mission/gameplaymanager.h>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreeniriswipe.h>
+#include <input/inputmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+
+#include <ai/sequencer/actioncontroller.h>
+#include <supersprint/supersprintmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+AvatarManager* AvatarManager::spAvatarManager = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+AvatarManager* AvatarManager::CreateInstance( void )
+{
+ rAssertMsg( spAvatarManager == 0, "AvatarManager already created.\n" );
+ spAvatarManager = new ( GMA_PERSISTENT ) AvatarManager;
+ return AvatarManager::GetInstance();
+}
+
+AvatarManager* AvatarManager::GetInstance( void )
+{
+ rAssertMsg( spAvatarManager != 0, "AvatarManager has not been created yet.\n" );
+ return spAvatarManager;
+}
+
+
+void AvatarManager::DestroyInstance( void )
+{
+ rAssertMsg( spAvatarManager != 0, "AvatarManager has not been created.\n" );
+ delete ( GMA_PERSISTENT, spAvatarManager );
+}
+//==============================================================================
+// AvatarManager::AvatarManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AvatarManager::AvatarManager()
+:
+mNumAvatars( 0 )
+{
+}
+
+//==============================================================================
+// AvatarManager::~AvatarManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AvatarManager::~AvatarManager()
+{
+ Destroy( );
+}
+
+void AvatarManager::Destroy( void )
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ mAvatarArray[ i ]->Destroy();
+ delete mAvatarArray[i];
+ }
+ mNumAvatars = 0;
+
+ GetEventManager()->RemoveAll( this );
+}
+
+
+/*
+==============================================================================
+AvatarManager::EnterGame
+==============================================================================
+Description: This will be called to create the avatars just before the
+ game session begins.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AvatarManager::EnterGame( void )
+{
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START );
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_START );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_CAMERA_CHANGE );
+
+ int i;
+ for (i = 0; i < MAX_AVATARS; i++)
+ {
+ mAvatarArray[i] = 0;
+ }
+ mNumAvatars = GetGameplayManager()->GetNumPlayers();
+
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ mAvatarArray[i] = new Avatar;
+
+ Vehicle* pVehicle = GetVehicleCentral()->GetVehicle( i );// GetNewVehicle();
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ rWarningMsg( controllerID != -1, "No controller ID registered for player avatar!" );
+
+ mAvatarArray[ i ]->SetControllerId( controllerID );
+ mAvatarArray[ i ]->SetPlayerId( i );
+ Character* pCharacter = GetCharacterManager( )->GetCharacter( i );
+ rAssert( pCharacter );
+ mAvatarArray[ i ]->SetCharacter( pCharacter );
+ rmt::Vector characterPosition;
+
+ // Single player set up.
+ //
+ pCharacter->SetInCar( false );
+ mAvatarArray[ i ]->SetVehicle( (Vehicle*)0 );
+ mAvatarArray[ i ]->SetCameraTargetToCharacter( true );
+ mAvatarArray[ i ]->SetOutOfCarController( );
+
+ //Chuck Adding support for loading of the last used skin for the level
+
+ if ( pVehicle )
+ {
+ // Hack. MS8.
+ // Position the character near the vehicle.
+ //
+ characterPosition.Set( 3.1f, 0.0f, 0.0f );
+
+ rmt::Matrix carToWorld = pVehicle->GetTransform();
+ characterPosition.Transform( carToWorld );
+ pCharacter->SetPosition( characterPosition );
+ pCharacter->UpdateTransformToLoco();
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+void AvatarManager::ExitGame ()
+{
+ Destroy ();
+}
+
+void AvatarManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_GETINTOVEHICLE_START:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pCharacter->GetTargetVehicle();
+ pAvatar->GetIntoVehicleStart( pVehicle );
+ }
+ break;
+ }
+ case EVENT_GETINTOVEHICLE_END:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pCharacter->GetTargetVehicle();
+ pAvatar->GetIntoVehicleEnd( pVehicle );
+ }
+ break;
+ }
+ case EVENT_GETOUTOFVEHICLE_START:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pAvatar->GetVehicle();
+ pAvatar->GetOutOfVehicleStart( pVehicle );
+ }
+ break;
+ }
+ case EVENT_GETOUTOFVEHICLE_END:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pAvatar->GetVehicle();
+ pAvatar->GetOutOfVehicleEnd( pVehicle );
+ }
+ break;
+ }
+ case EVENT_CAMERA_CHANGE:
+ {
+ SuperCam* sc = static_cast<SuperCam*>(pEventData);
+
+ int playerID = 0; // normal game defaults player to zero
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT ||
+ GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT )
+ {
+ playerID = SuperSprintManager::GetInstance()->GetOnlyHumanPlayerID();
+ /*
+ if( GetAvatarForPlayer( playerID )->GetPlayerId() != playerID )
+ {
+ break;
+ }
+ */
+ }
+
+ // can be -1 if in supersprint and more than 1 human is participating...
+ if( playerID == -1 )
+ {
+ break;
+ }
+
+ if ( sc == GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam() )
+ {
+ //Find the vehicle owned by this car.
+ //Vehicle* car = GetGameplayManager()->GetCurrentVehicle();
+ Vehicle* car = GetAvatarForPlayer( playerID )->GetVehicle();
+
+
+ if ( car )
+ {
+ if ( sc->GetType() == SuperCam::BUMPER_CAM )
+ {
+ //Only stop rendering for bumpercams.
+ car->DrawVehicle( false );
+ }
+ else
+ {
+ car->DrawVehicle( true );
+ }
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+Avatar* AvatarManager::GetAvatarForPlayer( int playerId )
+{
+ if( playerId < this->mNumAvatars )
+ {
+ return mAvatarArray[ playerId ];
+ }
+
+ return( NULL );
+}
+
+//=============================================================================
+// AvatarManager::PutCharacterInCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter, Vehicle* pVehicle )
+//
+// Return: void
+//
+//=============================================================================
+void AvatarManager::PutCharacterInCar( Character* pCharacter, Vehicle* pVehicle )
+{
+ rAssert( pCharacter != NULL );
+ rAssert( pVehicle != NULL );
+
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if ( mAvatarArray[ i ]->GetCharacter( ) == pCharacter )
+ {
+ if(pCharacter->IsInCar() && (pCharacter->GetTargetVehicle() == pVehicle))
+ {
+ return;
+ }
+
+ pCharacter->GetActionController()->Clear();
+
+ pCharacter->SetInCar( true );
+ pCharacter->UpdateTransformToInCar( );
+ pCharacter->SetTargetVehicle( pVehicle );
+
+ // transit the vehicle into VL_PHYSICS!
+ if(pVehicle->GetLocomotionType() == VL_TRAFFIC)
+ {
+ pVehicle->SetLocomotion(VL_PHYSICS);
+ pVehicle->mHijackedByUser = true;
+ }
+
+ mAvatarArray[ i ]->SetVehicle( pVehicle );
+ mAvatarArray[ i ]->SetCameraTargetToVehicle( true ); //CUT the camera
+ mAvatarArray[ i ]->SetInCarController( );
+
+ pCharacter->UpdateTransformToInCar();
+ if(pCharacter->GetStateManager()->GetState() == CharacterAi::INCAR)
+ {
+ pCharacter->GetStateManager()->ResetState();
+ }
+ else
+ {
+ pCharacter->GetStateManager()->SetState<CharacterAi::InCar>();
+ //pCharacter->GetStateManager()->Update( 16 );
+ }
+
+ pCharacter->SetDesiredSpeed(0.0f);
+
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->GetHudMap( i )->UnregisterIcon( 0 );
+ GetCurrentHud()->GetHudMap( i )->RegisterIcon( HudMapIcon::ICON_PLAYER,
+ rmt::Vector( 0, 0, 0 ),
+ pVehicle );
+ }
+
+ return;
+ }
+ }
+}
+
+//=============================================================================
+// AvatarManager::PutCharacterOnGround
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter, Vehicle* pVehicle )
+//
+// Return: void
+//
+//=============================================================================
+void AvatarManager::PutCharacterOnGround( Character* pCharacter, Vehicle* pVehicle )
+{
+ rAssert( pCharacter != NULL );
+ rAssert( pVehicle != NULL );
+
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if ( mAvatarArray[ i ]->GetCharacter( ) == pCharacter )
+ {
+ mAvatarArray[ i ]->GetOutOfVehicleStart( pVehicle );
+ mAvatarArray[ i ]->GetOutOfVehicleEnd( pVehicle );
+
+ pCharacter->SetInCar( false );
+
+ pCharacter->UpdateTransformToLoco();
+ pCharacter->GetStateManager()->SetState<CharacterAi::Loco>();
+ pCharacter->GetStateManager()->Update( 16 );
+
+ return;
+ }
+ }
+}
+
+/*
+==============================================================================
+AvatarManager::Update
+==============================================================================
+Description: Comment
+
+Parameters: (float dt)
+
+Return: void
+
+=============================================================================
+*/
+void AvatarManager::Update(float dt)
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ mAvatarArray[ i ]->Update( dt );
+ }
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+Avatar* AvatarManager::FindAvatarForCharacter( Character* pCharacter )
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if ( mAvatarArray[ i ]->GetCharacter( ) == pCharacter )
+ {
+ return mAvatarArray[ i ];
+ }
+ }
+ return 0;
+}
+
+
+//=============================================================================
+// AvatarManager::GetAvatarForVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: Avatar
+//
+//=============================================================================
+Avatar* AvatarManager::GetAvatarForVehicle(Vehicle* vehicle)
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if (mAvatarArray[i]->GetVehicle() == vehicle)
+ {
+ return mAvatarArray[i];
+ }
+ }
+ return 0;
+}
+
+//=============================================================================
+// AvatarManager::IsAvatarGettingInOrOutOfCar
+//=============================================================================
+// Description: returns true if the chacter for playerId is in a get into or get out of car transition state.
+//
+// Parameters: int playerId
+//
+// Return: bool
+//
+//=============================================================================
+bool AvatarManager::IsAvatarGettingInOrOutOfCar(int playerId)
+{
+ if (
+ GetAvatarForPlayer(0)->GetCharacter()->GetStateManager()->GetState() == CharacterAi::GET_IN
+ ||
+ GetAvatarForPlayer(0)->GetCharacter()->GetStateManager()->GetState() == CharacterAi::GET_OUT
+ )
+
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+
+
diff --git a/game/code/worldsim/avatarmanager.h b/game/code/worldsim/avatarmanager.h
new file mode 100644
index 0000000..c0230e7
--- /dev/null
+++ b/game/code/worldsim/avatarmanager.h
@@ -0,0 +1,87 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatarmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 4/3/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef AVATARMANAGER_H
+#define AVATARMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <worldsim/avatar.h>
+#include <events/eventlistener.h>
+
+#include <constants/maxplayers.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Character;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class AvatarManager
+:
+public EventListener
+{
+public:
+ AvatarManager();
+ ~AvatarManager();
+ void Destroy( void );
+
+ static AvatarManager* GetInstance( );
+ static AvatarManager* CreateInstance( );
+ static void DestroyInstance( );
+
+ void EnterGame( void );
+ void ExitGame( void );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ Avatar* GetAvatarForPlayer( int playerId );
+ Avatar* GetAvatarForVehicleId( int vehicleId );
+
+ Avatar* GetAvatarForVehicle(Vehicle* vehicle);
+
+ Avatar* FindAvatarForCharacter( Character* pCharacter );
+
+ void PutCharacterInCar( Character* pCharacter, Vehicle* pVehicle );
+ void PutCharacterOnGround( Character* pCharacter, Vehicle* pVehicle );
+
+ //chuck returns a true if the players character is in get in car or get out of car transition state.
+ bool IsAvatarGettingInOrOutOfCar(int playerId);
+
+ void Update(float dt);
+
+
+private:
+
+ //Prevent wasteful constructor creation.
+ AvatarManager( const AvatarManager& avatarmanager );
+ AvatarManager& operator=( const AvatarManager& avatarmanager );
+
+ // Data.
+ //
+ static AvatarManager* spAvatarManager;
+
+ static const int MAX_AVATARS = MAX_PLAYERS;
+
+ Avatar* mAvatarArray[ MAX_AVATARS ];
+ int mNumAvatars;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline AvatarManager* GetAvatarManager() { return( AvatarManager::GetInstance() ); }
+
+#endif //AVATARMANAGER_H
diff --git a/game/code/worldsim/character/aicharactercontroller.cpp b/game/code/worldsim/character/aicharactercontroller.cpp
new file mode 100644
index 0000000..71c9cbb
--- /dev/null
+++ b/game/code/worldsim/character/aicharactercontroller.cpp
@@ -0,0 +1,112 @@
+#include <worldsim/character/aicharactercontroller.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <main/game.h>
+
+class Behaviour
+{
+public:
+ virtual rmt::Vector& Tick( Character& me ) = 0;
+};
+
+class Wander
+:
+public Behaviour
+{
+public:
+
+ Wander( void );
+ virtual ~Wander( void );
+
+ rmt::Vector& GetTargetPoint( void );
+ void SetTargetPoint( rmt::Vector& targetPoint );
+
+ rmt::Vector& GetPosition( void );
+ void SetPosition( rmt::Vector& position );
+
+ float GetTargetRadius( void ) const;
+ void SetTargetRadius( float radius );
+
+ float GetSteeringRadius( void ) const;
+ void SetSteeringRadius( float radius );
+
+ rmt::Vector& Tick( Character& me );
+private:
+ rmt::Vector mTargetPoint;
+ rmt::Vector mPosition;
+ float mfTargetCircleRadius;
+ float mfSteeringCircleRadius;
+};
+
+Wander::Wander( void )
+:
+mTargetPoint( 0.0f, 0.0f, -1.0f ),
+mPosition( 0.0f, 0.0f, -1.0f ),
+mfTargetCircleRadius( 1.0f ),
+mfSteeringCircleRadius( 0.3f )
+{
+ mTargetPoint.z = mfTargetCircleRadius;
+}
+
+Wander::~Wander( void )
+{
+}
+
+rmt::Vector& Wander::Tick( Character& me )
+{
+ static rmt::Randomizer r( Game::GetRandomSeed () );
+ static rmt::Vector point( 0.0f, 0.0f, 0.0f );
+
+ point.x = r.FloatSigned( );
+ point.y = 0.0f;
+ point.z = r.FloatSigned( );
+
+ // Make a point on the new circle.
+ //
+ point.Normalize( );
+ point.Scale( mfSteeringCircleRadius );
+
+ // Add to point on circle.
+ //
+ mTargetPoint.Add( point );
+ // Project back to unit circle.
+ //
+ mTargetPoint.Normalize( );
+ // Scale to actual Target Circle.
+ //
+ mTargetPoint.Scale( mfTargetCircleRadius );
+
+ return mTargetPoint;
+}
+
+AICharacterController::AICharacterController( Character* pCharacter, int index, tCamera* pCamera )
+:
+mpBehaviour( 0 )
+{
+}
+
+void AICharacterController::Update( float timeins )
+{
+ static Character* pCharacter = GetCharacterManager( )->GetCharacter( 0 );
+ if ( !mpBehaviour )
+ {
+ mpBehaviour = new Wander;
+ }
+
+ mDirection = mpBehaviour->Tick( *pCharacter );
+}
+
+void AICharacterController::GetDirection( rmt::Vector& outDirection ) const
+{
+ outDirection = mDirection;
+}
+
+float AICharacterController::GetValue( int buttonId ) const
+{
+ return 0.0f;
+}
+
+bool AICharacterController::IsButtonDown( int buttonId ) const
+{
+ return false;
+} \ No newline at end of file
diff --git a/game/code/worldsim/character/aicharactercontroller.h b/game/code/worldsim/character/aicharactercontroller.h
new file mode 100644
index 0000000..af73274
--- /dev/null
+++ b/game/code/worldsim/character/aicharactercontroller.h
@@ -0,0 +1,27 @@
+#ifndef AICHARACTERCONTROLLER_H_
+#define AICHARACTERCONTROLLER_H_
+
+#include <worldsim/character/charactercontroller.h>
+#include <radmath/radmath.hpp>
+
+class Behaviour;
+class Character;
+class tCamera;
+
+class AICharacterController
+:
+public CharacterController
+{
+public:
+ AICharacterController( Character* pCharacter, int index, tCamera* pCamera );
+ void Update( float timeins );
+ void GetDirection( rmt::Vector& outDirection ) const;
+ float GetValue( int buttonId ) const;
+ bool IsButtonDown( int buttonId ) const;
+protected:
+private:
+ rmt::Vector mDirection;
+ Behaviour* mpBehaviour;
+};
+
+#endif // AICHARACTERCONTROLLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/allcharacter.cpp b/game/code/worldsim/character/allcharacter.cpp
new file mode 100644
index 0000000..2fc662d
--- /dev/null
+++ b/game/code/worldsim/character/allcharacter.cpp
@@ -0,0 +1,7 @@
+#include <worldsim/character/aicharactercontroller.cpp>
+#include <worldsim/character/character.cpp>
+#include <worldsim/character/charactercontroller.cpp>
+#include <worldsim/character/charactermanager.cpp>
+#include <worldsim/character/charactermappable.cpp>
+#include <worldsim/character/characterrenderable.cpp>
+#include <worldsim/character/charactertarget.cpp>
diff --git a/game/code/worldsim/character/character.cpp b/game/code/worldsim/character/character.cpp
new file mode 100644
index 0000000..61f82ae
--- /dev/null
+++ b/game/code/worldsim/character/character.cpp
@@ -0,0 +1,5171 @@
+#include <worldsim/character/character.h>
+
+#include <choreo/animation.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/matrixstack.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcollision/collisionvolume.hpp>
+
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/aicharactercontroller.h>
+#include <worldsim/character/characterrenderable.h>
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/character/charactermanager.h>
+
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <roads/geometry.h>
+#include <memory/srrmemory.h>
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/action.h>
+#include <ai/statemanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/RenderManager/RenderManager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <worldsim/harass/chasemanager.h>
+#include <ai/actor/intersectionlist.h>
+
+#include <render/DSG/StatePropDSG.h>
+#include <render/DSG/CollisionEntityDSG.h>
+
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <camera/supercammanager.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <meta/locator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+#include <meta/eventlocator.h>
+
+#include <main/game.h>
+
+#include <sound/soundmanager.h>
+
+#include <interiors/interiormanager.h>
+
+// Sim includes.
+//
+#include <simcommon/simstate.hpp>
+#include <simcommon/physicsproperties.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisionmanager.hpp>
+#include <simcollision/proximitydetection.hpp>
+#include <simcommon/simmath.hpp>
+
+#include <render/particles/particlemanager.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <render/Culling/WorldScene.h>
+#include <render/DSG/StaticPhysDSG.h>
+
+//#ifdef RAD_DEBUG
+ #define DRAW_CHARACTER_COLLISION
+//#endif // RAD_DEBUG
+
+#ifdef DRAW_CHARACTER_COLLISION
+ #include <main/commandlineoptions.h>
+ #include <simcollision/collisiondisplay.hpp>
+ #include <simcommon/simutility.hpp>
+ #include <simcollision/collisionvolume.hpp>
+ #include <simcollision/collisionobject.hpp>
+#endif //DRAW_CHARACTER_COLLISION
+
+// NPC includes
+//
+#include <input/controller.h>
+#include <input/inputmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <debug/profiler.h>
+
+#include <presentation/presentation.h>
+#include <presentation/presentationanimator.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#ifdef RAD_WIN32
+#include <camera/pccam.h>
+#include <input/usercontrollerwin32.h>
+#include <input/mouse.h>
+#include <input/realcontroller.h>
+#include <camera/supercam.h>
+#include <camera/supercamcontroller.h>
+#endif
+
+const static rmt::Vector vUp( 0.0f, 1.0f, 0.0f );
+
+const float KICK_ANGLE = 45;
+
+static int s_IntersectFrame;
+
+class AmbientDialogueButton : public ActionButton::ButtonHandler
+{
+public:
+ AmbientDialogueButton(Character* c) : mpCharacter(c)
+ {
+ }
+
+ bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+ {
+ //
+ // Send a couple of sound events for dialog.
+ //
+ if( SoundManager::IsFoodCharacter( mpCharacter ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_ASKFOOD );
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_FOODREPLY, mpCharacter );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_GREETING );
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_RESPONSE, mpCharacter );
+ }
+ return true;
+ }
+
+protected:
+ Character* mpCharacter;
+};
+
+class AmbientDialogueTrigger : public SphereTriggerVolume
+{
+public:
+
+ AmbientDialogueTrigger(Character* c, float radius) : SphereTriggerVolume(rmt::Vector(0,0,0), radius), mpCharacter(c)
+ {
+ SetLocator(new TriggerLocator);
+ GetLocator()->SetNumTriggers(1);
+ GetLocator()->AddTriggerVolume(this);
+ GetLocator()->AddRef();
+ mButton = new AmbientDialogueButton(c);
+ mButton->AddRef();
+ }
+
+ ~AmbientDialogueTrigger()
+ {
+ tRefCounted::Release(mButton);
+ }
+
+ void ClearLocator(void)
+ {
+ GetLocator()->Release();
+ }
+ void Trigger( unsigned int playerID, bool bActive )
+ {
+ if(bActive)
+ {
+ Character* character = GetAvatarManager()->GetAvatarForPlayer(playerID)->GetCharacter();
+ if ( character )
+ {
+ character->AddActionButtonHandler(mButton);
+ }
+ }
+ else
+ {
+ Character* character = GetAvatarManager()->GetAvatarForPlayer(playerID)->GetCharacter();
+ if ( character )
+ {
+ character->RemoveActionButtonHandler(mButton);
+ }
+ }
+ }
+
+protected:
+ Character* mpCharacter;
+ AmbientDialogueButton* mButton;
+};
+
+/*
+==============================================================================
+NPCharacter::NPCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: NPCharacter
+
+=============================================================================
+*/
+NPCharacter::NPCharacter( void )
+:
+Character( ),
+mMappableHandle( Input::INVALID_CONTROLLERID )
+{
+MEMTRACK_PUSH_GROUP( "NPCCharacter" );
+
+ mIsNPC = true;
+ SetInSubstep( false ); // initially false
+
+ /***
+ CharacterMappable* pCharacterMappable = new (GMA_PERSISTENT) BipedCharacterMappable;
+
+ PhysicalController* pController = new (GMA_PERSISTENT) PhysicalController;
+ this->SetController( pController );
+
+ pController->SetCharacterMappable( pCharacterMappable );
+ pCharacterMappable->SetCharacterController( pController );
+
+ mMappableHandle = InputManager::GetInstance()->RegisterMappable( 1, pCharacterMappable );
+ ***/
+ this->SetController( new(GMA_LEVEL_OTHER) NPCController );
+ this->GetController()->SetCharacter( this );
+MEMTRACK_POP_GROUP( "NPCCharacter" );
+}
+/*
+==============================================================================
+NPCharacter::~NPCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: NPCharacter
+
+=============================================================================
+*/
+NPCharacter::~NPCharacter( void )
+{
+ // if we don't clear this here, there may be actions cued that
+ // will screw up once we've been destructed
+ if(GetActionController())
+ {
+ GetActionController()->Clear();
+ }
+
+ /***
+ InputManager::GetInstance()->UnregisterMappable( 1, mMappableHandle );
+ ***/
+ mMappableHandle = Input::INVALID_CONTROLLERID;
+}
+
+#ifdef RAD_DEBUG
+ static float timeScale = 1.00f;
+#endif
+
+/*
+==============================================================================
+NPCharacter::OnUpdateRoot
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void NPCharacter::OnUpdateRoot( float timeins )
+{
+ // Intentionall left blank.
+ //
+}
+
+/*
+==============================================================================
+NPCharacter::OnPostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void NPCharacter::OnPostSimUpdate( float timeins )
+{
+ UpdatePuppet( timeins );
+}
+/*
+==============================================================================
+Character::Character
+==============================================================================
+Description: Constructor
+
+Parameters: ( )
+
+Return: na
+
+=============================================================================
+*/
+sim::TArray< sim::RayIntersectionInfo > Character::msIntersectInfo( 64 ); //.ResizeArray( 2 );
+
+Character::Character( )
+:
+mbCollidedWithVehicle( false ),
+mbInAnyonesFrustrum( false ),
+mbSurfing(false),
+mbAllowUnload(true),
+mbIsPlayingIdleAnim(false),
+
+#ifdef RAD_WIN32
+mPCCamFacing( 0 ),
+#endif
+
+mIsNPC( false ),
+
+mGroundPlaneSimState( 0 ),
+mGroundPlaneWallVolume( 0 ),
+mCollisionAreaIndex( WorldPhysicsManager::INVALID_COLLISION_AREA ),
+
+mpController( 0 ),
+mpCharacterRenderable( 0 ),
+mpPuppet( 0 ),
+mfFacingDir( 0.0f ),
+mfDesiredDir( 0.0f ),
+mfSpeed( 0.0f ),
+mVelocity (0.0f, 0.0f, 0.0f),
+mfDesiredSpeed( 0.0f ),
+mbInCar( false ),
+mpCharacterTarget( 0 ),
+mpActionController( 0 ),
+mpCurrentActionButtonHandler( 0 ),
+mpTargetVehicle( 0 ),
+mTerrainType( TT_Road ),
+mInteriorTerrain( false ),
+mpStateManager( 0 ),
+mfRadius( 0.35f ),
+mbCollided( false ),
+mCurrentCollision( 0 ),
+mbIsStanding( true ),
+mpWalkerLocomotion( 0 ),
+mpJumpLocomotion( 0 ),
+mpStandingCollisionVolume( 0 ),
+mpStandingJoint( 0 ),
+mfGroundVerticalVelocity( 0.0f ),
+mfGroundVerticalPosition( 0.0f ),
+mbTurbo( false ),
+mbIsJump( false ),
+mbSolveCollisions( true ),
+mpPropHandler( 0 ),
+mPropJoint( -1 ),
+mVisible( false ),
+mpWorldScene( 0 ),
+m_IsSimpleShadow( true ),
+mYAdjust( 0.0f ),
+mbBusy(false),
+mbSimpleLoco( false ),
+m_TimeLeftToShock( 0 ),
+m_IsBeingShocked( false ),
+mDoKickwave(false),
+mKickwave(NULL),
+mKickwaveController(NULL),
+mAmbient(false),
+mAmbientLocator(0),
+mAmbientTrigger(NULL),
+mLastFramePos(0.0f,0.0f,0.0f),
+mbDoGroundIntersect(true),
+mIntersectFrame(s_IntersectFrame++),
+mAllowRockin(false),
+mHasBeenHit(false),
+mbSnapToGround(false),
+mSecondsSinceActionControllerUpdate( 0.0f ),
+mTooFarToUpdate(false),
+mSecondsSinceOnPostSimUpdate( 0.0f ),
+mRole(ROLE_UNKNOWN),
+mScale(1.0f),
+mIsInSubstep(true),
+mLean(0.0f, 1.0f, 0.0f),
+mIsLisa(false),
+mIsMarge(false),
+mManaged(false)
+{
+
+ mLastInteriorLoadCheck = radTimeGetMicroseconds64();
+
+ mPrevSimTransform.Identity();
+
+ mTranslucent = true;
+
+ mShadowColour.Set( 0, 0, 0 );
+
+ mGroundNormal.Set( 0.0f, 0.0f, 0.0f );
+ mRealGroundPos.Set( 0.0f, 0.0f, 0.0f );
+ mRealGroundNormal.Set( 0.0f, 0.0f, 0.0f );
+
+ mLastGoodPosOverStatic.Set( 0.0f, 0.0f, 0.0f );
+
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ mpActionButtonHandlers[ i ] = NULL;
+ }
+
+}
+/*
+==============================================================================
+Character::Init
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::Init( void )
+{
+
+//#ifdef RAD_GAMECUBE
+// HeapMgr()->PushHeap(GMA_GC_VMM);
+//#else
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+//#endif
+
+ mpCharacterTarget = new CharacterTarget( this );
+
+ mbWasFootPlanted.resize(2, false);
+
+ mpActionController = new ActionController;
+ mpActionController->Clear( );
+
+ mpStateManager = new CharacterAi::StateManager( this );
+
+ {
+ // Manually create the physics objects for now.
+ // Set inventory section to "Default" because the sim library will store stuff
+ //
+ p3d::inventory->PushSection( );
+ p3d::inventory->SelectSection( "Default" );
+
+ // Manually create the physics objects for now.
+ //
+ sim::SymMatrix identity;
+ identity.Identity();
+ rmt::Vector offset( 0.0f, 0.9f, -0.05f );
+
+ sim::CylinderVolume* pCollisionVolume = new sim::CylinderVolume( offset, vUp, 0.55f, mfRadius );
+ sim::CollisionObject* pCollisionObject = new sim::CollisionObject( pCollisionVolume );
+ sim::PhysicsProperties* pPhysicsProperties = sim::PhysicsProperties::HardWoodProperties(p3d::inventory);
+ sim::PhysicsObject* pSimulatedObject = new sim::PhysicsObject( pPhysicsProperties, offset, identity, 2.0f );
+ pSimulatedObject->SetSimEnvironment( GetWorldPhysicsManager()->mSimEnvironment );
+ sim::SimState* pSimState = sim::SimState::CreateSimState( pCollisionObject, pSimulatedObject );
+ rAssert( pSimState );
+ SetSimState( pSimState );
+
+ pCollisionObject->SetCollisionEnabled(false);
+ SetSolveCollisions(false);
+
+ p3d::inventory->PopSection( );
+ }
+
+ int i;
+ for ( i = 0; i < CollisionData::MAX_COLLISIONS; i++ )
+ {
+ mCollisionData[ i ].mCollisionNormal.Set( 0.0f, 0.0f, 0.0f );
+ mCollisionData[ i ].mCollisionDistance = 0.0f;
+ mCollisionData[ i ].mpCollisionVolume = (sim::CollisionVolume*)0;
+ }
+ mbCollidedWithVehicle = false;
+
+ mpWalkerLocomotion = new WalkerLocomotionAction( this );
+ mpWalkerLocomotion->AddRef();
+
+ mpJumpLocomotion = new JumpAction( this, "jump_idle", Character::GetJumpHeight() );
+ mpJumpLocomotion->AddRef();
+
+ rmt::Matrix mat;
+ mat.Identity( );
+ SetParentTransform( mat );
+
+/* mpPropHandler = new ActionButton::AttachProp;
+ mpPropHandler->AddRef( );
+ */
+ mPropJoint = 0;
+
+ if( !IsNPC() )
+ {
+ InitGroundPlane();
+ }
+ //AssignCollisionAreaIndex( );
+
+ //////////////////////////////
+ // Update Simstate transform
+ rmt::Vector position;
+ GetPosition( position );
+ rmt::Vector facing;
+ GetFacing( facing );
+
+ mat.Identity( );
+ mat.Row( 2 ) = facing;
+ mat.FillTranslate( position );
+
+ mpSimStateObj->SetTransform( mat );
+
+ if( !mIsNPC )
+ {
+ // If you're a player character
+ // Add ourself, our groundplane, and our self-groundplane pair
+ // to collision manager
+ AddToPhysics();
+ }
+
+ // Initialize position at origin
+ rmt::Vector zero( 0.0f, 0.0f, 0.0f );
+ SetPosition( zero );
+
+ UpdateTransformToLoco( );
+
+ // Dusit [Nov 13,2002]:
+ // Need to initialize out the garbage values. The problem is that
+ // the garbage values happen to be quite large... In a later call to
+ // UpdateSimState, we overwrite the currently quite nice simstate &
+ // collisionvolume transforms with the puppet's garbage value (still
+ // garbage because cloned NPCs such as pedestrians are not given a valid
+ // position by locator, but by arbitrary spawning). So now simstate &
+ // collisionvolume have large garbage transforms. Later on when
+ // this cloned NPC gets spawned (e.g. in Pedestrian::Activate()) with
+ // a valid position, the collisionvolume tries to update itself
+ // by calculating the difference between LARGE garbage value and a
+ // comparatively smaller position value. The floating point accuracy
+ // favors the large value and instead of moving to the new smaller
+ // position value, we move to (0,0,0). At this point, simstate's
+ // transform and simcollisionvolume's transform are out of synch.
+ //
+ SetPosition( position );
+
+//#ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+//#else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+//#endif
+}
+
+
+void Character::InitGroundPlane()
+{
+ MEMTRACK_PUSH_GROUP( "Character" );
+
+ HeapMgr()->PushHeap (GMA_LEVEL_OTHER);
+
+ rmt::Vector p(0.0f, 0.0f, 0.0f);
+ rmt::Vector n(0.0f, 1.0f, 0.0f);
+
+ mGroundPlaneWallVolume = new sim::WallVolume(p, n);
+ mGroundPlaneWallVolume->AddRef();
+
+ mGroundPlaneSimState = (sim::ManualSimState*)(
+ sim::SimState::CreateManualSimState(mGroundPlaneWallVolume));
+ mGroundPlaneSimState->AddRef();
+
+ mGroundPlaneSimState->GetCollisionObject()->SetManualUpdate(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetAutoPair(false);
+ mGroundPlaneSimState->GetCollisionObject()->SetIsStatic(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetCollisionEnabled(false);
+
+ char buffy[128];
+ sprintf( buffy, "player_character_groundplane" );
+ mGroundPlaneSimState->GetCollisionObject()->SetName( buffy );
+
+ mGroundPlaneSimState->mAIRefIndex = PhysicsAIRef::redBrickPhizMoveableGroundPlane;
+ mGroundPlaneSimState->mAIRefPointer = (void*)this;
+
+ /*
+ mGroundPlanePhysicsProperties = new PhysicsProperties;
+ mGroundPlanePhysicsProperties->AddRef();
+
+ mGroundPlanePhysicsProperties->SetFrictCoeffCGS(0.8f);
+ mGroundPlanePhysicsProperties->SetRestCoeffCGS(1.15f);
+ mGroundPlanePhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ mGroundPlaneSimState->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+ */
+
+ HeapMgr()->PopHeap (GMA_LEVEL_OTHER);
+ MEMTRACK_POP_GROUP( "Character" );
+}
+
+bool Character::IsInCarOrGettingInOut( void )
+{
+ if(mbInCar ||
+ GetStateManager()->GetState() == CharacterAi::GET_IN ||
+ GetStateManager()->GetState() == CharacterAi::GET_OUT)
+ {
+ return true;
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::SetPuppet
+==============================================================================
+Description: Comment
+
+Parameters: ( choreo::Puppet* pPuppet )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetPuppet( choreo::Puppet* pPuppet )
+{
+ rAssert( pPuppet );
+
+ rmt::Vector position;
+ float facing, desiredFacing;
+ GetPosition( position );
+ facing = GetFacingDir();
+ desiredFacing = GetDesiredDir();
+ tRefCounted::Assign( mpPuppet, pPuppet );
+ SetPosition( position );
+ SetFacingDir(facing);
+ SetDesiredDir(desiredFacing);
+
+ if(mpWalkerLocomotion)
+ {
+ mpWalkerLocomotion->SwitchLocomotion( );
+ }
+
+ mbWasFootPlanted.resize( pPuppet->GetLegCount(), false );
+/*
+ if ( pPuppet )
+ {
+ mPropJoint = pPuppet->GetP3DPose( )->FindJointIndex( "Wrist_R" );
+ }
+ if ( mPropJoint == -1 )
+ {
+ // Safe value.
+ //
+ mPropJoint = 0;
+ }
+*/
+ // The feet collider doesn't work as well as the ray intersection.
+ // It isn't any cheaper in the grand scheme of things either.
+ //
+ //mpFeetCollider = new CharacterFeetCollider( this );
+ int legs = mpPuppet->GetLegCount( );
+ for ( int i = 0; i < legs; i++ )
+ {
+ GetPuppet( )->SetIsLegIKEnabled( i, false );
+ }
+
+ if(GetActionController())
+ {
+ GetActionController()->Clear();
+ }
+
+ if(mpStateManager)
+ {
+ SetInCar(false);
+
+ if(mpStateManager->GetState() != CharacterAi::NOSTATE)
+ {
+ mpStateManager->ResetState();
+ }
+ }
+
+ pPuppet->GetEngine()->GetPoseEngine()->Begin(true);
+}
+
+
+void Character::SetYAdjust( float yOffset )
+{
+ mYAdjust = yOffset;
+}
+
+float Character::GetYAdjust()
+{
+ return mYAdjust;
+}
+
+/*
+==============================================================================
+Character::ResetSpeed
+==============================================================================
+*/
+void Character::ResetSpeed( void )
+{
+ mpSimStateObj->ResetVelocities( );
+ mpWalkerLocomotion->SetDesiredSpeed( 0.0f );
+}
+static rmt::Vector dstart,dend;
+void Character::Kick()
+{
+ if(GetInteriorManager()->IsInside())
+ {
+ return;
+ }
+
+ // Fetch the current character position
+ rmt::Vector position;
+ GetPosition( &position );
+
+ // Fetch character facing vector
+ rmt::Vector facing;
+ GetFacing( facing );
+
+
+ // We want to rotate the facing vector upwards by N degrees
+ // Find the vector thats orthogonal to the facing vector and the world up axis
+ // Lets make sure that the facing vector isn't parallel with the up vector
+ // first
+ rmt::Vector kickdir;
+ const rmt::Vector WORLD_UP( 0, 1.0f, 0 );
+ const float DOT_PRODUCT_PARALLEL_EPSILON = 0.99f;
+ if ( facing.Dot( WORLD_UP ) < DOT_PRODUCT_PARALLEL_EPSILON )
+ {
+ // Not parallel, take the crossproduct between world and up
+ rmt::Vector right;
+ right.CrossProduct( WORLD_UP, facing );
+ rmt::Matrix rotation;
+ rotation.Identity();
+ rotation.FillRotation( right, rmt::DegToRadian( KICK_ANGLE ));
+ kickdir.Rotate( facing, rotation );
+ rAssert( kickdir.y > 0 );
+ }
+ else
+ {
+ // They are parallel, just use the facing vector as the kicking direction
+ kickdir = facing;
+ }
+
+
+
+
+ const float KICK_EFFECT_RADIUS = 5.0f;
+ WorldPhysicsManager::NumObjectsHit numObjectsHit;
+
+ // Use the intersection list for querying and performing sim collision testing
+ IntersectionList intersectList;
+ intersectList.FillIntersectionListDynamics( position, KICK_EFFECT_RADIUS, true, this );
+ DynaPhysDSG* objectHit = NULL;
+ const float KICK_RAY_LEN = 2.0f;
+
+
+ rmt::Vector kickDest = position + kickdir;
+ rmt::Vector kickStart = position - kickdir;
+
+
+ rmt::Vector kickIntersect;
+ if ( intersectList.TestIntersectionDynamics( kickStart, kickDest, &kickIntersect, &objectHit ) )
+ {
+ switch ( objectHit->GetAIRef() )
+ {
+ case PhysicsAIRef::NPCharacter:
+ {
+ // We kicked an NPC. Send some events
+ NPCharacter* ch = (NPCharacter*)objectHit;
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC, ch );
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC_SOUND, ch );
+ GetEventManager()->TriggerEvent( EVENT_PEDESTRIAN_SMACKDOWN );
+ ch->SetHasBeenHit(true);
+
+ // Make the object that got hit fly
+ CharacterAi::CharacterState state = ch->GetStateManager()->GetState();
+ if( state == CharacterAi::LOCO )
+ {
+ // call a special kick that will reset the pastangular and pastlinear
+ // histories...
+ ch->ApplyKickForce( kickdir, CharacterTune::sfKickingForce );
+ }
+ else
+ {
+ ch->ApplyForce( kickdir, CharacterTune::sfKickingForce );
+ }
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ }
+ break;
+
+ case PhysicsAIRef::redBrickVehicle:
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ break;
+
+ case PhysicsAIRef::StateProp:
+ {
+ rAssert( dynamic_cast< StatePropDSG* >( objectHit ) != NULL );
+ StatePropDSG* stateprop = static_cast< StatePropDSG* >( objectHit );
+ // Lets not send EVENT_OBJECT_KICKED when the object has no collision volume
+ if ( stateprop->IsCollisionEnabled() )
+ {
+ objectHit->ApplyForce( kickdir, CharacterTune::sfKickingForce );
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ }
+ }
+ break;
+
+ default:
+ // Make the object that got hit fly
+ objectHit->ApplyForce( kickdir, CharacterTune::sfKickingForce );
+ InstDynaPhysDSG* toBreak = dynamic_cast<InstDynaPhysDSG*>(objectHit);
+
+ if(toBreak)
+ {
+ toBreak->Break();
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ break;
+ }
+ }
+ GetEventManager()->TriggerEvent( EVENT_KICK, this );
+
+}
+
+void Character::Slam()
+{
+ int i;
+ WorldPhysicsManager::CollisionEntityDSGList dsgList;
+
+ rmt::Vector position;
+ GetPosition( &position );
+
+ int numObjectsKicked = GetWorldPhysicsManager()->ApplyForceToDynamicsSpherical( mCollisionAreaIndex,
+ position,
+ 2,
+ CharacterTune::sfSlamForce,
+ &dsgList );
+
+ if ( numObjectsKicked > 0 )
+ {
+ for( i = 0; i < WorldPhysicsManager::CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES; i++ )
+ {
+ CollisionEntityDSG* collObject = dsgList.collisionEntity[i];
+
+ if( collObject != NULL )
+ {
+ switch ( collObject->GetAIRef() )
+ {
+ case PhysicsAIRef::NPCharacter:
+ {
+ Character* ch = static_cast<Character*>(dsgList.collisionEntity[i]);
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC, ch );
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC_SOUND, ch );
+ GetEventManager()->TriggerEvent( EVENT_PEDESTRIAN_SMACKDOWN );
+ ch->SetHasBeenHit(true);
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, collObject );
+ }
+ break;
+ case PhysicsAIRef::StateProp:
+ {
+ rAssert( dynamic_cast< StatePropDSG* >( collObject ) != NULL );
+ StatePropDSG* stateprop = static_cast< StatePropDSG* >( collObject );
+ // Lets not send EVENT_OBJECT_KICKED when the object has no collision volume
+ if ( stateprop->IsCollisionEnabled() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, collObject );
+ }
+ break;
+ }
+ default:
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, collObject );
+ InstDynaPhysDSG* toBreak = dynamic_cast<InstDynaPhysDSG*>(collObject);
+
+ if(toBreak)
+ {
+ toBreak->Break();
+ }
+ break;
+ }
+
+ }
+ }
+ }
+ GetEventManager()->TriggerEvent( EVENT_STOMP, this );
+}
+
+void Character::RelocateAndReset( const rmt::Vector& position, float facing, bool resetMe, bool snapToGround )
+{
+ // TODO: Not done yet...
+ // What else need we do to relocate character & reset its states???
+ mLastFramePos = position;
+ mVelocity.Set(0.0f, 0.0f, 0.0f);
+
+ // Switch to AI control if we need to...
+ sim::SimState* simState = mpSimStateObj;
+ if( simState != NULL )
+ {
+ simState->SetControl( sim::simAICtrl );
+ }
+
+ // Update transforms for PoseJointZero, Puppet, and SimStateObj
+ SetPosition( position );
+
+ // update Puppet's root transform with facing
+ SetFacingDir( facing );
+ SetDesiredDir( facing );
+ ResetSpeed();
+
+ if( mpWalkerLocomotion != NULL )
+ {
+ // Gotta do this so facing is not overwritten by blend priorities
+ // when the player's character is involved...
+ // Without this call here, the player's character will gets its
+ // new facing value clobbered by old values stored in blend priorities
+ // when we go into UpdateTransformToLoco()
+ choreo::LocomotionDriver* locomod = mpWalkerLocomotion->GetDriver();
+ if( locomod != NULL )
+ {
+ locomod->SetActualFacingAngle( facing );
+ }
+ }
+
+ SetStandingJoint(NULL);
+
+ UpdateTransformToLoco();
+
+ // By this point, our position should be finalized and no UpdateBBox
+ // has been called (for moveinworldscene to do)
+
+ // Don't call UpdatePuppet() because it resets our puppet->engine->rootblender's
+ // and joints' transforms back to previous one due to blend priorities in UpdateRoot()
+ //UpdatePuppet( 0.0f );
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+ pPuppet->UpdatePose();
+// pPuppet->UpdateEnd();
+
+ // update jump's root transform after Puppet's transform is set
+ if( mpJumpLocomotion != NULL )
+ {
+ // Copies transform from Puppet->Engine->RootBlender.
+ // If that is correct, so will this be.
+ mpJumpLocomotion->SetRootTransform();
+ }
+ if( mpWalkerLocomotion != NULL )
+ {
+ // Characters obtained a new locomotion driver in previous call to
+ // UpdateTransformToLoco, so we have to reset that driver's
+ // actual facing angle again...
+ // Without this call, NPCs won't face the correct way...
+ // Player character can get away with it cuz the driver gets updated
+ // again later.
+ choreo::LocomotionDriver* locomod = mpWalkerLocomotion->GetDriver();
+ if( locomod != NULL )
+ {
+ locomod->SetActualFacingAngle( facing );
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////
+ //
+ // Force NPC to submit statics and whatever's below them &
+ // update their terrain intersect info. We should actually do this
+ // for EVERY character, but dammit we can't afford to.
+ //
+ if( mIsNPC && mRole != ROLE_PEDESTRIAN )
+ {
+ // temporarily transit the controller to STOPPED state so we force submit statics
+ // to actually submit statics...
+ NPCController* npcController = (NPCController*) mpController;
+ NPCController::State oldState = npcController->GetState();
+ npcController->TransitToState( NPCController::STOPPED );
+
+ // temporarily obtain collision area index if we don't got one...
+ int oldArea = mCollisionAreaIndex;
+ if( oldArea == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ AddToPhysics();
+ }
+
+ SubmitStatics();
+
+ rmt::Vector prevPosition = position;
+ rmt::Vector groundPosition = position;
+ rmt::Vector outnorm( 0.0f, 1.0f, 0.0f );
+ bool bFoundPlane = false;
+
+ mTerrainType = static_cast<eTerrainType>( GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition ) ); // OUT
+ mInteriorTerrain = ( (int)mTerrainType & 0x80 ) == 0x80;
+ mTerrainType = static_cast<eTerrainType>( ( (int)mTerrainType & ~0x80 ) );
+
+ if( bFoundPlane )
+ {
+ mRealGroundPos = groundPosition;
+ mRealGroundNormal = outnorm;
+ float tooHigh = 100000.0f;
+ rAssert( -tooHigh <= mRealGroundNormal.x && mRealGroundNormal.x <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.y && mRealGroundNormal.y <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.z && mRealGroundNormal.z <= tooHigh );
+ }
+ else
+ {
+ //
+ // If this assert goes off for the player charater when he's hit by traffic
+ // it means that Physics has placed him at some location other than his
+ // present location. This is a bad thing... possibly related to
+ // collisions with traffic cars.
+ //rAssertMsg( false, "We're SOOO far from the ground, we're not intersecting" );
+ }
+ rmt::Vector collisionPosition;
+ rmt::Vector collisionNormal;
+
+ collisionNormal.Clear( );
+ collisionPosition.Clear( );
+
+ bool bOverStatic = GetCollisionHeight( prevPosition, position, collisionPosition, collisionNormal );
+ if ( bOverStatic )
+ {
+ if ( !bFoundPlane || collisionPosition.y > groundPosition.y )
+ {
+ mGroundY = collisionPosition.y;
+ mGroundNormal = collisionNormal;
+
+ mLastGoodPosOverStatic = position;
+ mLastGoodPosOverStatic.y = mGroundY;
+ }
+ else
+ {
+ rAssert( bFoundPlane );
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ }
+ else if ( bFoundPlane )
+ {
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ else
+ {
+ mGroundY = position.y;
+ mGroundNormal.Set( 0.0f, 1.0f, 0.0f );
+ }
+
+ if( snapToGround )
+ {
+ rmt::Vector groundPos, groundNormal;
+ GetTerrainIntersect( groundPos, groundNormal );
+ SetPosition( groundPos );
+ SetGroundPoint( groundPos );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal(groundPos) );
+ snapToGround = false;
+ }
+
+ if( oldArea == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ RemoveFromPhysics();
+ }
+ npcController->TransitToState( oldState );
+ }
+ //////////////////////////////////////////////////////////////////////////
+
+
+
+ MoveInWorldScene();
+ if( GetCharacterManager()->GetCharacter(0) == this )
+ GetTriggerVolumeTracker()->ResetDynaloadZones();
+
+ mbSnapToGround = snapToGround;
+ mTooFarToUpdate = false;
+ mSecondsSinceActionControllerUpdate = 0.0f;
+ mSecondsSinceOnPostSimUpdate = 0.0f;
+
+
+ if( resetMe )
+ {
+ // Reset collisions (otherwise the collisions carry on from last placement)
+ ResetCollisions();
+
+ // do SimState reset
+ if( mpSimStateObj != NULL )
+ {
+ mpSimStateObj->ResetVelocities();
+ }
+
+ // Reset action button so we don't get blinking "y" after reset
+ ClearAllActionButtonHandlers();
+
+ mbTurbo = false;
+
+ // Clear props??
+// mpPropHandler->Reset();
+
+ GetStateManager()->SetState<CharacterAi::Loco>();
+
+ mbIsJump = false;
+
+ mpActionController->Clear();
+
+ /*
+ // TODO:
+ // Do more resetting here... If it gets to be a lot, gotta put it in a
+ // separate ResetStates() function
+
+ // Clear actions & priorities??
+
+ // TODO:
+ // How do we halt the jump sequence & reset back to standing?
+ // Actually. We need a general "HaltAnimations" function that stops
+ // whatever you're doing and reset to standing & idle animation...
+ // This includes stopping: jumping, opening doors, turboing, dashing, etc...
+
+ if( mbIsJump )
+ {
+ mpJumpLocomotion->End();
+ mpJumpLocomotion->Done();
+ mbIsJump = false;
+ }
+
+ // TODO:
+ // DO we need to do these things?
+ if( mpLocomotion != NULL )
+ {
+ mpLocomotion->Clear();
+ }
+
+ if( mpActionController != NULL )
+ {
+ mpActionController->Clear();
+ }
+
+ if( mpController != NULL )
+ {
+ mpController->ClearIntention();
+ }
+
+ if( mpStateManager != NULL )
+ {
+ mpStateManager->ResetState( CharacterAi::LOCO, this );
+ }
+
+
+ //mbWasFootPlanted.clear();
+ mbIsStanding = true;
+ mbSolveCollisions = false;
+
+ */
+ }
+}
+
+
+
+void Character::AddToPhysics( void )
+{
+ if( !mManaged )
+ {
+ return;
+ }
+
+ if( mCollisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+ sim::CollisionObject* groundPlaneColObj = mGroundPlaneSimState->GetCollisionObject();
+
+ AssignCollisionAreaIndex();
+
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ myColObj, mCollisionAreaIndex );
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ groundPlaneColObj, mCollisionAreaIndex);
+ GetWorldPhysicsManager()->mCollisionManager->AddPair(
+ groundPlaneColObj, myColObj, mCollisionAreaIndex);
+
+ myColObj->SetCollisionEnabled( true );
+
+ // disable groundplane collision till we need it
+ groundPlaneColObj->SetCollisionEnabled( false );
+
+ SetSolveCollisions( true );
+}
+
+void Character::RemoveFromPhysics( void )
+{
+ if( mCollisionAreaIndex == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+ sim::CollisionObject* groundPlaneColObj = mGroundPlaneSimState->GetCollisionObject();
+
+ myColObj->SetCollisionEnabled( false );
+ groundPlaneColObj->SetCollisionEnabled( false );
+
+ // Freeing a collision area will also empty it out, so no remove calls
+ // necessary here.
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex( mCollisionAreaIndex );
+
+ mCollisionAreaIndex = WorldPhysicsManager::INVALID_COLLISION_AREA;
+
+ SetSolveCollisions( false );
+}
+
+void NPCharacter::AddToPhysics( void )
+{
+ // NPCs don't keep track of their own groundplane... When they go into sim
+ // they are submitted by some other entity and get temp groundplanes
+ // assigned to them.
+ // So we only add ourselves to collisions
+
+ /*
+#ifdef RAD_DEBUG
+ rDebugPrintf( "+++++++++ Adding to Physics: %s, index %d\n", GetName(), mCollisionAreaIndex );
+#endif
+ */
+
+ if( mCollisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+
+ AssignCollisionAreaIndex();
+
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ myColObj, mCollisionAreaIndex );
+
+ myColObj->SetCollisionEnabled( true );
+
+ SetSolveCollisions( true );
+}
+
+void NPCharacter::RemoveFromPhysics( void )
+{
+ /*
+#ifdef RAD_DEBUG
+ rDebugPrintf( "-------- Removing from Physics: %s, index %d\n", GetName(), mCollisionAreaIndex );
+#endif
+ */
+
+ if( mCollisionAreaIndex == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+
+ myColObj->SetCollisionEnabled( false );
+
+ // Freeing a collision area will also empty it out, so no remove calls
+ // necessary here.
+
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex( mCollisionAreaIndex );
+
+ mCollisionAreaIndex = WorldPhysicsManager::INVALID_COLLISION_AREA;
+
+ SetSolveCollisions( false );
+
+}
+
+
+
+
+/*
+==============================================================================
+Character::~Character
+==============================================================================
+Description: Destructor
+
+Parameters: ( void )
+
+Return: na
+
+=============================================================================
+*/
+Character::~Character( void )
+{
+ tRefCounted::Release(mpStandingCollisionVolume);
+
+ if(GetActionController())
+ {
+ GetActionController()->Clear();
+ }
+
+ tRefCounted::Release(mKickwave);
+ tRefCounted::Release(mKickwaveController);
+
+ if( mGroundPlaneSimState )
+ {
+ if(mCollisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA)
+ {
+ GetWorldPhysicsManager()->mCollisionManager->RemoveCollisionObject(
+ mGroundPlaneSimState->GetCollisionObject(),
+ mCollisionAreaIndex);
+ }
+ mGroundPlaneSimState->Release();
+ mGroundPlaneSimState = NULL;
+ }
+ if( mGroundPlaneWallVolume )
+ {
+ mGroundPlaneWallVolume->Release();
+ mGroundPlaneWallVolume = NULL;
+ }
+
+ // NOTE:
+ // Do the same RemoveCollisionObject call for our simstate?
+ // No need. CharacterManager's destroy will call
+ // RemoveFromAllDynamicBlahblahblah for us.
+
+ if( WorldPhysicsManager::INVALID_COLLISION_AREA != mCollisionAreaIndex )
+ {
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex( mCollisionAreaIndex );
+ }
+
+ if( mpController )
+ {
+ mpController->Release( );
+ mpController = 0;
+ }
+ if( mpCharacterRenderable )
+ {
+ delete mpCharacterRenderable;
+ }
+ if ( mpCharacterTarget )
+ {
+ delete mpCharacterTarget;
+ mpCharacterTarget = 0;
+ }
+ if ( mpActionController )
+ {
+ delete mpActionController;
+ mpActionController = 0;
+ }
+ if ( mpStateManager )
+ {
+ delete mpStateManager;
+ mpStateManager = 0;
+ }
+ if ( mpWalkerLocomotion )
+ {
+ mpWalkerLocomotion->Release( );
+ mpWalkerLocomotion = 0;
+ }
+ if ( mpJumpLocomotion )
+ {
+ mpJumpLocomotion->Release( );
+ mpJumpLocomotion = 0;
+ }
+ if ( mbWasFootPlanted.empty() == false )
+ {
+ mbWasFootPlanted.clear();
+ }
+
+ SetTargetVehicle(NULL);
+ ClearAllActionButtonHandlers();
+// tRefCounted::Release( mpPropHandler );
+ if(mAmbientTrigger)
+ {
+ mAmbientTrigger->ClearLocator();
+ }
+ tRefCounted::Release( mAmbientTrigger );
+
+ //
+ // Delete the puppet last. I moved this down to the bottom because of an occasional
+ // non-reproducible crash on gameplay exit where the ActionController destruction above
+ // ended up referencing the puppet which had already been cleaned up. Other desctructors
+ // above (e.g. locomotion objects) may also end up referencing the puppet. Putting it
+ // at the bottom should avoid that. -- jdy
+ //
+ if ( mpPuppet )
+ {
+ mpPuppet->ReleaseVerified();
+ mpPuppet = 0;
+ }
+}
+/*
+==============================================================================
+Character::UpdateParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateParentTransform( float timeins )
+{
+ // Do this before UpdateRoot()
+ //
+
+ rmt::Matrix transform;
+ transform.Identity( );
+ if ( IsInCar() && GetTargetVehicle() )
+ {
+ transform = GetTargetVehicle()->GetTransform();
+ }
+ else if ( mpStandingJoint )
+ {
+ transform = mpStandingJoint->GetWorldMatrix( );
+ }
+ SetParentTransform( transform, timeins );
+
+}
+
+
+/*
+==============================================================================
+Character::SetParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Matrix& mat, float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetParentTransform( const rmt::Matrix& newTransform, float timeins )
+{
+ if ( !rmt::Epsilon( timeins, 0.0f ) )
+ {
+ float invDeltaTime = 1.0f / timeins;
+ mfGroundVerticalVelocity = newTransform.Row( 3 ).y - mfGroundVerticalPosition;
+ mfGroundVerticalVelocity *= invDeltaTime;
+ }
+ else
+ {
+ mfGroundVerticalVelocity = 0.0f;
+ }
+ mParentTransform = newTransform;
+ mfGroundVerticalPosition = mParentTransform.Row( 3 ).y;
+
+ //rAssertMsg( mParentTransform.IsOrthoNormal( ), "Your parent transform node is screwed!\n" );
+
+ if ( !IsInCar( ) )
+ {
+ // If we are not in the car, we always want to keep the character up
+ // at 0,1,0.
+ //
+ rmt::Vector facing = mParentTransform.Row( 2 );
+ mParentTransform.FillHeadingXZ( facing );
+
+ // We don't detect vertical motion with the transform.
+ // so we will zero it out.
+ //
+ mParentTransform.Row( 3 ).y = 0.0f;
+ }
+ mInvParentTransform.InvertOrtho( mParentTransform );
+ if ( mpPuppet )
+ {
+ poser::Transform transform;
+ transform.Identity( );
+ transform.SetMatrix( mParentTransform );
+ mpPuppet->SetParentTransform( transform );
+ }
+}
+
+
+void Character::UpdateGroundPlane( float timeins )
+{
+ // new approach with manual sim state
+ rAssert( mGroundPlaneWallVolume );
+
+ float tooHigh = 100000.0f;
+ rAssert( -tooHigh <= mRealGroundNormal.x && mRealGroundNormal.x <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.y && mRealGroundNormal.y <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.z && mRealGroundNormal.z <= tooHigh );
+
+ mGroundPlaneWallVolume->mPosition = mRealGroundPos; //p;
+ mGroundPlaneWallVolume->mNormal = mRealGroundNormal; //n;
+
+ rAssert( mGroundPlaneSimState );
+ sim::CollisionObject* co = mGroundPlaneSimState->GetCollisionObject();
+ co->PostManualUpdate();
+}
+
+
+
+/*
+==============================================================================
+Character::PreSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::PreSimUpdate( float timeins )
+{
+#ifdef RAD_DEBUG
+ timeins *= timeScale;
+#endif
+
+ if( !IsNPC() ) // ok, we're player character
+ {
+ UpdateGroundPlane( timeins );
+ }
+
+ /////////////////////////////////////////////////
+ // Test if we're too far from the camera for some updates
+ mTooFarToUpdate = false;
+
+ // if we're in First Person Cam, this is bad news... They can ZOOM!
+ // So treat it as though we're ALWAYS not too far to update.
+ if( GetInputManager()->GetGameState() != Input::ACTIVE_FIRST_PERSON )
+ {
+ rmt::Vector myPos;
+ GetPosition( myPos );
+
+ rmt::Vector testPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( testPos );
+ /*
+ // NOTE:
+ // Can't use the camera pos because we could get quite far from the
+ // cam.. use the avatar's pos
+ GetSuperCamManager()->GetSCC(0)->GetCamera()->GetPosition( &testPos );
+ */
+ float fov, aspect;
+ GetSuperCamManager()->GetSCC(0)->GetCamera()->GetFOV(&fov, &aspect);
+ static float fovCutoff = 1.0f;
+
+ const float DIST_TOO_FAR_TO_UPDATE_SQR = 625.0f;
+
+ float distSqr = (myPos - testPos).MagnitudeSqr();
+ if( distSqr > DIST_TOO_FAR_TO_UPDATE_SQR && (fov > fovCutoff))
+ {
+ mTooFarToUpdate = true;
+ }
+ }
+
+ //////////////////////////////////////////////////////
+ // Sense and think.
+ //
+ rmt::Vector direction;
+ UpdateController( direction, timeins );
+ if ( IsMovementLocked() == false )
+ {
+ UpdateDesiredDirAndSpeed( direction );
+ }
+ else
+ {
+ UpdateDesiredDirAndSpeed( rmt::Vector(0,0,0) );
+ }
+
+
+
+ if( mpSimStateObj->GetControl() == sim::simSimulationCtrl &&
+ GetStateManager()->GetState() != CharacterAi::INSIM )
+ {
+ // this will cause flail & get-up anims to be sequenced
+ GetStateManager()->SetState<CharacterAi::InSim>();
+ }
+
+
+ ////////////////////////////////////////////////////////
+ // Selectively update action controller every n frames if:
+ // - not in view, or
+ // - too far away
+
+ unsigned int modulo = 0x7;
+ unsigned int frameCount = GetGame()->GetFrameCount();
+
+ mSecondsSinceActionControllerUpdate += timeins;
+
+ //chuck: changed the herusitic to also include drivers in cars
+ //since we dont want driver pop a mission restarts
+ if(
+ !mIsNPC
+ ||
+ (mbInAnyonesFrustrum)
+ ||
+ ((frameCount & modulo) == (mIntersectFrame & modulo)
+ ||
+ (mpTargetVehicle != NULL)
+
+ )
+
+
+
+ )
+ {
+ BEGIN_PROFILE("ActionController()->Update")
+ GetActionController()->Update( mSecondsSinceActionControllerUpdate );
+ END_PROFILE("ActionController()->Update")
+
+ // Execute intentions based on current state
+ mpStateManager->Update( mSecondsSinceActionControllerUpdate );
+
+ // Do simulation
+ GetActionController()->WakeUp( mSecondsSinceActionControllerUpdate );
+ GetActionController()->DoSimulation( mSecondsSinceActionControllerUpdate );
+
+ mSecondsSinceActionControllerUpdate = 0.0f;
+ }
+ ////////////////////////////////////////////////////////
+
+
+
+ // Reset last frame's collisions in preparation for new collision data
+ // (given unto me by WorldPhysMan's substep)
+ ResetCollisions( );
+
+ mpPuppet->UpdateBegin();
+
+ // Zero out the prophandler. If it is still valid it will get set
+ // during CollisioDetect.
+ //
+// mpPropHandler->SetProp( 0 );
+}
+
+/*
+==============================================================================
+Character::ResetCollisions
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::ResetCollisions( void )
+{
+ // If the thing has moved, then there will be new collision state info.
+ // If NOT then we don't reset our collision state info, we will reuse it.
+ //
+ // if ( mpSimStateObj->GetCollisionObject( )->HasMoved() )
+ {
+ // Reset everything after this code, and before PostSimUpdate
+ // is when we do the collision detection.
+ //
+ mbCollided = false;
+ int i;
+ for ( i = 0; i < CollisionData::MAX_COLLISIONS; i++ )
+ {
+ mCollisionData[ i ].mCollisionNormal.Set( 0.0f, 0.0f, 0.0f );
+ mCollisionData[ i ].mCollisionDistance = 0.0f;
+ mCollisionData[ i ].mpCollisionVolume = (sim::CollisionVolume*)0;
+ }
+ mCurrentCollision = 0;
+ }
+
+ mbCollidedWithVehicle = false;
+
+}
+/*
+==============================================================================
+Character::UpdateController
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& direction, float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateController( rmt::Vector& direction, float timeins )
+{
+
+ if ( GetController( ) )
+ {
+ GetController( )->Update( timeins );
+ GetController( )->GetDirection( direction );
+ }
+ else
+ {
+ direction.Set( 0.0f, 0.0f, 0.0f );
+ }
+}
+/*
+==============================================================================
+Character::UpdateDesiredDirAndSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& dir )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateDesiredDirAndSpeed( const rmt::Vector& dir )
+{
+ rmt::Vector direction = dir;
+
+#ifdef RAD_WIN32
+ SuperCam* cam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ PCCam* pPCCam = NULL;
+ if( !mIsNPC && cam->GetType() == SuperCam::PC_CAM )
+ {
+ pPCCam = static_cast<PCCam*>(cam);
+ }
+#endif
+
+#ifndef RAD_WIN32
+ if ( direction.DotProduct( direction ) > 0.001f )
+ {
+ SetDesiredDir( choreo::GetWorldAngle( direction.x, direction.z ) );
+ }
+#else
+ if( !mIsNPC && pPCCam )
+ {
+ pPCCam->SetPitchVelocity( direction.x );
+
+ // if input is given in either z-axis (forward and back) or x-axis (left and right)
+ //
+ rAssert( dynamic_cast<CameraRelativeCharacterController*>( GetController() ) );
+ CharacterMappable* map = ((CameraRelativeCharacterController*)this->GetController())->GetCharacterMappable();
+
+ float dirPadZ = map->GetValue( CharacterController::DPadUp ) - map->GetValue( CharacterController::DPadDown );
+ float dirAnalogZ = map->GetValue( CharacterController::LeftStickY );
+ float dirZ = rmt::Fabs( dirPadZ ) > rmt::Fabs( dirAnalogZ ) ? dirPadZ : dirAnalogZ;
+
+ float dirPadX = map->GetValue( CharacterController::DPadRight ) - map->GetValue( CharacterController::DPadLeft );
+ float dirAnalogX = map->GetValue( CharacterController::LeftStickX );
+ float dirX = rmt::Fabs( dirPadX ) > rmt::Fabs( dirAnalogX ) ? dirPadX : dirAnalogX;
+
+ /*
+ float currAngle = GetFacingDir();
+
+ if ( !rmt::Epsilon( dirX, 0, 0.01f ) || !rmt::Epsilon( dirZ, 0, 0.01f ) )
+ {
+ rmt::Vector camHeading;
+ cam->GetHeading( &camHeading );
+ camHeading.y = 0.0f;
+
+ rmt::Vector tmpHeading;
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( camHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ tmpHeading.Set( dirX, 0.0f, dirZ );
+ tmpHeading.Transform( mat );
+
+ if( rmt::Epsilon( dirX, 0.0f ) )
+ {
+ if ( dirZ > 0.0f )
+ {
+ mPCCamFacing = 0;
+ }
+ else
+ {
+ mPCCamFacing = 1;
+ }
+ }
+ else
+ {
+ mPCCamFacing = 2;
+ }
+
+ mInvParentTransform.RotateVector( tmpHeading, &tmpHeading );
+ currAngle = choreo::GetWorldAngle( tmpHeading.x, tmpHeading.z );
+ SetFacingDir( currAngle );
+
+ direction.x = dirX;
+ }
+ else
+ {
+ // Upon no input, face the character in the direction of cam
+ //rmt::Vector camHeading;
+ //cam->GetHeading( &camHeading );
+ //camHeading.y = 0.0f;
+
+ // reset currAngle and modify the character's facing directly.
+ //currAngle = choreo::GetWorldAngle( camHeading.x, camHeading.z );
+ //SetFacingDir( currAngle );
+
+ //direction.Set( 0.0f, 0.0f, 0.0f );
+
+ //mPCCamFacing = 0;
+ }
+
+ static float X_SENSE_MOD = 0.01f;
+ float mouseSense = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityX();
+
+ static float ROT_DIR = 4.0f;
+
+ float fPitchVelocity = pPCCam->GetPitchVelocity();
+ float fScaleFactor = 1.0f;
+ if( mbTurbo ) fScaleFactor *= mVelocity.Magnitude();
+
+ currAngle += ROT_DIR * mouseSense * X_SENSE_MOD * pPCCam->GetAngularSpeed() * pPCCam->GetPitchVelocity();
+
+ SetDesiredDir( currAngle );
+ */
+
+ if ( !rmt::Epsilon( dirX, 0, 0.01f ) || !rmt::Epsilon( dirZ, 0, 0.01f ) )
+ {
+ rmt::Vector camHeading;
+ cam->GetHeading( &camHeading );
+ camHeading.y = 0.0f;
+
+ rmt::Vector tmpHeading;
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( camHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ tmpHeading.Set( dirX, 0.0f, dirZ );
+ tmpHeading.Transform( mat );
+
+ if( rmt::Epsilon( dirX, 0.0f ) )
+ {
+ if ( dirZ > 0.0f )
+ {
+ mPCCamFacing = 0;
+ }
+ else
+ {
+ mPCCamFacing = 1;
+ }
+ }
+ else
+ {
+ mPCCamFacing = 2;
+ }
+
+ mInvParentTransform.RotateVector( tmpHeading, &tmpHeading );
+ float currAngle = choreo::GetWorldAngle( tmpHeading.x, tmpHeading.z );
+ SetDesiredDir( currAngle );
+
+ direction.x = dirX;
+ }
+ }
+ else // We reached here cuz we're either an NPC or we're not in PC_CAM
+ {
+ if ( direction.DotProduct( direction ) > 0.001f )
+ {
+ SetDesiredDir( choreo::GetWorldAngle( direction.x, direction.z ) );
+ }
+ }
+#endif
+
+ float fDesiredSpeed = direction.Magnitude();
+
+ // apply non-linear response to input to Player Character only..
+ // the NPCs rely on desired speed to remain unaltered
+ // for the intended speed to be reached.
+ if( !mIsNPC )
+ {
+ fDesiredSpeed *= fDesiredSpeed;
+ }
+
+ // Now normalize the speed to a smaller range than 1.0, probably something like 0.84f
+ //
+ float fMaxScale = GetInputScale( );
+ fDesiredSpeed /= fMaxScale;
+ if ( fDesiredSpeed > 1.0f )
+ {
+ fDesiredSpeed = 1.0f;
+ }
+ SetDesiredSpeed( fDesiredSpeed * GetMaxSpeed( ) );
+}
+/*
+==============================================================================
+Character::UpdateRoot
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateRoot( float timeins )
+{
+ OnUpdateRoot( timeins );
+}
+
+/*
+==============================================================================
+Character::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::OnUpdateRoot( float timeins )
+{
+#ifdef RAD_DEBUG
+ timeins *= timeScale;
+#endif
+
+ UpdatePuppet( timeins );
+}
+/*
+==============================================================================
+Character::UpdatePuppet
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdatePuppet( float timeins )
+{
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+ // post sim inserted here
+ UpdateParentTransform( timeins );
+
+ mbNeedChoreoUpdate = !(
+ (pPuppet->GetEngine()->GetRootBlender()->GetRootDriverCount() <= 1)
+ && (!mVisible || !mbInAnyonesFrustrum)
+ && mbSimpleLoco);
+
+ if(pPuppet->GetDriverCount() == 0)
+ {
+// rDebugPrintf("WARNING : Character '%s' has no pose drivers, holding last pose\n", GetName());
+ mbNeedChoreoUpdate = false;
+ }
+
+ if(!mbNeedChoreoUpdate && mbSimpleLoco)
+ {
+ pPuppet->GetEngine()->GetRootBlender()->ClearRootDrivers();
+
+ rmt::Vector position = pPuppet->GetPosition();
+ rAssert( mpWalkerLocomotion != NULL );
+ choreo::LocomotionDriver* locoDriver = mpWalkerLocomotion->GetDriver();
+ rAssert( locoDriver != NULL );
+ float velocity = locoDriver->GetDesiredVelocity();
+
+ rmt::Vector facing;
+ this->GetFacing(facing);
+
+ facing *= velocity * timeins;
+ pPuppet->SetPosition(position + facing);
+
+ pPuppet->UpdateRoot();
+ }
+ else
+ {
+// if(pPuppet->GetDriverCount() != 0)
+ {
+ // Update choreo.
+ //
+ // Push() all drivers before call to choreo::Puppet::Begin()
+ //
+ pPuppet->Advance( timeins );
+
+ // choreo::Puppet::UpdateRoot() will change choreo::Puppet Position and Orientation
+ //
+ pPuppet->UpdateRoot();
+ }
+ }
+}
+
+bool Character::TestInAnyonesFrustrum()
+{
+ ////////////////////////////////////////////////
+ // Test if we're in anyone's frustrum
+ //
+ mbInAnyonesFrustrum = false;
+ int playerCount = 0;
+ int numPlayers = ::GetGameplayManager()->GetNumPlayers();
+ while( !mbInAnyonesFrustrum && playerCount < numPlayers )
+ {
+ TestInFrustrumOfPlayer(playerCount);
+ playerCount++;
+ }
+ if( mpCharacterRenderable != NULL )
+ {
+ // characterrenderable doesn't have pointer to character, so
+ // store result there too...
+ mpCharacterRenderable->SetInAnyonesFrustrum( mbInAnyonesFrustrum );
+ }
+ return mbInAnyonesFrustrum;
+}
+
+
+/*
+==============================================================================
+Character::PostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::PostSimUpdate( float timeins )
+{
+ TestInAnyonesFrustrum();
+
+#ifdef RAD_DEBUG
+ timeins *= timeScale;
+#endif
+
+ // determine how frequently we need to make some of the expensive calls
+ unsigned int modulo = 0x7;
+ unsigned int frameCount = GetGame()->GetFrameCount();
+ bool shouldUpdate = !mIsNPC || // update the PC at full rate
+ IsInCar() || // or parent transform will get screwed
+ (mbInAnyonesFrustrum) || // update if we are visible
+ ((frameCount & modulo) == (mIntersectFrame & modulo));
+
+BEGIN_PROFILE("OnPostSimUpdate")
+ mSecondsSinceOnPostSimUpdate += timeins;
+
+ if( shouldUpdate )
+ {
+ OnPostSimUpdate( mSecondsSinceOnPostSimUpdate );
+ mSecondsSinceOnPostSimUpdate = 0.0f;
+ }
+
+ mbNeedChoreoUpdate = mbNeedChoreoUpdate && shouldUpdate && (!mTooFarToUpdate || ((frameCount & modulo) == (mIntersectFrame & modulo)));
+
+END_PROFILE("OnPostSimUpdate")
+
+ // Call physics update.
+ // Needs to be called before UpdatePose in case we're in SimulationCtrl
+BEGIN_PROFILE("UpdateSimState")
+ UpdateSimState( timeins );
+END_PROFILE("UpdateSimState")
+
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+
+ ResolveCollisions();
+
+ BEGIN_PROFILE("UpdateGroundHeight")
+ if ( !IsInCar( ) )
+ {
+ UpdateGroundHeight();
+ }
+END_PROFILE("UpdateGroundHeight")
+
+ // Update the puppet with the physics transform?
+ //
+/*
+BEGIN_PROFILE("UpdateProps")
+ UpdateProps( timeins );
+END_PROFILE("UpdateProps")
+*/
+
+ if ( GetController() )
+ {
+ GetController()->ClearIntention();
+ }
+
+ if(IsNPC())
+ {
+ if(mAmbientTrigger)
+ {
+ rmt::Vector pos;
+ GetPosition(pos);
+ mAmbientTrigger->SetPosition(pos);
+ }
+ }
+ else
+ {
+ /*
+ // Clear the action handler when we are set to handle a prop
+ // that has been previously cleared.
+ //
+BEGIN_PROFILE("GetActionButtonHandler")
+ if( mpPropHandler->GetProp() == 0 )
+ {
+ mpPropHandler->Exit( this );
+ RemoveActionButtonHandler( mpPropHandler );
+ }
+END_PROFILE("GetActionButtonHandler")
+ */
+
+BEGIN_PROFILE("UpdateFootPlant")
+ if( !IsInCar() )
+ {
+ if( radTimeGetMicroseconds64()-mLastInteriorLoadCheck > 3000000 ) //(amortise/3s) if it's been more than 3 sec's since we last checked for volumes
+ {
+ rmt::Vector posn;
+ GetPosition(&posn);
+ GetTriggerVolumeTracker()->ActivateNearestInteriorLoadZone(0, posn, 40.0f);
+ mLastInteriorLoadCheck = radTimeGetMicroseconds64();
+ }
+
+ UpdateFootPlant( );
+ }
+END_PROFILE("UpdateFootPlant")
+
+ UpdateShock( timeins );
+
+ if(mDoKickwave && mKickwaveController)
+ {
+ mKickwaveController->Advance(timeins * 1000.0f);
+ if(mKickwaveController->LastFrameReached())
+ {
+ mDoKickwave = false;
+ }
+ }
+ }
+
+ rmt::Vector tmp, tmpDir;
+ GetPosition(tmp);
+ tmpDir.Sub(tmp, mLastFramePos);
+ mVelocity = tmpDir / timeins;
+
+ // if movement is too great while not in car, reset velocity & reset position to last frame's pos
+ // this is to stop the player character from going out of world or warping too high in the sky,
+ // or too low below ground, etc...
+
+ const float DIFF_SQR_IN_POS_TOO_HIGH_RESET = 2500.0f;
+
+ CharacterAi::CharacterState state = GetStateManager()->GetState();
+
+ if( !mIsNPC &&
+ ( state == CharacterAi::LOCO || state == CharacterAi::INSIM )&&
+ ( tmpDir.MagnitudeSqr() > DIFF_SQR_IN_POS_TOO_HIGH_RESET ) )
+ {
+ //rAssertMsg( false, "Player character got moved REALLY far in one frame... Using last frame pos!\n" );
+ mVelocity.Set( 0.0f, 0.0f, 0.0f );
+ SetPosition( mLastFramePos );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal( mLastFramePos ) );
+ this->SetGroundPoint( mLastFramePos );
+ }
+ else
+ {
+ if(mCollisionAreaIndex != -1)
+ {
+ if(GetWorldPhysicsManager()->FenceSanityCheck(mCollisionAreaIndex, mLastFramePos, tmp, &tmp))
+ {
+ SetPosition( tmp );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal( tmp ) );
+ this->SetGroundPoint( tmp );
+ }
+ }
+
+ mLastFramePos = tmp;
+ }
+
+BEGIN_PROFILE("MoveInWorldScene")
+ MoveInWorldScene();
+END_PROFILE("MoveInWorldScene")
+}
+
+void Character::ResolveCollisions(void)
+{
+ mCollidedThisFrame = false;
+ sim::SimState* simState = mpSimStateObj; //GetSimState();
+ if( simState != NULL )
+ {
+ if( simState->GetControl() == sim::simAICtrl )
+ {
+ bool inSR1Or2 = false;
+ Mission* m = GetGameplayManager()->GetCurrentMission();
+ if( m )
+ {
+ inSR1Or2 = m->mIsStreetRace1Or2;
+ }
+ bool shouldSolveCollisions = !IsInCar() &&
+ (!mIsNPC || (mIsNPC && !inSR1Or2));
+
+
+ if( shouldSolveCollisions )
+ {
+ rmt::Vector desiredPos = LocalToWorld( mpPuppet->GetPosition( ) );
+ rmt::Vector outPos;
+BEGIN_PROFILE("SolveCollisionWithStatic")
+ Character::eCollisionType collisionType = SolveCollisionWithStatic( desiredPos, outPos );
+END_PROFILE("SolveCollisionWithStatic")
+
+ if( collisionType & Character::HitHead )
+ {
+ if(mpJumpLocomotion->IsJumpState(JumpAction::Jump))
+ {
+ GetEventManager()->TriggerEvent(EVENT_HIT_HEAD, this);
+
+ if(!mbIsStanding && (mVelocity.y > 0.0f))
+ {
+ mpJumpLocomotion->Reset(0.0f, true);
+
+ if(!IsNPC())
+ {
+ outPos.y -= 0.01f;
+ }
+ }
+ }
+ }
+
+ if ( collisionType & ( Character::HitWall | Character::HitHead) )
+ {
+ // Fix the character position.
+ //
+ SetPosition( outPos );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal( outPos ) );
+ this->SetGroundPoint( outPos);
+ mCollidedThisFrame = true;
+ }
+
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+Character::UpdateFootPlant
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateFootPlant( void )
+{
+ int legs = mpPuppet->GetLegCount( );
+ for ( int i = 0; i < legs; i++ )
+ {
+ bool bFootPlanted = mpPuppet->IsFootPlanted( i );
+ if ( bFootPlanted )
+ {
+ if ( mbWasFootPlanted[ i ] == false )
+ {
+ // foot planted.
+ //
+
+ // If were are dashing around (turbo) and the foot just made
+ // contact with the ground, kick up a dust cloud
+ // also make sure the user isnt just holding in the turbo button while
+ // the character is just standing still
+ //
+
+
+ if ( GetDesiredSpeed() > 1.0f )
+ {
+ /* footprints
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( mpPuppet->GetFootPosition( i ) );
+
+ GetFootprintManager()->CreateFootprint( transform, FootprintManager::eSquishies );
+ */
+
+ // no puffs in interior
+ if(!GetInteriorManager()->IsInside())
+ {
+ rmt::Vector facing;
+ this->GetFacing( facing );
+ GetSparkleManager()->AddDash( mpPuppet->GetFootPosition( i ), facing, GetDesiredSpeed() * 0.125f );
+ }
+
+ if(this == GetCharacterManager()->GetCharacter(0))
+ {
+ GetEventManager()->TriggerEvent( EVENT_FOOTSTEP, this );
+ }
+ }
+ }
+ }
+ mbWasFootPlanted[ i ] = bFootPlanted;
+ }
+}
+/*
+==============================================================================
+Character::OnPostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::OnPostSimUpdate( float timeins )
+{
+}
+
+/*
+==============================================================================
+Character::UpdateShock
+==============================================================================
+Description: Comment
+
+Parameters: ( float delta time in seconds )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateShock( float timeins )
+{
+ if ( m_IsBeingShocked )
+ {
+ // UpdateDesiredDirAndSpeed( rmt::Vector( 0,0,0 ) );
+ m_TimeLeftToShock -= timeins;
+
+ if ( IsNPC() == false )
+ {
+ float blur = m_TimeLeftToShock * 2.0f;
+ if ( blur > 1.0f ) blur = 1.0f;
+ GetRenderManager()->SetBlurAlpha( blur );
+ }
+ if ( m_TimeLeftToShock <= 0 )
+ {
+ mpCharacterRenderable->SetShocked( false );
+ m_IsBeingShocked = false;
+ }
+ }
+ else
+ {
+ if ( IsNPC() == false )
+ GetRenderManager()->SetBlurAlpha( 0 );
+ }
+}
+
+
+/*
+==============================================================================
+Character::AddToWorldScene
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AddToWorldScene( void )
+{
+ if( !mVisible && mManaged )
+ {
+ UpdatePuppet( 0.0f );
+
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+
+ pPuppet->UpdatePose();
+
+ if(mpCharacterRenderable->GetDrawable())
+ {
+ mpPuppet->GetP3DPose()->SetSkeleton(mpCharacterRenderable->GetDrawable()->GetSkeleton());
+ pPuppet->UpdateEnd();
+ }
+
+ UpdateSimState( 0.0f );
+
+ // Call updateBBox so the DSG knows where to put this guy at the start.
+ rmt::Box3D dummyBox;
+ UpdateBBox( dummyBox );
+
+ rAssert( mpWorldScene == 0 );
+ mpWorldScene = ((WorldRenderLayer*)GetRenderManager()->mpLayer(RenderEnums::LevelSlot))->pWorldScene();
+ mpWorldScene->Add( this );
+
+ //UpdateProps( 0.0f );
+
+ mVisible = true;
+ }
+}
+/*
+==============================================================================
+Character::RemoveFromWorldScene
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return:
+
+=============================================================================
+*/
+void Character::RemoveFromWorldScene( void )
+{
+ if( mVisible )
+ {
+ mpWorldScene->Remove( this );
+ mpWorldScene = 0;
+ mVisible = false;
+ }
+}
+
+
+
+void Character::MoveInWorldScene( void )
+{
+ if( mVisible )
+ {
+ rmt::Box3D oldBox;
+ UpdateBBox( oldBox );
+ // now move!
+ //
+ mpWorldScene->Move(oldBox, (IEntityDSG*)this);
+ }
+}
+
+/*
+==============================================================================
+Character::UpdateSimState
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateSimState( float timeins )
+{
+ sim::SimState* simState = mpSimStateObj; //GetSimState();
+ if( simState != 0 )
+ {
+ if( simState->GetControl() == sim::simAICtrl )
+ {
+ /*
+ poser::Transform poserTrans = mpPuppet->GetRootTransform();
+ rmt::Matrix matrix = poserTrans.GetMatrix();
+ simState->SetTransform( matrix, timeins );
+ */
+
+ rmt::Matrix matrix;
+ rmt::Vector facing;
+ rmt::Vector side;
+ rmt::Vector position;
+
+ GetFacing(facing);
+ GetPosition( position );
+
+ side.CrossProduct(facing, rmt::Vector(0.0f, 1.0f, 0.0f));
+
+ // leaning while in jump or when doing idle is *very* likely to push us through stuff
+ bool dontLean = GetJumpLocomotionAction()->IsInJump() || mbIsPlayingIdleAnim;
+
+ if(!dontLean)
+ {
+ dontLean = mLean.Dot(rmt::Vector(0.0f, 1.0f, 0.0f)) > 0.925;
+ }
+
+ matrix.Identity();
+ matrix.Row(0) = side;
+ matrix.Row(1) = dontLean ? rmt::Vector(0.0f, 1.0f, 0.0f) : mLean;
+ matrix.Row(2).CrossProduct(side, mLean);
+ matrix.Row(3) = position;
+
+ rmt::Matrix oldMat = simState->GetTransform( );
+ simState->SetTransform( matrix, timeins );
+ simState->GetCollisionObject()->Update();
+
+ // TODO:
+ // Do we really need to update velocity here??
+ rmt::Vector velXZ = simState->VelocityState().mLinear;
+ velXZ.y = 0.0f;
+ mfSpeed = velXZ.Magnitude( );
+ }
+ else if( simState->GetControl() == sim::simSimulationCtrl )
+ {
+ // If simstate velocities are too great, fudge them here. We don't want characters flying too far away...
+ rmt::Vector vel = simState->VelocityState().mLinear;
+ float limit = 15.0f;
+ rAssert( limit >= 0.0f );
+ float linearSpdMps = vel.Length(); // *** SQUARE ROOT! ***
+ if( linearSpdMps > limit )
+ {
+ simState->VelocityState().mLinear.Scale( limit/linearSpdMps );
+ }
+
+ // Update Character's speed
+ rmt::Vector velXZ = simState->VelocityState().mLinear;
+ velXZ.y = 0.0f;
+ mfSpeed = velXZ.Magnitude( );
+
+ // Update Character's facing and position
+ rmt::Matrix matrix = (rmt::Matrix) simState->GetTransform();
+ rmt::Vector negZed( 0.0f, 0.0f, -1.0f );
+
+ poser::Transform transform( matrix );
+ mpPuppet->SetRootTransform( transform );
+
+ poser::Pose* pPose = mpPuppet->GetPose( );
+ // stuff fixed up root transform into joint
+ poser::Joint* joint = pPose->GetJoint( 0 );
+ joint->SetWorldTransform( transform );
+ joint->SetObjectTransform( transform );
+ }
+ }
+}
+
+/*
+==============================================================================
+Character::UpdateBBox
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Box3D& oldBox )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateBBox( rmt::Box3D& oldBox )
+{
+ oldBox = mBBox;
+
+ GetPosition( mPosn );
+
+ mBBox.low = mPosn;
+ mBBox.high = mPosn;
+
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre.Sub(mBBox.high,mBBox.low);
+ mSphere.centre *= 0.5f;
+ mSphere.centre.Add(mBBox.low);
+ mSphere.radius = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mSphereRadius;
+}
+// Implements CollisionEntityDSG
+//
+/*
+==============================================================================
+Character::PreReactToCollision
+==============================================================================
+Description: Comment
+
+Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+
+Return: bool
+
+=============================================================================
+*/
+sim::Solving_Answer Character::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ if((inCollision.mCollisionVolumeA->Type() == sim::BBoxVolumeType) ||
+ (inCollision.mCollisionVolumeB->Type() == sim::BBoxVolumeType))
+ {
+ return sim::Solving_Continue;
+ }
+
+ ////////////////////////////////////////////
+ // Deal properly with ignoring targetvehicle pointer
+ //
+ if( this->mpTargetVehicle && (pCollidedObj->mAIRefPointer == this->mpTargetVehicle))
+ {
+ return sim::Solving_Aborted;
+ }
+
+ if( this->mpTargetVehicle && this->mpTargetVehicle->mVehicleDestroyed)
+ {
+ if(pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ Vehicle* v = (Vehicle*)pCollidedObj->mAIRefPointer;
+ if(v->mVehicleID == VehicleEnum::HUSKA)
+ {
+ Vehicle* ov = GetVehicleCentral()->mHuskPool.FindOriginalVehicleGivenHusk(v);
+ if(ov && (ov == this->mpTargetVehicle))
+ {
+ return sim::Solving_Aborted;
+ }
+ }
+ }
+ }
+
+ if( this->mpTargetVehicle && this->mpTargetVehicle->GetDriver() &&
+ (pCollidedObj->mAIRefPointer == this->mpTargetVehicle->GetDriver()))
+ {
+ return sim::Solving_Aborted;
+ }
+ /////////////////////////////////////////
+
+ // Remember that we've legitimately collided with a vehicle
+ if( pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
+ {
+ mbCollidedWithVehicle = true;
+ }
+
+ // Don't store collision data if we're under simulation control
+ if( mpSimStateObj->GetControl() == sim::simSimulationCtrl )
+ {
+ // When a traffic vehicle hits an NPC (or vice versa) while in simulation control...
+ if( (pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle) &&
+ (mpSimStateObj->mAIRefIndex == PhysicsAIRef::NPCharacter) &&
+ (GetStateManager()->GetState() == CharacterAi::LOCO || GetStateManager()->GetState() == CharacterAi::INSIM) )
+ {
+
+ sim::Collision theCollision = inCollision;
+ sim::SimState* pSimState = theCollision.mCollisionObjectB->GetSimState();
+ if( pSimState->mAIRefPointer == this &&
+ (pSimState->mAIRefIndex == PhysicsAIRef::NPCharacter ||
+ pSimState->mAIRefIndex == PhysicsAIRef::PlayerCharacter) )
+ {
+ theCollision.mNormal.Scale( -1.0f );
+ if(theCollision.GetPositionB().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+ else
+ {
+ if(theCollision.GetPositionA().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+
+ // Grab vehicle's speed and if it's greater than threshold,
+ // transit to simulation control.
+ Vehicle* v = (Vehicle*) pCollidedObj->mAIRefPointer;
+ rAssert( v );
+
+ float vSpeedMps = v->mSpeed;
+ float speedThreshold = 1.0f;
+ if( vSpeedMps > speedThreshold && v->mVehicleType == VT_TRAFFIC )
+ {
+ /*
+ const int MAX_RAND_MODULUS = 3;
+ const float THEORETICAL_MAX_SPEED_MPS = 28.0f;
+
+ float speedRatio = v->mSpeed / THEORETICAL_MAX_SPEED_MPS;
+
+ const float BASE_SPEED_COMPONENT_MOD_MPS = 2.0f;
+
+ // Impart immediate vertical velocity! YA!
+ float randX, randY, randZ;
+ randX = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randY = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randZ = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+
+ rmt::Vector impactLinearVel( 0.0f, randY, 0.0f );
+ rmt::Vector impactAngularVel( randX, randY, randZ);
+
+ impactLinearVel += v->GetFacing() + theCollision.mNormal;
+ */
+ rmt::Vector impactLinearVel = theCollision.mNormal * 1.4f;
+
+
+ rmt::Vector& linearVel = mpSimStateObj->GetLinearVelocity();
+ linearVel.Add( impactLinearVel );
+ //rmt::Vector& angularVel = mpSimStateObj->GetAngularVelocity();
+ //angularVel.Add( impactAngularVel );
+ }
+ }
+ return sim::Solving_Continue;
+ }
+
+ //rAssert( mCurrentCollision < CollisionData::MAX_COLLISIONS );
+ if( mCurrentCollision >= CollisionData::MAX_COLLISIONS )
+ {
+ return sim::Solving_Continue;//false;
+ }
+
+ if( //mpSimStateObj->mAIRefIndex == PhysicsAIRef::PlayerCharacter &&
+ pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane )
+ {
+ return sim::Solving_Aborted;
+ }
+
+ //
+ // store some value for when player character (me) collides with another character
+ //
+ if( mpSimStateObj->mAIRefIndex == PhysicsAIRef::PlayerCharacter &&
+ pCollidedObj->mAIRefIndex == PhysicsAIRef::NPCharacter )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PC_NPC_COLLISION, pCollidedObj->mAIRefPointer );
+ }
+ static bool bTestNormals = true;
+ static float sfEdgeTune = 0.6f;
+ sim::Collision theCollision = inCollision;
+ // recall:
+ // mPositionA = mPositionB + mNormal * mDistance
+ // We want the normal to point from Object to Homer.
+ // Normals always point from B to A.
+ // So, if homer is B, then flip the normal.
+ //
+
+ sim::SimState* pSimState = theCollision.mCollisionObjectB->GetSimState();
+ if( pSimState->mAIRefPointer == this &&
+ (pSimState->mAIRefIndex == PhysicsAIRef::NPCharacter ||
+ pSimState->mAIRefIndex == PhysicsAIRef::PlayerCharacter) )
+ {
+ theCollision.mNormal.Scale( -1.0f );
+ if(theCollision.GetPositionB().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+ else
+ {
+ if(theCollision.GetPositionA().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+
+ if ( theCollision.mCollisionVolumeA == mpStandingCollisionVolume
+ || theCollision.mCollisionVolumeB == mpStandingCollisionVolume )
+ {
+ return sim::Solving_Continue;//false;
+ }
+
+ // When a vehicle hits us (or vice versa), transit to simulation control
+ // so physics take over if we are in locomiotion (for now, don't do this
+ // if we are not in loco (i.e. getting in or out of car) or state can get
+ // badly screwed
+ if( (pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle) &&
+ (GetStateManager()->GetState() == CharacterAi::LOCO ||
+ GetStateManager()->GetState() == CharacterAi::INSIM) )
+ {
+ // Grab vehicle's speed and if it's greater than threshold,
+ // transit to simulation control.
+ Vehicle* v = (Vehicle*) pCollidedObj->mAIRefPointer;
+ rAssert( v );
+
+ float vSpeedMps = v->mSpeed;
+
+ float speedThreshold = 1.0f;
+ if( vSpeedMps > speedThreshold )
+ {
+ if( mpSimStateObj->mAIRefIndex == PhysicsAIRef::NPCharacter )
+ {
+ if( v == GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PLAYER_MAKES_LIGHT_OF_CAR_HITTING_NPC );
+ GetEventManager()->TriggerEvent( EVENT_PLAYER_CAR_HIT_NPC, this );
+ mHasBeenHit = true;
+ }
+
+ /////////////////////////////
+ // We're entering simulation
+ /////////////////////////////
+ mpSimStateObj->SetControl( sim::simSimulationCtrl );
+ GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
+
+
+ // If a traffic vehicle hit us, its simstate will have zero velocity, so
+ // it won't be imparting anything upon us... So we fake it...
+ if( v->mVehicleType == VT_TRAFFIC )
+ {
+
+ //const int MIN_RAND_MODULUS = 1;
+ const int MAX_RAND_MODULUS = 6;
+ const float THEORETICAL_MAX_SPEED_MPS = 28.0f;
+
+ float speedRatio = v->mSpeed / THEORETICAL_MAX_SPEED_MPS;
+
+ //int delta = MAX_RAND_MODULUS - MIN_RAND_MODULUS;
+ //int randMod = (int)(speedRatio * delta) + MIN_RAND_MODULUS;
+ //rAssert( randMod >= MIN_RAND_MODULUS );
+
+ const float BASE_SPEED_COMPONENT_MOD_MPS = 6.0f;
+
+ // Impart immediate vertical velocity! YA!
+ float randX, randY, randZ;
+ randX = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randY = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randZ = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+
+ rmt::Vector impactLinearVel( 0.0f, randY, 0.0f );
+ rmt::Vector impactAngularVel( randX, randY, randZ);
+
+ impactLinearVel += v->GetFacing() + theCollision.mNormal;
+
+
+ rmt::Vector& linearVel = mpSimStateObj->GetLinearVelocity();
+ linearVel.Add( impactLinearVel );
+ rmt::Vector& angularVel = mpSimStateObj->GetAngularVelocity();
+ angularVel.Add( impactAngularVel );
+ }
+
+ }
+ else // it's the player character that's getting hit by a vehicle
+ {
+ // Ignore all other cars except VT_AI cars
+ // need to break out and keep solving though
+ if(((Vehicle*)pCollidedObj->mAIRefPointer)->mVehicleType != VT_AI )
+ {
+ goto KeepSolving;
+ }
+
+ if( !IsInCar() )
+ {
+
+ // ignore "up" collision normals (presumably we're standing on top
+ // of a traffic car)
+ const float TOP_OF_VEHICLE_COS_ALPHA = 0.9848077f; // approx 10 degrees
+ float dp = theCollision.mNormal.Dot( mRealGroundNormal );
+
+ // if deviate by > 10 degrees from ground normal, then we're
+ // probably not standing on it.
+ if( dp < TOP_OF_VEHICLE_COS_ALPHA )
+ {
+
+ /////////////////////////////
+ // We're entering simulation
+ /////////////////////////////
+
+ // Transit to Simulation control and set up its ground plane...
+ // Didn't need to be done for NPCs cuz they would have been
+ // submitted by other dynamics when hit (and would have obtained
+ // their ground plane at that point)
+ AddToSimulation();
+
+ rAssert( mGroundPlaneSimState );
+ mGroundPlaneSimState->GetCollisionObject()->SetCollisionEnabled( true );
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(v->mName)
+ {
+ if(chaseManager->IsModelRegistered(v->mName))
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_CAUGHT, NULL );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+// return sim::Solving_Continue;
+ }
+ }
+
+KeepSolving:
+
+ if ( bTestNormals )
+ {
+ for ( int i = 0; i < mCurrentCollision; i++ )
+ {
+ // Test each collision normal to see if it is a duplicate of a collision we
+ // have already stored.
+ //
+ if( ( mCollisionData[ i ].mpCollisionVolume == theCollision.mCollisionVolumeA ||
+ mCollisionData[ i ].mpCollisionVolume == theCollision.mCollisionVolumeB ) )
+ {
+ rAssert( mbCollided );
+
+ rmt::Vector facing;
+ GetFacing(facing);
+
+ bool store = ( mCollisionData[ i ].mCollisionNormal.DotProduct(facing) > theCollision.mNormal.DotProduct(facing));
+
+ if(store)
+ {
+ mCollisionData[ i ].mCollisionDistance = theCollision.mDistance;
+ GetPosition( mCollisionData[ i ].mCollisionPosition );
+ mCollisionData[ i ].mCollisionNormal = theCollision.mNormal;
+ mCollisionData[ i ].mpCollisionVolume = theCollision.mCollisionVolumeA == mpSimStateObj->GetCollisionObject()->GetCollisionVolume() ? theCollision.mCollisionVolumeB : theCollision.mCollisionVolumeA;
+ }
+
+
+ return sim::Solving_Continue;//true;
+ }
+ }
+ }
+
+
+ // Tests to see if we should skip this collision.
+ //
+ mbCollided = true;
+
+ mCollisionData[ mCurrentCollision ].mCollisionDistance = theCollision.mDistance;
+ GetPosition( mCollisionData[ mCurrentCollision ].mCollisionPosition );
+ mCollisionData[ mCurrentCollision ].mCollisionNormal = theCollision.mNormal;
+ mCollisionData[ mCurrentCollision ].mpCollisionVolume = theCollision.mCollisionVolumeA == mpSimStateObj->GetCollisionObject()->GetCollisionVolume() ? theCollision.mCollisionVolumeB : theCollision.mCollisionVolumeA;
+
+ mCurrentCollision++;
+
+ return sim::Solving_Continue;//true;
+
+}
+
+
+//=============================================================================
+// Character::PostReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer Character::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+ return sim::Solving_Continue;
+}
+
+
+/*
+==============================================================================
+Character::SolveCollisionWithStatic
+==============================================================================
+Description: desiredPos is the new position of the character after the
+ choreo::UpdateRoot( ) call.
+ the position of the character at the time of collision
+ was stored in the mCollisionData array.
+
+Parameters: ( const rmt::Vector& desiredPos )
+
+Return: rmt
+
+=============================================================================
+*/
+Character::eCollisionType Character::SolveCollisionWithStatic( const rmt::Vector& desiredPos, rmt::Vector& outPos )
+{
+ static bool sbOutput = false;
+
+ outPos = desiredPos; //.Set( 0.0f, 0.0f, 0.0f );
+ if ( !mbSolveCollisions )
+ {
+ return NoCollision;
+ }
+ eCollisionType collisionType = NoCollision;
+ int intCollisionType = collisionType;
+ int i = 0;
+
+ if(mCurrentCollision > 0)
+ {
+ if ( sbOutput ) rDebugPrintf( "solving %d\n", mCurrentCollision);
+ }
+
+ // unsightly hack to try and deal with multiple collisions trying
+ // to push you through walls
+ if(mCurrentCollision > 1)
+ {
+ bool lock = true;
+
+ if(mCurrentCollision == 2)
+ {
+ if( mCollisionData[ 0 ].mCollisionNormal.Dot(mCollisionData[ 1 ].mCollisionNormal) > 0.5f)
+ {
+ lock = false;
+ }
+ }
+
+ int nAway = 0;
+ rmt::Vector facing;
+ GetFacing(facing);
+
+ for(int i = 0; i < mCurrentCollision; i++)
+ {
+ if( mCollisionData[ i ].mCollisionNormal.Dot(facing) > 0.0f)
+ {
+ nAway++;
+ }
+ }
+
+ if(nAway == mCurrentCollision)
+ {
+ lock = false;
+ }
+
+ for(int i = 0; i < mCurrentCollision; i++)
+ {
+ if( mCollisionData[ i ].mCollisionNormal.y > 0.1f)
+ {
+ lock = false;
+ break;
+ }
+ }
+
+ if(lock)
+ {
+ if ( sbOutput ) rDebugPrintf( "locking\n", mCurrentCollision);
+ outPos = mLastFramePos;
+ if(this->GetJumpLocomotionAction()->IsInJump())
+ {
+ outPos.y = desiredPos.y;
+
+ for(int i = 0; i < mCurrentCollision; i++)
+ {
+ if ( mCollisionData[ i ].mCollisionNormal.y <= -0.5f )
+ {
+ return HitHead;
+ }
+ }
+ }
+ return HitWall;
+ }
+ }
+
+ int numSlides = 0;
+
+ for(i = 0; i < mCurrentCollision; i++)
+ {
+ if( mCollisionData[ i ].mCollisionNormal.y > 0.1f)
+ {
+ numSlides += 1;
+ }
+ }
+
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ if( !CanStandOnCollisionNormal( mCollisionData[ i ].mCollisionNormal ) )
+ {
+
+ // Desired motion vector.
+ //
+ rmt::Vector diffVect;
+
+ // outPos is the solved position. So it will change each time
+ // through the loop.
+ //
+ static bool sbUseOutpos = true;
+ rmt::Vector prevPos = mCollisionData[ i ].mCollisionPosition; //GetPuppet( )->GetPrevPosition( );
+ if ( sbUseOutpos )
+ diffVect.Sub( outPos, prevPos );
+ else
+ diffVect.Sub( desiredPos, prevPos );
+
+ // dampen the y a little, to avoid multiple sliding surfaces
+ // actually holding us in the air
+ if( mCollisionData[ i ].mCollisionNormal.y > 0.1f)
+ {
+ diffVect.y *= 1.0f / float(numSlides);
+ }
+
+ // Normalized desired motion vector.
+ //
+ rmt::Vector normalizeDiff = diffVect;
+
+ // The distance we want to travel this frame.
+ //
+ float dist = normalizeDiff.NormalizeSafe();
+
+ // handle the case when the character is not moving,
+ // but an animated collision volume has collided with it.
+ //
+ if ( dist == 0.0f )
+ {
+ // Possible solution. Take the collision normal,
+ // and use that for 'normalizeDiff'. ie assume the character
+ // is moving (relatively) in the direction of the normal.
+ //
+ normalizeDiff = mCollisionData[ i ].mCollisionNormal;
+ normalizeDiff.Scale(-1.0f);
+ dist = 0.0f;
+ }
+ // The dot will tell us if we are moving into ( dot < 0 )
+ // the collision or away from the collision (dot >= 0 )..
+ //
+ float dot = normalizeDiff.DotProduct( mCollisionData[ i ].mCollisionNormal );
+ rmt::Vector adjPos = mCollisionData[ i ].mCollisionNormal;
+ bool bSolve = true;
+ if ( bSolve )
+ {
+ // Scale the distance against the collision normal.
+ //
+ float tempDist = dist;
+ tempDist *= -dot;
+
+ // We only want to add the collision distance to
+ // tempDist (the distance we will scale the motion vector)
+ // when the collDist is gt zero, ie NOT interpentrating.
+ // If we are interpenetrating, NOT adding the collDist
+ // will snap us to the surface of the collision.
+ //
+ static bool sbPositive = true;
+ static bool sbNegative = true;
+ if ( mCollisionData[ i ].mCollisionDistance < 0.0f )
+ {
+ if ( sbNegative )
+ {
+ tempDist -= mCollisionData[ i ].mCollisionDistance;
+ if ( sbOutput ) rDebugPrintf( "collision %.4f (%.2f, %.2f, %.2f) %s\n",
+ mCollisionData[ i ].mCollisionDistance,
+ mCollisionData[ i ].mCollisionNormal.x,
+ mCollisionData[ i ].mCollisionNormal.y,
+ mCollisionData[ i ].mCollisionNormal.z,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( ) );
+ }
+ }
+ else if ( mCollisionData[ i ].mCollisionDistance > 0.0f )
+ {
+ if( sbPositive )
+ {
+ tempDist += mCollisionData[ i ].mCollisionDistance;
+ if ( sbOutput ) rDebugPrintf( "collision %.4f (%.2f, %.2f, %.2f) %s\n",
+ mCollisionData[ i ].mCollisionDistance,
+ mCollisionData[ i ].mCollisionNormal.x,
+ mCollisionData[ i ].mCollisionNormal.y,
+ mCollisionData[ i ].mCollisionNormal.z,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( ) );
+ }
+ }
+ adjPos.Scale( tempDist );
+
+ rmt::Vector tmpOutPos = outPos;
+ outPos.Add( adjPos );
+
+ // if the collisionNormal is straight down.
+ //
+ if ( mCollisionData[ i ].mCollisionNormal.y <= -0.5f )
+ {
+ // Ouch, you hit your head!
+ //
+ intCollisionType |= HitHead;
+ if(!IsNPC())
+ {
+ outPos.y -= 0.1f;
+ }
+ }
+ else
+ {
+
+ // D'oh. You ran into a wall.
+ //
+ // if we're surfing and the wall belongs to the car we're
+ // on, then we can ignore it. Physics gives us weird collisions
+ // from time to time, causing us to get shoved off the car
+ // we're surfing on.
+ //
+ bool ignoreThisOne = false;
+
+ if( mbSurfing && (this->GetLocoVelocity().MagnitudeSqr() < 0.001f) && mpStandingCollisionVolume)
+ {
+ sim::SimState* surfSimState = mpStandingCollisionVolume->GetCollisionObject( )->GetSimState();
+
+ rAssert( surfSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle );
+
+ sim::SimState* objSimState = mCollisionData[ i ].mpCollisionVolume->GetCollisionObject()->GetSimState();
+
+ if( objSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle &&
+ (Vehicle*)(objSimState->mAIRefPointer) == (Vehicle*)(surfSimState->mAIRefPointer) )
+ {
+ ignoreThisOne = true;
+ }
+ }
+
+ if( ignoreThisOne )
+ {
+ outPos = tmpOutPos;
+ }
+ else
+ {
+ intCollisionType |= HitWall;
+ }
+ }
+
+ if( intCollisionType != NoCollision )
+ {
+ // if we hit a static or a fence piece
+ sim::SimState* simState = mCollisionData[ i ].mpCollisionVolume->
+ GetCollisionObject()->GetSimState();
+
+ if( mIsNPC &&
+ (simState->mAIRefIndex == PhysicsAIRef::redBrickPhizFence ||
+ simState->mAIRefIndex == PhysicsAIRef::redBrickPhizStatic) )
+ {
+ // if we're not panicking, this call will have no effect
+ ((NPCController*)GetController())->QuellPanic();
+ }
+
+ }
+ }
+ else
+ {
+ if ( sbOutput ) rDebugPrintf( "Dot product reject. dot = %.2f, dist = %.6f, %s\n",
+ dot,
+ mCollisionData[ i ].mCollisionDistance,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( )
+ );
+ }
+ }
+ else
+ {
+ if ( sbOutput ) rDebugPrintf( "CollisionNormal reject: %.2f, %.2f, %.2f, %s\n",
+ mCollisionData[ i ].mCollisionNormal.x,
+ mCollisionData[ i ].mCollisionNormal.y,
+ mCollisionData[ i ].mCollisionNormal.z,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( )
+ );
+ }
+ }
+ collisionType = (eCollisionType)intCollisionType;
+ return collisionType;
+}
+/*
+==============================================================================
+Character::GetMaxSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+float Character::GetMaxSpeed( void ) const
+{
+ float fTurbo = 0.0f;
+ if ( IsTurbo( ) )
+ {
+ fTurbo = CharacterTune::sfDashBurstMax;
+ }
+ return CharacterTune::sfMaxSpeed + fTurbo;
+}
+
+/*
+==============================================================================
+Character::GetTerrainIntersect
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& pos, rmt::Vector& normal )
+
+Return: void
+
+=============================================================================
+*/
+void Character::GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const
+{
+ GetPosition( pos );
+ pos.y = mGroundY;
+ normal = mGroundNormal;
+}
+
+void Character::GetTerrainType( eTerrainType& TerrainType, bool& Interior ) const
+{
+ TerrainType = mTerrainType;
+ Interior = mInteriorTerrain;
+}
+
+void Character::SnapToGround(void)
+{
+ mbSnapToGround = true;
+ UpdateGroundHeight();
+}
+
+void Character::UpdateGroundHeight( void )
+{
+ unsigned int modulo = mbInAnyonesFrustrum ? 0x3 : 0x7;
+ if( !mbSnapToGround && IsNPC() &&
+ ((GetGame()->GetFrameCount() & modulo) != (mIntersectFrame & modulo)))
+ {
+ return;
+ }
+
+BEGIN_PROFILE("Character::UpdateGroundHeight")
+
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+
+ // Before
+ //
+ rmt::Vector prevPosition = mLastFramePos;//LocalToWorld( pPuppet->GetPrevPosition( ) );
+
+ // And after!
+ //
+ rmt::Vector position;
+ GetPosition( position );
+ // Updates the cached value.
+ //
+// UpdateGroundHeight( prevPosition, position, mGroundY, mGroundNormal );
+
+ rmt::Vector groundPosition = position;
+ rmt::Vector outnorm( 0.0f, 1.0f, 0.0f );
+
+ // I don't understand these ones.
+ //
+ rmt::Vector intersectPos = position;
+ rmt::Vector intersectNorm( 0.0f, 1.0f, 0.0f );
+ bool bFoundIntersect = false;
+ bool bFoundPlane = false;
+
+ mTerrainType = static_cast<eTerrainType>( GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition ) ); // OUT
+ mInteriorTerrain = ( (int)mTerrainType & 0x80 ) == 0x80;
+ mTerrainType = static_cast<eTerrainType>( ( (int)mTerrainType & ~0x80 ) );
+
+ if( bFoundPlane )
+ {
+ mRealGroundPos = groundPosition;
+ mRealGroundNormal = outnorm;
+ float tooHigh = 100000.0f;
+ rAssert( -tooHigh <= mRealGroundNormal.x && mRealGroundNormal.x <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.y && mRealGroundNormal.y <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.z && mRealGroundNormal.z <= tooHigh );
+ }
+ else
+ {
+ //
+ // If this assert goes off for the player charater when he's hit by traffic
+ // it means that Physics has placed him at some location other than his
+ // present location. This is a bad thing... possibly related to
+ // collisions with traffic cars.
+ //rAssertMsg( false, "We're SOOO far from the ground, we're not intersecting" );
+ }
+ rmt::Vector collisionPosition;
+ rmt::Vector collisionNormal;
+
+ collisionNormal.Clear( );
+ collisionPosition.Clear( );
+
+
+ rmt::Vector playerPos;
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition( playerPos );
+
+ rmt::Vector distVecToPlayer = playerPos - position;
+ distVecToPlayer.y = 0.0f;
+ float distSqrFromPlayer = distVecToPlayer.MagnitudeSqr();
+
+ float delta = position.y - groundPosition.y;
+
+ const float TOO_FAR_FROM_GROUND_DIST = 2.0f; // 2 meters? Maybe that's good enough
+ const float VISIBLE_TO_PLAYER_DIST_SQR = 90000.0f;
+ const float CLOSE_ENOUGH_TO_PLAYER_DIST_SQR = 400.0f; // always check if within 20 meters
+
+ bool bOverStatic = false;
+ if(IsNPC())
+ {
+ // discretionally ray-test against objects
+ if( delta > TOO_FAR_FROM_GROUND_DIST ||
+ (!bFoundPlane && distSqrFromPlayer <= VISIBLE_TO_PLAYER_DIST_SQR) ||
+ distSqrFromPlayer < CLOSE_ENOUGH_TO_PLAYER_DIST_SQR )
+ {
+ //rDebugPrintf( "**** NPC %s calling getCollisionHeight *****\n", GetName() );
+ bOverStatic = GetCollisionHeight( prevPosition, position, collisionPosition, collisionNormal );
+ }
+ /*
+ else if( distSqrFromPlayer >= CLOSE_ENOUGH_TO_PLAYER_DIST_SQR )
+ {
+ if( strcmp( GetName(), "b_ralph" ) == 0 )
+ {
+ rDebugPrintf( "%s not within 20 meters of player\n", GetName() );
+ }
+ }
+ */
+ }
+ else
+ {
+ bOverStatic = GetCollisionHeight( prevPosition, position, collisionPosition, collisionNormal );
+ }
+
+ bool bNpcShouldNotMove = false;
+
+ if ( bOverStatic )
+ {
+ if ( !bFoundPlane || collisionPosition.y > groundPosition.y )
+ {
+ mGroundY = collisionPosition.y;
+ mGroundNormal = collisionNormal;
+
+ mLastGoodPosOverStatic = position;
+ mLastGoodPosOverStatic.y = mGroundY;
+ }
+ else
+ {
+ rAssert( bFoundPlane );
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ }
+ else if ( bFoundPlane )
+ {
+ if( IsNPC() && mLastGoodPosOverStatic.Equals( position, 0.5f ) )
+ {
+ if( ((NPCController*)GetController())->GetState() == NPCController::TALKING_WITH_PLAYER )
+ {
+ bNpcShouldNotMove = false; // don't transit out of TALKING_WITH_PLAYER state
+ }
+ else
+ {
+ bNpcShouldNotMove = true;
+ }
+ mGroundY = position.y;
+ mGroundNormal.Set( 0.0f, 1.0f, 0.0f );
+ }
+ else
+ {
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ }
+ else
+ {
+ bNpcShouldNotMove = true;
+ mGroundY = position.y;
+ mGroundNormal.Set( 0.0f, 1.0f, 0.0f );
+ }
+
+ if(mpSimStateObj->GetControl() == sim::simSimulationCtrl)
+ {
+ return;
+ }
+
+ if( mIsNPC )
+ {
+ // if we're dropping more than N meters, we transit to sim,
+ // but only if we have a valid ground plane (otherwise we could
+ // fall through the world!)
+ delta = position.y - mGroundY;
+ if( delta > TOO_FAR_FROM_GROUND_DIST &&
+ distSqrFromPlayer <= VISIBLE_TO_PLAYER_DIST_SQR &&
+ mGroundPlaneIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ // transit to sim here so we "fall" to the ground
+ mpSimStateObj->SetControl( sim::simSimulationCtrl );
+ GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
+ }
+ }
+
+
+ static float sfFallingTolerance = 0.5f;
+
+ rmt::Vector pos;
+ GetPosition( pos );
+
+ rmt::Vector dummy, groundPos;
+ GetTerrainIntersect( groundPos, dummy );
+
+ delta = pos.y - groundPos.y;
+
+ JumpAction* pJumpAction = GetJumpLocomotionAction();
+ bool inJump = pJumpAction->IsInJump();
+
+ if (( mVelocity.y <= mfGroundVerticalVelocity && (delta < 0.0f )) ||
+ ( !inJump && (delta < sfFallingTolerance)) ||
+ ( IsNPC() && mpSimStateObj->GetControl() == sim::simAICtrl ) ||
+ mbSnapToGround)
+ {
+ mbIsStanding = true;
+
+ if(mbDoGroundIntersect || mbSnapToGround)
+ {
+ mbSnapToGround = false;
+
+ if( IsNPC() )
+ {
+ NPCController* npcController = (NPCController*) GetController();
+ if( bNpcShouldNotMove )
+ {
+ npcController->TransitToState( NPCController::NONE );
+ }
+ else
+ {
+ if( npcController->GetState() == NPCController::NONE )
+ {
+ npcController->TransitToState( NPCController::FOLLOWING_PATH );
+ }
+ }
+ }
+ SetPosition(groundPos);
+ this->SetGroundPoint(groundPos);
+ mpJumpLocomotion->SetRootPosition( WorldToLocal(groundPos) );
+ }
+ }
+ else
+ {
+ mbIsStanding = false;
+
+ if ( !mbIsStanding && mbDoGroundIntersect && !inJump && (this->mpSimStateObj->GetControl() == sim::simAICtrl))
+ {
+ //falling.
+ //
+ pJumpAction->Reset( 0.0f, true );
+ GetActionController()->Clear();;
+ Sequencer* seq = GetActionController()->GetNextSequencer();
+ seq->BeginSequence();
+ seq->AddAction(pJumpAction);
+ seq->EndSequence();
+ }
+ }
+
+END_PROFILE("Character::UpdateGroundHeight")
+}
+/*
+==============================================================================
+Character::pPosition
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: rmt
+
+=============================================================================
+*/
+rmt::Vector* Character::pPosition()
+{
+ rAssert( 0 );
+ return (Vector*)0;
+}
+
+/*
+==============================================================================
+Character::rPosition
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: const
+
+=============================================================================
+*/
+const rmt::Vector& Character::rPosition()
+{
+ GetPosition(lameAssPosition);
+ return lameAssPosition;
+}
+
+/*
+==============================================================================
+Character::GetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* ipPosn )
+
+Return: void
+
+=============================================================================
+*/
+void Character::GetPosition( rmt::Vector* ipPosn )
+{
+ GetPosition(*ipPosn);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void Character::SetFadeAlpha( int fadeAlpha )
+{
+ if( mpCharacterRenderable != NULL )
+ {
+ mpCharacterRenderable->SetFadeAlpha( fadeAlpha );
+ }
+}
+
+int Character::CastsShadow()
+{
+ return mpCharacterRenderable->CastsShadow();
+}
+/*
+==============================================================================
+Character::DisplayShadow
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void Character::DisplayShadow()
+{
+ /*
+ if( !IsInCar() && !IsSimpleShadow() )
+ {
+ mpCharacterRenderable->DisplayShadow( mpPuppet->GetP3DPose() );
+ }
+ */
+}
+
+/*
+==============================================================================
+Character::DisplaySimpleShadow
+==============================================================================
+Description: Draw the simple shadow during the simple shadow pass.
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void Character::DisplaySimpleShadow( void )
+{
+}
+
+
+//=============================================================================
+//Character::DisplaySimpleShadow
+//=============================================================================
+//Description: Draw the simple shadow during the simple shadow pass.
+//
+//Parameters: ()
+//
+//Return: void
+//=============================================================================
+bool Character::CanPlayAnimation( const tName& name ) const
+{
+ choreo::Bank* bank = mpPuppet->GetBank();
+ choreo::Animation* anim = choreo::find<choreo::Animation>( bank, name.GetUID() );
+ if( anim == NULL )
+ {
+ PrintAnimations();
+ }
+ return ( anim != NULL );
+}
+
+/*
+==============================================================================
+Character::CanStandOnCollisionVolume
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStandOnCollisionVolume( void ) const
+{
+ int i;
+
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ bool bCanStand = CanStandOnCollisionNormal( mCollisionData[ i ].mCollisionNormal );
+ if ( bCanStand )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::CanStandOnCollisionNormal
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& normal )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStandOnCollisionNormal( const rmt::Vector& normal ) const
+{
+ float dot = normal.DotProduct( vUp );
+ const float cos30 = 0.86602540378443864676372317075294f;
+ static float sfStandTolerance = cos30;
+ if ( dot > sfStandTolerance )
+ {
+ return true;
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::CanStaggerCollision
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStaggerCollision( void ) const
+{
+ int i;
+
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ if ( this->mpStandingCollisionVolume != mCollisionData[ i ].mpCollisionVolume )
+ {
+ bool bCanStagger = CanStaggerCollisionNormal( mCollisionData[ i ].mCollisionNormal );
+ if ( bCanStagger )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::CanStaggerCollisionNormal
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& normal )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStaggerCollisionNormal( const rmt::Vector& normal ) const
+{
+
+ static rmt::Vector facing;
+ GetFacing( facing );
+
+ float dot = normal.DotProduct( facing );
+ static float sfStaggerTolerance = -0.9f;
+ if ( dot < sfStaggerTolerance )
+ {
+ return true;
+ }
+ return false;
+}
+/*
+==============================================================================
+Character::FindStandingVolume
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& inPos, sim::CollisionVolume* inVolume, rmt::Vector& outNormal, float& outDist)
+
+Return: sim
+
+=============================================================================
+*/
+sim::CollisionVolume* Character::FindStandingVolume( const rmt::Vector& inPos, sim::CollisionVolume* inVolume, rmt::Vector& outNormal, float& outDist )
+{
+ sim::CollisionVolume* pOutVolume = 0;
+
+ switch (inVolume->Type())
+ {
+ case sim::SphereVolumeType:
+ case sim::CylinderVolumeType:
+ case sim::OBBoxVolumeType:
+ case sim::WallVolumeType:
+ {
+ float currentDist = VERY_LARGE;
+ rmt::Vector currentNormal;
+
+ // trivial reject stuff that is actually no where near us (probably a subvolume
+ // of a large volume we did intersect with)
+ if((rmt::Fabs(inVolume->mPosition.x - inPos.x) > (inVolume->mBoxSize.x + 1.0f)) ||
+ (rmt::Fabs(inVolume->mPosition.z - inPos.z) > (inVolume->mBoxSize.z + 1.0f)))
+ {
+ break;
+ }
+
+ sim::CollisionVolume* pOutSubVolume = sim::FindClosestPointOnVolume( inPos, inVolume, currentNormal, currentDist);
+ if ( pOutSubVolume && pOutSubVolume->GetCollisionObject()->GetCollisionEnabled())
+ {
+ if ( CanStandOnCollisionNormal( currentNormal ) )
+ {
+ // We must be above the object.
+ //
+ if ( currentDist >= 0.0f )
+ {
+ pOutVolume = pOutSubVolume;
+ outDist = currentDist;
+ outNormal = currentNormal;
+ }
+ }
+ }
+ break;
+ }
+ case sim::BBoxVolumeType:
+ {
+ float currentDist = VERY_LARGE;
+ rmt::Vector currentNormal;
+ for (int i=0; i<inVolume->SubVolumeList()->GetSize(); i++)
+ {
+ sim::CollisionVolume* pOutSubVolume = FindStandingVolume(inPos, inVolume->SubVolumeList()->GetAt(i), currentNormal, currentDist );
+ if ( pOutSubVolume && pOutSubVolume->GetCollisionObject()->GetCollisionEnabled())
+ {
+ // For each volume returned, keep the closest one only.
+ //
+ if ( currentDist < outDist )
+ {
+ outDist = currentDist;
+ outNormal = currentNormal;
+ pOutVolume = pOutSubVolume;
+ }
+ }
+ }
+ }
+ break;
+ case sim::MaxCollisionVolumeEnum:
+ case sim::CollisionVolumeType:
+ break;
+ }
+ return pOutVolume;
+}
+/*
+==============================================================================
+Character::GetCollisionHeight
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& prevPosition, const rmt::Vector& position, rmt::Vector& outPosition, rmt::Vector& collisionNormal )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::GetCollisionHeight( const rmt::Vector& prevPosition, const rmt::Vector& position, rmt::Vector& outPosition, rmt::Vector& collisionNormal )
+{
+BEGIN_PROFILE( "GetCollisionHeight" );
+
+ msIntersectInfo.Clear();
+
+ float fOldRayThickness = sim::RayIntersectionInfo::sRayThickness;
+ static float sfCharacterRayThickness = 0.3f;
+ sim::RayIntersectionInfo::sRayThickness = sfCharacterRayThickness;
+ const poser::Joint* pStandingJoint = 0;
+ bool bFoundIntersect = false;
+ sim::CollisionVolume* oldStanding = NULL;
+ tRefCounted::Assign(oldStanding, mpStandingCollisionVolume);
+ tRefCounted::Release(mpStandingCollisionVolume);
+
+ // Had to increase the fudge when I moved character::update calls outside
+ // of the physics substep.
+ //
+ static float sfFudge = 0.6f;
+ rmt::Vector testPosition;
+ testPosition = position;
+ testPosition.y = prevPosition.y;
+ testPosition.y += sfFudge;
+
+ float outDist = VERY_LARGE;
+ float currentDist = outDist;
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ // adds in the list the collision object interfering with the ray and ordered according to their distance to the source.
+ // use sim::RayIntersectionInfo::SetMethod(method) to set the method
+ // use sim::RayIntersectionInfo::SetReturnClosestOnly(true/false) if you need only the closest object
+ // nb. if SetReturnClosestOnly(true) is used, the previous returned list can be used as a cache.
+ sim::RayIntersectionInfo::SetReturnClosestOnly( false );
+ sim::RayIntersectionInfo::SetMethod( sim::RayIntersectionBBox );
+ rmt::Vector rayOrg( 0.0f, 0.0f, 0.0f );
+ rmt::Vector rayEnd( 0.0f, -100.0f, 0.0f );
+ rayOrg.Add( testPosition );
+ rayEnd.Add( testPosition );
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ // Do not allow ray test against the character collision object
+ // Otherwise, rayintersection will detect against player volume.
+ //
+ bool bRayRestore = mpSimStateObj->GetCollisionObject()->GetRayCastingEnabled( );
+ bool bCollRestore = mpSimStateObj->GetCollisionObject()->GetCollisionEnabled( );
+ mpSimStateObj->GetCollisionObject( )->SetRayCastingEnabled( false );
+ mpSimStateObj->GetCollisionObject( )->SetCollisionEnabled( false );
+
+ bool bGroundPlaneRestore = false;
+ if( mGroundPlaneSimState )
+ {
+ bGroundPlaneRestore = mGroundPlaneSimState->GetCollisionObject( )->GetRayCastingEnabled( );
+ mGroundPlaneSimState->GetCollisionObject( )->SetRayCastingEnabled( false );
+ }
+
+ // Test ray against remaining collision objects.
+ //
+ GetWorldPhysicsManager()->mCollisionManager->DetectRayIntersection(msIntersectInfo, rayOrg, rayEnd, false, this->GetCollisionAreaIndex() );
+ //GetWorldPhysicsManager()->mCollisionManager->DetectRayIntersection(msIntersectInfo, rayOrg, rayEnd, false,
+ // GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter()->GetCollisionAreaIndex() );
+
+ // Restore the state.
+ //
+ mpSimStateObj->GetCollisionObject( )->SetRayCastingEnabled( bRayRestore );
+ mpSimStateObj->GetCollisionObject( )->SetCollisionEnabled( bCollRestore );
+ if( mGroundPlaneSimState )
+ {
+ mGroundPlaneSimState->GetCollisionObject( )->SetRayCastingEnabled( bGroundPlaneRestore );
+ }
+
+
+ // Iterate through the entire list because of way DetectRayIntersection works.
+ // It checks the top level hierarchy of an object, so it will return bad values
+ // if the hierarchy is large, and you are standing on top of something in a different
+ // (smaller) hierarchy. see Level 9, duffTruck BV vs L9 BV.
+ //
+ int i;
+ for ( i = 0; i < msIntersectInfo.GetSize( ); i++ )
+ {
+ if ( msIntersectInfo.GetSize() > 0 && msIntersectInfo[ i ].mCollisionVolume )
+ {
+ rmt::Vector outNormal;
+ float prevOut = outDist;
+ sim::CollisionVolume* pOutVolume = FindStandingVolume( testPosition, msIntersectInfo[ i ].mCollisionVolume, outNormal, outDist );
+
+ bool vehicleIgnore = false;
+ if(this->mpTargetVehicle && pOutVolume)
+ {
+ if(pOutVolume->GetCollisionObject()->GetSimState()->mAIRefPointer == this->mpTargetVehicle)
+ {
+ if(oldStanding != pOutVolume)
+ {
+ vehicleIgnore = true;
+ outDist = prevOut;
+ }
+ }
+ }
+
+ if ( pOutVolume &&
+ pOutVolume->GetCollisionObject()->GetCollisionEnabled() &&
+ (pOutVolume->GetCollisionObject()->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickPhizVehicleGroundPlane) &&
+ (pOutVolume->GetCollisionObject()->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickPhizMoveableGroundPlane) &&
+ (!vehicleIgnore))
+ {
+ if ( outDist < currentDist )
+ {
+ currentDist = outDist;
+ tRefCounted::Assign(mpStandingCollisionVolume, pOutVolume);
+ collisionNormal = outNormal;
+ }
+ }
+ }
+ }
+
+ mbSurfing = false;
+
+ if ( mpStandingCollisionVolume )
+ {
+ rmt::Vector newPos = collisionNormal;
+ newPos.Scale( -currentDist );
+ testPosition.Add( newPos );
+ outPosition = testPosition;
+
+ // Test to see if we are standing on the collision volume.
+ //
+ if ( position.y - outPosition.y <= 0.1f )
+ {
+ // We are standing.
+ //
+ // If this is an animated object, find the transform to parent the character.
+ //
+ sim::SimState* pSimState = mpStandingCollisionVolume->GetCollisionObject( )->GetSimState();
+
+ if(pSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ mbSurfing = true;
+ }
+
+ if( !( PhysicsAIRef::redBrickPhizStatic & pSimState->mAIRefIndex ) )
+ {
+ if ( pSimState->IsArticulated( ) )
+ {
+ // Find joint based on mpStandingCollisionVolume->ObjRefIndex().
+ //
+ sim::SimStateArticulated* pSimStateArticulated = static_cast<sim::SimStateArticulated*>( pSimState );
+
+ // Michael - this will assert on articulated objects that were converted to rigid bodies
+ // (these always have their volume::objrefindex set to -1)
+ // commenting out, it doesn't appear to have any problems
+
+ // rAssert(mpStandingCollisionVolume->ObjRefIndex() != -1);
+ if(mpStandingCollisionVolume->ObjRefIndex() != -1)
+ {
+ const poser::Pose* pPose = pSimStateArticulated->GetPose( );
+ rAssert( pPose );
+ pStandingJoint = pPose->GetJoint( mpStandingCollisionVolume->ObjRefIndex() );
+ rAssert( pStandingJoint );
+ }
+ }
+
+ }
+ }
+ bFoundIntersect = true;
+ }
+
+ SetStandingJoint( pStandingJoint );
+ sim::RayIntersectionInfo::sRayThickness = fOldRayThickness;
+END_PROFILE( "GetCollisionHeight" );
+ tRefCounted::Release(oldStanding);
+ return bFoundIntersect;
+}
+
+#ifdef RAD_DEBUG
+
+void Character::PrintAnimations() const
+{
+ choreo::Bank* bank = mpPuppet->GetBank();
+ choreo::BaseBank::RawIterator* it = bank->NewRawIterator();
+ it->AddRef();
+
+ IRefCount* obj = it->First();
+ while( obj != NULL )
+ {
+ choreo::Animation* anim = dynamic_cast< choreo::Animation* >( obj );
+ if( anim != NULL )
+ {
+ tAnimation* tAnim = anim->GetP3DAnimation();
+ const char* name = tAnim->GetName();
+ rDebugPrintf( "animationName '%s'\n", name );
+ }
+ obj = it->Next();
+ }
+ it->Release();
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+void Character::SetStandingJoint( const poser::Joint* pJoint )
+{
+ if ( pJoint != mpStandingJoint )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+
+ rmt::Vector desiredFacing;
+ GetDesiredFacing( desiredFacing );
+ rmt::Vector facingVector = mpPuppet->GetFacingVector( );
+
+ mParentTransform.RotateVector( desiredFacing, &desiredFacing );
+ mParentTransform.RotateVector( facingVector, &facingVector );
+
+ rmt::Matrix transform;
+ if(pJoint)
+ {
+ transform = pJoint->GetWorldMatrix( );
+ }
+ else
+ {
+ transform.Identity();
+ }
+
+ SetParentTransform( transform );
+
+ SetPosition( position );
+ mInvParentTransform.RotateVector( desiredFacing, &desiredFacing );
+ mInvParentTransform.RotateVector( facingVector, &facingVector );
+ mpPuppet->SetFacingVector( facingVector );
+ SetDesiredDir( choreo::GetWorldAngle( desiredFacing.x, desiredFacing.z ) );
+ mpJumpLocomotion->SetRootTransform( );
+ mpStandingJoint = pJoint;
+ }
+}
+
+/*
+==============================================================================
+Character::SetGroundPoint
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& groundPoint )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetGroundPoint( const rmt::Vector& groundPoint )
+{
+ //choreo::Puppet* pPuppet = GetPuppet( );
+ //if ( pPuppet )
+ //{
+ // pPuppet->SetGroundPoint( groundPoint );
+ //}
+ //SetPosition( groundPoint );
+ if ( mpPuppet )
+ {
+ // Transform from world to object space.
+ //
+ rmt::Vector transformedPos = groundPoint;
+ transformedPos.Transform( mInvParentTransform );
+
+ mpPuppet->SetPosition( transformedPos );
+
+ poser::Pose* pPose = mpPuppet->GetPose( );
+ // stuff fixed up root transform into joint
+ poser::Joint* joint = pPose->GetJoint( 0 );
+ joint->SetObjectTranslation( groundPoint );
+ joint->SetWorldTranslation( groundPoint );
+ }
+}
+//=============================================================================
+// Character::UpdateTransformToLoco
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void Character::UpdateTransformToLoco( void )
+{
+ // This will get us the world space position and facing.
+ //
+ rmt::Vector position;
+ GetPosition( position );
+ rmt::Vector facing;
+ GetFacing( facing );
+ mParentTransform.RotateVector( facing, &facing );
+
+ tRefCounted::Release(mpStandingCollisionVolume);
+ mbSurfing = false;
+
+ // Go from the car space back to world space.
+ //
+ UpdateParentTransform( 0.0f );
+
+ SetDesiredDir( choreo::GetWorldAngle( facing.x, facing.z ) );
+ SetFacingDir( choreo::GetWorldAngle( facing.x, facing.z ) );
+ SetPosition( position );
+
+ SetInCar(false);
+
+ //AssignCollisionAreaIndex( );
+ //mpSimStateObj->GetCollisionObject()->SetCollisionEnabled( true );
+}
+/*
+==============================================================================
+Character::AssignCollisionAreaIndex
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AssignCollisionAreaIndex( void )
+{
+ if ( WorldPhysicsManager::INVALID_COLLISION_AREA == mCollisionAreaIndex )
+ {
+ mCollisionAreaIndex = GetWorldPhysicsManager()->GetCharacterCollisionAreaIndex();
+ }
+ rTuneAssert( mCollisionAreaIndex != -1 );
+}
+//=============================================================================
+// Character::UpdateTransformToInCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void Character::UpdateTransformToInCar( void )
+{
+ if( mbIsJump )
+ {
+ mpJumpLocomotion->Done();
+ mbIsJump = false;
+ }
+
+ rmt::Vector pos;
+ GetPosition(pos);
+
+ // Update the character parent transform to vehicleToWorld.
+ //
+ UpdateParentTransform( 0.0f );
+
+ SetStandingJoint(NULL);
+ tRefCounted::Release(mpStandingCollisionVolume);
+ mbSurfing = false;
+
+ SetPosition(pos);
+ SetDesiredDir( rmt::PI );
+ SetFacingDir( rmt::PI );
+}
+
+/*
+==============================================================================
+Character::SetInCar
+==============================================================================
+Description: Comment
+
+Parameters: ( bool bInCar )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetInCar( bool bInCar )
+{
+ mbInCar = bInCar;
+
+ mTranslucent = !mbInCar;
+
+ if(this == GetCharacterManager()->GetCharacter(0))
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( !mbInCar )
+ {
+ if( radTimeGetMicroseconds64()-mLastInteriorLoadCheck > 3000000 ) //(amortise/3s) if it's been more than 3 sec's since we last checked for volumes
+ {
+ rmt::Vector posn;
+ GetPosition(&posn);
+ GetTriggerVolumeTracker()->ActivateNearestInteriorLoadZone(0, posn, 40.0f);
+ mLastInteriorLoadCheck = radTimeGetMicroseconds64();
+ }
+
+ if(mpCurrentActionButtonHandler)
+ {
+ if ( mpCurrentActionButtonHandler->IsInstanceEnabled() )
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ else if(mbInCar && mpCurrentActionButtonHandler)
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+Character::GetActionButtonHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ActionButton
+
+=============================================================================
+*/
+ActionButton::ButtonHandler* Character::GetActionButtonHandler( void ) const
+{
+ return mpCurrentActionButtonHandler;
+}
+
+/*
+==============================================================================
+Character::AddActionButtonHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionButton::ButtonHandler* pActionButtonHandler )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AddActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler )
+{
+ if ( !IsNPC() )
+ {
+ unsigned int i;
+#ifdef RAD_DEBUG
+ //Make sure this is only added once.
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ rAssert( mpActionButtonHandlers[ i ] != pActionButtonHandler );
+ }
+#endif
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] == NULL )
+ {
+ //Add here.
+ mpActionButtonHandlers[ i ] = pActionButtonHandler;
+ mpActionButtonHandlers[ i ]->AddRef();
+ break;
+ }
+ }
+
+ rAssertMsg( i < MAX_ACTION_BUTTON_HANDLERS, "Need to increase the size of MAX_ACTION_BUTTON_HANDLERS" );
+ if ( i == MAX_ACTION_BUTTON_HANDLERS )
+ {
+ return;
+ }
+
+ ActionButton::ButtonHandler* newButton = TestPriority( pActionButtonHandler, mpCurrentActionButtonHandler );
+
+ if ( newButton != mpCurrentActionButtonHandler )
+ {
+ //This is a new action button of highest priority.
+
+ mpCurrentActionButtonHandler = pActionButtonHandler;
+
+ if ( !IsInCar() )
+ {
+ if ( mpCurrentActionButtonHandler->IsInstanceEnabled() )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// Character::RemoveActionButtonHandler
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionButtonHandler::ButtonHandler* pActionButtonHandler )
+//
+// Return: void
+//
+//=============================================================================
+void Character::RemoveActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler )
+{
+ if ( !IsNPC() )
+ {
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] == pActionButtonHandler )
+ {
+ //This is the one to remove.
+ mpActionButtonHandlers[ i ]->Release();
+ mpActionButtonHandlers[ i ] = NULL;
+
+ if ( mpCurrentActionButtonHandler == pActionButtonHandler )
+ {
+ mpCurrentActionButtonHandler = NULL;
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+
+ break;
+ }
+ }
+
+ if ( mpCurrentActionButtonHandler == NULL )
+ {
+ //Find a new one.
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] != NULL )
+ {
+ mpCurrentActionButtonHandler = TestPriority( mpCurrentActionButtonHandler, mpActionButtonHandlers[ i ] );
+ }
+ }
+
+ if ( mpCurrentActionButtonHandler != NULL )
+ {
+ if ( mpCurrentActionButtonHandler->IsInstanceEnabled() )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// Character::TestPriority
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionButton::ButtonHandler* bA, ActionButton::ButtonHandler* bB )
+//
+// Return: ActionButton
+//
+//=============================================================================
+ActionButton::ButtonHandler* Character::TestPriority( ActionButton::ButtonHandler* bA, ActionButton::ButtonHandler* bB )
+{
+ if ( NULL == bA )
+ {
+ return bB;
+ }
+ else if ( NULL == bB )
+ {
+ return bA;
+ }
+
+ ActionButton::ButtonHandler::Type bAType = bA->GetType();
+ ActionButton::ButtonHandler::Type bBType = bB->GetType();
+
+ if ( bAType == ActionButton::ButtonHandler::GET_IN_CAR || bBType == ActionButton::ButtonHandler::GET_IN_CAR )
+ {
+ //Is this the players car?
+ Avatar* myAvatar = GetAvatarManager()->FindAvatarForCharacter( this );
+
+ if ( myAvatar )
+ {
+ int id = myAvatar->GetPlayerId();
+ rAssert( id == 0 ); //ONly works in single player.
+
+ Vehicle* myVehicle = GetGameplayManager()->GetCurrentVehicle();
+ if ( myVehicle )
+ {
+ int actionId = (int)myVehicle->mpEventLocator->GetData( );
+ ActionButton::ButtonHandler* pActionButtonHandler = GetActionButtonManager()->GetActionByIndex( actionId );
+ rAssert( pActionButtonHandler );
+
+ if ( bA == pActionButtonHandler )
+ {
+ bAType = ActionButton::ButtonHandler::GET_IN_USER_CAR;
+ }
+
+ if ( bB == pActionButtonHandler )
+ {
+ bBType = ActionButton::ButtonHandler::GET_IN_USER_CAR;
+ }
+ }
+ }
+ }
+
+ return bAType < bBType ? bA : bB;
+}
+
+//=============================================================================
+// Character::ClearAllActionButtonHandlers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Character::ClearAllActionButtonHandlers()
+{
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] != NULL )
+ {
+ mpActionButtonHandlers[ i ]->Exit( this );
+ mpActionButtonHandlers[ i ]->Release();
+ mpActionButtonHandlers[ i ] = NULL;
+ }
+ }
+
+ mpCurrentActionButtonHandler = NULL;
+ if(this == GetCharacterManager()->GetCharacter(0))
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+}
+
+tSkeleton* Character::Prop::spSkeleton = 0;
+int Character::Prop::sSkelRefs = 0;
+
+Character::Prop::Prop( void )
+ :
+mpProp( 0 ),
+mpPose( 0 )
+{
+ if ( !spSkeleton )
+ {
+ spSkeleton = new tSkeleton( 1 );
+ rmt::Matrix mat;
+ mat.Identity( );
+ spSkeleton->GetJoint( 0 )->worldMatrix = spSkeleton->GetJoint( 0 )->restPose = mat;
+ spSkeleton->GetJoint( 0 )->inverseWorldMatrix = spSkeleton->GetJoint( 0 )->worldMatrix;
+ spSkeleton->GetJoint( 0 )->inverseWorldMatrix.InvertOrtho( );
+ }
+ spSkeleton->AddRef( );
+ sSkelRefs++;
+ tRefCounted::Assign( mpPose, spSkeleton->NewPose( ) );
+}
+
+Character::Prop::~Prop( void )
+{
+ tRefCounted::Release( mpProp );
+ tRefCounted::Release( mpPose );
+ spSkeleton->Release ();
+ sSkelRefs--;
+ if (sSkelRefs < 1)
+ {
+ spSkeleton = 0;
+ }
+}
+/*
+==============================================================================
+Character::TouchProp
+==============================================================================
+Description: Comment
+
+Parameters: ( InstDynaPhysDSG* pProp )
+
+Return: void
+
+=============================================================================
+*/
+void Character::TouchProp( InstDynaPhysDSG* pProp )
+{
+ /*
+ bool sbPickUp = false;
+ if ( sbPickUp )
+ {
+ mpPropHandler->SetProp( pProp );
+ AddActionButtonHandler( mpPropHandler );
+ mpPropHandler->Enter( this );
+ }
+ */
+}
+/*
+==============================================================================
+Character::AttachProp
+==============================================================================
+Description: Comment
+
+Parameters: ( InstDynaPhysDSG* pProp )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AttachProp( InstDynaPhysDSG* pProp )
+{
+ /*
+ int i;
+ for ( i = 0; i < MAX_PROPS; i++ )
+ {
+ if ( mPropList[ i ].mpProp == 0 )
+ {
+ tRefCounted::Assign( mPropList[ i ].mpProp, pProp );
+ GetPuppet( )->AttachProp( mPropJoint, mPropList[ i ].mpPose );
+
+ mPropList[ i ].mpProp->GetSimState()->GetCollisionObject()->SetCollisionEnabled( false );
+ mPropList[ i ].mpProp->GetSimState()->SetControl( sim::simAICtrl );
+ break;
+ }
+ }
+ */
+}
+/*
+==============================================================================
+Character::RemoveProp
+==============================================================================
+Description: Comment
+
+Parameters: ( InstDynaPhysDSG* pProp )
+
+Return: void
+
+=============================================================================
+*/
+void Character::RemoveProp( InstDynaPhysDSG* pProp )
+{
+ /*
+ int i;
+ for ( i = 0; i < MAX_PROPS; i++ )
+ {
+ if ( mPropList[ i ].mpProp == pProp )
+ {
+ tRefCounted::Release( mPropList[ i ].mpProp );
+ GetPuppet( )->RemoveAttachedProp( mPropList[ i ].mpPose );
+ break;
+ }
+ }
+ */
+}
+/*
+==============================================================================
+Character::UpdateProps
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateProps( float timeins )
+{
+ /*
+ int i;
+ for ( i = 0; i < MAX_PROPS; i++ )
+ {
+ if ( mPropList[ i ].mpProp != 0 )
+ {
+ mPropList[ i ].mpProp->GetSimState()->SetTransform( mPropList[ i ].mpPose->GetJoint( 0 )->worldMatrix, timeins );
+ mPropList[ i ].mpProp->Update( timeins );
+
+ CharacterController::eIntention theIntention = GetController()->GetIntention();
+ if( CharacterController::DoAction == theIntention )
+ {
+ // Throw the sum'bitch.
+ //
+ sim::SimState* pSimState = mPropList[ i ].mpProp->GetSimState();
+ rAssert( pSimState );
+ mPropList[ i ].mpProp->AddToSimulation();
+ GetFacing( pSimState->VelocityState( ).mLinear );
+ static float sfUpVelocity = 3.0f;
+ pSimState->VelocityState( ).mLinear.y = sfUpVelocity;
+ pSimState->GetCollisionObject()->SetCollisionEnabled( true );
+
+ // Not the most efficient.
+ //
+ RemoveProp( mPropList[ i ].mpProp );
+ }
+ }
+ }
+ */
+}
+
+
+
+void Character::UpdatePhysicsObjects( float timeins, int area )
+{
+ rAssert( area != -1 );
+ RestTest();
+ GetWorldPhysicsManager()->UpdateDynamicObjects(timeins, area );
+ GetWorldPhysicsManager()->UpdateAnimCollisions(timeins, area );
+}
+
+/*
+==============================================================================
+Character::SubmitStatics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SubmitStatics( void )
+{
+ BEGIN_PROFILE( "Per Character Submit STatics" );
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // TBJ [8/14/2002]
+ //
+ if( true ) //!(IsInCar()))
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+ static float sfCollisionRadius = 5.0f;
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState(), true);
+
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState(), true);
+ }
+ }
+ }
+ END_PROFILE( "Per Character Submit STatics" );
+}
+
+/*
+==============================================================================
+Character::SubmitAnimCollisions
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SubmitAnimCollisions( void )
+{
+ BEGIN_PROFILE( "Per Character Submit Anims" );
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // This is also nice because objects will animate while we are in the car.
+ //
+ // TBJ [8/14/2002]
+ //
+ if( true ) //!(IsInCar()))
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+
+ static float sfCollisionRadius = 1.5f;
+ GetWorldPhysicsManager()->SubmitAnimCollisionsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState());
+ static float sfUpdateRadius = 30.0f;//0.0f;
+ GetWorldPhysicsManager()->SubmitAnimCollisionsForUpdateOnly( position, sfUpdateRadius, collisionAreaIndex );
+ }
+ }
+ }
+ END_PROFILE( "Per Character Submit Anims" );
+}
+
+/*
+==============================================================================
+Character::SubmitDynamics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SubmitDynamics( void )
+{
+ BEGIN_PROFILE( "Per Character Submit Dyn" );
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // This is also nice because objects will animate while we are in the car.
+ //
+ // TBJ [8/14/2002]
+ //
+ if( true ) //!(IsInCar()))
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+
+ static float sfCollisionRadius = 10.5f;
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState(), true);
+ }
+ }
+ }
+ END_PROFILE( "Per Character Submit Dyn" );
+}
+
+bool Character::PosInFrustrumOfPlayer( const rmt::Vector& pos, int playerID )
+{
+ return GetGameplayManager()->TestPosInFrustrumOfPlayer( pos, playerID );
+}
+
+void Character::TestInFrustrumOfPlayer( int playerID )
+{
+ if( PosInFrustrumOfPlayer( mSphere.centre, playerID ) )
+ {
+ mbInAnyonesFrustrum = true;
+ }
+}
+
+void Character::SetShadowColour( tColour shadowColour )
+{
+ mShadowColour = shadowColour;
+ if( mpCharacterRenderable )
+ {
+ mpCharacterRenderable->SetShadowColour( shadowColour );
+ }
+}
+
+tColour Character::GetShadowColour()
+{
+ return mShadowColour;
+}
+
+void Character::SetSwatch( int swatchNum )
+{
+ rAssert( mpCharacterRenderable != NULL );
+ mpCharacterRenderable->SetSwatch( swatchNum );
+}
+
+void Character::SetDrawable( CharacterRenderable* pDrawable )
+{
+ if(mpCharacterRenderable)
+ {
+ delete mpCharacterRenderable;
+ }
+ mpCharacterRenderable = pDrawable;
+}
+
+void Character::Shock( float timeInSeconds )
+{
+ if ( mpCharacterRenderable )
+ {
+ m_TimeLeftToShock = timeInSeconds;
+ m_IsBeingShocked = true;
+
+ mpCharacterRenderable->SetShocked( true );
+
+ GetEventManager()->TriggerEvent( EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS );
+
+ //
+ // Throw KICK_NPC_SOUND to trigger the HitByW dialogue that Chris and Cory want -- Esan
+ //
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC_SOUND, this );
+ }
+}
+
+void Character::DoKickwave(void)
+{
+ if(!mKickwave)
+ {
+ tRefCounted::Assign(mKickwave, p3d::find<tDrawable>("kickwave"));
+ tRefCounted::Assign(mKickwaveController, p3d::find<tFrameController>("kickwave"));
+ }
+
+ if(mKickwave)
+ {
+ mDoKickwave = true;
+ mKickwaveController->SetFrame(0);
+ }
+}
+
+void Character::OnTransitToAICtrl()
+{
+ mPrevSimTransform = GetSimState()->GetTransform();
+ mGroundPlaneSimState->GetCollisionObject()->SetCollisionEnabled( false );
+
+ // get "up" out of sim control
+ rmt::Vector up, right, forward;
+ //right = mpCharacter->mPrevSimTransform.Row(0);
+ up = mPrevSimTransform.Row(1);
+ //forward = mpCharacter->mPrevSimTransform.Row(2);
+
+ float dir = choreo::GetWorldAngle( up.x, up.z );
+ RelocateAndReset( mPrevSimTransform.Row(3), dir );
+}
+
+void Character::Display(void)
+{
+ if(IS_DRAW_LONG) return;
+ DSG_BEGIN_PROFILE(" Character::Display")
+ if(!mpCharacterRenderable->GetDrawable())
+ {
+ return;
+ }
+
+ if( mbNeedChoreoUpdate)
+ {
+ mpPuppet->UpdatePose( );
+ mbNeedChoreoUpdate = false;
+ }
+
+ if(IsMarge() &&
+ ((GetStateManager()->GetState() == CharacterAi::INCAR) ||
+ (GetStateManager()->GetState() == CharacterAi::GET_IN) ||
+ (GetStateManager()->GetState() == CharacterAi::GET_OUT)) &&
+ GetTargetVehicle() &&
+ GetTargetVehicle()->mHighRoof)
+ {
+ poser::Pose* p = mpPuppet->GetPose();
+ int numJoints = p->GetJointCount();
+ if( numJoints >= 36 )
+ {
+ mpPuppet->GetPose()->GetJoint(33)->SetObjectMatrix(mpCharacterRenderable->GetDrawable()->GetSkeleton()->GetJoint(33)->restPose);
+ mpPuppet->GetPose()->GetJoint(34)->SetObjectMatrix(mpCharacterRenderable->GetDrawable()->GetSkeleton()->GetJoint(34)->restPose);
+ mpPuppet->GetPose()->GetJoint(35)->SetObjectMatrix(mpCharacterRenderable->GetDrawable()->GetSkeleton()->GetJoint(35)->restPose);
+ }
+ }
+
+ mpPuppet->GetP3DPose()->SetSkeleton(mpCharacterRenderable->GetDrawable()->GetSkeleton());
+ mpPuppet->UpdateEnd( );
+
+ static float JumpRatio = 0.0f;
+ if( JumpRatio == 0.0f && ( GetJumpHeight() != 0.0f ) )
+ {
+ JumpRatio = 0.5f / GetJumpHeight();
+ }
+ if( !IsInCar() && IsSimpleShadow() )
+ {
+ rmt::Vector groundPos;
+ rmt::Vector groundNormal;
+ rmt::Vector characterFacing;
+ GetTerrainIntersect( groundPos, groundNormal );
+ GetFacing( characterFacing );
+ struct BlobShadowParams p( groundPos, groundNormal, characterFacing );
+ const rmt::Vector& pos = rPosition();
+ p.ShadowScale = 1.0f - ( ( pos.y - groundPos.y ) * JumpRatio );
+ p.ShadowAlpha = p.ShadowScale * ( mInteriorTerrain ? 0.5f : 1.0f );
+ p3d::pddi->SetZWrite(false);
+ mpCharacterRenderable->DisplayShadow( mpPuppet->GetP3DPose(), &p );
+ p3d::pddi->SetZWrite( true );
+ }
+
+ tPose* pose = mpPuppet->GetP3DPose();
+
+ mLean = pose->GetJoint(17)->worldMatrix.Row(3);
+ mLean.Sub(pose->GetJoint(0)->worldMatrix.Row(3));
+ mLean.NormalizeSafe();
+
+ rmt::Matrix backToTheOrigin;
+
+ backToTheOrigin.Identity();
+ backToTheOrigin.Row(3) = pose->GetJoint(0)->worldMatrix.Row(3);
+ backToTheOrigin.InvertOrtho();
+
+ rmt::Vector rootPos = pose->GetJoint(0)->worldMatrix.Row(3);
+
+ bool shouldScale = mScale != 1.0f;
+
+ for(int i = 0; i < pose->GetNumJoint(); i++)
+ {
+ rmt::Matrix tmp;
+ tmp.Mult(pose->GetJoint(i)->worldMatrix, backToTheOrigin);
+ if(shouldScale)
+ {
+ rmt::Matrix scale;
+ scale.Identity();
+ scale.FillScale(mScale, mScale, mScale);
+ pose->GetJoint(i)->worldMatrix.Mult(tmp, scale);
+ }
+ else
+ {
+ pose->GetJoint(i)->worldMatrix = tmp;
+ }
+ }
+
+ p3d::stack->Push();
+ p3d::stack->Translate(0.0f, mYAdjust, 0.0f);
+
+ p3d::stack->Push();
+ p3d::stack->Translate(rootPos);
+
+ mpCharacterRenderable->Display( mSphere.centre, pose );
+
+ p3d::stack->Pop();
+ p3d::stack->Pop();
+
+ if(mDoKickwave && mKickwave)
+ {
+ p3d::stack->Push();
+ p3d::stack->Translate(rootPos);
+ p3d::stack->Multiply(pose->GetJoint(0)->worldMatrix);
+ mKickwave->Display();
+ p3d::stack->Pop();
+ }
+#ifdef DRAW_CHARACTER_COLLISION
+#ifdef RAD_RELEASE
+ if ( CommandLineOptions::Get( CLO_DEBUGBV ) )
+#else
+ if ( !mpCharacterRenderable->GetDrawable() || CommandLineOptions::Get( CLO_DEBUGBV ) )
+#endif
+ {
+ // The sim library allocates shaders and stuff for this, so we should ensure they're on the temp heap
+ //
+ HeapMgr()->PushHeap (GMA_TEMP);
+
+ sim::CollisionVolume* pVolume = GetSimState( )->GetCollisionObject()->GetCollisionVolume( );
+ sim::DrawCollisionVolume( pVolume );
+ if ( mpStandingCollisionVolume )
+ sim::DrawCollisionVolume( mpStandingCollisionVolume );
+ int i;
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ sim::CollisionVolume* pVolume = mCollisionData[ i ].mpCollisionVolume;
+ if ( pVolume )
+ sim::DrawCollisionVolume(pVolume);
+ }
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+ }
+#endif // DRAW_CHARACTER_COLLISION
+ DSG_END_PROFILE(" Character::Display")
+}
+
+void Character::SetAmbient(const char* location, float radius)
+{
+ mAmbient = true;
+ mAmbientLocator = tEntity::MakeUID(location);
+
+ if((mRole != ROLE_REWARD) && (radius != 0.0f))
+ {
+ tRefCounted::Assign(mAmbientTrigger, new AmbientDialogueTrigger(this, radius));
+ EnableAmbientDialogue(true);
+ }
+}
+
+void Character::EnableAmbientDialogue(bool e)
+{
+ if(!mAmbientTrigger)
+ return;
+
+ if(e)
+ {
+ GetTriggerVolumeTracker()->AddTrigger(mAmbientTrigger);
+ }
+ else
+ {
+ GetTriggerVolumeTracker()->RemoveTrigger(mAmbientTrigger);
+ }
+}
+
+void Character::ResetAmbientPosition(void)
+{
+ Locator* l = p3d::find<Locator>(mAmbientLocator);
+ if(l)
+ {
+ rmt::Vector pos;
+ l->GetPosition(&pos);
+ RelocateAndReset(pos, 0.0f);
+ }
+ AddToWorldScene();
+
+ static_cast<NPCController*>(GetController())->ClearTempWaypoint();
+}
+
+/////////////////////////// NPC STUFF ////////////////////////////////
+
+void NPCharacter::UpdatePhysicsObjects( float timeins, int area )
+{
+ // NPCs shouldn't be submitting to physics stuff around it.
+ //
+ RestTest();
+}
+
+void NPCharacter::AssignCollisionAreaIndex( void )
+{
+ Character::AssignCollisionAreaIndex();
+}
+
+
+void NPCharacter::SubmitStatics( void )
+{
+ BEGIN_PROFILE( "Per NPCharacter Submit Statics" );
+
+ // DUSIT [Oct 29,2002]:
+ // HACK:
+ // When should we submit statics around ourselves?
+ // characters too far away from player shouldn't be submitting statics
+ // characters standing still shouldn't submit.
+ // characters not "off path" shouldn't submit.
+ // characters in street races 1 & 2 shouldn't submit (or they'll pop out side
+ // the race props onto the race track).
+
+ NPCController* npcController = (NPCController*) GetController();
+ if( npcController != NULL )
+ {
+ NPCController::State state = npcController->GetState();
+ bool npcStateNeedsToSubmit =
+ (GetStateManager()->GetState() == CharacterAi::INSIM) ||
+ (
+ (GetStateManager()->GetState() != CharacterAi::INSIM) &&
+ (
+ (npcController->mOffPath && state == NPCController::FOLLOWING_PATH) ||
+ (state == NPCController::STOPPED ) ||
+ (state == NPCController::DODGING) ||
+ (state == NPCController::PANICKING) ||
+ (state == NPCController::TALKING_WITH_PLAYER)
+ )
+ );
+
+ if( npcStateNeedsToSubmit )
+ {
+ // We COULD do this to prevent code duplication. But a virtual function call
+ // is quite expensive.
+ //Character::SubmitStatics();
+
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // TBJ [8/14/2002]
+ //
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if( collisionAreaIndex == WorldPhysicsManager::INVALID_COLLISION_AREA &&
+ GetRole() != ROLE_PEDESTRIAN )
+ {
+ if(!IsInCar())
+ {
+ AddToPhysics();
+ collisionAreaIndex = GetCollisionAreaIndex();
+ }
+ }
+
+ if( collisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+ static float sfCollisionRadius = 1.0f;
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState());
+
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState());
+ }
+ }
+ }
+ }
+ else
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ //
+ // Empty the collision area index of all submissions...
+ // unfortunately, this takes US and our GROUNDPLANE out of the list too
+ // so we gotta re-add and re-pair.
+ //
+ GetWorldPhysicsManager()->EmptyCollisionAreaIndex( collisionAreaIndex );
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ mpSimStateObj->GetCollisionObject(), mCollisionAreaIndex );
+ //AddToPhysics();
+ }
+ }
+ }
+
+
+ END_PROFILE( "Per NPCharacter Submit Statics" );
+}
+
+void NPCharacter::SubmitDynamics( void )
+{
+}
+
+void NPCharacter::OnTransitToAICtrl()
+{
+ // do here what we need to do at the very moment we transit
+ // back from simulation control to AI control
+ mPrevSimTransform = GetSimState()->GetTransform();
+
+}
+
+void NPCharacter::ApplyForce( const rmt::Vector& direction, float force )
+{
+ if(!IsInCar())
+ {
+ DynaPhysDSG::ApplyForce( direction, force );
+ }
+}
+
+
+void NPCharacter::ApplyKickForce( const rmt::Vector& direction, float force )
+{
+ if(!IsInCar())
+ {
+ DynaPhysDSG::ApplyForce( direction, force );
+
+ /*
+ rmt::Vector& rAngular = mpSimStateObj->GetAngularVelocity();
+ float deltaV = force / 100.0f;
+ rAngular += (direction * deltaV);
+ */
+
+ rmt::Vector linVel = mpSimStateObj->GetLinearVelocity();
+ mPastLinear.SetAverage( linVel.Magnitude() );
+
+ rmt::Vector angVel = mpSimStateObj->GetAngularVelocity();
+ mPastAngular.SetAverage( angVel.Magnitude() );
+
+ }
+}
+
+
diff --git a/game/code/worldsim/character/character.h b/game/code/worldsim/character/character.h
new file mode 100644
index 0000000..b87dcf5
--- /dev/null
+++ b/game/code/worldsim/character/character.h
@@ -0,0 +1,1428 @@
+#ifndef CHARACTER_H_
+#define CHARACTER_H_
+
+#include <radmath/radmath.hpp>
+#include <radtime.hpp>
+
+#include <render/DSG/InstDynaPhysDSG.h>
+#include <ai/state.h>
+#include <ai/statemanager.h>
+//#include <ai/sequencer/action.h>
+#include <memory/memorypool.h>
+#include <p3d/memory.hpp>
+
+#include <ai/sequencer/task.h>
+#include <p3d/p3dtypes.hpp>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+#include <radmath/radmath.hpp>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <worldsim/character/charactercontroller.h>
+
+#include <choreo/puppet.hpp>
+#include <simcollision/proximitydetection.hpp>
+
+class tDrawablePose;
+class tCamera;
+class CharacterTarget;
+class ActionController;
+
+namespace ActionButton
+{
+ class ButtonHandler;
+ class AttachProp;
+}
+class Vehicle;
+
+namespace CharacterAi
+{
+ class StateManager;
+};
+
+namespace sim
+{
+ class CollisionVolume;
+};
+
+namespace poser
+{
+ class Joint;
+ class Transform;
+};
+
+struct CharacterTune
+{
+ static float sfLocoRotateRate;
+ static float sfLocoAcceleration;
+ static float sfLocoDecceleration;
+ static bool bLocoTest;
+ static float sfAirRotateRate;
+ static float sfAirAccelScale;
+ static float sfAirGravity;
+ static float sfStompGravityScale;
+ static float sfDashBurstMax;
+ static float sfDashAcceleration;
+ static float sfDashDeceleration;
+ static float sfJumpHeight;
+ static float sfDoubleJumpHeight;
+ static float sfDoubleJumpAllowUp;
+ static float sfDoubleJumpAllowDown;
+ static float sfHighJumpHeight;
+ static float sfMaxSpeed;
+
+ static rmt::Vector sGetInPosition;
+ static float sGetInHeightThreshold;
+ static float sGetInOpenDelay;
+ static float sGetInOpenSpeed;
+ static float sGetInCloseDelay;
+ static float sGetInCloseSpeed;
+ static float sGetOutOpenDelay;
+ static float sGetOutOpenSpeed;
+ static float sGetOutCloseDelay;
+ static float sGetOutCloseSpeed;
+
+ static float sfTurboRotateRate;
+
+ static float sfGetInOutOfCarAnimSpeed;
+ static float sfKickingForce;
+ static float sfSlamForce;
+ static float sfShockTime;
+};
+class tPose;
+class tSkeleton;
+class InstDynaPhysDSG;
+class WorldScene;
+class AmbientDialogueTrigger;
+class CharacterRenderable;
+class JumpAction;
+class WalkerLocomotionAction;
+
+class Character
+:
+public DynaPhysDSG
+{
+public:
+ enum Role {
+ ROLE_UNKNOWN,
+ ROLE_DRIVER,
+ ROLE_REWARD,
+ ROLE_ACTIVE_BONUS,
+ ROLE_COMPLETED_BONUS,
+ ROLE_PEDESTRIAN,
+ ROLE_MISSION
+ };
+
+public: // MEMBERS
+ Character( void );
+ virtual ~Character( void );
+ void Init( void );
+
+ virtual void OnTransitToAICtrl();
+ virtual int GetAIRef() { return PhysicsAIRef::PlayerCharacter;}
+
+ float GetFacingDir( void ) const;
+ void SetFacingDir( float fFacingDir );
+
+ float GetDesiredDir( void ) const;
+ void GetDesiredFacing( rmt::Vector& facing ) const;
+ //ICameraTarget.
+ //
+ void GetFacing( rmt::Vector& facing ) const;
+ void SetFacing( rmt::Vector& facing );
+
+ // Call this to place character at a particular place in the world
+ // and perhaps reset its states.
+ void RelocateAndReset( const rmt::Vector& position, float facing, bool resetMe=true, bool snapToGround = true );
+
+ void SetPosition( const rmt::Vector& position );
+ void GetPosition( rmt::Vector& position ) const;
+
+ void SetVelocity( rmt::Vector& velocity );
+ void GetVelocity( rmt::Vector& velocity ) const;
+ const rmt::Vector& GetLocoVelocity() const;
+
+ float GetSpeed( void ) const;
+ void SetSpeed( float fSpeed );
+ void ResetSpeed( void );
+ float GetDesiredSpeed( void ) const;
+
+
+ void StickReleased( void );
+ void StickPressed( void );
+
+ void SetPuppet( choreo::Puppet* pPuppet );
+ choreo::Puppet* GetPuppet( void ) const;
+
+ void SetDrawable( CharacterRenderable* pDrawablePose );
+
+ virtual void PreSimUpdate(float timeins);
+ virtual void UpdateRoot( float timeins );
+ virtual void ResolveCollisions(void);
+ virtual void PostSimUpdate(float timeins);
+
+ // Just use this to set a flag.
+ virtual void Update( float timeins ) {};
+
+ void UpdateSimState( float timeins );
+ void UpdateBBox( rmt::Box3D& oldBox );
+
+ bool IsInCar( void ) const;
+ void SetInCar( bool bInCar );
+ bool IsInCarOrGettingInOut( void );
+
+ CharacterTarget* GetTarget( void ) const;
+
+ void SetController( CharacterController* pController );
+ CharacterController* GetController( void ) const;
+
+ void SetDesiredDir( float fDesiredDir );
+ void SetDesiredSpeed( float fDesiredSpeed );
+
+ ActionButton::ButtonHandler* GetActionButtonHandler( void ) const;
+ void AddActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler );
+ void RemoveActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler );
+ void ClearAllActionButtonHandlers();
+
+ void SetTargetVehicle( Vehicle* pVehicle );
+ Vehicle* GetTargetVehicle( ) const;
+ float GetMaxSpeed( void ) const;
+
+ CharacterAi::StateManager* GetStateManager( void ) const;
+
+ ActionController* GetActionController( void ) const;
+
+ static float GetJumpHeight( void );
+ void GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const;
+ void GetTerrainType( eTerrainType& TerrainType, bool& Interior ) const;
+
+ virtual void SubmitStatics( void );
+ virtual void SubmitAnimCollisions( void );
+ virtual void SubmitDynamics( void );
+
+ // Implements CollisionEntityDSG
+ //
+ virtual void Display(void);
+ virtual rmt::Vector* pPosition() ;
+ virtual const rmt::Vector& rPosition() ;
+ virtual void GetPosition( rmt::Vector* ipPosn ) ;
+
+ void DisplayShadow();
+ void DisplaySimpleShadow( void );
+ int CastsShadow();
+ bool IsSimpleShadow( void );
+ void SetSimpleShadow( bool IsSimpleShadow );
+
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+
+ bool IsInCollision( void ) const;
+ void ResetCollisions( void );
+ bool CanStandOnCollisionVolume( void ) const;
+ bool CanStaggerCollision( void ) const;
+
+ enum eCollisionType
+ {
+ NoCollision = 0,
+ HitWall = 1 << 0,
+ HitHead = 1 << 1,
+ DangerousMultiple = 1 << 3
+ };
+
+ eCollisionType SolveCollisionWithStatic( const rmt::Vector& desiredPos, rmt::Vector& outPos );
+
+ bool IsStanding( void ) const;
+ void SetGroundPoint( const rmt::Vector& groundPoint );
+
+ WalkerLocomotionAction* GetWalkerLocomotionAction( void ) const;
+ JumpAction* GetJumpLocomotionAction( void ) const;
+
+ void UpdateParentTransform( float timeins );
+ void GetRootTransform( poser::Transform& out ) const;
+ void SetStandingJoint( const poser::Joint* pJoint );
+ const rmt::Matrix& GetParentTransform( void ) const;
+ const rmt::Matrix& GetInverseParentTransform( void ) const;
+ float GetGroundVerticalVelocity( void ) const;
+
+ void AddToWorldScene( void );
+ void RemoveFromWorldScene( void );
+ void MoveInWorldScene( void );
+ virtual void InitGroundPlane( void );
+ virtual void AddToPhysics( void );
+ virtual void RemoveFromPhysics( void );
+
+ int GetCollisionAreaIndex( void ) const;
+
+ rmt::Vector WorldToLocal( const rmt::Vector& world ) const;
+ rmt::Vector LocalToWorld( const rmt::Vector& local ) const;
+ void UpdateTransformToLoco( void );
+ void UpdateTransformToInCar( void );
+
+ bool IsJumping( void ) const;
+ void SetJumping( bool bIsJumping );
+
+ void SetTurbo( bool bTurbo );
+ bool IsTurbo( void ) const;
+
+ bool IsVisible( void ) const { return mVisible;}
+
+ void SetSolveCollisions( bool bCollide )
+ {
+ mbSolveCollisions = bCollide;
+ }
+
+ bool GetSolveCollisions( void ) const
+ {
+ return mbSolveCollisions;
+ }
+
+ struct Prop
+ {
+ Prop( void );
+ ~Prop( void );
+ InstDynaPhysDSG* mpProp;
+ tPose* mpPose;
+ static tSkeleton* spSkeleton;
+ static int sSkelRefs;
+ static int mPropCount;
+ };
+ void TouchProp( InstDynaPhysDSG* pProp );
+ void AttachProp( InstDynaPhysDSG* pProp );
+ void RemoveProp( InstDynaPhysDSG* pProp );
+ void UpdateProps( float timeins );
+
+ virtual void UpdatePhysicsObjects( float timeins, int area );
+ bool CanPlayAnimation( const tName& name ) const;
+ #ifdef RAD_DEBUG
+ void PrintAnimations() const;
+ #else
+ void PrintAnimations() const{};
+ #endif
+
+ void SetSwatch( int swatchNum );
+ bool PosInFrustrumOfPlayer( const rmt::Vector& pos, int playerID );
+
+ void SetYAdjust( float yOffset );
+ float GetYAdjust();
+
+ void Kick();
+ void Slam();
+
+ void SetFadeAlpha( int fadeAlpha );
+
+ bool IsBusy(void) {return mbBusy;}
+ void SetBusy(bool b) {mbBusy = b;}
+
+ bool IsSimpleLoco(void) {return mbSimpleLoco;}
+ void SetSimpleLoco(bool b) {mbSimpleLoco = b;}
+
+ void SetShadowColour( tColour shadowColour );
+ tColour GetShadowColour();
+
+ void Shock( float timeInSeconds );
+
+ void DoKickwave(void);
+
+ // Don't apply any velocity change to the player character
+ // There appear to be bugs when this happens ( character getting stuck in the ground )
+ virtual void ApplyForce( const rmt::Vector& direction, float force ){}
+
+ void SetAmbient(const char* location, float radius);
+ bool IsAmbient(void) { return mAmbient;}
+ void EnableAmbientDialogue(bool);
+ void ResetAmbientPosition(void);
+
+ bool IsNPC();
+
+ enum { MAX_ACTION_BUTTON_HANDLERS = 5 };
+
+ void DoGroundIntersect(bool b) { mbDoGroundIntersect = b;}
+
+ bool GetRockinIdle(void) { return mAllowRockin; }
+ void SetRockinIdle(bool b) { mAllowRockin = b; }
+
+ bool HasBeenHit() { return mHasBeenHit; }
+ void SetHasBeenHit( bool tf ) { mHasBeenHit = tf; }
+
+ void SetRole(Role r) { mRole = r;}
+ Role GetRole(void) { return mRole;}
+
+ void SetScale(float f) { mScale = f;}
+
+ bool CollidedThisFrame(void) { return mCollidedThisFrame; }
+
+ bool IsInSubstep() { return mIsInSubstep; }
+ void SetInSubstep( bool in ) { mIsInSubstep = in; }
+
+ bool TestInAnyonesFrustrum();
+
+ const rmt::Vector& GetLean(void) { return mLean; }
+
+ bool IsLisa(void) { return mIsLisa; }
+ bool IsMarge(void) { return mIsMarge; }
+ void SetIsLisa(bool b) { mIsLisa = b; }
+ void SetIsMarge(bool b) { mIsMarge = b; }
+
+ void SnapToGround(void);
+
+ unsigned GetActiveFrame(void) { return mIntersectFrame; }
+
+ void SetManaged(bool b) { mManaged = b;}
+ bool IsManaged(void) { return mManaged; }
+
+public: // MEMBERS
+ bool mbCollidedWithVehicle;
+ bool mbInAnyonesFrustrum; //if we have array of MAX_PLAYERS, we can distinguish betw each player's frustrum
+ bool mbSurfing;
+ bool mbAllowUnload;
+ bool mbIsPlayingIdleAnim; // shuffling feet, scratching bum, etc.
+#ifdef RAD_WIN32
+ int mPCCamFacing; // 0 = cam direction, 1 = cam's right, 2 = facing cam, 3 = cam's left
+#endif
+
+
+ rmt::Matrix mPrevSimTransform;
+
+protected: // METHODS
+ void TestInFrustrumOfPlayer( int playerID );
+
+ void UpdateGroundHeight(void);
+
+ void SetParentTransform( const rmt::Matrix& mat, float timeins = 0.0f );
+ sim::CollisionVolume* FindStandingVolume( const rmt::Vector& inPos, sim::CollisionVolume* inVolume, rmt::Vector& outNormal, float& outDist );
+ bool GetCollisionHeight( const rmt::Vector& prevPosition, const rmt::Vector& position, rmt::Vector& outPosition, rmt::Vector& collisionNormal ) ;
+ bool CanStandOnCollisionNormal( const rmt::Vector& normal ) const;
+ bool CanStaggerCollisionNormal( const rmt::Vector& normal ) const;
+
+ void UpdateController( rmt::Vector& direction, float timeins );
+ virtual void UpdateDesiredDirAndSpeed( const rmt::Vector& dir );
+ virtual void UpdateFootPlant( void );
+ void UpdatePuppet( float timeins );
+ void UpdateGroundPlane( float timeins );
+ // Is the character allowed to move?
+ bool IsMovementLocked() { return m_IsBeingShocked; }
+
+
+ virtual void AssignCollisionAreaIndex( void );
+ virtual float GetInputScale( void )
+ {
+ // Number arrived at via experimentation.
+ //
+ return 0.69f;
+ }
+
+protected: // MEMBERS
+ bool mIsNPC;
+
+ // ground plane stuff for player character only
+ sim::ManualSimState* mGroundPlaneSimState;
+ sim::WallVolume* mGroundPlaneWallVolume;
+
+ int mCollisionAreaIndex;
+
+ static sim::TArray<sim::RayIntersectionInfo> msIntersectInfo;
+
+ radTime64 mLastInteriorLoadCheck;
+
+private: // METHODS
+ virtual void OnUpdateRoot( float timeins );
+ virtual void OnPostSimUpdate(float timeins);
+ void UpdateShock( float timeins );
+ ActionButton::ButtonHandler* TestPriority( ActionButton::ButtonHandler* bA, ActionButton::ButtonHandler* bB );
+
+
+private: // MEMBERS
+ // The abstract controller object.
+ //
+ CharacterController* mpController;
+
+ // A pointer to the renderable object.
+ //
+ CharacterRenderable* mpCharacterRenderable;
+
+ // A pointer to the choreo::Puppet
+ //
+ choreo::Puppet* mpPuppet;
+
+ // The facing angle, in radians. 0 = ( 0, 0, -1 )
+ //
+ float mfFacingDir;
+
+ // The desired facing angle. The character will attempt to converge on this angle.
+ //
+ float mfDesiredDir;
+
+ // Scalar velocity along the facing angle.
+ //
+ float mfSpeed;
+
+ rmt::Vector mVelocity;
+
+ // The desired speed. The character will accel or decel towards this speed.
+ //
+ float mfDesiredSpeed;
+
+ // Is in car.
+ //
+ bool mbInCar;
+
+ // Whether the characters feet were planted last update
+ //
+ std::vector< bool, s2alloc<bool> > mbWasFootPlanted;
+
+ // For camera tracking.
+ //
+ CharacterTarget* mpCharacterTarget;
+
+ // For ai and animation.
+ //
+ ActionController* mpActionController;
+
+
+ //To control action button priority, we do the following.
+ ActionButton::ButtonHandler* mpActionButtonHandlers[ MAX_ACTION_BUTTON_HANDLERS ];
+ ActionButton::ButtonHandler* mpCurrentActionButtonHandler;
+
+ Vehicle* mpTargetVehicle;
+
+ static float sfMaxSpeed;
+
+ // Terrain positioning.
+ //
+ float mGroundY;
+ rmt::Vector mGroundNormal;
+ eTerrainType mTerrainType; // What type of terrain is the character on.
+ bool mInteriorTerrain; // The terrain is covered/inside. Such as a tunnel or building interior.
+
+ rmt::Vector mRealGroundPos; // ground, excluding statics we may be on.
+ rmt::Vector mRealGroundNormal; // ground, excluding statics we may be on.
+
+ CharacterAi::StateManager* mpStateManager;
+
+ float mfRadius;
+
+// Hack.
+ friend class CharacterRenderable;
+ // Collision detection and response.
+ //
+ bool mbCollided;
+ int mCurrentCollision;
+ struct CollisionData
+ {
+ CollisionData( void )
+ :
+ mCollisionDistance( 0.0f ),
+ mpCollisionVolume( 0 )
+ {
+ }
+ static const int MAX_COLLISIONS = 8;
+ rmt::Vector mCollisionPosition;
+ rmt::Vector mCollisionNormal;
+ float mCollisionDistance;
+ sim::CollisionVolume* mpCollisionVolume;
+ };
+ CollisionData mCollisionData[ CollisionData::MAX_COLLISIONS ];
+
+ bool mbIsStanding;
+ WalkerLocomotionAction* mpWalkerLocomotion;
+ JumpAction* mpJumpLocomotion;
+
+
+ sim::CollisionVolume* mpStandingCollisionVolume;
+ const poser::Joint* mpStandingJoint;
+ rmt::Matrix mParentTransform;
+ rmt::Matrix mInvParentTransform;
+ float mfGroundVerticalVelocity;
+ float mfGroundVerticalPosition;
+
+ bool mbTurbo;
+ bool mbIsJump;
+ bool mbSolveCollisions;
+
+ static const int MAX_PROPS = 1;
+
+ Prop mPropList[ MAX_PROPS ];
+
+ ActionButton::AttachProp* mpPropHandler;
+ int mPropJoint;
+
+ bool mVisible;
+
+ WorldScene* mpWorldScene;
+ bool m_IsSimpleShadow;
+
+ // when we apply choreo puppet skeleton over top of differently
+ // scaled character model, this value is to adjust the difference between
+ // the character model root and the npd skeleton root. Can be +ve or -ve
+ float mYAdjust;
+
+ bool mbBusy;
+
+ bool mbSimpleLoco;
+ bool mbNeedChoreoUpdate;
+
+ tColour mShadowColour;
+
+ float m_TimeLeftToShock;
+ bool m_IsBeingShocked;
+
+ bool mDoKickwave;
+ tDrawable* mKickwave;
+ tFrameController* mKickwaveController;
+
+ bool mAmbient;
+ tUID mAmbientLocator;
+ AmbientDialogueTrigger* mAmbientTrigger;
+
+ rmt::Vector mLastFramePos;
+
+ bool mbDoGroundIntersect;
+
+ unsigned mIntersectFrame;
+
+ bool mAllowRockin;
+
+ bool mHasBeenHit;
+
+ bool mbSnapToGround;
+
+ float mSecondsSinceActionControllerUpdate;
+
+ bool mTooFarToUpdate;
+
+ float mSecondsSinceOnPostSimUpdate;
+
+ Role mRole;
+
+ float mScale;
+
+ bool mCollidedThisFrame;
+
+ bool mIsInSubstep;
+
+ rmt::Vector mLean;
+
+ bool mIsLisa;
+ bool mIsMarge;
+
+ rmt::Vector mLastGoodPosOverStatic;
+
+ rmt::Vector lameAssPosition;
+
+ bool mManaged;
+};
+
+/*
+==============================================================================
+Character::GetFacing
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& facingVector )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetFacing( rmt::Vector& facingVector ) const
+{
+ if ( mpPuppet )
+ {
+ // Transform from object to world space.
+ //
+ facingVector = mpPuppet->GetFacingVector( );
+ // Transform from object to world space.
+ //
+ //mInvParentTransform.RotateVector( facingVector, &facingVector );
+ return;
+ }
+ facingVector.Set( 0.0f, 0.0f, -1.0f );
+}
+/*
+==============================================================================
+Character::GetFacingDir
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetFacingDir( void ) const
+{
+ rmt::Vector facing;
+ GetFacing( facing );
+ return choreo::GetWorldAngle( facing.x, facing.z );
+}
+
+/*
+==============================================================================
+Character::SetFacing
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& facingVector )
+
+Return: inline
+
+=============================================================================
+*/
+inline void Character::SetFacing( rmt::Vector& facingVector )
+{
+ if ( mpPuppet )
+ {
+ mpPuppet->SetFacingVector( facingVector );
+ }
+}
+/*
+==============================================================================
+Character::SetFacingDir
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDir )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetFacingDir( float fDir )
+{
+ rmt::Vector facing;
+ facing = choreo::DEFAULT_FACING_VECTOR;
+ choreo::RotateYVector( fDir, facing );
+ SetFacing( facing );
+}
+/*
+==============================================================================
+Character::GetDesiredFacing
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& facingVector )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetDesiredFacing( rmt::Vector& facingVector ) const
+{
+ facingVector = choreo::DEFAULT_FACING_VECTOR;
+ choreo::RotateYVector( mfDesiredDir, facingVector );
+ // Transform from object to world space.
+ //
+ //mInvParentTransform.RotateVector( facingVector, &facingVector );
+}
+/*
+==============================================================================
+Character::GetDesiredDir
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetDesiredDir( void ) const
+{
+ rmt::Vector facing;
+ GetDesiredFacing( facing );
+ return choreo::GetWorldAngle( facing.x, facing.z );
+}
+
+/*
+==============================================================================
+Character::SetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& position )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetPosition( const rmt::Vector& position )
+{
+ if ( mpPuppet )
+ {
+ // Transform from world to object space.
+ //
+ rmt::Vector transformedPos = position;
+ transformedPos.Transform( mInvParentTransform );
+
+ mpPuppet->SetPosition( transformedPos );
+ }
+
+ sim::SimState* simState = mpSimStateObj; //GetSimState();
+ if( simState != NULL && simState->GetControl() == sim::simAICtrl )
+ {
+ UpdateSimState( 0.0f );
+ }
+}
+
+/*
+==============================================================================
+Character::GetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& position )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetPosition( rmt::Vector& position ) const
+{
+ if ( mpPuppet )
+ {
+ position = mpPuppet->GetPosition( );
+ position.Transform( mParentTransform );
+ }
+ else
+ {
+ position.Set(0.0f, 0.0f, 0.0f);
+ }
+}
+/*
+==============================================================================
+Character::GetVelocity
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& velocity )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetVelocity( rmt::Vector& velocity ) const
+{
+ velocity = mVelocity;
+}
+/*
+==============================================================================
+Character::GetLocoVelocity
+==============================================================================
+Description: How fast is the character running/walking (locomoting)
+
+Parameters: ()
+
+Return: inline
+
+=============================================================================
+*/
+inline const rmt::Vector& Character::GetLocoVelocity() const
+{
+ return mpPuppet->GetVelocity( );
+}
+/*
+==============================================================================
+Character::GetSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetSpeed( void ) const
+{
+ return mfSpeed;
+}
+/*
+==============================================================================
+Character::SetSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( float fSpeed )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetSpeed( float fSpeed )
+{
+ rmt::Vector facing;
+ GetFacing( facing );
+ facing.Scale( fSpeed );
+ mParentTransform.RotateVector( facing, &facing );
+ mpSimStateObj->VelocityState( ).mLinear = facing;
+}
+/*
+==============================================================================
+Character::GetDesiredSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetDesiredSpeed( void ) const
+{
+ return mfDesiredSpeed;
+}
+/*
+
+/*
+==============================================================================
+Character::SetDesiredDir
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDesiredDir )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetDesiredDir( float fDesiredDir )
+{
+ mfDesiredDir = fDesiredDir;
+}
+/*
+==============================================================================
+Character::SetDesiredSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDesiredSpeed )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetDesiredSpeed( float fDesiredSpeed )
+{
+ if ( fDesiredSpeed > GetMaxSpeed( ) )
+ {
+ fDesiredSpeed = GetMaxSpeed( );
+ }
+ mfDesiredSpeed = fDesiredSpeed;
+}
+
+/*
+==============================================================================
+Character::GetPuppet
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: choreo
+
+=============================================================================
+*/
+inline choreo::Puppet* Character::GetPuppet( void ) const
+{
+ return mpPuppet;
+}
+
+/*
+==============================================================================
+Character::IsInCar
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsInCar( void ) const
+{
+ return mbInCar;
+}
+
+
+/*
+==============================================================================
+Character::SetController
+==============================================================================
+Description: Comment
+
+Parameters: ( CharacterController* pController )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetController( CharacterController* pController )
+{
+ tRefCounted::Assign( mpController, pController );
+}
+/*
+==============================================================================
+Character::GetController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterController
+
+=============================================================================
+*/
+inline CharacterController* Character::GetController( void ) const
+{
+ return mpController;
+}
+
+/*
+==============================================================================
+Character::GetTarget
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterTarget
+
+=============================================================================
+*/
+inline CharacterTarget* Character::GetTarget( void ) const
+{
+ rAssert( mpCharacterTarget );
+ return mpCharacterTarget;
+}
+
+/*
+==============================================================================
+Character::SetTargetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( Vehicle* pVehicle )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetTargetVehicle( Vehicle* pVehicle )
+{
+ mpTargetVehicle = pVehicle;
+}
+/*
+==============================================================================
+Character::GetTargetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( )
+
+Return: Vehicle
+
+=============================================================================
+*/
+inline Vehicle* Character::GetTargetVehicle( ) const
+{
+ return mpTargetVehicle;
+}
+/*
+==============================================================================
+Character::GetStateManager
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterAi
+
+=============================================================================
+*/
+inline CharacterAi::StateManager* Character::GetStateManager( void ) const
+{
+ return mpStateManager;
+}
+
+/*
+==============================================================================
+Character::GetActionController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ActionController
+
+=============================================================================
+*/
+inline ActionController* Character::GetActionController( void ) const
+{
+ return mpActionController;
+}
+/*
+==============================================================================
+Character::GetJumpHeight
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetJumpHeight( void )
+{
+ return CharacterTune::sfJumpHeight;
+}
+/*
+==============================================================================
+Character::GetRootTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( poser::Transform& out )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetRootTransform( poser::Transform& out ) const
+{
+ if ( mpPuppet )
+ {
+ out = mpPuppet->GetRootTransform( );
+ poser::Transform parentTransform;
+ parentTransform.SetMatrix( mParentTransform );
+ out.Mult( parentTransform );
+ }
+ else
+ {
+ out.Identity( );
+ }
+}
+/*
+==============================================================================
+Character::WorldToLocal
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& world )
+
+Return: rmt
+
+=============================================================================
+*/
+inline rmt::Vector Character::WorldToLocal( const rmt::Vector& world ) const
+{
+ rmt::Vector local = world;
+ local.Transform( mInvParentTransform );
+ return local;
+}
+/*
+==============================================================================
+Character::LocalToWorld
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& local )
+
+Return: rmt
+
+=============================================================================
+*/
+inline rmt::Vector Character::LocalToWorld( const rmt::Vector& local ) const
+{
+ rmt::Vector world = local;
+ world.Transform( mParentTransform );
+ return world;
+}
+/*
+==============================================================================
+Character::GetCollisionAreaIndex
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: int
+
+=============================================================================
+*/
+inline int Character::GetCollisionAreaIndex( void ) const
+{
+ return mCollisionAreaIndex;
+}
+/*
+==============================================================================
+Character::IsInCollision
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsInCollision( void ) const
+{
+ return mbCollided;
+}
+/*
+==============================================================================
+Character::IsStanding
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsStanding( void ) const
+{
+ return mbIsStanding;
+}
+/*
+==============================================================================
+Character::GetWalkerLocomotionAction
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: WalkerLocomotionAction
+
+=============================================================================
+*/
+inline WalkerLocomotionAction* Character::GetWalkerLocomotionAction( void ) const
+{
+ return mpWalkerLocomotion;
+}
+/*
+==============================================================================
+Character::GetJumpLocomotionAction
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: JumpAction
+
+=============================================================================
+*/
+inline JumpAction* Character::GetJumpLocomotionAction( void ) const
+{
+ return mpJumpLocomotion;
+}
+/*
+==============================================================================
+Character::GetParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+inline const rmt::Matrix& Character::GetParentTransform( void ) const
+{
+ return mParentTransform;
+}
+/*
+==============================================================================
+Character::GetInverseParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+inline const rmt::Matrix& Character::GetInverseParentTransform( void ) const
+{
+ return mInvParentTransform;
+}
+/*
+==============================================================================
+Character::GetGroundVerticalVelocity
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetGroundVerticalVelocity( void ) const
+{
+ return mfGroundVerticalVelocity;
+}
+
+/*
+==============================================================================
+Character::IsJumping
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsJumping( void ) const
+{
+ return mbIsJump;
+}
+/*
+==============================================================================
+Character::SetJumping
+==============================================================================
+Description: Comment
+
+Parameters: ( bool bIsJumping )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetJumping( bool bIsJumping )
+{
+ mbIsJump = bIsJumping;
+}
+
+/*
+==============================================================================
+Character::SetTurbo
+==============================================================================
+Description: Comment
+
+Parameters: ( bool bTurbo )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetTurbo( bool bTurbo )
+{
+ if( bTurbo != mbTurbo )
+ {
+ if( bTurbo )
+ {
+ GetEventManager()->TriggerEvent( EVENT_TURBO_START, this );
+ }
+ mbTurbo = bTurbo;
+ }
+}
+/*
+==============================================================================
+Character::IsTurbo
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsTurbo( void ) const
+{
+ return mbTurbo;
+}
+
+/*
+==============================================================================
+Character::IsSimpleShadow
+==============================================================================
+Description: Is the shadow a blobby shadow or a volume shadow?
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsSimpleShadow( void )
+{
+ return m_IsSimpleShadow;
+}
+/*
+==============================================================================
+Character::SetSimpleShadow
+==============================================================================
+Description: Set if the character's shadow is a blobby, in apposed to the
+ volumetric.
+
+Parameters: ( bool )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetSimpleShadow( bool IsSimpleShadow )
+{
+ m_IsSimpleShadow = IsSimpleShadow;
+}
+
+inline bool Character::IsNPC()
+{
+ return mIsNPC;
+}
+
+class NPCharacter
+:
+public Character
+{
+public:
+ NPCharacter( void );
+ ~NPCharacter( void );
+
+ virtual int GetAIRef() { return PhysicsAIRef::NPCharacter;}
+
+ virtual void AddToPhysics( void );
+ virtual void RemoveFromPhysics( void );
+
+ virtual void SubmitStatics( void );
+ virtual void SubmitAnimCollisions( void ) {};
+ virtual void SubmitDynamics( void );
+
+ virtual void UpdatePhysicsObjects( float timeins, int area );
+ virtual void OnTransitToAICtrl();
+
+ virtual void ApplyKickForce( const rmt::Vector& direction, float force );
+ virtual void ApplyForce( const rmt::Vector& direction, float force );
+protected:
+
+ virtual void AssignCollisionAreaIndex( void );
+ virtual float GetInputScale( void )
+ {
+ // Scale 1.0f does nothing.
+ return 1.0f;
+ }
+private:
+ virtual void OnUpdateRoot( float timeins );
+ virtual void OnPostSimUpdate(float timeins);
+ virtual void UpdateFootPlant( void )
+ {
+ }
+ int mMappableHandle;
+};
+
+
+
+#endif // CHARACTER_H_
+
diff --git a/game/code/worldsim/character/charactercontroller.cpp b/game/code/worldsim/character/charactercontroller.cpp
new file mode 100644
index 0000000..0b41b77
--- /dev/null
+++ b/game/code/worldsim/character/charactercontroller.cpp
@@ -0,0 +1,1668 @@
+#include <ai/sequencer/actioncontroller.h>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/character/controllereventhandler.h>
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/character.h>
+#include <input/inputmanager.h>
+#include <mission/gameplaymanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <p3d/camera.hpp>
+#include <p3d/pointcamera.hpp>
+
+#include <events/eventmanager.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+
+#include <roads/geometry.h>
+
+#include <worldsim/character/charactermanager.h>
+
+#include <ai/sequencer/action.h>
+#include <presentation/mouthflapper.h>
+#include <interiors/interiormanager.h>
+//#include <presentation/presentation.h>
+//#include <presentation/presentationanimator.h>
+
+/*
+==============================================================================
+CharacterController::CharacterController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterController
+
+=============================================================================
+*/
+CharacterController::CharacterController( void )
+:
+mpCharacter( 0 ),
+mIntention( NONE ),
+mPreserveIntention( NONE ),
+mActive( true )
+{
+}
+/*
+==============================================================================
+CharacterController::~CharacterControlller
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterController
+
+=============================================================================
+*/
+CharacterController::~CharacterController( void )
+{
+ if ( mpCharacter )
+ {
+ mpCharacter = 0;
+ }
+}
+/*
+==============================================================================
+CharacterController::GetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: Character
+
+=============================================================================
+*/
+Character* CharacterController::GetCharacter( void ) const
+{
+ return mpCharacter;
+}
+/*
+==============================================================================
+CharacterController::SetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterController::SetCharacter( Character* pCharacter )
+{
+ mpCharacter = pCharacter;
+}
+/*
+==============================================================================
+PhysicalController::PhysicalController
+==============================================================================
+Description: Constructor
+
+Parameters: ( void )
+
+Return: n/a
+
+=============================================================================
+*/
+PhysicalController::PhysicalController( void )
+:
+mpCharacterMappable( 0 )
+{
+}
+/*
+==============================================================================
+PhysicalController::~PhysicalController
+==============================================================================
+Description: Destructor
+
+Parameters: ( void )
+
+Return: n/a
+
+=============================================================================
+*/
+PhysicalController::~PhysicalController( void )
+{
+ // This should release and set mpCharacterMappable = 0;
+ //
+ mpCharacterMappable->Release( );
+ mpCharacterMappable = 0;
+}
+/*
+==============================================================================
+PhysicalController::SetCharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( CharacterMappable* pMappable )
+
+Return: void
+
+=============================================================================
+*/
+void PhysicalController::SetCharacterMappable( CharacterMappable* pMappable )
+{
+ tRefCounted::Assign( mpCharacterMappable, pMappable );
+}
+/*
+==============================================================================
+PhysicalController::GetDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& outDirection )
+
+Return: void
+
+=============================================================================
+*/
+void PhysicalController::GetDirection( rmt::Vector& outDirection )
+{
+ if(mActive)
+ {
+ mpCharacterMappable->GetDirection( outDirection );
+ }
+ else
+ {
+ outDirection.Set(0.0f, 0.0f, 0.0f);
+ }
+}
+/*
+==============================================================================
+PhysicalController::GetValue
+==============================================================================
+Description: Comment
+
+Parameters: ( int buttonId )
+
+Return: float
+
+=============================================================================
+*/
+float PhysicalController::GetValue( int buttonId ) const
+{
+ if(mActive)
+ {
+ return GetCharacterMappable( )->GetValue( buttonId );
+ }
+ else
+ {
+ return 0.0f;
+ }
+}
+/*
+==============================================================================
+PhysicalController::IsButtonDown
+==============================================================================
+Description: Comment
+
+Parameters: ( int buttonId )
+
+Return: bool
+
+=============================================================================
+*/
+bool PhysicalController::IsButtonDown( int buttonId ) const
+{
+ if(mActive)
+ {
+ return GetCharacterMappable( )->IsButtonDown( buttonId ) || GetIntention( ) == (eIntention)buttonId;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+int PhysicalController::TimeSinceChange( int buttonId ) const
+{
+ return IsButtonDown(buttonId) ? 0 : GetCharacterMappable( )->GetButton( buttonId )->TimeSinceChange();
+};
+
+/*
+==============================================================================
+PhysicalController::GetCharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterMappable
+
+=============================================================================
+*/
+CharacterMappable* PhysicalController::GetCharacterMappable( void ) const
+{
+ return mpCharacterMappable;
+}
+/*
+==============================================================================
+CameraRelativeCharacterController::CameraRelativeCharacterController
+==============================================================================
+Description: Constructor
+
+Parameters: ( int index, tCamera* pCamera )
+
+Return: n/a
+
+=============================================================================
+*/
+CameraRelativeCharacterController::CameraRelativeCharacterController( void )
+:
+mpCamera( 0 ),
+mbCameraChange( false )
+{
+ mpEventHandler = new CameraRelativeCharacterControllerEventHandler( this );
+ GetEventManager()->AddListener( mpEventHandler, EVENT_CAMERA_CHANGE );
+}
+void CameraRelativeCharacterController::Create( Character* pCharacter,
+ CharacterMappable* pCharacterMappable )
+{
+MEMTRACK_PUSH_GROUP( "Character Controller" );
+ SetCharacter( pCharacter );
+ SetCharacterMappable( pCharacterMappable );
+ pCharacterMappable->SetCharacterController( this );
+MEMTRACK_POP_GROUP( "Character Controller" );
+}
+/*
+==============================================================================
+CameraRelativeCharacterController::~CameraRelativeCharacterController
+==============================================================================
+Description: Destructor
+
+Parameters: ( void )
+
+Return: n/a
+
+=============================================================================
+*/
+CameraRelativeCharacterController::~CameraRelativeCharacterController( void )
+{
+ if ( mpCamera )
+ {
+ mpCamera->Release( );
+ mpCamera = 0;
+ }
+ if ( mpEventHandler )
+ {
+ GetEventManager()->RemoveListener( mpEventHandler, EVENT_CAMERA_CHANGE );
+ delete mpEventHandler;
+ mpEventHandler = 0;
+ }
+}
+
+
+void CameraRelativeCharacterController::SetIntention( eIntention intention )
+{
+ if(!mActive)
+ {
+ return;
+ }
+
+ // ignore input if we're not in locomotion state
+ if( mpCharacter->GetStateManager()->GetState() == CharacterAi::INSIM )
+ {
+ // TODO:
+ // Should clear the intention here? or maybe just let the
+ // old intention linger? Hmm...
+ mIntention = CharacterController::NONE;
+ }
+ else
+ {
+ mIntention = intention;
+ }
+
+}
+
+
+/*
+==============================================================================
+CameraRelativeCharacterController::GetDirection
+==============================================================================
+Description: Transforms Controller relative inputs into camera relative inputs.
+ The character will carry on moving the same world direction independent
+ of camera position until the controller direction changes.
+
+Parameters: ( rmt::Vector& outDirection )
+
+Return: void
+
+=============================================================================
+*/
+void CameraRelativeCharacterController::GetDirection( rmt::Vector& outDirection )
+{
+ if(!mActive)
+ {
+ outDirection.Set(0.0f,0.0f,0.0f);
+ return;
+ }
+
+ if( mpCharacter->GetStateManager()->GetState() == CharacterAi::INSIM )
+ {
+ outDirection.Set( 0.0f, 0.0f, 0.0f ); // feed zero, so no walk anim
+ return;
+ }
+
+ GetCharacterMappable( )->GetDirection( outDirection );
+
+#ifdef RAD_WIN32
+ SuperCam* cam = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ if( cam->GetType() == SuperCam::PC_CAM )
+ {
+ //We don't do anything to modify the direction.
+ return;
+ }
+
+#endif
+
+ if ( mbCameraChange )
+ {
+ float cos15 = 0.9659258262890682867497431997289f;
+ static float sfAngleTolerance = cos15;
+
+ // Compare the old direction with the new direction transformed
+ // by the old matrix.
+ //
+ outDirection.Transform( mLastCameraMatrix );
+
+ rmt::Vector normDir = outDirection;
+ normDir.Normalize();
+
+ rmt::Vector normLast = mLastDirection;
+ normLast.Normalize();
+
+ float dot = normDir.DotProduct( normLast );
+
+ if ( dot > sfAngleTolerance )
+ {
+ // Direction is the "same" within given tolerance.
+ //
+ outDirection = mLastDirection;
+ }
+ else
+ {
+ mbCameraChange = false;
+ }
+ }
+ else if ( mpCamera != (tCamera*)0 )
+ {
+ rmt::Matrix mat = mpCamera->GetCameraToWorldMatrix();
+ mat.IdentityTranslation( );
+ outDirection.Transform( mat );
+ }
+
+ // Rotate by the inv transform of whatever the character is
+ // standing on.
+ //
+ rmt::Matrix invMat = mpCharacter->GetInverseParentTransform();
+ invMat.IdentityTranslation( );
+ outDirection.Transform( invMat );
+
+ // This is designed to address analog stick hysteresis.
+ // If you hold the stick in a direction and release it, often it
+ // will snap back and momentarily read a value >90 deg from the intended direction.
+ // this will address those momentary values.
+ //
+ Character* pCharacter = GetCharacter();
+ rmt::Vector facing;
+ rmt::Vector normDir = outDirection;
+ normDir.Normalize();
+ pCharacter->GetFacing( facing );
+ float dot = facing.DotProduct( normDir );
+ static float sfDot = 0.000001f;
+ if ( dot <= -sfDot )
+ {
+ //rReleasePrintf( "%f dot\n", dot );
+ // gt 90 deg.
+ //
+ static float toleranceSqr = rmt::Sqr( 0.60f );
+ //rReleasePrintf( "%f MagnitudeSqr\n", outDirection.MagnitudeSqr( ) );
+ if ( outDirection.MagnitudeSqr( ) < toleranceSqr )
+ {
+ // Small value.
+ //
+ static float sfScale = 0.01f;
+ outDirection.Scale( sfScale );
+ }
+ }
+
+}
+/*
+==============================================================================
+CameraRelativeCharacterController::SetCamera
+==============================================================================
+Description: Comment
+
+Parameters: ( tCamera* pCamera )
+
+Return: void
+
+=============================================================================
+*/
+void CameraRelativeCharacterController::SetCamera( tCamera* pCamera )
+{
+ tRefCounted::Assign( mpCamera, pCamera );
+}
+
+/*
+==============================================================================
+CameraRelativeCharacterController::HandleEvent
+==============================================================================
+Description: Implements Devil May Cry style controls when the camera view switches.
+ The character will carry on moving the same world direction independent
+ of camera position until the controller direction changes.
+
+Parameters: ( EventEnum id, void* pEventData )
+
+Return: void
+
+=============================================================================
+*/
+void CameraRelativeCharacterController::HandleEvent( int id, void* pEventData )
+{
+ EventEnum eventId = (EventEnum)id;
+
+ if ( EVENT_CAMERA_CHANGE == eventId )
+ {
+ bool interior = GetInteriorManager()->IsEntering() || GetInteriorManager()->IsExiting() || GetInteriorManager()->IsInside();
+
+ if (mpCamera && !mbCameraChange && !interior)
+ {
+ // Get absolute direction we'll transform it down below.
+ //
+ GetCharacterMappable( )->GetDirection( mLastDirection );
+
+ // Get the camera matrix to store while in transition.
+ //
+ mLastCameraMatrix = mpCamera->GetCameraToWorldMatrix();
+ mLastCameraMatrix.IdentityTranslation( );
+
+ // Now transform mLastDirection by the camera matrix.
+ // We store it to maintain the CameraRelative controls to the previous
+ // camera.
+ //
+ // NB: We don't just call CameraRelativeCharacterController::GetDirection()
+ // because that will take into account the parentTransform as well.
+ //
+ mLastDirection.Transform( mLastCameraMatrix );
+
+ // Tell the controller that we want to hold onto mLastDirection.
+ //
+ mbCameraChange = true;
+
+ // Update the new camera.
+ //
+ SuperCam* pSuperCam = static_cast<SuperCam*>( pEventData );
+ tCamera* pCamera = pSuperCam->GetCamera();
+ SetCamera( pCamera );
+ }
+ }
+ else
+ {
+ rAssert( false );
+ }
+}
+
+////////////////////////////////////////////////// NPC CONTROLLER ///////////////////////////////////////////
+
+static const float SECONDS_BACK_TO_FOLLOW_PATH = 3.0f;
+static const float SECONDS_TRACKING_PLAYER = 0.3f;
+static const float NPC_FOLLOW_PATH_SPEED_MPS = 1.0f;
+
+static const float SECONDS_BETW_PANIC_CHANGE_DIRECTION = 2.0f;
+static const float NPC_PANIC_RUN_SPEED_MPS = 3.0f;
+
+static const int TALK_TIME_MILLISECONDS = 3000;
+static const int STOP_TALK_CHECK_INTERVAL_MILLISECONDS = 1000;
+
+static const float SECONDS_BETW_PLAYING_TURN_ANIMS = 0.5f;
+
+NPCController::NPCController()
+:
+mOffPath( false ),
+mMillisecondsInTalk( 0 ),
+mTalkTarget( NULL ),
+mListening( true ),
+mState( FOLLOWING_PATH ),
+mSecondsInStopped( 0.0f ),
+mNumNPCWaypoints( 0 ),
+mCurrNPCWaypoint( -1 ),
+mStartPanicking( false ),
+mSecondsChangePanicDir( 0.0f ),
+mSecondsSinceLastTurnAnim( SECONDS_BETW_PLAYING_TURN_ANIMS ),
+mUseTempWaypoint(false),
+mTempWaypoint(0.0f, 0.0f , 0.0f)
+{
+ mSpeedMps = GetFollowPathSpeedMps();
+ mDirection.Set( 0.0f, 0.0f, 1.0f );
+ mNormalizedDodgeDir.Set( 0.0f, 0.0f, 1.0f );
+ mMouthFlapper = new MouthFlapper();
+ mMouthFlapper->AddRef();
+ mMouthFlapper->SetIsEnabled( false );
+
+
+ mNormalizedPanicDir.Set( 0.0f, 0.0f, 1.0f );
+}
+
+
+NPCController::~NPCController( void )
+{
+ if( mMouthFlapper != NULL )
+ {
+ mMouthFlapper->Release();
+ }
+}
+
+void NPCController::SetCharacter( Character *pCharacter )
+{
+ CharacterController::SetCharacter( pCharacter );
+}
+
+bool NPCController::AddNPCWaypoint( const rmt::Vector& pt )
+{
+ // can't add another point
+ if( mNumNPCWaypoints >= MAX_NPC_WAYPOINTS )
+ {
+ return false;
+ }
+
+ if( mNumNPCWaypoints == 0 )
+ {
+ // set our sights on the first waypoint
+ mCurrNPCWaypoint = 0;
+ }
+ mNPCWaypoints[ mNumNPCWaypoints ] = pt;
+ mNumNPCWaypoints++;
+
+ return true;
+}
+void NPCController::IncitePanic()
+{
+ // he's gonna run, so let him RUN .... into things...
+ //GetCharacter()->SetSolveCollisions( true );
+
+ // flag this guy to start panicking, if he can't panick right now..
+ mStartPanicking = true;
+
+ // don't panick if already panicking or in limbo (deliberately set there)
+ if( mState == NPCController::PANICKING || mState == NPCController::NONE )
+ {
+ return;
+ }
+ /*
+ // if not in an ideal state for panicking...
+ if( mState != NPCController::FOLLOWING_PATH &&
+ mState != NPCController::STANDING &&
+ mState != NPCController::STOPPED )
+ {
+ return;
+ }
+ */
+
+ TransitToState( NPCController::PANICKING );
+
+}
+void NPCController::QuellPanic()
+{
+ mStartPanicking = false;
+ TransitToState( NPCController::STOPPED );
+}
+
+
+
+void NPCController::Update( float seconds )
+{
+
+ if( mpCharacter == NULL )
+ {
+ return;
+ }
+
+ // if character in simulation, no point
+ if( mpCharacter->GetSimState()->GetControl() == sim::simSimulationCtrl )
+ {
+ return;
+ }
+
+ // if character is not in locomotion state, no point...
+ if( mpCharacter->GetStateManager()->GetState() != CharacterAi::LOCO )
+ {
+ return;
+ }
+
+ //BEGIN_PROFILE( "NPCController::Update" );
+
+ // Grab the simstate control. we'll be checking this sporadically.
+ int control = mpCharacter->GetSimState()->GetControl();
+
+ if( mStartPanicking )
+ {
+ IncitePanic();
+ }
+
+ switch( mState )
+ {
+ case FOLLOWING_PATH:
+ {
+ //rAssert( control == sim::simAICtrl );
+ FollowPath( seconds );
+ DetectAndDodge( seconds );
+ }
+ break;
+ case STOPPED:
+ {
+ //rAssert( control == sim::simAICtrl );
+ mSecondsInStopped += seconds;
+ mSecondsSinceLastTurnAnim += seconds;
+
+ // set to 1.5f and they'll follow you terminator style!
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+
+ //
+ // Track player for awhile then we can start deciding
+ // whether or not we want/need to dodge...
+ //
+ rmt::Vector playerPos, myPos, myFacing;
+
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0);
+ avatar->GetPosition( playerPos );
+
+ mpCharacter->GetPosition( &myPos );
+
+ mpCharacter->GetFacing( myFacing );
+ //rAssert( rmt::Epsilon( myFacing.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ rmt::Vector toPlayer = playerPos - myPos;
+ toPlayer.NormalizeSafe();
+ rAssert( rmt::Epsilon( toPlayer.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ //cosN for N being > half the animation turn angle
+ const float COSALPHA_TURN_TOLERANCE = 0.8387f;
+
+ // cosN, so anything > than N we just set the direction directly rather than play anim
+ const float COSALPHA_QUICKTURN_TOLERANCE = 0.7071f;
+
+ float cosAlpha = myFacing.Dot( toPlayer );
+ if( cosAlpha < COSALPHA_QUICKTURN_TOLERANCE )
+ {
+ // just turn (no anim)
+ SetDirection( toPlayer );
+ float dir = choreo::GetWorldAngle( toPlayer.x, toPlayer.z );
+ mpCharacter->SetDesiredDir( dir );
+
+ // reset so we can do turn anim right away after this
+ mSecondsSinceLastTurnAnim = SECONDS_BETW_PLAYING_TURN_ANIMS;
+ }
+ else if( COSALPHA_QUICKTURN_TOLERANCE <= cosAlpha && cosAlpha <= COSALPHA_TURN_TOLERANCE )
+ {
+ if( mSecondsSinceLastTurnAnim >= SECONDS_BETW_PLAYING_TURN_ANIMS )
+ {
+ // figure out whether to turn left or right
+ rmt::Vector myRight = Get90DegreeRightTurn( myFacing );
+ if( myRight.Dot( toPlayer ) > 0.0f )
+ {
+ // player's on my right, turn right
+ SetIntention( TurnRight );
+ }
+ else
+ {
+ // player's on my left, turn left
+ SetIntention( TurnLeft );
+ }
+ mSecondsSinceLastTurnAnim = 0.0f; // back to zero!
+ }
+ }
+
+ if( mSecondsInStopped > SECONDS_BACK_TO_FOLLOW_PATH )
+ {
+
+ // if we are going to be too close to other characters, transit
+ // to stopped here.
+ CharacterManager* cm = GetCharacterManager();
+
+ const float TOO_CLOSE_TO_NPC_DIST_SQR = 2.0f;
+
+ //
+ // if I'm a ped, I can ignore other peds (in THIS check only) cuz I already
+ // deal with them safely elsewhere... If I'm a non-ped NPC, I would
+ // want to check for peds, so I don't walk through them.
+ //
+ bool ignorePeds = (mpCharacter->GetRole() == Character::ROLE_PEDESTRIAN)? true : false;
+
+ int maxChars = cm->GetMaxCharacters();
+
+ bool gonnaHit = false;
+
+ for( int i=0; i<maxChars; i++ )
+ {
+ Character* c = cm->GetCharacter( i );
+ if( c && c != mpCharacter )
+ {
+ if( ignorePeds && c->GetRole() == Character::ROLE_PEDESTRIAN )
+ {
+ continue;
+ }
+ rmt::Vector cPos;
+ c->GetPosition( cPos );
+
+ float distSqr = (cPos - myPos).MagnitudeSqr();
+ if( distSqr <= TOO_CLOSE_TO_NPC_DIST_SQR )
+ {
+ gonnaHit = true;
+ break;
+ }
+ }
+ }
+
+ if( gonnaHit )
+ {
+ mSecondsInStopped = 0.0f;
+ }
+ else
+ {
+ TransitToState(FOLLOWING_PATH);
+ }
+ }
+ //
+ // If we've been tracking player long enough,
+ // time to dodge!!!
+ //
+ if( mSecondsInStopped > SECONDS_TRACKING_PLAYER )
+ {
+ DetectAndDodge( seconds );
+ }
+
+ }
+ break;
+ case DODGING:
+ {
+ //rAssert( control == sim::simAICtrl );
+ // so NPCs don't dodge into someplace indeterminate
+ //mpCharacter->SetSolveCollisions( true );
+
+ // sets NeedsUpdate flag for character (so it doens't pause
+ // in midmotion if player moves out of physics simming radius)
+ mpCharacter->Update( seconds );
+ }
+ break;
+ case CRINGING:
+ {
+ //rAssert( control == sim::simAICtrl );
+ }
+ break;
+ case TALKING:
+ {
+ rAssert( mTalkTarget != NULL );
+ //rAssert( control == sim::simAICtrl );
+
+ int coinflip = 0;
+
+ // Direction is set for us when we transited to this state
+ // it never needs changing.
+ //SetDirection( );
+
+ // if target is no longer in TALKING state, we stop too...
+ if( mTalkTarget->GetState() != TALKING )
+ {
+ TransitToState(FOLLOWING_PATH);
+ break;
+ }
+
+ if( mListening )
+ {
+ rAssert( mTalkTarget != NULL );
+ rAssert( !mMouthFlapper->IsEnabled() );
+ rAssert( !mTalkTarget->mListening ); // one of us needs to do the talking! geez...
+ rAssert( mTalkTarget->mMouthFlapper->IsEnabled() );
+
+ mMillisecondsInTalk = 0;
+ }
+ else
+ {
+ rAssert( mTalkTarget != NULL );
+ rAssert( mMouthFlapper->IsEnabled() );
+ rAssert( mTalkTarget->mListening ); // only one of us can talk at once! geez...
+ rAssert( !mTalkTarget->mMouthFlapper->IsEnabled() );
+
+ mMillisecondsInTalk += (int)(seconds*1000);
+ if( mMillisecondsInTalk >= TALK_TIME_MILLISECONDS )
+ {
+ // ok, by now we've talked for longer than alotted time,
+ // so every check interval, we have a chance to
+ // stop talking and let the other person start
+ // talking.
+
+ if( (mMillisecondsInTalk - TALK_TIME_MILLISECONDS) >=
+ STOP_TALK_CHECK_INTERVAL_MILLISECONDS )
+ {
+ mMillisecondsInTalk = TALK_TIME_MILLISECONDS;
+
+ coinflip = rand() % 3;
+ // 2 in 3 chance in favor of stopping our yammer
+ if( coinflip != 2 )
+ {
+ StopTalking();
+ mTalkTarget->StartTalking();
+ }
+ }
+ }
+ }
+
+ /*
+ // there's a good chance we won't bother to detect cars while talking... uh oh!
+ coinflip = rand() % 1000;
+ if( coinflip == 0 )
+ {
+ DetectAndDodge( seconds );
+ }
+ */
+ }
+ break;
+ case STANDING:
+ {
+ //rAssert( control == sim::simAICtrl );
+ mSecondsInStopped += seconds;
+ if( mSecondsInStopped > SECONDS_BACK_TO_FOLLOW_PATH )
+ {
+ TransitToState(FOLLOWING_PATH);
+ break;
+ }
+ DetectAndDodge( seconds );
+ }
+ break;
+ case PANICKING:
+ {
+ //rAssert( control == sim::simAICtrl );
+
+ rmt::Vector myPos;
+ mpCharacter->GetPosition( myPos );
+
+ mSecondsChangePanicDir += seconds;
+ if( mSecondsChangePanicDir > SECONDS_BETW_PANIC_CHANGE_DIRECTION )
+ {
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( player );
+
+ rmt::Vector playerPos;
+ player->GetPosition( playerPos );
+
+ rmt::Vector awayFromPlayer = myPos - playerPos;
+
+ // vary direction of panic run by modifying at random .x & .z
+ // of the awayFromPlayer vector...
+
+ float randomXVariance = 0.0f;
+ float randomZVariance = 0.0f;
+ const float FAR_ENOUGH_TO_CHANGE_DIR_SQR = 36.0f;
+ if( awayFromPlayer.MagnitudeSqr() > FAR_ENOUGH_TO_CHANGE_DIR_SQR )
+ {
+ randomXVariance = (float)(rand()%500) / 100.0f;
+ randomXVariance *= (rand() % 2)? 1.0f : -1.0f;
+ randomZVariance = (float)(rand()%500) / 100.0f;
+ randomZVariance *= (rand() % 2)? 1.0f : -1.0f;
+ }
+ awayFromPlayer.x += randomXVariance;
+ awayFromPlayer.z += randomZVariance;
+
+ awayFromPlayer.NormalizeSafe(); // *** SQUARE ROOT! ***
+
+ mNormalizedPanicDir = awayFromPlayer;
+ mSecondsChangePanicDir = 0.0f;
+ }
+ SetDirection( mNormalizedPanicDir );
+
+ }
+ break;
+ case TALKING_WITH_PLAYER: // fall thru
+ case NONE:
+ break; // do nothing
+ default:
+ {
+ // bad state
+ rAssert( false );
+ }
+ }
+ //END_PROFILE( "NPCController::Update" );
+}
+
+float NPCController::GetFollowPathSpeedMps() const
+{
+ if( mOffPath )
+ {
+ return NPC_FOLLOW_PATH_SPEED_MPS;
+ }
+ if( mNumNPCWaypoints > 1 )
+ {
+ return NPC_FOLLOW_PATH_SPEED_MPS;
+ }
+ return 0.0f;
+}
+
+
+void NPCController::FollowPath( float seconds )
+{
+ // if we're in collision, we should transit to STOPPED.
+ if( mpCharacter->mbCollidedWithVehicle )
+ {
+ TransitToState( STOPPED );
+ return;
+ }
+
+ // get our pos
+ rmt::Vector currPos;
+ mpCharacter->GetPosition( &currPos );
+
+
+ // if we are going to be too close to other characters, transit
+ // to stopped here.
+ CharacterManager* cm = GetCharacterManager();
+
+ const float TOO_CLOSE_TO_NPC_DIST_SQR = 2.0f;
+
+ //
+ // if I'm a ped, I can ignore other peds (in THIS check only) cuz I already
+ // deal with them safely elsewhere... If I'm a non-ped NPC, I would
+ // want to check for peds, so I don't walk through them.
+ //
+ bool ignorePeds = (mpCharacter->GetRole() == Character::ROLE_PEDESTRIAN)? true : false;
+
+ int maxChars = cm->GetMaxCharacters();
+ for( int i=0; i<maxChars; i++ )
+ {
+ Character* c = cm->GetCharacter( i );
+ if( c && c != mpCharacter )
+ {
+ if( ignorePeds && c->GetRole() == Character::ROLE_PEDESTRIAN )
+ {
+ continue;
+ }
+ rmt::Vector cPos;
+ c->GetPosition( cPos );
+
+ float distSqr = (cPos - currPos).MagnitudeSqr();
+ if( distSqr <= TOO_CLOSE_TO_NPC_DIST_SQR )
+ {
+ TransitToState( STOPPED );
+ return;
+ }
+ }
+ }
+
+
+ //mpCharacter->SetSolveCollisions( true );
+
+ mSpeedMps = GetFollowPathSpeedMps();
+ mpCharacter->SetSpeed( mSpeedMps );
+
+ // If we're off our path, pathfind way back on path
+ rmt::Vector lastPos;
+
+ //rAssert( 1 <= mNumNPCWaypoints && mNumNPCWaypoints < MAX_NPC_WAYPOINTS );
+ //rAssert( 0 <= mCurrNPCWaypoint && mCurrNPCWaypoint < mNumNPCWaypoints );
+
+ // Find the good pos on path that we want to be at, or the point
+ // itself if no waypoints, ok?
+ if(mUseTempWaypoint)
+ {
+ lastPos = mTempWaypoint;
+ if((lastPos - mNPCWaypoints[0]).Magnitude() < 20.0f) // were close to our path again, clear the temp waypoint
+ {
+ lastPos = mNPCWaypoints[0];
+ mUseTempWaypoint = false;
+ }
+ }
+ else if( mNumNPCWaypoints == 1 )
+ {
+ lastPos = mNPCWaypoints[0]; // only one waypoint, so this is it.
+ }
+ else if( mNumNPCWaypoints > 1 )
+ {
+ rmt::Vector start, end;
+ end = mNPCWaypoints[ mCurrNPCWaypoint ];
+
+ int lastPt = mCurrNPCWaypoint - 1;
+ if( lastPt < 0 )
+ {
+ lastPt = mNumNPCWaypoints - 1;
+ }
+ rAssert( 0 <= lastPt && lastPt < mNumNPCWaypoints );
+ start = mNPCWaypoints[ lastPt ];
+
+ end.y = start.y = currPos.y; // ignore heights
+
+ FindClosestPointOnLine( start, end, currPos, lastPos );
+ }
+ else
+ {
+ // do nothing!
+ lastPos = currPos;
+ }
+
+ rmt::Vector epsilon;
+ GetAllowedPathOffset( epsilon );
+
+ bool same = false;
+ if( rmt::Epsilon( currPos.x, lastPos.x, epsilon.x ) &&
+ //rmt::Epsilon( currPos.y, lastPos.y, epsilon.y ) && // IGNORE y VALUE
+ rmt::Epsilon( currPos.z, lastPos.z, epsilon.z ) )
+ {
+ same = true;
+ }
+
+ if( !mOffPath && same )
+ {
+ // we were on path, and we're still on path
+ return;
+ }
+ else if( mOffPath && same )
+ {
+ // we were off path, now we're on path
+ mOffPath = false;
+ return;
+ }
+ else if( !same )
+ {
+ // we are not on path...
+ mOffPath = true;
+
+ bool tooFar = (lastPos - currPos).Magnitude() > 100.0f;
+
+ // If no one is looking, might as well warp baby!
+ if( !mpCharacter->mbInAnyonesFrustrum || tooFar)
+ {
+ // Make sure where we're warping to is also not in anyone's frustrum...
+ bool bLastPosInAnyonesFrustrum = false;
+ for( int i=0; i<GetGameplayManager()->GetNumPlayers(); i++ )
+ {
+ if( mpCharacter->PosInFrustrumOfPlayer( lastPos, i ) )
+ {
+ bLastPosInAnyonesFrustrum = true;
+ break;
+ }
+ }
+ if( !bLastPosInAnyonesFrustrum || tooFar)
+ {
+ float facing = mpCharacter->GetFacingDir();
+ mpCharacter->RelocateAndReset( lastPos, facing, false );
+ mOffPath = false;
+ return;
+ }
+ }
+
+ //mpCharacter->SetSolveCollisions( true );
+ OnOffPath( lastPos, currPos );
+ }
+}
+
+void NPCController::TeleportToPath( void )
+{
+ float facing = mpCharacter->GetFacingDir();
+ mpCharacter->RelocateAndReset( mNPCWaypoints[0], facing, false );
+ mOffPath = false;
+ ClearTempWaypoint();
+}
+
+void NPCController::DetectAndDodge( float seconds )
+{
+ // if we're in a street race, don't do any detecting or dodging
+ // cuz it could put us outside the race props boundaries.
+ Mission* m = GetGameplayManager()->GetCurrentMission();
+ if( m && m->mIsStreetRace1Or2 )
+ {
+ rmt::Vector dir( 0.0f, 0.0f, 0.0f );
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+
+ float hisVel = 0.0f;
+ float epsilon = 0.001f;
+
+ bool willGetHit = Detect( seconds, hisVel );
+
+ if( willGetHit )
+ {
+ //If we haven't tried stopping...
+ if( mState != STOPPED )
+ {
+ TransitToState( STOPPED );
+ }
+ // ok, we're stopped, but are they're still coming?
+ else
+ {
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+
+ if( hisVel > epsilon ) // we're already stopped, but he's still coming
+ {
+ // Well if they're not in a vehicle, who cares!
+ if( !avatar->IsInCar() )
+ {
+ return;
+ }
+
+ int coinflip = rand() % 2;
+ if( coinflip == 0 )
+ {
+ //TransitToState(CRINGING);
+ PerformCringe();
+ return;
+ }
+ else
+ {
+ //TransitToState(DODGING);
+ PerformDodge( );
+ return;
+ }
+ }
+ }
+ }
+
+ //
+ // The reason we do this after is that we may have needed to transit
+ // back to STOPPED (in the code above) after we've just transitted
+ // from there to FOLLOWING_PATH because we might hit the player.
+ // So if the state is still FOLLOWING_PATH despite detecting and
+ // dodging, we'll go ahead and traverse the pedpathsegments
+ //
+ if( mState == FOLLOWING_PATH )
+ {
+ TraversePath( seconds );
+ }
+}
+
+
+void NPCController::TraversePath( float seconds )
+{
+ rmt::Vector dir( 0.0f, 0.0f, 0.0f );
+ if( mOffPath )
+ {
+ // allow pathfinding back (don't set speed to zero or set direction to zero)
+ return;
+ }
+
+ if( mCurrNPCWaypoint == -1 )
+ {
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+ if( (mNumNPCWaypoints < 2) || mUseTempWaypoint)
+ {
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+ // loop back to the first waypoint
+ if( mCurrNPCWaypoint >= mNumNPCWaypoints )
+ {
+ mCurrNPCWaypoint = 0;
+ }
+
+ rAssert( 0 <= mCurrNPCWaypoint && mCurrNPCWaypoint < mNumNPCWaypoints );
+ rmt::Vector wayPos = mNPCWaypoints[ mCurrNPCWaypoint ];
+
+ rmt::Vector myPos;
+ GetCharacter()->GetPosition( myPos );
+
+ // if player not close enough, don't bother to do this...
+ rmt::Vector playerPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( playerPos );
+
+ const float DIST_PLAYER_TOO_FAR_TO_BOTHER_SQR = 22500.0f;
+ if( (playerPos-myPos).MagnitudeSqr() >= DIST_PLAYER_TOO_FAR_TO_BOTHER_SQR )
+ {
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+ // ignore height differences
+ wayPos.y = myPos.y;
+ dir = wayPos - myPos;
+
+ float distToWaypoint = dir.Magnitude(); // *** SQUARE ROOT! ***
+ const float DIST_CLOSE_ENOUGH_TO_NPC_WAYPOINT = 0.5f;
+ if( distToWaypoint < DIST_CLOSE_ENOUGH_TO_NPC_WAYPOINT )
+ {
+ // increment curr waypoint
+ mCurrNPCWaypoint++;
+ if( mCurrNPCWaypoint >= mNumNPCWaypoints )
+ {
+ mCurrNPCWaypoint = 0; // increment for next round
+ }
+
+ // Non-ped NPCs will want to do some idle anims... Peds
+ // will just keep on going...
+ OnReachedWaypoint();
+ }
+
+ if( distToWaypoint > 0.0f )
+ {
+ SetDirection( dir / distToWaypoint );
+ mSpeedMps = GetFollowPathSpeedMps();
+ mpCharacter->SetSpeed( mSpeedMps );
+ }
+}
+
+void NPCController::OnReachedWaypoint()
+{
+ /*
+ // Reached a waypoint, so do some anim here if we're in the player's
+ // frustrum
+ if( mpCharacter->IsVisible() && mpCharacter->mbInAnyonesFrustrum )
+ {
+ PresentationAnimator* npcAnimator = GetPresentationManager()->GetAnimatorNpc();
+ Character* oldCharacter = npcAnimator->GetCharacter();
+ const bool oldRandomSelection = npcAnimator->GetRandomSelection();
+
+ npcAnimator->SetCharacter( mpCharacter );
+ npcAnimator->SetRandomSelection( true );
+ npcAnimator->PlaySpecialAmbientAnimation();
+ npcAnimator->SetCharacter( oldCharacter );
+ npcAnimator->SetRandomSelection( oldRandomSelection );
+ }
+ */
+}
+
+
+
+static const float DODGE_SPEED_MPS = 1.0f; // doesn't seem to matter as long as > 0.0f
+static const float CRINGE_SPEED_MPS = 0.0f;
+
+static const float VEHICLE_SPAN_METERS = 1.5f;
+static const float CHARACTER_SPAN_METERS = 0.5f;
+static const float COLLISION_LOOK_AHEAD_SECONDS = 1.0f;
+static const float COLLISION_DISTANCE_SQUARED = 400.0f; // must be within this to calc collisions
+
+bool NPCController::Detect( float seconds, float& hisVel )
+{
+ hisVel = 0.0f; // init return value to something
+
+ // Clear out previous use of mDodgeInfo
+ mDodgeInfo.wasSetThisFrame = false;
+
+
+ // Things we use over and over again in this function
+ rmt::Vector playerPos, playerHeading, playerSide, playerUp, playerVel;
+ rmt::Vector myPos, myHeading, mySide, myUp, myVel;
+
+
+ //////////////////////////////////////
+ // Information about player
+ //////////////////////////////////////
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+ float playerCollisionRadius =
+ (avatar->IsInCar())? VEHICLE_SPAN_METERS : CHARACTER_SPAN_METERS;
+
+
+ //////////////////////////////////////
+ // Information about self
+ //////////////////////////////////////
+ mpCharacter->GetPosition( myPos );
+ float myCollisionRadius = CHARACTER_SPAN_METERS;
+
+
+ //////////////////////////////////////
+ // Detect if player will hit us
+ //////////////////////////////////////
+
+ //
+ // CRUDE TEST
+ // ==========
+ // Test against hard-coded distanceSquared to see if we're
+ // close enough to player to even bother to proceed...
+ //
+ rmt::Vector myPosToPlayerPos = playerPos - myPos;
+ if( myPosToPlayerPos.MagnitudeSqr() > COLLISION_DISTANCE_SQUARED )
+ {
+ return false;
+ }
+
+ //
+ // MORE DETAILED TEST
+ // ===================
+ // Treat everything only on x-z plane. In other words, we won't
+ // detect a vehicle coming at us from above or below.
+ //
+
+ // Get more info about player and self
+ float epsilon = 0.005f;
+ bool gonnaHit = false;
+ float spanDistSum = playerCollisionRadius + myCollisionRadius;
+
+ myPos.y = 0.0f;
+ mpCharacter->GetFacing( myHeading );
+ rAssert( rmt::Epsilon(myHeading.MagnitudeSqr(), 1.0f, epsilon) );
+ myVel = myHeading * GetSpeedMps();
+ myVel.y = 0.0f;
+ myUp.Set( 0.0f, 1.0f, 0.0f );
+ float mySpeedMps = myVel.Length(); // *** SQUARE ROOT! ***
+
+
+ playerPos.y = 0.0f;
+ avatar->GetVelocity( playerVel );
+ playerVel.y = 0.0f;
+ playerUp.Set( 0.0f, 1.0f, 0.0f );
+ float playerSpeedMps = playerVel.Length(); // *** SQUARE ROOT! ***
+
+ hisVel = playerSpeedMps;
+
+ /////////////////////////////////////////////////////////
+ // If player is stationary
+ //
+ if( playerSpeedMps <= epsilon || avatar->GetCharacter()->mbIsPlayingIdleAnim )
+ {
+ mySide.CrossProduct( myUp, myHeading );
+ mySide.Normalize();
+
+ // sanity check lengths to 1.0f. myHeading should not be 0.0f since
+ // we're in the case where mySpeedMps > 0.0f. mySide shouldn't be 0.0f
+ // since we made myUp and myHeading orthonormal
+
+ rAssert( rmt::Epsilon( myHeading.MagnitudeSqr(), 1.0f, epsilon ) );
+ rAssert( rmt::Epsilon( mySide.MagnitudeSqr(), 1.0f, epsilon ) );
+
+ float lookAheadDist = GetFollowPathSpeedMps() * COLLISION_LOOK_AHEAD_SECONDS + spanDistSum;
+
+ //float lookAheadDist = mySpeedMps * COLLISION_LOOK_AHEAD_SECONDS + spanDistSum;
+ bool playerPosLiesOnMyRight;
+
+ gonnaHit = WillCollide( myPos,
+ myHeading, // Must be normalized to 1
+ mySide, // Must be normalized to 1
+ spanDistSum,
+ lookAheadDist,
+ playerPos,
+ playerPosLiesOnMyRight );
+ }
+ //////////////////////////////////////////////////////////////
+ // If player is not stationary, but I am...
+ //
+ else if( playerSpeedMps > epsilon && mySpeedMps <= epsilon )
+ {
+ playerHeading = playerVel / playerSpeedMps;
+ playerSide.CrossProduct( playerUp, playerHeading );
+ playerSide.Normalize();
+
+ rAssert( rmt::Epsilon( playerHeading.MagnitudeSqr(), 1.0f, epsilon ) );
+ rAssert( rmt::Epsilon( playerSide.MagnitudeSqr(), 1.0f, epsilon ) );
+
+ float lookAheadDist = playerSpeedMps * COLLISION_LOOK_AHEAD_SECONDS + spanDistSum;
+ bool myPosLiesOnPlayersRight;
+
+ gonnaHit = WillCollide( playerPos,
+ playerHeading, // Must be normalized to 1
+ playerSide, // Must be normalized to 1
+ spanDistSum,
+ lookAheadDist,
+ myPos,
+ myPosLiesOnPlayersRight );
+
+ //
+ // Store Dodge info to be used in PerformDodge
+ // because this should be the only control flow that allows you
+ // to call PerformDodge() later.
+ //
+ if( gonnaHit )
+ {
+ mDodgeInfo.wasSetThisFrame = true;
+ mDodgeInfo.myPosIsOnPlayersRightSide = myPosLiesOnPlayersRight;
+ mDodgeInfo.playerRightSide = playerSide;
+ }
+ }
+ /////////////////////////////////////////////////////////////
+ // If both moving, gotta do full collision test
+ //
+ else
+ {
+ rAssert( playerSpeedMps > epsilon && mySpeedMps > epsilon );
+ //
+ // Easiest, Crudest way to go about this is to do sphere-sphere
+ // collision on the 2 motion vectors. We don't need sophisticated
+ // check because this only provides an early warning about nearby
+ // activities...
+ //
+
+ //
+ // Compute future positions
+ //
+ rmt::Vector playerPos2, myPos2;
+ playerPos2 = playerPos + playerVel * COLLISION_LOOK_AHEAD_SECONDS;
+ myPos2 = myPos + myVel * COLLISION_LOOK_AHEAD_SECONDS;
+
+ //
+ // Find midpoints & radii
+ //
+ rmt::Vector playerMid, myMid;
+ playerMid = ( playerPos2 + playerPos ) * 0.5f;
+ myMid = (myPos2 + myPos ) * 0.5f;
+
+ float playerMotionRadius, myMotionRadius;
+ playerMotionRadius = playerSpeedMps * COLLISION_LOOK_AHEAD_SECONDS * 0.5f;
+ myMotionRadius = mySpeedMps * COLLISION_LOOK_AHEAD_SECONDS * 0.5f;
+
+ //
+ // Test for sphere-sphere collision
+ //
+ float minDist = myMotionRadius + playerMotionRadius;
+ float minDistSqr = minDist * minDist;
+ float actualDistSqr = (playerMid - myMid).MagnitudeSqr();
+
+ if( actualDistSqr < minDistSqr )
+ {
+ gonnaHit = true;
+ }
+ else
+ {
+ gonnaHit = false;
+ }
+
+ }
+ return gonnaHit;
+
+}
+
+
+void NPCController::PerformCringe( )
+{
+ SetIntention( Cringe );
+ SetSpeedMps( CRINGE_SPEED_MPS );
+
+ // Determine direction...
+ rmt::Vector myPos, playerPos;
+ mpCharacter->GetPosition( myPos );
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+
+ rmt::Vector toPlayer = playerPos - myPos;
+ toPlayer.Normalize(); // *** SQUARE ROOT! ***
+
+ SetDirection( toPlayer );
+}
+
+void NPCController::PerformDodge( )
+{
+ if( !mDodgeInfo.wasSetThisFrame )
+ {
+ return;
+ }
+
+ // NOTE:
+ // I wouldn't be here unless I transited from STOPPED state,
+ // meaning my speed was 0.0f when Detect() was called just before
+ // this method was called, meaning that mDodgeInfo should
+ // have been set by the control flow through Detect() that
+ // happens when I am stationary and player is not.
+
+ // Set Controller information
+ SetIntention( Dodge );
+ SetSpeedMps( DODGE_SPEED_MPS );
+
+ // Determine which way to dodge...
+ if( mDodgeInfo.myPosIsOnPlayersRightSide )
+ {
+ mNormalizedDodgeDir = mDodgeInfo.playerRightSide;
+ }
+ else
+ {
+ mNormalizedDodgeDir = mDodgeInfo.playerRightSide * -1;
+ }
+ SetDirection( mNormalizedDodgeDir );
+
+ //
+ // Say funny ha-ha line
+ //
+ GetEventManager()->TriggerEvent( EVENT_PEDESTRIAN_DODGE, mpCharacter );
+}
+
+void NPCController::GetDirection( rmt::Vector& outDirection )
+{
+ // mDirection initialized and kept at 0,0,0
+ rAssert( mpCharacter->GetMaxSpeed() > 0.0f );
+ outDirection = mDirection * (mSpeedMps/mpCharacter->GetMaxSpeed());
+}
+
+void NPCController::TransitToState( State state )
+{
+ // If in TALKING but transiting to state other than TALKING,
+ // must StopTalking().
+ if( mState == TALKING && state != TALKING )
+ {
+ StopTalking();
+ mTalkTarget = NULL;
+ }
+
+ mpCharacter->SetInSubstep( false );
+
+ switch( state )
+ {
+ case TALKING:
+ {
+ // initially, tell anyone who transits to TALKING to start
+ // by listening. It's up to the function that sets them TALKING
+ // to call StartTalking() on one of the parties...
+ mListening = true;
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ }
+ break;
+ case TALKING_WITH_PLAYER: // fall thru
+ case STOPPED: // fall thru
+ case STANDING: // fall thru
+ case NONE:
+ {
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ mSecondsInStopped = 0.0f;
+ mSecondsSinceLastTurnAnim = 0.0f;
+ }
+ break;
+ case PANICKING:
+ {
+ // test to see if we were ever told to transit to panic
+ // while already in panic. This isn't anything fatal, it's just
+ // more work than necessary
+ rAssert( mState != PANICKING );
+
+ mSpeedMps = NPC_PANIC_RUN_SPEED_MPS;
+ mpCharacter->SetSpeed( mSpeedMps );
+ mSecondsChangePanicDir = 0.0f;
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( player );
+
+ rmt::Vector myPos, playerPos;
+ mpCharacter->GetPosition( myPos );
+ player->GetPosition( playerPos );
+
+ rmt::Vector awayFromPlayer = myPos - playerPos;
+ awayFromPlayer.NormalizeSafe(); // *** SQUARE ROOT! ***
+
+ mNormalizedPanicDir = awayFromPlayer;
+ }
+ break;
+ case DODGING:
+ {
+ mpCharacter->SetInSubstep( true );
+ }
+ break;
+ default:
+ {
+ //nothing
+ }
+ // Put other cases here if they need specific operations...
+ }
+
+ mState = state;
+}
+void NPCController::GetAllowedPathOffset( rmt::Vector& offset )
+{
+ offset.Set( 1.0f, 1.0f, 1.0f );
+}
+
+void NPCController::OnOffPath( rmt::Vector lastPos, rmt::Vector currPos )
+{
+ rAssert( mOffPath );
+
+ // set direction...
+ rmt::Vector dirBackToPath = lastPos - currPos;
+ dirBackToPath.y = 0.0f;
+
+ dirBackToPath.Normalize(); // *** SQUARE ROOT! ***
+ SetDirection( dirBackToPath );
+}
+
+
+void NPCController::StartTalking()
+{
+ if( mListening && mState == TALKING )
+ {
+ mMouthFlapper->SetCharacter( mpCharacter );
+ mMillisecondsInTalk = 0;
+ mListening = false;
+ mpCharacter->GetPuppet()->GetEngine()->GetPoseEngine()->AddPoseDriver( 2, mMouthFlapper );
+ mMouthFlapper->SetIsEnabled( true );
+ }
+}
+void NPCController::StopTalking()
+{
+ if( !mListening && mState == TALKING )
+ {
+ mMillisecondsInTalk = 0;
+ mListening = true;
+ mMouthFlapper->SetIsEnabled( false );
+ mpCharacter->GetPuppet()->GetEngine()->GetPoseEngine()->RemovePoseDriver( 2, mMouthFlapper );
+ }
+}
+
+void NPCController::SetTempWaypont(const rmt::Vector& w)
+{
+ mTempWaypoint = w;
+ mUseTempWaypoint = true;
+}
+
+void NPCController::ClearTempWaypoint(void)
+{
+ mUseTempWaypoint = false;
+}
diff --git a/game/code/worldsim/character/charactercontroller.h b/game/code/worldsim/character/charactercontroller.h
new file mode 100644
index 0000000..85431a8
--- /dev/null
+++ b/game/code/worldsim/character/charactercontroller.h
@@ -0,0 +1,308 @@
+#ifndef CHARACTERCONTROLLER_H_
+#define CHARACTERCONTROLLER_H_
+
+#include <p3d/refcounted.hpp>
+#include <radmath/radmath.hpp>
+
+//////// FORWARD DECLARATIONS /////////
+class Character;
+class MouthFlapper;
+///////////////////////////////////////
+
+class CharacterController
+:
+public tRefCounted
+{
+public:
+ enum eIntention
+ {
+ NONE,
+ LeftStickX,
+ LeftStickY,
+ DoAction, // DUSIT [Nov 6,2002]: "Action" conflicts with class name
+ Jump,
+ Dash,
+ Attack,
+ DPadUp,
+ DPadDown,
+ DPadLeft,
+ DPadRight,
+#ifdef RAD_WIN32
+ GetOutCar,
+ MouseLookLeft,
+ MouseLookRight,
+#endif
+ NUM_INPUTS, // "real" inputs (stuff with buttons) goes
+ // before this, things that are only triggerd by AI go after
+ Dodge,
+ Cringe,
+ TurnRight,
+ TurnLeft,
+ CelebrateSmall,
+ CelebrateBig,
+ WaveHello,
+ WaveGoodbye
+ };
+
+ CharacterController( void );
+ virtual ~CharacterController( void );
+
+ virtual void Update( float timeins ) {};
+ virtual void GetDirection( rmt::Vector& outDirection ) = 0;
+ virtual float GetValue( int buttonId ) const = 0;
+ virtual bool IsButtonDown( int buttonId ) const = 0;
+ virtual int TimeSinceChange( int buttonId ) const { return IsButtonDown(buttonId) ? 0 : 5000; };
+
+ Character* GetCharacter( ) const;
+ virtual void SetCharacter( Character* pCharacter );
+
+ virtual void SetIntention( eIntention intention )
+ {
+ if( mActive )
+ {
+ mIntention = intention;
+ }
+ }
+ eIntention GetIntention( void ) const
+ {
+ return mIntention;
+ }
+
+ void PreserveIntention( eIntention intention )
+ {
+ mPreserveIntention = intention;
+ }
+
+ void ClearIntention( void )
+ {
+ mIntention = mPreserveIntention;
+ mPreserveIntention = NONE;
+ }
+
+ void SetActive( bool active ) { mActive = active; }
+
+protected:
+ Character* mpCharacter;
+ eIntention mIntention;
+ eIntention mPreserveIntention;
+ bool mActive;
+};
+
+
+////////////////////////////////////////////////////////////
+// Dusit
+////////////////////////////////////////////////////////////
+
+class NPCController
+:
+public CharacterController
+{
+public: // METHODS
+ NPCController( void );
+ virtual ~NPCController( void );
+
+ virtual void Update( float seconds );
+ virtual float GetSpeedMps() const;
+ virtual void SetSpeedMps( float fSpeedMps );
+ virtual void SetDirection( const rmt::Vector& inDirection );
+ virtual void GetDirection( rmt::Vector& outDirection );
+ virtual float GetValue( int buttonId ) const;
+ virtual bool IsButtonDown( int buttonId ) const;
+
+ void SetCharacter( Character *pCharacter );
+
+ enum State
+ {
+ // We enumerate here, all the different states specific to
+ // NPC behavior controller... which are essentially substates
+ // of the character's locomotion state (in character's statemanager)
+
+ FOLLOWING_PATH, // following predescribed path
+ STOPPED, // not moving, turns to track player, will resume following path
+ DODGING, // uh... dodging
+ CRINGING, // if we're not dodging, we're cringing...
+ TALKING, // when peds want to talk to one another!
+ STANDING, // just not moving, will resume following path
+ PANICKING, // running in random direction, more or less away from player
+ TALKING_WITH_PLAYER, // just for conversations..
+ NONE, // limbo... do nothing... stand there... don't resume following path
+ NUM_STATES
+ };
+ virtual State GetState() const
+ {
+ return mState;
+ }
+ void TransitToState( State state );
+
+ bool AddNPCWaypoint( const rmt::Vector& pt );
+ void ClearNPCWaypoints();
+
+ void StartTalking();
+ void StopTalking();
+ void IncitePanic();
+ void QuellPanic();
+
+ void TeleportToPath(void);
+ void SetTempWaypont(const rmt::Vector&);
+ void ClearTempWaypoint(void);
+
+public: // MEMBERS
+
+ enum { MAX_NPC_WAYPOINTS = 32 };
+
+ bool mOffPath;
+ MouthFlapper* mMouthFlapper;
+ int mMillisecondsInTalk;
+ NPCController* mTalkTarget;
+ bool mListening; // if in talking state but mouth not flapping
+
+protected: // METHODS
+
+ virtual void OnOffPath( rmt::Vector lastPos, rmt::Vector currPos );
+ virtual void GetAllowedPathOffset( rmt::Vector& offset );
+ virtual float GetFollowPathSpeedMps() const;
+ virtual void FollowPath( float seconds );
+ virtual void OnReachedWaypoint();
+
+ // detect collision with player vehicle and set dodge intention.
+ // returns true if need to dodge, false otherwise...
+ virtual void DetectAndDodge( float seconds );
+ virtual void TraversePath( float seconds );
+
+ bool Detect( float seconds, float& hisVel );
+ void PerformDodge();
+ void PerformCringe();
+
+protected: // MEMBERS
+
+ // This struct is filled in the Detect call before PerformDodge is called
+ // and used from within PerformDodge to determine direction of dodge.
+ struct DodgeInfo
+ {
+ bool wasSetThisFrame;
+ rmt::Vector playerRightSide;
+ bool myPosIsOnPlayersRightSide;
+ };
+ DodgeInfo mDodgeInfo;
+
+ State mState;
+ float mSecondsInStopped;
+ float mSpeedMps;
+ rmt::Vector mNormalizedDodgeDir; // Normalized
+
+ rmt::Vector mNPCWaypoints[ MAX_NPC_WAYPOINTS ];
+ int mNumNPCWaypoints;
+ int mCurrNPCWaypoint;
+
+ bool mStartPanicking;
+ float mSecondsChangePanicDir;
+ rmt::Vector mNormalizedPanicDir;
+
+ float mSecondsSinceLastTurnAnim; // while in STOPPED state, we sometimes play turn animations
+
+ bool mUseTempWaypoint;
+ rmt::Vector mTempWaypoint;
+
+private: // METHODS
+
+private: // MEMBERS
+ rmt::Vector mDirection;
+};
+
+inline void NPCController::ClearNPCWaypoints()
+{
+ mNumNPCWaypoints = 0;
+ mCurrNPCWaypoint = -1;
+}
+
+inline float NPCController::GetSpeedMps() const
+{
+ return mSpeedMps;
+}
+inline void NPCController::SetSpeedMps( float fSpeedMps )
+{
+ mSpeedMps = fSpeedMps;
+}
+inline void NPCController::SetDirection( const rmt::Vector& inDirection )
+{
+ mDirection = inDirection;
+}
+inline float NPCController::GetValue( int buttonId ) const
+{
+ return 0.0f;
+}
+inline bool NPCController::IsButtonDown( int buttonId ) const
+{
+ return false;
+}
+
+/////////////////////////////////////////////////////////////
+
+
+
+
+class CharacterMappable;
+class tCamera;
+
+class PhysicalController
+:
+public CharacterController
+{
+public:
+ PhysicalController( void );
+ virtual ~PhysicalController( void );
+
+ virtual void GetDirection( rmt::Vector& outDirection );
+ virtual float GetValue( int buttonId ) const;
+ virtual bool IsButtonDown( int buttonId ) const;
+ virtual int TimeSinceChange( int buttonId ) const;
+ virtual void SetIntention( eIntention intention ) {};
+
+ CharacterMappable* GetCharacterMappable( void ) const;
+ void SetCharacterMappable( CharacterMappable* pMappable );
+private:
+ CharacterMappable* mpCharacterMappable;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+//
+//
+//
+// TBJ [8/9/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+
+class CameraRelativeCharacterControllerEventHandler;
+
+class CameraRelativeCharacterController
+:
+public PhysicalController
+{
+public:
+
+ CameraRelativeCharacterController( void );
+ virtual ~CameraRelativeCharacterController( void );
+
+ void Create( Character* pCharacter, CharacterMappable* pCharacterMappable );
+ void GetDirection( rmt::Vector& outDirection );
+ virtual void SetIntention( eIntention intention );
+
+ void SetCamera( tCamera* pCamera );
+
+
+protected:
+ friend class CameraRelativeCharacterControllerEventHandler;
+ void HandleEvent( int id, void* pEventData );
+private:
+
+ // Don't much like this pointer here.
+ //
+ tCamera* mpCamera;
+ CameraRelativeCharacterControllerEventHandler* mpEventHandler;
+ bool mbCameraChange;
+ rmt::Vector mLastDirection;
+ rmt::Matrix mLastCameraMatrix;
+};
+
+#endif // CHARACTERCONTROLLER_H_
diff --git a/game/code/worldsim/character/charactermanager.cpp b/game/code/worldsim/character/charactermanager.cpp
new file mode 100644
index 0000000..e5f003b
--- /dev/null
+++ b/game/code/worldsim/character/charactermanager.cpp
@@ -0,0 +1,2488 @@
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/characterrenderable.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <ai/actionbuttonmanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <meta/triggervolume.h>
+#include <meta/carstartlocator.h>
+
+#include <camera/supercamcentral.h>
+#include <camera/supercammanager.h>
+
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <p3d/anim/drawablepose.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/shadow.hpp>
+
+// Choreo includes.
+//
+#include <p3d/anim/pose.hpp>
+#include <choreo/bank.hpp>
+#include <choreo/load.hpp>
+#include <choreo/puppet.hpp>
+#include <choreo/utility.hpp>
+#include <simcommon/simstate.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simcollision/collisionobject.hpp>
+
+#include <console/console.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+// Temp hack to get NPCs in.
+// TBJ [9/3/2002]
+//
+#include <mission/gameplaymanager.h>
+#include <constants/maxplayers.h>
+
+#include <stdlib.h>
+#include <debug/profiler.h>
+#include <worldsim/avatarmanager.h>
+
+#include <radtime.hpp>
+#include <p3d/anim/skeleton.hpp>
+
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/action.h>
+#include <ai/sequencer/sequencer.h>
+#include <presentation/blinker.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+CharacterManager* CharacterManager::spCharacterManager = 0;
+
+/************ OUTPUT_TIMES FOR DLOP **********/
+//#define OUTPUT_TIMES
+/**********************************************/
+
+// Init the character tunables.
+//
+// Statics.
+//
+float CharacterTune::sfLocoRotateRate;
+float CharacterTune::sfLocoAcceleration;
+float CharacterTune::sfLocoDecceleration;
+bool CharacterTune::bLocoTest;
+float CharacterTune::sfAirRotateRate;
+float CharacterTune::sfAirAccelScale;
+float CharacterTune::sfAirGravity;
+float CharacterTune::sfStompGravityScale;
+float CharacterTune::sfDashBurstMax;
+float CharacterTune::sfDashAcceleration;
+float CharacterTune::sfDashDeceleration;
+float CharacterTune::sfJumpHeight;
+float CharacterTune::sfDoubleJumpHeight;
+float CharacterTune::sfDoubleJumpAllowUp;
+float CharacterTune::sfDoubleJumpAllowDown;
+
+float CharacterTune::sfHighJumpHeight;
+float CharacterTune::sfMaxSpeed;
+float CharacterTune::sfTurboRotateRate;
+
+float CharacterTune::sfGetInOutOfCarAnimSpeed;
+rmt::Vector CharacterTune::sGetInPosition;
+float CharacterTune::sGetInHeightThreshold;
+float CharacterTune::sGetInOpenDelay;
+float CharacterTune::sGetInOpenSpeed;
+float CharacterTune::sGetInCloseDelay;
+float CharacterTune::sGetInCloseSpeed;
+float CharacterTune::sGetOutOpenDelay;
+float CharacterTune::sGetOutOpenSpeed;
+float CharacterTune::sGetOutCloseDelay;
+float CharacterTune::sGetOutCloseSpeed;
+float CharacterTune::sfKickingForce;
+float CharacterTune::sfSlamForce;
+// How long in seconds that the character will get shocked for
+float CharacterTune::sfShockTime;
+
+bool CharacterManager::sbFixedSimRate;
+
+static const unsigned INVALID_LOAD = 0xffffffff;
+
+Blinker g_Blinkers[ 64 ]; //should match max_characters
+
+/*
+==============================================================================
+CharacterManager::CreateInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterManager
+
+=============================================================================
+*/
+CharacterManager* CharacterManager::CreateInstance( void )
+{
+ rAssertMsg( spCharacterManager == 0, "CharacterManager already created.\n" );
+#ifdef RAD_GAMECUBE
+ spCharacterManager = new ( GMA_GC_VMM ) CharacterManager;
+#else
+ spCharacterManager = new ( GMA_PERSISTENT ) CharacterManager;
+#endif
+
+ return( spCharacterManager );
+}
+/*
+==============================================================================
+CharacterManager::GetInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterManager
+
+=============================================================================
+*/
+CharacterManager* CharacterManager::GetInstance( void )
+{
+ rAssertMsg( spCharacterManager != 0, "CharacterManager has not been created yet.\n" );
+ return spCharacterManager;
+}
+/*
+==============================================================================
+CharacterManager::DestroyInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::DestroyInstance( void )
+{
+ rAssertMsg( spCharacterManager != 0, "CharacterManager has not been created.\n" );
+ delete ( GMA_PERSISTENT, spCharacterManager );
+}
+
+/*
+==============================================================================
+CharacterManager::CharacterManager
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterManager
+
+=============================================================================
+*/
+CharacterManager::CharacterManager( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ mpCharacter[ i ] = 0;
+ mGarbage[i] = false;
+ }
+
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::CAR_DOOR ) );
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::BOUNCEPAD ) );
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT ) );
+ GetEventManager()->AddListener( this, EVENT_DEATH_VOLUME_SCREEN_BLANK );
+ GetEventManager()->AddListener( this, EVENT_STAGE_COMPLETE );
+ GetEventManager()->AddListener( this, EVENT_MISSION_SUCCESS );
+ GetEventManager()->AddListener( this, EVENT_CARD_COLLECTED );
+ GetEventManager()->AddListener( this, EVENT_MISSION_CHARACTER_RESET );
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_TOGGLE_FIRSTPERSON );
+
+ CharacterTune::sfLocoRotateRate = 10.0f;
+ CharacterTune::sfLocoAcceleration = 20.0f;
+ CharacterTune::sfLocoDecceleration = -10.0f;
+
+ CharacterTune::bLocoTest = false;
+
+ CharacterTune::sfAirRotateRate = 4.0f;
+ CharacterTune::sfAirAccelScale = 0.078f; //was .1
+ CharacterTune::sfAirGravity = -25.0f; //was -30
+ CharacterTune::sfStompGravityScale = 3.22f;
+ CharacterTune::sfJumpHeight = 1.9f; //was 1.65
+ CharacterTune::sfDoubleJumpHeight = 1.0f;
+ CharacterTune::sfDoubleJumpAllowUp = 2.0f;
+ CharacterTune::sfDoubleJumpAllowDown = 12.0f; //was 6
+ CharacterTune::sfHighJumpHeight = 1.9f;
+
+ CharacterTune::sfDashBurstMax = 4.0f; // 7.0
+ CharacterTune::sfDashAcceleration = 200.0f;
+ CharacterTune::sfDashDeceleration = 200.0f;
+ CharacterTune::sfMaxSpeed = 4.0f;
+ CharacterTune::sGetInPosition.Set( 1.05f, 0.0f, -0.86f );
+ CharacterTune::sGetInHeightThreshold = 0.1f;
+ CharacterTune::sGetInOpenDelay = 0.0f;
+ CharacterTune::sGetInOpenSpeed = 0.2f;
+ CharacterTune::sGetInCloseDelay = 0.0f;
+ CharacterTune::sGetInCloseSpeed = 0.2f;
+ CharacterTune::sGetOutOpenDelay = 0.0f;
+ CharacterTune::sGetOutOpenSpeed = 0.2f;
+ CharacterTune::sGetOutCloseDelay = 0.0f;
+ CharacterTune::sGetOutCloseSpeed = 0.2f;
+
+ CharacterTune::sfTurboRotateRate = 2.0f; // 1.2
+
+ CharacterTune::sfGetInOutOfCarAnimSpeed = 30.0f;
+ CharacterTune::sfKickingForce = 400.0f;
+ CharacterTune::sfSlamForce = 800.0f;
+
+ CharacterManager::sbFixedSimRate = false;
+
+ Console* pConsole = GetConsole( );
+ rAssert( pConsole );
+ if ( pConsole )
+ {
+ pConsole->AddFunction( "SetCharacterPosition", CharacterManager::SetCharacterPosition, "Sets the character position", 3, 3 );
+ pConsole->AddFunction( "ResetCharacter", CharacterManager::ResetCharacter, "Sets the character to the named locator", 2, 2 );
+ pConsole->AddFunction( "AddTeleportDest", AddTeleportDest, "Set a valid location for a teleport", 3, 5 );
+
+ pConsole->AddFunction( "SetInitialWalk", SetInitialWalk, "Set locator to walk to on startup", 1, 1 );
+ }
+#ifdef DEBUGWATCH
+ const int MAX_LEN = 64;
+ char debugName[ MAX_LEN ];
+ int len = sprintf( debugName, "Character" );
+ rAssert( len < MAX_LEN );
+
+ radDbgWatchAddBoolean(&CharacterTune::bLocoTest, "Steer Facing", debugName, NULL, 0 );
+ radDbgWatchAddFloat(&CharacterTune::sfLocoRotateRate, "Loco Turning Rate", debugName, NULL, 0, 0.1f, 50.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfLocoAcceleration, "Loco Acceleration", debugName, NULL, 0, 0.1f, 50.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfLocoDecceleration, "Loco Deceleration", debugName, NULL, 0, -50.0f, -0.1f);
+
+ radDbgWatchAddFloat(&CharacterTune::sfAirRotateRate, "Air Turning Rate", debugName, NULL, 0, 0.0f, 20.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfAirAccelScale, "Air Acceleration", debugName, NULL, 0, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfAirGravity, "Air Gravity", debugName, NULL, 0, -100.0f, 0.1f );
+ radDbgWatchAddFloat(&CharacterTune::sfStompGravityScale, "Stomp Gravity Scale", debugName, NULL, 0, 0.1f, 4.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfJumpHeight, "Jump Height (Tap)", debugName, NULL, 0, 0.0f, 100.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpHeight, "Jump Height (Double Jump)", debugName, NULL, 0, 0.0f, 100.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpAllowUp, "Double Jump Sweet Spot (Up)", debugName, NULL, 0, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpAllowDown, "Double Jump Sweet Spot (Down)", debugName, NULL, 0, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfHighJumpHeight, "Jump Height (Full Press)", debugName, NULL, 0, 0.0f, 100.0f );
+
+ radDbgWatchAddFloat(&CharacterTune::sfDashAcceleration, "Dash Accel", debugName, NULL, 0, 0.0f, 200.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDashDeceleration, "Dash Decel", debugName, NULL, 0, 0.0f, 200.0f );
+
+ radDbgWatchAddFloat(&CharacterTune::sfMaxSpeed, "Max Speed", debugName, NULL, 0, 0.0f, 8.0f );
+
+ radDbgWatchAddFloat((float*)&CharacterTune::sGetInPosition.x, "Get In Offset X", "Get In/Get Out", NULL, 0, 0.0f, 3.0f );
+ radDbgWatchAddFloat((float*)&CharacterTune::sGetInPosition.z, "Get In Offset Z", "Get In/Get Out", NULL, 0, -1.5f, 1.5f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInHeightThreshold, "Height Threshold", "Get In/Get Out", NULL, 0, -2.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfGetInOutOfCarAnimSpeed, "Speed", "Get In/Get Out", NULL, 0, 15.0f, 240.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInOpenDelay, "Get In Open Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInOpenSpeed, "Get In Open Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInCloseDelay, "Get In Close Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInCloseSpeed, "Get In Close Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutOpenDelay, "Get Out Open Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutOpenSpeed, "Get Out Open Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutCloseDelay, "Get Out Close Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutCloseSpeed, "Get Out Close Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+
+ radDbgWatchAddBoolean( &CharacterManager::sbFixedSimRate, "Fixed sim rate", debugName, NULL, 0 );
+
+ radDbgWatchAddFloat(&CharacterTune::sfDashBurstMax, "Turbo Speed", debugName, NULL, 0, 0.0f, 100.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfTurboRotateRate, "Turbo Rotate Rate", debugName, NULL, 0, 0.0f, 10.0f );
+
+ radDbgWatchAddString(sCharacterToSpawn, 64, "NPC to Spawn", "CharacterManager");
+ radDbgWatchAddFunction( "Spawn NPC", &Spawn, NULL, "CharacterManager");
+
+ radDbgWatchAddFunction( "Next Skin", &NextSkin, NULL, "CharacterManager");
+ radDbgWatchAddFloat(&CharacterTune::sfKickingForce, "Kicking Force", debugName, NULL, 0, 0.0f, 2000.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfSlamForce, "Stomping Force", debugName, NULL, 0, 0.0f, 4000.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfShockTime, "Time(sec) to be shocked",debugName , NULL, NULL ,0 , 10.0f);
+
+#ifdef RAD_XBOX
+ extern float g_XBoxMipmapBias;
+ radDbgWatchAddFloat(&g_XBoxMipmapBias, "Mipmap Bias", "XBox", NULL, NULL , -10.0f , 10.0f);
+#endif
+
+#endif //DEBUGWATCH
+
+ strcpy(mDummyLoadData.modelName, "npd");
+ strcpy(mDummyLoadData.animName, "npd");
+ FillLoadData( mDummyLoadData, "npd", "npd");
+
+ mGarbageCollect = false;
+
+ mUniversalPose = new tPose(64);
+ mUniversalPose->AddRef();
+
+ mNumCharactersAdded = 0;
+}
+
+CharacterManager::~CharacterManager( void )
+{
+ Destroy(true);
+ tRefCounted::Release(mUniversalPose);
+ GetEventManager()->RemoveAll( this );
+}
+
+void CharacterManager::PreLoad( void )
+{
+ // Load the global textures
+ char fname[128];
+ sprintf(fname, "art\\chars\\global.p3d");
+ char section[128];
+ sprintf(section, "Eakkachaichanvet");
+
+ p3d::inventory->AddSection(section);
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, fname, GMA_LEVEL_OTHER, section );
+
+ // Load in a dummy, lightweight model
+ LoadModel("nps");
+ LoadModel("ndr");
+ LoadModel("npd");
+
+ // these animation banks are loaded all the time
+ LoadAnimation("nps");
+ LoadAnimation("ndr");
+ LoadAnimation("npd");
+}
+
+
+void CharacterManager::Destroy(bool permenant)
+{
+ mUniversalPose->SetSkeleton(NULL);
+
+ ClearTeleportDests();
+
+ if(permenant)
+ {
+ p3d::inventory->RemoveSectionElements( "Eakkachaichanvet" );
+ p3d::inventory->DeleteSection( "Eakkachaichanvet" );
+ }
+
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ g_Blinkers[ i ].SetCharacter( NULL );
+ if ( mpCharacter[ i ] != 0 )
+ {
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(mpCharacter[i]);
+ mpCharacter[ i ]->RemoveFromWorldScene( );
+ mpCharacter[ i ]->ClearAllActionButtonHandlers();
+ mpCharacter[ i ]->SetManaged(false);
+ mpCharacter[ i ]->Release();
+ mpCharacter[ i ] = 0;
+ }
+
+ // Clean out the load data names, to make sure we clean up all memory in use
+ //
+ mCharacterLoadData[ i ].modelSection.SetText (0);
+ mCharacterLoadData[ i ].animSection.SetText (0);
+ mCharacterLoadData[ i ].animModelSection.SetText (0);
+ mCharacterLoadData[ i ].mModelHigh.SetText (0);
+ mCharacterLoadData[ i ].mModelMedium.SetText (0);
+ mCharacterLoadData[ i ].mModelLow.SetText (0);
+ mCharacterLoadData[ i ].mChoreoName.SetText (0);
+ }
+
+ for ( i = permenant ? 0 : 3; i < MAX_LOADS; i++ )
+ {
+ rAssert(mModelData[i].state != LOADING);
+ rAssert(mAnimData[i].state != LOADING);
+
+ if(mModelData[i].state == LOADED || mModelData[i].state == GARBAGE )
+ {
+ p3d::inventory->RemoveSectionElements(mModelData[i].section);
+ p3d::inventory->DeleteSection(mModelData[i].section);
+ }
+
+ if(mAnimData[i].state == LOADED || mAnimData[i].state == GARBAGE )
+ {
+ p3d::inventory->RemoveSectionElements(mAnimData[i].section);
+ p3d::inventory->DeleteSection(mAnimData[i].section);
+ }
+
+ mModelData[i].state = NOT_LOADED;
+ mAnimData[i].state = NOT_LOADED;
+ }
+
+ mNumCharactersAdded = 0;
+}
+
+void CharacterManager::RemoveCharacter( Character* c)
+{
+ rAssert(c);
+ if(c)
+ {
+ for ( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == c )
+ {
+ if(mpCharacter[ i ]->GetRole() == Character::ROLE_COMPLETED_BONUS)
+ {
+ GetVehicleCentral()->RemoveSuppressedDriver(this->mRealModelNames[i]);
+ }
+
+ // if you get this assert, please leave it running and go get Nigel or Dusit
+ rAssert(mpCharacter[ i ]->GetRole() != Character::ROLE_PEDESTRIAN);
+
+ g_Blinkers[ i ].SetCharacter( NULL );
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(mpCharacter[i]);
+ mpCharacter[ i ]->RemoveFromWorldScene( );
+ GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mpCharacter[ i ]);
+ mpCharacter[ i ]->SetManaged(false);
+ mpCharacter[ i ] = 0;
+
+ GarbageCollectModel(mCharacterModelData[i]);
+ GarbageCollectAnim(mCharacterAnimData[i]);
+ return;
+ }
+ }
+ }
+}
+
+
+void CharacterManager::SetGarbage(Character* c, bool garbage)
+{
+ for( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == c)
+ {
+ mGarbage[i] = garbage;
+ }
+ }
+}
+
+void CharacterManager::GarbageCollectModel(unsigned modelIndex)
+{
+ bool modelUsed = (modelIndex < 3); // first three animation are npd, ndr, nps, don't garbage colelct
+
+ for ( int j = 0; (j < MAX_CHARACTERS) && !modelUsed; j++ )
+ {
+ if(mpCharacter[j])
+ {
+ modelUsed |= modelIndex == mCharacterModelData[j];
+ }
+ }
+
+ if(!modelUsed)
+ {
+ if(mModelData[modelIndex].state == LOADING)
+ {
+ mModelData[modelIndex].state = LOADING_GARBAGE;
+ mModelData[modelIndex].gracePeriod = 3.0f;
+ }
+ else
+ {
+ mModelData[modelIndex].state = GARBAGE;
+ mModelData[modelIndex].gracePeriod = 3.0f;
+ }
+ }
+}
+
+void CharacterManager::GarbageCollectAnim(unsigned animIndex)
+{
+ bool animUsed = (animIndex < 3); // first three animation are npd, ndr, nps, don't garbage colelct
+
+ for ( int j = 0; (j < MAX_CHARACTERS) && !animUsed; j++ )
+ {
+ if(mpCharacter[j])
+ {
+ animUsed |= animIndex == mCharacterAnimData[j];
+ }
+ }
+
+ if(!animUsed)
+ {
+ if(mAnimData[animIndex].state == LOADING)
+ {
+ mAnimData[animIndex].state = LOADING_GARBAGE;
+ }
+ else
+ {
+ mAnimData[animIndex].state = GARBAGE;
+ }
+
+ }
+}
+
+bool CharacterManager::IsModelLoaded(const char* name)
+{
+ unsigned index = FindLoad(mModelData, tEntity::MakeUID(name));
+ if( index != INVALID_LOAD && mModelData[index].state == LOADED )
+ {
+ return true;
+ }
+ return false;
+
+ //return FindLoad(mModelData, tEntity::MakeUID(name)) != INVALID_LOAD;
+}
+
+bool CharacterManager::IsAnimLoaded(const char* name)
+{
+ unsigned index = FindLoad(mAnimData, tEntity::MakeUID(name));
+ if( index != INVALID_LOAD && mAnimData[index].state == LOADED )
+ {
+ return true;
+ }
+ return false;
+
+ //return FindLoad(mAnimData, tEntity::MakeUID(name)) != INVALID_LOAD;
+}
+
+unsigned CharacterManager::LoadModel(const char* model, LoadingManager::ProcessRequestsCallback* callback, void* userData)
+{
+ tUID uid = tEntity::MakeUID(model);
+ unsigned loadIndex = AllocLoad(mModelData, uid);
+
+
+ if(mModelData[loadIndex].state == GARBAGE)
+ {
+ mModelData[loadIndex].state = LOADED;
+ }
+
+ if(mModelData[loadIndex].state == LOADING_GARBAGE)
+ {
+ mModelData[loadIndex].state = LOADING;
+ }
+
+ if((mModelData[loadIndex].state == LOADED) && callback)
+ {
+ callback->OnProcessRequestsComplete(userData);
+ }
+ else
+ {
+ mModelData[loadIndex].callback = callback;
+ mModelData[loadIndex].userData = userData;
+ }
+
+ if(mModelData[loadIndex].state == NOT_LOADED)
+ {
+ char truncModel[ 32 ];
+
+ // filenames are truncated at 6 characters
+ strcpy(truncModel, model);
+ truncModel[6] = 0;
+
+ char modelName[128];
+ sprintf(modelName, "art\\chars\\%s_m.p3d", truncModel);
+ char modelSection[128];
+ sprintf(modelSection, "%s_m", model);
+
+ mModelData[loadIndex].name = uid;
+ mModelData[loadIndex].section = tEntity::MakeUID(modelSection);
+ mModelData[loadIndex].state = LOADING;
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ p3d::inventory->AddSection(modelSection);
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, modelName, GMA_CHARS_AND_GAGS, modelSection);
+ GetLoadingManager()->AddCallback(this, (void*)loadIndex);
+ }
+
+ return loadIndex;
+}
+
+unsigned CharacterManager::LoadAnimation(const char* anim)
+{
+ tUID uid = tEntity::MakeUID(anim);
+ unsigned loadIndex = AllocLoad(mAnimData, uid);
+
+ if(mAnimData[loadIndex].state == GARBAGE)
+ {
+ mAnimData[loadIndex].state = LOADED;
+ }
+
+ if(mAnimData[loadIndex].state == LOADING_GARBAGE)
+ {
+ mAnimData[loadIndex].state = LOADING;
+ }
+
+ if(mAnimData[loadIndex].state == NOT_LOADED)
+ {
+ char truncAnim[ 32 ];
+
+ // filenames are truncated at 6 characters
+ strcpy(truncAnim, anim);
+ truncAnim[6] = 0;
+
+ char animName[128];
+ char choreoName[128];
+ sprintf(animName, "art\\chars\\%s_a.p3d", truncAnim);
+ sprintf(choreoName, "art\\chars\\%s.cho", anim);
+ char animSection[128];
+ sprintf(animSection, "%s_a", anim);
+
+ mAnimData[loadIndex].name = uid;
+ mAnimData[loadIndex].section = tEntity::MakeUID(animSection);
+ mAnimData[loadIndex].state = LOADING;
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ p3d::inventory->AddSection(animSection);
+ HeapMgr()->PopHeap( GMA_TEMP );
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, animName, GMA_LEVEL_OTHER, animSection);
+ GetLoadingManager()->AddRequest( FILEHANDLER_CHOREO, choreoName, GMA_LEVEL_OTHER, animSection);
+ GetLoadingManager()->AddCallback(this, (void*)(0x10000000 | loadIndex));
+ }
+
+ return loadIndex;
+}
+
+unsigned CharacterManager::FindLoad(Load* loads, tUID name)
+{
+ for(int i = 0; i < MAX_LOADS; i++)
+ {
+ if(loads[i].name == name)
+ {
+ return i;
+ }
+ }
+
+ return INVALID_LOAD;
+}
+
+unsigned CharacterManager::AllocLoad(Load* loads, tUID name)
+{
+ unsigned index = FindLoad(loads, name);
+
+ if(index == INVALID_LOAD)
+ {
+ for(int i = 0; i < MAX_LOADS; i++)
+ {
+ if(loads[i].state == NOT_LOADED)
+ {
+ loads[i].name = name;
+ return i;
+ }
+ }
+ }
+ else
+ {
+ return index;
+ }
+
+ rAssertMsg(0, "Can't load any more character data");
+ return INVALID_LOAD;
+}
+
+CharacterManager::LoadState CharacterManager::GetState(Load* loads, tUID name)
+{
+ unsigned index = FindLoad(loads, name);
+ if(index != INVALID_LOAD)
+ {
+ return loads[index].state;
+ }
+ return NOT_LOADED;
+}
+
+void CharacterManager::FillLoadData( CharacterLoadData& data, const char* useModel, const char* useAnim)
+{
+ const int MAX_LEN = 32;
+ char modelName[ MAX_LEN ];
+
+ int len = sprintf( modelName, "%s_h", useModel);
+ rAssert( len < MAX_LEN );
+ data.mModelHigh.SetText( modelName );
+
+ len = sprintf( modelName, "%s_m", useModel );
+ rAssert( len < MAX_LEN );
+ data.mModelMedium.SetText( modelName );
+
+ len = sprintf( modelName, "%s_l", useModel );
+ rAssert( len < MAX_LEN );
+ data.mModelLow.SetText( modelName );
+
+ data.mChoreoName.SetText( useAnim );
+
+ char sec[256];
+ sprintf(sec, "%s_m", useModel);
+ data.modelSection.SetText(sec);
+ sprintf(sec, "%s_a", useAnim);
+ data.animSection.SetText(sec);
+ sprintf(sec, "%s_m", useAnim);
+ data.animModelSection.SetText(sec);
+}
+
+Character* CharacterManager::AddCharacter( CharacterType type, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location)
+{
+ unsigned int addCharTime = radTimeGetMicroseconds();
+
+
+ unsigned int MakeUIDTime = radTimeGetMicroseconds();
+ tUID modelUID = tEntity::MakeUID(modelName);
+ tUID animUID = tEntity::MakeUID(choreoPuppet);
+ MakeUIDTime = radTimeGetMicroseconds() - MakeUIDTime;
+
+ Character* character;
+
+
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PushHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+// #endif
+
+ unsigned int NewTime = radTimeGetMicroseconds();
+ if(type == PC)
+ {
+ if( (int)mNumCharactersAdded >= GetGameplayManager()->GetNumPlayers() )
+ {
+ rReleasePrintf("Tried to add too many PCs, not supported right now. Check level scrips for multiple AddCharacter calls.\n");
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+// #endif
+ return NULL;
+ }
+ MEMTRACK_PUSH_GROUP( "CharacterManager - Add PC" );
+ character = new Character;
+ character->mbAllowUnload = false;
+ //character->SetSimpleShadow( false );
+ }
+ else
+ {
+ rAssert( mNumCharactersAdded == (unsigned int)GetGameplayManager()->GetNumPlayers() );
+
+ MEMTRACK_PUSH_GROUP( "CharacterManager - Add NPC" );
+ character = new NPCharacter;
+ }
+
+ if((strcmp(modelName, "lisa") == 0) || (strncmp(modelName, "l_", 2) == 0))
+ {
+ character->SetIsLisa(true);
+ }
+
+ if((strcmp(modelName, "marge") == 0) || (strncmp(modelName, "m_", 2) == 0))
+ {
+ character->SetIsMarge(true);
+ }
+
+ rAssert( character );
+ NewTime = radTimeGetMicroseconds() - NewTime;
+
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+// #endif
+
+ unsigned int SetNameTime = radTimeGetMicroseconds();
+ character->SetName( characterName );
+ SetNameTime = radTimeGetMicroseconds() - SetNameTime;
+
+
+ unsigned int AddCharacterTime = radTimeGetMicroseconds();
+
+ int characterIndex = AddCharacter( character, type );
+ strcpy(mRealModelNames[ characterIndex ], modelName);
+ strcpy(mRealAnimNames[ characterIndex ], choreoPuppet);
+ mLoaded[characterIndex] = true;
+ mGarbage[characterIndex] = false;
+
+ if ( type == PC )
+ {
+ rAssert( characterIndex == (int)mNumCharactersAdded );
+ ++mNumCharactersAdded;
+ }
+
+ AddCharacterTime = radTimeGetMicroseconds() - AddCharacterTime;
+
+ unsigned int FillLoadTime = radTimeGetMicroseconds();
+ strcpy(mCharacterLoadData[characterIndex].modelName, modelName);
+ strcpy(mCharacterLoadData[characterIndex].animName, choreoPuppet);
+
+ FillLoadData( mCharacterLoadData[characterIndex], modelName, choreoPuppet);
+ FillLoadTime = radTimeGetMicroseconds() - FillLoadTime;
+
+ unsigned int LoadModelTime = 0;
+ unsigned int SetupCharacterTime;
+
+ LoadModelTime = radTimeGetMicroseconds();
+ mCharacterModelData[characterIndex] = LoadModel(modelName);
+ mCharacterAnimData[characterIndex] = LoadAnimation(choreoPuppet);
+ LoadModelTime = radTimeGetMicroseconds() - LoadModelTime;
+
+ if( (GetState(mAnimData, animUID) == LOADED) && (GetState(mModelData, modelUID) == LOADED) )
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ SetupCharacter( mCharacterLoadData[characterIndex], character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+ else
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ SetupCharacter( mDummyLoadData, character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+
+ unsigned int InitTime = radTimeGetMicroseconds();
+ character->Init();
+ InitTime = radTimeGetMicroseconds() - InitTime;
+
+ if(type == NPC)
+ {
+ // Dusit [Oct 22, 2002]:
+ // Don't solve collisions for NPCs.. They just sit there...
+ // We'll enable solve collisions as needed...
+ character->SetSolveCollisions( false );
+ }
+ else
+ {
+ // PCs get immediatly added to scene, NPCs might not be visible (i.e pedestrians)
+ character->AddToWorldScene();
+ }
+
+ if(location)
+ {
+ //Set the initial character position.
+ Locator* loc = p3d::find<Locator>( location );
+ CarStartLocator* cloc = dynamic_cast<CarStartLocator*>( loc );
+
+ rmt::Vector position;
+ if ( cloc )
+ {
+ cloc->GetLocation( &position );
+ character->RelocateAndReset( position, cloc->GetRotation() );
+ }
+ else if ( loc )
+ {
+ rmt::Vector position;
+ loc->GetLocation( &position );
+ character->RelocateAndReset( position, 0 );
+ }
+ else
+ {
+ rDebugPrintf( "Couldn't find locator \"%s\" for %s\n", location, characterName );
+ }
+
+ // if NPC, add the locator as the first waypoint of this character
+ if( loc && type == NPC )
+ {
+ // the controller should have been created in NPC constructor
+ NPCController* npcController = (NPCController*) character->GetController();
+ rAssert( npcController );
+
+ bool res = npcController->AddNPCWaypoint( position );
+ rAssert( res );
+ }
+ }
+ addCharTime = radTimeGetMicroseconds() - addCharTime;
+
+#ifdef OUTPUT_TIMES
+ rReleasePrintf("AddCharacter %s Time %d\n",characterName, addCharTime);
+ rReleasePrintf(" AddCharacter MakeUIDTime %s Time %d\n",characterName, MakeUIDTime);
+ rReleasePrintf(" AddCharacter NewTime %s Time %d\n",characterName, NewTime);
+ rReleasePrintf(" AddCharacter SetNameTime %s Time %d\n",characterName, SetNameTime);
+ rReleasePrintf(" AddCharacter AddCharacterTime %s Time %d\n",characterName, AddCharacterTime);
+ rReleasePrintf(" AddCharacter FillLoadTime %s Time %d\n",characterName, FillLoadTime);
+ rReleasePrintf(" AddCharacter LoadModelTime %s Time %d\n",characterName, LoadModelTime);
+ rReleasePrintf(" AddCharacter SetupCharacterTime %s Time %d\n",characterName, SetupCharacterTime);
+ rReleasePrintf(" AddCharacter InitTime %s Time %d\n",characterName, InitTime);
+#endif
+ if(type == PC)
+ {
+ MEMTRACK_POP_GROUP( "CharacterManager - Add PC" );
+ }
+ else
+ {
+ MEMTRACK_POP_GROUP( "CharacterManager - Add NPC" );
+ }
+
+ if(type == PC && GetGameplayManager()->IsSuperSprint() == false && GetGameplayManager()->mIsDemo == false)
+ {
+ //check to see if there is a valid skin that is not called NULL
+ if(strcmp(GetCharacterSheetManager()->QueryCurrentSkin(GetGameplayManager()->GetCurrentLevelIndex()),"NULL") != 0)
+ {
+ SwapData(character, GetCharacterSheetManager()->QueryCurrentSkin(GetGameplayManager()->GetCurrentLevelIndex()), GetAnimName(character));
+ }
+ }
+
+ return character;
+}
+
+Character* CharacterManager::AddCharacterDeferedLoad( CharacterType type, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location)
+{
+ Character* c = AddCharacter(type, characterName, "npd", "npd", location);
+
+ unsigned characterIndex = INVALID_LOAD;
+
+ for ( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == c )
+ {
+ characterIndex = i;
+ break;
+ }
+ }
+
+ rAssert(characterIndex != INVALID_LOAD);
+
+ strcpy(mRealModelNames[ characterIndex ], modelName);
+ strcpy(mRealAnimNames[ characterIndex ], choreoPuppet);
+ mLoaded[characterIndex] = false;
+
+ return c;
+}
+
+void CharacterManager::SwapData(Character* character, const char* modelName, const char* choreoPuppet)
+{
+ unsigned index = InternalSwapData(character, modelName, choreoPuppet);
+ rAssert(index != INVALID_LOAD);
+ strcpy(mRealModelNames[ index ], modelName);
+ strcpy(mRealAnimNames[ index ], choreoPuppet);
+}
+
+
+unsigned CharacterManager::InternalSwapData(Character* character, const char* modelName, const char* choreoPuppet)
+{
+ unsigned int SwapDataTime = radTimeGetMicroseconds();
+
+
+ unsigned characterIndex = INVALID_LOAD;
+
+ for ( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == character )
+ {
+ characterIndex = i;
+ break;
+ }
+ }
+
+ rAssert(characterIndex != INVALID_LOAD);
+
+ tUID modelUID;
+ tUID animUID;
+
+ unsigned modelIndex = mCharacterModelData[characterIndex];
+ unsigned animIndex = mCharacterAnimData[characterIndex];
+
+ if(modelName)
+ {
+ modelUID = tEntity::MakeUID(modelName);
+ strcpy(mCharacterLoadData[characterIndex].modelName, modelName);
+ }
+ else
+ {
+ modelUID = tEntity::MakeUID(mCharacterLoadData[characterIndex].modelName);
+ }
+
+ if(choreoPuppet)
+ {
+ animUID = tEntity::MakeUID(choreoPuppet);
+ strcpy(mCharacterLoadData[characterIndex].animName, choreoPuppet);
+ }
+ else
+ {
+ animUID = tEntity::MakeUID(mCharacterLoadData[characterIndex].animName);
+ }
+
+
+ unsigned int FillLoadTime = radTimeGetMicroseconds();
+ FillLoadData( mCharacterLoadData[characterIndex], mCharacterLoadData[characterIndex].modelName, mCharacterLoadData[characterIndex].animName);
+ FillLoadTime = radTimeGetMicroseconds() - FillLoadTime;
+
+ unsigned int LoadModelTime = 0;
+ unsigned int SetupCharacterTime = 0;
+ unsigned int CreatePuppetTime = 0;
+
+ LoadModelTime = radTimeGetMicroseconds();
+ mCharacterModelData[characterIndex] = LoadModel(mCharacterLoadData[characterIndex].modelName);
+ mCharacterAnimData[characterIndex] = LoadAnimation(mCharacterLoadData[characterIndex].animName);
+ LoadModelTime = radTimeGetMicroseconds() - LoadModelTime;
+
+ if((GetState(mAnimData, animUID) == LOADED) && (GetState(mModelData, modelUID) == LOADED))
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ CreatePuppetTime = SetupCharacter( mCharacterLoadData[characterIndex], character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+ else
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ CreatePuppetTime = SetupCharacter( mDummyLoadData, character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+
+ unsigned int GarbageCollectTime = radTimeGetMicroseconds();
+ GarbageCollectModel(modelIndex);
+ GarbageCollectAnim(animIndex);
+ GarbageCollectTime = radTimeGetMicroseconds() - GarbageCollectTime;
+
+ SwapDataTime = radTimeGetMicroseconds() - SwapDataTime;
+
+#ifdef OUTPUT_TIMES
+ rReleasePrintf("SwapDataTime %s Time %d\n",modelName, SwapDataTime);
+ rReleasePrintf(" FillLoadTime %s Time %d\n",modelName, FillLoadTime);
+ if( LoadModelTime != 0 )
+ rReleasePrintf(" LoadModelTime %s Time %d\n",modelName, LoadModelTime);
+ rReleasePrintf(" GarbageCollectTime %s Time %d\n",modelName, GarbageCollectTime );
+ rReleasePrintf(" SetupCharacterTime %s Time %d\n",modelName, SetupCharacterTime );
+ rReleasePrintf(" CreatePuppetTime %s Time %d\n\n",modelName, CreatePuppetTime );
+#endif
+
+ return characterIndex;
+}
+
+//=============================================================================
+// CharacterManager::SetupCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CharacterLoadData& data, Character* pCharacter, bool isNPC )
+//
+// Return: void
+//
+//=============================================================================
+unsigned int CharacterManager::SetupCharacter( CharacterLoadData& data, Character* pCharacter)
+{
+ p3d::inventory->PushSection();
+
+ bool currOnly = p3d::inventory->GetCurrentSectionOnly();
+ tName curr = p3d::inventory->GetCurrentSection()->GetName();
+ p3d::inventory->SetCurrentSectionOnly(true);
+
+// #ifdef RAD_GAEMCUBE
+// HeapMgr()->PushHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+// #endif
+
+MEMTRACK_PUSH_GROUP( "CharacterManager - CharacterRenderable" );
+ p3d::inventory->SelectSection(data.modelSection.GetUID());
+
+ CharacterRenderable* pCharacterRenderable;
+ if(data.modelSection.GetUID() != tEntity::MakeUID("npd_m"))
+ {
+ pCharacterRenderable = new CharacterRenderable(
+ p3d::find<tDrawablePose>( data.mModelHigh.GetUID() ),
+ p3d::find<tDrawablePose>( data.mModelMedium.GetUID() ),
+ p3d::find<tDrawablePose>( data.mModelLow.GetUID() ));
+ pCharacterRenderable->SetSwatchShader( p3d::find<tShader>( "char_swatches_lit_m" ) );
+ }
+ else
+ {
+ pCharacterRenderable = new CharacterRenderable(NULL, NULL, NULL);
+ pCharacterRenderable->SetSwatchShader( NULL );
+ }
+
+ pCharacterRenderable->SetShadowColour( pCharacter->GetShadowColour() );
+
+ // Lets find the electrocution effect
+ p3d::inventory->SelectSection( P3D_DEFAULT_INV_SECTION );
+ p3d::inventory->SetCurrentSectionOnly( false );
+ pCharacterRenderable->SetShockEffect( p3d::find< tDrawable >( "electrocuted" ) );
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+
+ p3d::inventory->SelectSection( "Eakkachaichanvet" );
+ pCharacterRenderable->SetSwatchTexture( 0, p3d::find<tTexture>( "char_swatches_lit.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 1, p3d::find<tTexture>( "char_swatches1.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 2, p3d::find<tTexture>( "char_swatches2.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 3, p3d::find<tTexture>( "char_swatches3.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 4, p3d::find<tTexture>( "char_swatches4.bmp" ) );
+ pCharacterRenderable->SetSwatch( 0 );
+ p3d::inventory->SelectSection( data.modelSection.GetUID() );
+
+ pCharacter->SetDrawable( pCharacterRenderable );
+MEMTRACK_POP_GROUP("CharacterManager - CharacterRenderable");
+
+ tDrawablePose* pDrawablePose = pCharacterRenderable->GetDrawable();
+ tSkeleton* pSkeleton = NULL;
+
+ if(pDrawablePose)
+ {
+ pSkeleton = pDrawablePose->GetSkeleton();
+ }
+ else
+ {
+ p3d::inventory->SelectSection(data.animModelSection.GetUID());
+ pSkeleton = p3d::find<tSkeleton>( data.mChoreoName.GetUID() );
+ }
+
+ if(!pSkeleton)
+ {
+ tUID uid = tEntity::MakeUID("npd");
+ p3d::inventory->SelectSection(uid);
+ pSkeleton = p3d::find<tSkeleton>(uid);
+ }
+
+ rAssert( pSkeleton);
+
+ p3d::inventory->SelectSection(data.animSection.GetUID());
+ choreo::Bank* bank = p3d::find<choreo::Bank>( data.mChoreoName.GetUID() );
+ rAssert( bank != 0 );
+
+MEMTRACK_PUSH_GROUP( "CharacterManager - Puppet" );
+ unsigned int PuppetTime = radTimeGetMicroseconds();
+ // Make sure the skeleton to be remapped are equivalent.
+ //
+ choreo::Rig* rig = bank->GetRig();
+ rAssert( rig != 0 );
+ tSkeleton* rigSkeleton = rig->GetSkeleton( );
+ int i = 0;
+ if ( rigSkeleton->GetNumJoint() != pSkeleton->GetNumJoint() )
+ {
+ rDebugPrintf( "%s skeleton is not the same as choreo::Rig skeleton %s. Animations will be hooped.\n",
+ rigSkeleton->GetName( ), pSkeleton->GetName( ) );
+ }
+
+ mUniversalPose->SetSkeleton(pSkeleton);
+ mUniversalPose->ResetToRestPose();
+
+ pCharacter->SetInCar(false);
+ pCharacter->SetPuppet( new choreo::Puppet( mUniversalPose, bank, false, 0x20, 5 ) );
+
+ choreo::Puppet* pPuppet = pCharacter->GetPuppet();
+
+ PuppetTime = radTimeGetMicroseconds() - PuppetTime;
+
+// #ifdef RAD_GAEMCUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+// #endif
+
+MEMTRACK_POP_GROUP( "CharacterManager - Puppet" );
+
+
+ //
+ // Determine & store y offset needed for this character
+ //
+
+ float yAdjustDrawable = 0.0f, yAdjustPuppet = 0.0f;
+ if( pCharacterRenderable->GetDrawable() &&
+ pCharacterRenderable->GetDrawable()->GetSkeleton() )
+ {
+ yAdjustDrawable = pCharacterRenderable->GetDrawable()->GetSkeleton()->
+ FindJoint( tEntity::MakeUID("Balance_Root") )->worldMatrix.Row(3).y;
+ }
+ if( pPuppet && pPuppet->GetSkeleton() )
+ {
+ yAdjustPuppet = pPuppet->GetSkeleton()->
+ FindJoint( tEntity::MakeUID("Balance_Root") )->worldMatrix.Row(3).y;
+ }
+
+ pCharacter->SetYAdjust((yAdjustDrawable - yAdjustPuppet) * 0.30f);
+
+ //
+ // Setup the blinker for this character
+ //
+ unsigned int characterIndex = GetCharacterIndex( pCharacter );
+ g_Blinkers[ characterIndex ].SetCharacter( pCharacter );
+
+ p3d::inventory->SetCurrentSectionOnly(currOnly);
+ p3d::inventory->SelectSection( curr );
+
+ p3d::inventory->PopSection();
+
+ return PuppetTime;
+
+}
+
+void CharacterManager::GarbageCollect(bool ignoreDist)
+{
+ int i;
+
+ const float GARBAGE_COLLECT_DISTANCE = 100.0f;
+
+ // garbage collect
+ if(mGarbageCollect)
+ {
+ for(i = MAX_PLAYERS; i < MAX_CHARACTERS; i++)
+ {
+ if(mpCharacter[i])
+ {
+ rmt::Vector charPos;
+ rmt::Vector camPos;
+ rmt::Vector distance;
+
+ mpCharacter[i]->GetPosition(charPos);
+// camPos = GetSuperCamManager()->GetSCC(0)->GetCamera()->GetPosition();
+ GetCharacter(0)->GetPosition(camPos);
+ distance.Sub(camPos, charPos);
+
+ float fDist = rmt::Abs(distance.Magnitude());
+
+ if(fDist > 10000)
+ {
+ continue;
+ }
+
+ bool suppressLoad = false;
+
+ if(mpCharacter[i]->GetRole() == Character::ROLE_ACTIVE_BONUS)
+ {
+ if(!GetGameplayManager()->GetCurrentMission()->IsBonusMission() &&
+ !GetGameplayManager()->GetCurrentMission()->IsRaceMission() &&
+ !GetGameplayManager()->GetCurrentMission()->IsWagerMission() &&
+ !GetGameplayManager()->GetCurrentMission()->IsSundayDrive())
+ {
+ suppressLoad = true;
+ }
+ }
+
+ if(mGarbage[i])
+ {
+ if(fDist > GARBAGE_COLLECT_DISTANCE || GetGameplayManager()->IsIrisClosed() || ignoreDist )
+ {
+ if(mpCharacter[i]->GetRole() != Character::ROLE_ACTIVE_BONUS)
+ {
+ if(mpCharacter[i]->IsAmbient())
+ {
+ mGarbage[i] = false;
+ mpCharacter[i]->EnableAmbientDialogue(true);
+ mpCharacter[i]->ResetAmbientPosition();
+ }
+ else
+ {
+ RemoveCharacter(mpCharacter[i]);
+ }
+ }
+ else
+ {
+ mGarbage[i] = false;
+ }
+ }
+ }
+ else if (mLoaded[i] && (fDist > 150) && mpCharacter[i]->mbAllowUnload)
+ {
+ InternalSwapData(mpCharacter[i], "npd", "npd");
+ mLoaded[i] = false;
+ mpCharacter[i]->RemoveFromPhysics();
+ }
+ else if (!mLoaded[i] && (fDist < 150) && !suppressLoad)
+ {
+ if( !CommandLineOptions::Get( CLO_NO_PEDS ) )
+ InternalSwapData(mpCharacter[i], mRealModelNames[i], mRealAnimNames[i]);
+ mLoaded[i] = true;
+ if(!mpCharacter[i]->IsInCar())
+ {
+ mpCharacter[i]->AddToPhysics();
+ }
+ }
+ }
+
+ }
+
+ for(i = 1; i < MAX_LOADS; i++)
+ {
+ if(mModelData[i].state == GARBAGE)
+ {
+ if(mModelData[i].gracePeriod < 0.0f)
+ {
+ p3d::inventory->RemoveSectionElements(mModelData[i].section);
+ p3d::inventory->DeleteSection(mModelData[i].section);
+ mModelData[i].state = NOT_LOADED;
+ }
+ }
+
+ if(mAnimData[i].state == GARBAGE)
+ {
+ if(mAnimData[i].gracePeriod < 0.0f)
+ {
+ p3d::inventory->RemoveSectionElements(mAnimData[i].section);
+ p3d::inventory->DeleteSection(mAnimData[i].section);
+ mAnimData[i].state = NOT_LOADED;
+ }
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::PreSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PreSimUpdate( float timeins )
+{
+ int i;
+
+ for ( i = 1; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] && (mpCharacter[ i ]->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) )
+ {
+BEGIN_PROFILE("Character::PreSimUpdate")
+ mpCharacter[ i ]->PreSimUpdate( timeins );
+END_PROFILE("Character::PreSimUpdate")
+ }
+ }
+
+ for(i = 1; i < MAX_LOADS; i++)
+ {
+ if(mModelData[i].state == GARBAGE)
+ {
+ mModelData[i].gracePeriod -= timeins;
+ }
+
+ if(mAnimData[i].state == GARBAGE)
+ {
+ mAnimData[i].gracePeriod -= timeins;
+ }
+ }
+
+}
+
+/*
+==============================================================================
+CharacterManager::PostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PostSimUpdate( float timeins )
+{
+ int i;
+ for ( i = 1; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] && (mpCharacter[ i ]->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) )
+ {
+BEGIN_PROFILE("Character::PostSimUpdate")
+ mpCharacter[ i ]->PostSimUpdate( timeins );
+END_PROFILE("Character::PostSimUpdate")
+ }
+ }
+}
+
+
+/*
+==============================================================================
+CharacterManager::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::Update( float timeins )
+{
+ int i;
+
+ for( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ Character* character = mpCharacter[i];
+ if( character && (character->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) )
+ {
+ g_Blinkers[ i ].Update( static_cast< int >( timeins * 1000 ) );
+
+ int collisionAreaIndex = character->GetCollisionAreaIndex();
+ if( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ BEGIN_PROFILE("Character::UpdatePhObjects");
+ character->UpdatePhysicsObjects( timeins, collisionAreaIndex );
+ END_PROFILE("Character::UpdatePhObjects");
+ }
+
+ character->UpdateRoot( timeins );
+
+ sim::SimState* simState = character->GetSimState();
+ if( simState != NULL && simState->GetControl() == sim::simSimulationCtrl )
+ {
+ simState->GetSimulatedObject()->Update( timeins );
+ character->UpdateSimState( timeins );
+
+ sim::CollisionObject* collObj = simState->GetCollisionObject();
+ collObj->Update();
+
+ }
+ }
+ }
+
+}
+
+/*
+==============================================================================
+CharacterManager::PreSubstepUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PreSubstepUpdate( float timeins )
+{
+ for( int i=0; i<MAX_CHARACTERS; i++ )
+ {
+ if( mpCharacter[ i ] && mpCharacter[ i ]->IsInSubstep() )
+ {
+ BEGIN_PROFILE("Character::PreSimUpdate")
+ mpCharacter[ i ]->PreSimUpdate( timeins );
+ END_PROFILE("Character::PreSimUpdate")
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::PostSubstepUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PostSubstepUpdate( float timeins )
+{
+ if ( mpCharacter[ 0 ] )
+ {
+BEGIN_PROFILE("Character::PostSimUpdate")
+ mpCharacter[ 0 ]->PostSimUpdate( timeins );
+END_PROFILE("Character::PostSimUpdate")
+ }
+}
+
+
+void CharacterManager::ClearTargetVehicle(Vehicle* v)
+{
+ for (int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( NULL != mpCharacter[ i ] )
+ {
+ if(mpCharacter[ i ]->GetTargetVehicle() == v)
+ {
+ mpCharacter[ i ]->SetTargetVehicle(NULL);
+ }
+ }
+ }
+}
+
+void CharacterManager::ResetBonusCharacters(void)
+{
+ for (int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( NULL != mpCharacter[ i ] )
+ {
+ if(mpCharacter[ i ]->GetRole() == Character::ROLE_ACTIVE_BONUS)
+ {
+ ((NPCController*)(mpCharacter[ i ]->GetController()))->TeleportToPath();
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::AddCharacter
+==============================================================================
+Description: Add a character to the array. Returns true if succesfully added.
+
+Parameters: ( Character* pCharacter, int& number, bool isNPC )
+
+Return: bool
+
+=============================================================================
+*/
+int CharacterManager::AddCharacter( Character* pCharacter, CharacterType type )
+{
+ int i = 0;
+
+ // if it's an NPC, skip the first couple of entries (reserved for players)
+ if ( type == NPC )
+ {
+ i = MAX_PLAYERS;
+ }
+
+ // find an empty slot
+ for (; i < MAX_CHARACTERS; i++ )
+ {
+ if ( 0 == mpCharacter[ i ] )
+ {
+ break;
+ }
+ }
+
+ rAssertMsg(( i < MAX_CHARACTERS ), "Couldn't add character, no space in character array");
+
+ tRefCounted::Assign( mpCharacter[ i ], pCharacter );
+ mpCharacter[ i ]->SetManaged(true);
+ mGarbage[i] = false;
+ return i;
+}
+
+void CharacterManager::OnProcessRequestsComplete( void* pUserData )
+{
+ unsigned index = (unsigned)pUserData;
+
+
+ if(index & 0x10000000)
+ {
+ index &= 0x0fffffff;
+
+ if(mAnimData[index].state == LOADING)
+ {
+ mAnimData[index].state = LOADED;
+
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(mpCharacter[i] && (mCharacterAnimData[i] == index))
+ {
+ if(mModelData[mCharacterModelData[i]].state == LOADED)
+ {
+ unsigned int PuppetTime = SetupCharacter(mCharacterLoadData[i], mpCharacter[i] );
+ #ifdef OUTPUT_TIMES
+ rReleasePrintf( "A) Calling SetupCharacter from OnProcessRequestsComplete for %s took %d\n",
+ mCharacterLoadData[i].modelName, PuppetTime );
+ #endif
+ }
+ }
+ }
+ } else if(mAnimData[index].state == LOADING_GARBAGE)
+ {
+ mAnimData[index].state = GARBAGE;
+ }
+ else
+ {
+ rAssert(0);
+ }
+ }
+ else
+ {
+ if(mModelData[index].state == LOADING)
+ {
+ mModelData[index].state = LOADED;
+
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(mpCharacter[i] && (mCharacterModelData[i] == index))
+ {
+ if(mAnimData[mCharacterAnimData[i]].state == LOADED)
+ {
+ unsigned int PuppetTime = SetupCharacter(mCharacterLoadData[i], mpCharacter[i] );
+ #ifdef OUTPUT_TIMES
+ rReleasePrintf( "B) Calling SetupCharacter from OnProcessRequestsComplete for %s took %d\n",
+ mCharacterLoadData[i].modelName, PuppetTime );
+ #endif
+ }
+ }
+ }
+
+ if(mModelData[index].callback)
+ {
+ mModelData[index].callback->OnProcessRequestsComplete(mModelData[index].userData);
+ mModelData[index].callback = NULL;
+ }
+ } else if(mModelData[index].state == LOADING_GARBAGE)
+ {
+ mModelData[index].state = GARBAGE;
+ }
+ else
+ {
+ rAssert(0);
+ }
+
+ }
+}
+
+//=============================================================================
+Character* CharacterManager::GetCharacterByName( const char* name ) const
+{
+ return GetCharacterByName( tEntity::MakeUID( name ) );
+}
+
+/*
+==============================================================================
+CharacterManager::GetCharacterByName
+==============================================================================
+Description: Comment
+
+Parameters: ( const tUID uid )
+
+Return: Character
+
+=============================================================================
+*/
+Character* CharacterManager::GetCharacterByName( const tUID uid ) const
+{
+ int i;
+ for( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] != 0 && mpCharacter[ i ]->GetUID() == uid )
+ {
+ return mpCharacter[ i ];
+ }
+ }
+ return NULL;
+
+}
+
+//=============================================================================
+Character* CharacterManager::GetMissionCharacter( const char* name ) const
+{
+ Character* c = NULL;
+
+ // pick an appropriate name based on if we are a bonus mission or not
+ char n[64];
+ if(GetGameplayManager()->GetCurrentMission()->IsBonusMission())
+ {
+ sprintf(n, "b_%s", name);
+ c = GetCharacterManager()->GetCharacterByName( n );
+ }
+
+ if(!c)
+ {
+ sprintf(n, "reward_%s", name);
+ c = GetCharacterManager()->GetCharacterByName( n );
+ }
+
+ if(!c)
+ {
+ c = GetCharacterManager()->GetCharacterByName( name );
+ }
+
+ return c;
+}
+
+/*
+==============================================================================
+CharacterManager::GetCharacter
+==============================================================================
+Description: Return a pointer to the character.
+
+Parameters: ( int i )
+
+Return: Character
+
+=============================================================================
+*/
+Character* CharacterManager::GetCharacter( int i ) const
+{
+ if ( i < MAX_CHARACTERS )
+ {
+ return mpCharacter[ i ];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::HandleEvent
+==============================================================================
+Description: Comment
+
+Parameters: ( EventEnum id, void* pEventData )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_GETINTOVEHICLE_START :
+ {
+ Character* c = (Character*)pEventData;
+ if(c->GetTargetVehicle())
+ {
+ if(c->GetTargetVehicle()->mpDriver && (c->GetTargetVehicle()->mpDriver != c))
+ {
+ c->GetTargetVehicle()->mpDriver->GetController()->SetIntention(CharacterController::WaveHello);
+ }
+ }
+ }
+ break;
+ case EVENT_GETOUTOFVEHICLE_END :
+ {
+ Character* c = (Character*)pEventData;
+ if(c->GetTargetVehicle())
+ {
+ if(c->GetTargetVehicle()->mpDriver && (c->GetTargetVehicle()->mpDriver != c))
+ {
+ c->GetTargetVehicle()->mpDriver->GetController()->SetIntention(CharacterController::WaveGoodbye);
+ }
+ }
+ }
+ break;
+ case EVENT_TOGGLE_FIRSTPERSON :
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if(pEventData == 0)
+ {
+ if(GetCharacter(0)->GetActionButtonHandler())
+ {
+ if ( GetCharacter(0)->GetActionButtonHandler()->IsInstanceEnabled() )
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ else
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ break;
+
+ case EVENT_STAGE_COMPLETE :
+ {
+ if(pEventData)
+ {
+ Character* ch = GetCharacter(0);
+ ch->GetController()->SetIntention(CharacterController::CelebrateSmall);
+ }
+ }
+ break;
+
+ case EVENT_CARD_COLLECTED :
+ case EVENT_MISSION_SUCCESS :
+ {
+ Character* ch = GetCharacter(0);
+ ch->GetController()->SetIntention(CharacterController::CelebrateBig);
+ }
+ break;
+
+ case EVENT_MISSION_CHARACTER_RESET :
+ {
+ if(pEventData && (sInitialWalkLocator[0] != 0))
+ {
+ rmt::Vector dest;
+ Locator* l = p3d::find<Locator>(sInitialWalkLocator);
+
+ if(l)
+ {
+ l->GetLocation(&dest);
+
+ Character* c = GetCharacterManager()->GetCharacter(0);
+
+ if(c->GetWalkerLocomotionAction()->GetStatus() != RUNNING)
+ {
+ c->GetWalkerLocomotionAction()->SetStatus(SLEEPING);
+ }
+
+ Sequencer* pSeq = c->GetActionController()->GetNextSequencer();
+ pSeq->BeginSequence();
+ pSeq->AddAction( new Arrive( c, dest) );
+ pSeq->AddAction( c->GetWalkerLocomotionAction() );
+ pSeq->EndSequence( );
+
+ }
+ }
+ sInitialWalkLocator[0] = 0;
+ }
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::CAR_DOOR:
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ unsigned int actionId = pLocator->GetData( );
+ rAssert( actionId >= 0 );
+
+ ActionButton::GetInCar* pGetInCarAction = static_cast<ActionButton::GetInCar* >( GetActionButtonManager()->GetActionByIndex( actionId ) );
+ rAssert( dynamic_cast<ActionButton::GetInCar*>( pGetInCarAction ) );
+ rAssert( pGetInCarAction );
+
+ if ( pLocator->GetPlayerEntered() )
+ {
+
+ // Entered a door volume.
+ //
+
+#ifdef RAD_DEBUG
+ int vehicleId = pGetInCarAction->GetVehicleId();
+ Vehicle* pVehicle = GetVehicleCentral()->GetVehicle( vehicleId );
+ rAssert( pVehicle );
+#endif
+
+ pCharacter->AddActionButtonHandler( pGetInCarAction );
+ pGetInCarAction->Enter( pCharacter );
+
+ // TODO: Think about what will happen when the character is in two vols.
+ // at the same time.
+ //
+
+ }
+ else if ( !pLocator->GetPlayerEntered() )
+ {
+ // Exited a door volume.
+ //
+ pGetInCarAction->Exit( pCharacter );
+ pCharacter->RemoveActionButtonHandler( pGetInCarAction );
+ }
+ break;
+ }
+ case EVENT_LOCATOR + LocatorEvent::BOUNCEPAD:
+ {
+
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ if ( pLocator->GetPlayerEntered() )
+ {
+ rAssert( pLocator );
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ ActionButton::Bounce::OnEnter( pCharacter, pLocator );
+ }
+ break;
+ }
+ case EVENT_LOCATOR + LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT:
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+
+ if ( pLocator->GetPlayerEntered() )
+ {
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ unsigned int actionId = pLocator->GetData( );
+ rAssert( actionId >= 0 );
+
+ ActionButton::GenericEventButtonHandler* pTED = static_cast<ActionButton::GenericEventButtonHandler* >( GetActionButtonManager()->GetActionByIndex( actionId ) );
+ rAssert( dynamic_cast< ActionButton::GenericEventButtonHandler* > (pTED) );
+ rAssert( pTED );
+
+ if ( pCharacter != static_cast<Character*>(pTED->GetEventData()) )
+ {
+ pCharacter->AddActionButtonHandler( pTED );
+ pTED->Enter( pCharacter );
+ }
+ }
+ else
+ {
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ unsigned int actionId = pLocator->GetData( );
+ rAssert( actionId >= 0 );
+
+ ActionButton::GenericEventButtonHandler* pTED = static_cast<ActionButton::GenericEventButtonHandler* >( GetActionButtonManager()->GetActionByIndex( actionId ) );
+
+ if ( pTED && (pCharacter != static_cast<Character*>(pTED->GetEventData())) )
+ {
+ rAssert( dynamic_cast<ActionButton::GenericEventButtonHandler* >( pTED ) != NULL );
+ pTED->Exit( pCharacter );
+ pCharacter->RemoveActionButtonHandler( pTED );
+ }
+ }
+
+ break;
+ }
+ case EVENT_DEATH_VOLUME_SCREEN_BLANK:
+ {
+ //TODO: Make the blends go away.
+ //Also, cut the camera properly...
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ if( pLocator == NULL )
+ {
+ break;
+ }
+
+ if ( pLocator->GetPlayerID() >= static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //Ignore this!
+ break;
+ }
+
+ //Ignore when player leaves.
+ if ( pLocator->GetPlayerEntered() )
+ {
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ //Only do this if he's on foot.
+ if ( pCharacter->GetStateManager()->GetState() == CharacterAi::GET_IN)
+ {
+ GetAvatarManager()->PutCharacterInCar(pCharacter, pCharacter->GetTargetVehicle());
+ GetEventManager()->TriggerEvent(EVENT_GETINTOVEHICLE_END, pCharacter);
+ }
+ else
+ {
+ if ( pCharacter->GetStateManager()->GetState() == CharacterAi::GET_OUT )
+ {
+ GetAvatarManager()->PutCharacterOnGround( pCharacter, pCharacter->GetTargetVehicle() );
+ }
+ else if ( pCharacter->IsInCar() )
+ {
+ return;
+ }
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+ rmt::Vector facing = mat.Row(2);
+ rmt::Vector pos;
+ pLocator->GetLocation( &pos );
+
+ float fDesiredDir = choreo::GetWorldAngle( facing.x, facing.z );
+ pCharacter->RelocateAndReset( pos, fDesiredDir, true, false );
+
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+const char* CharacterManager::GetModelName(Character* c)
+{
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(c == mpCharacter[i])
+ {
+ return mRealModelNames[i];
+ }
+ }
+ return NULL;
+}
+
+const char* CharacterManager::GetAnimName(Character* c)
+{
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(c == mpCharacter[i])
+ {
+ return mRealAnimNames[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+==============================================================================
+CharacterManager::SubmitStatics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SubmitStatics( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ Character* character = mpCharacter[i];
+ if( character != NULL )
+ {
+ character->SubmitStatics();
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::SubmitAnimCollisions
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SubmitAnimCollisions( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] )
+ {
+ mpCharacter[ i ]->SubmitAnimCollisions();
+ }
+ }
+}
+
+ /*
+==============================================================================
+CharacterManager::SubmitDynamics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SubmitDynamics( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ Character* character = mpCharacter[i];
+ if( character != NULL )
+ {
+ character->SubmitDynamics();
+ }
+ }
+}
+
+
+/*
+==============================================================================
+CharacterManager::SetCharacterPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SetCharacterPosition( int argc, char** argv )
+{
+ int index = ::atoi( argv[ 1 ] );
+ Character* pCharacter = GetCharacterManager()->GetCharacter( index );
+ if ( pCharacter )
+ {
+ rmt::Vector pos;
+ pos.x = static_cast<float>( atoi( argv[ 2 ] ) );
+ pos.y = 0.0f;
+ pos.z = static_cast<float>( atoi( argv[ 3 ] ) );
+ pCharacter->SetPosition( pos );
+ }
+}
+/*
+==============================================================================
+CharacterManager::ResetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::ResetCharacter( int argc, char** argv )
+{
+ int index = ::atoi( argv[ 1 ] );
+ Character* pCharacter = GetCharacterManager()->GetCharacter( index );
+ Locator* loc = p3d::find<Locator>( argv[2] );
+ if ( pCharacter && loc )
+ {
+ rmt::Vector pos;
+ loc->GetLocation( &pos );
+ pCharacter->RelocateAndReset(pos, pCharacter->GetDesiredDir());
+ }
+}
+
+
+char CharacterManager::sInitialWalkLocator[64] = "";
+
+void CharacterManager::SetInitialWalk(int argc, char** argv)
+{
+ strcpy(sInitialWalkLocator, argv[1]);
+}
+
+char CharacterManager::sCharacterToSpawn[64] = "homer";
+Character* CharacterManager::sSpawnedCharacter = NULL;
+
+void CharacterManager::Spawn(void*)
+{
+ if(sSpawnedCharacter)
+ {
+ GetCharacterManager()->RemoveCharacter(sSpawnedCharacter);
+ sSpawnedCharacter = NULL;
+ }
+
+ sSpawnedCharacter = GetCharacterManager()->AddCharacter(NPC, "dummy", sCharacterToSpawn, "npd", "next_to_pc");
+
+ rmt::Vector position;
+ rmt::Vector facing;
+ GetCharacterManager()->GetCharacter(0)->GetPosition( &position );
+ GetCharacterManager()->GetCharacter(0)->GetFacing( facing );
+ position += facing;
+ position += facing;
+
+ sSpawnedCharacter->RelocateAndReset( position, 0);
+
+ if(sSpawnedCharacter)
+ sSpawnedCharacter->AddToWorldScene();
+}
+
+
+static const int sNumBartSkins = 7;
+static int sCurBartSkin = 1;
+static char sBartSkins [sNumBartSkins][16] =
+{
+ "bart",
+ "b_man",
+ "b_military",
+ "b_ninja",
+ "b_football",
+ "b_tall",
+ "b_hugo"
+};
+
+static const int sNumHomerSkins = 7;
+static int sCurHomerSkin = 1;
+static char sHomerSkins [sNumHomerSkins][16] =
+{
+ "homer",
+ "h_fat",
+ "h_donut",
+ "h_stcrobe",
+ "h_undrwr",
+ "h_scuzzy",
+ "h_evil"
+};
+
+static const int sNumLisaSkins = 4;
+static int sCurLisaSkin = 1;
+static char sLisaSkins [sNumLisaSkins][16] =
+{
+ "lisa",
+ "l_cool",
+ "l_florida",
+ "l_jersey"
+};
+
+static const int sNumMargeSkins = 4;
+static int sCurMargeSkin = 1;
+static char sMargeSkins [sNumMargeSkins][16] =
+{
+ "marge",
+ "m_police",
+ "m_pink",
+ "m_prison"
+};
+
+
+static const int sNumApuSkins = 4;
+static int sCurApuSkin = 1;
+static char sApuSkins [sNumApuSkins][16] =
+{
+ "apu",
+ "a_american",
+ "a_besharp",
+ "a_army"
+};
+
+void CharacterManager::NextSkin( void* )
+{
+ tUID bart = tEntity::MakeUID("bart");
+ tUID homer = tEntity::MakeUID("homer");
+ tUID lisa = tEntity::MakeUID("lisa");
+ tUID marge = tEntity::MakeUID("marge");
+ tUID apu = tEntity::MakeUID("apu");
+
+ Character* pc = GetCharacterManager()->GetCharacter(0);
+
+ if(pc->GetUID() == bart)
+ {
+ GetCharacterManager()->SwapData(pc, sBartSkins[sCurBartSkin], "bart");
+ sCurBartSkin = (sCurBartSkin + 1) % sNumBartSkins;
+ }
+
+ if(pc->GetUID() == homer)
+ {
+ GetCharacterManager()->SwapData(pc, sHomerSkins[sCurHomerSkin], "homer");
+ sCurHomerSkin = (sCurHomerSkin + 1) % sNumHomerSkins;
+ }
+
+ if(pc->GetUID() == lisa)
+ {
+ GetCharacterManager()->SwapData(pc, sLisaSkins[sCurLisaSkin], "lisa");
+ sCurLisaSkin = (sCurLisaSkin + 1) % sNumLisaSkins;
+ }
+
+ if(pc->GetUID() == marge)
+ {
+ GetCharacterManager()->SwapData(pc, sMargeSkins[sCurMargeSkin], "marge");
+ sCurMargeSkin = (sCurMargeSkin + 1) % sNumMargeSkins;
+ }
+
+ if(pc->GetUID() == apu)
+ {
+ GetCharacterManager()->SwapData(pc, sApuSkins[sCurApuSkin], "apu");
+ sCurApuSkin = (sCurApuSkin + 1) % sNumApuSkins;
+ }
+
+}
+
+static const int s_numCheatModels = 107;
+
+static char s_CheatModels[s_numCheatModels][107] =
+{
+ "apu",
+ "askinner",
+ "a_american",
+ "a_army",
+ "a_besharp",
+ "barney",
+ "bart",
+ "beeman",
+ "brn_unf",
+ "burns",
+ "b_football",
+ "b_hugo",
+ "b_man",
+ "b_military",
+ "b_ninja",
+ "b_tall",
+ "captain",
+ "carl",
+ "cbg",
+ "cletus",
+ "dolph",
+ "eddie",
+ "frink",
+ "gil",
+ "grandpa",
+ "hibbert",
+ "homer",
+ "h_donut",
+ "h_evil",
+ "h_fat",
+ "h_scuzzy",
+ "h_stcrobe",
+ "h_undrwr",
+ "jasper",
+ "jimbo",
+ "kearney",
+ "krusty",
+ "lenny",
+ "lisa",
+ "lou",
+ "louie",
+ "l_cool",
+ "l_florida",
+ "l_jersey",
+ "marge",
+ "milhouse",
+ "moe",
+ "moleman",
+ "m_pink",
+ "m_police",
+ "m_prison",
+ "ned",
+ "nelson",
+ "nriviera",
+ "otto",
+ "patty",
+ "ralph",
+ "selma",
+ "skinner",
+ "smithers",
+ "snake",
+ "teen",
+ "wiggum",
+ "willie",
+ "boy1",
+ "boy2",
+ "boy3",
+ "bum",
+ "busm1",
+ "busm2",
+ "busw1",
+ "const1",
+ "const2",
+ "farmr1",
+ "fem1",
+ "fem2",
+ "fem3",
+ "fem4",
+ "girl1",
+ "girl2",
+ "hooker",
+ "joger1",
+ "joger2",
+ "male1",
+ "male2",
+ "male3",
+ "male4",
+ "male5",
+ "male6",
+ "mobstr",
+ "nuclear",
+ "olady1",
+ "olady2",
+ "olady3",
+ "rednk1",
+ "rednk2",
+ "sail1",
+ "sail2",
+ "sail3",
+ "sail4",
+ "zfem1",
+ "zfem5",
+ "zmale1",
+ "zmale3",
+ "zmale4",
+ "frankenstein",
+ "witch"
+};
+
+static int s_curCheatModel = 0;
+
+void CharacterManager::NextCheatModel(void)
+{
+ if( CommandLineOptions::Get( CLO_NO_PEDS ) )
+ return;
+
+ Character* pc = GetCharacterManager()->GetCharacter(0);
+ GetCharacterManager()->SwapData(pc, s_CheatModels[s_curCheatModel], GetCharacterManager()->GetAnimName(pc));
+ s_curCheatModel = (s_curCheatModel + 1) % s_numCheatModels;
+}
+
+#include <mission/gameplaymanager.h>
+#include <meta/zoneeventlocator.h>
+
+static const unsigned MAX_TELEPORT_DESTS = 64;
+static ZoneEventLocator* s_dynaloadLoc = NULL;
+static unsigned s_numTeleportDests = 0;
+struct TeleportDest
+{
+ char name[32];
+ bool useLoc;
+ char loc[32];
+ rmt::Vector pos;
+ char zone[64];
+} s_teleportDests[MAX_TELEPORT_DESTS];
+
+unsigned CharacterManager::GetNumTeleportDests(void)
+{
+ return s_numTeleportDests;
+}
+
+const char* CharacterManager::GetTeleportDest(unsigned i)
+{
+ static char dummy[] = "Invalid Teleport Destination";
+
+ if(i < s_numTeleportDests)
+ {
+ return s_teleportDests[i].name;
+ }
+
+ return dummy;
+}
+
+void CharacterManager::ClearTeleportDests(void)
+{
+ for(unsigned i = 0; i < s_numTeleportDests; i++)
+ {
+ radDbgWatchDelete( &DoTeleport );
+ }
+ tRefCounted::Release(s_dynaloadLoc);
+ s_numTeleportDests = 0;
+}
+
+void CharacterManager::AddTeleportDest(int argc, char** argv)
+{
+ rAssert(argc != 5);
+ strcpy(s_teleportDests[s_numTeleportDests].name, argv[1]);
+
+ if(argc == 4)
+ {
+ strcpy(s_teleportDests[s_numTeleportDests].loc, argv[2]);
+ strcpy(s_teleportDests[s_numTeleportDests].zone, argv[3]);
+ s_teleportDests[s_numTeleportDests].useLoc = true;
+ }
+ else
+ {
+ s_teleportDests[s_numTeleportDests].pos.Set((float)atof(argv[2]), (float)atof(argv[3]), (float)atof(argv[4]));
+ strcpy(s_teleportDests[s_numTeleportDests].zone, argv[5]);
+ s_teleportDests[s_numTeleportDests].useLoc = false;
+ }
+ radDbgWatchAddFunction( argv[1], &DoTeleport, (void*)s_numTeleportDests, "Teleport");
+ s_numTeleportDests++;
+}
+
+void CharacterManager::DoTeleport(void* data)
+{
+ int which = (int)data;
+ GetRenderManager()->mpLayer( RenderEnums::LevelSlot )->DumpAllDynaLoads(1, GetRenderManager()->mEntityDeletionList );
+
+ tRefCounted::Assign(s_dynaloadLoc,new(GetGameplayManager()->GetCurrentMissionHeap()) ZoneEventLocator);
+ s_dynaloadLoc->SetZoneSize( strlen(s_teleportDests[which].zone) + 1 );
+ s_dynaloadLoc->SetZone( s_teleportDests[which].zone );
+ s_dynaloadLoc->SetPlayerEntered();
+ GetEventManager()->TriggerEvent( EVENT_FIRST_DYNAMIC_ZONE_START, s_dynaloadLoc );
+
+ rmt::Vector pos = s_teleportDests[which].pos;
+ if(s_teleportDests[which].useLoc)
+ {
+ Locator* l = p3d::find<Locator>(s_teleportDests[which].loc);
+ l->GetLocation(&pos);
+ }
+
+ if(GetCharacterManager()->GetCharacter(0)->IsInCar())
+ {
+ GetCharacterManager()->GetCharacter(0)->GetTargetVehicle()->SetPosition(&pos);
+ }
+ else
+ {
+ GetCharacterManager()->GetCharacter(0)->RelocateAndReset(pos, 0);
+ }
+}
+
+
+//=============================================================================
+// CharacterManager::ResetCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+unsigned int CharacterManager::GetCharacterIndex( const Character* character ) const
+{
+ unsigned int i;
+ unsigned int size = MAX_CHARACTERS;
+ for( i = 0; i < size; ++i )
+ {
+ if( mpCharacter[ i ] == character )
+ {
+ return i;
+ }
+ }
+ rAssertMsg( false, "Searched for a chraracter that does not exist" );
+ return 0;
+} \ No newline at end of file
diff --git a/game/code/worldsim/character/charactermanager.h b/game/code/worldsim/character/charactermanager.h
new file mode 100644
index 0000000..8f5f87b
--- /dev/null
+++ b/game/code/worldsim/character/charactermanager.h
@@ -0,0 +1,240 @@
+#ifndef CHARACTERMANAGER_H_
+#define CHARACTERMANAGER_H_
+
+#include <events/eventlistener.h>
+#include <loading/loadingmanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+// Forward declarations.
+//
+class Character;
+class CharacterRenderable;
+class ActionButtonHandler;
+class tSkeleton;
+class tPose;
+class Vehicle;
+
+namespace choreo
+{
+ class Puppet;
+};
+
+class CharacterManager
+:
+public EventListener,
+public LoadingManager::ProcessRequestsCallback
+{
+public:
+ // Are we a PC or NPC
+ //
+ enum CharacterType
+ {
+ PC,
+ NPC
+ };
+
+ // Singleton stuff
+ //
+ static CharacterManager* GetInstance( );
+ static CharacterManager* CreateInstance( );
+ static void DestroyInstance( );
+
+ // Called before loading begins to preload a little neccesary data
+ //
+ void PreLoad( void );
+
+ // Called when exiting gameplay, cleans up everything
+ //
+ void Destroy( bool permenant = false);
+
+ // Create a new character and add it to the manager
+ //
+ Character* AddCharacter( CharacterType, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location = NULL);
+ Character* AddCharacterDeferedLoad( CharacterType, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location = NULL);
+
+ // preload data for a character
+ //
+ void PreloadCharacter(const char* name, const char* anim, LoadingManager::ProcessRequestsCallback* callback = NULL, void* userData = NULL) { LoadAnimation(anim); LoadModel(name, callback, userData); }
+ bool IsModelLoaded(const char* name);
+ bool IsAnimLoaded(const char* name);
+
+ // legacy add functions (just map to AddCharacter now)
+ //
+ Character* AddPCCharacter( const char* characterName, const char* choreoPuppet ) { return AddCharacter(PC, characterName, characterName, choreoPuppet); }
+
+ // Get character by name
+ //
+ Character* GetCharacterByName( const char* name ) const;
+ Character* GetCharacterByName( const tUID uid ) const;
+ Character* GetMissionCharacter( const char* name ) const;
+
+ // swap character data
+ void SwapData(Character*, const char* mesh, const char* anim);
+
+ // Return a pointer to the character.
+ //
+ Character* GetCharacter( int i ) const;
+
+ // remove a character
+ //
+ void RemoveCharacter( Character* ) ;
+
+ // Character garbage collection
+ void AllowGarbageCollection(bool allow) { mGarbageCollect = allow;}
+ void SetGarbage(Character*, bool garbage);
+
+ // event handling (mainly when characters touch various locators)
+ //
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void GarbageCollect(bool ignoreDist = false);
+
+ const char* GetModelName(Character*);
+ const char* GetAnimName(Character*);
+
+ void ClearTargetVehicle(Vehicle*);
+ void ResetBonusCharacters(void);
+
+ // Various simulation stuff
+ //
+ void PreSimUpdate(float timeins);
+ void PostSimUpdate(float timeins);
+ void PreSubstepUpdate(float timeins);
+ void PostSubstepUpdate(float timeins);
+ void Update( float timeins );
+ void SubmitStatics( void );
+ void SubmitAnimCollisions( void );
+ void SubmitDynamics( void );
+
+ // Scripter hooks
+ //
+ static void SetCharacterPosition( int argc, char** argv );
+ static void ResetCharacter( int argc, char** argv );
+
+ // ???
+ //
+ static bool sbFixedSimRate;
+
+ static float sfKickingForce;
+ static float sfSlamForce;
+
+ enum LoadState
+ {
+ NOT_LOADED,
+ GARBAGE,
+ LOADING,
+ LOADING_GARBAGE,
+ LOADED
+ };
+
+ static void NextCheatModel(void);
+
+ static int GetMaxCharacters();
+
+ static void Teleport(unsigned i) { DoTeleport((void*)i); }
+ static unsigned GetNumTeleportDests(void);
+ static const char* GetTeleportDest(unsigned i);
+
+ void ClearInitialWalk(void) {sInitialWalkLocator[0] = 0;}
+
+protected:
+ unsigned int GetCharacterIndex( const Character* character ) const;
+private:
+ static const int MAX_NPCS = 8;
+ static const int MAX_PEDESTRIANS = PedestrianManager::MAX_PEDESTRIANS;
+ static const int MAX_CHARACTERS = 64; //MAX_PLAYERS + MAX_PEDESTRIANS + MAX_NPCS;
+ static const int MAX_LOADS = 40;
+
+ // Data we need for a character load.
+ //
+ struct CharacterLoadData
+ {
+ char modelName[64];
+ char animName[64];
+
+ tName modelSection;
+ tName animSection;
+ tName animModelSection;
+
+ tName mModelHigh;
+ tName mModelMedium;
+ tName mModelLow;
+ tName mChoreoName;
+ };
+
+ struct Load
+ {
+ Load() { state = NOT_LOADED; callback = NULL; }
+ tUID name;
+ tUID section;
+ LoadState state;
+ float gracePeriod;
+ LoadingManager::ProcessRequestsCallback* callback;
+ void* userData;
+ };
+
+ CharacterManager( void );
+ ~CharacterManager( void );
+
+ int AddCharacter( Character* pCharacter, CharacterType );
+
+ // [Dusit: Nov 26,2002]
+ // Hacked setupcharacter to return the radTime for OUTPUT_TIMES debugging
+ // Uncomment #define OUTPUT_TIMES in .cpp to have rReleasePrintfs show time taken
+ // in dynaload calls for peds & other characters
+ unsigned int SetupCharacter( CharacterLoadData& data, Character* pCharacter);
+
+ void FillLoadData( CharacterLoadData& data, const char* model, const char* anim);
+
+ unsigned LoadModel(const char* model, LoadingManager::ProcessRequestsCallback* callback = NULL, void* userData = NULL);
+ unsigned LoadAnimation(const char* anim);
+ unsigned FindLoad(Load* loads, tUID name);
+ unsigned AllocLoad(Load* loads, tUID name);
+ LoadState GetState(Load* loads, tUID name);
+
+ unsigned InternalSwapData(Character*, const char* mesh, const char* anim);
+
+ void GarbageCollectModel(unsigned index);
+ void GarbageCollectAnim(unsigned index);
+
+ void OnProcessRequestsComplete( void* pUserData );
+
+ static CharacterManager* spCharacterManager;
+
+ CharacterLoadData mDummyLoadData;
+
+ Character* mpCharacter[ MAX_CHARACTERS ];
+ char mRealModelNames[ MAX_CHARACTERS ][64];
+ char mRealAnimNames[ MAX_CHARACTERS ][64];
+ bool mLoaded[MAX_CHARACTERS];
+ CharacterLoadData mCharacterLoadData[ MAX_CHARACTERS ];
+ unsigned mCharacterModelData[MAX_CHARACTERS];
+ unsigned mCharacterAnimData[MAX_CHARACTERS];
+ Load mModelData[MAX_LOADS];
+ Load mAnimData[MAX_LOADS];
+
+ bool mGarbageCollect;
+ bool mGarbage[MAX_CHARACTERS];
+
+ tPose* mUniversalPose;
+
+ static char sInitialWalkLocator[64];
+ static void SetInitialWalk(int argc, char** argv);
+
+ static char sCharacterToSpawn[64];
+ static Character* sSpawnedCharacter;
+ static void Spawn(void*);
+ static void NextSkin( void* );
+
+ static void DoTeleport(void*);
+ static void ClearTeleportDests(void);
+ static void AddTeleportDest(int argc, char** argv);
+
+ unsigned int mNumCharactersAdded;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline CharacterManager* GetCharacterManager() { return( CharacterManager::GetInstance() ); }
+
+inline int CharacterManager::GetMaxCharacters() { return CharacterManager::MAX_CHARACTERS; }
+
+#endif //CHARACTERMANAGER_H_
diff --git a/game/code/worldsim/character/charactermappable.cpp b/game/code/worldsim/character/charactermappable.cpp
new file mode 100644
index 0000000..b798761
--- /dev/null
+++ b/game/code/worldsim/character/charactermappable.cpp
@@ -0,0 +1,281 @@
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactercontroller.h>
+#include <p3d/camera.hpp>
+
+#include <input/inputmanager.h>
+#include <input/usercontrollerwin32.h>
+
+//
+// Temp
+#include <worldsim/avatarmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+
+// Constructor.
+//
+/*
+==============================================================================
+CharacterMappable::CharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterMappable
+
+=============================================================================
+*/
+CharacterMappable::CharacterMappable( void )
+:
+Mappable(Input::ACTIVE_GAMEPLAY),
+mpCharacterController( 0 )
+{
+}
+
+// Destructor
+//
+/*
+==============================================================================
+CharacterMappable::~CharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterMappable
+
+=============================================================================
+*/
+CharacterMappable::~CharacterMappable( void )
+{
+ if ( mpCharacterController )
+ {
+ mpCharacterController->Release( );
+ mpCharacterController = 0;
+ }
+}
+/*
+==============================================================================
+CharacterMappable::GetCharacterController
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: CharacterController
+
+=============================================================================
+*/
+CharacterController* CharacterMappable::GetCharacterController() const
+{
+ return mpCharacterController;
+}
+
+void CharacterMappable::SetCharacterController( CharacterController* pCharacterController )
+{
+ tRefCounted::Assign( mpCharacterController, pCharacterController );
+}
+// This method is called when ever a button state changes.
+//void
+void CharacterMappable::OnButton( int controllerId, int id, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Pressed" to "Released".
+//
+void CharacterMappable::OnButtonUp( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Released" to "Pressed".
+//
+void CharacterMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+// This is how we create our controller device mappings to logical game mappings.
+// The mappings set up in this method are platform specific.
+//
+// The basic format of the calls is to "Map" a input, to a enumerated output id.
+// The output of the specified input will be contained in the Button[] array.
+// This id will also be sent as a the second parameter in the OnButton... messages.
+//
+void CharacterMappable::LoadControllerMappings( unsigned int controllerId )
+{
+#ifdef RAD_XBOX
+ ClearMap( 0 );
+ Map( "LeftStickX", CharacterController::LeftStickX, 0, controllerId );
+ Map( "LeftStickY", CharacterController::LeftStickY, 0, controllerId );
+ Map( "DPadUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "DPadDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "DPadLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "DPadRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "Y", CharacterController::DoAction, 0, controllerId );
+ Map( "A", CharacterController::Jump, 0, controllerId );
+ Map( "B", CharacterController::Dash, 0, controllerId );
+ Map( "X", CharacterController::Attack, 0, controllerId );
+#endif
+#ifdef RAD_PS2
+ ClearMap( 0 );
+ Map( "LeftStickX", CharacterController::LeftStickX, 0, controllerId );
+ Map( "LeftStickY", CharacterController::LeftStickY, 0, controllerId );
+ Map( "DPadUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "DPadDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "DPadLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "DPadRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "Triangle", CharacterController::DoAction, 0, controllerId );
+ Map( "X", CharacterController::Jump, 0, controllerId );
+ Map( "Circle", CharacterController::Dash, 0, controllerId );
+ Map( "Square", CharacterController::Attack, 0, controllerId );
+#endif
+#ifdef RAD_GAMECUBE
+ ClearMap( 0 );
+ Map( "LeftStickX", CharacterController::LeftStickX, 0, controllerId );
+ Map( "LeftStickY", CharacterController::LeftStickY, 0, controllerId );
+ Map( "DPadUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "DPadDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "DPadLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "DPadRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "Y", CharacterController::DoAction, 0, controllerId );
+ Map( "A", CharacterController::Jump, 0, controllerId );
+ Map( "X", CharacterController::Dash, 0, controllerId );
+ Map( "B", CharacterController::Attack, 0, controllerId );
+#endif
+#ifdef RAD_WIN32
+ ClearMap( 0 );
+ Map( "MoveUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "MoveDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "MoveLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "MoveRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "DoAction", CharacterController::DoAction, 0, controllerId );
+ Map( "GetOutCar", CharacterController::GetOutCar, 0, controllerId );
+ Map( "Jump", CharacterController::Jump, 0, controllerId );
+ Map( "Sprint", CharacterController::Dash, 0, controllerId );
+ Map( "Attack", CharacterController::Attack, 0, controllerId );
+ Map( "feMouseRight", CharacterController::MouseLookRight, 0, controllerId );
+ Map( "feMouseLeft", CharacterController::MouseLookLeft, 0, controllerId );
+#endif
+}
+
+void CharacterMappable::GetDirection( rmt::Vector& outDirection ) const
+{
+}
+
+BipedCharacterMappable::BipedCharacterMappable( void )
+:
+CharacterMappable( )
+{
+}
+
+BipedCharacterMappable::~BipedCharacterMappable( )
+{
+}
+void BipedCharacterMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ CharacterMappable::OnButtonDown( controllerId, buttonId, pButton );
+
+ switch ( buttonId )
+ {
+ case CharacterController::DoAction:
+ {
+ GetCharacterController()->SetIntention( CharacterController::DoAction );
+ break;
+ }
+ case CharacterController::Jump:
+ {
+ GetCharacterController()->SetIntention( CharacterController::Jump );
+ break;
+ }
+ case CharacterController::Dash:
+ {
+ GetCharacterController()->SetIntention( CharacterController::Dash );
+ break;
+ }
+ case CharacterController::Attack:
+ {
+ GetCharacterController()->SetIntention( CharacterController::Attack );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+void BipedCharacterMappable::GetDirection( rmt::Vector& outDirection ) const
+{
+#ifdef RAD_WIN32
+ if ( GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam()->GetType() == SuperCam::PC_CAM ) //Mouse look enabled
+ {
+ float right = GetValue( CharacterController::MouseLookRight );
+ float left = GetValue( CharacterController::MouseLookLeft );
+ outDirection.x = ( right > left ) ? right : -left;
+
+ float dirPad = GetValue( CharacterController::DPadUp ) - GetValue( CharacterController::DPadDown );
+ float dirAnalog = GetValue( CharacterController::LeftStickY );
+
+ outDirection.z = rmt::Fabs( dirPad ) > rmt::Fabs( dirAnalog ) ? dirPad : dirAnalog;
+ }
+ else
+ {
+#endif
+ rmt::Vector tempDir;
+ tempDir.x = GetValue( CharacterController::LeftStickX );
+ tempDir.y = 0.0f;
+ tempDir.z = GetValue( CharacterController::LeftStickY );
+
+ rmt::Vector tempDir2;
+ tempDir2.x = GetValue( CharacterController::DPadRight ) - GetValue( CharacterController::DPadLeft );
+ tempDir2.y = 0.0f;
+ tempDir2.z = GetValue( CharacterController::DPadUp ) - GetValue( CharacterController::DPadDown );
+
+ //The DPad overrides the analog stick.
+ outDirection = tempDir2.MagnitudeSqr() != 0.0f ? tempDir2 : tempDir;
+#ifdef RAD_WIN32
+ }
+#endif
+}
+
+InCarCharacterMappable::InCarCharacterMappable( void )
+:
+CharacterMappable( )
+{
+}
+
+InCarCharacterMappable::~InCarCharacterMappable( )
+{
+}
+
+void InCarCharacterMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ CharacterMappable::OnButtonDown( controllerId, buttonId, pButton );
+
+ switch ( buttonId )
+ {
+ case CharacterController::DoAction:
+ {
+ GetCharacterController()->SetIntention( CharacterController::DoAction );
+ break;
+ }
+#ifdef RAD_WIN32
+ case CharacterController::GetOutCar:
+ {
+ GetCharacterController()->SetIntention( CharacterController::GetOutCar );
+ break;
+ }
+#endif
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void InCarCharacterMappable::GetDirection( rmt::Vector& outDirection ) const
+{
+ outDirection.Set( 0.0f, 0.0f, 0.0f );
+}
diff --git a/game/code/worldsim/character/charactermappable.h b/game/code/worldsim/character/charactermappable.h
new file mode 100644
index 0000000..6c67f5f
--- /dev/null
+++ b/game/code/worldsim/character/charactermappable.h
@@ -0,0 +1,126 @@
+#ifndef BIPEDCHARACTERMAPPABLE_H_
+#define BIPEDCHARACTERMAPPABLE_H_
+
+#include <input/mappable.h>
+#include <radmath/radmath.hpp>
+
+
+class Character;
+class CharacterController;
+class tCamera;
+
+class CharacterMappable
+:
+public Mappable
+{
+public:
+
+ CharacterMappable( void );
+ virtual ~CharacterMappable( void );
+
+ // This method is called when ever a button state changes.
+ //
+ virtual void OnButton( int controllerId, int id, const IButton* pButton );
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ //
+ virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ //
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ //
+ virtual void LoadControllerMappings( unsigned int controllerId );
+
+ // Return the controller input direction transformed from camera to world space.
+ //
+ virtual void GetDirection( rmt::Vector& outDirection ) const;
+
+ CharacterController* GetCharacterController( void ) const;
+ void SetCharacterController( CharacterController* pCharacterController );
+private:
+ CharacterController* mpCharacterController;
+};
+
+class BipedCharacterMappable
+:
+public CharacterMappable
+{
+public:
+
+ BipedCharacterMappable( void );
+ virtual ~BipedCharacterMappable( void );
+
+ // This method is called when ever a button state changes.
+ //
+ //virtual void OnButton( int controllerId, int id, const IButton* pButton );
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ //
+ //virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ //
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ //
+ //virtual void LoadControllerMappings( unsigned int controllerId );
+
+ // Return the controller input direction transformed from camera to world space.
+ //
+ virtual void GetDirection( rmt::Vector& outDirection ) const;
+
+private:
+};
+
+
+class InCarCharacterMappable
+:
+public CharacterMappable
+{
+public:
+
+ InCarCharacterMappable( void );
+ virtual ~InCarCharacterMappable( void );
+
+ // This method is called when ever a button state changes.
+ //
+ //virtual void OnButton( int controllerId, int id, const IButton* pButton );
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ //
+ //void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ //
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ //
+ //virtual void LoadControllerMappings( unsigned int controllerId );
+
+ // Return the controller input direction transformed from camera to world space.
+ //
+ virtual void GetDirection( rmt::Vector& outDirection ) const;
+
+private:
+};
+#endif // BIPEDCHARACTERMAPPABLE_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/characterrenderable.cpp b/game/code/worldsim/character/characterrenderable.cpp
new file mode 100644
index 0000000..eb94e36
--- /dev/null
+++ b/game/code/worldsim/character/characterrenderable.cpp
@@ -0,0 +1,581 @@
+#include <worldsim/character/characterrenderable.h>
+#include <mission/gameplaymanager.h>
+#include <p3d/texture.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/anim/drawablepose.hpp>
+#include <p3d/anim/pose.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/shadow.hpp>
+#include <p3d/view.hpp>
+#include <pddi/pddi.hpp>
+#include <camera/supercammanager.h>
+#include <contexts/bootupcontext.h>
+
+// Hack.
+// [6/27/2002]
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+
+/*
+==============================================================================
+CharacterRenderable::CharacterRenderable
+==============================================================================
+Description: Comment
+
+Parameters: (
+ tDrawablePose* pDrawablePoseHigh,
+ tDrawablePose* pDrawablePoseMedium,
+ tDrawablePose* pDrawablePoseLow,
+ tPose* pPose )
+
+Return: CharacterRenderable
+
+=============================================================================
+*/
+CharacterRenderable::CharacterRenderable(
+ tDrawablePose* pDrawablePoseHigh,
+ tDrawablePose* pDrawablePoseMedium,
+ tDrawablePose* pDrawablePoseLow)
+:
+mCurrentLOD( High ),
+mbInAnyonesFrustrum( false ),
+mpSwatchTexture( NULL ),
+mpSwatchShader( NULL ),
+mHaveShadowJoints( false ),
+mFadeAlpha( 255 ),
+mpShockedDrawable( NULL ),
+mIsShocked( false ),
+mDisplayingSkeletonShock( false )
+{
+ int i;
+ for ( i = 0; i < MAX_LOD; i++ )
+ {
+ mpDrawableList[ i ] = 0;
+ }
+ tRefCounted::Assign( mpDrawableList[ High ], pDrawablePoseHigh );
+ tRefCounted::Assign( mpDrawableList[ Medium ], pDrawablePoseMedium );
+ tRefCounted::Assign( mpDrawableList[ Low ], pDrawablePoseLow );
+
+ if(pDrawablePoseHigh)
+ {
+ pDrawablePoseHigh->SetPose(NULL);
+ }
+
+ if(pDrawablePoseMedium)
+ {
+ pDrawablePoseMedium->SetPose(NULL);
+ }
+
+ if(pDrawablePoseLow)
+ {
+ pDrawablePoseLow->SetPose(NULL);
+ }
+
+ // null these out for now... CharacterManager::SetupCharacter will be
+ // setting these swatch textures...
+ for( i=0; i<CharacterRenderable::NUM_CHARACTER_SWATCHES; i++ )
+ {
+ mpSwatchTextures[i] = NULL;
+ }
+
+ mShadowColour.Set( 0, 0, 0 );
+}
+
+/*
+==============================================================================
+CharacterRenderable::~CharacterRenderable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterRenderable
+
+=============================================================================
+*/
+CharacterRenderable::~CharacterRenderable( void )
+{
+ int i;
+ for ( i = 0; i < MAX_LOD; i++ )
+ {
+ if ( mpDrawableList[ i ] )
+ {
+ mpDrawableList[ i ]->Release();
+ mpDrawableList[ i ] = 0;
+ }
+ }
+
+ if( mpSwatchTexture )
+ {
+ mpSwatchTexture->Release();
+ mpSwatchTexture = NULL;
+ }
+
+ for( i=0; i<CharacterRenderable::NUM_CHARACTER_SWATCHES; i++ )
+ {
+ if( mpSwatchTextures[i] != NULL )
+ {
+ mpSwatchTextures[i]->Release();
+ mpSwatchTextures[i] = NULL;
+ }
+ }
+ if( mpSwatchShader )
+ {
+ mpSwatchShader->Release();
+ mpSwatchShader = NULL;
+ }
+
+ if ( mpShockedDrawable != NULL )
+ {
+ mpShockedDrawable->Release();
+ mpShockedDrawable = NULL;
+ }
+}
+/*
+==============================================================================
+CharacterRenderable::Display
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterRenderable::Display( rmt::Vector iPosn, tPose* pose )
+{
+BEGIN_PROFILE("CharRender Cull")
+ tPointCamera* pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(0)->GetCamera();
+ rmt::Vector camPosn;
+ pCam->GetPosition( &camPosn );
+ iPosn.Sub( iPosn, camPosn );
+ float sqrDistFromCam = iPosn.MagnitudeSqr();
+END_PROFILE("CharRender Cull")
+
+ float dist = (pCam->GetNearPlane() + 1.5f);
+ dist *= dist;
+
+ if ( sqrDistFromCam < dist )
+ {
+ return;
+ }
+
+ if( mbInAnyonesFrustrum )
+ {
+
+ if(sqrDistFromCam > 900.0f )
+ {
+ if(sqrDistFromCam > 6400.0f ) //>80m
+ {
+ SetLOD(Low);
+ }
+ else // <= 80m
+ {
+ SetLOD(Medium);
+ }
+ }
+ else // <= 30m
+ {
+ SetLOD(High);
+ }
+
+ BEGIN_PROFILE("CharRender Display")
+ if ( mIsShocked == false )
+ {
+ DisplayModel( pose );
+ }
+ else
+ {
+ DisplayShocked( pose );
+ }
+
+ END_PROFILE("CharRender Display")
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+int CharacterRenderable::CastsShadow()
+{
+ return 1;
+}
+
+/*
+==============================================================================
+CharacterRenderable::DisplayShadow
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void CharacterRenderable::DisplayShadow( tPose* pose, const BlobShadowParams* BlobParams )
+{
+ if( BlobParams )
+ {
+ BEGIN_PROFILE("Char Blobby Shadow");
+ if( !mHaveShadowJoints )
+ {
+ mHaveShadowJoints = true;
+ mShadowJoints[ 0 ] = pose->FindJoint( "Elbow_L" );
+ mShadowJoints[ 1 ] = pose->FindJoint( "Ankle_L" );
+ mShadowJoints[ 2 ] = pose->FindJoint( "Elbow_R" );
+ mShadowJoints[ 3 ] = pose->FindJoint( "Ankle_R" );
+ }
+
+ tColour OutsideColour;
+ tColour insideColour;
+
+ // Hack for brightly colored shadows for special game mode
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ OutsideColour.Set( 0, 0, 0, 0 ); // black outline
+ //OutsideColour = mShadowColour;
+ insideColour = mShadowColour;
+ }
+ else
+ {
+ OutsideColour.Set( 255, 255, 255, 255 );
+ const int Inside = 128;
+ float shadowAlpha = BlobParams->ShadowAlpha;
+ if( mFadeAlpha != 255 )
+ {
+ shadowAlpha = mFadeAlpha / 255.0f;
+ }
+ int c = rmt::Clamp( int( Inside + ( 255 - Inside ) * ( 1.0f - shadowAlpha ) ), 0, 255 );
+ insideColour.Set( c, c, c, c );
+ }
+
+
+ const float ArticulatedBlobbyShadowLODThreshold = 15.0f;
+ const float MinShadowSize = 0.15f;
+ const float NonArticulatedScale = 0.2f;
+ const int NumShadowJoints = 4;
+
+
+ const float BlobRadius = 1.0f;
+ const float BlobFadeOff = 0.2f;
+ const int NumBlobSlices = 16; // Keep the number even or you're in trouble.
+ const int HalfCircle = NumBlobSlices >> 1; // We only keep half the circle and then mirror it. Hence the reason to keep the number of points even.
+ static float BlobPoints[ HalfCircle ][ 2 ];
+ static float FadePoints[ HalfCircle ][ 2 ];
+ float pointScales[ NumBlobSlices ]; // How much to move the points to envolope the character.
+ bool articulated = false;
+
+ static float DoOnce = true;
+ if( DoOnce )
+ {
+ DoOnce = false;
+ const float angleStep = rmt::PI_2 / ((float) NumBlobSlices );
+ const float angleHalfStep = angleStep * 0.5f;
+ float angle = rmt::PI_2;
+ for( int j = 0; j < HalfCircle; ++j )
+ {
+ float sa, ca;
+ rmt::SinCos( angle, &sa, &ca);
+ BlobPoints[ j ][ 0 ] = sa * BlobRadius;
+ BlobPoints[ j ][ 1 ] = ca * BlobRadius;
+ rmt::SinCos( angle - angleHalfStep, &sa, &ca );
+ FadePoints[ j ][ 0 ] = sa * BlobFadeOff;
+ FadePoints[ j ][ 1 ] = ca * BlobFadeOff;
+ angle -= angleStep;
+ }
+ }
+
+ // Translate the shadow towards the camera slightly, instead of moving it off the
+ //ground in the direction of the the ground normal. Hopefully this will cause less distortion of the shadow.
+ rmt::Vector camPos;
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ camera->GetWorldPosition( &camPos );
+ camPos.Sub( BlobParams->GroundPos );
+ float camDirDot = camPos.DotProduct( camera->GetCameraToWorldMatrix().Row( 2 ) );
+ // Note that the camPos vector is towards the camera so the camDirDot will be negative when the camera is facing
+ //the object. So if it's positive then we can assume it's behind the camera.
+ if( camDirDot > 0.0f )
+ {
+ // Please excuse the early return.
+ END_PROFILE( "Char Blobby Shadow" );
+ return;
+ }
+ float cameraToShadow = camPos.Magnitude();
+ camPos.Normalize();
+ camPos.Scale( 0.25f );
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( BlobParams->GroundPos );
+ transform.FillHeading( BlobParams->GroundNormal, BlobParams->ShadowFacing );
+ transform.Row( 3 ).Add( camPos );
+ p3d::stack->PushMultiply( transform );
+
+ if( ( mHaveShadowJoints ) && ( cameraToShadow < ArticulatedBlobbyShadowLODThreshold ) )
+ {
+ articulated = true;
+ // Find the position of the joints to deform the shadow by the character's movement.
+ float jointPos[ NumShadowJoints ][ 2 ];
+/*
+ Uncomment this if you want to weight the joint position by how close
+ they line up to the blob point position.
+ float jointNor[ NumShadowJoints ][ 2 ];
+*/
+ rmt::Vector tempPos;
+ rmt::Matrix m = pose->GetJoint( 0 )->worldMatrix;
+ m.Invert();
+ for( int i = 0; i < NumShadowJoints; ++i )
+ {
+ m.Transform( mShadowJoints[ i ]->worldMatrix.Row( 3 ), &tempPos );
+ jointPos[ i ][ 0 ] = tempPos.x;
+ jointPos[ i ][ 1 ] = -tempPos.z;
+/*
+ Uncomment this if you want to weight the joint positions by how close
+ they line up to the blob point position.
+ float mag;
+ mag = rmt::Sqrt( ( tempPos.x * tempPos.x ) + ( tempPos.z * tempPos.z ) );
+ mag = ( mag != 0.0f ) ? 1 / mag : 0.0f;
+ jointNor[ i ][ 0 ] = jointPos[ i ][ 0 ] * mag;
+ jointNor[ i ][ 1 ] = jointPos[ i ][ 1 ] * mag;
+*/
+ }
+
+ for( int i = 0; i < HalfCircle; ++i )
+ {
+ pointScales[ i ] = MinShadowSize;
+ pointScales[ i + HalfCircle ] = -MinShadowSize;
+
+ for( int j = 0; j < NumShadowJoints; ++j )
+ {
+ float dot;
+ // Project joint position onto blob point position.
+ dot = ( BlobPoints[ i ][ 0 ] * jointPos[ j ][ 0 ] ) + ( BlobPoints[ i ][ 1 ] * jointPos[ j ][ 1 ] );
+/*
+ Uncomment this if you want to weight the joint position by how close
+ they line up to the blob point position.
+ dot *= rmt::Abs( ( BlobPoints[ i ][ 0 ] * jointNor[ j ][ 0 ] ) + ( BlobPoints[ i ][ 1 ] * jointNor[ j ][ 1 ] ) );
+*/
+ pointScales[ i ] = rmt::Max( pointScales[ i ], dot );
+ pointScales[ i + HalfCircle ] = rmt::Min( pointScales[ i + HalfCircle ], dot );
+ }
+ }
+ // Since we'll be mirroring the circle, we'll conviently keep the second half of the scales array
+ //as negative numbers so that it will automatically mirror our points for us.
+ }
+
+ pddiPrimStream* blob = 0;
+
+ pddiShader* blobShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( blobShader != NULL );
+
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
+ }
+ else
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_MODULATE );
+ }
+ blobShader->SetInt( PDDI_SP_ISLIT, 0 );
+ blobShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ blobShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_GOURAUD );
+
+ blob = p3d::pddi->BeginPrims( blobShader, PDDI_PRIM_TRIANGLES, PDDI_V_C, 9 * NumBlobSlices );
+ for(int i=0; i < NumBlobSlices; i++)
+ {
+ float x0, y0, x1, y1, x2, y2, x3, y3;
+ int index = i % HalfCircle;
+ int nextIndex = ( i + 1 ) % HalfCircle;
+ int nextScaleIndex = ( i + 1 ) % NumBlobSlices;
+ float fadeScale = ( i < HalfCircle ) ? BlobParams->ShadowScale : -BlobParams->ShadowScale;
+ float nextFadeScale = ( nextScaleIndex < HalfCircle ) ? BlobParams->ShadowScale : -BlobParams->ShadowScale;
+
+ // Draw inside.
+ blob->Colour( insideColour );
+ blob->Coord( 0.0f, 0.0f, 0.0f );
+
+ blob->Colour( insideColour );
+ x0 = BlobPoints[ index ][ 0 ];
+ x0 *= BlobParams->ShadowScale;
+ x0 *= articulated ? pointScales[ i ] : ( index == i ) ? NonArticulatedScale : -NonArticulatedScale;
+ y0 = BlobPoints[ index ][ 1 ];
+ y0 *= BlobParams->ShadowScale;
+ y0 *= articulated ? pointScales[ i ] : ( index == i ) ? NonArticulatedScale : -NonArticulatedScale;
+ blob->Coord( x0, y0, 0.0f );
+
+ blob->Colour( insideColour );
+ x1 = BlobPoints[ nextIndex ][ 0 ];
+ x1 *= BlobParams->ShadowScale;
+ x1 *= articulated ? pointScales[ nextScaleIndex ] : ( nextIndex == nextScaleIndex ) ? NonArticulatedScale : -NonArticulatedScale;
+ y1 = BlobPoints[ nextIndex ][ 1 ];
+ y1 *= BlobParams->ShadowScale;
+ y1 *= articulated ? pointScales[ nextScaleIndex ] : ( nextIndex == nextScaleIndex ) ? NonArticulatedScale : -NonArticulatedScale;
+ blob->Coord( x1, y1, 0.0f );
+
+ // Draw first part of outside.
+ blob->Colour( insideColour );
+ blob->Coord( x0, y0, 0.0f );
+
+ blob->Colour( OutsideColour );
+ x2 = x1 + ( FadePoints[ nextIndex ][ 0 ] * nextFadeScale );
+ y2 = y1 + ( FadePoints[ nextIndex ][ 1 ] * nextFadeScale );
+ blob->Coord( x2, y2, 0.0f );
+
+ blob->Colour( insideColour );
+ blob->Coord( x1, y1, 0.0f );
+
+ // Draw second part of outside.
+ blob->Colour( insideColour );
+ blob->Coord( x0, y0, 0.0f );
+
+ blob->Colour( OutsideColour );
+ x3 = x0 + ( FadePoints[ index ][ 0 ] * fadeScale );
+ y3 = y0 + ( FadePoints[ index ][ 1 ] * fadeScale );
+ blob->Coord( x3, y3, 0.0f );
+
+ blob->Colour( OutsideColour );
+ blob->Coord( x2, y2, 0.0f );
+ }
+ p3d::pddi->EndPrims( blob );
+
+ p3d::stack->Pop();
+ END_PROFILE( "Char Blobby Shadow" );
+ }
+}
+/*
+==============================================================================
+CharacterRenderable::GetDrawable
+==============================================================================
+Description: Comment
+
+Parameters: ( )
+
+Return: tDrawablePose
+
+=============================================================================
+*/
+tDrawablePose* CharacterRenderable::GetDrawable( ) const
+{
+ for(int lod = (int)mCurrentLOD; lod >= (int)Low; lod--)
+ {
+ if(mpDrawableList[ lod ] )
+ {
+ return mpDrawableList[ lod ];
+ }
+ }
+ return NULL;
+}
+
+/*
+==============================================================================
+CharacterRenderable::GetLOD
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterRenderable
+
+=============================================================================
+*/
+int CharacterRenderable::GetLOD( void ) const
+{
+ return mCurrentLOD;
+}
+/*
+==============================================================================
+CharacterRenderable::SetLOD
+==============================================================================
+Description: Comment
+
+Parameters: ( CharacterLOD LOD )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterRenderable::SetLOD( int LOD )
+{
+ mCurrentLOD = (LOD > High) ? LOD : High;
+}
+
+void CharacterRenderable::SetSwatch( int index )
+{
+ rAssert( 0 <= index && index < NUM_CHARACTER_SWATCHES );
+ if( mpSwatchTextures[index] != NULL && mpSwatchTexture != mpSwatchTextures[index] )
+ {
+ // TODO:
+ // Assign here? or just set mpSwatchTexture = mpSwatchTextures[index]?
+ tRefCounted::Assign(mpSwatchTexture, mpSwatchTextures[index]);
+ }
+}
+
+void CharacterRenderable::SetSwatchTexture( int index, tTexture* pTexture )
+{
+ rAssert( 0 <= index && index < NUM_CHARACTER_SWATCHES );
+ tRefCounted::Assign(mpSwatchTextures[index], pTexture);
+}
+
+void CharacterRenderable::SetShockEffect( tDrawable* pDrawable )
+{
+ tRefCounted::Assign(mpShockedDrawable, pDrawable);
+}
+
+
+void CharacterRenderable::SetSwatchShader( tShader* pShader )
+{
+ tRefCounted::Assign(mpSwatchShader, pShader);
+}
+
+void CharacterRenderable::SetFadeAlpha( int fadeAlpha )
+{
+ mFadeAlpha = rmt::Clamp( fadeAlpha, 0, 255 );
+}
+
+void CharacterRenderable::SetShadowColour( tColour colour )
+{
+ mShadowColour = colour;
+}
+
+void CharacterRenderable::DisplayModel( tPose* pose )
+{
+ tDrawablePose* draw = GetDrawable();
+ if(draw)
+ {
+ if( mpSwatchShader != NULL )
+ {
+ if( mpSwatchTexture != NULL )
+ {
+ mpSwatchShader->SetTexture( PDDI_SP_BASETEX, mpSwatchTexture );
+ }
+ }
+ tShaderIntBroadcast blendAlpha( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ draw->ProcessShaders( blendAlpha );
+ tShaderIntBroadcast emissiveFade( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ draw->ProcessShaders( emissiveFade );
+ draw->Display( pose );
+ }
+}
+
+
+void CharacterRenderable::DisplayShocked( tPose* pose )
+{
+ // Alternate displaying the skeleton and displaying the model
+ if ( mDisplayingSkeletonShock )
+ {
+ if ( mpShockedDrawable != NULL )
+ {
+ p3d::stack->PushMultiply( pose->GetJoint(0)->worldMatrix );
+ mpShockedDrawable->Display();
+ p3d::stack->Pop();
+ }
+ mDisplayingSkeletonShock = false;
+ }
+ else
+ {
+
+ //pose->GetSkeleton();
+ //DisplayModel( pose );
+ mDisplayingSkeletonShock = true;
+ }
+}
+
diff --git a/game/code/worldsim/character/characterrenderable.h b/game/code/worldsim/character/characterrenderable.h
new file mode 100644
index 0000000..be5a1ab
--- /dev/null
+++ b/game/code/worldsim/character/characterrenderable.h
@@ -0,0 +1,100 @@
+#ifndef CHARACTERRENDERABLE_H_
+#define CHARACTERRENDERABLE_H_
+
+#include <radmath/vector.hpp>
+#include <p3d/anim/pose.hpp>
+
+class tDrawablePose;
+class tShadowSkin;
+class tShader;
+class tTexture;
+class tDrawable;
+
+struct BlobShadowParams
+{
+ BlobShadowParams( const rmt::Vector& Pos, const rmt::Vector& Normal, const rmt::Vector& Facing ) :
+ GroundPos( Pos ), GroundNormal( Normal ), ShadowFacing( Facing ), ShadowAlpha( 1.0f ), ShadowScale( 1.0f ) {}
+ const rmt::Vector& GroundPos;
+ const rmt::Vector& GroundNormal;
+ const rmt::Vector& ShadowFacing;
+ float ShadowAlpha;
+ float ShadowScale;
+};
+
+class CharacterRenderable
+{
+public:
+ static const int NUM_CHARACTER_SWATCHES = 5;
+
+public:
+ // Constructor
+ CharacterRenderable( tDrawablePose* pDrawablePoseHigh,
+ tDrawablePose* pDrawablePoseMedium,
+ tDrawablePose* pDrawablePoseLow);
+
+ ~CharacterRenderable( void );
+
+ void Display( rmt::Vector iPosn, tPose* pose );
+
+ enum CharacterLOD
+ {
+ Low,
+ Medium,
+ High,
+ MAX_LOD
+ };
+
+ int GetLOD( void ) const;
+ void SetLOD( int LOD );
+
+ tDrawablePose* GetDrawable( ) const;
+
+ int CastsShadow();
+
+ // If GroundPos/GroundNormal are null then it's a volumetric shadow, otherwise it's a simple shadow.
+ void DisplayShadow( tPose* pose, const BlobShadowParams* BlobParams );
+
+ void SetInAnyonesFrustrum( bool inFrustrum ) {mbInAnyonesFrustrum = inFrustrum;}
+
+ void SetSwatch( int index );
+ void SetSwatchShader( tShader* pShader );
+ void SetSwatchTexture( int index, tTexture* pTexture );
+ void SetShockEffect( tDrawable* pDrawable );
+
+ void SetFadeAlpha( int fadeAlpha );
+
+ void SetShadowColour( tColour colour );
+
+ void SetShocked( bool isShocked ){ mIsShocked = isShocked; }
+ bool GetIsShocked()const{ return mIsShocked; }
+
+private:
+ // No default construction.
+ CharacterRenderable( void );
+ CharacterRenderable( const CharacterRenderable& characterRenderable );
+
+ void DisplayModel( tPose* pose );
+ void DisplayShocked( tPose* pose );
+
+ tDrawablePose* mpDrawableList[ MAX_LOD ];
+ int mCurrentLOD;
+
+ bool mbInAnyonesFrustrum;
+
+ tTexture* mpSwatchTexture;
+ tTexture* mpSwatchTextures[NUM_CHARACTER_SWATCHES];
+ tShader* mpSwatchShader;
+
+ tPose::Joint* mShadowJoints[ 4 ];
+ bool mHaveShadowJoints;
+
+ int mFadeAlpha;
+
+ tColour mShadowColour;
+ tDrawable* mpShockedDrawable;
+ bool mIsShocked;
+ bool mDisplayingSkeletonShock;
+};
+
+
+#endif // CHARACTERRENDERABLE_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/charactertarget.cpp b/game/code/worldsim/character/charactertarget.cpp
new file mode 100644
index 0000000..39c7f45
--- /dev/null
+++ b/game/code/worldsim/character/charactertarget.cpp
@@ -0,0 +1,312 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class CharacterTarget
+//
+// History: 5/13/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/character/character.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+/*
+==============================================================================
+CharacterTarget::CharacterTarget
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: CharacterTarget
+
+=============================================================================
+*/
+CharacterTarget::CharacterTarget( Character* pCharacter ) :
+ mWalkerID( CharacterEnum::INVALID )
+{
+ mpCharacter = pCharacter;
+}
+/*
+==============================================================================
+CharacterTarget::~CharacterTarget
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: CharacterTarget
+
+=============================================================================
+*/
+CharacterTarget::~CharacterTarget()
+{
+}
+
+/*
+==============================================================================
+CharacterTarget::GetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* position )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetPosition( rmt::Vector* position )
+{
+ mpCharacter->GetPosition( *position );
+}
+/*
+==============================================================================
+CharacterTarget::GetHeading
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* heading )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetHeading( rmt::Vector* heading )
+{
+ mpCharacter->GetFacing( *heading );
+}
+/*
+==============================================================================
+CharacterTarget::GetVUP
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* vup )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetVUP( rmt::Vector* vup )
+{
+ vup->Set( 0.0f, 1.0f, 0.0f );
+}
+/*
+==============================================================================
+CharacterTarget::GetVelocity
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* velocity )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetVelocity( rmt::Vector* velocity )
+{
+ mpCharacter->GetVelocity( *velocity );
+}
+
+//=============================================================================
+// CharacterTarget::GetID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+unsigned int CharacterTarget::GetID()
+{
+ return mWalkerID;
+}
+
+/*
+==============================================================================
+CharacterTarget::IsCar
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsCar() const
+{
+ return false;
+}
+/*
+==============================================================================
+CharacterTarget::IsAirborn
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsAirborn()
+{
+ return !mpCharacter->IsStanding();
+}
+/*
+==============================================================================
+CharacterTarget::IsUnstable
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsUnstable()
+{
+ return false;
+ /*
+ return( !mpCharacter->IsNPC() &&
+ mpCharacter->GetSimState()->GetControl() == sim::simSimulationCtrl );
+ */
+}
+/*
+==============================================================================
+ CharacterTarget::IsQuickTurn
+ ==============================================================================
+ Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsQuickTurn()
+{
+ return false;
+}
+
+//=============================================================================
+// CharacterTarget::GetFirstPersonPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* position )
+//
+// Return: void
+//
+//=============================================================================
+void CharacterTarget::GetFirstPersonPosition( rmt::Vector* position )
+{
+ poser::Pose* pose = mpCharacter->GetPuppet()->GetPose();
+
+ rmt::Vector pos;
+ pos = pose->GetJoint(17)->GetWorldMatrix().Row(3); // 17 is the head (should be looking it up by name, but what the hell)
+ pos -= pose->GetJoint(0)->GetWorldMatrix().Row(3); // 0 is the motion root
+ pos.y += 0.05f; // bump it up a little, the head joint actually a little too low
+
+ //Find the eye spot on the character and return that position.
+ position->Set( 0.0f, pos.y, 0.0f );
+}
+
+
+/*
+==============================================================================
+CharacterTarget::GetName
+==============================================================================
+Description: This is only for debugging, so in the implementation go ahead and
+ make this return NULL in release.
+
+Parameters: ()
+
+Return: const
+
+=============================================================================
+*/
+const char* const CharacterTarget::GetName()
+{
+ return 0;
+}
+
+//=============================================================================
+// CharacterTarget::SetID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CharacterEnum::WalkerID id )
+//
+// Return: void
+//
+//=============================================================================
+void CharacterTarget::SetID( CharacterEnum::WalkerID id )
+{
+ mWalkerID = id;
+}
+
+//=============================================================================
+// CharacterTarget::IsInReverse
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool CharacterTarget::IsInReverse()
+{
+ return false;
+}
+
+//=============================================================================
+// CharacterTarget::GetTerrainIntersect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& pos, rmt::Vector& normal )
+//
+// Return: void
+//
+//=============================================================================
+void CharacterTarget::GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const
+{
+ mpCharacter->GetTerrainIntersect( pos, normal );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/character/charactertarget.h b/game/code/worldsim/character/charactertarget.h
new file mode 100644
index 0000000..0ad5595
--- /dev/null
+++ b/game/code/worldsim/character/charactertarget.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: charactertarget.h
+//
+// Description: Blahblahblah
+//
+// History: 5/13/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef CHARACTERTARGET_H
+#define CHARACTERTARGET_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+#include <camera/isupercamtarget.h>
+#include <constants/characterenum.h>
+#include <presentation/gui/utility/hudmap.h>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class CharacterTarget
+:
+public ISuperCamTarget, public IHudMapIconLocator
+{
+public:
+ CharacterTarget( Character* pCharacter );
+ ~CharacterTarget();
+
+ virtual void GetPosition( rmt::Vector* position );
+ virtual void GetHeading( rmt::Vector* heading );
+ virtual void GetVUP( rmt::Vector* vup );
+ virtual void GetVelocity( rmt::Vector* velocity );
+ virtual unsigned int GetID();
+ virtual bool IsCar() const;
+ virtual bool IsAirborn();
+ virtual bool IsUnstable();
+ virtual bool IsQuickTurn();
+ virtual bool IsInReverse();
+ virtual void GetFirstPersonPosition( rmt::Vector* position );
+ virtual void GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const;
+
+
+
+ //This is only for debugging, so in the implementation go ahead and
+ //make this return NULL in release.
+ virtual const char* const GetName();
+
+ void SetID( CharacterEnum::WalkerID id );
+private:
+
+ //Prevent wasteful constructor creation.
+ CharacterTarget( void );
+ CharacterTarget( const CharacterTarget& charactertarget );
+ CharacterTarget& operator=( const CharacterTarget& charactertarget );
+
+ Character* mpCharacter;
+ CharacterEnum::WalkerID mWalkerID;
+};
+
+#endif //CHARACTERTARGET_H \ No newline at end of file
diff --git a/game/code/worldsim/character/controllereventhandler.h b/game/code/worldsim/character/controllereventhandler.h
new file mode 100644
index 0000000..7d3c024
--- /dev/null
+++ b/game/code/worldsim/character/controllereventhandler.h
@@ -0,0 +1,54 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: controllereventhandler.h
+//
+// Description: Blahblahblah
+//
+// History: 09/08/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef CONTROLLEREVENTHANDLER_H_
+#define CONTROLLEREVENTHANDLER_H_
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+#include <events/eventlistener.h>
+
+class CameraRelativeCharacterController;
+
+class CameraRelativeCharacterControllerEventHandler
+:
+public EventListener
+{
+public:
+ CameraRelativeCharacterControllerEventHandler( CameraRelativeCharacterController* pParent )
+ :
+ mpParent( pParent )
+ {
+ }
+ // Derived classes must implement this method to receive
+ // event notification.
+ virtual void HandleEvent( EventEnum id, void* pEventData )
+ {
+ mpParent->HandleEvent( id, pEventData );
+ }
+protected:
+private:
+ CameraRelativeCharacterControllerEventHandler( void );
+ CameraRelativeCharacterController* mpParent;
+};
+
+#endif //CONTROLLEREVENTHANDLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/footprint/allfootprint.cpp b/game/code/worldsim/character/footprint/allfootprint.cpp
new file mode 100644
index 0000000..88e1a6f
--- /dev/null
+++ b/game/code/worldsim/character/footprint/allfootprint.cpp
@@ -0,0 +1 @@
+#include <worldsim\character\footprint\footprintmanager.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/character/footprint/footprint.cpp b/game/code/worldsim/character/footprint/footprint.cpp
new file mode 100644
index 0000000..7b019ab
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprint.cpp
@@ -0,0 +1,49 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprintmanager
+//
+// Description: Holds a list of all footprints, and fades them out over time
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <worldsim/character/footprint/footprint.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+Footprint::Footprint()
+{
+
+}
+
+Footprint::~Footprint()
+{
+
+}
+
+void
+Footprint::Display()
+{
+
+}
+
diff --git a/game/code/worldsim/character/footprint/footprint.h b/game/code/worldsim/character/footprint/footprint.h
new file mode 100644
index 0000000..3e299a0
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprint.h
@@ -0,0 +1,73 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprint
+//
+// Description: Simple class inherited from tDrawable. Draws a foot print
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef FOOTPRINT_H
+#define FOOTPRINT_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d\drawable.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class Footprint : public tDrawable
+{
+ public:
+ Footprint();
+ virtual ~Footprint();
+
+ virtual void Display();
+
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow Footprint from being copied and assigned.
+ Footprint( const Footprint& );
+ Footprint& operator=( const Footprint& );
+
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/character/footprint/footprintmanager.cpp b/game/code/worldsim/character/footprint/footprintmanager.cpp
new file mode 100644
index 0000000..7a9a313
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprintmanager.cpp
@@ -0,0 +1,252 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprintmanager
+//
+// Description: Holds a list of all footprints, and fades them out over time
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <memory/srrmemory.h>
+#include <p3d/utility.hpp>
+#include <contexts/bootupcontext.h>
+#include <p3d/texture.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/shader.hpp>
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float FOOTPRINT_LIVE_TIME = 0.5f;
+const float FOOTPRINT_FADE_PER_MS = 1 / 2000.0f;
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+FootprintManager* FootprintManager::spFootprintManager = 0;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+FootprintManager::Footprint::Footprint():
+alpha( 0 )
+{
+ const float FOOTPRINT_WIDTH = 1.0f;
+ const float FOOTPRINT_HEIGHT = 1.0f;
+ const float HALF_FOOTPRINT_WIDTH = FOOTPRINT_WIDTH / 2.0f;
+ const float HALF_FOOTPRINT_HEIGHT = FOOTPRINT_HEIGHT / 2.0f;
+
+ rmt::Vector topLeft( -HALF_FOOTPRINT_WIDTH, 0, HALF_FOOTPRINT_HEIGHT );
+ rmt::Vector topRight( HALF_FOOTPRINT_WIDTH, 0 , HALF_FOOTPRINT_HEIGHT );
+ rmt::Vector bottomLeft( -HALF_FOOTPRINT_WIDTH,0 ,-HALF_FOOTPRINT_HEIGHT );
+ rmt::Vector bottomRight( HALF_FOOTPRINT_WIDTH,0, -HALF_FOOTPRINT_HEIGHT );
+
+ points[0] = bottomLeft;
+ points[1] = bottomRight;
+ points[2] = topLeft;
+ points[3] = topRight;
+}
+
+FootprintManager::FootprintManager()
+{
+ for ( int i = 0 ; i < eNumFootprintTypes ; i++ )
+ {
+ m_ActiveFootprints[ i ].Allocate( MAX_NUM_FOOTPRINTS );
+ mpTextures[ i ] = NULL;
+ }
+}
+
+FootprintManager::~FootprintManager()
+{
+ FreeTextures();
+}
+
+FootprintManager* FootprintManager::CreateInstance( void )
+{
+ rAssertMsg( spFootprintManager == 0, "FootprintManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ spFootprintManager = new FootprintManager;
+ rAssert( spFootprintManager );
+ HeapManager::GetInstance()->PopHeap(GMA_PERSISTENT);
+ return FootprintManager::GetInstance();
+}
+
+FootprintManager* FootprintManager::GetInstance( void )
+{
+ rAssertMsg( spFootprintManager != 0, "FootprintManager has not been created yet.\n" );
+ return spFootprintManager;
+}
+
+void FootprintManager::DestroyInstance( void )
+{
+ rAssertMsg( spFootprintManager != 0, "FootprintManager has not been created.\n" );
+ delete spFootprintManager;
+ spFootprintManager = 0;
+}
+
+bool FootprintManager::CreateFootprint( const rmt::Matrix& transform, TYPE type )
+{
+
+ bool footprintCreated;
+
+ if ( mpTextures[ type ] == NULL )
+ return false;
+
+ Footprint footprint;
+ footprint.alpha = 1.0f;
+ for ( int i = 0 ; i < 4 ; i++ )
+ {
+ transform.Transform( footprint.points[i], &footprint.points[i] );
+ }
+
+ if ( m_ActiveFootprints[ type ].mSize > m_ActiveFootprints[ type ].mUseSize )
+ {
+ m_ActiveFootprints[ type ].Add( footprint );
+ footprintCreated = true;
+ }
+ else
+ {
+ footprintCreated = false;
+ }
+ return footprintCreated;
+}
+
+void FootprintManager::Render()
+{
+ return;
+ int i,j;
+
+
+ SetTexture( FootprintManager::eSquishies, "footprint.bmp" );
+
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_LESS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_LESS );
+ }
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+ for ( i = 0 ; i < eNumFootprintTypes ;i++)
+ {
+ if ( m_ActiveFootprints[i].mUseSize == 0 )
+ continue;
+
+ if ( mpTextures[i] == NULL )
+ continue;
+
+ pddiShader* pShader = GetBootupContext()->GetSharedShader();
+ pShader->SetInt( PDDI_SP_ISLIT, 0 );
+ pShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ pShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+ pShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ pShader->SetTexture( PDDI_SP_BASETEX, mpTextures[ i ]->GetTexture() );
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( pShader, PDDI_PRIM_TRIANGLES, PDDI_V_CT, m_ActiveFootprints[i].mUseSize * 6 );
+
+ for ( j = 0 ; j < m_ActiveFootprints[i].mUseSize ; j++ )
+ {
+ Footprint& footprint = m_ActiveFootprints[i][j];
+ tColour colour;
+ colour.Set( 255,255,255, static_cast< int >( footprint.alpha * 255.0f ));
+
+ // 2 triangles for every foot step
+ // Now draw the triangle.
+ // bottom left tri.
+ stream->Colour( colour );
+ stream->UV( 0.0f, 0.0f );
+ stream->Coord( footprint.points[0].x, footprint.points[0].y, footprint.points[0].z );
+
+ stream->Colour( colour );
+ stream->UV( 1.0f, 0.0f );
+ stream->Coord( footprint.points[1].x, footprint.points[1].y, footprint.points[1].z );
+
+ stream->Colour( colour );
+ stream->UV( 0.0f, 1.0f );
+ stream->Coord( footprint.points[2].x, footprint.points[2].y, footprint.points[2].z );
+
+ // Top right tri.
+ stream->Colour( colour );
+ stream->UV( 1.0f, 1.0f );
+ stream->Coord( footprint.points[3].x, footprint.points[3].y, footprint.points[3].z );
+
+ stream->Colour( colour );
+ stream->UV( 0.0f, 1.0f );
+ stream->Coord( footprint.points[2].x, footprint.points[2].y, footprint.points[2].z );
+
+ stream->Colour( colour );
+ stream->UV( 1.0f, 0.0f );
+ stream->Coord( footprint.points[1].x, footprint.points[1].y, footprint.points[1].z );
+
+ }
+ p3d::pddi->EndPrims( stream );
+ pShader->SetTexture( PDDI_SP_BASETEX, 0 );
+
+ }
+
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::pddi->SetCullMode(cm);
+
+
+}
+
+void FootprintManager::Update( unsigned int timeInMS )
+{
+ for ( int i = 0 ; i < eNumFootprintTypes ; i++ )
+ {
+ for ( int j = 0 ; j < m_ActiveFootprints[ i ].mUseSize ; j++ )
+ {
+ m_ActiveFootprints[i][j].alpha -= static_cast< float >( timeInMS )* FOOTPRINT_FADE_PER_MS;
+ if ( m_ActiveFootprints[i][j].alpha < 0 )
+ {
+ m_ActiveFootprints[i].Remove( j );
+ }
+ }
+ }
+}
+
+void FootprintManager::SetTexture( TYPE type, const char* textureName )
+{
+ tTexture* pTexture = p3d::find< tTexture >( textureName );
+ if ( pTexture != NULL )
+ {
+ tRefCounted::Assign( mpTextures[type], pTexture );
+ }
+}
+
+void FootprintManager::FreeTextures()
+{
+ for ( int i = 0 ; i < eNumFootprintTypes ; i++)
+ {
+ if ( mpTextures[i] != NULL )
+ {
+ mpTextures[ i ]->Release();
+ mpTextures[ i ] = NULL;
+ }
+ }
+}
+
+
diff --git a/game/code/worldsim/character/footprint/footprintmanager.h b/game/code/worldsim/character/footprint/footprintmanager.h
new file mode 100644
index 0000000..12f83d2
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprintmanager.h
@@ -0,0 +1,105 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprintmanager
+//
+// Description: Holds a list of all footprints, and fades them out over time
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef FOOTPRINTMANAGER_H
+#define FOOTPRINTMANAGER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <worldsim\character\footprint\footprintmanager.h>
+#include <render\culling\swaparray.h>
+#include <p3d\utility.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tShader;
+class tTexture;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Footprint manager - creates and renders footprints
+//
+// Constraints:
+//
+//
+//===========================================================================
+class FootprintManager
+{
+ public:
+ FootprintManager();
+ ~FootprintManager();
+
+ static FootprintManager* GetInstance( void );
+ static FootprintManager* CreateInstance( void );
+ static void DestroyInstance( void );
+
+ enum { MAX_NUM_FOOTPRINTS = 1 };
+ enum TYPE { eSquishies, eNumFootprintTypes };
+
+ bool CreateFootprint( const rmt::Matrix& transform, TYPE type );
+ void Update( unsigned int timeInMS );
+ void FreeTextures();
+
+ void SetTexture( TYPE, const char* texture );
+ void ClearAllFootPrints();
+ void Render();
+
+ protected:
+
+ private:
+
+ static FootprintManager* spFootprintManager;
+
+ struct Footprint
+ {
+ Footprint();
+
+ rmt::Vector points[4];
+ float alpha;
+ };
+
+ typedef SwapArray< Footprint > ListOfFootprints;
+
+ ListOfFootprints m_ActiveFootprints[eNumFootprintTypes];
+
+ tTexture* mpTextures[ eNumFootprintTypes ];
+
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow FootprintManager from being copied and assigned.
+ FootprintManager( const FootprintManager& );
+ FootprintManager& operator=( const FootprintManager& );
+
+ // A little syntactic sugar for getting at this singleton.
+};
+
+inline FootprintManager* GetFootprintManager() { return( FootprintManager::GetInstance() ); }
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/coins/allcoins.cpp b/game/code/worldsim/coins/allcoins.cpp
new file mode 100644
index 0000000..b8ea98e
--- /dev/null
+++ b/game/code/worldsim/coins/allcoins.cpp
@@ -0,0 +1,2 @@
+#include <worldsim/coins/coinmanager.cpp>
+#include <worldsim/coins/sparkle.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/coins/coinmanager.cpp b/game/code/worldsim/coins/coinmanager.cpp
new file mode 100644
index 0000000..0f7d50d
--- /dev/null
+++ b/game/code/worldsim/coins/coinmanager.cpp
@@ -0,0 +1,1132 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: CoinManager.cpp
+//
+// Description: Implementation of class CoinManager
+//
+// History: 29/1/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radmath/matrix.hpp>
+#include <radmath/random.hpp>
+#include <p3d/entity.hpp>
+#include <p3d/view.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/matrixstack.hpp>
+#include <pddi/pddi.hpp>
+
+//#include <math.h> //need this if we want extra accurate coin paths.
+
+//========================================
+// Project Includes
+//========================================
+#include <gameflow/gameflow.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <render/intersectmanager/intersectmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <data/persistentworldmanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+/* Watcher stuff */
+#ifndef RAD_RELEASE
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+static float COIN_HOVER = 0.5f; // How far off the ground will the coin hover.
+#if !defined( RAD_PS2 ) && defined( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 60.0f;
+//static float COIN_DRAG = 0.9848f; // .4 to the power of 1/60 of a second.
+#elif defined( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 30.0f;
+//static float COIN_DRAG = 0.9700f; // .4 to the power of 1/30 of a second.
+#else
+static const float FRAME_RATIO = 1.0f / 15.0f;
+//static float COIN_DRAG = 0.9407f; // .4 to the power of 1/15 of a second.
+#endif
+static float COIN_SPAWN_VELOCITY = 9.5f;// * FRAME_RATIO;
+static float COIN_EXTRA_VERTICAL = 1.1f;// * FRAME_RATIO;
+// Use these values if you want accurate coins.
+static float GRAVITY = 21.0f; // * FRAME_RATIO;
+static float COIN_DRAG = 0.1f;
+
+// Use these if you want faster (but less accurate coins.
+//static float GRAVIYT = 0.9f * FRAME_RATIO;
+
+static float LIFE_TIME = 5.0f; // How many seconds the coin lives for.
+static float DECAY_TIME = 5.0f; // How long does it take to disappear.
+#define I_DECAY_TIME ( 1.0f / DECAY_TIME ) // Instead of doing a divide.
+static float DECAY_EXTRA_SPIN = rmt::PI * 8.0f; // How much extra spin per frame do we want.
+static float SPIN_MULTIPLIER = 3.0f;
+static float RANGE = 2.25f; // Coin collection radius.
+#define INNER_RANGE ( RANGE * ( 4.0f / 3.0f ) )
+static float COLLECT_TIME = 1.0f; // How long does it take a coin to suck into the player.
+#define I_COLLECT_TIME ( 1.0f / COLLECT_TIME )
+static float POP_SCALE = 0.4f; // How much pop up does the coin have when it is collected.
+static float BOUNCE_DAMPENING = 0.6f; // How much energy does the coin retain when it hits the ground.
+
+static float FLYING_TIME = 0.25f; // How long does it take to fly up to the HUD.
+#define I_FLYING_TIME ( 1.0f / FLYING_TIME )
+static float MAX_VISIBLITY = 60.0f; // How far away is the coin.
+static float COUNTER_X = 0.45f; // Where is the destination we are flying the coin up to.
+static float COUNTER_Y = 0.45f; // Where is the destination we are flying the coin up to.
+#ifdef RAD_XBOX
+static float SPARKLE_FLOAT = 0.025f; // How much vertical velocity for the sparkles per frame.
+#else
+static float SPARKLE_FLOAT = 0.05f;
+#endif
+static float IN_CAR_RANGE_MULTIPLIER = 2.25f;
+static int dbg_CoinsDrawn = 0;
+static int dbg_MaxCoins = 0;
+
+#else /* Same as the variables available to the watcher, but const so the compiler can optimize them. */
+const static float COIN_HOVER = 0.5f; // How far off the ground will the coin hover.
+// Use this for more accurate coins.
+//static const float COIN_SPAWN_VELOCITY = 4.0f;
+//static const float COIN_EXTRA_VERTICAL = 2.0f
+#if !defined( RAD_PS2 ) && defined ( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 60.0f;
+//static const float COIN_DRAG = 0.9848f; // .4 to the power of 1/60 of a second.
+#elif defined( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 30.0f;
+//static const float COIN_DRAG = 0.9700f; // .4 to the power of 1/30 of a second.
+#else
+static const float FRAME_RATIO = 1.0f / 15.0f;
+//static const float COIN_DRAG = 0.9407f; // .4 to the power of 1/15 of a second.
+#endif
+static const float COIN_SPAWN_VELOCITY = 9.5f;// * FRAME_RATIO;
+static const float COIN_EXTRA_VERTICAL = 1.1f;// * FRAME_RATIO;
+static const float GRAVITY = 21.0f;
+static const float COIN_DRAG = 0.1f;
+//static const float GRAVITY = 0.9f * FRAME_RATIO;
+static const float LIFE_TIME = 11.0f; // How many seconds the coin lives for.
+static const float DECAY_TIME = 5.0f; // How long does it take to disappear.
+static const float I_DECAY_TIME = 1.0f / DECAY_TIME; // Instead of doing a divide.
+static const float DECAY_EXTRA_SPIN = rmt::PI * 8.0f; // How much extra spin per frame do we want.
+static const float SPIN_MULTIPLIER = 3.0f;
+static const float RANGE = 2.25f; // Coin collection radius.
+static const float INNER_RANGE = RANGE * ( 4.0f / 3.0f );
+static const float COLLECT_TIME = 1.0f; // How long does it take a coin to suck into the player.
+static const float I_COLLECT_TIME = 1.0f / COLLECT_TIME;
+static const float POP_SCALE = 0.4f; // How much pop up does the coin have when it is collected.
+// Use these values if you want accurate coins.
+/*
+static const float COIN_DRAG = 0.5f;
+static const float GRAVITY = 0.9f;
+*/
+static const float BOUNCE_DAMPENING = 0.6f; // How much energy does the coin retain when it hits the ground.
+static const float FLYING_TIME = 0.25f; // How long does it take to fly up to the HUD.
+static const float I_FLYING_TIME = 1.0f / FLYING_TIME;
+static const float MAX_VISIBLITY = 60.0f; // How far away is the coin.
+static const float COUNTER_X = 0.25f; // Where is the destination we are flying the coin up to.
+static const float COUNTER_Y = 0.4f; // Where is the destination we are flying the coin up to.
+#ifdef RAD_XBOX
+static const float SPARKLE_FLOAT = 0.025f;
+#else
+static const float SPARKLE_FLOAT = 0.05f;
+#endif
+static const float IN_CAR_RANGE_MULTIPLIER = 2.25f;
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+CoinManager* CoinManager::spCoinManager = 0;
+
+static const int NUM_COINS = 200;
+static const int NUM_SPARKLES = 120; // Although we seldom use all these sparkles we do when the vehicle explodes.
+#ifdef RAD_XBOX
+static int Sparkle_Rate = 6;
+static const int Glint_Rate = 4;
+static const int SPARKLE_RATE_TOGGLE = Sparkle_Rate ^ ( Sparkle_Rate + 2 );
+static const int HUD_SPARKLE_RATE = 60;
+#else
+static int Sparkle_Rate = 3;
+static const int Glint_Rate = 2;
+static const int SPARKLE_RATE_TOGGLE = Sparkle_Rate ^ ( Sparkle_Rate + 1 );
+static const int HUD_SPARKLE_RATE = 30;
+#endif
+static int DoSparkle = 0;
+static int DoGlint = 0;
+static int GlintDelay = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+CoinManager* CoinManager::CreateInstance( void )
+{
+ rAssertMsg( spCoinManager == 0, "CoinManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ spCoinManager = new CoinManager;
+ rAssert( spCoinManager );
+ HeapManager::GetInstance()->PopHeap( GMA_PERSISTENT );
+ return CoinManager::GetInstance();
+}
+
+CoinManager* CoinManager::GetInstance( void )
+{
+ rAssertMsg( spCoinManager != 0, "CoinManager has not been created yet.\n" );
+ return spCoinManager;
+}
+
+void CoinManager::DestroyInstance( void )
+{
+ rAssertMsg( spCoinManager != 0, "CoinManager has not been created.\n" );
+ delete spCoinManager;
+ spCoinManager = 0;
+}
+
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CoinManager::CoinManager() :
+ m_pCoinDrawable( 0 ),
+ mActiveCoins( NULL),
+ mNumActiveCoins( 0 ),
+ mNextInactiveCoin( 0 ),
+ mNumHUDFlying( 0 ),
+ mHUDSparkle( 0 ),
+ mHUDCoinX( -10.0f ),
+ mHUDCoinY( -10.0f ),
+ mHUDCoinAngle( 0.0f ),
+ mDrawAfterGui(false)
+{
+#ifndef RAD_RELEASE
+ radDbgWatchAddFloat( &COIN_SPAWN_VELOCITY, "Spawn - Initial velocity", "Coins", 0, 0, 0.01f, 5.0f );
+ radDbgWatchAddFloat( &COIN_EXTRA_VERTICAL, "Spawn - Initial extra vertical", "Coins", 0, 0, 0.0f, 2.5f );
+ radDbgWatchAddFloat( &COIN_DRAG, "Spawn - Movement drag", "Coins", 0, 0, 0.1f, 1.0f );
+ radDbgWatchAddFloat( &GRAVITY, "Spawn - Gravity", "Coins", 0, 0, 0.1f, 2.0f );
+ radDbgWatchAddFloat( &BOUNCE_DAMPENING, "Spawn - Bounce energy dampening", "Coins", 0, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat( &COIN_HOVER, "Spawn - Ground hover", "Coins", 0, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat( &LIFE_TIME, "Duration - Life", "Coins", 0, 0, 1.0f, 30.0f );
+ radDbgWatchAddFloat( &DECAY_TIME, "Duration - Decay", "Coins", 0, 0, 1.0f, 10.0f );
+ radDbgWatchAddFloat( &RANGE, "Collection - Radius", "Coins", 0, 0, 0.5f, 10.0f );
+ radDbgWatchAddFloat( &IN_CAR_RANGE_MULTIPLIER, "Collection - In car multiplier", "Coins", 0, 0, 0.0f, 5.0f );
+ radDbgWatchAddFloat( &DECAY_EXTRA_SPIN, "Animation - Extra Decay spin", "Coins", 0, 0, 0.0f, 50.0f );
+ radDbgWatchAddFloat( &SPIN_MULTIPLIER, "Animation - Spin multiplier", "Coins", 0, 0, 0.01f, 5.0f );
+ radDbgWatchAddFloat( &COLLECT_TIME, "Animation - Collection duration", "Coins", 0, 0, 0.1f, 5.0f );
+ radDbgWatchAddFloat( &POP_SCALE, "Animation - Collection pop up", "Coins", 0, 0, 0.1f, 1.0f );
+ radDbgWatchAddFloat( &FLYING_TIME, "Animation - Fly to HUD duration", "Coins", 0, 0, 0.1f, 1.0f );
+ radDbgWatchAddFloat( &COUNTER_X, "Animation - HUD X position", "Coins", 0, 0, -0.7f, 0.7f );
+ radDbgWatchAddFloat( &COUNTER_Y, "Animation - HUD Y position", "Coins", 0, 0, -0.7f, 0.7f );
+ radDbgWatchAddFloat( &SPARKLE_FLOAT, "Animation - Sparkle float", "Coins", 0, 0, 0.0f, 0.5f );
+ radDbgWatchAddFloat( &MAX_VISIBLITY, "Visibility culling distance", "Coins", 0, 0, 10.0f, 100.0f );
+ radDbgWatchAddShort( &mNumActiveCoins, "Active coin count", "Coins", 0, 0, 0, NUM_COINS, true );
+ radDbgWatchAddInt( &dbg_CoinsDrawn, "Drawn coin count", "Coins", 0, 0, 0, NUM_COINS, true );
+ radDbgWatchAddInt( &dbg_MaxCoins, "Max active coins", "Coins", 0, 0, 0, NUM_COINS, true );
+#endif
+}
+
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CoinManager::~CoinManager()
+{
+#ifndef RAD_RELEASE
+ radDbgWatchDelete( &COIN_SPAWN_VELOCITY );
+ radDbgWatchDelete( &COIN_EXTRA_VERTICAL );
+ radDbgWatchDelete( &COIN_DRAG );
+ radDbgWatchDelete( &GRAVITY );
+ radDbgWatchDelete( &BOUNCE_DAMPENING );
+ radDbgWatchDelete( &COIN_HOVER );
+ radDbgWatchDelete( &LIFE_TIME );
+ radDbgWatchDelete( &DECAY_TIME );
+ radDbgWatchDelete( &RANGE );
+ radDbgWatchDelete( &DECAY_EXTRA_SPIN );
+ radDbgWatchDelete( &SPIN_MULTIPLIER );
+ radDbgWatchDelete( &COLLECT_TIME );
+ radDbgWatchDelete( &POP_SCALE );
+ radDbgWatchDelete( &FLYING_TIME );
+ radDbgWatchDelete( &COUNTER_X );
+ radDbgWatchDelete( &COUNTER_Y );
+ radDbgWatchDelete( &SPARKLE_FLOAT );
+ radDbgWatchDelete( &MAX_VISIBLITY );
+ radDbgWatchDelete( &mNumActiveCoins );
+ radDbgWatchDelete( &dbg_CoinsDrawn );
+ radDbgWatchDelete( &dbg_MaxCoins );
+#endif
+ tRefCounted::Release( m_pCoinDrawable );
+ Destroy( );
+}
+
+//==============================================================================
+// Description: Destruction method for all transient data.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void CoinManager::Destroy( void )
+{
+ SetCoinDrawable( NULL );
+
+ if( mActiveCoins != NULL )
+ {
+ GetCheatInputSystem()->UnregisterCallback( this );
+
+ GetEventManager()->RemoveAll( this );
+
+ delete [] mActiveCoins;
+ mActiveCoins = NULL;
+ }
+ mNumHUDFlying = 0;
+ mNumActiveCoins = 0;
+}
+
+/*==============================================================================
+Description: This will be called set up as the game session begins.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================*/
+void CoinManager::Init( void )
+{
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ mActiveCoins = new ActiveCoin[ NUM_COINS ];
+ rAssert( mActiveCoins );
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+ mNextInactiveCoin = 0;
+ mNumActiveCoins = 0;
+ mNumHUDFlying = 0;
+ DoSparkle = 0;
+ DoGlint = 0;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = 0;
+#endif
+
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_DUMP_DYNA_SECTION );
+
+ GetCheatInputSystem()->RegisterCallback( this );
+}
+
+void CoinManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ Vehicle* vehicle = reinterpret_cast<Vehicle*>( pEventData );
+ OnVehicleDestroyed( vehicle );
+ }
+ break;
+ case EVENT_DUMP_DYNA_SECTION:
+ {
+ RemoveWorldCoins( reinterpret_cast<tName*>( pEventData )->GetUID() );
+ }
+ break;
+ default:
+ {
+ break;
+ }
+ }
+}
+
+/*=============================================================================
+Go nosing through he active coin array looking for an inactive coin. Update
+the next inactive coin index while we are at it to speed up the search.
+=============================================================================*/
+CoinManager::ActiveCoin* CoinManager::GetInactiveCoin( void )
+{
+ if( mNumActiveCoins >= NUM_COINS )
+ {
+ return 0;
+ }
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin* c = &(mActiveCoins[ mNextInactiveCoin ]);
+ mNextInactiveCoin = ( mNextInactiveCoin + 1 ) % NUM_COINS;
+ if( c->State == CS_Inactive )
+ {
+ c->HeadingCos = 0.0f;
+ c->HeadingSin = 1.0f;
+ return c;
+ }
+ }
+ return 0;
+}
+
+/*=============================================================================
+Spawn count number of coins at position. You can optionally override the
+groundY position (handy for exploding wasps), or it takes the Y value form
+the position.
+=============================================================================*/
+void CoinManager::SpawnCoins( int Count, const rmt::Vector& Position, const rmt::Vector* Direction, bool Force )
+{
+ SpawnCoins( Count, Position, Position.y, Direction, Force );
+}
+
+void CoinManager::SpawnCoins( int Count, const rmt::Vector& Position, float GroundY, const rmt::Vector* Direction, bool Force )
+{
+ rAssert( Count >= 0 );
+ if( Count <= 0 )
+ {
+ return;
+ }
+
+
+ // Check for a valid avatar pointer
+ rTuneAssert( GetAvatarManager() != NULL );
+ rTuneAssert( GetAvatarManager()->GetAvatarForPlayer(0) != NULL );
+ bool instaCollect = !Force && GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar();
+
+ for( int i = 0; i < Count; ++i )
+ {
+ ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ CollectCoins( Count - i );
+ break;
+ }
+ SpawnCoin( *c, Position, GroundY + COIN_HOVER, Direction, instaCollect );
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_SPAWNED_COINS, reinterpret_cast<void*>( Count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+
+}
+
+/*=============================================================================
+This spawns a coin which is immediately collected by the player. Used for giving
+the player a coin reward and you want to make sure they don't miss it.
+=============================================================================*/
+void CoinManager::SpawnInstantCoins(int Count, const rmt::Vector& Position)
+{
+ rAssert( Count >= 0 );
+ if( Count <= 0 )
+ {
+ return;
+ }
+
+ // Check for a valid avatar pointer
+ rTuneAssert( GetAvatarManager() != NULL );
+ rTuneAssert( GetAvatarManager()->GetAvatarForPlayer(0) != NULL );
+
+ for( int i = 0; i < Count; ++i )
+ {
+ ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ CollectCoins( Count - i );
+ break;
+ }
+ SpawnCoin(*c, Position, Position.y + COIN_HOVER, 0, true);
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_SPAWNED_COINS, reinterpret_cast<void*>( Count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+}
+
+/*=============================================================================
+Spawn a single coin.
+=============================================================================*/
+void CoinManager::SpawnCoin( ActiveCoin& Coin, const rmt::Vector& Start, float Ground, const rmt::Vector* Direction, bool InstaCollect )
+{
+ Coin.Position = Start;
+ Coin.Position.y = Ground + COIN_HOVER;
+ Coin.State = InstaCollect ? CS_SpawnToCollect : CS_InitialSpawning;
+ Coin.Age = Sparkle::sRandom.Float() * rmt::PI;
+ Coin.Ground = Ground;
+ Coin.Velocity.x = Sparkle::sRandom.FloatSigned();
+ Coin.Velocity.y = Sparkle::sRandom.Float();
+ Coin.Velocity.z = Sparkle::sRandom.FloatSigned();
+ Coin.Velocity.Normalize();
+ Coin.Velocity.y += COIN_EXTRA_VERTICAL;
+ if( Direction )
+ {
+ if( Direction->Dot( Coin.Velocity ) < 0 )
+ {
+ Coin.Velocity.x *= -1.0f;
+ Coin.Velocity.z *= -1.0f;
+ }
+ }
+ Coin.Velocity.Scale(COIN_SPAWN_VELOCITY);
+ ++mNumActiveCoins;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = rmt::Max( (int)mNumActiveCoins, dbg_MaxCoins );
+#endif
+}
+
+/*=============================================================================
+Add a HUD coin at the coin counter position and have it fly down to the middle
+of the screen. Used when the player's coin amount decrements so that they can
+see that it's going down a little easier.
+=============================================================================*/
+void CoinManager::AddFlyDownCoin(void)
+{
+ return;
+ // They didn't like it. Method is now a nop...until they decide they want it again.
+/* ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ return;
+ }
+ c->Position.x = 0.0f;
+ c->Position.y = 0.0f;
+ c->Position.z = 0.0f;
+ c->State = CS_FlyingFromHUD;
+ c->Age = 0.0f;
+ c->Velocity.Set(-0.5f, -0.5f, 0.0f);
+ c->Velocity.Scale(FLYING_TIME * 2.0f);
+ ++mNumActiveCoins;
+ ++mNumHUDFlying;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = rmt::Max( (int)mNumActiveCoins, dbg_MaxCoins );
+#endif
+*/
+}
+
+/*=============================================================================
+Update the position/animation of all the coins.
+=============================================================================*/
+void CoinManager::Update( int ElapsedMS )
+{
+ float deltaSeconds = (float)ElapsedMS * 0.001f;
+ mHUDCoinAngle += deltaSeconds;
+#ifndef RAD_RELEASE
+ dbg_CoinsDrawn = 0;
+#endif
+ if(GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE)
+ {
+ return;
+ }
+ rmt::Vector avatarPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( avatarPos );
+ bool isInCar = GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar();
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if( c.State != CS_Inactive )
+ {
+ float decaySpin = 0.0f;
+ if( c.State != CS_InitialSpawning && c.State != CS_SpawnToCollect && c.State != CS_Collecting && c.State != CS_Collected )
+ {
+ CheckCollection( c, avatarPos, isInCar ? IN_CAR_RANGE_MULTIPLIER : 1.0f );
+ }
+ if( c.State == CS_Collecting )
+ {
+ UpdateCollecting( c, avatarPos );
+ }
+ else if((c.State == CS_FlyingToHUD) || (c.State == CS_FlyingFromHUD))
+ {
+ UpdateHUDFlying( c );
+ }
+ else if( c.State != CS_RestingIndefinitely && c.State != CS_Collecting && c.State != CS_Collected )
+ {
+ if( c.Age > LIFE_TIME + DECAY_TIME )
+ {
+ // Coin has expired.
+ c.State = CS_Inactive;
+ --mNumActiveCoins;
+ GetSparkleManager()->AddSparkle( rmt::Vector( c.Position.x, c.Position.y + mCoinBounding.radius, c.Position.z ), 0.25f, 0.5f, rmt::Vector( 0.0f, 0.01f, 0.0f ), Sparkle::SE_Vanish );
+ }
+ else if( c.Age > LIFE_TIME )
+ {
+ c.State = CS_Decaying;
+ decaySpin = ( c.Age - LIFE_TIME ) * I_DECAY_TIME;
+ decaySpin *= decaySpin;
+ }
+ if( c.State == CS_Spawning || c.State == CS_InitialSpawning || c.State == CS_SpawnToCollect )
+ {
+ UpdateSpawning(c, avatarPos, deltaSeconds);
+ }
+ }
+ c.Age += deltaSeconds;
+ // Rotate the coin based on age.
+ float coinSin, coinCos;
+ rmt::SinCos( ( c.Age * SPIN_MULTIPLIER ) + ( decaySpin * DECAY_EXTRA_SPIN ), &coinSin, &coinCos );
+ c.HeadingCos = coinCos;
+ c.HeadingSin = coinSin;
+ }
+ }
+}
+
+/*=============================================================================
+Damaging out the player's vehicle causes their bank to go down.
+=============================================================================*/
+void CoinManager::OnVehicleDestroyed( Vehicle* DestroyedVehicle )
+{
+ if( DestroyedVehicle->mVehicleType == VT_USER )
+ {
+ int lose = rmt::Min( 20, GetBankValue() );
+ LoseCoins( lose, &( DestroyedVehicle->GetPosition() ) );
+ }
+}
+
+/*=============================================================================
+Whatever drawable is set here will be used for all the coin drawables. Note
+that there isn't any support for shadows or animation. It's just geometry.
+=============================================================================*/
+void CoinManager::SetCoinDrawable( tDrawable* Drawable )
+{
+ tRefCounted::Assign( m_pCoinDrawable, Drawable );
+ if( m_pCoinDrawable )
+ {
+ m_pCoinDrawable->GetBoundingSphere( &mCoinBounding );
+ }
+ else
+ {
+ mCoinBounding.centre.Set( 0.0f, 0.0f, 0.0f );
+ mCoinBounding.radius = 0.0f;
+ }
+}
+
+/*=============================================================================
+Check if a coin is within range to do the collection animation to the player.
+If it is we set the state to Collecting.
+=============================================================================*/
+void CoinManager::CheckCollection( ActiveCoin& Coin, const rmt::Vector& AvatarPos, float RangeMultiplier )
+{
+ float x = rmt::Abs( Coin.Position.x - AvatarPos.x );
+ float z = rmt::Abs( Coin.Position.z - AvatarPos.z );
+ float y = rmt::Abs( Coin.Position.y - AvatarPos.y );
+ // Note, no multipler for Y.
+ if( ( x < ( RANGE * RangeMultiplier ) ) && ( z < ( RANGE * RangeMultiplier ) ) && ( y < RANGE ) && ( x + z < ( INNER_RANGE * RangeMultiplier ) ) )
+ {
+ if( Coin.State == CS_RestingIndefinitely )
+ {
+ GetPersistentWorldManager()->OnObjectBreak( Coin.PersistentObjectID );
+ }
+ Coin.State = CS_Collecting;
+ Coin.Age = 0.0f;
+ }
+}
+
+/*=============================================================================
+Player has picked up a coin. This will increase the player's bank value, fire
+a coin collected event so that we can play a sound
+=============================================================================*/
+void CoinManager::CollectCoins( int Count )
+{
+ AdjustBankValue( Count );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_COLLECTED_COINS, reinterpret_cast<void*>( Count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+}
+
+/*=============================================================================
+Player loses coins for whatever reason. We play a sound and HUD animation.
+If Position isn't null then we respawn the coins at the specified location.
+=============================================================================*/
+void CoinManager::LoseCoins( int Count, const rmt::Vector* Position )
+{
+ int count = rmt::Min( Count, GetBankValue() );
+ rAssert( count >= 0 );
+ AdjustBankValue( -count );
+ if( Position )
+ {
+ // Because we are losing the coins when we are shot (or car explodes) the position tends to be lower
+ //then for a crate. So we raise the position a tiny bit so that the coin's travel is the same as
+ //the designers tune for when kicking the crates around.
+ SpawnCoins( count, rmt::Vector(Position->x, Position->y + 0.25f, Position->z), Position->y, 0, true );
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_LOST_COINS, reinterpret_cast<void*>( count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+}
+
+int
+CoinManager::GetBankValue() const
+{
+ RenderEnums::LevelEnum currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ int bankValue = GetCharacterSheetManager()->GetNumberOfTokens( currentLevel );
+
+ return bankValue;
+}
+
+/*=============================================================================
+Modify the player's bank value without any special effects.
+=============================================================================*/
+void CoinManager::AdjustBankValue( int DeltaCoins )
+{
+/*
+ mBankValue += DeltaCoins;
+ mBankValue = rmt::Max( mBankValue, 0 );
+*/
+ // tell the character sheet manager
+ //
+ GetCharacterSheetManager()->AddTokens( GetGameplayManager()->GetCurrentLevelIndex(),
+ DeltaCoins );
+
+ if( DeltaCoins > 0 )
+ {
+ GetEventManager()->TriggerEvent( EVENT_COLLECTED_COINS );
+ }
+ else if( DeltaCoins < 0 )
+ {
+ GetEventManager()->TriggerEvent( EVENT_LOST_COINS );
+ }
+}
+
+/*
+/*=============================================================================
+Force bank to a specific value. No special effects.
+=============================================================================/
+void CoinManager::SetBankValue( int Coins )
+{
+ rAssert( Coins >= 0 );
+ mBankValue = Coins;
+
+ // update character sheet
+ //
+ GetCharacterSheetManager()->SetNumberOfTokens( GetGameplayManager()->GetCurrentLevelIndex(),
+ mBankValue );
+}
+*/
+
+/*=============================================================================
+World coins just sit around until their zone unloads. They take up space in the
+active coin array so don't use them lightly.
+=============================================================================*/
+void CoinManager::AddWorldCoin( const rmt::Vector& Position, tUID Sector )
+{
+ if( !mActiveCoins )
+ {
+ return;
+ }
+
+ short persistentID = GetPersistentWorldManager()->GetPersistentObjectID( Sector );
+ if( persistentID < -1 )
+ {
+ return;
+ }
+
+ ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ return;
+ }
+
+ c->Position = Position;
+ c->State = CS_RestingIndefinitely;
+ c->Age = Sparkle::sRandom.Float() * rmt::PI;
+ c->Sector = Sector;
+ c->PersistentObjectID = persistentID;
+ ++mNumActiveCoins;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = rmt::Max( (int)mNumActiveCoins, dbg_MaxCoins );
+#endif
+}
+
+/*=============================================================================
+Remove any coins resting in the world which are in the sector which was just
+unloaded. Only remove the coins which are in CS_RestingIndefinite state since
+others could be spawning or collecting, etc (that's kind of unlikely however).
+=============================================================================*/
+void CoinManager::RemoveWorldCoins( tUID Sector )
+{
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if( ( c.State == CS_RestingIndefinitely ) && ( c.Sector == Sector ) )
+ {
+ c.State = CS_Inactive;
+ --mNumActiveCoins;
+ }
+ }
+}
+
+/*=============================================================================
+Update the animation for the coins when they are collected.
+=============================================================================*/
+void CoinManager::UpdateCollecting( ActiveCoin& Coin, const rmt::Vector& AvatarPos )
+{
+ if( Coin.Age > ( COLLECT_TIME * 0.4f ) )
+ {
+ Coin.State = CS_Collected;
+ Coin.Age = 0.0f;
+ Coin.HeadingCos = 1;
+ CollectCoins( 1 );
+ }
+ else
+ {
+ rmt::Vector diff;
+ diff.Sub( AvatarPos, Coin.Position );
+ diff.Scale( Coin.Age * I_COLLECT_TIME );
+ float pop = ( COLLECT_TIME - Coin.Age ) * I_COLLECT_TIME;
+ diff.y += pop * POP_SCALE;
+ Coin.Position.Add( diff );
+ --DoSparkle;
+ if( DoSparkle < 0 )
+ {
+ DoSparkle = Sparkle_Rate;
+ Sparkle_Rate ^= SPARKLE_RATE_TOGGLE;
+ rmt::Vector vel;
+ vel.Scale(FRAME_RATIO * 0.1f, Coin.Velocity);
+ vel.y += SPARKLE_FLOAT;
+ GetSparkleManager()->AddSparkle( Coin.Position, 0.5f, 1.0f, vel, Sparkle::SE_Trail );
+ }
+ }
+}
+
+/*=============================================================================
+Update the spawning animation for the coins.
+=============================================================================*/
+void CoinManager::UpdateSpawning(ActiveCoin& Coin, const rmt::Vector& AvatarPos, float DeltaSeconds)
+{
+ rmt::Vector vel;
+ vel.Scale(DeltaSeconds, Coin.Velocity);
+ Coin.Velocity.Scale( (float)pow(COIN_DRAG, DeltaSeconds) );
+ Coin.Velocity.y -= GRAVITY * DeltaSeconds;
+ Coin.Position.Add( vel );
+ // Use this if we want faster (but less accurate) coins.
+ /*
+ Coin.Velocity.Scale( COIN_DRAG );
+ Coin.Velocity.y -= GRAVITY;
+ Coin.Position.Add( Coin.Velocity );
+ */
+ if( Coin.State == CS_InitialSpawning )
+ {
+ --DoSparkle;
+ if( DoSparkle < 0 )
+ {
+ DoSparkle = Sparkle_Rate;
+ Sparkle_Rate ^= SPARKLE_RATE_TOGGLE;
+ GetSparkleManager()->AddSparkle( Coin.Position, 0.5f, 1.0f, rmt::Vector( Coin.Velocity.x * 0.1f, ( Coin.Velocity.y * 0.1f ) + SPARKLE_FLOAT, Coin.Velocity.z * 0.1f ), Sparkle::SE_Trail );
+ }
+ }
+ if( Coin.Position.y < Coin.Ground )
+ {
+ // Bounce.
+ Coin.Velocity.y = rmt::Abs( Coin.Velocity.y * BOUNCE_DAMPENING );
+ if( Coin.State == CS_SpawnToCollect )
+ {
+ Coin.State = CS_Collecting;
+ Coin.Age = 0.0f;
+ }
+ else
+ {
+ Coin.State = CS_Spawning;
+ if( Coin.Velocity.y < 0.01f )
+ {
+ Coin.State = CS_Resting;
+ }
+ }
+ Coin.Position.y = Coin.Ground;
+ }
+}
+
+void CoinManager::AddGlint( ActiveCoin& Coin, const rmt::Vector& ToCamera, const rmt::Vector& CoinAxis, const rmt::Vector& CameraRight )
+{
+ rmt::Vector pos;
+ pos.Add( Coin.Position, mCoinBounding.centre );
+ float dot = ToCamera.DotProduct( CoinAxis );
+ float radius = ( dot < 0.0f ) ? -mCoinBounding.radius : mCoinBounding.radius;
+ pos.ScaleAdd( radius, CoinAxis );
+ rmt::Vector left;
+ left.Scale( -0.0025f, CameraRight );
+ GetSparkleManager()->AddSparkle( pos, 1.0f, 0.25f, left, Sparkle::SE_Glint );
+}
+
+/*=============================================================================
+Animation of the coin zipping up to the HUD counter.
+=============================================================================*/
+void CoinManager::UpdateHUDFlying( ActiveCoin& Coin )
+{
+ // Do fly to HUD animation.
+ if(((Coin.State == CS_FlyingToHUD) && (Coin.Age > FLYING_TIME )) ||
+ ((Coin.State == CS_FlyingFromHUD) && (Coin.Age > 0.5f)))
+ {
+ Coin.State = CS_Inactive;
+ --mNumActiveCoins;
+ --mNumHUDFlying;
+ }
+}
+
+void CoinManager::ClearHUDCoins(void)
+{
+ if(mNumHUDFlying == 0)
+ {
+ return;
+ }
+ for(int i = 0; i < NUM_COINS; ++i)
+ {
+ ActiveCoin& c = mActiveCoins[i];
+ if((c.State == CS_FlyingToHUD) || (c.State == CS_FlyingFromHUD))
+ {
+ c.State = CS_Inactive;
+ --mNumActiveCoins;
+ --mNumHUDFlying;
+ }
+ }
+}
+
+/*=============================================================================
+Render pass called form within the opaque rendering stage of world render. It
+does a simple distance and camera culling check to determine which coins to
+draw. We don't add the coins to the DSG since the overhead of moving/adding/
+removing them is too much. Also, the DSG nodes are hard coded to only allow a
+certain number of extra entities over the amount preallocated in the pipeline.
+It's very possible that we'll end up with more then then that value.
+=============================================================================*/
+void CoinManager::Render( void )
+{
+ if( ( mNumActiveCoins - mNumHUDFlying ) > 0 )
+ {
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ const rmt::Matrix& camTrans = camera->GetCameraToWorldMatrix();
+
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if( c.State != CS_Inactive )
+ {
+ if( c.State == CS_Collected )
+ {
+ camera->WorldToView( c.Position, &c.Position );
+ c.Velocity.Sub( rmt::Vector( COUNTER_X, COUNTER_Y, 0.0f ), c.Position );
+ c.State = CS_FlyingToHUD;
+ ++mNumHUDFlying;
+ continue;
+ }
+ if( !m_pCoinDrawable )
+ {
+ continue;
+ }
+ // Visiblity test.
+ rmt::Vector diff;
+ diff.Sub( camTrans.Row( 3 ), c.Position );
+ float camDirDot = diff.DotProduct( camTrans.Row( 2 ) );
+ // Note that the diff vector is towards the camera so the camDirDot will be negative when the camera is facing
+ //the object. So if it's positive then we can assume it's behind the camera.
+ if( camDirDot > 0.0f )
+ {
+ continue;
+ }
+ // Distance check.
+ float distance = diff.Magnitude();
+ if( distance > MAX_VISIBLITY )
+ {
+ continue;
+ }
+#ifndef RAD_RELEASE
+ ++dbg_CoinsDrawn;
+#endif
+ if( ( GlintDelay <= 0 ) && ( DoGlint == i ) )
+ {
+ if( c.State != CS_InitialSpawning && c.State != CS_Collecting && c.State != CS_Collected )
+ {
+ if(GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE)
+ {
+ AddGlint( c, diff, rmt::Vector( c.HeadingCos, 0.0f, -c.HeadingSin ), camTrans.Row( 0 ) );
+ }
+ }
+ }
+ // Down the columns, across the rows.
+ rmt::Matrix transform( c.HeadingCos, 0.0f, -c.HeadingSin, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ c.HeadingSin, 0.0f, c.HeadingCos, 0.0f,
+ c.Position.x, c.Position.y, c.Position.z, 1.0f );
+
+ p3d::pddi->PushMultMatrix( PDDI_MATRIX_MODELVIEW, &transform );
+ m_pCoinDrawable->Display();
+ p3d::pddi->PopMatrix( PDDI_MATRIX_MODELVIEW );
+ }
+ }
+ }
+ --GlintDelay;
+ if( GlintDelay < 0 )
+ {
+ GlintDelay = Glint_Rate;
+ DoGlint = Sparkle::sRandom.IntRanged( 0, NUM_COINS );
+ }
+}
+
+void CoinManager::SetHUDCoin( int X, int Y, bool IsShowing )
+{
+ const static float ScreenWidthRatio = 1.0f / 640.0f;
+ const static float ScreenHeightRatio = 1.0f / 480.0f;
+ const static float ScreenAspect = 4.0f / 3.0f;
+ if( IsShowing )
+ {
+ mHUDCoinX = ( ( ( (float)X * ScreenWidthRatio ) ) - 0.5f ) * ScreenAspect * ScreenAspect;
+ mHUDCoinY = ( ( ( (float)Y * ScreenHeightRatio ) ) - 0.5f ) * ScreenAspect;
+ }
+ else
+ {
+ mHUDCoinX = -10.f;
+ }
+}
+
+void CoinManager::OnCheatEntered( eCheatID cheatID, bool isEnabled )
+{
+ if( cheatID == CHEAT_ID_EXTRA_COINS )
+ {
+ this->AdjustBankValue( +100 ); // add 100 coins to bank value
+ }
+}
+
+/*=============================================================================
+Rendering pass for the HUD. Used for when the coin flies up to the HUD counter.
+We set the state once and do all the rendering.
+=============================================================================*/
+void CoinManager::HUDRender( void )
+{
+ if( !m_pCoinDrawable )
+ {
+ return;
+ }
+ bool renderHUDCoin = false;
+ if( mHUDCoinX > -1.0f && mHUDCoinX < 1.0f && mHUDCoinY > -1.0f && mHUDCoinY < 1.0f )
+ {
+ renderHUDCoin = true;
+ }
+ if( ( mNumHUDFlying > 0 ) || renderHUDCoin )
+ {
+ p3d::stack->Push();
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_ALWAYS );
+ }
+ p3d::pddi->SetProjectionMode( PDDI_PROJECTION_ORTHOGRAPHIC );
+ pddiColour oldAmbient = p3d::pddi->GetAmbientLight();
+ p3d::pddi->SetAmbientLight( pddiColour( 255, 255, 255 ) );
+
+ rmt::Vector pos;
+ if( renderHUDCoin )
+ {
+ p3d::stack->LoadIdentity();
+ p3d::stack->Scale( 0.1f, 0.1f, 1.0f );
+ float coinSin, coinCos;
+ rmt::SinCos( ( mHUDCoinAngle * SPIN_MULTIPLIER ), &coinSin, &coinCos );
+ rmt::Matrix transform( coinCos, 0.0f, -coinSin, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ coinSin, 0.0f, coinCos, 0.0f,
+ mHUDCoinX * 5.0f, mHUDCoinY * 5.0f, 5.0f, 1.0f );
+#ifndef RAD_RELEASE
+ ++dbg_CoinsDrawn;
+#endif
+ if( mHUDSparkle <= 0 )
+ {
+ rmt::Vector pos;
+ pos.ScaleAdd( rmt::Vector( mHUDCoinX, mHUDCoinY, 5.0f ), 0.2f, mCoinBounding.centre );
+ float radius = 0.2f * ( ( coinCos < 0.0f ) ? mCoinBounding.radius : -mCoinBounding.radius );
+ pos.Add( rmt::Vector( coinCos * radius, 0.0f, coinSin * radius ) );
+ GetSparkleManager()->AddSparkle( pos, 1.0f, 0.25f, rmt::Vector( 0.0f, 0.0f, 0.0f ), Sparkle::SE_HUDGlint );
+ mHUDSparkle = Sparkle::sRandom.IntRanged( HUD_SPARKLE_RATE, HUD_SPARKLE_RATE * 3 );
+ }
+ --mHUDSparkle;
+ p3d::pddi->PushMultMatrix( PDDI_MATRIX_MODELVIEW, &transform );
+ m_pCoinDrawable->Display();
+ p3d::pddi->PopMatrix( PDDI_MATRIX_MODELVIEW );
+ }
+ p3d::stack->LoadIdentity();
+ p3d::stack->Scale( 0.05f, 0.05f, 1.0f);
+
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if((c.State == CS_FlyingToHUD) || (c.State == CS_FlyingFromHUD))
+ {
+ if(c.State == CS_FlyingToHUD)
+ {
+ float a = c.Age * I_FLYING_TIME;
+ pos.ScaleAdd( c.Position, a * a, c.Velocity );
+ }
+ else
+ {
+ pos.ScaleAdd(c.Position, c.Age * I_FLYING_TIME, c.Velocity);
+ }
+ // Down the columns, across the rows.
+ rmt::Matrix transform( 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ pos.x * 10.0f, pos.y * 10.0f, 5.0f, 1.0f );
+ #ifndef RAD_RELEASE
+ ++dbg_CoinsDrawn;
+ #endif
+ p3d::pddi->PushMultMatrix( PDDI_MATRIX_MODELVIEW, &transform );
+ m_pCoinDrawable->Display();
+ p3d::pddi->PopMatrix( PDDI_MATRIX_MODELVIEW );
+ }
+ }
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_PERSPECTIVE);
+ p3d::pddi->SetAmbientLight( oldAmbient );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::stack->Pop();
+ }
+} \ No newline at end of file
diff --git a/game/code/worldsim/coins/coinmanager.h b/game/code/worldsim/coins/coinmanager.h
new file mode 100644
index 0000000..28faee2
--- /dev/null
+++ b/game/code/worldsim/coins/coinmanager.h
@@ -0,0 +1,168 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: coinmanager.h
+//
+// Description: Coin manager looks after everything to do with the collectable coins
+//
+// History: 29/01/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+#ifndef COINMANAGER_H
+#define COINMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/vector.hpp>
+#include <radmath/geometry.hpp>
+#include <events/eventlistener.h>
+#include <constants/breakablesenum.h>
+#include <cheats/cheatinputsystem.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class tDrawable;
+class tTexture;
+
+//=============================================================================
+//
+// Synopsis: Creating and placing coins in the world, picking up coins,
+// losing coins,
+//
+//=============================================================================
+class CoinManager : public EventListener,
+ public ICheatEnteredCallback
+{
+public:
+ CoinManager();
+ ~CoinManager();
+
+ static CoinManager* GetInstance( void );
+ static CoinManager* CreateInstance( void );
+ static void DestroyInstance( void );
+
+ void Init( void );
+ void Destroy( void );
+ void SetCoinDrawable( tDrawable* Drawable );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void Update( int ElapsedMS ); // Elapsed milliseconds.
+ void Render( void ); // Draw coins. Do simple frustum and distance check on them.
+ void HUDRender( void ); // Draw any HUD animations.
+
+ int GetBankValue( void ) const; // How much is in the bank.
+ void AdjustBankValue( int DeltaCoins ); // Adjust the bank value without any visual SFX.
+ void CollectCoins( int Count ); // Collect coins with sound SFX.
+ void LoseCoins( int Count, const rmt::Vector* Position = 0 ); // decrease bank value with visual SFX.
+ // spawn coins from this position. If in car, the coin actually just go to the player, unless the Force
+ //parameter is true.
+ void SpawnCoins( int Count, const rmt::Vector& Position, const rmt::Vector* Direction = 0, bool Force = false );
+ void SpawnCoins( int Count, const rmt::Vector& Position, float GroundY, const rmt::Vector* Direction = 0, bool Force = false ); // spawn coins and force specific ground value.
+ void SpawnInstantCoins(int Count, const rmt::Vector& Position);
+
+ void AddWorldCoin( const rmt::Vector& Position, tUID Sector ); // Place a coin to sit happily in a zone until the zone unloads.
+
+ void SetHUDCoin( int X, int Y, bool IsShowing = true );
+ void AddFlyDownCoin(void); // Create an animation of a single coin zipping down from the HUD counter.
+ void ClearHUDCoins(void); // Remove all HUD coins.
+
+ bool DrawAfterGui() const { return mDrawAfterGui;}
+ void SetDrawAfterGui(bool d) { mDrawAfterGui = d;}
+
+ enum eCoinState
+ {
+ CS_Inactive,
+ CS_InitialSpawning, // Coins aren't collectable during this time.
+ CS_SpawnToCollect, // Coins spawn and then are collected on first bounce.
+ CS_Spawning, // Still moving, but can be collected.
+ CS_Resting, // Sitting happily.
+ CS_RestingIndefinitely, // Sitting happily in the world. I doesn't decay.
+ CS_Decaying, // Spinning away into nothing.
+ CS_Collecting, // Reverse spawning. Attracted to the player.
+ CS_Collected, // Special state held for one frame before the coin heads up into the HUD.
+ CS_FlyingToHUD, // Coin is doing the little fly up animation.
+ CS_FlyingFromHUD, // Coin is doing the little fly down animation.
+ CS_NUM_STATES
+ };
+
+ tDrawable* GetCoinDrawable( void ) const { return m_pCoinDrawable; }
+ const rmt::Sphere& GetCoinBounding( void ) const { return mCoinBounding; }
+
+ virtual void OnCheatEntered( eCheatID cheatID, bool isEnabled );
+
+protected:
+ //Prevent wasteful constructor creation.
+ CoinManager( const CoinManager& That );
+ CoinManager& operator=( const CoinManager& That );
+
+ void OnVehicleDestroyed( Vehicle* DestroyedVehicle );
+ struct ActiveCoin;
+ void SpawnCoin( ActiveCoin& Coin, const rmt::Vector& Start, float Ground, const rmt::Vector* Direction = 0, bool InstaCollect = false );
+ void CheckCollection( ActiveCoin& Coin, const rmt::Vector& AvatarPos, float RangeMultiplier = 1.0f );
+ void RemoveWorldCoins( tUID Sector );
+ void AddGlint( ActiveCoin& Coin, const rmt::Vector& ToCamera, const rmt::Vector& CoinAxis, const rmt::Vector& CameraRight );
+
+ void UpdateCollecting( ActiveCoin& Coin, const rmt::Vector& AvatarPos );
+ void UpdateSpawning(ActiveCoin& Coin, const rmt::Vector& AvatarPos, float DeltaSeconds);
+ void UpdateHUDFlying( ActiveCoin& Coin );
+
+ static CoinManager* spCoinManager;
+
+ tDrawable* m_pCoinDrawable;
+ rmt::Sphere mCoinBounding;
+
+ struct ActiveCoin
+ {
+ ActiveCoin() : State( CS_Inactive ) {};
+ union
+ {
+#ifdef RAD_GAMECUBE
+ rmt::Vector Velocity;
+ tUID Sector;
+#else
+ struct
+ {
+ rmt::Vector Velocity;
+ };
+
+ struct
+ {
+ tUID Sector;
+ short PersistentObjectID;
+ };
+#endif
+ };
+#ifdef RAD_GAMECUBE
+ short PersistentObjectID;
+#endif
+ rmt::Vector Position;
+ float HeadingCos;
+ float HeadingSin;
+ float Age;
+ float Ground;
+ eCoinState State;
+ };
+
+ ActiveCoin* GetInactiveCoin( void );
+
+ ActiveCoin* mActiveCoins;
+ short mNumActiveCoins;
+ short mNextInactiveCoin;
+ short mNumHUDFlying; // So we can early out of the HUD render.
+ short mHUDSparkle;
+
+ float mHUDCoinX;
+ float mHUDCoinY;
+ float mHUDCoinAngle;
+
+ bool mDrawAfterGui;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline CoinManager* GetCoinManager() { return( CoinManager::GetInstance() ); }
+
+#endif //COINMANAGER_H
diff --git a/game/code/worldsim/coins/sparkle.cpp b/game/code/worldsim/coins/sparkle.cpp
new file mode 100644
index 0000000..77004b8
--- /dev/null
+++ b/game/code/worldsim/coins/sparkle.cpp
@@ -0,0 +1,1222 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Sparkle.cpp
+//
+// Description: Implementation of class Sparkle
+//
+// History: 18/2/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radmath/matrix.hpp>
+#include <p3d/entity.hpp>
+#include <p3d/view.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/texture.hpp>
+#include <pddi/pddi.hpp>
+#ifndef RAD_RELEASE
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#endif
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/bootupcontext.h>
+#include <worldsim/coins/sparkle.h>
+#include <memory/srrmemory.h>
+#include <main/game.h>
+#include <camera/supercam.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercammanager.h>
+#include <gameflow/gameflow.h>
+#include <mission/gameplaymanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#if defined( RAD_XBOX ) || defined( RAD_WIN32 )
+ #ifdef RAD_RELEASE
+ static const float FRAME_RATE = 60.0f;
+ #elif RAD_TUNE
+ static const float FRAME_RATE = 30.0f;
+ #else
+ static const float FRAME_RATE = 15.0f;
+ #endif
+ static const float FRAME_RATIO = 1.0f / FRAME_RATE;
+#endif
+
+static const float DRIFT_DRAG = 0.99f; // How much horizontal inertia is lost per frame.
+static const int NUM_SPARK_PARTICLES = 10;
+static const float SPARK_RADIUS = 1.8f;
+static const float SPARK_SIZE = 0.25f;
+static const float SPARK_VERTICAL_SCALE = 0.25f;
+static const float SPARK_DURATION = 0.3f;
+static const float SPARK_DURATION_RATIO = 1.0f / SPARK_DURATION;
+static const float SPARK_VELOCITY_TO_SIZE_RATIO = 1.0f / ( SPARK_RADIUS * SPARK_DURATION_RATIO * FRAME_RATIO );
+static const float DASH_SIZE = 0.2f;
+static const float DASH_SIZE_INCREASE = 0.2f;
+static const float DASH_DURATION_RATIO = 2.0f;
+static const float LANDING_SIZE = 0.5f;
+static const float LANDING_NUM = 16;
+static const float LANDING_DURATION_RATIO = 1.5f;
+static const int NUM_PAINT_CHIPS = 10;
+static const float PAINT_CHIP_SIZE = 0.05f;
+static const float SHOCK_RING_RADIUS = 3.0f;
+static const float SHOCK_RING_SPEED = 1.75f;
+static const int NUM_STAR_PARTICLES = 5;
+static const float STAR_DURATION_RATIO = 1.0f / 0.5f;
+static const float STAR_RADIUS = 10.0f;
+static const float STAR_SIZE = 0.25f;
+static const float STAR_DUST_RADIUS = 2.0f;
+static const float STAR_DUST_SIZE = 1.0f;
+static const float STAR_DUST_DURATION_RATIO = 1.0f;
+static const int NUM_BOTTOMOUT_SPARKS = 30;
+static const float BOTTOMOUT_RADIUS = 2.5f;
+static const int NUM_SMOKE_PARTICLES = 2; // Maximum number of particles per call for the smoke.
+static const float SMOKE_SIZE = 0.3f;
+static const int NUM_GAG_SPARKLES = 1;
+static const float GAG_SPARKLE_ELEVATION = 1.0f;
+static const float GAG_SPARKLE_EMIT_RADIUS = 0.75f;
+static const float GAG_SPARKLE_DISTANCE = 0.15f;
+static const float GAG_SPARKLE_DURATION_RATIO = 1.0f / 1.6f;
+static const float GAG_SPARKLE_SIZE = 0.45f;
+static const tColour GAG_SPARKLE_COLOUR(32, 128, 192);
+static const tColour STAR_COLOUR( 255, 240, 100, 255 );
+static const tColour STAR_DUST_COLOUR( 150, 140, 110, 128 );
+static const tColour TRAIL_COLOUR( 0xFF, 0xFF, 0x7F );
+static const tColour SPARK_COLOUR( 0xFF, 0xC6, 0x00 );
+static const tColour DASH_COLOUR( 220, 210, 180, 32 );
+static const tColour LANDING_COLOUR( 220, 210, 180, 128 );
+static const tColour RING_COLOUR( 0xFF, 0xFF, 0xFF );
+static const tColour LEAF_COLOUR( 120, 200, 100 );
+static const tColour LIGHT_SMOKE_COLOUR( 0xC0, 0xD0, 0xE0, 0x10 );
+static const tColour DARK_SMOKE_COLOUR( 0x40, 0x40, 0x40, 0xA0 );
+#ifndef RAD_RELEASE
+static int dbg_MaxSparkles = 0;
+#endif
+
+rmt::Randomizer Sparkle::sRandom( 0 ); // Set in the constructor.
+bool Sparkle::sRandomSeeded = false;
+
+Sparkle* Sparkle::spInstance = 0;
+
+Sparkle* Sparkle::CreateInstance( unsigned char NumTextures, unsigned short NumSparkles )
+{
+ rAssertMsg( spInstance == 0, "SparkleManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ spInstance = new Sparkle( NumTextures, NumSparkles );
+ rAssert( spInstance );
+ HeapManager::GetInstance()->PopHeap( GMA_PERSISTENT );
+ return Sparkle::GetInstance();
+}
+
+Sparkle* Sparkle::GetInstance( void )
+{
+ rAssertMsg( spInstance != 0, "SparkleManager has not been created yet.\n" );
+ return spInstance;
+}
+
+void Sparkle::DestroyInstance( void )
+{
+ rAssertMsg( spInstance != 0, "SparkleManager has not been created.\n" );
+ delete spInstance;
+ spInstance = 0;
+}
+
+
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Sparkle::Sparkle( unsigned char NumTextures, unsigned short NumSparkles ) :
+ mActiveSparkles( 0 ),
+ mpTextures( 0 ),
+ mNumSparkles( NumSparkles ),
+ mNextInactiveSparkle( 0 ),
+ mNumActiveSparkles( 0 ),
+ mNumHUDSparkles( 0 ),
+ mNumTextures( NumTextures )
+{
+ rAssert( mNumSparkles <= 65535 ); // We're using an unsigned short for our counters!
+ rAssert( mNumTextures <= ST_NUM_TEXTURES );
+
+ if (!sRandomSeeded)
+ {
+ sRandom.Seed (Game::GetRandomSeed ());
+ sRandomSeeded = true;
+ }
+}
+
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Sparkle::~Sparkle()
+{
+ Destroy();
+#ifndef RAD_RELEASE
+ radDbgWatchDelete( &dbg_MaxSparkles );
+#endif
+}
+
+void Sparkle::Init( void )
+{
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ mActiveSparkles = new ActiveSparkle[ mNumSparkles ];
+ rAssert( mActiveSparkles );
+ mpTextures = new tTexture*[ mNumTextures ];
+ rAssert( mpTextures );
+ mNumActiveSparkles = new unsigned short[ mNumTextures << 1 ];
+ rAssert( mNumActiveSparkles );
+ mNumHUDSparkles = new unsigned short[ mNumTextures ];
+ rAssert( mNumHUDSparkles );
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+
+ for( int i = 0; i < mNumTextures; ++i )
+ {
+ mpTextures[ i ] = 0;
+ mNumActiveSparkles[ i ] = 0;
+ mNumActiveSparkles[ i + ST_NUM_TEXTURES ] = 0;
+ mNumHUDSparkles[ i ] = 0;
+ }
+}
+
+void Sparkle::Destroy( void )
+{
+ if( mpTextures )
+ {
+ for( int i = 0; i < mNumTextures; ++i )
+ {
+ tRefCounted::Release( mpTextures[ i ] );
+ }
+ delete [] mpTextures;
+ mpTextures = 0;
+ }
+ delete [] mNumActiveSparkles;
+ mNumActiveSparkles = 0;
+ delete [] mNumHUDSparkles;
+ mNumHUDSparkles = 0;
+ delete [] mActiveSparkles;
+ mActiveSparkles = 0;
+}
+
+/*=============================================================================
+Set the texture the sparkles will be drawn with. Perhaps in the future we'll
+have some method of doing multiple textures.
+=============================================================================*/
+void Sparkle::SetTexture( unsigned char Index, tTexture* Texture )
+{
+ rAssert( Index < mNumTextures );
+ tRefCounted::Assign( mpTextures[ Index ], Texture );
+}
+
+/*=============================================================================
+Find an unused sparkle in our pre-allocated array of sparkles.
+=============================================================================*/
+Sparkle::ActiveSparkle* Sparkle::GetInactiveSparkle( void )
+{
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle* s = &(mActiveSparkles[ mNextInactiveSparkle ]);
+ mNextInactiveSparkle = ( mNextInactiveSparkle + 1 ) % mNumSparkles;
+ if( !s->Active )
+ {
+ s->Active = 1;
+ s->HUD = 0;
+ return s;
+ }
+ }
+#ifndef RAD_RELEASE
+ // rTunePrintf( "WARNING: Ran out of sparkles!" );
+#endif
+ return 0;
+}
+
+/*=============================================================================
+Add a sparkle to the world. This is used for collection/spawn trails.
+=============================================================================*/
+void Sparkle::AddSparkle( const rmt::Vector& Position, float Size, float Duration, const rmt::Vector& Velocity, eSparkleEffect Effect )
+{
+ rAssert( Duration > 0.0f ); // No infinite druation sparkles.
+ if( Duration <= 0.0f )
+ {
+ return;
+ }
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Texture = ST_Sparkle;
+ if( Effect == SE_Trail )
+ {
+ s->Align = SA_None;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearRedDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ }
+ else if( Effect == SE_Glint )
+ {
+ s->Align = SA_None;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ }
+ else if( Effect == SE_Vanish )
+ {
+ s->Align = SA_None;
+ s->Scale = SS_BulgeDown;
+ s->ColourAnim = SC_LinearRedDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ }
+ else
+ {
+ s->HUD = 1;
+ s->Align = SA_None;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ ++( mNumHUDSparkles[ s->Texture ] );
+ }
+ s->Size = Size;
+ s->DurationRatio = 1.0f / Duration;
+ s->Life = 1.0f;
+ s->Position = Position;
+ s->Velocity = Velocity;
+ ++( mNumActiveSparkles[ s->Texture ] );
+#ifndef RAD_RELEASE
+// dbg_MaxSparkles = rmt::Max( mNumActiveSparkles, dbg_MaxSparkles );
+#endif
+}
+
+/*=============================================================================
+Add the blue gag sparkle to the world. Size is used to scale the particles down
+for indoors. A 1 is full sized, .5 is half, etc. The Strength value is used to
+fade the sparkle out for distance culling.
+Okay, so the problem is we don't want sparkles shooting out every frame for all
+those gags. It's too many particles and it looks bad. We'd either have to store
+a state with each gag, some sort of counter, or store a state here to only
+activate every few calls. The problem with storing a state with each gag is an
+extra byte for anything using this.
+Currently only gags uses the sparkles, but it could be others (doorbells, NPCs
+you talk to). The problem with only activating every few calls, is if there is
+exactly the number of gags as the number we wait, then only one gag gets a
+sparkle. I perfer to hold the state here anyway, since it's less memory. So we
+have two solutions, randomize the amount we skip before we spit out another
+sparkle, or hold the state of the objects here. If we held the state here we
+could create a hash table and then group the calling objects by their hash
+value (we'd have to pass in a this pointer so we know who called us). So if we
+took bits 4 to 7 (i.e. the high order bits of the first byte of the address
+(remember since the address is aligned we don't want to take the first bits of
+the first bytes) we'd end up with a number between 0 and 15 which we could use
+as an index into some bits we are using to hold a counter controling when to
+spit out a sparkle. I think I'll try something along those lines.
+=============================================================================*/
+void Sparkle::AddGagSparkle(const rmt::Vector& Position, float Size, float Strength, unsigned int Caller)
+{
+ static unsigned int actionFlag = 0;
+ if(GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE)
+ {
+ return;
+ }
+
+ if((Size <= 0.0f) || (Strength <= 0.0f) || (int(255 * Strength) <= 0))
+ {
+ return;
+ }
+ int bitMask = 1 << ((Caller >> 4) & 31);
+ actionFlag ^= bitMask;
+ if(actionFlag & bitMask)
+ {
+ return;
+ }
+ ActiveSparkle* s = GetInactiveSparkle();
+ if(!s)
+ {
+ return;
+ }
+ s->Align = SA_None;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Sparkle;
+ s->Position = Position;
+ s->Position.y += GAG_SPARKLE_ELEVATION * Size;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Position.ScaleAdd(GAG_SPARKLE_EMIT_RADIUS * Size, s->Velocity);
+ s->Velocity.Normalize();
+ s->Velocity.Scale( GAG_SPARKLE_DISTANCE * GAG_SPARKLE_DURATION_RATIO * FRAME_RATIO * Size);
+ s->Size = GAG_SPARKLE_SIZE * Size;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = GAG_SPARKLE_DURATION_RATIO;
+ int red = rmt::Clamp(int(GAG_SPARKLE_COLOUR.Red() * Strength), 0, 255);
+ int green = rmt::Clamp(int(GAG_SPARKLE_COLOUR.Green() * Strength), 0, 255);
+ int blue = rmt::Clamp(int(GAG_SPARKLE_COLOUR.Blue() * Strength), 0, 255);
+ s->Colour.Set(red, green, blue);
+ ++(mNumActiveSparkles[ s->Texture ]);
+}
+
+/*=============================================================================
+Add spark to the world. A spark is like a spherical explosion.
+=============================================================================*/
+void Sparkle::AddSparks( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength )
+{
+ int numParticles = int( sRandom.IntRanged( NUM_SPARK_PARTICLES, NUM_SPARK_PARTICLES * 2 ) * Strength ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_MotionStreak;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Decelerate;
+ s->Texture = ST_Spark;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( SPARK_RADIUS * SPARK_DURATION_RATIO );
+ s->Velocity.ScaleAdd(SPARK_DURATION_RATIO, Velocity );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = s->Velocity.Magnitude() * SPARK_VELOCITY_TO_SIZE_RATIO * SPARK_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ s->Colour = SPARK_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+This is basically the same as the AddSparks, except it's radial sparks at the
+position and more pronounced.
+=============================================================================*/
+void Sparkle::AddBottomOut( const rmt::Vector& Position )
+{
+ for( int i = 0; i < NUM_BOTTOMOUT_SPARKS; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_MotionStreak;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Decelerate;
+ s->Texture = ST_Spark;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.Float() * 0.1f;
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( BOTTOMOUT_RADIUS * SPARK_DURATION_RATIO );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = SPARK_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ s->Colour = SPARK_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Add the dash cloud. Note that the velocity is the travel of the character,
+not the direction you want the dash cloud to travel.
+=============================================================================*/
+void Sparkle::AddDash( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength )
+{
+ int alpha = int(DASH_COLOUR.Alpha() * (Strength*Strength+Strength)*0.5f);
+ if(alpha <= 0)
+ {
+ return;
+ }
+ int numParticles = sRandom.IntRanged( NUM_SPARK_PARTICLES >> 2, NUM_SPARK_PARTICLES ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_LinearGrow;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Puff;
+ s->Position = Position;
+ s->Position.y += 0.05f;
+ s->Position.ScaleAdd(-0.05f, Velocity);
+ s->Velocity.x = -Velocity.x * sRandom.FloatSigned() * 2.0f;
+ s->Velocity.y = ( sRandom.Float() * 0.5f ) + 0.5f;
+ s->Velocity.z = -Velocity.z * sRandom.FloatSigned() * 2.0f;
+ s->Velocity.Normalize();
+ s->Velocity.Scale( 0.2f + ( 0.2f * sRandom.Float() ) );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = DASH_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.5f );
+ s->DurationRatio = DASH_DURATION_RATIO;
+ s->Colour = DASH_COLOUR;
+ s->Colour.SetAlpha(int(DASH_COLOUR.Alpha() * (Strength*Strength+Strength)*0.5f));
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Add the landing puff. It's a radial explosion of puffs used in the dash.
+The strength is used to control the look over a jump landing (~0.5), a jump-
+jump landing (~0.7), and a stomp (1.0).
+=============================================================================*/
+void Sparkle::AddLanding( const rmt::Vector& Position, float Strength )
+{
+ if( Strength <= 0.0f )
+ {
+ return;
+ }
+ int numParticles = int( LANDING_NUM + ( LANDING_NUM * Strength ) );
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_LinearUp;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Puff;
+ s->Position = Position;
+ s->Position.y += 0.25f * Strength;
+
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.Float() * Strength * 0.25f;
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( 1.0f + Strength );
+ s->Velocity.Scale( FRAME_RATIO );
+
+ s->Size = LANDING_SIZE * Strength;
+ s->Life = ( sRandom.Float() * 0.2f ) + Strength;
+ s->DurationRatio = LANDING_DURATION_RATIO;
+ s->Colour = DASH_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+/*=============================================================================
+Add a shockwave ring, which is basically a billboard with a texture which
+expands from the center and then disappears. Really easy. Handy for things like
+the jump-jump-stomp.
+=============================================================================*/
+void Sparkle::AddShockRing( const rmt::Vector& Position, float Strength )
+{
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Flat;
+ s->Scale = SS_LinearUp;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Ring;
+ s->Position = Position;
+ s->Position.y += 0.25f;
+ s->Velocity.Set( 0.0f, 0.0f, 0.0f );
+ s->Size = SHOCK_RING_RADIUS * Strength;
+ s->Life = 1.0f;
+ s->DurationRatio = SHOCK_RING_SPEED;
+ s->Colour = RING_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+}
+
+/*=============================================================================
+Add the leaves spray when the car drives over a hedge. The animation is like a
+coins spawning.
+=============================================================================*/
+void Sparkle::AddLeaves( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength )
+{
+ int numParticles = int( sRandom.IntRanged( NUM_SPARK_PARTICLES, NUM_SPARK_PARTICLES * 2 ) * Strength ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Gravity;
+ s->Texture = ST_Leaf;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( SPARK_RADIUS * SPARK_DURATION_RATIO );
+ s->Velocity.ScaleAdd(SPARK_DURATION_RATIO, Velocity );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = s->Velocity.Magnitude() * SPARK_VELOCITY_TO_SIZE_RATIO;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ s->Colour = LEAF_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Add paint chips when upon vehicle-vehicle collision. The colour depends
+upon the colour of the vehicle getting hit.
+=============================================================================*/
+void Sparkle::AddPaintChips( const rmt::Vector& Position, const rmt::Vector& Velocity, pddiColour Colour, float Strength )
+{
+ return;
+
+ int numParticles = int( sRandom.IntRanged( NUM_PAINT_CHIPS, NUM_PAINT_CHIPS * 2 ) * Strength ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+
+ const float VELOCITY_DAMPENING = 0.2f;
+
+ s->Align = SA_Rotation;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Gravity;
+ s->Texture = ST_Paint;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( VELOCITY_DAMPENING );
+
+ s->Size = PAINT_CHIP_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ const float COLOUR_DAMPENING = 0.5f;
+ int red = static_cast< int >( static_cast< float >(Colour.Red()) * COLOUR_DAMPENING );
+ int green = static_cast< int >( static_cast< float >(Colour.Green()) * COLOUR_DAMPENING );
+ int blue = static_cast< int >( static_cast< float >(Colour.Blue()) * COLOUR_DAMPENING );
+ s->Colour.Set( red, green, blue );
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+/*=============================================================================
+Explosion of stars and smoke. Useful when the car hits a static.
+=============================================================================*/
+void Sparkle::AddStars( const rmt::Vector& Position, float Strength )
+{
+ int numParticles = NUM_STAR_PARTICLES + int( NUM_STAR_PARTICLES * Strength );
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_MotionStreak;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( FRAME_RATIO * STAR_RADIUS );
+ s->DurationRatio = STAR_DURATION_RATIO;
+ s->Size = s->Velocity.Magnitude() * SPARK_VELOCITY_TO_SIZE_RATIO * SPARK_SIZE;
+ s->Life = 1.0f;
+ s->Texture = ST_Spark;
+ s->Colour = STAR_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_LinearGrow;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 1;
+ s->Motion = SM_Linear;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( FRAME_RATIO * STAR_DUST_RADIUS );
+ s->DurationRatio = STAR_DUST_DURATION_RATIO;
+ s->Size = STAR_DUST_SIZE;
+ s->Life = 1.0f;
+ s->Texture = ST_Puff;
+ s->Colour = STAR_DUST_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Upward spray of smoke. Used for car damage smoke.
+Transform is the position of the vehicle and the offset is the position of the
+emission of the smoke _relative_ to the vehicle. The velocity is the vehicle's
+movement per frame.
+=============================================================================*/
+void Sparkle::AddSmoke( const rmt::Matrix& Transform, const rmt::Vector& Offset, const rmt::Vector& Velocity, float Strength )
+{
+ if( Strength <= 0.0f )
+ {
+ return;
+ }
+ rmt::Vector pos;
+ Transform.Transform( Offset, &pos );
+ // Do some distance type LODing. The further from the camera, the fewer particles.
+ rmt::Vector camPos;
+ GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam()->GetPosition( &camPos );
+ camPos.Sub( pos );
+ int d = 0;
+ if( GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT )
+ {
+ d = int( rmt::Max( rmt::Abs( camPos.x ), rmt::Abs( camPos.z ) ) * 0.02f );
+ }
+ int numParticles = int( NUM_SMOKE_PARTICLES * Strength ) + 1 - d;
+ if( numParticles <= 0 )
+ {
+ return;
+ }
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_None;
+ s->Scale = SS_LinearTriple;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Drift;
+ s->Position = pos;
+ s->Position.x += sRandom.FloatSigned() * 0.15f;
+ s->Position.z += sRandom.FloatSigned() * 0.15f;
+ s->Position.y += sRandom.FloatSigned() * 0.15f;
+ s->Velocity.x = sRandom.FloatSigned() * 2.0f;
+ s->Velocity.z = sRandom.FloatSigned() * 2.0f;
+ s->Velocity.y = 2.0f + ( sRandom.Float() * 2.0f );
+ s->Velocity.Normalize();
+ s->Velocity.Scale( FRAME_RATIO * ( 2.0f + ( 1.0f * Strength ) ) );
+ //s->Velocity.ScaleAdd( s->Velocity, FRAME_RATIO, Velocity );
+ s->Velocity.Add(Velocity);
+ s->DurationRatio = 0.2f / 1.0f + Strength + sRandom.Float();
+ s->Size = SMOKE_SIZE; // + ( ( Strength + sRandom.Float() ) * 0.1f );
+ s->Life = 1.0f + ( Strength * 0.5f );
+ int red = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Red() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Red() * Strength ) ), 0, 255 );
+ int green = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Green() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Green() * Strength ) ), 0, 255 );
+ int blue = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Blue() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Blue() * Strength ) ), 0, 255 );
+ int alpha = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Alpha() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Alpha() * Strength ) ), 0, 255 );
+ s->Colour.Set( red, green, blue, alpha );
+ s->Texture = ST_SortedPuff;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Update the position/animation of all the coins.
+=============================================================================*/
+void Sparkle::Update( unsigned int Elapsedms )
+{
+ float deltaSeconds = (float)Elapsedms * 0.001f;
+ bool HUDOnly = GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE;
+
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle& s = mActiveSparkles[ i ];
+ if(s.Active)
+ {
+ if(HUDOnly && s.HUD != 1)
+ {
+ continue;
+ }
+ if( s.Life < 0.0f )
+ {
+ if(s.HUD)
+ {
+ s.HUD = 0;
+ --( mNumHUDSparkles[ s.Texture ] );
+ }
+ s.Active = 0;
+ --(mNumActiveSparkles[ s.Texture ]);
+ }
+ if( s.Motion == SM_Decelerate )
+ {
+ s.Position.ScaleAdd( 2.0f - s.Life, s.Velocity );
+ }
+ else if ( s.Motion == SM_Gravity )
+ {
+ s.Position.Add( s.Velocity );
+ const float GRAVITY = 0.02f;
+ s.Velocity.y -= static_cast<float>(Elapsedms) * GRAVITY;
+ }
+ else if( s.Motion == SM_Drift )
+ {
+ s.Position.Add( s.Velocity );
+ s.Velocity.x *= DRIFT_DRAG;
+ s.Velocity.z *= DRIFT_DRAG;
+ }
+ else
+ {
+ s.Position.Add( s.Velocity );
+ }
+ s.Life -= deltaSeconds * s.DurationRatio;
+ }
+ }
+}
+
+Sparkle::ActiveSparkle::ActiveSparkle() : Active( 0 ), HUD( 0 )
+{}
+
+
+/*=============================================================================
+Render pass for the world sparkles. They are aligned to face the camera.
+=============================================================================*/
+void Sparkle::Render( eSparkleRenderMode Mode )
+{
+ unsigned short numWorldSparkles[ ST_NUM_TEXTURES ];
+ int totalSparkles = 0;
+ int ti;
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ if( mpTextures[ ti ] )
+ {
+ if( Mode == SRM_IncludeSorted )
+ {
+ numWorldSparkles[ ti ] = mNumActiveSparkles[ ti ] + mNumActiveSparkles[ ti + ST_NUM_TEXTURES ] - mNumHUDSparkles[ ti ];
+ }
+ else if( Mode == SRM_ExcludeSorted )
+ {
+ numWorldSparkles[ ti ] = mNumActiveSparkles[ ti ] - mNumHUDSparkles[ ti ];
+ }
+ else
+ {
+ numWorldSparkles[ ti ] = mNumActiveSparkles[ ti + ST_NUM_TEXTURES ];
+ }
+ }
+ else
+ {
+ numWorldSparkles[ ti ] = 0;
+ }
+ totalSparkles += numWorldSparkles[ ti ];
+ }
+ if( totalSparkles <= 0 )
+ {
+ return;
+ }
+
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ const rmt::Matrix& camTransform = camera->GetCameraToWorldMatrix();
+ rmt::Vector cameraDiagR = camTransform.Row( 1 );
+ rmt::Vector cameraDiagL = cameraDiagR;
+ cameraDiagR.Add( camTransform.Row( 0 ) );
+ cameraDiagL.ScaleAdd( -1.0f, camTransform.Row( 0 ) );
+ rmt::Vector offset;
+ offset.Scale( -0.1f, camTransform.Row( 2 ) );
+
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+
+ pddiPrimStream* sprite = 0;
+
+ pddiShader* spriteShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( spriteShader );
+
+ spriteShader->SetInt( PDDI_SP_ISLIT, 0 );
+ spriteShader->SetInt( PDDI_SP_ALPHATEST, 1 );
+ spriteShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+ spriteShader->SetInt( PDDI_SP_FILTER, PDDI_FILTER_BILINEAR );
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ if( numWorldSparkles[ ti ] <= 0 )
+ {
+ continue;
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, mpTextures[ ti ]->GetTexture() );
+ if((ti == ST_Puff) || (ti == ST_Leaf))
+ {
+ spriteShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ spriteShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ }
+ else
+ {
+ spriteShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+
+
+ sprite = p3d::pddi->BeginPrims( spriteShader, PDDI_PRIM_TRIANGLES, PDDI_V_CT , numWorldSparkles[ ti ] * 6 );
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle& s = mActiveSparkles[ i ];
+ if( ( s.Active == 0 ) || ( s.HUD == 1 ) )
+ {
+ continue;
+ }
+ unsigned short matchTexture = s.Texture;
+ if( Mode == SRM_IncludeSorted )
+ {
+ matchTexture %= ST_NUM_TEXTURES;
+ }
+ else if( Mode == SRM_SortedOnly )
+ {
+ matchTexture -= ST_NUM_TEXTURES;
+ }
+ if( ( matchTexture != ti ) )
+ {
+ continue;
+ }
+ float life = rmt::Max( s.Life, 0.0f );
+ float scale;
+ // Triangle points:
+ // 2 - 3
+ // | \ |
+ // 0 - 1
+ tColour colour[ 4 ];
+ rmt::Vector corners[ 4 ];
+
+ // Calculate the scale first.
+ if( s.Scale == SS_LinearDown )
+ {
+ scale = s.Size * life;
+ }
+ else if( s.Scale == SS_BulgeDown )
+ {
+ scale = s.Size * ( ( 1.0f - ( rmt::Abs( ( life * life ) - 0.5f ) * 2.0f ) ) + life );
+ }
+ else if( s.Scale == SS_LinearGrow )
+ {
+ scale = s.Size + ( s.Size * ( 1.0f - life ) * DASH_SIZE_INCREASE );
+ }
+ else if( s.Scale == SS_LinearTriple )
+ {
+ scale = s.Size + ( s.Size * 3.0f * ( 1.0f - rmt::Min( life, 1.0f ) ) );
+ }
+ else if( s.Scale == SS_LinearUp )
+ {
+ scale = s.Size * ( 1.0f - life );
+ }
+ else
+ {
+ scale = s.Size;
+ }
+ // Now do colour.
+ if( s.ColourAnim == SC_LinearRedDownAtHalf )
+ {
+ float l = rmt::Clamp( life * 2.0f, 0.0f, 1.0f );
+ float l_half = rmt::Clamp( life - 0.5f, 0.0f, 1.0f );
+ colour[ 0 ].Set( rmt::FtoL( s.Colour.Red() * l ),
+ rmt::FtoL( s.Colour.Green() * life ),
+ rmt::FtoL( s.Colour.Blue() * l_half ) );
+ }
+ else if( s.ColourAnim == SC_LinearDownAtHalf )
+ {
+ float l = rmt::Clamp( life * 2.0f, 0.0f, 1.0f );
+ colour[ 0 ].Set( rmt::FtoL( s.Colour.Red() * l ),
+ rmt::FtoL( s.Colour.Green() * l),
+ rmt::FtoL( s.Colour.Blue() * l ) );
+ }
+ else if( s.ColourAnim == SC_LinearAlphaDown )
+ {
+ float l = rmt::Clamp( life, 0.0f, 1.0f );
+ colour[ 0 ].Set( s.Colour.Red(),
+ s.Colour.Green(),
+ s.Colour.Blue(),
+ rmt::FtoL( s.Colour.Alpha() * l ) );
+ }
+ else
+ {
+ float l = rmt::Clamp( life, 0.0f, 1.0f );
+ colour[ 0 ].Set( rmt::FtoL( s.Colour.Red() * l ),
+ rmt::FtoL( s.Colour.Green() * l ),
+ rmt::FtoL( s.Colour.Blue() * l ) );
+ }
+ if( s.CornerFade == 1 )
+ {
+ colour[ 1 ] = colour[ 0 ];
+ colour[ 1 ].SetAlpha( int( colour[ 0 ].Alpha() * 0.75f ) );
+ colour[ 2 ] = colour[ 1 ];
+ colour[ 3 ] = colour[ 0 ];
+ colour[ 3 ].SetAlpha( colour[ 0 ].Alpha() >> 1 );
+ }
+ else
+ {
+ for( int i = 1; i < 4; ++i )
+ {
+ colour[ i ] = colour[ 0 ];
+ }
+ }
+
+ // Now do corners two corners.
+ if( s.Align == SA_MotionStreak )
+ {
+ // Align to motion.
+ // Project travel onto plane of the camera.
+ rmt::Vector travel = s.Velocity;
+ travel.Normalize();
+ float dot = travel.DotProduct( camTransform.Row( 2 ) );
+ travel.ScaleAdd( -dot, camTransform.Row( 2 ) );
+ dot = 1.0f - rmt::Abs( dot );
+ corners[ 2 ].Set( -scale * dot, scale * SPARK_VERTICAL_SCALE, 0.0f );
+ corners[ 3 ].Set( scale * dot, scale * SPARK_VERTICAL_SCALE, 0.0f );
+
+ travel.Normalize();
+ // Because textures like to be wider then they are taller
+ //on the PS2, the spark textures is aligned down the X axis instead of Y.
+ //so travel becomes the right in the matrix, the camera direction becomes facing,
+ //and the up is a cross of those two.
+ rmt::Vector cross;
+ cross.CrossProduct( camTransform.Row( 2 ), travel );
+ rmt::Matrix r( travel.x, travel.y, travel.z, 0.0f,
+ cross.x, cross.y, cross.z, 0.0f,
+ camTransform.Row( 2 ).x, camTransform.Row( 2 ).y, camTransform.Row( 2 ).z, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f );
+ r.Transform( 2, &( corners[ 2 ] ), &( corners[ 2 ] ) );
+ }
+ else if( s.Align == SA_Rotation )
+ {
+ rmt::Vector diagR;
+ diagR.Scale( scale, cameraDiagR );
+ rmt::Vector diagL;
+ diagL.Scale( scale, cameraDiagL );
+ corners[ 2 ] = diagL;
+ corners[ 3 ] = diagR;
+ rmt::Matrix r;
+ r.Identity();
+ r.FillRotation( camTransform.Row( 2 ), rmt::PI_BY2 * life );
+ r.Transform( 2, &( corners[ 2 ] ), &( corners[ 2 ] ) );
+ }
+ else if( s.Align == SA_Flat )
+ {
+ corners[ 2 ].Set( scale, 0.0f, -scale );
+ corners[ 3 ].Set( scale, 0.0f, scale );
+ }
+ else
+ {
+ rmt::Vector diagR;
+ diagR.Scale( scale, cameraDiagR );
+ rmt::Vector diagL;
+ diagL.Scale( scale, cameraDiagL );
+ corners[ 2 ] = diagL;
+ corners[ 3 ] = diagR;
+ }
+
+ // Now mirror those two corners.
+ corners[ 0 ].Scale( -1.0f, corners[ 3 ] );
+ corners[ 1 ].Scale( -1.0f, corners[ 2 ] );
+
+ // Now move corners into world position.
+ rmt::Vector pos;
+ pos.Add( s.Position, offset );
+ for( int i = 0; i < 4; ++i )
+ {
+ corners[ i ].Add( pos );
+ }
+
+ // Now draw the triangle.
+ // bottom left tri.
+ sprite->Colour( colour[ 0 ] );
+ sprite->UV( 0.0f, 0.0f );
+ sprite->Coord( corners[ 0 ].x, corners[ 0 ].y, corners[ 0 ].z );
+ sprite->Colour( colour[ 1 ] );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( corners[ 1 ].x, corners[ 1 ].y, corners[ 1 ].z );
+ sprite->Colour( colour[ 2 ] );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( corners[ 2 ].x, corners[ 2 ].y, corners[ 2 ].z );
+
+ // Top right tri.
+ sprite->Colour( colour[ 3 ] );
+ sprite->UV( 1.0f, 1.0f );
+ sprite->Coord( corners[ 3 ].x, corners[ 3 ].y, corners[ 3 ].z );
+ sprite->Colour( colour[ 2 ] );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( corners[ 2 ].x, corners[ 2 ].y, corners[ 2 ].z );
+ sprite->Colour( colour[ 1 ] );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( corners[ 1 ].x, corners[ 1 ].y, corners[ 1 ].z );
+ }
+ p3d::pddi->EndPrims( sprite );
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, 0 );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+}
+
+/*=============================================================================
+Rendering pass for the HUD. Used for when the coin flies up to the HUD counter.
+We set the state once and do all the rendering.
+=============================================================================*/
+void Sparkle::HUDRender( void )
+{
+ if( !mpTextures )
+ {
+ return;
+ }
+ unsigned char totalSparkles = 0;
+ unsigned int ti;
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ totalSparkles += mNumHUDSparkles[ ti ];
+ }
+ if( totalSparkles <= 0 )
+ {
+ return;
+ }
+ p3d::stack->Push();
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_ALWAYS );
+ }
+ p3d::stack->LoadIdentity();
+ p3d::stack->Scale( 0.1f, 0.1f, 1.0f);
+ p3d::pddi->SetProjectionMode( PDDI_PROJECTION_ORTHOGRAPHIC );
+ pddiColour oldAmbient = p3d::pddi->GetAmbientLight();
+ p3d::pddi->SetAmbientLight( pddiColour( 255, 255, 255 ) );
+
+ pddiPrimStream* sprite = 0;
+
+ pddiShader* spriteShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( spriteShader );
+
+ spriteShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ spriteShader->SetInt( PDDI_SP_ISLIT, 0 );
+ spriteShader->SetInt( PDDI_SP_ALPHATEST, 1 );
+ spriteShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ if( mNumHUDSparkles[ ti ] <= 0 )
+ {
+ continue;
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, mpTextures[ ti ]->GetTexture() );
+
+ sprite = p3d::pddi->BeginPrims( spriteShader, PDDI_PRIM_TRIANGLES, PDDI_V_CT, mNumHUDSparkles[ ti ] * 6 );
+
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle& s = mActiveSparkles[ i ];
+ if( s.Active == 0 || s.HUD == 0 || ( s.Texture != ti ) )
+ {
+ continue;
+ }
+ float life = s.Life;
+ tColour colour;
+ colour.Set( rmt::Clamp( int( 255 * life ), 0, 255 ),
+ rmt::Clamp( int( 255 * life ), 0, 255 ),
+ rmt::Clamp( int( ( 255 * life ) - 128 ), 0, 255 ) );
+ float scale;
+ scale = s.Size * life;
+ rmt::Vector pos = s.Position;
+ pos.Scale( 5.0f );
+
+ // bottom left tri.
+ sprite->Colour( colour );
+ sprite->UV( 0.0f, 0.0f );
+ sprite->Coord( pos.x - scale, pos.y - scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( pos.x + scale, pos.y - scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( pos.x - scale, pos.y + scale, pos.z );
+
+ // Top right tri.
+ sprite->Colour( colour );
+ sprite->UV( 1.0f, 1.0f );
+ sprite->Coord( pos.x + scale, pos.y + scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( pos.x - scale, pos.y + scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( pos.x + scale, pos.y - scale, pos.z );
+ }
+ p3d::pddi->EndPrims( sprite );
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, 0 );
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_PERSPECTIVE);
+ p3d::pddi->SetAmbientLight( oldAmbient );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::stack->Pop();
+} \ No newline at end of file
diff --git a/game/code/worldsim/coins/sparkle.h b/game/code/worldsim/coins/sparkle.h
new file mode 100644
index 0000000..aa977f7
--- /dev/null
+++ b/game/code/worldsim/coins/sparkle.h
@@ -0,0 +1,174 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: coinmanager.h
+//
+// Description: Sparkle effect looks after drawing sparkle effects. It bundles
+// them together under one set up call to PDDI.
+//
+// History: 29/01/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+#ifndef SPARKLE_H
+#define SPARKLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/vector.hpp>
+#include <radmath/matrix.hpp>
+#include <radmath/random.hpp>
+#include <render/enums/renderenums.h>
+
+//========================================
+// Forward References
+//========================================
+class tDrawable;
+class tTexture;
+
+//=============================================================================
+//
+// Synopsis: Creating, place, animate sparkles in the world. Handy for coin
+// collection trails, etc.
+//
+//=============================================================================
+static const int DEFAULT_NUM_SPARKLES = 150;
+
+class Sparkle
+{
+public:
+ enum eSparkleTextures
+ {
+ ST_Sparkle, // 0
+ ST_Spark, // 1
+ ST_Puff, // 2
+ ST_Leaf, // 3
+ ST_BlueSparkle, // 4 - used for gags.
+ ST_Paint, // 5
+ ST_Ring, // 6
+ ST_NUM_TEXTURES,
+ ST_SortedSparkle,
+ ST_SortedSpark,
+ ST_SortedPuff,
+ ST_SortedLeaf,
+ ST_SortedBlueSparkle,
+ ST_SortedPaint,
+ ST_SortedRing
+ };
+
+ Sparkle( unsigned char NumTextures = 1, unsigned short NumSparkles = DEFAULT_NUM_SPARKLES );
+ ~Sparkle();
+
+ static Sparkle* GetInstance( void );
+ static Sparkle* CreateInstance( unsigned char NumTextures = ST_NUM_TEXTURES, unsigned short NumSparkles = DEFAULT_NUM_SPARKLES );
+ static void DestroyInstance( void );
+
+ void Init( void );
+ void Destroy( void );
+
+ enum eSparkleEffect
+ {
+ SE_Trail,
+ SE_Glint,
+ SE_HUDGlint,
+ SE_Vanish
+ };
+
+ void AddSparkle( const rmt::Vector& Position, float Size, float Duration, const rmt::Vector& Velocity, eSparkleEffect Effect );
+ void AddGagSparkle(const rmt::Vector& Position, float Size, float Strength, unsigned int Caller); // See notes for information on the Caller.
+ void AddSparks( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength );
+ void AddDash( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength ); // NOTE: Velocity is direction of character travel.
+ void AddLanding( const rmt::Vector& Position, float Strength );
+ void AddLeaves( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength );
+ void AddPaintChips( const rmt::Vector& Position, const rmt::Vector& Velocity, pddiColour colour, float Strength );
+ void AddShockRing( const rmt::Vector& Position, float Strength );
+ void AddStars( const rmt::Vector& Position, float Strength );
+ void AddBottomOut( const rmt::Vector& Position );
+ void AddSmoke( const rmt::Matrix& Transform, const rmt::Vector& Offset, const rmt::Vector& Velocity, float Strength );
+
+ void Update( unsigned int Elapsedms ); // Elapsed milliseconds.
+ enum eSparkleRenderMode
+ {
+ SRM_IncludeSorted,
+ SRM_ExcludeSorted,
+ SRM_SortedOnly
+ };
+ // Note that the RenderSorted Draws all the particles tagged as being sorted. This is a pretty crude implementation and designed just the fix the car smoke.
+ void Render( eSparkleRenderMode Mode = SRM_IncludeSorted );
+ void HUDRender( void ); // Draw any HUD sparkles.
+
+ void SetTexture( unsigned char Index, tTexture* Texture );
+
+ static rmt::Randomizer sRandom;
+ static bool sRandomSeeded;
+
+protected:
+ //Prevent wasteful constructor creation.
+ Sparkle( const Sparkle& That );
+ Sparkle& operator=( const Sparkle& That );
+
+ struct ActiveSparkle
+ {
+ ActiveSparkle();
+ rmt::Vector Position;
+ rmt::Vector Velocity;
+ float Size;
+ float DurationRatio;
+ float Life;
+ pddiColour Colour;
+ unsigned int Active : 1;
+ unsigned int HUD : 1;
+ unsigned int Texture : 4;
+ unsigned int Align : 2;
+ unsigned int Scale : 3;
+ unsigned int ColourAnim : 3;
+ unsigned int CornerFade : 1;
+ unsigned int Motion : 2;
+ };
+ enum eSparkleMotion
+ {
+ SM_Linear,
+ SM_Decelerate,
+ SM_Gravity,
+ SM_Drift
+ };
+ enum eSparkleColour
+ {
+ SC_LinearDown,
+ SC_LinearDownAtHalf,
+ SC_LinearAlphaDown,
+ SC_LinearRedDownAtHalf,
+ SC_BulgeDown
+ };
+ enum eSparkleScale
+ {
+ SS_Constant,
+ SS_LinearDown,
+ SS_BulgeDown,
+ SS_LinearUp,
+ SS_LinearGrow,
+ SS_LinearTriple
+ };
+ enum eSparkleAlign
+ {
+ SA_None,
+ SA_MotionStreak,
+ SA_Flat,
+ SA_Rotation
+ };
+ ActiveSparkle* GetInactiveSparkle( void );
+ static Sparkle* spInstance;
+ ActiveSparkle* mActiveSparkles;
+ tTexture** mpTextures;
+ unsigned short mNumSparkles;
+ unsigned short mNextInactiveSparkle;
+ unsigned short* mNumActiveSparkles;
+ unsigned short* mNumHUDSparkles;
+ unsigned char mNumTextures;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline Sparkle* GetSparkleManager() { return( Sparkle::GetInstance() ); }
+
+#endif // #ifndef SPARKLE_H
diff --git a/game/code/worldsim/groundplanepool.cpp b/game/code/worldsim/groundplanepool.cpp
new file mode 100644
index 0000000..6a8bba8
--- /dev/null
+++ b/game/code/worldsim/groundplanepool.cpp
@@ -0,0 +1,326 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: groundplanepool.cpp
+//
+// Description: manage pool of ground planes for dynamic physics objects
+//
+// History: July 31, 2002 - created, gmayer
+//
+//=============================================================================
+
+
+//========================================
+// System Includes
+//========================================
+
+#include <simcommon/simstate.hpp>
+
+
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionmanager.hpp>
+
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisiondisplay.hpp>
+
+#include <simcommon/physicsproperties.hpp>
+
+#include <raddebug.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/groundplanepool.h>
+#include <worldsim/physicsairef.h>
+
+#include <memory/srrmemory.h>
+
+
+//=============================================================================
+// GroundPlanePool::GroundPlanePool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: GroundPlanePool
+//
+//=============================================================================
+GroundPlanePool::GroundPlanePool(int num)
+{
+MEMTRACK_PUSH_GROUP( "GroundPlanePool" );
+ mTotalNum = num;
+
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PushHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+// #endif
+
+ mPool = new sim::ManualSimState*[mTotalNum];
+ //mPool = new sim::SimState*[mTotalNum];
+ mInUse = new bool[mTotalNum];
+
+ mSimStateOwners = new sim::SimState*[mTotalNum];
+
+ mGroundPlanePhysicsProperties = new sim::PhysicsProperties;
+
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ rmt::Vector p(0.0f, 0.0f, 0.0f);
+ rmt::Vector n(0.0f, 1.0f, 0.0f);
+
+ sim::WallVolume* tempwall = new sim::WallVolume(p, n);
+
+ // TODO - are the temp volumes getting deleted by the sim state?
+ mPool[i] = (sim::ManualSimState*)(sim::SimState::CreateManualSimState(tempwall));
+
+ //static SimState* CreateSimState(CollisionVolume* inCollisionVolume, char* inName = NULL, tEntityStore* inStore = NULL);
+
+ //mPool[i] = sim::SimState::CreateSimState(tempwall);
+
+ mPool[i]->AddRef();
+
+ mPool[i]->GetCollisionObject()->SetManualUpdate(true);
+ mPool[i]->GetCollisionObject()->SetAutoPair(false);
+ mPool[i]->GetCollisionObject()->SetIsStatic(true);
+
+ //mPool[i]->GetCollisionObject()->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+ mPool[i]->SetPhysicsProperties(this->mGroundPlanePhysicsProperties);
+
+ // give a reasonable name for debugging purposes...
+ char buffy[128];
+ sprintf(buffy, "groundplanepool_id%d", i);
+ mPool[i]->GetCollisionObject()->SetName(buffy);
+
+ mInUse[i] = false;
+
+ mPool[i]->GetCollisionObject()->SetCollisionEnabled(false);
+
+ mPool[i]->mAIRefIndex = PhysicsAIRef::redBrickPhizMoveableGroundPlane;
+ mPool[i]->mAIRefPointer = 0; // only set if object is derived from CollisionEntityDSG
+
+
+ mSimStateOwners[i] = 0;
+
+
+
+ }
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+// #endif
+MEMTRACK_POP_GROUP( "GroundPlanePool" );
+}
+
+//=============================================================================
+// GroundPlanePool::~GroundPlanePool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: GroundPlanePool
+//
+//=============================================================================
+GroundPlanePool::~GroundPlanePool()
+{
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ mPool[i]->Release();
+ }
+ delete mPool;
+ delete mInUse;
+
+ delete mSimStateOwners;
+
+}
+
+
+//=============================================================================
+// GroundPlanePool::GetNewGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int GroundPlanePool::GetNewGroundPlane(sim::SimState* simStateOwner)
+{
+ // find first one that's not in use
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ if(mInUse[i] == false)
+ {
+ mInUse[i] = true;
+
+ mSimStateOwners[i] = simStateOwner;
+
+ return i;
+ }
+ }
+
+ // failure
+ return -1;
+}
+
+//=============================================================================
+// GroundPlanePool::UpdateGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::UpdateGroundPlane(int index, rmt::Vector& position, rmt::Vector& normal)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+
+ if(index > -1 && index < mTotalNum) // just in case
+ {
+ rAssert(mInUse[index]);
+
+ // for convenience:
+ sim::CollisionObject* co = mPool[index]->GetCollisionObject();
+ sim::WallVolume* wall = (sim::WallVolume*)(co->GetCollisionVolume());
+
+ wall->mPosition = position;
+ wall->mNormal = normal;
+
+ co->PostManualUpdate();
+
+ //co->Relocated();
+ //obbox->UpdateBBox();
+
+ // only need to do this once - TODO .. ? only enable when 'owner' object get's hit? -
+ //co->SetCollisionEnabled(true);
+ }
+}
+
+
+//=============================================================================
+// GroundPlanePool::EnableCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::EnableCollision(int index)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+ rAssert( mInUse[index] );
+
+ sim::CollisionObject* co = mPool[index]->GetCollisionObject();
+ co->SetCollisionEnabled(true);
+}
+
+
+//=============================================================================
+// GroundPlanePool::DisableCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::DisableCollision(int index)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+ rAssert( mInUse[index] );
+
+ sim::CollisionObject* co = mPool[index]->GetCollisionObject();
+ co->SetCollisionEnabled(false);
+}
+
+
+//=============================================================================
+// GroundPlanePool::FreeGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::FreeGroundPlane(int index)
+{
+ // make this safe to call with any value.
+ //
+ // note: we are not responsible for taking it out of any collision or anything like that.
+ if(index < 0 || index >= mTotalNum)
+ {
+ return;
+ }
+ mInUse[index] = false;
+ mSimStateOwners[index] = 0;
+}
+
+
+
+//=============================================================================
+// GroundPlanePool::FreeAllGroundPlanes
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool GroundPlanePool::FreeAllGroundPlanes() // returns false if there was a problem...
+{
+ // called when quitting game or a level
+ bool ok = true;
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ if(mInUse[i])
+ {
+ ok = false;
+ rAssert(false);
+ }
+ mInUse[i] = false;
+ mSimStateOwners[i] = 0;
+ }
+ return ok;
+
+}
+
+
+
+//=============================================================================
+// GroundPlanePool::GetSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: sim
+//
+//=============================================================================
+sim::ManualSimState* GroundPlanePool::GetSimState(int index)
+//sim::SimState* GroundPlanePool::GetSimState(int index)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+ rAssert(mInUse[index]);
+
+ return mPool[index];
+}
+
+
diff --git a/game/code/worldsim/groundplanepool.h b/game/code/worldsim/groundplanepool.h
new file mode 100644
index 0000000..ca93e33
--- /dev/null
+++ b/game/code/worldsim/groundplanepool.h
@@ -0,0 +1,71 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: groundplanepool.h
+//
+// Description: manage pool of ground planes for dynamic physics objects
+//
+// History: July 31, 2002 - created, gmayer
+//
+//=============================================================================
+
+#ifndef GROUNDPLANEPOOL_H
+#define GROUNDPLANEPOOL_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <simcommon/simstate.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+
+// note to self: maybe vehicles should use this?
+
+class GroundPlanePool
+{
+public:
+
+ GroundPlanePool(int num); // how big the pool should be
+ ~GroundPlanePool();
+
+ //sim::ManualSimState* GetNewGroundPlane();
+
+ //int GetNewGroundPlane(); // user refers to it with the returned index
+
+ int GetNewGroundPlane(sim::SimState* simStateOwner);
+
+
+ void UpdateGroundPlane(int index, rmt::Vector& position, rmt::Vector& normal);
+ void FreeGroundPlane(int index);
+ bool FreeAllGroundPlanes(); // returns false if there was a problem...
+
+ sim::ManualSimState* GetSimState(int index);
+ //sim::SimState* GetSimState(int index);
+
+ void EnableCollision(int index);
+ void DisableCollision(int index);
+
+
+private:
+
+ int mTotalNum;
+ sim::ManualSimState** mPool;
+ //sim::SimState** mPool;
+
+ bool* mInUse;
+
+
+ sim::SimState** mSimStateOwners;
+
+ // to save memory, and potentially have more control over the values
+ sim::PhysicsProperties* mGroundPlanePhysicsProperties;
+
+
+};
+
+
+#endif //GROUNDPLANEPOOL_H
diff --git a/game/code/worldsim/harass/allharass.cpp b/game/code/worldsim/harass/allharass.cpp
new file mode 100644
index 0000000..d587474
--- /dev/null
+++ b/game/code/worldsim/harass/allharass.cpp
@@ -0,0 +1 @@
+#include <worldsim/harass/chasemanager.cpp>
diff --git a/game/code/worldsim/harass/chasemanager.cpp b/game/code/worldsim/harass/chasemanager.cpp
new file mode 100644
index 0000000..1aa9d0e
--- /dev/null
+++ b/game/code/worldsim/harass/chasemanager.cpp
@@ -0,0 +1,911 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chasemanager.cpp
+//
+// Description: ChaseManager Class Implementation
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#include <worldsim/harass/chasemanager.h>
+#include <stdlib.h>
+
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+/*
+#include <camera/supercammanager.h>
+#include <camera/supercam.h>
+*/
+#include <ai/vehicle/chaseai.h>
+#include <roads/roadsegment.h>
+#include <roads/geometry.h>
+#include <render/Culling/ReserveArray.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <events/EventManager.h>
+
+#include <mission/gameplaymanager.h>
+
+////////////////////////////////////
+// Initialize Statics
+//ChaseManager* spChaseManager = NULL;
+static const float HARASS_BEELINE_DIST = 60.0f;
+
+/////////////////////////////////////
+
+// Constructors/Destructors
+ChaseManager::ChaseManager()
+:
+mNumRegisteredModels( 0 ),
+mTotalSpawnFrequencies( 0 ),
+mMaxVehicles( MAX_CHASE_VEHICLES )
+{
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ //GetEventManager()->AddListener( this, EVENT_REPAIR_CAR ); // Chase cars won't be repairing.
+
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ mModels[i].spawnFreq = 0;
+ }
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ mVehicles[i].v = NULL;
+ mVehicles[i].husk = NULL;
+ mVehicles[i].vAI = NULL;
+ mVehicles[i].isActive = false;
+ //mVehicles[i].activeListIndex = -1;
+ mVehicles[i].isOutOfSight = true;
+ mVehicles[i].markedForDeletion = false;
+ mVehicles[i].secondsOutOfSight = 0.0f;
+ }
+
+ // Gotta set stuff belonging to superclass to prevent a check in superclass from failing
+ mSpawnRadius = CHASE_SPAWN_RADIUS;
+ mRemoveRadius = CHASE_REMOVE_RADIUS;
+}
+
+ChaseManager::~ChaseManager()
+{
+ GetEventManager()->RemoveAll( this );
+ DeactivateAllVehicles();
+}
+
+bool ChaseManager::IsChaseVehicle( Vehicle* v )
+{
+ ChaseVehicle* cv = FindChaseVehicle( v );
+ return( cv != NULL );
+}
+
+
+
+ChaseManager::ChaseVehicle* ChaseManager::FindChaseVehicle( Vehicle* v )
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( v == mVehicles[i].v )
+ {
+ return &mVehicles[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+void ChaseManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ if( id == EVENT_VEHICLE_DESTROYED )
+ {
+ Vehicle* v = (Vehicle*) pEventData;
+ rAssert( v );
+
+ ChaseVehicle* cv = FindChaseVehicle( v );
+ if( cv == NULL )
+ {
+ return;
+ }
+
+ rAssert( cv->isActive );
+
+ DeactivateVehicle( cv );
+ /***
+ WELL now they don't want husks for specifically for chase/harass cars
+ and so we go round in circles.... Initially, they wanted ALL to have husks
+ "for consistency"... Now, not so much.
+
+ // obtain info from the vehicle
+ rmt::Vector initPos, initDir;
+ v->GetPosition( &initPos );
+ initDir = v->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // Remove original from VehicleCentral's ActiveList
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ vc->RemoveVehicleFromActiveList( cv->v );
+ GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_DESTROYED, cv->v );
+
+ // Update chase AI
+ vc->SetVehicleController( cv->activeListIndex, NULL );
+ cv->activeListIndex = -1;
+ cv->vAI->SetActive( false );
+ cv->vAI->Finalize();
+
+ //
+ // Now we grab husk and put it in place of the original vehicle
+ //
+ Vehicle* husk = GetVehicleCentral()->mHuskPool.RequestHusk( VT_AI, v );
+ if( husk == NULL )
+ {
+ return;
+ }
+ int res = GetVehicleCentral()->AddVehicleToActiveList( husk );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ cv->activeListIndex = res;
+
+ husk->AddRef();
+ husk->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ husk->SetResetFacingInRadians( angle );
+ husk->Reset();
+ husk->SetLocomotion( VL_PHYSICS );
+
+ cv->husk = husk;
+ */
+ }
+}
+
+
+
+void ChaseManager::Init()
+{
+ SpawnManager::Init();
+
+ ////////////////////////////////////////////////////////////////
+ // CLEAN UP PREVIOUS RUN
+ //
+ DeactivateAllVehicles();
+
+ ////////////////////////////////////////////////////////////////
+ // INITIALIZE FOR NEW RUN
+ //
+
+ int count = 0;
+
+ // create a temp string array of exactly this many elems or "buckets"
+ char** names = new char* [mTotalSpawnFrequencies];
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ for( int j=count; j<(count+mModels[i].spawnFreq); j++ )
+ {
+ names[j] = new char[160];
+ strcpy( names[j], mModels[i].name );
+ }
+ count += mModels[i].spawnFreq;
+ }
+ rAssert( count == mTotalSpawnFrequencies );
+
+ // pick a bucket random and spawn a car by that name
+ VehicleCentral* vc = ::GetVehicleCentral();
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ int coinflip = rand() % mTotalSpawnFrequencies;
+
+
+
+ Vehicle* v = vc->InitVehicle( names[coinflip], false, mConfileName, VT_AI,
+ VehicleCentral::ALLOW_DRIVER,
+ false, // not a playercar
+ false); // start with in car bv representation immediately
+ rAssert( v != NULL );
+
+ mVehicles[i].v = v;
+ mVehicles[i].husk = NULL;
+ mVehicles[i].v->AddRef();
+ mVehicles[i].v->SetLocomotion( VL_PHYSICS );
+ mVehicles[i].vAI = new (GMA_LEVEL_OTHER) ChaseAI( v, HARASS_BEELINE_DIST );
+ mVehicles[i].vAI->AddRef(); // Corresponding call to Release() will already call delete if refcount<=1
+ mVehicles[i].isActive = false;
+ mVehicles[i].isOutOfSight = true;
+ mVehicles[i].markedForDeletion = false;
+ mVehicles[i].secondsOutOfSight = 0.0f;
+ }
+
+ // clean up our temporary name buckets...
+ for( int i=0; i<mTotalSpawnFrequencies; i++ )
+ {
+ delete[] names[i];
+ }
+ delete[] names;
+}
+
+
+
+void ChaseManager::ClearAllObjects()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+}
+
+int ChaseManager::GetNumActiveVehicles()
+{
+ int numActive = 0;
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ numActive++;
+ }
+ }
+ return numActive;
+}
+
+void ChaseManager::ClearOutOfSightVehicles()
+{
+ static const float SECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL = 5.0f;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive &&
+ mVehicles[i].isOutOfSight &&
+ ( mVehicles[i].husk != NULL || mVehicles[i].markedForDeletion == true ) &&
+ mVehicles[i].secondsOutOfSight > SECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+}
+
+void ChaseManager::ClearObjectsInsideRadius( rmt::Vector center, float radius )
+{
+ int nObjectsRemoved = 0;
+ float minDistSqr = radius * radius;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ Vehicle* v = mVehicles[i].v;
+ rAssert( v != NULL );
+
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+
+ rmt::Vector toSphere = center - vPos;
+
+ if( toSphere.MagnitudeSqr() <= minDistSqr )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+ }
+}
+
+
+void ChaseManager::ClearObjectsOutsideRadius( rmt::Vector center, float radius )
+{
+ float minDistSqr = radius * radius;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ Vehicle* v = mVehicles[i].v;
+ rAssert( v != NULL );
+
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+
+ rmt::Vector toSphere = center - vPos;
+
+ if( toSphere.MagnitudeSqr() > minDistSqr )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+ }
+}
+
+bool ChaseManager::RegisterModel( const char* name, int spawnFreq )
+{
+ rAssert( name != NULL );
+ rAssert( spawnFreq > 0 );
+
+ // search existing SPARSE list for name
+ int freeIndex = -1;
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ // use spawnFreq = 0 to mark empty spots
+ if( mModels[i].spawnFreq == 0 )
+ {
+ freeIndex = i;
+ continue;
+ }
+
+ // found an existing model registered under same name, so overwrite w/ new values
+ if( strcmp(mModels[i].name, name)==0 )
+ {
+ mTotalSpawnFrequencies += spawnFreq - mModels[i].spawnFreq;
+ mModels[i].spawnFreq = spawnFreq;
+ return true;
+ }
+ }
+
+ if( 0 <= freeIndex && freeIndex < MAX_MODELS )
+ {
+ int len = strlen( name );
+ rAssert( len <= MAX_STRING_LEN );
+ strncpy( mModels[freeIndex].name, name, MAX_STRING_LEN );
+ mModels[freeIndex].name[MAX_STRING_LEN] = '\0';
+
+ mModels[freeIndex].spawnFreq = spawnFreq;
+ mTotalSpawnFrequencies += spawnFreq;
+ mNumRegisteredModels++;
+ return true;
+ }
+ return false;
+}
+
+bool ChaseManager::IsModelRegistered( const char* name )
+{
+ rAssert( name != NULL );
+
+ // search existing SPARSE list for name
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ // found an existing model registered under this name
+ if( strcmp(mModels[i].name, name)==0 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ChaseManager::UnregisterModel( const char* name )
+{
+ rAssert( name != NULL );
+
+ // search existing SPARSE list for name
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ if( mModels[i].spawnFreq == 0 )
+ {
+ continue;
+ }
+ int nameLen = strlen( name );
+ int modelNameLen = strlen( mModels[i].name );
+ if( (nameLen == modelNameLen) &&
+ (strncmp(mModels[i].name, name, nameLen)==0) )
+ {
+ mTotalSpawnFrequencies -= mModels[i].spawnFreq;
+ mNumRegisteredModels--;
+ mModels[i].spawnFreq = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ChaseManager::SetConfileName( const char* name )
+{
+ int len = strlen( name );
+ rAssert( len <= MAX_STRING_LEN );
+ strncpy( mConfileName, name, MAX_STRING_LEN );
+ mConfileName[MAX_STRING_LEN] = '\0';
+}
+
+
+void ChaseManager::DisableAllActiveVehicleAIs()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ //mVehicles[i].vAI->SetActive( false );
+ mVehicles[i].vAI->EnterLimbo();
+ }
+ }
+}
+
+void ChaseManager::EnableAllActiveVehicleAIs()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ //mVehicles[i].vAI->SetActive( true );
+ mVehicles[i].vAI->ExitLimbo();
+ }
+ }
+}
+
+void ChaseManager::AddObjects( float seconds )
+{
+ rAssert( seconds >= 0.0f );
+
+ // Get VehicleCentral. We'll need it lots.
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ // Get Player info.
+ rmt::Vector playerPos, playerVel;
+
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+ superCam->GetPosition( &playerPos );
+ superCam->GetVelocity( &playerVel );
+ */
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+ avatar->GetVelocity(playerVel);
+
+
+ float spawnRadius = GetSpawnRadius();
+
+
+ // Do FindRoadElems to get the road segments that intersect our sphere.
+ // For each road segment's lane, tally a list of all intersection points (max of 2)
+ // For each intersection point,
+ // if mNumObjects < mMaxObjects,
+ // ActivateVehicle()
+ // Initialize vehicle
+
+ ReserveArray<RoadSegment*> orList;
+ ::GetIntersectManager()->FindRoadSegmentElems( playerPos, spawnRadius, orList );
+
+ // This is the vehicle from our list of inactive chase vehicles that we'll spawn
+ // Abort early if there's no more vehicle available to spawn...
+ ChaseVehicle* cv = GetInactiveVehicle();
+ if( cv == NULL )
+ {
+ return;
+ }
+
+ for( int i=0; i<orList.mUseSize; i++ )
+ {
+ RoadSegment* segment = orList.mpData[i];
+ rAssert( segment != NULL );
+
+ // loop through all the lanes for this segment
+ for( unsigned int j=0; j<segment->GetNumLanes(); j++ )
+ {
+ // Find places where lane intersects with spawn radius (max 2 locations)
+ rmt::Vector startPos, startDir, endPos, endDir;
+ segment->GetLaneLocation( 0.0f, j, startPos, startDir );
+ segment->GetLaneLocation( 1.0f, j, endPos, endDir );
+
+ rmt::Vector intPts[2];
+ rmt::Sphere playerSphere( playerPos, spawnRadius );
+ int numIntersections = IntersectLineSphere( startPos, endPos, playerSphere, intPts );
+ if(numIntersections <= 0)
+ {
+ continue; // doesn't intersect our spawn sphere; skip this lane
+ }
+
+ // for each intersection point found, plant a vehicle
+ for( int k=0; k<numIntersections; k++ )
+ {
+ if( mNumObjects < mMaxVehicles )
+ {
+ rmt::Vector vPos = intPts[k];
+
+ if( cv == NULL )
+ {
+ return; // no more inactive vehicles available for spawning
+ }
+ Vehicle* v = cv->v;
+ rAssert( v != NULL );
+
+ //
+ // Detect if we're placing this car on top of another car
+ // by looping through active vehicles list. If there is car beneath us,
+ // don't spawn here and try next point.
+ //
+ rmt::Sphere vSphere;
+ v->GetBoundingSphere( &vSphere );
+ vSphere.centre = vPos;
+ if( SpawningOnTopOfAnotherVehicle(vSphere) )
+ {
+ continue; // gonna spawn on a car; try next spawn point
+ }
+
+ //
+ // Check if in the next n seconds, vehicle will still be in
+ // player's spawn zone (so we didn't add it in vain).
+ //
+
+ float timeToLiveInSeconds = 1.0f; //GetSecondsBetwAdds();
+
+ //
+ // Predict player pos after timeToLiveInSeconds seconds
+ //
+ rmt::Vector playerPos2 = playerPos + playerVel * timeToLiveInSeconds;
+
+ //
+ // Predict v pos after timeToLiveInSeconds seconds
+ //
+ rmt::Vector vDir = endPos - startPos;
+ vDir.Normalize(); // *** SQUARE ROOT! ***
+
+ float vAccel = v->mDesignerParams.mDpGasScale *
+ v->mSlipGasModifier *
+ 1.0f; //v->mGas;
+
+ float vDistWillTravel = 0.5f * vAccel * timeToLiveInSeconds * timeToLiveInSeconds;
+
+ rmt::Vector vPos2 = vPos + vDir * vDistWillTravel;
+
+ //
+ // Make sure vPos2 still inside playerPos2's radius before we spawn it
+ //
+ float minDistSqr = spawnRadius * spawnRadius;
+ float distSqr = (vPos2 - playerPos2).MagnitudeSqr();
+ if( distSqr < minDistSqr )
+ {
+ // want the vehicle to spawn facing the player
+ rmt::Vector initFacing = playerPos - vPos;
+
+ // initialize vehicle
+ v->SetInitialPosition( &vPos );
+ float angle = GetRotationAboutY( initFacing.x, initFacing.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+
+ bool succeeded = ActivateVehicle( cv );
+ if( !succeeded )
+ {
+ // vehiclecentral's activelist is full..
+ // no point trying to add anymore
+ return;
+ }
+
+ // just used the inactive vehicle; grab a new inactive vehicle
+ cv = GetInactiveVehicle();
+
+ } // end-of-if vehicle's next pos lies in radius of player's next spawn sphere
+ } // end-of-if mNumObjects < mMaxObjects
+ } // end-of-loop through all intersection points on spawn radius for this lane
+ } // end-of-loop through all lanes belonging to one segment
+ }// end-of-loop through all segments returned by DSGFind
+}
+
+void ChaseManager::RemoveObjects( float seconds )
+{
+ // Get Player info
+
+ rmt::Vector playerPos;
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+ superCam->GetPosition( &playerPos );
+ */
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+
+ float radius = GetRemoveRadius();
+
+ // Remove!
+ ClearObjectsOutsideRadius( playerPos, radius );
+
+ ClearOutOfSightVehicles();
+}
+
+void ChaseManager::UpdateObjects( float seconds )
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ if( mVehicles[i].v->mVehicleDestroyed &&
+ mVehicles[i].vAI->GetState() != VehicleAI::STATE_LIMBO )
+ {
+ mVehicles[i].vAI->EnterLimbo();
+ // NOTE:
+ // Never set mVehicles[i].isActive to false here because
+ // it means something entirely different (it means that it's not
+ // within your radius). Just set the AI's active flag to false
+ //mVehicles[i].isActive = false;
+ }
+
+ if( mVehicles[i].husk || mVehicles[i].markedForDeletion )
+ {
+ Vehicle* vehicle = mVehicles[i].v;
+ if(mVehicles[i].husk)
+ {
+ vehicle = mVehicles[i].husk;
+ }
+
+ // Test for out of sight of player 0... If so, increment timer, if not reset timer
+ rmt::Vector pos;
+
+ vehicle->GetPosition( &pos );
+ mVehicles[i].isOutOfSight = !GetGameplayManager()->TestPosInFrustrumOfPlayer( pos, 0 );
+
+ mVehicles[i].secondsOutOfSight += seconds;
+ if( !mVehicles[i].isOutOfSight )
+ {
+ mVehicles[i].secondsOutOfSight = 0.0f;
+ }
+ }
+ }
+ }
+
+ /*
+ // VehicleCentral automatically updates all vehicles & their AI
+ // controllers if they're under Physics Locomotion (which they are)
+ // so we need do nothing here.
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ mVehicles[i].v->Update( seconds );
+ }
+ }
+ */
+}
+
+void ChaseManager::SuspendAllVehicles()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].v != NULL && mVehicles[i].isActive)
+ {
+ mVehicles[i].v->CarDisplay( false );
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicles[i].v );
+ }
+ }
+}
+
+void ChaseManager::ResumeAllVehicles()
+{
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].v != NULL && mVehicles[i].isActive)
+ {
+ mVehicles[i].v->CarDisplay( true );
+ // add to vehiclecentral's activelist
+ if( vc->ActiveVehicleListIsFull() )
+ {
+ rAssert(false);
+ return;
+ }
+ int res = vc->AddVehicleToActiveList( mVehicles[i].v );
+ if( res == -1 )
+ {
+ // not supposed to happen since we already eliminated the
+ // safe failure condition (activelistisfull).. So something else
+ // went wrong...
+ rAssert( false );
+ }
+
+ // update chase AI
+ vc->SetVehicleController( res, mVehicles[i].vAI );
+ mVehicles[i].vAI->Initialize();
+ mVehicles[i].vAI->SetActive( true );
+ }
+ }
+}
+
+void ChaseManager::DeactivateVehicle( ChaseVehicle* cv )
+{
+ rAssert( cv->v != NULL );
+ rAssert( cv->vAI != NULL );
+ rAssert( cv->isActive );
+
+ // Remove from VehicleCentral's ActiveList
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ if( cv->husk == NULL )
+ {
+ vc->RemoveVehicleFromActiveList( cv->v );
+ GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_DESTROYED, cv->v );
+ }
+ else
+ {
+ bool succeeded = vc->RemoveVehicleFromActiveList( cv->husk );
+ rAssert( succeeded );
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( cv->husk );
+ cv->husk->Release(); // don't verify destruction cuz huskpool has final ownership
+ cv->husk = NULL;
+ }
+
+ //if( cv->activeListIndex != -1 )
+ //{
+ // vc->SetVehicleController( cv->activeListIndex, NULL );
+ //}
+ cv->vAI->SetActive( false );
+ cv->vAI->Finalize();
+ //cv->activeListIndex = -1;
+ cv->isActive = false;
+ cv->isOutOfSight = true;
+ cv->markedForDeletion = false;
+ cv->secondsOutOfSight = 0.0f;
+ mNumObjects--;
+}
+
+bool ChaseManager::ActivateVehicle( ChaseVehicle* cv )
+{
+ rAssert( cv->v != NULL );
+ rAssert( cv->vAI != NULL );
+ //rAssert( cv->activeListIndex == -1 );
+ rAssert( !cv->isActive );
+
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ // add to vehiclecentral's activelist
+ if( vc->ActiveVehicleListIsFull() )
+ {
+ return false;
+ }
+ int res = vc->AddVehicleToActiveList( cv->v );
+ if( res == -1 )
+ {
+ // not supposed to happen since we already eliminated the
+ // safe failure condition (activelistisfull).. So something else
+ // went wrong...
+ rAssert( false );
+ }
+
+ // update chase AI
+ vc->SetVehicleController( res, cv->vAI );
+ cv->vAI->Initialize();
+ cv->vAI->SetActive( true );
+
+ // update our variables
+ //cv->activeListIndex = res;
+ cv->isActive = true;
+ rAssert( cv->husk == NULL );
+ cv->isOutOfSight = true;
+ cv->markedForDeletion = false;
+ cv->secondsOutOfSight = 0.0f;
+ mNumObjects++;
+
+ // tell sound
+ GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_SPAWNED, cv->v );
+
+ return true;
+}
+
+void ChaseManager::DeactivateAllVehicles()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ if( mVehicles[i].v != NULL )
+ {
+ mVehicles[i].v->Release();
+ mVehicles[i].v = NULL;
+ }
+ if( mVehicles[i].vAI != NULL )
+ {
+ mVehicles[i].vAI->Release();
+ mVehicles[i].vAI = NULL;
+ }
+ }
+ rAssert( mNumObjects == 0 );
+}
+
+void ChaseManager::MarkAllVehiclesForDeletion()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].v != NULL )
+ {
+ mVehicles[i].markedForDeletion = true;
+ }
+ }
+}
+
+ChaseManager::ChaseVehicle* ChaseManager::GetInactiveVehicle()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( !mVehicles[i].isActive )
+ {
+ return &mVehicles[i];
+ }
+ }
+ return NULL;
+}
+
+bool ChaseManager::SpawningOnTopOfAnotherVehicle( const rmt::Sphere& s )
+{
+ VehicleCentral* vc = ::GetVehicleCentral();
+
+ int nActiveVehicles = 0;
+ Vehicle** activeVehicles = NULL;
+ vc->GetActiveVehicleList( activeVehicles, nActiveVehicles );
+
+ Vehicle* aCar;
+ rmt::Vector aPos;
+ rmt::Sphere aSphere;
+
+ //
+ // Because VehicleCentral's ActiveList is a SPARSE array, we have to loop
+ // through the MAX array size. But since we know how many actual vehicles
+ // there are in there, we can quit after we've reached that number. So keep
+ // a counter.
+ //
+ int vCount = 0;
+ for( int i=0; i<vc->GetMaxActiveVehicles(); i++ )
+ {
+ if( vCount >= nActiveVehicles )
+ {
+ break;
+ }
+
+ aCar = activeVehicles[i];
+ if( aCar == NULL )
+ {
+ continue;
+ }
+ vCount++;
+ aCar->GetPosition( &aPos );
+ aCar->GetBoundingSphere( &aSphere );
+
+ float distSqr = (aPos - s.centre).MagnitudeSqr();
+ float minDist = aSphere.radius + s.radius;
+ float minDistSqr = minDist * minDist;
+
+ // if we're colliding with another car
+ if( distSqr < minDistSqr )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+float ChaseManager::GetClosestCarPosition(rmt::Vector* toPos, rmt::Vector* carPos)
+{
+ float distSqr = 999999999.0f;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ if( mVehicles[i].v != NULL )
+ {
+ rmt::Vector currPos;
+ mVehicles[i].v->GetPosition(&currPos);
+ float currDistSqr = (*toPos - currPos).MagnitudeSqr();
+ if(currDistSqr < distSqr)
+ {
+ distSqr = currDistSqr;
+ *carPos = currPos;
+ }
+ }
+ }
+ }
+ return distSqr;
+}
+
diff --git a/game/code/worldsim/harass/chasemanager.h b/game/code/worldsim/harass/chasemanager.h
new file mode 100644
index 0000000..50fdc31
--- /dev/null
+++ b/game/code/worldsim/harass/chasemanager.h
@@ -0,0 +1,188 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chasemanager.h
+//
+// Description: ChaseManager Class declaration.
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef CHASEMANAGER_H
+#define CHASEMANAGER_H
+
+#include <worldsim/spawn/spawnmanager.h>
+#include <events/eventlistener.h>
+
+class Vehicle;
+class VehicleAI;
+
+static const float CHASE_SPAWN_RADIUS = 100.0f;
+static const float CHASE_REMOVE_RADIUS = 110.0f;
+static const float SECONDS_BETW_CHASE_ADDS = 0.7f;
+static const float SECONDS_BETW_CHASE_REMOVES = 0.5f;
+static const float SECONDS_BETW_CHASE_UPDATES = 0.0f;
+
+class ChaseManager
+: public SpawnManager, EventListener
+{
+public:
+
+ static const int MAX_CHASE_VEHICLES = 5;
+ static const int MAX_MODELS = 1;
+ static const int MAX_STRING_LEN = 64;
+
+ // Dusit [Nov 4,2002]:
+ // Don't make ChaseManager static. Make it possible for there
+ // to be 1 ChaseManager to spawn the 1 cSedan that needs to always
+ // be present, and another ChaseManager for the other set of chase cars.
+ ///////////////////////////////////////////////////////////////
+ // Statics
+ //static ChaseManager* GetInstance();
+ //static ChaseManager* DestroyInstance();
+
+ ChaseManager();
+ virtual ~ChaseManager();
+
+ ///////////////////////////////////////////////////////////////
+ // Implementing SpawnManager Stuff
+ void Init();
+ void ClearAllObjects();
+ void ClearObjectsInsideRadius( rmt::Vector center, float radius );
+ void ClearObjectsOutsideRadius( rmt::Vector center, float radius );
+ int GetAbsoluteMaxObjects() const;
+ int GetMaxObjects() const;
+ void SetMaxObjects( int maxObjects );
+ int GetMaxModels() const;
+ int GetNumRegisteredModels() const;
+ bool RegisterModel( const char* name, int spawnFreq );
+ bool IsModelRegistered( const char* name );
+ bool UnregisterModel( const char* name );
+ void SetConfileName( const char* name );
+ ///////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////
+ // EventListener
+ void HandleEvent( EventEnum id, void* pEventData );
+ ///////////////////////////////////////////////////////////////
+
+ bool IsChaseVehicle( Vehicle* v );
+ void DisableAllActiveVehicleAIs();
+ void EnableAllActiveVehicleAIs();
+ void DeactivateAllVehicles();
+ int GetNumActiveVehicles();
+ void MarkAllVehiclesForDeletion();
+
+ void SuspendAllVehicles();
+ void ResumeAllVehicles();
+
+ float GetClosestCarPosition(rmt::Vector* toPos, rmt::Vector* carPos);
+
+protected:
+ ///////////////////////////////////////////////////////////////
+ // Implementing SpawnManager Stuff
+ void AddObjects( float seconds );
+ void RemoveObjects( float seconds );
+ void UpdateObjects( float seconds );
+ float GetSecondsBetwAdds() const;
+ float GetSecondsBetwRemoves() const;
+ float GetSecondsBetwUpdates() const;
+
+
+private:
+
+ //static ChaseManager* spChaseManager;
+
+ struct Model
+ {
+ char name[MAX_STRING_LEN+1];
+ int spawnFreq;
+ };
+ Model mModels[MAX_MODELS];
+ int mNumRegisteredModels;
+ int mTotalSpawnFrequencies;
+
+ struct ChaseVehicle
+ {
+ Vehicle* v;
+ Vehicle* husk;
+ VehicleAI* vAI;
+ //int activeListIndex;
+ bool isActive;
+ bool isOutOfSight;
+ bool markedForDeletion;
+ float secondsOutOfSight;
+ };
+
+ int mMaxVehicles; // betw 0 and AbsoluteMax
+
+ ChaseVehicle mVehicles[MAX_CHASE_VEHICLES];
+
+ char mConfileName[MAX_STRING_LEN+1];
+
+ void ClearOutOfSightVehicles();
+
+ void DeactivateVehicle( ChaseVehicle* cv );
+ bool ActivateVehicle( ChaseVehicle* cv );
+
+ ChaseVehicle* FindChaseVehicle( Vehicle* v );
+
+ ChaseVehicle* GetInactiveVehicle();
+ bool SpawningOnTopOfAnotherVehicle( const rmt::Sphere& s );
+};
+
+////////////////////////////// INLINES /////////////////////////////////
+
+// etc, etc, etc...
+inline int ChaseManager::GetAbsoluteMaxObjects() const
+{
+ rAssert( MAX_CHASE_VEHICLES >= 0 );
+ return MAX_CHASE_VEHICLES;
+}
+inline int ChaseManager::GetMaxObjects() const
+{
+ return mMaxVehicles;
+}
+inline void ChaseManager::SetMaxObjects( int maxObjects )
+{
+ int absoluteMax = GetAbsoluteMaxObjects();
+ rAssert( absoluteMax >= 0 );
+
+ if( maxObjects < 0 )
+ {
+ maxObjects = 0;
+ }
+ else if( maxObjects > absoluteMax )
+ {
+ maxObjects = absoluteMax;
+ }
+
+ mMaxVehicles = maxObjects;
+}
+
+inline int ChaseManager::GetMaxModels() const
+{
+ return MAX_MODELS;
+}
+inline int ChaseManager::GetNumRegisteredModels() const
+{
+ return mNumRegisteredModels;
+}
+inline float ChaseManager::GetSecondsBetwAdds() const
+{
+ rAssert( SECONDS_BETW_CHASE_ADDS >= 0.0f );
+ return SECONDS_BETW_CHASE_ADDS;
+}
+inline float ChaseManager::GetSecondsBetwRemoves() const
+{
+ rAssert( SECONDS_BETW_CHASE_REMOVES >= 0.0f );
+ return SECONDS_BETW_CHASE_REMOVES;
+}
+inline float ChaseManager::GetSecondsBetwUpdates() const
+{
+ rAssert( SECONDS_BETW_CHASE_UPDATES >= 0.0f );
+ return SECONDS_BETW_CHASE_UPDATES;
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/hitnrunmanager.cpp b/game/code/worldsim/hitnrunmanager.cpp
new file mode 100644
index 0000000..070178b
--- /dev/null
+++ b/game/code/worldsim/hitnrunmanager.cpp
@@ -0,0 +1,1118 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: HitnRunManager.cpp
+//
+// Description: Implementation of class HitnRunManager
+//
+// History: 26/03/2003 + Created -- Jesse Cluff
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radmath/matrix.hpp>
+#include <radmath/random.hpp>
+
+//#include <math.h> //need this if we want extra accurate coin paths.
+
+//========================================
+// Project Includes
+//========================================
+#include <gameflow/gameflow.h>
+
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/coins/coinmanager.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <render/intersectmanager/intersectmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/dsg/statepropdsg.h>
+#include <data/persistentworldmanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <interiors/interiormanager.h>
+#include <constants/breakablesenum.h>
+#include <constants/vehicleenum.h>
+#include <worldsim/harass/chasemanager.h>
+#include <presentation\gui\ingame\guimanageringame.h>
+#include <presentation\gui\ingame\guiscreenhud.h>
+#include <presentation\gui\guisystem.h>
+#include <ai\statemanager.h>
+#include <contexts/context.h>
+
+/* Watcher stuff */
+#ifndef RAD_RELEASE
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#endif
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+static const float MAX_HITNRUN = 100.0f;
+static const float MIN_HITNRUN = 0.0f;
+static const float FADE_TIME = 750.0f;
+static float STOP_HITNRUN = 5.0f; //was 78
+static const float ALLOW_THROB = 60.0f;
+
+HitnRunManager* HitnRunManager::smpHitnRunManager = NULL;
+
+static const char* OBJECTS_THAT_NEVER_GIVE_COINS[] =
+{
+ "l1_hedgeunit",
+ "l7_hedgeunit"
+};
+
+static const int NUM_OBJECTS_THAT_NEVER_GIVE_COINS = sizeof(OBJECTS_THAT_NEVER_GIVE_COINS)/sizeof(OBJECTS_THAT_NEVER_GIVE_COINS[0]);
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+HitnRunManager* HitnRunManager::CreateInstance( void )
+{
+ rAssertMsg( smpHitnRunManager == NULL, "HitnRunManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ smpHitnRunManager = new HitnRunManager;
+ rAssert( smpHitnRunManager );
+ HeapManager::GetInstance()->PopHeap( GMA_PERSISTENT );
+ return HitnRunManager::GetInstance();
+}
+
+HitnRunManager* HitnRunManager::GetInstance( void )
+{
+ rAssertMsg( smpHitnRunManager != NULL, "HitnRunManager has not been created yet.\n" );
+ return smpHitnRunManager;
+}
+
+void HitnRunManager::DestroyInstance( void )
+{
+ rAssertMsg( smpHitnRunManager != NULL, "HitnRunManager has not been created.\n" );
+ delete smpHitnRunManager;
+ smpHitnRunManager = NULL;
+}
+
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+HitnRunManager::HitnRunManager() :
+ mCurrHitnRun(0.0f),
+ mDecayRatePerSecond(1.0f),
+ mDecayRateWhileSpawning(6.0f),
+ mDecayRateInsidePerSecond(10.0f),
+ mVehicleDestroyedDelta(25.0f),
+ mVehicleDestroyedCoins( 10 ),
+ mVehicleHitDelta(5.0f),
+ mHitBreakableDelta(7.5f),
+ mHitBreakableCoins( 1 ),
+ mHitMoveableDelta(7.5f),
+ mHitMoveableCoins( 1 ),
+ mHitKrustyGlassDelta(0.0f),
+ mHitKrustyGlassCoins( 5 ),
+ mColaPropDestroyedDelta(0.0f),
+ mColaPropDestroyedCoins( 10 ),
+ mKickNPCDelta(10.0f),
+ mKickNPCCoins( 0 ), //no coins on peds
+ mPlayerCarHitNPCDelta(13.0f),
+ mPlayerCarHitNPCCoins( 0 ), //no coins on peds
+ mBustedCoins( 50 ),
+ mSwitchSkinDelta(-100.0f),
+ mChangeVehicleDelta(-100.0f),
+ mPrevVehicleID(VehicleEnum::INVALID),
+// mPrevVehicleHit(NULL),
+// mPrevMoveableHit(NULL),
+ mLeastRecentlyHit(0),
+ mChaseOn(false),
+ mSpawnOn(false),
+ mDecayDelayMS(0.0f),
+ mDecayDelay(3000.0f),
+ mDecayDelayWhileSpawning(0.0f), // was 3000
+ mNumChaseCars(1),
+ mHitnRunDisabled(false),
+ mDecayDisabled(false),
+ mCopTicketDistance(10.0f),
+ mCopTicketDistanceOnFoot(10.0f),
+ mCopTicketSpeedThreshold(30.0f),
+ mCopTicketTimeThreshold(0.75f), // was 0.1; trying something to get rid of "bump = busted"
+ mTicketTimer(0.0f),
+ mLastUpdateValue(0.0f),
+ mLastHitnRunValue(0.0f),
+ mVehicleReset(true),
+ mVehicleResetTimer(0.0f),
+ mUnresponsiveTime(1500.0f),
+ mFadeDone(true),
+ mFadeTimer(0.0f),
+ mImmunityVehicle(NULL),
+ mWarningThreshold(0.78f),
+ mAllowThrob(true)
+{
+ for(int i = 0; i < 16; i++)
+ {
+ mPrevHits[i] = NULL;
+ }
+ // We don't want to do string compares when determining if an object that we just hit
+ // is to not give a coin
+ // So build a list of tUIDs in the ctor
+ mObjectsThatNeverGiveCoins = new tUID[ NUM_OBJECTS_THAT_NEVER_GIVE_COINS ];
+ for(int i = 0 ; i < NUM_OBJECTS_THAT_NEVER_GIVE_COINS ; i++ )
+ {
+ mObjectsThatNeverGiveCoins[ i ] = tName::MakeUID( OBJECTS_THAT_NEVER_GIVE_COINS[i] );
+ }
+
+#ifndef RAD_RELEASE
+ radDbgWatchAddFloat( &mCurrHitnRun, "Current HitnRun Value", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayRatePerSecond, "Decay Rate", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayRateWhileSpawning, "Decay Rate while spawning", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayRateInsidePerSecond, "Interior Decay Rate", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mVehicleDestroyedDelta, "Vehicle Destroyed Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mVehicleDestroyedCoins, "Vehicle Destroyed Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mHitBreakableDelta, "Hit Breakable Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mVehicleHitDelta, "Vehicle Hit Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mHitBreakableCoins, "Hit Breakable Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mHitMoveableDelta, "Hit Moveable Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mHitMoveableCoins, "Hit Moveable Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mHitKrustyGlassDelta, "Hit Krusty Glass Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mHitKrustyGlassCoins, "Hit Krusty Glass Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mColaPropDestroyedDelta, "Cola Prop Destroyed Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mColaPropDestroyedCoins, "Cola Prop Destroyed Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mKickNPCDelta, "Kick NPC Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mKickNPCCoins, "Kick NPC Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mPlayerCarHitNPCDelta, "Vehicle Hit NPC Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mPlayerCarHitNPCCoins, "Vehicle Hit NPC Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddInt( &mBustedCoins, "Coins lost when busted", "HitnRun", 0, 0, 0, 100, false );
+ radDbgWatchAddFloat( &mSwitchSkinDelta, "Switch Skin Delta", "HitnRun", NULL, NULL, 0.0f, -100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mChangeVehicleDelta, "Change Vehicle Delta", "HitnRun", NULL, NULL, 0.0f, -100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mNumChaseCars, "Number of Chase Vehicles", "HitnRun", NULL, NULL, 0, 5, false );
+ radDbgWatchAddFloat( &mDecayDelay, "Delay until meter decay", "HitnRun", NULL, NULL, 0.0f, 5000.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayDelayWhileSpawning, "Decay delay while spawning", "HitnRun", NULL, NULL, 0.0f, 5000.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &STOP_HITNRUN, "Spawning Stop Percentage", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketDistance, "Cop Ticket Distance", "HitnRun", NULL, NULL, 0.0f, 20.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketDistanceOnFoot, "Cop Ticket Distance On Foot", "HitnRun", NULL, NULL, 0.0f, 20.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketSpeedThreshold, "Cop Ticket Speed Threshold", "HitnRun", NULL, NULL, 0.0f, 30.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketTimeThreshold, "Cop Ticket Time Threshold", "HitnRun", NULL, NULL, 0.0f, 10.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mUnresponsiveTime, "Busted Unresponsive Time", "HitnRun", NULL, NULL, 0.0f, 10000.0f, false ); //false for read and write
+#endif
+}
+
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+HitnRunManager::~HitnRunManager()
+{
+#ifndef RAD_RELEASE
+ radDbgWatchDelete( &mCurrHitnRun );
+ radDbgWatchDelete( &mDecayRatePerSecond );
+ radDbgWatchDelete( &mDecayRateWhileSpawning );
+ radDbgWatchDelete( &mDecayRateInsidePerSecond );
+ radDbgWatchDelete( &mVehicleDestroyedDelta );
+ radDbgWatchDelete( &mVehicleDestroyedCoins );
+ radDbgWatchDelete( &mHitBreakableDelta );
+ radDbgWatchDelete( &mVehicleHitDelta );
+ radDbgWatchDelete( &mHitBreakableCoins );
+ radDbgWatchDelete( &mHitMoveableDelta );
+ radDbgWatchDelete( &mHitMoveableCoins );
+ radDbgWatchDelete( &mHitKrustyGlassDelta );
+ radDbgWatchDelete( &mHitKrustyGlassCoins );
+ radDbgWatchDelete( &mColaPropDestroyedDelta );
+ radDbgWatchDelete( &mColaPropDestroyedCoins );
+ radDbgWatchDelete( &mKickNPCDelta );
+ radDbgWatchDelete( &mKickNPCCoins );
+ radDbgWatchDelete( &mPlayerCarHitNPCDelta );
+ radDbgWatchDelete( &mPlayerCarHitNPCCoins );
+ radDbgWatchDelete( &mBustedCoins );
+ radDbgWatchDelete( &mSwitchSkinDelta );
+ radDbgWatchDelete( &mChangeVehicleDelta );
+ radDbgWatchDelete( &mNumChaseCars );
+ radDbgWatchDelete( &mDecayDelay );
+ radDbgWatchDelete( &mDecayDelayWhileSpawning );
+ radDbgWatchDelete( &STOP_HITNRUN );
+ radDbgWatchDelete( &mCopTicketDistance );
+ radDbgWatchDelete( &mCopTicketDistanceOnFoot );
+ radDbgWatchDelete( &mCopTicketSpeedThreshold );
+ radDbgWatchDelete( &mCopTicketTimeThreshold );
+ radDbgWatchDelete( &mUnresponsiveTime );
+#endif
+ Destroy( );
+ delete [] mObjectsThatNeverGiveCoins;
+ mObjectsThatNeverGiveCoins = NULL;
+}
+
+//==============================================================================
+// Description: Destruction method for all transient data.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void HitnRunManager::Destroy( void )
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+/*==============================================================================
+Description: This will be called set up as the game session begins.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================*/
+void HitnRunManager::Init( void )
+{
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+
+ //do some allocations here
+
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED_BY_USER );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_VEHICLE_COLLISION );
+ //GetEventManager()->AddListener( this, EVENT_HIT_BREAKABLE ); //currently get multiple events - don't use
+ GetEventManager()->AddListener( this, EVENT_HIT_MOVEABLE );
+ GetEventManager()->AddListener( this, EVENT_OBJECT_KICKED );
+ GetEventManager()->AddListener( this, EVENT_COLAPROP_DESTROYED ); //event waiting for mike r
+ GetEventManager()->AddListener( this, EVENT_KICK_NPC ); //add trigger - complicated
+ GetEventManager()->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC );
+
+ GetEventManager()->AddListener( this, EVENT_SWITCH_SKIN );
+ //GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END ); //doesn't work for traffic cars
+ GetEventManager()->AddListener( this, EVENT_ENTERING_PLAYER_CAR );
+ GetEventManager()->AddListener( this, EVENT_ENTERING_TRAFFIC_CAR );
+ GetEventManager()->AddListener( this, EVENT_LEVEL_START );
+
+ GetEventManager()->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+ GetEventManager()->AddListener( this, EVENT_CHASE_VEHICLE_SPAWNED );
+
+ GetEventManager()->AddListener( this, EVENT_MISSION_START );
+ GetEventManager()->AddListener( this, EVENT_MISSION_SUCCESS );
+ GetEventManager()->AddListener( this, EVENT_MISSION_FAILURE );
+ GetEventManager()->AddListener( this, EVENT_MISSION_RESET );
+
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_END );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_END );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_START );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_SKIP );
+}
+
+void HitnRunManager::ResetState( void )
+{
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ mDecayDisabled = false;
+ mHitnRunDisabled = false;
+ mAllowThrob = true;
+ mVehicleResetTimer = 0.0f;
+ mFadeTimer = 0.0f;
+ mFadeDone = true;
+ mVehicleReset = true;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ //chaseManager->DisableAllActiveVehicleAIs();
+ //chaseManager->MarkAllVehiclesForDeletion();
+ chaseManager->ClearAllObjects();
+ }
+ }
+}
+
+void HitnRunManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_ENTER_INTERIOR_START:
+ case EVENT_EXIT_INTERIOR_START:
+ case EVENT_CONVERSATION_START:
+ {
+ mHitnRunDisabled = true;
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->SuspendAllVehicles();
+ }
+ }
+ }
+ break;
+ case EVENT_ENTER_INTERIOR_END:
+ case EVENT_EXIT_INTERIOR_END:
+ case EVENT_CONVERSATION_DONE:
+ case EVENT_CONVERSATION_SKIP:
+ {
+ mHitnRunDisabled = false;
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->EnableAllActiveVehicleAIs();
+ chaseManager->ResumeAllVehicles();
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if(!mHitnRunDisabled && !GetInteriorManager()->IsInside())
+ {
+ float decayDelay = mDecayDelay;
+ if(mSpawnOn)
+ {
+ decayDelay = mDecayDelayWhileSpawning;
+ }
+
+ switch ( id )
+ {
+ case EVENT_VEHICLE_DESTROYED_BY_USER:
+ {
+ Vehicle* vehicle = static_cast<Vehicle*>( pEventData );
+ bool isChaseVehicle = false;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(vehicle->mName)
+ {
+ isChaseVehicle = chaseManager->IsModelRegistered(vehicle->mName);
+ }
+ }
+ }
+ if( !isChaseVehicle && ( vehicle->mVehicleType == VT_TRAFFIC || vehicle->mVehicleType == VT_AI ) && vehicle != mImmunityVehicle)
+ {
+ mCurrHitnRun += mVehicleDestroyedDelta;
+ mDecayDelayMS = decayDelay;
+ GetCoinManager()->SpawnCoins( mVehicleDestroyedCoins, vehicle->GetPosition(), vehicle->GetGroundY() );
+ }
+ }
+ break;
+ case EVENT_VEHICLE_VEHICLE_COLLISION:
+ {
+ CarOnCarCollisionEventData* data = static_cast<CarOnCarCollisionEventData*>( pEventData );
+ Vehicle* vehicle = data->vehicle;
+ bool isChaseVehicle = false;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(vehicle->mName)
+ {
+ isChaseVehicle = chaseManager->IsModelRegistered(vehicle->mName);
+ }
+ }
+ }
+ if( !isChaseVehicle && ( vehicle->mVehicleType == VT_TRAFFIC || vehicle->mVehicleType == VT_AI ) && vehicle != mImmunityVehicle)
+ {
+ if(mDecayDelayMS <= decayDelay/2)
+ {
+ mCurrHitnRun += mVehicleHitDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ }
+ }
+ break;
+ case EVENT_HIT_MOVEABLE:
+ //case EVENT_HIT_BREAKABLE:
+ {
+ //currently this assumes hitting a breakable breaks it ;)
+ sim::SimState* simState = static_cast<sim::SimState*>(pEventData);
+ //if(simState && simState->GetControl() == sim::simAICtrl)
+ if(simState)
+ {
+ sim::SimControlEnum con = simState->GetControl();
+ if(con == sim::simAICtrl)
+ {
+ CollisionEntityDSG* cedsg = static_cast< CollisionEntityDSG*>(simState->mAIRefPointer);
+ CollisionAttributes* collAttribs = cedsg->GetCollisionAttributes();
+ if(collAttribs)
+ {
+ if(HasntBeenHit((void*)cedsg))
+ {
+ BreakablesEnum::BreakableID id = collAttribs->GetBreakable();
+ if(id == BreakablesEnum::eKrustyGlassBreaking)
+ {
+ mCurrHitnRun += mHitKrustyGlassDelta;
+ GetCoinManager()->SpawnCoins( mHitKrustyGlassCoins, simState->GetPosition() );
+ }
+ else if(id == BreakablesEnum::eNull)
+ {
+ // Lets check if this thing that we impacted is actually allowed to give
+ // coins. (e.g. hedges are stateprops that unlike most
+ // others, should NEVER give coins and are thus in this list
+ if ( DoesObjectGiveCoins( cedsg ) )
+ {
+ mCurrHitnRun += mHitMoveableDelta;
+ GetCoinManager()->SpawnCoins( mHitMoveableCoins, simState->GetPosition() );
+ }
+ }
+ else
+ {
+ mCurrHitnRun += mHitBreakableDelta;
+ GetCoinManager()->SpawnCoins( mHitBreakableCoins, simState->GetPosition() );
+ }
+ RegisterHit((void*)cedsg);
+ mDecayDelayMS = decayDelay;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case EVENT_OBJECT_KICKED:
+ {
+ CollisionEntityDSG* cedsg = static_cast< CollisionEntityDSG*>(pEventData);
+ CollisionAttributes* collAttribs = cedsg->GetCollisionAttributes();
+ if(collAttribs)
+ {
+ if(HasntBeenHit((void*)cedsg))
+ {
+
+ StatePropDSG* spdsg = dynamic_cast<StatePropDSG*>(cedsg);
+ if(spdsg)
+ {
+ if(spdsg->GetStatePropUID() == tEntity::MakeUID("waspray"))
+ {
+ break;
+ }
+ }
+
+ rmt::Vector pos;
+ cedsg->GetPosition(&pos);
+
+ BreakablesEnum::BreakableID id = collAttribs->GetBreakable();
+ if(id == BreakablesEnum::eKrustyGlassBreaking)
+ {
+ mCurrHitnRun += mHitKrustyGlassDelta;
+ GetCoinManager()->SpawnCoins( mHitKrustyGlassCoins, pos );
+ }
+ else if(id == BreakablesEnum::eNull)
+ {
+ mCurrHitnRun += mHitMoveableDelta;
+ GetCoinManager()->SpawnCoins( mHitMoveableCoins, pos );
+ }
+ else
+ {
+ mCurrHitnRun += mHitBreakableDelta;
+ GetCoinManager()->SpawnCoins( mHitBreakableCoins, pos );
+ }
+ RegisterHit((void*)cedsg);
+ mDecayDelayMS = decayDelay;
+ }
+ }
+ }
+ break;
+ case EVENT_COLAPROP_DESTROYED:
+ {
+ //handles cola state props being destroyed (krustyglass is handled as a breakable)
+ mCurrHitnRun += mColaPropDestroyedDelta;
+ mDecayDelayMS = decayDelay;
+ StatePropDSG* prop = static_cast<StatePropDSG*>( pEventData );
+ GetCoinManager()->SpawnCoins( mColaPropDestroyedCoins, prop->rPosition() );
+ }
+ break;
+ case EVENT_KICK_NPC:
+ {
+ Character* ch = static_cast<Character*>(pEventData);
+ mCurrHitnRun += mKickNPCDelta;
+ mDecayDelayMS = decayDelay;
+ if( !ch->HasBeenHit() )
+ {
+ rmt::Vector pos;
+ ch->GetPosition( pos );
+ GetCoinManager()->SpawnCoins( mKickNPCCoins, pos );
+ }
+ }
+ break;
+ case EVENT_PLAYER_CAR_HIT_NPC:
+ {
+ Character* ch = static_cast<Character*>(pEventData);
+ if(ch->IsNPC())
+ {
+ // TODO:
+ // Make sure this is correct by verifying with Jesse.
+ // We don't use NPCController::FLAILING state anymore..
+ // Need a diff mechanism
+ if( ch->GetStateManager()->GetState() != CharacterAi::INSIM )
+ {
+ mCurrHitnRun += mPlayerCarHitNPCDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ /*
+ NPCharacter* npc = static_cast<NPCharacter*>(ch);
+ NPCController* npcCont = static_cast<NPCController*>(npc->GetController());
+ if(npcCont->GetState() != NPCController::FLAILING)
+ {
+ mCurrHitnRun += mPlayerCarHitNPCDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ */
+ }
+ if( !ch->HasBeenHit() )
+ {
+ rmt::Vector pos;
+ ch->GetPosition( pos );
+ GetCoinManager()->SpawnCoins( mPlayerCarHitNPCCoins, pos );
+ }
+ }
+ break;
+ case EVENT_SWITCH_SKIN:
+ {
+ //switching to same skin not handled currently
+ mCurrHitnRun += mSwitchSkinDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ break;
+ //case EVENT_GETINTOVEHICLE_END:
+ case EVENT_ENTERING_PLAYER_CAR:
+ case EVENT_ENTERING_TRAFFIC_CAR:
+ {
+ //have to track vehicle type to prevent getting in and out of same car
+ //Character* character = static_cast<Character*>(pEventData);
+ //Vehicle* vehicle = character->GetTargetVehicle();
+ Vehicle* vehicle = static_cast<Vehicle*>(pEventData);
+ if(mPrevVehicleID != vehicle->mVehicleID)
+ {
+ mCurrHitnRun += mChangeVehicleDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ mPrevVehicleID = vehicle->mVehicleID;
+ }
+ break;
+ case EVENT_HIT_AND_RUN_CAUGHT: //triggered via collision system
+ {
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL && mChaseOn )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->MarkAllVehiclesForDeletion();
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ GetCoinManager()->LoseCoins( mBustedCoins );
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( player );
+ Character* ch = player->GetCharacter();
+
+ mVehicleResetTimer = mUnresponsiveTime;
+ mVehicleReset = false;
+
+ if(ch->GetStateManager()->GetState() == CharacterAi::INCAR)
+ {
+ Vehicle* playerVehicle = player->GetVehicle();
+ //make car unresponsive
+ playerVehicle->SetDisableGasAndBrake(true);
+ }
+ }
+ }
+ }
+ break;
+ case EVENT_CHASE_VEHICLE_SPAWNED:
+ {
+ mChaseOn = true;
+ }
+ break;
+ default:
+ {
+ }
+ break;
+ }
+ }
+
+ switch ( id ) //do a second switch because these events are handled whether h&r is disabled or not
+ {
+ //case EVENT_MISSION_START:
+ // {
+ // mLastHitnRunValue = mCurrHitnRun;
+ // }
+ // break;
+ //case EVENT_MISSION_RESET: //commented out because it happens all the time before EVENT_MISSION_START
+ // {
+ // if(mLastHitnRunValue < mCurrHitnRun)
+ // {
+ // mCurrHitnRun = mLastHitnRunValue;
+ // }
+ // mSpawnOn = false;
+ // mChaseOn = false;
+ // }
+ // break;
+ case EVENT_MISSION_START:
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ mAllowThrob = true;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->MarkAllVehiclesForDeletion();
+ }
+ }
+ }
+ break;
+ case EVENT_MISSION_SUCCESS:
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ mDecayDisabled = false;
+ mHitnRunDisabled = false;
+ mAllowThrob = true;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->MarkAllVehiclesForDeletion();
+ }
+ }
+ }
+ break;
+ //case EVENT_MISSION_FAILURE:
+ // {
+ // //if(mLastHitnRunValue < mCurrHitnRun)
+ // //{
+ // // mCurrHitnRun = mLastHitnRunValue;
+ // //}
+ // mCurrHitnRun = MIN_HITNRUN;
+ // mDecayDisabled = false;
+ // mHitnRunDisabled = false;
+ // mSpawnOn = false;
+ // mChaseOn = false;
+ // }
+ // break;
+ case EVENT_GUI_FADE_OUT_DONE:
+ {
+ mFadeTimer = FADE_TIME;
+ mFadeDone = false;
+ GetEventManager()->RemoveListener( this, EVENT_GUI_FADE_OUT_DONE );
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->ClearAllObjects();
+ }
+ }
+ }
+ break;
+ default:
+ {
+ }
+ break;
+
+ }
+ if(mCurrHitnRun < MIN_HITNRUN)
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ }
+ else if(mCurrHitnRun > MAX_HITNRUN)
+ {
+ mCurrHitnRun = MAX_HITNRUN;
+ }
+
+}
+
+
+
+
+
+/*=============================================================================
+Update the position/animation of all the coins.
+=============================================================================*/
+void HitnRunManager::Update( int elapsedMS )
+{
+ if(mHitnRunDisabled)
+ return;
+
+ if(mCurrHitnRun < MAX_HITNRUN * (STOP_HITNRUN/100.0f))
+ {
+ //do chase stuff
+ mSpawnOn = false;
+ }
+
+ if(mCurrHitnRun < ALLOW_THROB)
+ {
+ mAllowThrob = true;
+ }
+ else if(mAllowThrob && (mCurrHitnRun > (mWarningThreshold * 100.0f)))
+ {
+ mAllowThrob = false;
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_METER_THROB, NULL );
+ }
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(!mSpawnOn)
+ {
+ if(mCurrHitnRun >= MAX_HITNRUN)
+ {
+ //do chase stuff
+ mSpawnOn = true;
+ chaseManager->SetMaxObjects(mNumChaseCars);
+ chaseManager->SetActive(true);
+ chaseManager->EnableAdd( true );
+ chaseManager->EnableRemove( true );
+ chaseManager->EnableUpdate( true );
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_START, NULL );
+ }
+ else
+ {
+ chaseManager->SetMaxObjects(0); //!!!!!!!!!!!!!!!!!!!!!!!!!!!every frame!
+ //chaseManager->SetActive( true );
+ chaseManager->EnableAdd( false );
+ chaseManager->EnableRemove( true );
+ chaseManager->EnableUpdate( true );
+ }
+ }
+
+ if(mChaseOn)
+ {
+ int cars = chaseManager->GetNumActiveVehicles();
+ if(cars <= 0)
+ {
+ if(!mSpawnOn)
+ {
+ mChaseOn = false;
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_EVADED, NULL );
+ }
+ }
+ else
+ {
+ //If the player's vehicle speed is < CopTicketSpeedThreshold FOR CopTicketTimeThreshold seconds
+ //WHILE the distance from the nearest cop is < CopTicketDistance, you get ticketed
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( player );
+
+ Vehicle* playerVehicle = player->GetVehicle();
+ if( playerVehicle != NULL )
+ {
+ float currSpeed = playerVehicle->mSpeedKmh;
+ float copTicketDistSqr = mCopTicketDistance * mCopTicketDistance;
+
+ // figure rough distance to player
+ rmt::Vector copPos, playerPos;
+ playerVehicle->GetPosition( &playerPos );
+ float distToPlayerSqr = chaseManager->GetClosestCarPosition( &playerPos, &copPos );
+
+ if( currSpeed < mCopTicketSpeedThreshold && distToPlayerSqr < copTicketDistSqr)
+ {
+ mTicketTimer += elapsedMS;
+ }
+ else
+ {
+ mTicketTimer = 0.0f;
+ }
+ //rTunePrintf("dist: %f, timer: %f\n", distToPlayerSqr, mTicketTimer);
+
+ if( mTicketTimer >= (mCopTicketTimeThreshold*1000.0f)) //convert from seconds to MS
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_CAUGHT, NULL );
+ mTicketTimer = 0.0f;
+ }
+ }
+ else
+ {
+ float copTicketDistSqr = (mCopTicketDistanceOnFoot) * (mCopTicketDistanceOnFoot);
+
+ // figure rough distance to player
+ rmt::Vector copPos, playerPos;
+ player->GetPosition( playerPos );
+ float distToPlayerSqr = chaseManager->GetClosestCarPosition( &playerPos, &copPos );
+
+ if( distToPlayerSqr < copTicketDistSqr)
+ {
+ mTicketTimer += elapsedMS;
+ }
+ else
+ {
+ mTicketTimer = 0.0f;
+ }
+ //rTunePrintf("dist: %f, timer: %f\n", distToPlayerSqr, mTicketTimer);
+
+ if( mTicketTimer >= (mCopTicketTimeThreshold*1000.0f*2)) //convert from seconds to MS
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_CAUGHT, NULL );
+ mTicketTimer = 0.0f;
+ }
+
+ }
+ }
+ }
+ else //if(!mChaseOn)
+ {
+ mTicketTimer = 0.0f;
+ }
+ }
+ }
+
+ if( fabsf(mLastUpdateValue - mCurrHitnRun) > 0.5f)
+ {
+ //update hud (before decreasing value)
+ CGuiScreenHud* hud = GetCurrentHud();
+ if(hud)
+ {
+ hud->SetHitAndRunMeter( mCurrHitnRun / MAX_HITNRUN );
+ mLastUpdateValue = mCurrHitnRun;
+ }
+ }
+
+ if(mDecayDelayMS <= 0.0f)
+ {
+ if( !mDecayDisabled )
+ {
+ if(mCurrHitnRun > MIN_HITNRUN)
+ {
+ float decayRatePerSecond = mDecayRatePerSecond;
+
+ if( GetInteriorManager()->IsInside() )
+ {
+ decayRatePerSecond = mDecayRateInsidePerSecond;
+ }
+ else if(mSpawnOn)
+ {
+ decayRatePerSecond = mDecayRateWhileSpawning;
+ }
+
+
+ float delta = decayRatePerSecond * elapsedMS * (-1.0f / 1000.0f);
+ mCurrHitnRun += delta;
+ if(mCurrHitnRun <= MIN_HITNRUN)
+ {
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(chaseManager->GetNumActiveVehicles() > 0)
+ {
+ chaseManager->MarkAllVehiclesForDeletion();
+ }
+ }
+ }
+ mCurrHitnRun = MIN_HITNRUN;
+ }
+ }
+ }
+ }
+ else
+ {
+ mDecayDelayMS -= elapsedMS;
+ }
+
+ if(!mVehicleReset)
+ {
+ if(mVehicleResetTimer <= 0.0f)
+ {
+ //reset vehicle
+ mVehicleReset = true;
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( player );
+
+ if( player->IsInCar() )
+ {
+ Vehicle* playerVehicle = player->GetVehicle();
+ playerVehicle->SetDisableGasAndBrake(false);
+ GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >(playerVehicle) );
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->ClearAllObjects();
+ }
+ }
+ }
+ else
+ {
+ GetEventManager()->AddListener( this, EVENT_GUI_FADE_OUT_DONE );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_FADE_OUT );
+ }
+
+ }
+ else
+ {
+ mVehicleResetTimer -= elapsedMS;
+ }
+ }
+
+ if(!mFadeDone)
+ {
+ if(mFadeTimer <= 0.0f)
+ {
+ //reset vehicle
+ mFadeDone = true;
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_FADE_IN );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Resume();
+ }
+ else
+ {
+ mFadeTimer -= elapsedMS;
+ }
+ }
+
+}
+
+float HitnRunManager::GetHitnRunValue() const
+{
+ return mCurrHitnRun;
+}
+
+/*=============================================================================
+Modify the player's Hit and Run meter
+=============================================================================*/
+void HitnRunManager::AdjustHitnRunValue( float delta )
+{
+ mCurrHitnRun += delta;
+ if(mSpawnOn)
+ {
+ mDecayDelayMS = mDecayDelayWhileSpawning;
+ }
+ else
+ {
+ mDecayDelayMS = mDecayDelay;
+ }
+
+ if(mCurrHitnRun < MIN_HITNRUN)
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ }
+ else if(mCurrHitnRun > MAX_HITNRUN)
+ {
+ mCurrHitnRun = MAX_HITNRUN;
+ }
+
+}
+
+
+/*=============================================================================
+Force the player's Hit and Run meter to a specific value
+=============================================================================*/
+void HitnRunManager::SetHitnRunValue( float value )
+{
+ rAssert(value <= MAX_HITNRUN && value >= MIN_HITNRUN);
+ mCurrHitnRun = value;
+}
+
+void HitnRunManager::MaxHitnRunValue()
+{
+ mCurrHitnRun = MAX_HITNRUN;
+}
+
+bool HitnRunManager::HasntBeenHit( void* ptr )
+{
+ for(int i = 0; i < 16; i++)
+ {
+ if( mPrevHits[i] == ptr )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void HitnRunManager::RegisterHit( void* ptr )
+{
+ mPrevHits[mLeastRecentlyHit] = ptr;
+ mLeastRecentlyHit++;
+ if(mLeastRecentlyHit >= 16)
+ {
+ mLeastRecentlyHit = 0;
+ }
+}
+
+
+bool HitnRunManager::DoesObjectGiveCoins( CollisionEntityDSG* cedsg )
+{
+ bool givesCoins = true;
+
+ // We want to get the type name, not the instance name. Get the type name from a
+ // stateprop from its embedded CStateProp object
+ if ( cedsg->GetAIRef() == PhysicsAIRef::StateProp )
+ {
+ const StatePropDSG* spdsg = static_cast< const StatePropDSG* >( cedsg );
+ rAssert( dynamic_cast< StatePropDSG* >( cedsg ) != NULL );
+ tUID typeName = spdsg->GetStatePropUID();
+ for ( int i = 0 ; i < NUM_OBJECTS_THAT_NEVER_GIVE_COINS ; i++ )
+ {
+ if ( typeName == mObjectsThatNeverGiveCoins[i] )
+ {
+ // Its a match, this object does not give coins
+ givesCoins = false;
+ break;
+ }
+ }
+ }
+ return givesCoins;
+}
+
+void HitnRunManager::RegisterVehicleImmunity( Vehicle* v )
+{
+ mImmunityVehicle = v;
+}
diff --git a/game/code/worldsim/hitnrunmanager.h b/game/code/worldsim/hitnrunmanager.h
new file mode 100644
index 0000000..3fa4b5c
--- /dev/null
+++ b/game/code/worldsim/hitnrunmanager.h
@@ -0,0 +1,150 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: hitnrunmanager.h
+//
+// Description: Hit & Run manager looks after everything to do with the hit & run system
+//
+// History: 26/03/2003 + Created -- Jesse Cluff
+//
+//=============================================================================
+
+#ifndef HITNRUNMANAGER_H
+#define HITNRUNMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventlistener.h>
+#include <constants/breakablesenum.h>
+#include <constants/vehicleenum.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class CollisionEntityDSG;
+
+//=============================================================================
+//
+// Synopsis: Handling events that cause the hit & run value to change and spawning
+// chase vehicles when value exceeds a given threshold
+//
+//=============================================================================
+class HitnRunManager : public EventListener
+{
+public:
+ static HitnRunManager* GetInstance( void );
+ static HitnRunManager* CreateInstance( void );
+ static void DestroyInstance( void );
+
+ void Init( void );
+ void Destroy( void );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void Update( int elapsedMS ); // spawns chase vehicles if hit & run value is too high.
+ void HUDRender( void ); // Renders HUD data.
+
+ float GetHitnRunValue( void ) const; // What is the current Hit & Run value.
+ void AdjustHitnRunValue( float delta ); // Adjust the Hit & Run value.
+ void SetHitnRunValue( float value );
+ void MaxHitnRunValue(); //sets hitnrun meter to maximum value
+
+ void DisableMeterDecay() { mDecayDisabled = true; }
+ void EnableMeterDecay() { mDecayDisabled = false ; }
+
+ bool IsHitnRunActive(){ return mChaseOn; }
+ bool IsHitnRunDisabled(){ return mHitnRunDisabled; }
+
+ void ResetState();
+
+ void EnableHitnRun() { ResetState(); mHitnRunDisabled = false; }
+ void DisableHitnRun() { ResetState(); mHitnRunDisabled = true; }
+
+ void SetNumChaseCars( int num ) { mNumChaseCars = num; }
+ void SetDecayRate( float rate ) { mDecayRatePerSecond = rate; }
+ float GetDecayRate() {return mDecayRatePerSecond;}
+ void SetDecayRateInside( float rate ) { mDecayRateInsidePerSecond = rate; }
+ float GetDecayRateInside() {return mDecayRateInsidePerSecond;}
+
+ bool IsWaitingForReset() { return !mVehicleReset; }
+
+ bool HasntBeenHit( void* ptr );
+ void RegisterHit( void* ptr );
+
+ void RegisterVehicleImmunity( Vehicle* v );
+
+ float GetWarningThreshold() { return mWarningThreshold; }
+
+ bool BustingPlayer() { return ( mVehicleResetTimer > 0.0f || mFadeTimer > 0.0f ); }
+
+protected:
+ //Prevent wasteful constructor creation.
+ HitnRunManager( const HitnRunManager& That );
+ HitnRunManager& operator=( const HitnRunManager& That );
+
+ static HitnRunManager* smpHitnRunManager;
+
+private:
+ HitnRunManager();
+ ~HitnRunManager();
+
+ float mCurrHitnRun;
+ float mDecayRatePerSecond;
+ float mDecayRateWhileSpawning;
+ float mDecayRateInsidePerSecond;
+ float mVehicleDestroyedDelta;
+ int mVehicleDestroyedCoins;
+ float mVehicleHitDelta;
+ float mHitBreakableDelta;
+ int mHitBreakableCoins;
+ float mHitMoveableDelta;
+ int mHitMoveableCoins;
+ float mHitKrustyGlassDelta;
+ int mHitKrustyGlassCoins;
+ float mColaPropDestroyedDelta;
+ int mColaPropDestroyedCoins;
+ float mKickNPCDelta;
+ int mKickNPCCoins;
+ float mPlayerCarHitNPCDelta;
+ int mPlayerCarHitNPCCoins;
+ int mBustedCoins;
+ float mSwitchSkinDelta;
+ float mChangeVehicleDelta;
+ VehicleEnum::VehicleID mPrevVehicleID;
+ //Vehicle* mPrevVehicleHit;
+ //CollisionEntityDSG* mPrevMoveableHit;
+ void* mPrevHits[16];
+ int mLeastRecentlyHit;
+ bool mChaseOn;
+ bool mSpawnOn;
+ float mDecayDelayMS;
+ float mDecayDelay;
+ float mDecayDelayWhileSpawning;
+ int mNumChaseCars;
+ bool mHitnRunDisabled;
+ bool mDecayDisabled;
+ float mCopTicketDistance;
+ float mCopTicketDistanceOnFoot;
+ float mCopTicketSpeedThreshold;
+ float mCopTicketTimeThreshold;
+ float mTicketTimer;
+ float mLastUpdateValue;
+ float mLastHitnRunValue;
+ bool mVehicleReset;
+ float mVehicleResetTimer;
+ float mUnresponsiveTime;
+ bool mFadeDone;
+ float mFadeTimer;
+
+ tUID* mObjectsThatNeverGiveCoins;
+ bool DoesObjectGiveCoins( CollisionEntityDSG* ); //const
+
+ Vehicle* mImmunityVehicle;
+ float mWarningThreshold;
+ bool mAllowThrob;
+};
+
+inline HitnRunManager* GetHitnRunManager() { return( HitnRunManager::GetInstance() ); }
+
+#endif //HITNRUNMANAGER_H
diff --git a/game/code/worldsim/huskpool.cpp b/game/code/worldsim/huskpool.cpp
new file mode 100644
index 0000000..4a7cdd1
--- /dev/null
+++ b/game/code/worldsim/huskpool.cpp
@@ -0,0 +1,309 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: huskpool.cpp
+//
+// Description:
+//
+// History: Mar 27, 2003 - created, gmayer
+//
+//=============================================================================
+
+
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/huskpool.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+
+#include <memory/srrmemory.h>
+
+#include <mission/gameplaymanager.h>
+
+
+//=============================================================================
+// HuskPool::HuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int num)
+//
+// Return: HuskPool
+//
+//=============================================================================
+HuskPool::HuskPool()
+{
+ mTotalNum = 0;
+}
+
+
+//=============================================================================
+// ::~HuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: HuskPool
+//
+//=============================================================================
+HuskPool::~HuskPool()
+{
+
+}
+
+
+//=============================================================================
+// HuskPool::Init
+//=============================================================================
+// Description: Comment
+//
+// this has to be called from GameplayContext and DemoContext OnStart
+
+//
+// Parameters: (int num)
+//
+// Return: void
+//
+//=============================================================================
+void HuskPool::Init(int num)
+{
+
+MEMTRACK_PUSH_GROUP( "HuskPool Init" );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ HeapMgr()->PushHeap (gma);
+
+ mTotalNum = num;
+
+ mHuskArray = new HuskData[num];
+
+ for( int i=0; i<num; i++)
+ {
+ // initially make all husks VT_TRAFFIC
+ // set the husk type to whatever the vehicle that just got destroyed is...
+
+ Vehicle* huskVehicle = GetVehicleCentral()->InitVehicle("huskA", false, 0, VT_TRAFFIC);
+ rAssert( huskVehicle );
+
+ huskVehicle->AddRef();
+ huskVehicle->SetLocomotion( VL_PHYSICS );
+#ifdef DEBUGWATCH
+ // HACK:
+ // Automatically take it out of vehicleAIRender, so we don't
+ // clutter up the render slots
+ huskVehicle->mTrafficLocomotion->GetAI()->UnregisterAI();
+#endif
+
+ mHuskArray[i].huskVehicle = huskVehicle;
+ mHuskArray[i].originalVehicle = NULL;
+ mHuskArray[i].inUse = false;
+ }
+ HeapMgr()->PopHeap (gma);
+
+MEMTRACK_PUSH_GROUP( "HuskPool Init" );
+
+}
+
+
+//=============================================================================
+// HuskPool::Empty
+//=============================================================================
+// Description: Comment
+//
+//
+// called from PauseContext and DemoContext OnStop
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void HuskPool::Empty()
+{
+ // called when you're leaving the level
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ // TODO:
+ // Should do this just in case?
+ //GetVehicleCentral()->RemoveVehicleFromActiveList( mHuskArray[i].huskVehicle );
+
+ mHuskArray[i].huskVehicle->ReleaseVerified();
+ mHuskArray[i].huskVehicle = NULL;
+
+ if( mHuskArray[i].originalVehicle )
+ {
+ mHuskArray[i].originalVehicle->Release();
+ mHuskArray[i].originalVehicle = NULL;
+ }
+ }
+
+ mTotalNum = 0;
+ delete[] mHuskArray;
+}
+
+
+//=============================================================================
+// HuskPool::RequestHusk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* HuskPool::RequestHusk( VehicleType vt, Vehicle* originalVehicle )
+{
+ rAssert( originalVehicle );
+ if( originalVehicle == NULL )
+ {
+ return NULL;
+ }
+
+ if( !WillMakeConvincingHusk( originalVehicle ) )
+ {
+ return NULL;
+ }
+
+ for( int i = 0; i < mTotalNum; i++ )
+ {
+ if(mHuskArray[i].inUse == false)
+ {
+ // give 'em this one
+ mHuskArray[i].inUse = true;
+ mHuskArray[i].huskVehicle->mVehicleType = vt; // I'm pretty sure this is safe to set on the fly
+ mHuskArray[i].huskVehicle->mGeometryVehicle->SetFadeAlpha( 255 ); // restore vehicle fade alpha
+
+ // store away the original vehicle
+ rAssert( mHuskArray[i].originalVehicle == NULL );
+ mHuskArray[i].originalVehicle = originalVehicle;
+ mHuskArray[i].originalVehicle->AddRef();
+
+ return mHuskArray[i].huskVehicle;
+ }
+ }
+
+ //rReleaseAssertMsg(0, "Not enough husks to go around!");
+ return NULL;
+}
+
+
+//=============================================================================
+// HuskPool::FreeHusk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* husk)
+//
+// Return: void
+//
+//=============================================================================
+void HuskPool::FreeHusk(Vehicle* husk)
+{
+ if( husk == NULL )
+ {
+ return;
+ }
+
+ for( int i = 0; i < mTotalNum; i++)
+ {
+ if( mHuskArray[i].huskVehicle == husk &&
+ mHuskArray[i].inUse )
+ {
+ mHuskArray[i].inUse = false;
+
+ rAssert( mHuskArray[i].originalVehicle );
+ mHuskArray[i].originalVehicle->Release();
+ mHuskArray[i].originalVehicle = NULL;
+
+ return;
+ }
+ }
+}
+
+bool HuskPool::IsHuskType( VehicleEnum::VehicleID id )
+{
+ // NOTE:
+ // If we start using other Husk models other than "huskA",
+ // we add the ID checks here...
+ if( id == VehicleEnum::HUSKA )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+Vehicle* HuskPool::FindOriginalVehicleGivenHusk( Vehicle* husk )
+{
+ if( husk == NULL )
+ {
+ return NULL;
+ }
+
+ for( int i = 0; i < mTotalNum; i++)
+ {
+ if(mHuskArray[i].huskVehicle == husk)
+ {
+ rAssert( mHuskArray[i].inUse );
+ rAssert( mHuskArray[i].originalVehicle );
+ return mHuskArray[i].originalVehicle;
+ }
+ }
+
+ return NULL;
+}
+
+Vehicle* HuskPool::FindHuskGivenOriginalVehicle( Vehicle* v )
+{
+ if( v == NULL )
+ {
+ return NULL;
+ }
+
+ for( int i = 0; i < mTotalNum; i++)
+ {
+ if(mHuskArray[i].originalVehicle == v)
+ {
+ rAssert( mHuskArray[i].inUse );
+ rAssert( mHuskArray[i].huskVehicle );
+ return mHuskArray[i].huskVehicle;
+ }
+ }
+
+ return NULL;
+
+}
+
+
+bool HuskPool::WillMakeConvincingHusk( Vehicle* origV )
+{
+ // these cars don't look anything remotely like
+ // HuskA, so we blow them up and leave nothing behind
+
+ if( origV->mVehicleID == VehicleEnum::HONOR_V ||
+ origV->mVehicleID == VehicleEnum::SCHOOLBU ||
+ origV->mVehicleID == VehicleEnum::WILLI_V ||
+ origV->mVehicleID == VehicleEnum::HALLO ||
+ origV->mVehicleID == VehicleEnum::OBLIT_V ||
+
+ origV->mVehicleID == VehicleEnum::MONO_V ||
+ origV->mVehicleID == VehicleEnum::KNIGH_V ||
+ origV->mVehicleID == VehicleEnum::CFIRE_V ||
+ origV->mVehicleID == VehicleEnum::CCOLA ||
+ origV->mVehicleID == VehicleEnum::HBIKE_V ||
+
+ origV->mVehicleID == VehicleEnum::ROCKE_V ||
+ origV->mVehicleID == VehicleEnum::ATV_V ||
+ origV->mVehicleID == VehicleEnum::DUNE_V ||
+ origV->mVehicleID == VehicleEnum::COFFIN ||
+ origV->mVehicleID == VehicleEnum::SHIP ||
+
+ origV->mVehicleID == VehicleEnum::WITCHCAR )
+ {
+ return false;
+ }
+ return true;
+} \ No newline at end of file
diff --git a/game/code/worldsim/huskpool.h b/game/code/worldsim/huskpool.h
new file mode 100644
index 0000000..7c582ac
--- /dev/null
+++ b/game/code/worldsim/huskpool.h
@@ -0,0 +1,56 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: huskpool.h
+//
+// Description:
+//
+// History: Mar 27, 2003 - created, gmayer
+//
+//=============================================================================
+
+#ifndef HUSKPOOL_H
+#define HUSKPOOL_H
+
+
+
+#include <worldsim/redbrick/vehicle.h>
+
+// vehicle central can own this
+class HuskPool
+{
+public:
+
+ HuskPool();
+ ~HuskPool();
+
+ void Init(int num); // how big the pool should be - the max number of simultaneous husks we ever want to have
+ void Empty(); // empty lists - for the OnStop..
+
+ Vehicle* RequestHusk( VehicleType vt, Vehicle* originalVehicle );
+ void FreeHusk( Vehicle* husk );
+
+ Vehicle* FindOriginalVehicleGivenHusk( Vehicle* husk );
+ Vehicle* FindHuskGivenOriginalVehicle( Vehicle* v );
+
+ bool IsHuskType( VehicleEnum::VehicleID id );
+
+private:
+ bool WillMakeConvincingHusk( Vehicle* origV );
+
+private:
+ int mTotalNum;
+
+ struct HuskData
+ {
+ Vehicle* originalVehicle;
+ Vehicle* huskVehicle;
+ bool inUse;
+ };
+
+ HuskData* mHuskArray;
+
+};
+
+
+#endif // HUSKPOOL_H
diff --git a/game/code/worldsim/parkedcars/allparkedcars.cpp b/game/code/worldsim/parkedcars/allparkedcars.cpp
new file mode 100644
index 0000000..1318e1e
--- /dev/null
+++ b/game/code/worldsim/parkedcars/allparkedcars.cpp
@@ -0,0 +1 @@
+#include <worldsim/parkedcars/parkedcarmanager.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/parkedcars/parkedcarmanager.cpp b/game/code/worldsim/parkedcars/parkedcarmanager.cpp
new file mode 100644
index 0000000..6d959d0
--- /dev/null
+++ b/game/code/worldsim/parkedcars/parkedcarmanager.cpp
@@ -0,0 +1,943 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: parkedcarmanager.cpp
+//
+// Description: Implement ParkedCarManager
+//
+// History: 2/6/2003 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp>
+#include <string.h>
+#include <stdlib.h>
+#include <p3d/utility.hpp>
+#include <pddi/pdditype.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <memory/srrmemory.h>
+
+#include <events/eventmanager.h>
+
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+
+#include <meta/carstartlocator.h>
+
+#include <main/commandlineoptions.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <cheats/cheatinputsystem.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/utility/hudmap.h>
+
+static const char FREE_CAR_SECTION[] = "FreeCar";
+
+//TODO: SHOULD I USE VT_USER?
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+ParkedCarManager* ParkedCarManager::spInstance = NULL;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ParkedCarManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: ParkedCarManager
+//
+//=============================================================================
+ParkedCarManager& ParkedCarManager::GetInstance()
+{
+ if ( spInstance == NULL )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ spInstance = new ParkedCarManager();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ }
+
+ return *spInstance;
+}
+
+//=============================================================================
+// ParkedCarManager::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::DestroyInstance()
+{
+ if ( spInstance != NULL )
+ {
+ delete spInstance;
+ spInstance = NULL;
+ }
+}
+
+//=============================================================================
+// ParkedCarManager::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_DYNAMIC_ZONE_LOAD_ENDED:
+ {
+ //Now we've finished loading locators (maybe)
+ //Let's choose some for this zone and reset the data.
+ if ( mNumLocators > 0 )
+ {
+ //Choose a locator or two.
+ unsigned int max = mNumCarTypes / MAX_ZONES;
+ bool isOdd = mNumCarTypes % 2 == 1;
+ if ( isOdd || mNumCarTypes < MAX_ZONES ) //This is an odd number
+ {
+ max += 1;
+ }
+
+ //Allocate the max num of parked cars among these locators.
+ unsigned int i;
+ unsigned int allocated = 0;
+ for ( i = 0; i < mNumLocators && i < max && allocated < mNumCarTypes - mNumParkedCars; ++i )
+ {
+ unsigned int which = rand() % mNumLocators;
+ if ( !(mLocators[ which ]->GetFlag( Locator::ACTIVE )) )
+ {
+ //Find another one, this one is used.
+ unsigned int j;
+ for ( j = (which + 1) % mNumLocators; j != which; j = (j + 1) % mNumLocators )
+ {
+ if ( mLocators[ j ]->GetFlag( Locator::ACTIVE ) )
+ {
+ which = j;
+ break;
+ }
+ }
+ }
+
+ if ( CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) || rand() % mNumCarTypes <= 2 ) //Weight it to placing the car.
+ {
+ //Mark the locator as used.
+
+ //Park this one!
+ Vehicle* car = NULL;
+ ParkedCarInfo* info = NULL;
+
+ //Find an available car
+ unsigned int j;
+ for ( j = 0; j < mNumCarTypes; ++j )
+ {
+ bool inUseByPlayer = false;
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ inUseByPlayer = ( avatar &&
+ avatar->GetCharacter()->IsInCar() &&
+ avatar->GetVehicle() == mParkedCars[ j ].mCar );
+
+ if ( mParkedCars[ j ].mLoadedZoneUID == static_cast< tUID >( 0 ) && !inUseByPlayer )
+ {
+ //We found one!
+ info = &mParkedCars[ j ];
+ car = mParkedCars[ j ].mCar;
+ mParkedCars[ j ].mLoadedZoneUID = mLoadingZoneUID;
+ break;
+ }
+ }
+
+ //rAssert( car ); //If this fails then mNumParkedCars is invalid or there's mem stompage
+
+ if ( car )
+ {
+ //Assign a new random colour
+ pddiColour colour;
+ TrafficManager::GetInstance()->GenerateRandomColour( colour );
+ car->mGeometryVehicle->SetTrafficBodyColour( colour );
+
+ int added = GetVehicleCentral()->AddVehicleToActiveList( car );
+ rAssert( added != -1 );
+ //info->mActiveListIndex = added;
+
+ //Set the position and facing and reset.
+ rmt::Vector location;
+ mLocators[ which ]->GetLocation( &location );
+ car->SetInitialPositionGroundOffsetAutoAdjust( &location );
+
+ car->SetResetFacingInRadians( mLocators[ which ]->GetRotation() );
+
+ car->Reset();
+
+ car->GetSimState()->SetControl( sim::simSimulationCtrl );
+
+ //Turn off the headlights
+ car->mGeometryVehicle->EnableLights( false );
+
+ ++mNumParkedCars;
+ ++allocated;
+ }
+ }
+ }
+
+ //Remove all the locators
+ for ( i = 0; i < mNumLocators; ++i )
+ {
+ p3d::inventory->Remove( mLocators[ i ] );
+ }
+
+ //Reset the data.
+ mLoadingZoneUID = 0;
+ mNumLocators = 0;
+ }
+ break;
+ }
+ case EVENT_DUMP_DYNA_SECTION:
+ {
+ //Make all the parked cars in the dynazone available to the new zone.
+ tUID sectionNameUID = static_cast<tName*>(pEventData)->GetUID();
+ unsigned int i;
+ for ( i = 0; i < mNumCarTypes; ++i )
+ {
+ if ( mParkedCars[ i ].mLoadedZoneUID == sectionNameUID )
+ {
+ //Make this puppy available.
+ mParkedCars[ i ].mLoadedZoneUID = 0;
+ mNumParkedCars--;
+
+ if( mParkedCars[ i ].mHusk )
+ {
+ // remove husk
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( mParkedCars[ i ].mHusk );
+ rAssert( succeeded );
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( mParkedCars[ i ].mHusk );
+ mParkedCars[ i ].mHusk->Release();
+ mParkedCars[ i ].mHusk = NULL;
+ }
+ }
+ }
+
+ //Now test to see if we should dump the free car too.
+ if (
+ (sectionNameUID == mFreeCar.mLoadedZoneUID || mFreeCar.mLoadedZoneUID == static_cast< tUID > ( 0 ) )
+ &&
+ (mFreeCar.mCar != NULL)
+ )
+
+ {
+ //Can we dump this car?
+ bool dump = true;
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar );
+
+ if ( ( GetGameplayManager()->GetCurrentVehicle() == mFreeCar.mCar ) ||
+ ( GetVehicleCentral()->IsCarUnderConstruction(mFreeCar.mCar) == true ) )
+ {
+ dump = false;
+ }
+ else
+ {
+ //Either the guy's not in the car or he's in another car.
+ rmt::Vector charToCar;
+ rmt::Vector carPos, charPos;
+ avatar->GetPosition( charPos );
+ rTuneAssertMsg(mFreeCar.mCar != NULL,"Attempting to calculate distance from Null free car to Character, Crash Imminent\n");
+ mFreeCar.mCar->GetPosition( &carPos );
+
+ charToCar.Sub( carPos, charPos );
+
+ const float minDist = 200.0f * 200.0f;
+ if ( charToCar.MagnitudeSqr() > minDist )
+ {
+ dump = true;
+ }
+ }
+
+ if ( dump )
+ {
+ RemoveFreeCar();
+ p3d::inventory->RemoveSectionElements( FREE_CAR_SECTION );
+ }
+
+ //Reset this as it is either a message that we can try to dump the car,
+ //or the car is already gone.
+ mFreeCar.mLoadedZoneUID = 0;
+
+ if ( mFreeCarLocator )
+ {
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+ }
+ }
+ break;
+ }
+
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ Vehicle* v = (Vehicle*) pEventData;
+ rAssert( v );
+
+ // the destroyed vehicle could be a free "moment" car or
+ // a normal parked traffic car. We must search for both
+
+ ParkedCarInfo* info = NULL;
+ bool isFreeCar = false;
+ FindParkedCarInfo( v, info, isFreeCar );
+
+ // if we never found it, this event doesn't concern us
+ if( info == NULL )
+ {
+ break;
+ }
+ // otherwise, it sure does...
+
+ // First, grab some information about the car
+ rmt::Vector initPos, initDir;
+ v->GetPosition( &initPos );
+ initDir = v->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // Now remove the old car from the activelist..
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( v );
+ rAssert( succeeded );
+ //GetVehicleCentral()->SetVehicleController( info->mActiveListIndex, NULL );
+ //info->mActiveListIndex = -1;
+
+ tUID zoneUID = info->mLoadedZoneUID;
+ info->mLoadedZoneUID = 0;
+ if( !isFreeCar )
+ {
+ mNumParkedCars--;
+ }
+
+ // Now grab a husk (if available), add it to the activelist,
+ // and place it exactly where theother car is...
+
+ Vehicle* husk = NULL;
+
+ // since we are just now destroying this car, it shouldn't have
+ // a husk yet.
+ rAssert( info->mHusk == NULL );
+ if( info->mHusk )
+ {
+ // weird! we shouldn't be here.. but we'll handle it gracefully
+ // otherwise we'll leak.
+ husk = info->mHusk;
+ }
+ else
+ {
+ husk = GetVehicleCentral()->mHuskPool.RequestHusk( VT_TRAFFIC, v );
+ if( husk )
+ {
+ husk->AddRef();
+ }
+ }
+
+ if( husk )
+ {
+ int res = GetVehicleCentral()->AddVehicleToActiveList( husk );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ break;
+ }
+ //info->mActiveListIndex = res;
+
+ husk->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ husk->SetResetFacingInRadians( angle );
+ husk->Reset();
+ husk->SetLocomotion( VL_PHYSICS );
+
+ info->mHusk = husk;
+ info->mLoadedZoneUID = zoneUID;
+
+ // add back the numparkedcars
+ mNumParkedCars++;
+ }
+ }
+ break;
+
+ case EVENT_REPAIR_CAR:
+ {
+ // NOTE: When we want to support other cars than the user car
+ // picking up Wrench/repair icons in the main game, we should pass in
+ // the vehicle pointer when we trigger this event.
+ //
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+ if( v == NULL )
+ {
+ return; // no current vehicle to repair.. oh well...
+ }
+
+ ParkedCarInfo* info = NULL;
+ bool isFreeCar = false;
+ FindParkedCarInfo( v, info, isFreeCar );
+
+ if( info == NULL )
+ {
+ return;
+ }
+
+ // if the vehicle is a husk.. gotta replace husk with original vehicle
+ if( v->mVehicleDestroyed )
+ {
+ if( info->mHusk )
+ {
+ // Destroyed, but has a husk
+ // Husk had to have existed to get to this point
+ Vehicle* husk = info->mHusk;
+
+ // obtain info from the husk
+ rmt::Vector initPos, initDir;
+ husk->GetPosition( &initPos );
+ initDir = husk->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // remove husk
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( husk );
+ rAssert( succeeded );
+ //GetVehicleCentral()->SetVehicleController( info->mActiveListIndex, NULL );
+ //info->mActiveListIndex = -1;
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( husk );
+ husk->Release();
+ husk = NULL;
+ info->mHusk = NULL;
+
+ v->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+
+ }
+
+ // put in original vehicle
+ int res = GetVehicleCentral()->AddVehicleToActiveList( v );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ //info->mActiveListIndex = res;
+
+ // if the avatar is inside a vehicle the vehicle
+ // is probably a husk, update this vehicle to be the original
+ // vehicle and place the character in this new vehicle
+ //
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar );
+ if( avatar->IsInCar() )
+ {
+ rAssert( avatar->GetVehicle() );
+ rAssert( GetVehicleCentral()->mHuskPool.IsHuskType( avatar->GetVehicle()->mVehicleID ) );
+
+ avatar->SetVehicle( v );
+
+ Character* character = avatar->GetCharacter();
+ GetAvatarManager()->PutCharacterInCar( character, v );
+ }
+
+ // fire off event so Esan can know when we switch the vehicle on him.
+ GetEventManager()->TriggerEvent(
+ EVENT_VEHICLE_DESTROYED_SYNC_SOUND, v );
+
+ }
+ // repair any damage to original vehicle
+ bool resetDamage = true;
+ v->ResetFlagsOnly( resetDamage );
+ }
+ break;
+
+ default:
+ {
+ rAssert( false ); //Why?
+ break;
+ }
+ }
+}
+
+void ParkedCarManager::FindParkedCarInfo( Vehicle* v, ParkedCarInfo*& info, bool& isFreeCar )
+{
+ info = NULL;
+ isFreeCar = false;
+
+ if( mFreeCar.mCar == v )
+ {
+ info = &mFreeCar;
+ isFreeCar = true;
+ }
+ if( info == NULL )
+ {
+ for( unsigned int i=0; i<mNumCarTypes; i++ )
+ {
+ if( mParkedCars[i].mCar == v )
+ {
+ // found it
+ info = &(mParkedCars[i]);
+ break;
+ }
+ }
+ }
+}
+
+
+
+//=============================================================================
+// ParkedCarManager::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::OnProcessRequestsComplete( void* pUserData )
+{
+ if ( mFreeCar.mLoadedZoneUID != static_cast< tUID >( 0 ) )
+ {
+ //Create the car.
+ CreateFreeCar();
+ }
+ else
+ {
+ //Throw this data away...
+ rAssert( false );
+
+ //Fragment.
+ p3d::inventory->RemoveSectionElements( FREE_CAR_SECTION );
+ }
+}
+
+
+//=============================================================================
+// ParkedCarManager::AddCarType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::AddCarType( const char* name )
+{
+ //return;
+
+#ifdef RAD_DEBUG
+ if ( CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) )
+ {
+ rAssert( mNumCarTypes < NUM_TEST_PARKED_CARS );
+ }
+ else
+ {
+ rAssert( mNumCarTypes < MAX_DIFFERENT_CARS );
+ }
+#endif
+
+ rAssert( strlen( name ) < MAX_CAR_NAME_LEN );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ unsigned int i;
+ unsigned int iterations = CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) ? NUM_TEST_PARKED_CARS / MAX_DIFFERENT_CARS : 1;
+ for ( i = 0; i < iterations; ++i )
+ {
+ //Set the name.
+ unsigned int nameLen = strlen( name ) > MAX_CAR_NAME_LEN ? MAX_CAR_NAME_LEN : strlen( name );
+ strncpy( mParkedCars[ mNumCarTypes ].mName, name, nameLen );
+ mParkedCars[ mNumCarTypes ].mName[ nameLen ] = '\0';
+
+ mParkedCars[ mNumCarTypes ].mLoadedZoneUID = 0;
+
+ Vehicle* car = GetVehicleCentral()->InitVehicle( mParkedCars[ mNumCarTypes ].mName, false, NULL, VT_TRAFFIC ); //TODO: Should I change the NULL to a con file?
+ rAssert( car );
+ car->mCreatedByParkedCarManager = true;
+
+ car->AddRef();
+
+ if ( car )
+ {
+ mParkedCars[ mNumCarTypes ].mCar = car;
+ ++mNumCarTypes;
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+//=============================================================================
+// ParkedCarManager::AddLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CarStartLocator* loc )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::AddLocator( CarStartLocator* loc )
+{
+ if(!mParkedCarsEnabled)
+ {
+ return;
+ }
+
+ //return;
+ rAssert( mNumLocators < MAX_LOCATORS_PER_ZONE );
+
+ //Adding locators from the same dyna zone.
+ if ( mNumLocators < MAX_LOCATORS_PER_ZONE )
+ {
+ mLocators[ mNumLocators ] = loc;
+ ++mNumLocators;
+ }
+
+ if ( mLoadingZoneUID == static_cast< tUID >( 0 ) )
+ {
+ //Set the zone ID
+ mLoadingZoneUID = GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID();
+ }
+}
+
+//=============================================================================
+// ParkedCarManager::AddFreeCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name, CarStartLocator* loc )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::AddFreeCar( const char* name, CarStartLocator* loc )
+{
+ if ( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_REDBRICK ) )
+ {
+ //This Bud's for you.
+ name = "redbrick";
+ }
+
+ if(!mParkedCarsEnabled)
+ {
+ return;
+ }
+
+ if ( mFreeCar.mCar == NULL )
+ {
+ unsigned int nameLen = strlen( name ) > MAX_CAR_NAME_LEN ? MAX_CAR_NAME_LEN : strlen( name );
+ strncpy( mFreeCar.mName, name, nameLen );
+ mFreeCar.mName[ nameLen ] = '\0';
+
+ mFreeCar.mLoadedZoneUID = GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID();
+ rAssert( mFreeCar.mLoadedZoneUID != static_cast< tUID >( 0 ) );
+
+ tRefCounted::Assign( mFreeCarLocator, loc );
+
+ //Try to load the car
+ char fileName[MAX_CAR_NAME_LEN + 5 + 32];
+ sprintf( fileName, "art\\cars\\%s.p3d", name );
+ GetLoadingManager()->AddRequest( FILEHANDLER_LEVEL, fileName, GMA_LEVEL_OTHER, FREE_CAR_SECTION, FREE_CAR_SECTION, this );
+ }
+}
+
+//=============================================================================
+// ParkedCarManager::RemoveFreeCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::RemoveFreeCar()
+{
+ //TODO: Do I need this? What if I make sure the car is not in the view frustrum?
+ //p3d::pddi->DrawSync();
+ if ( mFreeCar.mCar != NULL )
+ {
+ if( mFreeCar.mHusk == NULL )
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mFreeCar.mCar );
+ }
+ else
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mFreeCar.mHusk );
+
+ GetVehicleCentral()->mHuskPool.FreeHusk( mFreeCar.mHusk );
+ mFreeCar.mHusk->Release();
+ mFreeCar.mHusk = NULL;
+ }
+ /*
+ if( mFreeCar.mActiveListIndex != -1 )
+ {
+ GetVehicleCentral()->SetVehicleController( mFreeCar.mActiveListIndex, NULL );
+ }
+ mFreeCar.mActiveListIndex = -1;
+ */
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ HudMapIcon* playerCarIcon = currentHud->GetHudMap( 0 )->FindIcon( HudMapIcon::ICON_PLAYER_CAR );
+ if( playerCarIcon != NULL && playerCarIcon->m_dynamicLocator == mFreeCar.mCar )
+ {
+ GetGameplayManager()->UnregisterVehicleHUDIcon();
+ }
+ }
+
+ mFreeCar.mCar->Release();
+ mFreeCar.mCar = NULL;
+ mFreeCar.mLoadedZoneUID = 0;
+ }
+
+ if ( mFreeCarLocator )
+ {
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+ }
+}
+
+void ParkedCarManager::RemoveFreeCarIfClose( const rmt::Vector& position )
+{
+ // where is the free car?
+ rmt::Vector freeCarPosition;
+ if ( mFreeCar.mCar != NULL )
+ {
+ if(mFreeCar.mCar == GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle())
+ {
+ return;
+ }
+
+ mFreeCar.mCar->GetPosition( &freeCarPosition );
+ }
+ if( ( position - freeCarPosition ).MagnitudeSqr() < 10.0 * 10.0 )
+ {
+ RemoveFreeCar();
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ParkedCarManager::ParkedCarManager
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ParkedCarManager::ParkedCarManager() :
+ mNumCarTypes( 0 ),
+ mNumParkedCars( 0 ),
+ mNumLocators( 0 ),
+ mLoadingZoneUID( 0 ),
+ mFreeCarLocator( NULL )
+{
+ GetEventManager()->AddListener( this, EVENT_DUMP_DYNA_SECTION );
+ GetEventManager()->AddListener( this, EVENT_DYNAMIC_ZONE_LOAD_ENDED );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_REPAIR_CAR );
+
+ if ( CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) )
+ {
+ mParkedCars = new ParkedCarInfo[ NUM_TEST_PARKED_CARS ];
+ }
+ else
+ {
+ mParkedCars = new ParkedCarInfo[ MAX_DIFFERENT_CARS ];
+ }
+
+ //We want an inventory section for the car
+ p3d::inventory->AddSection( FREE_CAR_SECTION );
+
+ mParkedCarsEnabled = true;
+
+}
+
+//=============================================================================
+// ParkedCarManager::~ParkedCarManager
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ParkedCarManager::~ParkedCarManager()
+{
+ GetEventManager()->RemoveAll( this );
+
+ //Remove all the car types
+ MDKParkedCars();
+ delete[] mParkedCars;
+
+ //Clear out leftover locators.
+ for( unsigned int i = 0; i < mNumLocators; ++i )
+ {
+ p3d::inventory->Remove( mLocators[ i ] );
+ }
+
+ RemoveFreeCar();
+
+ if ( mFreeCarLocator )
+ {
+ p3d::inventory->Remove( mFreeCarLocator );
+
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+ }
+
+ p3d::inventory->RemoveSectionElements( FREE_CAR_SECTION );
+ p3d::inventory->DeleteSection( FREE_CAR_SECTION );
+
+}
+
+//=============================================================================
+// ParkedCarManager::CreateFreeCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::CreateFreeCar()
+{
+ rAssert( mFreeCarLocator );
+
+ Vehicle* car = GetVehicleCentral()->InitVehicle( mFreeCar.mName, true, NULL, VT_TRAFFIC ); //TODO: Should I change the NULL to a con file?
+ rAssert( car );
+ car->mCreatedByParkedCarManager = true;
+
+ car->AddRef();
+
+
+ //Set the position and facing and reset.
+ rmt::Vector location;
+ mFreeCarLocator->GetLocation( &location );
+ car->SetInitialPositionGroundOffsetAutoAdjust( &location );
+
+ car->SetResetFacingInRadians( mFreeCarLocator->GetRotation() );
+
+ car->Reset();
+
+
+ int added = GetVehicleCentral()->AddVehicleToActiveList( car );
+ rAssert( added != -1 );
+ //mFreeCar.mActiveListIndex = added;
+
+
+ //Turn off the lights.
+ car->mGeometryVehicle->EnableLights( false );
+
+ mFreeCar.mCar = car;
+
+ //Throw away the locator since we'll not need it again and we'll prevent the little memory block.
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+}
+
+//=============================================================================
+// ParkedCarManager::MDKParkCar
+//=============================================================================
+// Description: Deletes the park car instances , so that the art can be removed from the inventory.
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+
+
+void ParkedCarManager::MDKParkedCars()
+{
+ //Remove all the car types
+ for( unsigned int i = 0; i < mNumCarTypes; ++i )
+ {
+ rAssert( mParkedCars[ i ].mCar != NULL );
+
+ if( mParkedCars[ i ].mHusk == NULL )
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mParkedCars[ i ].mCar );
+ }
+ else
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mParkedCars[ i ].mHusk );
+
+ GetVehicleCentral()->mHuskPool.FreeHusk( mParkedCars[ i ].mHusk );
+ mParkedCars[ i ].mHusk->Release();
+ mParkedCars[ i ].mHusk = NULL;
+
+ }
+ /*
+ if( mParkedCars[ i ].mActiveListIndex != -1 )
+ {
+ GetVehicleCentral()->SetVehicleController( mParkedCars[ i ].mActiveListIndex, NULL );
+ }
+ mParkedCars[ i ].mActiveListIndex = -1;
+ */
+ mParkedCars[ i ].mCar->Release();
+ mParkedCars[ i ].mCar = NULL;
+ mParkedCars[ i ].mLoadedZoneUID = 0;
+ }
+
+ mNumParkedCars = 0;
+
+}
diff --git a/game/code/worldsim/parkedcars/parkedcarmanager.h b/game/code/worldsim/parkedcars/parkedcarmanager.h
new file mode 100644
index 0000000..35d0096
--- /dev/null
+++ b/game/code/worldsim/parkedcars/parkedcarmanager.h
@@ -0,0 +1,127 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: parkedcarmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 2/6/2003 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef PARKEDCARMANAGER_H
+#define PARKEDCARMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+#include <events/eventlistener.h>
+
+#include <worldsim/vehiclecentral.h>
+
+#include <loading/loadingmanager.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class CarStartLocator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ParkedCarManager : public EventListener, public LoadingManager::ProcessRequestsCallback
+{
+public:
+ enum
+ {
+ MAX_DIFFERENT_CARS = 5,
+ MAX_CAR_NAME_LEN = 32,
+ MAX_LOCATORS_PER_ZONE = 50,
+ MAX_ZONES = 3,
+ NUM_TEST_PARKED_CARS = VehicleCentral::MAX_ACTIVE_VEHICLES - 6
+ };
+
+ static ParkedCarManager& GetInstance();
+ static void DestroyInstance();
+
+ //From EventListener
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ //From LoadingManager
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ //Local
+ void AddCarType( const char* name );
+ void AddLocator( CarStartLocator* loc );
+
+ void AddFreeCar( const char* name, CarStartLocator* loc );
+ void RemoveFreeCar();
+ void RemoveFreeCarIfClose( const rmt::Vector& position );
+
+ //Chuck: release the parkcar instances so Gameplaymanager can free up the art
+ void MDKParkedCars();
+
+ void EnableParkedCars() {mParkedCarsEnabled = true;}
+ void DisableParkedCars() {mParkedCarsEnabled = false;}
+
+private:
+ static ParkedCarManager* spInstance;
+
+ struct ParkedCarInfo
+ {
+ ParkedCarInfo() :
+ mCar( NULL ),
+ mHusk( NULL ),
+ mLoadedZoneUID( 0 )
+ //mActiveListIndex( -1 )
+ { mName[0] ='\0'; };
+
+ char mName[ MAX_CAR_NAME_LEN + 1];
+ Vehicle* mCar;
+ Vehicle* mHusk;
+ tUID mLoadedZoneUID;
+ //int mActiveListIndex;
+ };
+
+ //Where all the parked cars are
+ ParkedCarInfo* mParkedCars;
+ unsigned int mNumCarTypes;
+ unsigned int mNumParkedCars;
+
+ //For loading locators, we store all the locators loaded while a given UID is active.
+ CarStartLocator* mLocators[ MAX_LOCATORS_PER_ZONE ];
+ unsigned int mNumLocators;
+ tUID mLoadingZoneUID;
+
+ //Free "Moment" cars
+ ParkedCarInfo mFreeCar;
+ CarStartLocator* mFreeCarLocator;
+
+ ParkedCarManager();
+ virtual ~ParkedCarManager();
+
+ void CreateFreeCar();
+
+ void FindParkedCarInfo( Vehicle* v, ParkedCarInfo*& info, bool& isFreeCar );
+
+ bool mParkedCarsEnabled; // little bool to disable these during street races
+
+ //Prevent wasteful constructor creation.
+ ParkedCarManager( const ParkedCarManager& parkedcarmanager );
+ ParkedCarManager& operator=( const ParkedCarManager& parkedcarmanager );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+inline ParkedCarManager& GetPCM() { return ParkedCarManager::GetInstance(); };
+
+#endif //PARKEDCARMANAGER_H
diff --git a/game/code/worldsim/ped/allped.cpp b/game/code/worldsim/ped/allped.cpp
new file mode 100644
index 0000000..62dbc20
--- /dev/null
+++ b/game/code/worldsim/ped/allped.cpp
@@ -0,0 +1,2 @@
+#include <worldsim/ped/pedestrianmanager.cpp>
+#include <worldsim/ped/pedestrian.cpp>
diff --git a/game/code/worldsim/ped/pedestrian.cpp b/game/code/worldsim/ped/pedestrian.cpp
new file mode 100644
index 0000000..b6ac4a9
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrian.cpp
@@ -0,0 +1,448 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrian.cpp
+//
+// Description: Implements Pedestrian class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+#include <string.h>
+#include <raddebug.hpp> // for rAssert & other debug print outs
+#include <radmath/radmath.hpp> // for rmt::Vector
+
+#include <worldsim/ped/pedestrian.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercam.h>
+
+#include <pedpaths/path.h>
+#include <pedpaths/pathsegment.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <ai/sequencer/action.h>
+#include <input/inputmanager.h>
+
+#include <mission/gameplaymanager.h>
+
+static const float SECONDS_BETW_ACTIVATE_POLLS = 0.0f;//0.5f;
+static const float PEDESTRIAN_PATHFIND_BACK_SPEED_MPS = 1.0f;
+static const float PEDESTRIAN_WALK_SPEED_MPS = 1.0f;
+static const float PEDESTRIAN_BRISK_WALK_SPEED_MPS = 1.5f;
+static const float PEDESTRIAN_RUN_SPEED_MPS = 3.0f;
+
+Pedestrian::Pedestrian() :
+ mMillisecondsOutOfSight( 0 ),
+ mIsActive( false ),
+ mPath( NULL ),
+ mFollowPathSpeedMps( 0.0f ),
+ mMarkedForActivation( false ),
+ mSecondsTillActivateSelf( 0.0f )
+{
+}
+
+Pedestrian::~Pedestrian()
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+// CharacterController
+/////////////////////////////////////////////////////////////////////////
+void Pedestrian::Update( float seconds )
+{
+ if( mMarkedForActivation )
+ {
+ rAssert( !mIsActive );
+
+
+ //mSecondsTillActivateSelf -= seconds;
+ //if( mSecondsTillActivateSelf < 0.0f )
+ //{
+ // mSecondsTillActivateSelf = SECONDS_BETW_ACTIVATE_POLLS;
+ if( ::GetCharacterManager()->IsModelLoaded( mModelName ) )
+ {
+ ActivateSelf();
+ }
+ //}
+ else
+ {
+ // don't update if marked for activation but not yet activated
+ return;
+ }
+ }
+
+ // don't update if not active!
+ if( !mIsActive )
+ {
+ return;
+ }
+ BEGIN_PROFILE( "Pedestrian::Update" );
+
+ if( mState == NPCController::FOLLOWING_PATH )
+ {
+
+ // Detect if another ped is near me
+ // if so, deal with "walk through one another" scenario if we're
+ // close enough to the player
+
+ rmt::Vector myPos;
+ GetCharacter()->GetPosition( &myPos );
+
+ rmt::Vector playerPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( playerPos );
+
+ const float MIN_DIST_SQR_FROM_PLAYER = 1225.0f;
+ if( (myPos - playerPos).MagnitudeSqr() <= MIN_DIST_SQR_FROM_PLAYER ||
+ GetInputManager()->GetGameState() == Input::ACTIVE_FIRST_PERSON )
+ {
+
+ int i = PedestrianManager::GetInstance()->mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ PedestrianManager::PedestrianStruct* pedStr = (PedestrianManager::PedestrianStruct*)
+ PedestrianManager::GetInstance()->mActivePeds.GetDataAt(i);
+ rAssert( pedStr );
+
+ Pedestrian* ped = pedStr->ped;
+ rAssert( ped );
+
+ if( !ped->GetIsActive() )
+ {
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ continue;
+ }
+
+ if( ped != this )
+ {
+ rmt::Vector pedPos;
+ ped->GetCharacter()->GetPosition( &pedPos );
+
+ rmt::Vector toPed = pedPos - myPos;
+
+ static const float MINDISTSQR_FROM_ANOTHER_PED_SMALL = 2.25f;
+ static const float MINDISTSQR_FROM_ANOTHER_PED_LARGE = 4.00f;
+
+ float distToleranceFromAnotherPedSqr = (mFollowPathSpeedMps > 1.0f) ?
+ MINDISTSQR_FROM_ANOTHER_PED_SMALL :
+ MINDISTSQR_FROM_ANOTHER_PED_LARGE;
+
+ if( toPed.MagnitudeSqr() < distToleranceFromAnotherPedSqr )
+ {
+ // here, we're too close to another ped on same path...
+ // If he's just following path as well, engage him in conversation!
+ if( ped->GetState() == NPCController::FOLLOWING_PATH )
+ {
+ // determine direction
+ toPed.Normalize(); // *** SQUARE ROOT! ***
+
+ // tell myself to transit to talking
+ TransitToState( NPCController::TALKING );
+ mTalkTarget = ped;
+ SetDirection( toPed );
+ mpCharacter->SetDesiredDir( choreo::GetWorldAngle(toPed.x,toPed.z) );
+
+ // tell him to transit to talking
+ toPed.Scale( -1.0f );
+ ped->TransitToState( NPCController::TALKING );
+ ped->mTalkTarget = this;
+ ped->SetDirection( toPed );
+ ped->GetCharacter()->SetDesiredDir( choreo::GetWorldAngle(toPed.x,toPed.z) );
+
+ StartTalking();
+
+ break;
+ }
+ else if( ped->GetState() == NPCController::PANICKING )
+ {
+ // He's... um.. panicking... We'd better panick too
+ IncitePanic();
+ break;
+ }
+ else
+ {
+ // he's not available for talking, but he's still nearby
+ // check if he's in front of us. If so, STAND and wait.
+
+ rmt::Vector forward, right, up;
+ up.Set( 0.0f, 1.0f, 0.0f );
+
+ mpCharacter->GetFacing( forward );
+ rAssert( rmt::Epsilon(forward.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ right.CrossProduct( up, forward );
+ bool pedOnMyRight = false;
+ bool gonnaHit = WillCollide( myPos,
+ forward, // heading (forward)
+ right, // right vector
+ 1.0f, 3.0f, pedPos, pedOnMyRight );
+
+ if( gonnaHit )
+ {
+ TransitToState( STANDING );
+ break;
+ }
+ }
+ }
+ }
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ } // end of WHILE
+ }
+ }
+ NPCController::Update( seconds );
+
+ END_PROFILE( "Pedestrian::Update" );
+
+}
+
+float Pedestrian::GetValue( int buttonId ) const
+{
+ return 0.0f;
+}
+
+bool Pedestrian::IsButtonDown( int buttonId ) const
+{
+ return false;
+}
+/////////////////////////////////////////////////////////////////////////
+
+
+
+void Pedestrian::Activate(
+ Path* path,
+ PathSegment* pathSegment,
+ rmt::Vector spawnPos,
+ const char* modelName
+ )
+
+{
+ rAssert( path );
+ rAssert( pathSegment );
+ rAssert( modelName );
+ rAssert( path->IsClosed() );
+
+ mStartPanicking = false;
+
+ // set where I am on the path
+ mPath = path;
+
+ /////////////////////////////////////////////////////////
+ // Add NPC Waypoints based on the path given...
+ int numSegs = path->GetNumPathSegments();
+ int numPts = numSegs; // a closed path has same number of points as segments
+
+ rAssert( 1 < numPts && numPts <= MAX_NPC_WAYPOINTS );
+
+ int currSegIndex = pathSegment->GetIndexToParentPath();
+
+ mNumNPCWaypoints = 0;
+ while( mNumNPCWaypoints < numPts )
+ {
+ rmt::Vector segEndPos;
+ pathSegment->GetEndPos( segEndPos );
+
+ AddNPCWaypoint( segEndPos );
+
+ // next segment!
+ currSegIndex = ( currSegIndex + 1 ) % numSegs;
+ rAssert( 0 <= currSegIndex && currSegIndex < numSegs );
+ pathSegment = path->GetPathSegmentByIndex( currSegIndex );
+ }
+ mCurrNPCWaypoint = 0;
+
+
+ //////////////////////////////////////////////////////////
+ rAssert( modelName != NULL );
+ int modelNameLen = strlen(modelName);
+ rAssert( modelNameLen <= Pedestrian::MAX_STRING_LEN );
+ strncpy( mModelName, modelName, Pedestrian::MAX_STRING_LEN );
+ mModelName[ modelNameLen ] = '\0';
+
+ mMarkedForActivation = true;
+ mSecondsTillActivateSelf = SECONDS_BETW_ACTIVATE_POLLS;
+
+ // set the Character's new spawn position (so whenever we do
+ // a Character::GetPosition, we get the correct position)
+ GetCharacter()->RelocateAndReset(spawnPos, 0.0f);
+
+ // diff between spawnPos & actual character position at this point
+ // is the ground fix up... spawnpos is the actual ON SEGMENT
+ // location itself..
+ mSpawnPos = spawnPos;
+}
+
+void Pedestrian::ActivateSelf()
+{
+ mMillisecondsOutOfSight = 0;
+ mMarkedForActivation = false;
+
+ // Test to see how close we are to the player... if too close
+ // just deactivate...
+ rmt::Vector myPos;
+ mpCharacter->GetPosition( myPos );
+
+ rmt::Vector playerPos, playerVel;
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+
+ /*
+ rmt::Vector camTarget;
+ SuperCam* pCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ pCam->GetHeadingNormalized( &camTarget );
+ rAssert( rmt::Epsilon(camTarget.MagnitudeSqr(), 1.0f, 0.0005f) );
+
+ rmt::Vector center = playerPos + camTarget * PedestrianManager::CENTER_OFFSET;
+
+ float distSqr = (myPos - center).MagnitudeSqr();
+ */
+ float distSqr = (myPos - playerPos).MagnitudeSqr();
+ float removeDistSqr = PedestrianManager::GetInstance()->FADE_RADIUS;
+ removeDistSqr *= removeDistSqr;
+
+ if( GetGameplayManager()->TestPosInFrustrumOfPlayer( myPos, 0 ) &&
+ distSqr < removeDistSqr )
+ {
+ PedestrianManager::GetInstance()->RemovePed( mpCharacter );
+ return;
+ }
+
+ // make sure we don't activate too near another ped...
+ const float TOO_CLOSE_TO_ANOTHER_PED_SQR = 2.25f;
+ rmt::Vector pedPos;
+ int i = PedestrianManager::GetInstance()->mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ PedestrianManager::PedestrianStruct* pedStr = (PedestrianManager::PedestrianStruct*)
+ PedestrianManager::GetInstance()->mActivePeds.GetDataAt(i);
+ rAssert( pedStr != NULL );
+
+ if( pedStr->ped == this )
+ {
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ continue; // I'm this ped! Ignore...
+ }
+
+ if( !pedStr->ped->GetIsActive() )
+ {
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ continue; // ped is waiting to self-activate, ignore it
+ }
+
+ pedStr->ped->GetCharacter()->GetPosition( &pedPos );
+ pedPos.y = myPos.y; // ignore diff in y
+
+ if( (pedPos-myPos).MagnitudeSqr() < TOO_CLOSE_TO_ANOTHER_PED_SQR )
+ {
+ PedestrianManager::GetInstance()->RemovePed( mpCharacter );
+ return;
+ }
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ }
+
+
+ /////////////////////////////////
+ // Otherwise, we're free to activate self
+
+ mIsActive = true;
+
+ // set my state
+ TransitToState(FOLLOWING_PATH);
+ mOffPath = false;
+ DetermineFollowPathSpeed();
+
+ mpCharacter->ResetCollisions();
+ mpCharacter->AddToWorldScene( );
+ mpCharacter->SetHasBeenHit(false);
+
+ mpCharacter->AddToPhysics();
+}
+
+void Pedestrian::InitZero()
+{
+ mPath = NULL;
+
+ mMillisecondsOutOfSight = 0;
+ mMarkedForActivation = false;
+ mIsActive = false;
+
+ TransitToState(STOPPED);
+ mOffPath = false;
+}
+
+void Pedestrian::Deactivate()
+{
+ InitZero();
+
+ // transit back to locomotion state
+ if( mpCharacter->GetStateManager()->GetState() != CharacterAi::LOCO )
+ {
+ mpCharacter->GetStateManager()->SetState<CharacterAi::Loco>();
+ }
+
+ // transit back to AI control...
+ sim::SimState* simState = mpCharacter->GetSimState();
+ if( simState != NULL )
+ {
+ simState->SetControl( sim::simAICtrl );
+ }
+
+ //mpCharacter->SetSolveCollisions( false );
+ mpCharacter->RemoveFromPhysics();
+ mpCharacter->RemoveFromWorldScene( );
+}
+
+void Pedestrian::OnReachedWaypoint()
+{
+ // do nothing
+}
+
+
+float Pedestrian::GetFollowPathSpeedMps() const
+{
+ // when offpath, don't just RUN BACK on the path
+ // this can make us thrash back and forth (Oh we're off path!
+ // Oh now we're off path on the other side! Oh we're off path! So on...)
+ if( mOffPath )
+ {
+ return PEDESTRIAN_PATHFIND_BACK_SPEED_MPS;
+ }
+ return mFollowPathSpeedMps;
+}
+
+void Pedestrian::GetBoundingSphere( rmt::Sphere& s )
+{
+ rmt::Sphere sphere;
+ mpCharacter->GetBoundingSphere( &sphere );
+
+ // make sure the bounding sphere has same position as character!!!
+ s = sphere;
+}
+
+
+void Pedestrian::DetermineFollowPathSpeed()
+{
+ int coinflip = rand() % 10;
+ if( 0 <= coinflip && coinflip < 4 )
+ {
+ mFollowPathSpeedMps = PEDESTRIAN_WALK_SPEED_MPS;
+ }
+ else if( 4 <= coinflip && coinflip < 8 )
+ {
+ mFollowPathSpeedMps = PEDESTRIAN_BRISK_WALK_SPEED_MPS;
+ }
+ else
+ {
+ mFollowPathSpeedMps = PEDESTRIAN_RUN_SPEED_MPS;
+ }
+}
+
diff --git a/game/code/worldsim/ped/pedestrian.h b/game/code/worldsim/ped/pedestrian.h
new file mode 100644
index 0000000..88e9d01
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrian.h
@@ -0,0 +1,128 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrian.h
+//
+// Description: Defines Pedestrian class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef PEDESTRIAN_H // 80 character mark
+#define PEDESTRIAN_H
+
+#include <worldsim/character/charactercontroller.h>
+#include <roads/geometry.h>
+
+// because we're only storing pointers to these
+// objects (all pointers are same size), we can
+// use forward declarations here.
+class Character;
+class Path;
+class PathSegment;
+
+
+class Pedestrian
+: public NPCController
+{
+// METHODS
+public:
+ Pedestrian();
+ virtual ~Pedestrian();
+
+ void GetBoundingSphere( rmt::Sphere& s );
+
+ /////////////////////////////////////////////////////////////////////////
+ // CharacterController
+ /////////////////////////////////////////////////////////////////////////
+ virtual void Update( float seconds );
+ virtual float GetValue( int buttonId ) const;
+ virtual bool IsButtonDown( int buttonId ) const;
+ /////////////////////////////////////////////////////////////////////////
+
+ // a magic all-in-one call to place ped in the world & make it active
+ // This only marks it for activation while waiting for CharaterManager
+ // to load our data... won't put in world till model is done loading...
+ void Activate(
+ Path* path,
+ PathSegment* pathSegment,
+ rmt::Vector spawnPos,
+ const char* modelName );
+
+ // This immediately removes model from the world and makes it inactive
+ void Deactivate();
+
+ // ACCESSORS
+ bool GetIsActive() const;
+ void SetIsActive( bool isActive );
+
+ Path* GetPath();
+ void SetPath( Path* path );
+
+ void InitZero();
+
+// MEMBERS
+public:
+ unsigned int mMillisecondsOutOfSight;
+
+protected:
+ float GetFollowPathSpeedMps() const;
+ void OnReachedWaypoint();
+
+// METHODS
+private:
+ void DetermineFollowPathSpeed();
+ void ActivateSelf();
+
+ //Prevent wasteful constructor creation.
+ Pedestrian( const Pedestrian& ped );
+ Pedestrian& operator=( const Pedestrian& ped );
+
+// MEMBERS
+private:
+
+ /////////////////////////////////////////////////////////////////////////
+ // CharacterController
+ /////////////////////////////////////////////////////////////////////////
+ rmt::Vector mNormalizedDir;
+ /////////////////////////////////////////////////////////////////////////
+
+ bool mIsActive;
+
+ Path* mPath; // path I'm on
+
+ float mFollowPathSpeedMps;
+ bool mMarkedForActivation;
+
+ enum {
+ MAX_STRING_LEN = 64
+ };
+
+ char mModelName [MAX_STRING_LEN+1];
+ rmt::Vector mSpawnPos;
+ float mSecondsTillActivateSelf;
+
+};
+
+
+// **************************** INLINES ******************************
+
+inline bool Pedestrian::GetIsActive() const
+{
+ return mIsActive;
+}
+inline void Pedestrian::SetIsActive( bool isActive )
+{
+ mIsActive = isActive;
+}
+inline Path* Pedestrian::GetPath()
+{
+ return mPath;
+}
+inline void Pedestrian::SetPath( Path* path )
+{
+ mPath = path;
+}
+
+#endif // PEDESTRIAN_H \ No newline at end of file
diff --git a/game/code/worldsim/ped/pedestrianmanager.cpp b/game/code/worldsim/ped/pedestrianmanager.cpp
new file mode 100644
index 0000000..f4e9011
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrianmanager.cpp
@@ -0,0 +1,1593 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrianmanager.cpp
+//
+// Description: Implements Pedestrian Manager class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+#define USEAVATARPOS
+
+#include <raddebug.hpp> // for rAssert & other debug print outs
+#include <stdlib.h>
+#include <string.h>
+
+#include <debug/profiler.h> // for the Profiler
+#include <radmath/radmath.hpp> // for rmt::Vector
+#include <memory/srrmemory.h>
+
+#include <meta/locatorevents.h>
+#include <meta/pedgrouplocator.h>
+
+#include <worldsim/ped/pedestrianmanager.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+
+/*
+#include <camera/supercammanager.h>
+#include <camera/supercam.h>
+*/
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/characterrenderable.h>
+
+#include <render/Culling/ReserveArray.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <roads/geometry.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <pedpaths/path.h>
+#include <pedpaths/pathsegment.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/vehiclecentral.h>
+
+// in meters, how much to translate spawn & remove radii forward
+// in the direction of the camera lookat
+
+//#define PED_TEST
+#ifdef PED_TEST
+ const float PedestrianManager::FADE_RADIUS = 5.0f; // in meters
+ const float PedestrianManager::CENTER_OFFSET = 5.0f; // in meters
+ const float PedestrianManager::ADD_RADIUS = 10.0f; // in meters
+ const float PedestrianManager::REMOVE_RADIUS = 15.0f; // in meters
+ const float PedestrianManager::INITIAL_ADD_RADIUS = 10.0f; // in meters
+#else
+ const float PedestrianManager::FADE_RADIUS = 60.0f; // in meters
+ const float PedestrianManager::CENTER_OFFSET = 30.0f; // in meters
+ const float PedestrianManager::ADD_RADIUS = 45.0f; // in meters
+ const float PedestrianManager::REMOVE_RADIUS = 50.0f; // in meters
+ const float PedestrianManager::INITIAL_ADD_RADIUS = 90.0f; // in meters
+#endif
+const unsigned int PedestrianManager::MILLISECONDS_PER_GROUND_INTERSECT = 10;
+const unsigned int PedestrianManager::MILLISECONDS_BETW_ADDS = 1;
+const unsigned int PedestrianManager::MILLISECONDS_BETW_REMOVES = 1000;
+const unsigned int PedestrianManager::MILLISECONDS_POPULATE_WORLD = 3000;
+
+int PedestrianManager::mDefaultModelGroup = 0;
+
+// *********************** STATICS ***********************
+
+PedestrianManager* PedestrianManager::mInstance = NULL;
+
+PedestrianManager* PedestrianManager::GetInstance()
+{
+ if( mInstance == NULL )
+ {
+ mInstance = new(GMA_LEVEL_OTHER) PedestrianManager();
+ }
+ return mInstance;
+}
+
+void PedestrianManager::DestroyInstance()
+{
+ if( mInstance != NULL )
+ {
+ mInstance->mFreePeds.Clear();
+ mInstance->mActivePeds.Clear();
+
+ // Clean up Pedestrians.
+ int i = 0;
+ for( i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ PedestrianStruct* pedStr = &(mInstance->mPeds[i]);
+ rAssert( pedStr != NULL );
+
+ Pedestrian* ped = pedStr->ped;
+ rAssert( ped != NULL );
+
+ if( ped->GetIsActive() )
+ {
+ ped->Deactivate();
+ }
+ }
+ // Delete self
+ delete mInstance;
+ }
+ mInstance = NULL;
+}
+
+
+// ******************** PUBLICS *************************
+
+// call this in loading context
+void PedestrianManager::InitDefaultModelGroups()
+{
+ // initialize all model groups with a default group
+ // This is why we put PedManager::Init in LoadingContext, after
+ // CharacterManager::PreLoad and GamePlayManager::LoadBlahBlah,
+ // cuz GamePlayManager will want to parse the level script which
+ // will register the modelgroups...
+ for( int i=0; i<PedestrianManager::MAX_MODEL_GROUPS; i++ )
+ {
+ mModelGroups[i].numModels = 4;
+ mModelGroups[i].models[0].Init( "male1", 5 );
+ mModelGroups[i].models[1].Init( "fem1", 5 );
+ mModelGroups[i].models[2].Init( "boy1", 5 );
+ mModelGroups[i].models[3].Init( "girl1", 5 );
+ for( int j=4; j<PedestrianManager::MAX_MODELS; j++ )
+ {
+ mModelGroups[i].models[j].InitZero();
+ }
+ }
+}
+
+void PedestrianManager::SetDefaultModelGroup( int toGroupID )
+{
+ mDefaultModelGroup = toGroupID;
+}
+
+void PedestrianManager::Init()
+{
+ mAllowAdd = true;
+ mMillisecondsTillRemove = PedestrianManager::MILLISECONDS_BETW_REMOVES;
+ mMillisecondsTillAdd = PedestrianManager::MILLISECONDS_BETW_ADDS;
+
+ mMillisecondsPopulateWorld = PedestrianManager::MILLISECONDS_POPULATE_WORLD;
+
+ mFreePeds.Clear();
+ mActivePeds.Clear();
+
+ for( int i=0; i<PedestrianManager::MAX_MODELS_IN_USE; i++ )
+ {
+ mModelsInUse[i].uid.SetText( NULL );
+ mModelsInUse[i].currCount = 0;
+ }
+ mNumActiveModels = 0;
+
+ // Recall that at this point, MissionManager has parsed the level script
+ // which may or may not have populated our model groups. Recall also that
+ // before that happened, we init all our model groups with a default group
+ // Therefore, we can safely switch our current model group to group 0
+ // (it should exist one way or another).
+
+ // switch to our group zero
+ UnregisterAllModels();
+
+ mCurrGroupID = -1;
+ SwitchModelGroup( mDefaultModelGroup ); // will set mCurrGroupID to 0
+
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ PedestrianStruct* pedStr = &(mPeds[i]);
+ rAssert( pedStr != NULL );
+
+ Pedestrian* ped = pedStr->ped;
+ rAssert( ped != NULL );
+
+ char name[5];
+ sprintf( name, "ped%d", i );
+ name[4] = '\0';
+
+ // call AddCharacter with an initial, lightweight dummy model so
+ // we can init all the sim objects, the animation drivers, etc.
+ Character* character = ::GetCharacterManager()->AddCharacter(
+ CharacterManager::NPC, name, "npd", "npd" );
+
+ character->SetRole(Character::ROLE_PEDESTRIAN);
+
+ rAssert( character != NULL );
+ character->AddRef();
+ character->SetController( pedStr->ped );
+ character->mbAllowUnload = false;
+ pedStr->ped->SetCharacter( character );
+
+ int nameLen = strlen("npd");
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( pedStr->modelName, "npd", PedestrianManager::MAX_STRING_LEN );
+ pedStr->modelName[nameLen] = '\0';
+
+ ped->InitZero();
+
+ // add ped to list of free peds
+ pedStr->listIndex = mFreePeds.AddLast( pedStr );
+ rAssert( pedStr->listIndex != -1 );
+
+ }
+}
+
+
+
+
+void PedestrianManager::Update( unsigned int milliseconds )
+{
+BEGIN_PROFILE( "Pedestrian Manager" );
+
+ //rTunePrintf( "<<<<<<<<<<<<<<<<<<<<<<< NUM ACTIVE PEDS %d >>>>>>>>>>>>>>>>>>>>>>\n", mActivePeds.GetNumElems() );
+
+ PedestrianStruct* pedStr = NULL;
+ int i = 0;
+
+ //
+ // ================================
+ // Get information about the player
+ // ================================
+ //
+ rmt::Vector pPos, pVel;//, pDir;
+ float pSpeed;
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+
+ // Avatar::GetPosition() will return position of vehicle if avatar inside
+ // vehicle. Same deal with Avatar::GetVelocity() & GetSpeedMps()
+ // Remember that we should use VELOCITY rather than facing because
+ // the player can face one way and travel in reverse.
+ //
+ avatar->GetPosition(pPos);
+ avatar->GetVelocity(pVel);
+ pSpeed = avatar->GetSpeedMps();
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+
+ superCam->GetPosition( &pPos );
+ superCam->GetVelocity( &pVel );
+ pSpeed = pVel.Magnitude(); // *** SQUARE ROOT! ***
+ */
+
+ int debugCount, debugExpCount;
+
+ // Get the camera for Player 1.
+ // It's what we need to apply the center offset to our spawn & remove radii
+ SuperCam* pCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+
+ rmt::Vector camTarget;
+ pCam->GetHeadingNormalized( &camTarget );
+ rAssert( rmt::Epsilon(camTarget.MagnitudeSqr(), 1.0f, 0.0005f) );
+
+ rmt::Vector center;
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ center = pPos;
+ }
+ else
+ {
+ center = pPos + camTarget * PedestrianManager::CENTER_OFFSET;
+ }
+
+
+
+ // ====================================
+ // Update PedestrianManager properties
+ // ======================================
+
+ if( mMillisecondsPopulateWorld > milliseconds )
+ {
+ mMillisecondsPopulateWorld -= milliseconds;
+ }
+ else
+ {
+ mMillisecondsPopulateWorld = 0;
+ }
+
+
+ static bool remove = true;
+ remove = !remove;
+
+ //
+ // ==================================================
+ // Remove active pedestrians that are outside radius
+ // ==================================================
+ //
+ if( remove && (mMillisecondsPopulateWorld == 0))
+ {
+ mMillisecondsTillRemove = PedestrianManager::MILLISECONDS_BETW_REMOVES;
+
+ debugCount = 0;
+ debugExpCount = mActivePeds.GetNumElems();
+
+ int j = mActivePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ debugCount++;
+
+ pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j );
+ rAssert( pedStr != NULL );
+
+ // Do radius test on character's bounding sphere
+ // versus pedestrian radius...
+ //
+ bool remove = false;
+
+ rmt::Vector pedPos;
+ pedStr->ped->GetCharacter()->GetPosition( pedPos );
+
+ float distSqr = (center - pedPos).MagnitudeSqr();
+ float zoneDistSqr = PedestrianManager::REMOVE_RADIUS * PedestrianManager::REMOVE_RADIUS;
+ if( distSqr >= zoneDistSqr )
+ {
+ remove = true;
+ }
+
+ // Do frustrum test too... if outside our frustrum, remove it within n seconds
+
+ const float MAX_OUT_OF_SIGHT_MILLISECONDS = 2000;
+
+ if( !pedStr->ped->GetCharacter()->mbInAnyonesFrustrum &&
+ pedStr->ped->mMillisecondsOutOfSight > MAX_OUT_OF_SIGHT_MILLISECONDS )
+ {
+ remove = true;
+ pedStr->ped->mMillisecondsOutOfSight = 0;
+ }
+
+ j = mActivePeds.GetNextOf( j );
+
+ if( remove )
+ {
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ DeactivatePed( pedStr );
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ break;
+ }
+ }
+// rAssert( debugCount == debugExpCount );
+ }
+ else
+ {
+ //
+ // ====================
+ // Add Pedestrians
+ // ====================
+ //
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ AddPeds( center, PedestrianManager::INITIAL_ADD_RADIUS );
+ }
+ else
+ {
+ AddPeds( center, PedestrianManager::ADD_RADIUS );
+ }
+ }
+
+
+ //
+ // =================================
+ // Update active pedestrians:
+ // - fade them as necessary
+ // =================================
+ //
+ i = mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(i);
+ rAssert( pedStr != NULL );
+
+ if( pedStr->ped->GetIsActive() )
+ {
+ ////////////////////////////////////////////////////
+ // Update out of sight stuff
+ if( !pedStr->ped->GetCharacter()->mbInAnyonesFrustrum )
+ {
+ pedStr->ped->mMillisecondsOutOfSight += milliseconds;
+ }
+ else
+ {
+ pedStr->ped->mMillisecondsOutOfSight = 0;
+ }
+
+
+ ////////////////////////////////////////////////////
+ // Set fade if getting near fringe...
+ //
+ rmt::Vector myPos;
+ pedStr->ped->GetCharacter()->GetPosition( myPos );
+
+ float dist2Player = (pPos - myPos).Length(); // *** SQUARE ROOT! ***
+
+ float fadeMinLimit = PedestrianManager::FADE_RADIUS;
+ float fadeMaxLimit = PedestrianManager::ADD_RADIUS + PedestrianManager::CENTER_OFFSET;
+ int fadeAlpha = 255;
+ if( fadeMinLimit <= dist2Player && dist2Player <= fadeMaxLimit )
+ {
+ // if we're in the fading zone, gotta change fade alpha
+ float fadeRatio = (dist2Player - fadeMinLimit)/(fadeMaxLimit - fadeMinLimit);
+ fadeAlpha = (int) (255.0f * (1.0f - fadeRatio));
+ }
+ else if( dist2Player > fadeMaxLimit )
+ {
+ fadeAlpha = 0;
+ }
+ pedStr->ped->GetCharacter()->SetFadeAlpha( fadeAlpha );
+ }
+ i = mActivePeds.GetNextOf( i );
+ }
+
+
+
+END_PROFILE( "Pedestrian Manager" );
+
+}
+
+void PedestrianManager::RemovePed( Character* character )
+{
+ if( character == NULL )
+ {
+ return;
+ }
+ int j = mActivePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j );
+ rAssert( pedStr != NULL );
+
+ if( pedStr->ped->GetCharacter() == character )
+ {
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ DeactivatePed( pedStr );
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ return;
+ }
+ j = mActivePeds.GetNextOf( j );
+ }
+}
+
+void PedestrianManager::RemoveAllPeds()
+{
+ int j = mActivePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j );
+ rAssert( pedStr != NULL );
+
+ j = mActivePeds.GetNextOf( j );
+
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ DeactivatePed( pedStr );
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ }
+}
+
+void PedestrianManager::AllowAddingPeds( bool allowed )
+{
+ mAllowAdd = allowed;
+}
+
+
+// ************************ PRIVATES *************************
+
+PedestrianManager::PedestrianManager() :
+ mMillisecondsTillRemove( 0 ),
+ mMillisecondsTillAdd( 0 ),
+ mMillisecondsPopulateWorld( 0 ),
+ mNumRegisteredModels( 0 ),
+ mCurrGroupID( -1 ),
+ mNumActiveModels( 0 ),
+ mAllowAdd( true )
+{
+ // check some stuff once per compile..
+ rAssert( PedestrianManager::ADD_RADIUS > 0 );
+ rAssert( PedestrianManager::REMOVE_RADIUS > 0 );
+ rAssert( PedestrianManager::MAX_STRING_LEN >= 6 );
+ rAssert( 1 <= PedestrianManager::MAX_MODELS );
+ rAssert( 0 <= PedestrianManager::MAX_PEDESTRIANS &&
+ PedestrianManager::MAX_PEDESTRIANS <= DListArray::MAX_ELEMS );
+
+ // listen to appropriate events
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::LOAD_PED_MODEL_GROUP ) );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC );
+ GetEventManager()->AddListener( this, EVENT_OBJECT_KICKED );
+ GetEventManager()->AddListener( this, EVENT_PLAYER_VEHICLE_HORN );
+
+ /*** DEBUG ***
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_GETOUTOFVEHICLE_END) );
+ *** DEBUG ***/
+
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ mPeds[i].ped = new (GMA_LEVEL_OTHER) Pedestrian();
+ mPeds[i].ped->AddRef();
+ mPeds[i].listIndex = -1;
+ }
+}
+
+PedestrianManager::~PedestrianManager()
+{
+ GetEventManager()->RemoveAll( this );
+
+ int i;
+ for( i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ if( mPeds[i].ped != NULL )
+ {
+ Character* character = mPeds[i].ped->GetCharacter();
+ if( character != NULL )
+ {
+ // Just release the pointer; don't bother calling
+ // RemoveCharacter(). The character gets cleaned up
+ // later by CharacterManager's destructor
+ character->Release();
+ }
+ mPeds[i].ped->Release();
+ }
+ }
+}
+
+void PedestrianManager::DeactivatePed( PedestrianStruct* pedStr )
+{
+ rAssert( pedStr != NULL );
+ rAssert( pedStr->ped != NULL );
+
+ rAssert( 1 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE );
+
+ //rDebugPrintf( "*** Removing %s\n", pedStr->modelName );
+
+ // remove ped from path
+ Path* path = pedStr->ped->GetPath();
+ rAssert( path != NULL );
+ bool res = path->RemovePedestrian();
+ rAssert( res );
+
+ // reset ped parameters
+ pedStr->ped->Deactivate();
+
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+ // If we can find the model in list of currently registered models,
+ // decrement count. The reason we need to search is that the model
+ // can be unregistered and re-registered in a different location.
+ // If we can't find it anywhere, then it must have been unregistered.
+ int modelID = GetModelIDByName( pedStr->modelName );
+ if( modelID != -1 )
+ {
+ mModels[modelID].currCount--;
+ }
+ else
+ {
+ rDebugPrintf( " Not found in model group, so I'll swap in npd!\n" );
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ // we're swapping out the model for npd... better update the modelsinuse list
+ FindModelInUseAndRemove( pedStr->modelName );
+
+ // since this model has been unregistered, it's not in the current
+ // model group, so keeping it around will only waste memory...
+ // we need to swap in "npd" in its place.
+ Character* character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, "npd", "npd" );
+ sprintf( pedStr->modelName, "npd" );
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ }
+
+ // manage the ped manager's active/free lists
+ res = mActivePeds.Remove( pedStr->listIndex );
+ rAssert( res );
+ pedStr->listIndex = mFreePeds.AddLast( pedStr );
+ rAssert( pedStr->listIndex > -1 );
+
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+
+}
+
+float PedestrianManager::GetRandomMinDistSqr()
+{
+ static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_A = 16.0f;
+ static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_B = 49.0f;
+ static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_C = 144.0f;
+
+ int coinflip = rand() % 3;
+ float mindistsqr = 0.0f;
+ switch( coinflip )
+ {
+ case 0:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_A;
+ }
+ break;
+ case 1:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_B;
+ }
+ break;
+ case 2:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_C;
+ }
+ break;
+ default:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_A;
+ }
+ break;
+ }
+
+ return mindistsqr;
+}
+
+bool PedestrianManager::ActivatePed( PathSegment* segment, rmt::Vector spawnPos )
+{
+ rAssert( segment != NULL );
+
+ PedestrianStruct* pedStr = NULL;
+ Pedestrian* ped = NULL;
+
+ // get the path for this pedestrian & try to add ped to it &
+ // store index
+ Path* path = segment->GetParentPath();
+ rAssert( path != NULL );
+
+ // Check to make sure it isn't already at max ped limit
+ if( path->IsFull() )
+ {
+ return false;
+ }
+
+ /*
+ // Make sure we're not too close to another ped...
+ float minDistSqr = GetRandomMinDistSqr();
+ rmt::Vector pedPos;
+ int i = mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(i);
+ rAssert( pedStr != NULL );
+
+ pedStr->ped->GetCharacter()->GetPosition( &pedPos );
+ pedPos.y = spawnPos.y; // ignore diff in y
+
+ if( (pedPos-spawnPos).MagnitudeSqr() < minDistSqr )
+ {
+ return false;
+ }
+ i = mActivePeds.GetNextOf( i );
+ }
+ */
+
+ // make sure we're not spawning too close to another character
+ // or vehicle
+ float minDistSqr = GetRandomMinDistSqr();
+ float distSqr = 100000.0f;
+ rmt::Vector hisPos;
+
+ CharacterManager* cm = GetCharacterManager();
+ int maxChars = cm->GetMaxCharacters();
+ for( int i=0; i<maxChars; i++ )
+ {
+ Character* c = cm->GetCharacter(i);
+ if( c )
+ {
+ c->GetPosition( hisPos );
+ hisPos.y = spawnPos.y; // ignore diff in y
+
+ distSqr = (hisPos - spawnPos).MagnitudeSqr();
+ if( distSqr < minDistSqr )
+ {
+ return false;
+ }
+ }
+ }
+
+ VehicleCentral* vc = GetVehicleCentral();
+ for( int i=0; i<VehicleCentral::MAX_ACTIVE_VEHICLES; i++ )
+ {
+ Vehicle* v = vc->GetVehicle(i);
+ if( v )
+ {
+ v->GetPosition( &hisPos );
+ hisPos.y = spawnPos.y; // ignore diff in y
+
+ rmt::Sphere s;
+ v->GetBoundingSphere( &s );
+
+ distSqr = (hisPos - spawnPos).MagnitudeSqr();
+
+ minDistSqr = s.radius + 1.0f;
+ minDistSqr *= minDistSqr;
+
+ if( distSqr < minDistSqr )
+ {
+ return false;
+ }
+ }
+ }
+
+
+
+ // Choose a model for our new pedestrian
+ int modelID = GetRandomModel();
+ if( modelID <= -1 )
+ {
+ return false;
+ }
+
+ //*** Test ***
+ //rDebugPrintf( "Model randomly chosen: %d, %s\n", modelID, mModels[modelID].name );
+ //*** Test ***
+
+
+ // if reached maxcount for this model, go to next model and use it instead
+ bool foundUsable = false;
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( !mModels[modelID].free &&
+ mModels[modelID].currCount < mModels[modelID].maxCount )
+ {
+ foundUsable = true;
+ break;
+ }
+ // go to next model
+ modelID = (modelID+1) % PedestrianManager::MAX_MODELS;
+ }
+ // if we never found a model with currCount < maxCount, then
+ // we can't actually do any spawning...
+ if( !foundUsable )
+ {
+ rTunePrintf( "Can't spawn a new ped. Not enough Ped model "
+ "instances allowed in leveli.mfk. See Dusit or Sheik.\n" );
+ return false;
+ }
+
+ PedestrianStruct* freeNonNPDNotInCurrModelGroup = NULL;
+ PedestrianStruct* pedStrFoundInFreeList = NULL;
+
+ // See if this model is already available in our freepeds list...
+ // If so, we don't need to call SwapData (save some processing time)
+ bool foundInFreeList = false;
+ int j = mFreePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ pedStr = (PedestrianStruct*) mFreePeds.GetDataAt(j);
+ rAssert( pedStr != NULL );
+ if( !foundInFreeList && strcmp(pedStr->modelName, mModels[modelID].name)==0 )
+ {
+ foundInFreeList = true;
+ pedStrFoundInFreeList = pedStr;
+ //rDebugPrintf( "*** Yes! We found an existing model %s in the freelist!\n", pedStr->modelName );
+ //break;
+ }
+ else
+ {
+ if( strcmp(pedStr->modelName, "npd") != 0 &&
+ GetModelIDByName( pedStr->modelName ) == -1 )
+ {
+ freeNonNPDNotInCurrModelGroup = pedStr;
+ }
+ }
+ j = mFreePeds.GetNextOf( j );
+ }
+
+ // if none of the freepeds has the model we want, we need
+ // to swap out one of them and put in the model we want.
+ if( !foundInFreeList )
+ {
+ //
+ // Since we never found the model we want in the list of free peds,
+ // we should try to find a "free" model (npd, or a model not
+ // currently in our model group, so we don't clobber another real
+ // model that might be wanted later)..
+ //
+ // We could achieve this by searching for models not in current group in free list...
+ // However, if we never found one, then we have to just pick one, preferrably an "npd"
+ // Since we always add the most recently removed ped to the END of the freepeds
+ // list, we should take from the FRONT of the freepeds list... sort of a mini-scheduling
+ // algorithm, ya know...
+ //
+ if( freeNonNPDNotInCurrModelGroup )
+ {
+ pedStr = freeNonNPDNotInCurrModelGroup;
+ }
+ else
+ {
+ pedStr = (PedestrianStruct*) mFreePeds.GetFirst();
+ }
+ rAssert( pedStr != NULL );
+ bool res = SwapModels( modelID, pedStr );
+ if( !res )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ pedStr = pedStrFoundInFreeList;
+ }
+
+ //////////////////////////////////////////////////////////
+ // NOTE:
+ // At this point we're ready to do the actual activating (no more bailing out)
+
+ //*** Test ***
+ //rDebugPrintf( "*** Adding %s\n", mModels[modelID].name );
+ //*** Test ***
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+ mModels[modelID].currCount++;
+
+ rAssert( strcmp( pedStr->modelName, mModels[modelID].name ) == 0 );
+
+ rAssert( pedStr->ped != NULL );
+
+ // choose a random swatch
+ int swatch = rand() % CharacterRenderable::NUM_CHARACTER_SWATCHES;
+ pedStr->ped->GetCharacter()->SetSwatch( swatch );
+
+ // add ped to path
+ bool res = path->AddPedestrian( );
+ rAssert( res );
+
+ /*
+ // get the segment's index (to the parent path's list of segments)
+ // this will be needed by Pedestrian::Update() when locomoting.
+ int pathSegmentIndex = segment->GetIndexToParentPath();
+ rAssert( 0 <= pathSegmentIndex && pathSegmentIndex < path->GetNumPathSegments() );
+ */
+
+ // set the ped's parameters
+ pedStr->ped->Activate( path, segment, spawnPos, pedStr->modelName );
+
+ // manage the ped manager's active/free list
+ res = mFreePeds.Remove( pedStr->listIndex );
+ rAssert( res );
+
+ pedStr->listIndex = mActivePeds.AddLast( pedStr );
+ rAssert( pedStr->listIndex != -1 );
+
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+ return true;
+}
+
+bool PedestrianManager::SwapModels( int modelID, PedestrianStruct* pedStr )
+{
+ rAssert( pedStr != NULL );
+ rTuneAssert( 0 <= modelID && modelID < PedestrianManager::MAX_MODELS );
+
+ Character* character = NULL;
+
+#ifdef RAD_DEBUG
+ SanityCheck();
+#endif
+ FindModelInUseAndRemove( pedStr->modelName );
+ bool res = FindModelInUseAndAdd( mModels[modelID].name );
+ if( res == false )
+ {
+ // we already removed pedStr->modelName from list, but haven't really
+ // swapped him out...
+ // If he's in the list of currently registered models, we don't want
+ // to really swap him out anyway (so call FindModelInUseAndAdd(pedStr->modelName))
+ // But if he's not in the list of currently registered models, we DO
+ // want to swap him out.. so call SwapData passing in "npd"
+ if( strcmp( pedStr->modelName, "npd" ) != 0 )
+ {
+ int index = GetModelIDByName( pedStr->modelName );
+ if( index == -1 )
+ {
+ character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, "npd", "npd" );
+ sprintf( pedStr->modelName, "npd" );
+ }
+ else
+ {
+ res = FindModelInUseAndAdd( pedStr->modelName );
+ rAssert( res );
+ }
+ }
+ return false;
+ }
+
+ character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, mModels[modelID].name, "npd" );
+
+ int nameLen = strlen( mModels[modelID].name );
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( pedStr->modelName, mModels[modelID].name, PedestrianManager::MAX_STRING_LEN );
+ pedStr->modelName[nameLen] = '\0';
+
+#ifdef RAD_DEBUG
+ SanityCheck();
+#endif
+
+
+ return true;
+}
+
+int PedestrianManager::GetRandomModel()
+{
+ if( mNumRegisteredModels < 1 )
+ {
+ rTunePrintf( "PedestrianManager: You haven't registered a pedestrian model!\n" );
+ return -1;
+ }
+
+ // generate a number between 1 and mNumRegisteredModels
+ int num = (rand() % mNumRegisteredModels) + 1;
+ rAssert( 1 <= num && num <= mNumRegisteredModels );
+
+ // loop through models array counting till we get to num & return this modelID
+ int count = 0;
+ for(int i=0; i<PedestrianManager::MAX_MODELS; i++)
+ {
+ if( !mModels[i].free )
+ {
+ count++;
+ }
+ if( count==num )
+ {
+ return i;
+ }
+ }
+ rAssert( false ); // shouldn't get here
+ return -1;
+}
+
+bool PedestrianManager::RegisterModel( const char* name, int maxCount )
+{
+ rTuneAssert( name != NULL );
+ rTuneAssert( maxCount > 0 );
+
+ // search existing SPARSE list for name
+ int freeIndex = -1;
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( mModels[i].free )
+ {
+ freeIndex = i;
+ continue;
+ }
+
+ // found an existing model registered under same name, so overwrite w/ new values
+ if( strcmp(mModels[i].name, name)==0 )
+ {
+ mModels[i].maxCount = maxCount;
+ // currCount should be the same as there are active instances
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ return true;
+ }
+ }
+
+ // If we haven't found an existing model, then we're adding one.
+ // See if we can...
+ if( mNumRegisteredModels >= PedestrianManager::MAX_MODELS )
+ {
+ return false;
+ }
+
+ if( 0 <= freeIndex && freeIndex < PedestrianManager::MAX_MODELS )
+ {
+ // loop through activelist looking for this model and counting the
+ // number of instances... init it with this value
+ int count = 0;
+ int n = mActivePeds.GetHead();
+ while( 0 <= n && n < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(n);
+ rAssert( pedStr );
+
+ if( strcmp( pedStr->modelName, name )==0 )
+ {
+ count++;
+ }
+
+ n = mActivePeds.GetNextOf( n );
+ }
+
+ mModels[freeIndex].Init( false, name, maxCount, count );
+ mNumRegisteredModels++;
+ return true;
+ }
+
+ return false;
+}
+
+bool PedestrianManager::UnregisterModel( const char* name )
+{
+ rAssert( name != NULL );
+
+ if( mNumRegisteredModels <= 0 )
+ {
+ return false;
+ }
+
+ // search existing SPARSE list for name
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( mModels[i].free )
+ {
+ continue;
+ }
+ int nameLen = strlen( name );
+ int modelNameLen = strlen( mModels[i].name );
+ if( (nameLen == modelNameLen) &&
+ (strncmp(mModels[i].name, name, nameLen)==0) )
+ {
+ mNumRegisteredModels--;
+ mModels[i].InitZero();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PedestrianManager::UnregisterModel( int modelID )
+{
+ rAssert( 0 <= modelID && modelID < PedestrianManager::MAX_MODELS );
+
+ if( mNumRegisteredModels <= 0 )
+ {
+ return false;
+ }
+ if( mModels[modelID].free )
+ {
+ return false;
+ }
+ mNumRegisteredModels--;
+ mModels[modelID].InitZero();
+ return true;
+}
+
+void PedestrianManager::UnregisterAllModels()
+{
+ mNumRegisteredModels = 0;
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ mModels[i].InitZero();
+ }
+}
+
+void PedestrianManager::SwitchModelGroup( int toGroupID )
+{
+ rAssert( 0 <= toGroupID && toGroupID < PedestrianManager::MAX_MODEL_GROUPS );
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+ // if already in this group, forget it
+ if( mCurrGroupID == toGroupID )
+ {
+ return;
+ }
+
+ // NOTE:
+ // Don't want to UnregisterAllModels here because if say male1 exists in new group as
+ // well as in old group, we'll be wiping its currcount. So just go around resetting
+ // maxcount to zero and let RegisterModel overwrite the ones that are pertinent.
+ // In the end, unregister everything that doesn't have maxcount > 0
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ mModels[i].maxCount = 0;
+ }
+ for( int i=0; i<mModelGroups[toGroupID].numModels; i++ )
+ {
+ RegisterModel(
+ mModelGroups[toGroupID].models[i].name,
+ mModelGroups[toGroupID].models[i].maxCount );
+ }
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( mModels[i].maxCount <= 0 )
+ {
+ UnregisterModel( i );
+ }
+ }
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+ mCurrGroupID = toGroupID;
+
+#ifdef RAD_DEBUG
+ char msg[256];
+ sprintf( msg, "*** SWITCHING TO NEW MODEL GROUP: " );
+ for( int i=0; i<mModelGroups[toGroupID].numModels; i++ )
+ {
+ sprintf( msg, "%s%s ", msg, mModelGroups[toGroupID].models[i].name );
+ }
+ sprintf( msg, "%s\n", msg );
+ //rDebugPrintf( msg );
+#endif
+
+
+}
+
+int PedestrianManager::GetModelIDByName( const char* name )
+{
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( strcmp( mModels[i].name, name )==0 )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void PedestrianManager::SetModelGroup( int groupID, const ModelGroup& group )
+{
+ rAssert( 0 <= groupID && groupID < PedestrianManager::MAX_MODEL_GROUPS );
+ rAssert( 0 <= group.numModels && group.numModels <= PedestrianManager::MAX_MODELS );
+
+ mModelGroups[groupID].numModels = group.numModels;
+
+ for( int i=0; i<group.numModels; i++ )
+ {
+ mModelGroups[groupID].models[i].Init(
+ group.models[i].name, group.models[i].maxCount );
+ }
+
+ for( int i=group.numModels ; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ mModelGroups[groupID].models[i].InitZero();
+ }
+}
+
+//=============================================================================
+// PedestrianManager::IsPed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* character )
+//
+// Return: bool
+//
+//=============================================================================
+bool PedestrianManager::IsPed( Character* character )
+{
+ int i;
+ for (i = 0; i < MAX_PEDESTRIANS; ++i )
+ {
+ if ( mPeds[ i ].ped->GetCharacter() == character )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void PedestrianManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ /*** DEBUG ***
+ switch( id )
+ {
+ case EVENT_GETOUTOFVEHICLE_END:
+ {
+ static int count = 0;
+ SwitchModelGroup( count % 2 );
+ count++;
+ }
+ break;
+ }
+ *** DEBUG ***/
+
+ // Handle the events appropriately:
+ // - determine which model group we want to switch to and call SwitchToGroup on it
+ switch ( id )
+ {
+ case EVENT_LOCATOR + LocatorEvent::LOAD_PED_MODEL_GROUP:
+ {
+ PedGroupLocator* pLocator = (PedGroupLocator*)pEventData;
+ rAssert( pLocator != NULL );
+
+ unsigned int groupID = pLocator->GetGroupNum();
+ if( pLocator->GetPlayerEntered() )
+ {
+ SwitchModelGroup( (int)groupID );
+ }
+ break;
+ }
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ // spawn a pedestrian in th driver's seat when a traffic vehicle is destroyed
+ Vehicle* vehicle = (Vehicle*)pEventData;
+ if( (GetGameplayManager()->GetCurrentLevelIndex() != RenderEnums::L7) &&
+ (strcmp(vehicle->GetDriverName(), "phantom") == 0) &&
+ mActivePeds.GetNumElems() > 0 )
+ {
+ int randPedNum = rand() % mActivePeds.GetNumElems();
+
+ Character* character = NULL;
+
+ int count = 0;
+ int ind = mActivePeds.GetHead();
+ while( 0 <= ind && ind <= MAX_PEDESTRIANS )
+ {
+ if( count == randPedNum )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( ind );
+ character = pedStr->ped->GetCharacter();
+ break;
+ }
+ count++;
+ ind = mActivePeds.GetNextOf( ind );
+ }
+
+ //Character* character = mPeds[rand() % MAX_PEDESTRIANS].ped->GetCharacter();
+
+ if(character)
+ {
+ vehicle->SetDriver(character);
+
+ character->SetFadeAlpha( 255 ); // make sure we're not faded.
+ character->SetTargetVehicle( vehicle );
+ character->SetInCar( true );
+ character->UpdateTransformToInCar();
+ character->GetStateManager()->SetState<CharacterAi::InCar>();
+ }
+ }
+ }
+ break;
+ case EVENT_PLAYER_VEHICLE_HORN: // fall thru
+ case EVENT_PLAYER_CAR_HIT_NPC: // fall thru
+ case EVENT_OBJECT_KICKED:
+ {
+ static const float TRIGGER_PANIC_RADIUS_SQR = 144.0f;
+
+ // Get player data
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rmt::Vector playerPos;
+ player->GetPosition( playerPos );
+
+ // for any ped nearby, make him run away...
+ int i = mActivePeds.GetHead();
+ while( 0 <= i && i <= DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( i );
+ rAssert( pedStr );
+
+ if( pedStr->ped->GetIsActive() )
+ {
+ rmt::Vector pedPos;
+ pedStr->ped->GetCharacter()->GetPosition( pedPos );
+ float distSqr = (playerPos - pedPos).MagnitudeSqr();
+
+ if( distSqr < TRIGGER_PANIC_RADIUS_SQR )
+ {
+ pedStr->ped->IncitePanic();
+ }
+ }
+ i = mActivePeds.GetNextOf( i );
+ }
+ }
+ break;
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+
+void PedestrianManager::AddPeds( rmt::Vector center, float addRadius )
+{
+ if( CommandLineOptions::Get( CLO_NO_PEDS ) )
+ {
+ return;
+ }
+
+ if( !mAllowAdd )
+ {
+ return;
+ }
+
+ // if no more free pedestrians...
+ if( mFreePeds.GetNumElems() <= 0 )
+ {
+ return;
+ }
+
+ // Invoke search on DSG for path segments that intersect with
+ // our radius
+ ReserveArray<PathSegment*> orList;
+ ::GetIntersectManager()->FindPathSegmentElems( center, addRadius, orList );
+
+ //
+ // For each segment we "intersect" with (in the loose sense), we
+ // a) break if we have no more pedestrians to spawn
+ // b) break if segment (or path?) already has a pedestrian
+ // c) put pedestrian on the segment at the intersection point.
+ // d) set pedestrian's facing/velocity/position, whatever's necessary
+ //
+ if(orList.mUseSize == 0)
+ return;
+
+ int which = rand() % orList.mUseSize;
+
+ PathSegment* segment = NULL;
+
+ segment = orList.mpData[which];
+ rAssert( segment != NULL );
+
+ // determine intersection point of the path segment & ped radius
+ // at normalized value "t"
+
+ rmt::Sphere s;
+ s.Set( center, addRadius );
+
+ rmt::Vector start, end;
+ segment->GetStartPos( start );
+ segment->GetEndPos( end );
+
+ rmt::Vector intPts[2];
+ int numIntersections = IntersectLineSphere( start, end, s, intPts );
+
+ // if no intersection, a mild surprise.
+ if( numIntersections <= 0 )
+ {
+ return;
+ }
+
+ // Just use the first intersection point
+ rmt::Vector spawnPos = intPts[0];
+ float t = GetLineSegmentT( start, end, spawnPos );
+
+ // activate the pedestrian
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+ bool succeeded = ActivatePed( segment, spawnPos );
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+}
+
+void PedestrianManager::FindModelInUseAndRemove( const char* name )
+{
+ if( tName::MakeUID(name) == tName::MakeUID("npd") )
+ {
+ return;
+ }
+ // should be able to find it in the list of models being used...
+ tUID uid = tEntity::MakeUID( name );
+ bool foundUid = false;
+
+ rAssert( 0 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE );
+ for( int i=0; i<mNumActiveModels; i++ )
+ {
+ if( uid == mModelsInUse[i].uid.GetUID() )
+ {
+ // count for this ped should be > 0 if it was found in this list
+ rAssert( mModelsInUse[i].currCount > 0 );
+ mModelsInUse[i].currCount--;
+
+ // if currCount is zero, remove it from list of models currently in use.
+ if( mModelsInUse[i].currCount == 0 )
+ {
+ // swap last entry into this position
+ //rDebugPrintf( "*** Removing from ModelsInUse %s\n", name );
+ mModelsInUse[i].currCount = mModelsInUse[mNumActiveModels-1].currCount;
+ mModelsInUse[i].uid = mModelsInUse[mNumActiveModels-1].uid;
+ mNumActiveModels--;
+ }
+ foundUid = true;
+ break; // NOTE: MUST break here since we may have modified mNumActiveModels
+ }
+ }
+ if( !foundUid )
+ {
+ // should have found it! Something wrong with our code...
+ rAssert( false );
+ }
+}
+
+bool PedestrianManager::FindModelInUseAndAdd( const char* name )
+{
+ if( strcmp( name, "npd" ) == 0 )
+ {
+ return true;
+ }
+
+ // PS2 memory can only support a few (4) models in use at all times...
+ // see if we have room for this model
+ bool founduid = false;
+ tUID uid = tEntity::MakeUID( name );
+
+ rAssert( 0 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE );
+ for( int i=0; i<mNumActiveModels; i++ )
+ {
+ if( uid == mModelsInUse[i].uid.GetUID() )
+ {
+ mModelsInUse[i].currCount++;
+ founduid = true;
+ break;
+ }
+ }
+ if( !founduid )
+ {
+ // couldn't find it in list of models currently in use, so try to add it
+ // to the list. If we can't, we don't spawn this model
+ if( mNumActiveModels == PedestrianManager::MAX_MODELS_IN_USE )
+ {
+ return false;
+ }
+ //rDebugPrintf( "*** Adding to ModelsInUse %s\n", name );
+ mModelsInUse[mNumActiveModels].uid.SetText( name );
+ mModelsInUse[mNumActiveModels].currCount = 1;
+ mNumActiveModels++;
+ }
+ return true;
+}
+
+void PedestrianManager::SanityCheck()
+{
+#ifdef RAD_DEBUG
+ // make sure every non-"npd" model in mPeds exists in ModelsInUse
+ char msg[256];
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ if( strcmp( mPeds[i].modelName, "npd" ) == 0 )
+ {
+ continue;
+ }
+
+ bool found = false;
+ for( int j=0; j<mNumActiveModels; j++ )
+ {
+ tUID test = tEntity::MakeUID( mPeds[i].modelName );
+ if( test == mModelsInUse[j].uid.GetUID() )
+ {
+ sprintf( msg, "%s already came up once in mModelsInUse list\n",
+ mPeds[i].modelName );
+ rAssertMsg( !found, msg);
+
+ sprintf( msg, "UIDs equal but names are not! %s vs %s\n",
+ mPeds[i].modelName, mModelsInUse[j].uid.GetText() );
+ rAssertMsg( strcmp( mPeds[i].modelName, mModelsInUse[j].uid.GetText() ) == 0, msg );
+ found = true;
+ }
+ }
+ sprintf( msg, "%s in mPeds could not be found in mModelsInUse\n",
+ mPeds[i].modelName );
+ rAssertMsg( found, msg );
+ }
+
+ // make sure every model in ModelsInUse is non-"npd" and exists in mPeds, with correct count
+ for( int i=0; i<mNumActiveModels; i++ )
+ {
+ rAssertMsg( strcmp( mModelsInUse[i].uid.GetText(), "npd" ) != 0,
+ "Found npd in mModelsInUse\n" );
+
+ bool found = false;
+ int count = 0;
+ for( int j=0; j<PedestrianManager::MAX_PEDESTRIANS; j++ )
+ {
+ tUID test = tEntity::MakeUID( mPeds[j].modelName );
+ if( test == mModelsInUse[i].uid.GetUID() )
+ {
+ count++;
+ sprintf( msg, "UIDs equal but names are not! %s vs %s\n",
+ mPeds[j].modelName, mModelsInUse[i].uid.GetText() );
+ rAssertMsg( strcmp( mPeds[j].modelName, mModelsInUse[i].uid.GetText() ) == 0, msg );
+ found = true;
+ }
+ }
+ sprintf( msg, "%s in mModelsInUse could not be found in mPeds\n",
+ mModelsInUse[i].uid.GetText() );
+ rAssertMsg( found, msg );
+
+ sprintf( msg, "Not equals numbers of %s found in mPeds (%d) and mModelsInUse (%d)\n",
+ mModelsInUse[i].uid.GetText(), count, mModelsInUse[i].currCount );
+ rAssertMsg( count == mModelsInUse[i].currCount, msg );
+ }
+#endif
+}
+
+
+void PedestrianManager::CheckModelCounts()
+{
+ // *** DEBUG ***
+ // do some checking here to make sure mModels' currcount is
+ // correct
+ for( int m=0; m<PedestrianManager::MAX_MODELS; m++ )
+ {
+ if( mModels[m].free )
+ {
+ continue;
+ }
+ int expectedCount = mModels[m].currCount;
+
+ int count = 0;
+ int n = mActivePeds.GetHead();
+ while( 0 <= n && n < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(n);
+ rAssert( pedStr );
+
+ if( strcmp( pedStr->modelName, mModels[m].name )==0 )
+ {
+ count++;
+ }
+
+ n = mActivePeds.GetNextOf( n );
+ }
+ rAssert( count == expectedCount );
+ }
+ // *** DEBUG ***
+}
+
+void PedestrianManager::DumpAllPedModels()
+{
+ // deactivate all current peds
+ RemoveAllPeds();
+
+ // Swap "npd" in for the other models
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ PedestrianStruct* pedStr = &(mPeds[i]);
+ rAssert( pedStr );
+
+ if( tName::MakeUID( pedStr->modelName ) == tName::MakeUID( "npd" ) )
+ {
+ continue;
+ }
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ // we're swapping out the model for npd... better update the modelsinuse list
+ FindModelInUseAndRemove( pedStr->modelName );
+
+ // since this model has been unregistered, it's not in the current
+ // model group, so keeping it around will only waste memory...
+ // we need to swap in "npd" in its place.
+ Character* character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, "npd", "npd" );
+ sprintf( pedStr->modelName, "npd" );
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ }
+
+} \ No newline at end of file
diff --git a/game/code/worldsim/ped/pedestrianmanager.h b/game/code/worldsim/ped/pedestrianmanager.h
new file mode 100644
index 0000000..41829e5
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrianmanager.h
@@ -0,0 +1,241 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrianmanager.h
+//
+// Description: Defines Pedestrian Manager class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+
+#ifndef PEDESTRIANMANAGER_H
+#define PEDESTRIANMANAGER_H
+
+
+#include <worldsim/ped/pedestrian.h>
+#include <roads/geometry.h>
+#include <events/eventlistener.h>
+#include <p3d/entity.hpp>
+#include <string.h>
+
+class PathSegment;
+
+// Hey look! I'm a singleton!
+class PedestrianManager :
+public EventListener
+{
+
+// MEMBERS
+public:
+ // STATICS
+ static const int MAX_PEDESTRIANS = 7; // should be <= DListArray::MAX_ELEMS;
+ static const int MAX_MODELS_IN_USE = 4; // number of distinct models we can current support at a time
+ static const int MAX_MODELS = 10; // num distinct models for 2 groups (while one takes over the other)
+ static const int MAX_STRING_LEN = 64;
+ static const int MAX_MODEL_GROUPS = 10; // number of different groups we allow to be defined
+
+ static PedestrianManager* mInstance;
+ static const float FADE_RADIUS;
+ static const float CENTER_OFFSET;
+ static const float ADD_RADIUS;
+ static const float INITIAL_ADD_RADIUS;
+ static const float REMOVE_RADIUS;
+ static const unsigned int MILLISECONDS_PER_GROUND_INTERSECT;
+ static const unsigned int MILLISECONDS_BETW_ADDS;
+ static const unsigned int MILLISECONDS_BETW_REMOVES;
+ static const unsigned int MILLISECONDS_POPULATE_WORLD;
+
+ // NON-STATICS
+ struct ModelData
+ {
+ ModelData() : maxCount( 0 ) {};
+ char name[MAX_STRING_LEN+1];
+ int maxCount;
+ void Init( const char* modelName, int max )
+ {
+ rAssert( modelName != NULL );
+ rAssert( max > 0 );
+
+ int nameLen = strlen( modelName );
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( name, modelName, PedestrianManager::MAX_STRING_LEN );
+ name[ nameLen ] = '\0';
+
+ maxCount = max;
+ }
+ void InitZero()
+ {
+ name[0] = '\0';
+ maxCount = 0;
+ }
+
+ };
+ struct ModelGroup
+ {
+ ModelGroup() : numModels( 0 ) {};
+ int numModels;
+ ModelData models[MAX_MODELS];
+ };
+
+ // Need lists to keep track of inactive peds & active peds
+ //
+ // Whenever we set mPeds[i] to active, remove it from free list
+ // and add it to active list:
+ //
+ // mFreePeds.Remove(mPeds[i].listIndex);
+ // mPeds[i].listIndex = mActivePeds.AddLast( &mPeds[i] );
+ //
+ // Whenever we set mPeds[i] to inactive, add it to free list &
+ // remove it from active list:
+ //
+ // mActivePeds.Remove(mPeds[i].listIndex);
+ // mPeds[i].listIndex = mFreePeds.AddLast(&mPeds[i]);
+ //
+ DListArray mFreePeds;
+ DListArray mActivePeds;
+
+ // Pedestrian alone is not sufficient for our purposes...
+ struct PedestrianStruct
+ {
+ PedestrianStruct() : ped( NULL ), listIndex( -1 ) {};
+ Pedestrian* ped;
+ int listIndex; // index to mActivePeds or mFreePeds depending on whether ped is active
+ char modelName [MAX_STRING_LEN+1]; // name of model
+ };
+
+
+// METHODS
+public:
+
+ // STATICS
+ static PedestrianManager* GetInstance();
+ static void DestroyInstance();
+ static int GetMaxPedestrians( void )
+ {
+ return MAX_PEDESTRIANS;
+ }
+
+ // NON-STATICS
+
+ void Init(); // Initializes pedmanager itself; called from GamePlayContext::OnStart()
+ void Update( unsigned int milliseconds ); // called once per frame by GamePlayContext::Update()
+
+ void RemovePed( Character* character );
+ void RemoveAllPeds();
+ void AllowAddingPeds( bool allowed );
+
+ void DumpAllPedModels(); // will remove all peds and dump their models (so revert all to "npd")
+
+ ////////// EVENT LISTENER STUFF /////////////
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ /////////////////////////////////////////////
+
+ // Makes all model groups default to:
+ // numModels = 4
+ // model 0 = male1, 2
+ // model 1 = fem1, 1
+ // model 2 = boy1, 1
+ // model 3 = girl1, 1
+ // called from LoadingContext::OnStart()
+ void InitDefaultModelGroups();
+
+ void SetModelGroup( int groupID, const ModelGroup& group );
+
+ bool IsPed( Character* character );
+
+ void SwitchModelGroup( int toGroupID );
+
+ static void SetDefaultModelGroup( int toGroupID );
+
+// MEMBERS
+private:
+ static int mDefaultModelGroup;
+
+ unsigned int mMillisecondsTillRemove;
+ unsigned int mMillisecondsTillAdd;
+
+ // all peds we'll ever use/need
+ PedestrianStruct mPeds[ MAX_PEDESTRIANS ];
+
+ unsigned int mMillisecondsPopulateWorld;
+
+ struct Model
+ {
+ Model() : free( true ), maxCount( 0 ), currCount( 0 ) {};
+
+ bool free; // whether or not this model slot has been registered
+ char name[MAX_STRING_LEN+1]; // name of the model (duh)
+ int maxCount; // max ped instances of this model
+ int currCount; // current num instances of this model
+ void InitZero()
+ {
+ free = true;
+ name[0] = '\0';
+ maxCount = 0;
+ currCount = 0;
+ }
+ void Init( bool isFree, const char* modelName, int max, int curr )
+ {
+ rAssert( modelName != NULL );
+ rAssert( max > 0 );
+ rAssert( curr >= 0 );
+
+ free = isFree;
+
+ int nameLen = strlen( modelName );
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( name, modelName, PedestrianManager::MAX_STRING_LEN );
+ name[ nameLen ] = '\0';
+
+ maxCount = max;
+ currCount = curr;
+ }
+ };
+ Model mModels[MAX_MODELS]; // array of currently registered models (of current group)
+ int mNumRegisteredModels; // number of currently registerd models
+
+ ModelGroup mModelGroups[MAX_MODEL_GROUPS]; // groups of models for swapping
+ int mCurrGroupID;
+
+ struct UsedModel
+ {
+ UsedModel() : uid( 0 ), currCount( 0 ) {};
+ tName uid;
+ int currCount;
+ };
+ UsedModel mModelsInUse[ MAX_MODELS_IN_USE ];
+ int mNumActiveModels;
+
+ bool mAllowAdd;
+
+// METHODS
+private:
+ PedestrianManager();
+ virtual ~PedestrianManager();
+
+ void AddPeds( rmt::Vector center, float addRadius );
+ void DeactivatePed( PedestrianStruct* ped );
+ bool ActivatePed( PathSegment* segment, rmt::Vector spawnPos );
+
+ float GetRandomMinDistSqr();
+
+ int GetRandomModel();
+ bool SwapModels( int modelID, PedestrianStruct* pedStr );
+
+ bool RegisterModel( const char* name, int maxCount );
+ bool UnregisterModel( const char* name );
+ bool UnregisterModel( int modelID );
+ void UnregisterAllModels();
+ int GetModelIDByName( const char* name );
+
+ void FindModelInUseAndRemove( const char* name );
+ bool FindModelInUseAndAdd( const char* name );
+
+ void SanityCheck();
+ void CheckModelCounts();
+
+};
+
+#endif //PEDESTRIANMANAGER_H
diff --git a/game/code/worldsim/physicsairef.h b/game/code/worldsim/physicsairef.h
new file mode 100644
index 0000000..bff6d06
--- /dev/null
+++ b/game/code/worldsim/physicsairef.h
@@ -0,0 +1,51 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: physicsairef.h
+//
+// Description: Blahblahblah
+//
+// History: 6/17/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef PHYSICSAIREF_H
+#define PHYSICSAIREF_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+namespace PhysicsAIRef
+{
+ enum
+ {
+ redBrickPhizDefault = 0,
+ redBrickVehicle,
+ redBrickPhizVehicleGroundPlane,
+ redBrickPhizMoveableGroundPlane, // might not need to differnetiate here!
+ redBrickPhizFence, // might not need to differnetiate here!
+ redBrickPhizStatic = 1 << 16,
+ redBrickPhizMoveable = 1 << 17, // don't think we actually need to differentiate between moveable and moveableinstance
+ PlayerCharacter,
+ NPCharacter,
+ redBrickPhizMoveableAnim = 1 << 18,
+ CameraSphere,
+ StateProp,
+ ActorStateProp,
+ redBrickPhizLast
+ }; // phizGround, phizStatic, phizMoveableAnim, phizMoveable, phizCamera, phizLast };
+};
+
+
+#endif //PHYSICSAIREF_H
diff --git a/game/code/worldsim/redbrick/allredbrick.cpp b/game/code/worldsim/redbrick/allredbrick.cpp
new file mode 100644
index 0000000..27f9cca
--- /dev/null
+++ b/game/code/worldsim/redbrick/allredbrick.cpp
@@ -0,0 +1,13 @@
+#include <worldsim/redbrick/geometryvehicle.cpp>
+#include <worldsim/redbrick/physicslocomotion.cpp>
+#include <worldsim/redbrick/physicslocomotioncontrollerforces.cpp>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.cpp>
+#include <worldsim/redbrick/rootmatrixdriver.cpp>
+#include <worldsim/redbrick/suspensionjointdriver.cpp>
+#include <worldsim/redbrick/trafficlocomotion.cpp>
+#include <worldsim/redbrick/vehicle.cpp>
+#include <worldsim/redbrick/vehicleinit.cpp>
+#include <worldsim/redbrick/vehiclelocomotion.cpp>
+#include <worldsim/redbrick/wheel.cpp>
+#include <worldsim/redbrick/vehicleeventlistener.cpp>
+#include <worldsim/redbrick/trafficbodydrawable.cpp>
diff --git a/game/code/worldsim/redbrick/geometryvehicle.cpp b/game/code/worldsim/redbrick/geometryvehicle.cpp
new file mode 100644
index 0000000..8b7472b
--- /dev/null
+++ b/game/code/worldsim/redbrick/geometryvehicle.cpp
@@ -0,0 +1,3384 @@
+/*===========================================================================
+ geometryvehicle.cpp
+
+ created Dec 7, 2001
+ by Greg Mayer
+
+ Copyright (c) 2001 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+#include <camera/supercammanager.h>
+#include <contexts/bootupcontext.h>
+#include <worldsim/character/characterrenderable.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <render/RenderManager/RenderManager.h>
+
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/billboardobject.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/poseanimation.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/directionallight.hpp>
+#include <p3d/light.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/shadow.hpp>
+#include <p3d/view.hpp>
+#include <p3d/effects/particlesystem.hpp>
+#include <pddi/pddi.hpp>
+#include <p3d/utility.hpp>
+#include <typeinfo>
+#include <p3d/anim/visibilityanimation.hpp>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <mission/statepropcollectible.h>
+#include <memory/srrmemory.h>
+
+#include <mission/gameplaymanager.h>
+#include <render/particles/particlemanager.h>
+#include <radmath/util.hpp>
+#include <debug/debuginfo.h>
+#include <worldsim/redbrick/trafficbodydrawable.h>
+#include <worldsim/coins/sparkle.h>
+
+//#define SHADOW_EDITTING
+
+const int TYPICAL_NUMBER_OF_FRAME_CONTROLLERS = 16;
+
+// this is by artist design... the value is between 0 and 255
+static const int INCAR_ROOF_ALPHA = 100;
+// The velocity at which variable emission particle systems
+// reach their peak (13.5 meters/second ~ 50 kph)
+static const float PARTICLE_SYSTEM_MAX_VELOCITY = 13.5f;
+static const float MAX_Y_OFFSET = 0.25f; // Max distance the shadow will raise to avoid Z Chew.
+
+// Listing of special frame controllers
+// This list might be best moved into some sort of script file or data chunk
+// rather than being hardcoded if many more get added
+// Currently its got framecontrollers for the flame effects on the hoverbike
+static const GeometryVehicle::VehicleFrameController FRAME_CONTROLLER_DATA[] =
+{
+ // These are reversable framecontrollers
+ { "BQG_flame4Shape", NULL, true, eNone, 0, 1.0f, 0, 0 },
+ { "BQG_flame3Shape", NULL, true, eNone, 0, 1.0f, 0, 0 },
+ { "BQG_flame2Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "BQG_flame1Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "BQG_flame5Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "BQG_flame6Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "EFX_backfireSystShape", NULL, false, eBackfire, 0,0,0, 0 },
+ { "BQG_backfireflashGroupShape", NULL, false, eBackfire, 0,0,0 },
+ { "PTRN_ship", NULL, false, eNone, 0, 0, 1.0f/25.0f, 0 },
+ { "PTRN_redbrick", NULL, false, eNone, 0, 0, 0.08f, 5.0f }
+ //,
+// { "PTRN_ship", NULL, false, eNone, 2.0f,-2.0f,0,0 }
+};
+
+const rmt::Vector BARRACUDA_COLLECTIBLE_POS( 0, 0.4f, -3.0f );
+const rmt::Vector JEEP_COLLECTIBLE_POS( -0.237f, 0.216f, -2.243f );
+const rmt::Vector DUNE_COLLECTIBLE_ROT( rmt::DegToRadian( -90 ), rmt::DegToRadian( 0 ), rmt::DegToRadian( 0 ));
+const rmt::Vector DUNE_COLLECTIBLE_POS( 0, 0.714f, 0);
+
+const float BARREL_RADIUS = 0.351f;
+
+const rmt::Vector WITCH_COLLECTIBLE_POS( 0, 0.4f, -1.31f - BARREL_RADIUS );
+const rmt::Vector COFFIN_COLLECTIBLE_POS( 0, 0.4f, -1.59f - BARREL_RADIUS );
+const rmt::Vector HALLO_COLLECTIBLE_POS( 0, 0.4f, -3.467f - BARREL_RADIUS );
+
+
+const rmt::Matrix DEFAULT_COLLECTIBLE_TRANSFORM( 0.9219f, -0.3875f, 0.0f, 0.0f,
+ 0.3797f, 0.9034f, -0.1994f, 0.0f,
+ 0.0773f, 0.1838f, 0.9799f, 0.0f,
+ 0.0f, 0.1270f, -2.6610f, 1.0f );
+
+
+static const int NUM_FRAME_CONTROLLERS = sizeof( FRAME_CONTROLLER_DATA ) / sizeof( FRAME_CONTROLLER_DATA[0] );
+static bool sbDrawVehicle = true;
+static float refractiveIndex = 10.0f;
+
+//------------------------------------------------------------------------
+GeometryVehicle::GeometryVehicle():
+ m_Collectible( NULL ),
+ m_EnvRef( 0x40 )
+{
+ mCompositeDrawable = 0;
+
+ mChassisGeometry = 0;
+
+
+ mAnim = 0;
+
+ mAnimRevPerSecondBase = 1.0f;
+
+ mVehicleOwner = 0;
+
+ //mRevMult = 0.5f;
+ //mRevMult = 0.4f;
+ mRevMult = 0.1f; // good for frink - anyone else need this?
+
+
+ mHoodShader = 0;
+ mTrunkShader = 0;
+ mDoorPShader = 0;
+ mDoorDShader = 0;
+
+ mHoodTextureDam = 0;
+ mTrunkTextureDam = 0;
+ mDoorPTextureDam = 0;
+ mDoorDTextureDam = 0;
+
+ mHoodTextureNorm = 0;
+ mTrunkTextureNorm = 0;
+ mDoorPTextureNorm = 0;
+ mDoorDTextureNorm = 0;
+
+
+ mChassisShader = 0;
+ mChassisTextureNorm = 0;
+ mChassisTextureDam = 0;
+
+ mParticleEmitter = 0;
+ mVariableEmissionParticleSystem = 0;
+ mEngineParticleAttr.mType = ParticleEnum::eNull;
+ mLeftWheelParticleAttr.mType = ParticleEnum::eNull;
+ mRightWheelParticleAttr.mType = ParticleEnum::eNull;
+ mTailPipeParticleAttr.mType = ParticleEnum::eNull;
+
+ mSkidMarkGenerator = 0;
+
+ mSpecialEffect = ParticleEnum::eNull;
+
+ mLastPosition = rmt::Vector (0.0f, 0.0f, 0.0f);
+ mCurEnvMapRotation = 0.0f;
+
+ mBrakeLightJoints[0] = -1;
+ mBrakeLightJoints[1] = -1;
+ mBrakeLightJoints[2] = -1;
+ mBrakeLightJoints[3] = -1;
+
+ mReverseLightJoints[0] = -1;
+ mReverseLightJoints[1] = -1;
+ mReverseLightJoints[2] = -1;
+ mReverseLightJoints[3] = -1;
+
+ mTrafficBodyDrawable = NULL;
+ mTrafficDoorDrawable = NULL;
+
+ for( int i = 0; i < 4; ++i )
+ {
+ mShadowPointAdjustments[ i ][ 0 ] = 0.0f;
+ mShadowPointAdjustments[ i ][ 1 ] = 0.0f;
+ }
+
+ mFadeAlpha = 255;
+
+ for( int i=0; i<NUM_BRAKELIGHT_BBQS; i++ )
+ {
+ mBrakeLights[i] = NULL;
+ }
+
+#ifdef RAD_WIN32
+ mFrinkArc = NULL;
+#endif
+
+ mUsingTrafficModel = false;
+
+ /*
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ mHeadLights[i] = NULL;
+ }
+ */
+
+ mHasGhostGlow = false;
+ for( int i=0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ mGhostGlows[i] = NULL;
+ }
+
+ mHasNukeGlow = false;
+ for( int i=0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ mNukeGlows[i] = NULL;
+ }
+
+ mBrakeLightsOn = false;
+ mBrakeLightScale = 0.0f;
+ mHeadLightScale = 0.0f;
+ mEnableLights = true; // sets visibility on headlights and brakelights
+ mLightsOffDueToDamage = false;
+
+ mRoofShader = NULL;
+ mRoofAlpha = 255;
+ mRoofTargetAlpha = 255;
+
+ mRoofOpacShape = NULL;
+ mRoofAlphaShape = NULL;
+
+ for( int i = 0; i < MAX_REFRACTION_SHADERS; ++i )
+ {
+ mRefractionShader[ i ] = NULL;
+ }
+ // Lets make a collectible transform
+ // Hard code it for now. Later use car specific ones from Kevin
+ m_CollectibleTransform = DEFAULT_COLLECTIBLE_TRANSFORM;
+
+ mFrameControllers.reserve( TYPICAL_NUMBER_OF_FRAME_CONTROLLERS );
+
+ //
+ // Add refractive index to the watcher
+ //
+ static bool refractiveIndexAdded = false;
+ if( !refractiveIndexAdded )
+ {
+ radDbgWatchAddFloat( &refractiveIndex, "RefractionShader", "RefraciveIndex", 0, 0, -20.0f, 20.0f );
+ refractiveIndexAdded = true;
+ }
+}
+
+
+//------------------------------------------------------------------------
+GeometryVehicle::~GeometryVehicle()
+{
+#ifdef SHADOW_EDITTING
+ for( int i = 0; i < 4; ++i )
+ {
+ radDbgWatchDelete( &(mShadowPointAdjustments[ i ][ 0 ]) );
+ radDbgWatchDelete( &(mShadowPointAdjustments[ i ][ 1 ]) );
+ }
+#endif
+ if(mCompositeDrawable)
+ {
+ GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mCompositeDrawable);
+ mCompositeDrawable = 0;
+ }
+
+ if(mVariableEmissionParticleSystem)
+ {
+ GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mVariableEmissionParticleSystem);
+ mVariableEmissionParticleSystem = 0;
+ }
+
+ if(mChassisGeometry)
+ {
+ mChassisGeometry->Release();
+ mChassisGeometry = 0;
+ }
+
+ if(mAnim)
+ {
+ mAnim->Release();
+ mAnim = 0;
+ }
+
+
+
+ for( int i = 0; i < MAX_REFRACTION_SHADERS; ++i )
+ {
+ tRefCounted::Release( mRefractionShader[ i ] );
+ mRefractionShader[ i ] = 0;
+ }
+
+ // damage state crapola
+
+ if(mHoodShader) mHoodShader->Release();
+ if(mTrunkShader) mTrunkShader->Release();
+ if(mDoorPShader) mDoorPShader->Release();
+ if(mDoorDShader) mDoorDShader->Release();
+
+ if(mHoodTextureNorm) mHoodTextureNorm->Release();
+ if(mTrunkTextureNorm) mTrunkTextureNorm->Release();
+ if(mDoorPTextureNorm) mDoorPTextureNorm->Release();
+ if(mDoorDTextureNorm) mDoorDTextureNorm->Release();
+
+ if(mHoodTextureDam) mHoodTextureDam->Release();
+ if(mTrunkTextureDam) mTrunkTextureDam->Release();
+ if(mDoorPTextureDam) mDoorPTextureDam->Release();
+ if(mDoorDTextureDam) mDoorDTextureDam->Release();
+
+
+ if(mChassisShader) mChassisShader->Release();
+ if(mChassisTextureNorm) mChassisTextureNorm->Release();
+ if(mChassisTextureDam) mChassisTextureDam->Release();
+
+ delete mParticleEmitter;
+ if ( mSpecialEffect != ParticleEnum::eNull )
+ {
+ GetParticleManager()->DeleteSystem( mSpecialEffect );
+ }
+
+ if ( mVariableEmissionParticleSystem != 0 )
+ {
+ mVariableEmissionParticleSystem->ReleaseParticles();
+ mVariableEmissionParticleSystem = 0;
+ }
+
+ if(mSkidMarkGenerator)
+ {
+ delete mSkidMarkGenerator;
+ }
+
+ if( mTrafficBodyDrawable )
+ {
+ mTrafficBodyDrawable->Release();
+ mTrafficBodyDrawable = NULL;
+ }
+
+ if( mTrafficDoorDrawable )
+ {
+ mTrafficDoorDrawable->Release();
+ mTrafficDoorDrawable = NULL;
+ }
+
+ for( int i=0; i<NUM_BRAKELIGHT_BBQS; i++ )
+ {
+ if( mBrakeLights[i] )
+ {
+ mBrakeLights[i]->Release();
+ mBrakeLights[i] = NULL;
+ }
+ }
+
+#ifdef RAD_WIN32
+ if( mFrinkArc )
+ {
+ mFrinkArc->Release();
+ mFrinkArc = NULL;
+ }
+#endif
+ /*
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ if( mHeadLights[i] )
+ {
+ mHeadLights[i]->Release();
+ mHeadLights[i] = NULL;
+ }
+ }
+ */
+ for( int i=0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ if( mGhostGlows[i] )
+ {
+ mGhostGlows[i]->Release();
+ mGhostGlows[i] = NULL;
+ }
+ }
+ for( int i=0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ if( mNukeGlows[i] )
+ {
+ mNukeGlows[i]->Release();
+ mNukeGlows[i] = NULL;
+ }
+ }
+ if( mRoofShader )
+ {
+ mRoofShader->Release();
+ mRoofShader = NULL;
+ }
+ if( mRoofOpacShape )
+ {
+ mRoofOpacShape = NULL;
+ }
+ if( mRoofAlphaShape )
+ {
+ mRoofAlphaShape = NULL;
+ }
+
+
+ for ( unsigned int fc = 0 ; fc < mFrameControllers.size() ; fc++ )
+ {
+ mFrameControllers[fc].frameController->Release();
+ }
+
+ if ( m_Collectible != NULL )
+ {
+ m_Collectible->Release();
+ m_Collectible = NULL;
+ }
+}
+
+
+//------------------------------------------------------------------------
+bool GeometryVehicle::Init( const char* name, Vehicle* owner, int num)
+{
+#ifdef SHADOW_EDITTING
+ for( int i = 0; i < 4; ++i )
+ {
+ mShadowPointAdjustments[ i ][ 0 ] = 0.0f;
+ mShadowPointAdjustments[ i ][ 1 ] = 0.0f;
+ char text[ 128 ];
+ sprintf( text, "%s Shadow point %d X", name, i );
+ radDbgWatchAddFloat( &(mShadowPointAdjustments[ i ][ 0 ]), text, "VehicleShadow", 0, 0, -5.0f, 5.0f );
+ sprintf( text, "%s Shadow point %d Y", name, i );
+ radDbgWatchAddFloat( &(mShadowPointAdjustments[ i ][ 1 ]), text, "VehicleShadow", 0, 0, -5.0f, 5.0f );
+ }
+#endif
+ mVehicleOwner = owner;
+
+ mCurEnvMapRotation = 0.0f;
+
+ return GetArt(name);
+}
+
+
+//=============================================================================
+// GeometryVehicle::InitSkidMarks
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::InitSkidMarks()
+{
+ // called from vehicle Init
+ // only for user vehicles
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mSkidMarkGenerator = new(gma)SkidMarkGenerator;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // initial value
+
+ rmt::Vector offset = mVehicleOwner->mSuspensionRestPoints[i];
+ offset.y -= mVehicleOwner->mWheels[i]->mRadius;
+
+ mSkidMarkGenerator->SetWheelOffset(i, offset); // need to do this everyframe
+
+ float length = mVehicleOwner->mWheels[i]->mRadius * 0.6f;
+ //float width = mVehicleOwner->mWheels[i]->mRadius * 0.5f; //??
+ float width = mVehicleOwner->mWheels[i]->mRadius * 0.8f; //??
+
+ mSkidMarkGenerator->SetWheelDimensions(i, width, length);
+
+ //void GenerateSkid( int wheel, const SkidData& );
+
+ //void Update();
+
+ }
+
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::InitParticles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::InitParticles()
+{
+MEMTRACK_PUSH_GROUP( "GeometryVehicle" );
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mParticleEmitter = new(gma)VehicleParticleEmitter;
+
+ rmt::Vector smokeOffset = mVehicleOwner->GetSmokeOffset();
+
+ mParticleEmitter->SetPartLocation(VehicleParticleEmitter::eEngine, smokeOffset);
+
+ rmt::Vector wheelOffset = mVehicleOwner->GetWheel0Offset();
+ mParticleEmitter->SetPartLocation(VehicleParticleEmitter::eRightBackTire, wheelOffset);
+
+ wheelOffset = mVehicleOwner->GetWheel1Offset();
+ mParticleEmitter->SetPartLocation(VehicleParticleEmitter::eLeftBackTire, wheelOffset);
+MEMTRACK_POP_GROUP("GeometryVehicle");
+}
+
+
+
+//=============================================================================
+// GeometryVehicle::GetHeadlightScale
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float GeometryVehicle::GetHeadlightScale()
+{
+ return mHeadLightScale;
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetHeadlightScale
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float scale )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetHeadlightScale( float scale )
+{
+ rAssert( 0.0f <= scale );
+ this->mHeadLightScale = scale;
+}
+
+
+//=============================================================================
+// GeometryVehicle::AttachCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( StatePropCollectible* drawable )
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::AttachCollectible( StatePropCollectible* drawable )
+{
+ bool wasAttached;
+ // We only have one slot for a collectible per vehicle
+ // check that this vehicle's slot is currently empty
+ if ( m_Collectible == NULL )
+ {
+ tRefCounted::Assign( m_Collectible, drawable );
+ rmt::Matrix identity;
+ identity.Identity();
+ drawable->SetTransform( identity );
+ m_Collectible->EnableCollisionVolume( false );
+ m_Collectible->SetState( 1 );
+ m_Collectible->RemoveFromDSG();
+ m_Collectible->EnableHudIcon( false );
+ m_Collectible->EnableCollisionTesting( false );
+ wasAttached = true;
+ }
+ else
+ {
+ wasAttached = false;
+ }
+
+ return wasAttached;
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetAttachedCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: StatePropCollectible
+//
+//=============================================================================
+StatePropCollectible* GeometryVehicle::GetAttachedCollectible()
+{
+ return m_Collectible;
+}
+
+
+//=============================================================================
+// GeometryVehicle::DetachCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& velocity, bool explode )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DetachCollectible( const rmt::Vector& velocity, bool explode )
+{
+ if ( m_Collectible )
+ {
+ m_Collectible->EnableHudIcon( false );
+ m_Collectible->EnableCollisionVolume( false );
+ m_Collectible->SetState( 0 );
+ sim::SimState* sim = m_Collectible->GetSimState();
+ if ( sim )
+ {
+ sim->ResetVelocities();
+ }
+ m_Collectible->AddToDSG();
+ // Lets set the transform to have no rotation, but position remains the same
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( m_CollectibleTransform.Row(3) + mVehicleOwner->GetPosition() );
+ m_Collectible->SetTransform(transform);
+
+ m_Collectible->Release();
+ if ( explode )
+ m_Collectible->Explode();
+
+ m_Collectible = NULL;
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::Display()
+{
+BEGIN_PROFILE("GeometryVehicle::Display SetUp")
+ rmt::Vector pos = mVehicleOwner->GetPosition ();
+ rmt::Vector movement = pos - mLastPosition;
+ bool smokeFirst = true; // Used to fix draw order problem with the smoke and a vehicle's windshield.
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ const rmt::Matrix& camTrans = camera->GetCameraToWorldMatrix();
+
+ if( mVehicleOwner->mVehicleType == VT_USER )
+ {
+ // Set the rotation vector on all the car's shaders.
+ // Base the rotation on how far the car has moved in the last known direction,
+ // so that the reflection map moves across the car when the car moves. -- jdy
+ //
+
+ bool fucked = false;
+ if ( mLastPosition.MagnitudeSqr() != 0.0f &&
+ ( rmt::Fabs( movement.x ) > 1000.0f ||
+ rmt::Fabs( movement.y ) > 1000.0f ||
+ rmt::Fabs( movement.z ) > 1000.0f ) )
+ {
+ //TODO: What the hell is causing this?
+ //mVehicleOwner position and facing are fucked.
+ //We've moved too far. Clamp.
+ pos = mLastPosition;
+ fucked = true;
+ }
+
+ if ( !fucked )
+ {
+ float distance = movement.DotProduct( mVehicleOwner->GetFacing() );
+ rAssert( !rmt::IsNan( distance ) );
+ if( rmt::IsNan( distance ) )
+ {
+ distance = 0.0f;
+ }
+ if( rmt::Fabs( distance ) > 0.01f && rmt::Fabs( distance ) < 50.0f ) // Car must move at least 1 cm for an adjustment to be made
+ {
+ // Find the normal of the plane defined by the direction of movement and the camera facing.
+ //This will be the axis we rotate around.
+ // Note that the reflection map is relative to the object so we need to transform everything
+ //into the object's space.
+ rmt::Matrix carOri = mVehicleOwner->GetTransform();
+ carOri.Invert();
+ carOri.FillTranslate( rmt::Vector( 0.0f, 0.0f, 0.0f ) );
+ rmt::Vector camRelCar;
+ carOri.Transform( camTrans.Row( 2 ), &camRelCar );
+ rmt::Vector carRelMovement;
+ carOri.Transform( movement, &carRelMovement );
+ rmt::Vector axis;
+ float camMovementFacing = carRelMovement.Dot( camRelCar );
+ if( camMovementFacing < 0.0f )
+ {
+ axis.CrossProduct(camRelCar, carRelMovement);
+ }
+ else
+ {
+ axis.CrossProduct(carRelMovement, camRelCar);
+ }
+ // Check if the car is moving towards or away from the camera.
+ mCurEnvMapRotation += distance * 0.05f;
+ while( mCurEnvMapRotation >= rmt::PI_2 )
+ {
+ mCurEnvMapRotation -= rmt::PI_2;
+ }
+ while( mCurEnvMapRotation < 0.0f )
+ {
+ mCurEnvMapRotation += rmt::PI_2;
+ }
+ float mag;
+ float angle;
+ CartesianToPolar( axis.x, axis.y, &mag, &angle );
+
+ tShaderVectorBroadcast cbPos( PDDI_SP_ROTVEC, rmt::Vector( mCurEnvMapRotation, 0.0f, -angle ) );
+ int count = mCompositeDrawable->GetNumDrawableElement ();
+ for (int i = 0; i < count; i++)
+ {
+ tCompositeDrawable::DrawableElement* pElement = mCompositeDrawable->GetDrawableElement( i );
+ tDrawable* pDrawable = pElement->GetDrawable();
+ pDrawable->ProcessShaders( cbPos );
+ }
+ }
+ // Remember the position of the car for next time
+ //
+ mLastPosition = pos;
+ }
+ }
+ else
+ {
+ mLastPosition = pos;
+ }
+
+ rmt::Vector vehicleVelocity;
+ mVehicleOwner->GetVelocity( &vehicleVelocity );
+
+ if(mEngineParticleAttr.mType != ParticleEnum::eNull)
+ {
+ // Figure out which to draw first, the smoke or the car.
+ //We do this by transforming the SmokeOffset into world space and then dot product with the
+ //the camera's facing vector. If it is towards the camera we draw the smoke after the car.
+ //Crude, but quick and hopefully there won't be too many visual artifacts.
+ rmt::Matrix facing = mVehicleOwner->mTransform;
+ facing.FillTranslate( rmt::Vector( 0.0f, 0.0f, 0.0f ) );
+ rmt::Vector smoke = mVehicleOwner->GetSmokeOffset();
+ facing.Transform( smoke, &smoke );
+ float dot = smoke.DotProduct( camTrans.Row( 2 ) );
+ smokeFirst = dot > 0.6f;
+
+ if( ( mEngineParticleAttr.mType == ParticleEnum::eEngineSmokeLight ) ||
+ ( mEngineParticleAttr.mType == ParticleEnum::eEngineSmokeMedium ) )
+ {
+ GetSparkleManager()->AddSmoke( mVehicleOwner->mTransform, mVehicleOwner->GetSmokeOffset(), movement, ( ( 0.4f - mVehicleOwner->GetVehicleLifePercentage( mVehicleOwner->mHitPoints ) ) * 2.5f ) );
+ }
+ else
+ {
+ // draw some particles
+ //mEngineParticleAttr.mVelocity = vehicleVelocity / 45.0f;
+ mParticleEmitter->Generate( VehicleParticleEmitter::eEngine,
+ mEngineParticleAttr, mVehicleOwner->mTransform);
+
+
+ // TODO - HOW ABOUT TRAFFIC - WITH THE ONE-TIME POOF!! DON'T DRAW CONTINUOUSLY
+ }
+ }
+ if ( mSpecialEffect != ParticleEnum::eNull )
+ {
+ ParticleAttributes attr;
+ attr.mType = mSpecialEffect;
+
+ mParticleEmitter->Generate( VehicleParticleEmitter::eSpecialEmitter,
+ attr, mVehicleOwner->mTransform);
+
+ }
+ // If this vehicle has tailpipe particles, generate them, always
+
+ // 31.25 (trails too long)
+ //mTailPipeParticleAttr.mVelocity = vehicleVelocity / 29.0f;
+
+ if ( mTailPipeParticleAttr.mType != ParticleEnum::eNull )
+ {
+ mParticleEmitter->Generate( VehicleParticleEmitter::eRightTailPipe,
+ mTailPipeParticleAttr, mVehicleOwner->mTransform );
+
+ mParticleEmitter->Generate( VehicleParticleEmitter::eLeftTailPipe,
+ mTailPipeParticleAttr, mVehicleOwner->mTransform );
+ }
+
+ if(mLeftWheelParticleAttr.mType != ParticleEnum::eNull)
+ {
+ mParticleEmitter->Generate( VehicleParticleEmitter::eLeftBackTire,
+ mLeftWheelParticleAttr, mVehicleOwner->mTransform);
+ }
+ if(mRightWheelParticleAttr.mType != ParticleEnum::eNull)
+ {
+ mParticleEmitter->Generate( VehicleParticleEmitter::eRightBackTire,
+ mRightWheelParticleAttr, mVehicleOwner->mTransform);
+ }
+
+// if( mVehicleOwner->mVehicleType == VT_USER )
+ {
+ // Change the vehicle environment reflection
+ //depending on if it is interior or damaged.
+ unsigned char blend;
+ if( !mVehicleOwner->mInterior )
+ {
+ blend = m_EnvRef;
+ }
+ else
+ {
+ blend = m_EnvRef >> 2; // Quarter value for interior.
+ }
+ float damage = mVehicleOwner->GetVehicleLifePercentage(mVehicleOwner->mHitPoints);
+ damage = rmt::Clamp( ( damage * 0.75f ) + 0.25f, 0.0f, 1.0f );
+ blend = (unsigned char)( blend * damage );
+ tShaderColourBroadcast envBlend( PDDI_SP_ENVBLEND, tColour( blend, blend, blend ) );
+ int count = mCompositeDrawable->GetNumDrawableElement ();
+ for (int i = 0; i < count; i++)
+ {
+ tCompositeDrawable::DrawableElement* pElement = mCompositeDrawable->GetDrawableElement (i);
+ tDrawable* pDrawable = pElement->GetDrawable ();
+ pDrawable->ProcessShaders( envBlend );
+ }
+ }
+
+END_PROFILE("GeometryVehicle::Display SetUp")
+BEGIN_PROFILE("GeometryVehicle::Display Render")
+
+ if( mUsingTrafficModel )
+ {
+ if( mTrafficBodyDrawable != NULL )
+ {
+ if( mFadeAlpha != 255 )
+ {
+ mTrafficBodyDrawable->mFading = true;
+ }
+ else
+ {
+ mTrafficBodyDrawable->mFading = false;
+ }
+ mTrafficBodyDrawable->mFadeAlpha = mFadeAlpha;
+ }
+ if( mTrafficDoorDrawable != NULL )
+ {
+ if( mFadeAlpha != 255 )
+ {
+ mTrafficDoorDrawable->mFading = true;
+ }
+ else
+ {
+ mTrafficDoorDrawable->mFading = false;
+ }
+ mTrafficDoorDrawable->mFadeAlpha = mFadeAlpha;
+ }
+
+
+ // NOTE:
+ // This is a big cause for major grievances. It will force the blend mode of
+ // all the shaders of the compositedrawable's elements to be BLEND_ALPHA...
+ // bad news for billboards that are BLEND_NONE or BLEND_ADD...
+ //
+ tShaderIntBroadcast blendModeAlpha( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ mCompositeDrawable->ProcessShaders( blendModeAlpha );
+
+ tShaderIntBroadcast emissiveAlpha( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ mCompositeDrawable->ProcessShaders( emissiveAlpha );
+ }
+
+
+#ifdef RAD_WIN32
+ const tName& name = mVehicleOwner->GetNameObject();
+ if( name == "frink_v" )
+ {
+ float topSpeed = mVehicleOwner->GetTopSpeed();
+ float percentOfTopSpeed = vehicleVelocity.Magnitude() / topSpeed;
+ float refraction = percentOfTopSpeed;
+ refraction = rmt::Clamp( refraction, 0.0f, 1.0f );
+
+ int emissiveAlpha = (int)( 255.0f * (1.0f - refraction) );
+
+ tShaderIntBroadcast blendMode( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ tShaderIntBroadcast emissive( PDDI_SP_EMISSIVEALPHA, emissiveAlpha );
+ int count = mCompositeDrawable->GetNumDrawableElement ();
+ for (int j = 0; j < count; j++)
+ {
+ tCompositeDrawable::DrawableElement* pElement = mCompositeDrawable->GetDrawableElement( j );
+ tDrawable* pDrawable = pElement->GetDrawable();
+ pDrawable->ProcessShaders( blendMode );
+ pDrawable->ProcessShaders( emissive );
+ }
+
+ // so since we just made EVERY SHADER alpha blend, we need to take
+ // care of the BBQGs (that need to be additive blended)
+ if( mFrinkArc )
+ {
+ tColour arcColour;
+ for( int j=0; j< NUM_FRINKARC_BBQS; j++ )
+ {
+ tBillboardQuad* bbq = mFrinkArc->GetQuad(j);
+ rAssert( bbq );
+
+ arcColour.Set(
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Red()*(1.0f - refraction)), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Green()*(1.0f - refraction)), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Blue()*(1.0f - refraction)), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Alpha()*(1.0f - refraction)), 0, 255 )
+ );
+ bbq->SetColour( arcColour );
+ }
+ mFrinkArc->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+#endif
+
+
+
+
+
+ tColour brakecolour;
+
+ // if not turning on brakelights, we need to take into account the daytime
+ // running lights settings
+ float dayReduce = 2.5f; // by default, if braking is on, we magnify this value
+ if( !mBrakeLightsOn )
+ {
+ dayReduce = mBrakeLightScale;
+ }
+ float fadeFactor = mFadeAlpha / 255.0f;
+BEGIN_PROFILE("GeometryVehicle::Breaklights")
+ for( int i=0; i<NUM_BRAKELIGHT_BBQS; i++ )
+ {
+ if( mBrakeLights[i] != NULL )
+ {
+ brakecolour.Set(
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Red()*fadeFactor*dayReduce), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Green()*fadeFactor*dayReduce), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Blue()*fadeFactor*dayReduce), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Alpha()*fadeFactor*dayReduce), 0, 255 )
+ );
+ for( int j=0; j<mBrakeLights[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* bbq = mBrakeLights[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( brakecolour );
+ bbq->SetVisibility( mEnableLights );
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend for
+ // brakelights... We set it back.
+ mBrakeLights[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+END_PROFILE("GeometryVehicle::Breaklights")
+
+BEGIN_PROFILE("GeometryVehicle::Headlights")
+ tColour headlightcolour;
+ int count = 0;
+ for( int i=0; i<VehicleCentral::NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ if( GetVehicleCentral()->mHeadLights[i] != NULL )
+ {
+ for( int j=0; j<GetVehicleCentral()->mHeadLights[i]->GetNumQuads(); j++ )
+ {
+ headlightcolour.Set(
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Red()*fadeFactor*mHeadLightScale), 0, 255 ),
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Green()*fadeFactor*mHeadLightScale), 0, 255 ),
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Blue()*fadeFactor*mHeadLightScale), 0, 255 ),
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Alpha()*fadeFactor*mHeadLightScale), 0, 255 )
+ );
+ tBillboardQuad* bbq = GetVehicleCentral()->mHeadLights[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( headlightcolour );
+ bbq->SetVisibility( mEnableLights );
+ count++;
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ GetVehicleCentral()->mHeadLights[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+END_PROFILE("GeometryVehicle::Headlights")
+
+BEGIN_PROFILE("GeometryVehicle::Ghost")
+ // this means we're the ghost pirate ship... The glow must be ADDITIVE as well...
+ if( mHasGhostGlow )
+ {
+ count = 0;
+ tColour glowcolour;
+ for( int i=0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ if( mGhostGlows[i] != NULL )
+ {
+ for( int j=0; j<mGhostGlows[i]->GetNumQuads(); j++ )
+ {
+ glowcolour.Set(
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Red()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Green()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Blue()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Alpha()*fadeFactor), 0, 255 )
+ );
+ tBillboardQuad* bbq = mGhostGlows[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( glowcolour );
+ count++;
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ mGhostGlows[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+ }
+END_PROFILE("GeometryVehicle::Ghost")
+
+ // this means we're the ghost pirate ship... The glow must be ADDITIVE as well...
+ if( mHasNukeGlow )
+ {
+ count = 0;
+ tColour glowcolour;
+ for( int i=0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ if( mNukeGlows[i] != NULL )
+ {
+ for( int j=0; j<mNukeGlows[i]->GetNumQuads(); j++ )
+ {
+ glowcolour.Set(
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Red()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Green()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Blue()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Alpha()*fadeFactor), 0, 255 )
+ );
+ tBillboardQuad* bbq = mNukeGlows[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( glowcolour );
+ count++;
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ mNukeGlows[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+ }
+
+ // fade roof...
+ if( mRoofShader )
+ {
+ if( mRoofAlpha > mRoofTargetAlpha )
+ {
+ mRoofAlpha--;
+ }
+ else if( mRoofAlpha < mRoofTargetAlpha )
+ {
+ mRoofAlpha++;
+ }
+ //mRoofShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ mRoofShader->SetInt( PDDI_SP_EMISSIVEALPHA, mRoofAlpha );
+ if( mRoofOpacShape && mRoofAlphaShape )
+ {
+ if( mRoofAlpha == 255 )
+ {
+ mRoofOpacShape->SetVisibility( true );
+ mRoofAlphaShape->SetVisibility( false );
+ }
+ else
+ {
+ mRoofOpacShape->SetVisibility( false );
+ mRoofAlphaShape->SetVisibility( true );
+ }
+ }
+ }
+
+BEGIN_PROFILE("GeometryVehicle::CompDraw->Disp")
+ if( smokeFirst )
+ {
+ GetSparkleManager()->Render( Sparkle::SRM_SortedOnly );
+ }
+ if( sbDrawVehicle )
+ {
+ mCompositeDrawable->Display();
+ }
+ if( !smokeFirst )
+ {
+ GetSparkleManager()->Render( Sparkle::SRM_SortedOnly );
+ }
+ if( m_Collectible )
+ {
+ rmt::Matrix transform = mVehicleOwner->GetTransform();
+
+ p3d::stack->PushMultiply( transform );
+ p3d::stack->PushMultiply( m_CollectibleTransform );
+ m_Collectible->Display();
+ p3d::stack->Pop();
+ p3d::stack->Pop();
+ }
+
+END_PROFILE("GeometryVehicle::CompDraw->Disp")
+END_PROFILE("GeometryVehicle::Display Render")
+}
+
+
+//=============================================================================
+// GeometryVehicle::DisplaySkids
+//=============================================================================
+// Description: Comment
+//
+// Parameters: void GeometryVehicle::DisplaySkids(int wheel
+
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetSkidValues(int wheel, float intensity, rmt::Vector& groundPlaneNormal, eTerrainType terrainType )
+{
+ // if this got called, intensity is > 0.0
+
+ if(mSkidMarkGenerator)// && mDrawSkids) // test here though this should only be getting called for vehicles that have it... ie. VT_USER
+ {
+ /*
+ struct SkidData
+ {
+ // Direction that the vehicle is moving
+ rmt::Vector velocityDirection;
+ // Orientation and position of the vehicle
+ rmt::Matrix transform;
+ // Other data?????
+ float intensity; // 1 most intense, 0 nonexistent skid
+ };
+ */
+
+ rmt::Vector offset = mVehicleOwner->mSuspensionRestPoints[wheel];
+ offset.y -= mVehicleOwner->mWheels[wheel]->mRadius;
+ offset.y += mVehicleOwner->mWheels[wheel]->mYOffset; // hmmm....
+
+ //if(mVehicleOwner->mWheels[wheel]->mYOffset != 0.0f)
+ //{
+ // int stophere = 1;
+ //}
+
+ mSkidMarkGenerator->SetWheelOffset(wheel, offset); // need to do this everyframe
+
+ SkidMarkGenerator::SkidData sd;
+ sd.intensity = intensity;
+ sd.transform = mVehicleOwner->mTransform;
+ sd.terrainType = terrainType;
+
+ sd.groundPlaneNormal = groundPlaneNormal;
+
+
+ rmt::Vector velocityDir = mVehicleOwner->mVelocityCM;
+ velocityDir.NormalizeSafe();
+
+ // TODO
+ // Michael - would this blow up if I passed in 0,0,0 ?
+ sd.velocityDirection = velocityDir;
+
+ mSkidMarkGenerator->GenerateSkid(wheel, sd);
+
+ }
+
+}
+
+//=============================================================================
+// GeometryVehicle::DisplaySkids
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::UpdateSkids()
+{
+ if(mSkidMarkGenerator)
+ {
+ mSkidMarkGenerator->Update();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int GeometryVehicle::CastsShadow()
+{
+ return 2;
+}
+//------------------------------------------------------------------------
+void GeometryVehicle::DisplayShadow( BlobShadowParams* BlobParams )
+{
+ BEGIN_PROFILE("GeometryVehicle::DisplayShadow")
+ rAssert(BlobParams);
+ tColour OutsideColour, insideColour;
+
+ // Hack for brightly colored shadows for special game mode
+ rmt::Vector camPos;
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert(camera);
+ camera->GetWorldPosition( &camPos );
+ camPos.Sub( BlobParams->GroundPos );
+ float yOffset = 0.0f; // Move the shadow up a bit with distance to avoid Z chew.
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ rAssert( mVehicleOwner );
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForVehicle( mVehicleOwner );
+
+ OutsideColour.Set( 0, 0, 0, 0 ); // black outline
+ insideColour.Set( 0, 0, 0 ); // default black if no Avatar
+ if( avatar )
+ {
+ int id = avatar->GetPlayerId();
+
+ if ( this->mVehicleOwner->mNumTurbos > 0 )
+ {
+ insideColour = ::GetGameplayManager()->GetControllerColour( id );
+ }
+ else
+ {
+ insideColour = tColour( 0, 0, 0 );
+ }
+ }
+ }
+ else
+ {
+ // Camera culling.
+ float camDirDot = camPos.DotProduct( camera->GetCameraToWorldMatrix().Row( 2 ) );
+ // Note that the camPos vector is towards the camera so the camDirDot will be negative when the camera is facing
+ //the object. So if it's positive then we can assume it's behind the camera.
+ if( camDirDot > 0.0f )
+ {
+ END_PROFILE("GeometryVehicle::DisplayShadow")
+ return;
+ }
+
+ // Blobby shadow for vehicle.
+ OutsideColour.Set( 255, 255, 255, 255 );
+ const int Inside = 128;
+
+ float fadeFactor = 1.0f;
+ if( mVehicleOwner->mVehicleType == VT_TRAFFIC &&
+ mFadeAlpha != 255 )
+ {
+ fadeFactor = 1.0f - (mFadeAlpha/255.0f);
+ }
+ else
+ {
+ fadeFactor = 1.0f - BlobParams->ShadowAlpha;
+ }
+ // DistanceAlpha: over 30 is 1, under 10 is 0, linear between.
+ float distanceAlpha = rmt::Clamp((camPos.Magnitude() - 10.0f) * 0.05f, 0.0f, 1.0f);
+
+ fadeFactor *= 1.0f - distanceAlpha;
+
+ int c = rmt::Clamp( int( Inside + ( 255 - Inside ) * fadeFactor ), 0, 255 );
+ if( c == 255)
+ {
+ return;
+ }
+ insideColour.Set( c, c, c, c );
+ yOffset = MAX_Y_OFFSET * distanceAlpha;
+ }
+ const int NumPoints = 6;
+ const float BlobLength = 2.2f;
+ const float BlobWidth = 1.2f;
+ const float BlobCant = 0.2f;
+ const float BlobFade = 0.4f;
+ static float Points[ NumPoints ][ 2 ];
+ static float Fades[ NumPoints ][ 2 ];
+
+ static bool DoOnce = true;
+ if( DoOnce )
+ {
+ DoOnce = false;
+ /* Here are the points we'll use for the car:
+
+ ....----0
+ . 1
+ . |
+ . |
+ . + 2
+ . 3
+ . |
+ . 4
+ ....----5
+ We'll mirror the blob along the Z axis.
+ The shadow point adjusts work like this:
+ 0 - affects points 0 and 1, the top corner.
+ 1 - affects point 2, the position for the top division.
+ 2 - affects point 3, the position for the bottom division.
+ 3 - affects point 4 and 5, the bottom corner.
+ */
+ Points[ 0 ][ 0 ] = BlobWidth - BlobCant;
+ Points[ 0 ][ 1 ] = BlobLength;
+ Points[ 1 ][ 0 ] = BlobWidth;
+ Points[ 1 ][ 1 ] = BlobLength - BlobCant;
+ Points[ 2 ][ 0 ] = BlobWidth;
+ Points[ 2 ][ 1 ] = BlobCant;
+ Points[ 3 ][ 0 ] = BlobWidth;
+ Points[ 3 ][ 1 ] = -BlobCant;
+ Points[ 4 ][ 0 ] = BlobWidth;
+ Points[ 4 ][ 1 ] = -BlobLength + BlobCant;
+ Points[ 5 ][ 0 ] = BlobWidth - BlobCant;
+ Points[ 5 ][ 1 ] = -BlobLength;
+ Fades[ 0 ][ 0 ] = 0.5f * BlobFade; // Sin( 30 )
+ Fades[ 0 ][ 1 ] = 0.866f * BlobFade; // Cos( 30 )
+ Fades[ 1 ][ 0 ] = 0.866f * BlobFade; // Sin( 60 )
+ Fades[ 1 ][ 1 ] = 0.5f * BlobFade; // Cos( 60 )
+ Fades[ 2 ][ 0 ] = 0.866f * BlobFade; // Sin( 60 )
+ Fades[ 2 ][ 1 ] = 0.5f * BlobFade; // Cos( 60 )
+ Fades[ 3 ][ 0 ] = 0.866f * BlobFade; // Sin( 60 )
+ Fades[ 3 ][ 1 ] = 0.5f * BlobFade; // Cos( 60 )
+ Fades[ 4 ][ 0 ] = 0.866f * BlobFade; // Sin( 120 )
+ Fades[ 4 ][ 1 ] = -0.5f * BlobFade; // Cos( 120 )
+ Fades[ 5 ][ 0 ] = 0.5f * BlobFade; // Sin( 150 )
+ Fades[ 5 ][ 1 ] = -0.866f * BlobFade; // Cos( 150 )
+ }
+
+ camPos.Normalize();
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( BlobParams->GroundPos );
+ transform.FillHeading( BlobParams->GroundNormal, BlobParams->ShadowFacing );
+ transform.Row( 3 ).Add( camPos );
+ transform.Row(3).y += yOffset;
+ p3d::stack->PushMultiply( transform );
+
+ pddiShader* blobShader = BootupContext::GetInstance()->GetSharedShader();
+
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
+ }
+ else
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_MODULATE );
+ }
+ blobShader->SetInt( PDDI_SP_ISLIT, 0 );
+ blobShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ blobShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_GOURAUD );
+ pddiPrimStream* blob = p3d::pddi->BeginPrims( blobShader, PDDI_PRIM_TRIANGLES, PDDI_V_C, 6 * NumPoints );
+ for( int i = 0; i < NumPoints * 2; ++i )
+ {
+ int index = i;
+ int nextIndex = ( i + 1 ) % ( NumPoints * 2 );
+ int mirrorX = int( ( index * ( 1.0f / NumPoints ) ) );
+ int nextMirrorX = int( ( nextIndex * ( 1.0f / NumPoints ) ) );
+ index %= NumPoints;
+ nextIndex %= NumPoints;
+ index = rmt::Abs( ( mirrorX * 5 ) - index );
+ nextIndex = rmt::Abs( ( nextMirrorX * 5 ) - nextIndex );
+ mirrorX = 1 - ( mirrorX * 2 );
+ nextMirrorX = 1 - ( nextMirrorX * 2 );
+ int adjust = rmt::Clamp( index - 1, 0, NumPoints - 3 );
+ int nextAdjust = rmt::Clamp( nextIndex - 1, 0, NumPoints - 3 );
+
+ float x, y;
+ blob->Colour( insideColour );
+ blob->Coord( 0.0f, 0.0f, 0.0f );
+ x = ( Points[ nextIndex ][ 0 ] + mShadowPointAdjustments[ nextAdjust ][ 0 ] ) * BlobParams->ShadowScale;
+ y = ( Points[ nextIndex ][ 1 ] + mShadowPointAdjustments[ nextAdjust ][ 1 ] ) * BlobParams->ShadowScale;
+ blob->Colour( insideColour );
+ blob->Coord( x * nextMirrorX, y, 0.0f );
+ x = ( Points[ index ][ 0 ] + mShadowPointAdjustments[ adjust ][ 0 ] ) * BlobParams->ShadowScale;
+ y = ( Points[ index ][ 1 ] + mShadowPointAdjustments[ adjust ][ 1 ] ) * BlobParams->ShadowScale;
+ blob->Colour( insideColour );
+ blob->Coord( x * mirrorX, y, 0.0f );
+ }
+ p3d::pddi->EndPrims( blob );
+ // How the soft edge.
+ blob = p3d::pddi->BeginPrims( blobShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( ( 2 * NumPoints ) + 1 ) * 2 );
+ for( int i = 0; i <= NumPoints * 2; ++i )
+ {
+ int index = i;
+ int mirrorX = int( ( index * ( 1.0f / NumPoints ) ) ) % 2;
+ index %= NumPoints;
+ index = rmt::Abs( ( mirrorX * 5 ) - index );
+ mirrorX = 1 - ( mirrorX * 2 );
+ int adjust = rmt::Clamp( index - 1, 0, NumPoints - 3 );
+
+ float inX, inY;
+ float outX, outY;
+ inX = ( Points[ index ][ 0 ] + mShadowPointAdjustments[ adjust ][ 0 ] ) * BlobParams->ShadowScale;
+ inY = ( Points[ index ][ 1 ] + mShadowPointAdjustments[ adjust ][ 1 ] ) * BlobParams->ShadowScale;
+ inX *= mirrorX;
+ outX = inX + ( Fades[ index ][ 0 ] * mirrorX * BlobParams->ShadowScale );
+ outY = inY + ( Fades[ index ][ 1 ] * BlobParams->ShadowScale );
+ blob->Colour( OutsideColour );
+ blob->Coord( outX, outY, 0.0f );
+ blob->Colour( insideColour );
+ blob->Coord( inX, inY, 0.0f );
+ }
+ p3d::pddi->EndPrims( blob );
+ p3d::stack->Pop();
+ END_PROFILE("GeometryVehicle::DisplayShadow")
+}
+
+//=============================================================================
+// GeometryVehicle::DisplayLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: rmt::Vector& GroundPos - Position of car.
+// rmt::Vector& GroundNormal - Ground normal at car position.
+// rmt::Vector& GroundUp - Up direction at ground position
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DisplayLights( const HeadLightParams& LightParams )
+{
+ BEGIN_PROFILE("GeometryVehicle::DisplayLights")
+ const int NumPoints = 20;
+ const float LightXSize = 1.0f;
+ const float LightZSize = 0.125f;
+ const float FadeSize = 0.3f;
+ const float LightLength = NumPoints * LightZSize;
+ static bool DoOnce = true;
+ static float PointsX[ NumPoints ];
+ static tColour Colours[ NumPoints ];
+ static float FadePoints[ NumPoints ][ 2 ];
+ const tColour Colour( 65, 70, 46 );
+ const tColour Black( 0, 0, 0 );
+
+ if( DoOnce )
+ {
+ DoOnce = false;
+ // The light cone is made of of two tri strips one is the inner bright parabula
+ // which fades out with distance, and the other is the soft edge around it.
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float z = ( i + 1 ) * LightZSize;
+ float x = rmt::Sqrt( ( i + 1 ) * 0.5f );
+ PointsX[ i ] = x;
+ z -= LightLength;
+ float mag = rmt::Sqrt( ( x * x ) + ( z * z ) );
+ mag = ( ( mag != 0.0f ) ? 1.0f / mag : 0.0f ) * FadeSize;
+ x *= mag;
+ z *= mag;
+ FadePoints[ i ][ 0 ] = x;
+ FadePoints[ i ][ 1 ] = z;
+ float colourScale = ( 1.0f - ( (float)i / (float)NumPoints ) );
+ int red = rmt::Clamp( int( Colour.Red() * colourScale ), 0, 255 );
+ int green = rmt::Clamp( int( Colour.Green() * colourScale ), 0, 255 );
+ int blue = rmt::Clamp( int( Colour.Blue() * colourScale ), 0, 255 );
+ Colours[ i ].Set( red, green, blue );
+ }
+ // Now calculate the offset for the fade points.
+ }
+
+ float xScale = 1.0f;
+ float zScale = 1.0f;
+ float scale = 1.0f;
+
+ scale = LightParams.LightReach;
+ float offset = ( 1.0f - LightParams.LightReach ) * LightLength;
+
+ rmt::Vector camPos;
+ rmt::Vector groundPos = LightParams.GroundPos;
+ groundPos.ScaleAdd( offset, LightParams.VehicleHeading );
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ camera->GetWorldPosition( &camPos );
+ camPos.Sub( groundPos );
+ float cameraToLight = camPos.Magnitude();
+ camPos.Normalize();
+ rmt::Matrix toCamera;
+ toCamera.Identity();
+ toCamera.FillTranslate( camPos );
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( groundPos );
+ transform.FillHeading( LightParams.GroundNormal, LightParams.VehicleHeading );
+ transform.Mult( toCamera );
+ p3d::stack->PushMultiply( transform );
+
+ float dist = LightParams.VehicleHeading.Dot( LightParams.GroundNormal );
+ rmt::Vector groundByVehicle( LightParams.GroundNormal );
+ if( dist != 0.0f )
+ {
+ groundByVehicle.ScaleAdd( -dist, LightParams.VehicleHeading );
+ groundByVehicle.Normalize();
+ }
+ float vehicleRoll = groundByVehicle.Dot( LightParams.VehicleUp );
+ vehicleRoll *= vehicleRoll;
+#ifdef RAD_DEBUG
+ if( this->mVehicleOwner->mVehicleType != VT_TRAFFIC )
+ {
+ char text[128];
+ sprintf( text, "Ground to vehicle ratio: %.4f", vehicleRoll );
+ DebugInfo::GetInstance()->Push( "Vehicle lights" );
+ DebugInfo::GetInstance()->AddScreenText( text );
+ DebugInfo::GetInstance()->Pop();
+ }
+#endif
+
+ xScale *= scale * LightXSize * vehicleRoll;
+ zScale *= scale * LightZSize;
+
+ pddiShader* lightShader = BootupContext::GetInstance()->GetSharedShader();
+ lightShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ lightShader->SetInt( PDDI_SP_ISLIT, 0 );
+ lightShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ lightShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_GOURAUD );
+
+ // Center light.
+ pddiPrimStream* light = p3d::pddi->BeginPrims( lightShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( NumPoints << 1 ) + 1 );
+ light->Colour( Colour );
+ light->Coord( 0.0f, 0.0f, 0.0f );
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float inX = PointsX[ i ] * xScale;
+ float inZ = ( i + 1 ) * zScale;
+ light->Colour( Colours[ i ] );
+ light->Coord( inX, inZ, 0.0f );
+ light->Colour( Colours[ i ] );
+ light->Coord( -inX, inZ, 0.0f );
+ }
+ p3d::pddi->EndPrims( light );
+ // Fade on the negative side.
+ pddiPrimStream* nFade = p3d::pddi->BeginPrims( lightShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( NumPoints + 1 ) << 1 );
+ nFade->Colour( Black );
+ nFade->Coord( 0.0f, -FadeSize, 0.0f );
+ nFade->Colour( Colour);
+ nFade->Coord( 0.0f, 0.0f, 0.0f );
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float inX = -PointsX[ i ] * xScale;
+ float inZ = ( i + 1 ) * zScale;
+ float outX = inX - FadePoints[ i ][ 0 ];
+ float outZ = inZ + FadePoints[ i ][ 1 ];
+ nFade->Colour( Black );
+ nFade->Coord( outX, outZ, 0.0f );
+ nFade->Colour( Colours[ i ] );
+ nFade->Coord( inX, inZ, 0.0f );
+ }
+ p3d::pddi->EndPrims( nFade );
+
+ // Fade on the positive side.
+ pddiPrimStream* pFade = p3d::pddi->BeginPrims( lightShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( NumPoints + 1 ) << 1 );
+ pFade->Colour( Colour);
+ pFade->Coord( 0.0f, 0.0f, 0.0f );
+ pFade->Colour( Black );
+ pFade->Coord( 0.0f, -FadeSize, 0.0f );
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float inX = PointsX[ i ] * xScale;
+ float inZ = ( i + 1 ) * zScale;
+ float outX = inX + FadePoints[ i ][ 0 ];
+ float outZ = inZ + FadePoints[ i ][ 1 ];
+ pFade->Colour( Colours[ i ] );
+ pFade->Coord( inX, inZ, 0.0f );
+ pFade->Colour( Black );
+ pFade->Coord( outX, outZ, 0.0f );
+ }
+ p3d::pddi->EndPrims( pFade );
+
+ p3d::stack->Pop();
+ END_PROFILE("GeometryVehicle::DisplayLights")
+}
+
+//=============================================================================
+// GeometryVehicle::FindAndTurnOffWheels
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindAndTurnOffWheels()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "w%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ prop->SetVisibility(false);
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindAndTurnOffFrontWheelsOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindAndTurnOffFrontWheelsOnly()
+{
+ int i;
+ for(i = 2; i < 4; i++)
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "w%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ prop->SetVisibility(false);
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindHeadLightBillboardJoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindHeadLightBillboardJoints()
+{
+ // grab the pose
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ // grab the headlight bbqgs from common section
+
+ // put in left headlight
+ char buffy[128];
+ sprintf( buffy, "hll" );
+ int left = p3dPose->FindJointIndex( buffy );
+ if( left >= 0 )
+ {
+ for( int i=0; i<VehicleCentral::NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( GetVehicleCentral()->mHeadLights[i] );
+ mCompositeDrawable->AddProp( GetVehicleCentral()->mHeadLights[i], left );
+ }
+ }
+
+ // now the right side
+ sprintf( buffy, "hlr" );
+ int right = p3dPose->FindJointIndex( buffy );
+ if( right >= 0 )
+ {
+ for( int i=0; i<VehicleCentral::NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( GetVehicleCentral()->mHeadLights[i] );
+ mCompositeDrawable->AddProp( GetVehicleCentral()->mHeadLights[i], right );
+ }
+ }
+
+}
+//=============================================================================
+// GeometryVehicle::FindBrakeLightBillboardJoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindBrakeLightBillboardJoints()
+{
+ int i;
+ for(i = 1; i < 5; i++) // 'cause Kevin starts counting at 1, not 0
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "brake%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ //mBrakeLights[i - 1] = (tBillboardQuadGroup*) prop->GetDrawable();
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg != NULL )
+ {
+ tRefCounted::Assign( mBrakeLights[i - 1], bbqg );
+
+ // the structure for storing the original colors only supports 1 quad per quadgroup
+ rAssert( mBrakeLights[i-1]->GetNumQuads() == 1 );
+ tBillboardQuad* quad = mBrakeLights[i-1]->GetQuad( 0 );
+ mOriginalBrakeLightColours[i-1] = quad->GetColour();
+ mBrakeLightJoints[i - 1] = jointIndex;
+ }
+ }
+ }
+
+ }
+
+ for(i = 1; i < 5; i++) // 'cause Kevin starts counting at 1, not 0
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "rev%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ prop->SetVisibility(false);
+ mReverseLightJoints[i - 1] = jointIndex;
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindGhostGlowBillboards
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* model )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindGhostGlowBillboards( const char* model )
+{
+ int count = 0;
+ for( int i = 0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ char name[64];
+ sprintf( name, "%sGlow%dShape", model, i+1 );
+ tUID test = tEntity::MakeUID( name );
+
+ for( int j=0; j<mCompositeDrawable->GetNumDrawableElement(); j++ )
+ {
+ tCompositeDrawable::DrawablePropElement* prop =
+ (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+
+ if( prop && prop->GetUID() == test )
+ {
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg != NULL )
+ {
+ tRefCounted::Assign( mGhostGlows[i], bbqg );
+
+ for( int j=0; j<mGhostGlows[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mGhostGlows[i]->GetQuad( j );
+ rAssert( quad );
+ mOriginalGhostGlowColours[count] = quad->GetColour();
+ count++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindNukeGlowBillboards
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* model )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindNukeGlowBillboards( const char* model )
+{
+ int count = 0;
+ for( int i = 0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ char name[64];
+ sprintf( name, "nucGlowGroupShape" );
+ tUID test = tEntity::MakeUID( name );
+
+ for( int j=0; j<mCompositeDrawable->GetNumDrawableElement(); j++ )
+ {
+ tCompositeDrawable::DrawablePropElement* prop =
+ (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+
+ if( prop && prop->GetUID() == test )
+ {
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg != NULL )
+ {
+ tRefCounted::Assign( mNukeGlows[i], bbqg );
+
+ for( int j=0; j<mNukeGlows[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mNukeGlows[i]->GetQuad( j );
+ rAssert( quad );
+ mOriginalNukeGlowColours[count] = quad->GetColour();
+ count++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+//=============================================================================
+// GeometryVehicle::SetCollectibleHardpointPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& position )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetCollectibleHardpointPosition( const rmt::Vector& position )
+{
+ // Use the default orientation, but replace the translational compoonent
+ m_CollectibleTransform = DEFAULT_COLLECTIBLE_TRANSFORM;
+ m_CollectibleTransform.FillTranslate( position );
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetCollectibleHardpointTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rotx ,float roty ,float rotz, const rmt::Vector& position )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetCollectibleHardpointTransform( float rotx ,float roty ,float rotz,
+ const rmt::Vector& position )
+{
+ m_CollectibleTransform.Identity();
+ m_CollectibleTransform.FillRotateXYZ( rotx, roty, rotz );
+ m_CollectibleTransform.FillTranslate( position );
+}
+
+
+//=============================================================================
+// GeometryVehicle::ShowBrakeLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::ShowBrakeLights()
+{
+ mBrakeLightsOn = true;
+ /*
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mBrakeLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mBrakeLightJoints[i], false);
+ }
+ }
+ */
+}
+
+//=============================================================================
+// GeometryVehicle::HideBrakeLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::HideBrakeLights()
+{
+ mBrakeLightsOn = false;
+ /*
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mBrakeLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mBrakeLightJoints[i], true);
+ }
+ }
+ */
+}
+
+
+//=============================================================================
+// GeometryVehicle::ShowReverseLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::ShowReverseLights()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mReverseLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mReverseLightJoints[i], false);
+ }
+ }
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::HideReverseLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::HideReverseLights()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mReverseLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mReverseLightJoints[i], true);
+ }
+ }
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetArt
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name)
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::GetArt( const char* name)
+{
+ // expect a composite drawable named "name" in the file.
+
+ tCompositeDrawable* master = p3d::find<tCompositeDrawable>(name);
+ rTuneAssert( master != NULL );
+
+#ifndef FINAL
+ if (master == NULL)
+ {
+ char buffer [255];
+ sprintf(buffer,"Script ERROR: Can't Find Vehicle:%s Make sure you loaded it!\n",name);
+ rTuneAssertMsg(0,buffer);
+ }
+#endif
+
+ mCompositeDrawable = master->Clone();
+
+ //mCompositeDrawable = p3d::find<tCompositeDrawable>(name);
+ rAssert(mCompositeDrawable);
+ mCompositeDrawable->AddRef();
+
+ {
+ char controllerName[255];
+ strcpy( controllerName, "EFX_" );
+ strcat( controllerName, name );
+ strcat( controllerName, "velocpartSystShape" );
+
+ tEffectController* particleSysController = p3d::find < tEffectController >( controllerName );
+ if ( particleSysController != NULL )
+ {
+ tEffect* effect = particleSysController->GetEffect();
+ rAssert( dynamic_cast< tParticleSystem* >( effect ) != NULL );
+ tParticleSystem* particleSystem = static_cast< tParticleSystem* > ( effect );
+ mVariableEmissionParticleSystem = particleSystem;
+ mVariableEmissionParticleSystem->AddRef();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+ // Traffic "swatches" (in a sense)
+
+ // Any car using traffic model (regardless of whether they are in fact traffic cars)
+ // Must contain a trafficbodydrawable so that it will render correctly (due to fading
+ // and swatch tricks done for traffic models).
+
+ if( IsTrafficModel() )
+ {
+ mUsingTrafficModel = true;
+
+ // Grab the shader to be used for swatches
+ char shadername[64];
+ sprintf( shadername, "%s_m", name );
+ tShader* bodyShade = p3d::find<tShader>( shadername );
+
+ // Find the body prop in compositedrawable's list of elements by name
+ // e.g. compactAShape
+ char propname[64];
+ sprintf( propname, "%sShape", name );
+ tUID uid = tEntity::MakeUID(propname);
+
+ int poseIndex = -1;
+ tCompositeDrawable::DrawablePropElement* body = NULL;
+
+ for( int i=0; i<mCompositeDrawable->GetNumDrawableElement(); i++ )
+ {
+ body = (tCompositeDrawable::DrawablePropElement*) (mCompositeDrawable->GetDrawableElement(i));
+ if( body != NULL )
+ {
+ if( body->GetDrawable()->GetUID() == uid )
+ {
+ poseIndex = i;
+ break;
+ }
+ }
+ }
+
+ if( poseIndex > -1 && body != NULL )
+ {
+ // create a trafficbodydrawable
+ tRefCounted::Assign( mTrafficBodyDrawable, new TrafficBodyDrawable() );
+ rAssert( mTrafficBodyDrawable );
+
+ // store away the body prop's old drawable
+ rAssert( body->GetDrawable() != NULL );
+ mTrafficBodyDrawable->SetBodyPropDrawable( body->GetDrawable() );
+
+ // Grab the shader for the vehicle chassis and store it too
+ if( bodyShade == NULL )
+ {
+ rDebugPrintf( "Warning: Couldn't find shader \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ shadername, name );
+ }
+ mTrafficBodyDrawable->SetBodyShader( bodyShade );
+
+ // make the new traffic body drawable the drawable for the body prop
+ body->SetDrawable( mTrafficBodyDrawable );
+ }
+ else
+ {
+ rDebugPrintf( "Warning: Couldn't find tCompositeDrawable prop \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ propname, name );
+ }
+
+ // Find the door prop in compositedrawable's list of elements by name
+ // e.g. compactAShape
+ sprintf( propname, "doorPRotShape" );
+ uid = tEntity::MakeUID(propname);
+
+ poseIndex = -1;
+ body = NULL;
+ for( int i=0; i<mCompositeDrawable->GetNumDrawableElement(); i++ )
+ {
+ body = (tCompositeDrawable::DrawablePropElement*)
+ (mCompositeDrawable->GetDrawableElement(i));
+ if( body != NULL )
+ {
+ if( body->GetDrawable()->GetUID() == uid )
+ {
+ poseIndex = i;
+ break;
+ }
+ }
+ }
+
+ if( poseIndex > -1 && body != NULL )
+ {
+ // create a trafficbodydrawable
+ tRefCounted::Assign( mTrafficDoorDrawable, new TrafficBodyDrawable() );
+ rAssert( mTrafficDoorDrawable );
+
+ // store away the body prop's old drawable
+ rAssert( body->GetDrawable() != NULL );
+ mTrafficDoorDrawable->SetBodyPropDrawable( body->GetDrawable() );
+
+ // Grab the shader for the vehicle chassis and store it too
+ if( bodyShade == NULL )
+ {
+ rDebugPrintf( "Warning: Couldn't find shader \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ shadername, name );
+ }
+ mTrafficDoorDrawable->SetBodyShader( bodyShade );
+
+ // make the new traffic body drawable the drawable for the body prop
+ body->SetDrawable( mTrafficDoorDrawable );
+ }
+ else
+ {
+ rDebugPrintf( "Warning: Couldn't find tCompositeDrawable prop \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ propname, name );
+ }
+
+ }
+ // is this ok for release?
+ // I think so
+
+ // little hack just for frink
+ // no wheels
+ if( strcmp(name, "frink_v") == 0 || strcmp(name, "honor_v") == 0 || strcmp(name, "hbike_v") == 0 ||
+ strcmp(name, "witchcar") == 0 || strcmp(name, "ship") == 0 || strcmp(name, "mono_v") == 0 )
+ {
+ FindAndTurnOffWheels();
+ }
+
+ if( strcmp( name, "frink_v" ) == 0 )
+ {
+ int count = 0;
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "special_m" ) ); //must be first
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vInt_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vTrim_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vDoorPNorm_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vExtras_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vHoodNorm_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vVent_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vcoil_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "BottomA_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vDoorDNorm_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "engine_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vBackNorm_m" ) );
+
+ int size = count;
+ int i;
+ for( i = 0; i < size; ++i )
+ {
+ rAssert( mRefractionShader[ i ] != NULL );
+ }
+
+#ifdef RAD_WIN32
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "frinkArcGroup", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ for(int j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg )
+ {
+ tRefCounted::Assign( mFrinkArc, bbqg );
+ for( int k = 0; k < NUM_FRINKARC_BBQS; k++ )
+ {
+ tBillboardQuad* quad = mFrinkArc->GetQuad( k );
+ mOriginalFrinkArcColour[k] = quad->GetColour();
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ if(strcmp(name, "rocke_v") == 0)
+ {
+ this->FindAndTurnOffFrontWheelsOnly();
+ }
+
+ // store brake light joint indices
+ FindBrakeLightBillboardJoints();
+ FindHeadLightBillboardJoints();
+
+ if( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ mHeadLightScale = 1.0f;
+ mBrakeLightScale = 0.4f;
+ if( mVehicleOwner->mVehicleType == VT_USER || mVehicleOwner->mVehicleType == VT_AI )
+ {
+ EnableLights( true );
+ }
+ }
+ else
+ {
+ // if we are on a level other than 4 or 7, we want to turn the headlights off
+ if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L1 || // BROAD DAYLIGHT
+ GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L2 )
+ {
+ // Which values should we use for headlights and brakelights
+ // when we just have running lights (when not braking)
+ mHeadLightScale = 0.0f;
+ mBrakeLightScale = 0.0f;//0.4f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L3 ) // SUNSET
+ {
+ mHeadLightScale = 0.2f;
+ mBrakeLightScale = 0.3f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L5 ) // DUSK
+ {
+ mHeadLightScale = 0.4f;
+ mBrakeLightScale = 0.4f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L6 ) // TWILIGHT
+ {
+ mHeadLightScale = 0.4f;
+ mBrakeLightScale = 0.4f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L4 || // NIGHT
+ GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L7 )
+ {
+ // full headlights on night levels
+ mHeadLightScale = 0.6f;
+ mBrakeLightScale = 0.4f;
+ }
+ if( mVehicleOwner->mVehicleType == VT_USER )
+ {
+ EnableLights( false );
+ }
+ }
+
+
+
+ // hack for ghost ship
+ if( strcmp(name, "ship") == 0 )
+ {
+ mHasGhostGlow = true;
+ FindGhostGlowBillboards( name );
+ }
+
+ if( strcmp( name, "nuctruck" ) == 0 )
+ {
+ mHasNukeGlow = true;
+ FindNukeGlowBillboards( name );
+ }
+
+
+ // TODO - might need to clone this too
+// mAnimController = p3d::find<tPoseAnimationController>(buffy);
+
+ // We will advance the multicontroller, not just the posecontroller
+
+ FindAnimationControllers( name );
+
+ char buffy[128];
+ sprintf(buffy, "PTRN_%s", name);
+
+ tPoseAnimationController* poseController = p3d::find<tPoseAnimationController>(buffy);
+
+ // only play an anim if it's there:
+
+ if( poseController )
+ {
+ poseController->SetPose(mCompositeDrawable->GetPose());
+
+ // TODO
+ // cast here might be unsafe in the general case
+ //mAnim = (tPoseAnimation*)(poseController->GetAnimation());
+ mAnim = poseController->GetAnimation();
+ mAnim->AddRef(); // ? already addref'd by controller?
+ mAnim->SetCyclic(true);
+
+ float numFrames = mAnim->GetNumFrames();
+ float speed = mAnim->GetSpeed();
+
+ rAssert( numFrames != 0 );
+
+ mAnimRevPerSecondBase = speed / (float)numFrames;
+ }
+
+ char roofShaderName[64];
+ sprintf( roofShaderName, "%sRoof_m", name );
+ tRefCounted::Assign( mRoofShader, p3d::find<tShader>( tEntity::MakeUID( roofShaderName ) ) );
+
+ FindRoofGeometry( name );
+ if( mRoofOpacShape && mRoofAlphaShape )
+ {
+ mRoofOpacShape->SetVisibility( true );
+ mRoofAlphaShape->SetVisibility( false );
+ }
+
+ if ( mVehicleOwner->GetNameObject() == "gramR_v" )
+ {
+ SetCollectibleHardpointPosition( JEEP_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "homer_v" )
+ {
+ SetCollectibleHardpointPosition( BARRACUDA_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "dune_v" )
+ {
+ SetCollectibleHardpointTransform( DUNE_COLLECTIBLE_ROT.x,
+ DUNE_COLLECTIBLE_ROT.y,
+ DUNE_COLLECTIBLE_ROT.z,
+ DUNE_COLLECTIBLE_POS);
+ }
+ else if ( mVehicleOwner->GetNameObject() == "hallo" )
+ {
+ SetCollectibleHardpointPosition( HALLO_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "coffin" )
+ {
+ SetCollectibleHardpointPosition( COFFIN_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "witchcar" )
+ {
+ SetCollectibleHardpointPosition( WITCH_COLLECTIBLE_POS );
+ }
+
+ if(mVehicleOwner->mVehicleType == VT_USER)
+ {
+ tCompositeDrawable::DrawableElement* agnes = mCompositeDrawable->FindNode("agnusGeoShape");
+ if(agnes)
+ {
+ if(GetVehicleCentral()->IsDriverSuppressed("skinner"))
+ {
+ agnes->SetVisibility(false);
+ }
+ }
+ }
+
+ //THERE'S AN UNTRACKED ALLOCATION HERE!
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+
+
+ return FindDamageShadersAndTextures(name);
+ //return true;
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindDamageShadersAndTextures
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name)
+//
+// Return: void
+//
+//=============================================================================
+bool GeometryVehicle::FindDamageShadersAndTextures( const char* carname)
+{
+ // names we expect to have:
+ //
+ // for shaders
+ // <carname>DoorDNorm_m
+ // <carname>DoorPNorm_m
+ // <carname>HoodNorm_m
+ // <carname>BackNorm_m
+
+
+ // for normal textures:
+ // <carname>DoorDNorm.bmp
+ // <carname>DoorPNorm.bmp
+ // <carname>HoodNorm.bmp
+ // <carname>BackNorm.bmp current: BarTrunk.bmp
+
+
+ // for damage textures:
+ // <carname>DoorPDam.bmp current: BarracudaDoorPDam.bmp
+ // <carname>HoodDam.bmp current: BarracudaHoodDam.bmp
+ // <carname>DoorDDam.bmp current: BarracudaDoorDDam.bmp
+ // <carname>BackDam.bmp current: BarracudaBackDam.bmp
+
+ // !!
+ // TODO - search a particular inventory section only?
+
+ char buffy[128];
+
+
+ // change logic here - if we find at least one set of shader, normal texture, damage texture
+ // return true.
+
+
+
+ bool result = false;
+
+ bool doorD = true;
+ bool doorP = true;
+ bool hood = true;
+ bool trunk = true;
+
+
+ //-------
+ //shaders
+ //-------
+
+ sprintf(buffy, "%sDoorDNorm_m", carname);
+ mDoorDShader = p3d::find<tShader>(buffy);
+ if(mDoorDShader)
+ {
+ mDoorDShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorD = false;
+ }
+
+ sprintf(buffy, "%sDoorPNorm_m", carname);
+ mDoorPShader = p3d::find<tShader>(buffy);
+ if(mDoorPShader)
+ {
+ mDoorPShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorP = false;
+ }
+
+
+ sprintf(buffy, "%sHoodNorm_m", carname);
+ mHoodShader = p3d::find<tShader>(buffy);
+ if(mHoodShader)
+ {
+ mHoodShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ hood = false;
+ }
+
+
+ sprintf(buffy, "%sBackNorm_m", carname);
+ mTrunkShader = p3d::find<tShader>(buffy);
+ if(mTrunkShader)
+ {
+ mTrunkShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ trunk = false;
+ }
+
+
+
+ //----------------
+ // normal textures
+ //----------------
+
+ sprintf(buffy, "%sDoorDNorm.bmp", carname);
+ mDoorDTextureNorm = p3d::find<tTexture>(buffy);
+ if(mDoorDTextureNorm)
+ {
+ mDoorDTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorD = false;
+ }
+
+
+ sprintf(buffy, "%sDoorPNorm.bmp", carname);
+ mDoorPTextureNorm = p3d::find<tTexture>(buffy);
+ if(mDoorPTextureNorm)
+ {
+ mDoorPTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorP = false;
+ }
+
+
+ sprintf(buffy, "%sHoodNorm.bmp", carname);
+ mHoodTextureNorm = p3d::find<tTexture>(buffy);
+ if(mHoodTextureNorm)
+ {
+ mHoodTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ hood = false;
+ }
+
+
+ sprintf(buffy, "%sBackNorm.bmp", carname);
+ mTrunkTextureNorm = p3d::find<tTexture>(buffy);
+ if(mTrunkTextureNorm)
+ {
+ mTrunkTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ trunk = false;
+ }
+
+
+
+ //----------------
+ // damage textures
+ //----------------
+
+ sprintf(buffy, "%sDoorDDam.bmp", carname);
+ mDoorDTextureDam = p3d::find<tTexture>(buffy);
+ if(mDoorDTextureDam)
+ {
+ mDoorDTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorD = false;
+ }
+
+
+ sprintf(buffy, "%sDoorPDam.bmp", carname);
+ mDoorPTextureDam = p3d::find<tTexture>(buffy);
+ if(mDoorPTextureDam)
+ {
+ mDoorPTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorP = false;
+ }
+
+
+ sprintf(buffy, "%sHoodDam.bmp", carname);
+ mHoodTextureDam = p3d::find<tTexture>(buffy);
+ if(mHoodTextureDam)
+ {
+ mHoodTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ hood = false;
+ }
+
+
+ sprintf(buffy, "%sBackDam.bmp", carname);
+ mTrunkTextureDam = p3d::find<tTexture>(buffy);
+ if(mTrunkTextureDam)
+ {
+ mTrunkTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ trunk = false;
+ }
+
+
+ // now if any of doorD, doorP, hood, or trunk are true - return true
+
+ result = doorD | doorP | hood | trunk;
+
+
+
+ //----------------------------------------------------
+ // all of the above was if we are a type 1 or 2 damage
+ //
+ // for type 3 we just expect the following:
+ //----------------------------------------------------
+
+ //-------
+ // shader
+ //-------
+ /*
+
+
+ // ancient fucking history
+
+
+ sprintf(buffy, "%sChassisNorm_m", carname);
+ mChassisShader = p3d::find<tShader>(buffy);
+ if(mChassisShader)
+ {
+ mChassisShader->AddRef();
+ }
+
+ //---------------
+ // normal texture
+ //---------------
+
+ sprintf(buffy, "%sChassisNorm.bmp", carname);
+ mChassisTextureNorm = p3d::find<tTexture>(buffy);
+ if(mChassisTextureNorm)
+ {
+ mChassisTextureNorm->AddRef();
+ }
+
+ //---------------
+ // damage texture
+ //---------------
+
+ sprintf(buffy, "%sChassisDam.bmp", carname);
+ mChassisTextureDam = p3d::find<tTexture>(buffy);
+ if(mChassisTextureDam)
+ {
+ mChassisTextureDam->AddRef();
+ }
+ */
+
+
+ return result;
+
+}
+
+
+
+//=============================================================================
+// GeometryVehicle::SetSmoke
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (ParticleEnum::ParticleID pid)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetEngineSmoke(ParticleEnum::ParticleID pid)
+{
+ mEngineParticleAttr.mType = pid;
+}
+
+
+/*
+ParticleEnum::ParticleID mType;
+
+ // only one bias so far, the emission bias
+ // other biases would be simple to put in
+ // NumParticles/Life/Speed/Weight/Gravity/Drag/Size/Spin
+ float mEmissionBias;
+*/
+
+
+//=============================================================================
+// GeometryVehicle::SetWheelSmoke
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (ParticleEnum::ParticleID pid, float bias)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetWheelSmoke( int wheel, ParticleEnum::ParticleID pid, float bias)
+{
+ switch( wheel )
+ {
+ case 0:
+ mRightWheelParticleAttr.mType = pid;
+ mRightWheelParticleAttr.mEmissionBias = bias;
+ break;
+ case 1:
+ mLeftWheelParticleAttr.mType = pid;
+ mLeftWheelParticleAttr.mEmissionBias = bias;
+ break;
+ default:
+ mRightWheelParticleAttr.mType = pid;
+ mRightWheelParticleAttr.mEmissionBias = bias;
+
+ mLeftWheelParticleAttr.mType = pid;
+ mLeftWheelParticleAttr.mEmissionBias = bias;
+ break;
+ };
+}
+
+
+//=============================================================================
+// GeometryVehicle::DamageTextureChassis
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureChassis(bool on)
+{
+ rAssert(0); // this is fucking history
+ if(mChassisShader)
+ {
+ if(on)
+ {
+ mChassisShader->SetTexture(PDDI_SP_BASETEX, mChassisTextureDam);
+ }
+ else
+ {
+ mChassisShader->SetTexture(PDDI_SP_BASETEX, mChassisTextureNorm);
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::DamageTextureTrunk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureTrunk(bool on)
+{
+ if(mTrunkShader && mTrunkTextureDam && mTrunkTextureNorm)
+ {
+ if(on)
+ {
+ mTrunkShader->SetTexture(PDDI_SP_BASETEX, mTrunkTextureDam);
+ }
+ else
+ {
+ mTrunkShader->SetTexture(PDDI_SP_BASETEX, mTrunkTextureNorm);
+ }
+ }
+}
+
+//=============================================================================
+// GeometryVehicle::DamageTextureHood
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureHood(bool on)
+{
+ if(mHoodShader && mHoodTextureDam && mHoodTextureNorm)
+ {
+ if(on)
+ {
+ mHoodShader->SetTexture(PDDI_SP_BASETEX, mHoodTextureDam);
+ }
+ else
+ {
+ mHoodShader->SetTexture(PDDI_SP_BASETEX, mHoodTextureNorm);
+ }
+ }
+}
+
+//=============================================================================
+// GeometryVehicle::DamageTextureDoorP
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return:
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureDoorP(bool on)
+{
+ if(mDoorPShader && mDoorPTextureDam && mDoorPTextureNorm)
+ {
+ if(on)
+ {
+ mDoorPShader->SetTexture(PDDI_SP_BASETEX, mDoorPTextureDam);
+ }
+ else
+ {
+ mDoorPShader->SetTexture(PDDI_SP_BASETEX, mDoorPTextureNorm);
+ }
+ }
+}
+
+//=============================================================================
+// GeometryVehicle::DamageTextureDoorD
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureDoorD(bool on)
+{
+ if(mDoorDShader && mDoorDTextureDam && mDoorDTextureNorm)
+ {
+ if(on)
+ {
+ mDoorDShader->SetTexture(PDDI_SP_BASETEX, mDoorDTextureDam);
+ }
+ else
+ {
+ mDoorDShader->SetTexture(PDDI_SP_BASETEX, mDoorDTextureNorm);
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::HideFlappingPiece
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int jointindex)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::HideFlappingPiece(int jointindex, bool doit)
+{
+
+ //tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(jointindex));
+
+ int i;
+ for(i = 0; i < mCompositeDrawable->GetNumDrawableElement(); i++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(i));
+ if(prop && prop->GetPoseIndex() == jointindex)
+ {
+ //prop->SetVisibility(false);
+ prop->SetVisibility(!doit);
+ }
+ }
+
+}
+
+
+//------------------------------------------------------------------------
+void GeometryVehicle::Update(float dt)
+{
+ AdvanceAnimationControllers( dt * 1000.0f );
+
+
+
+ rmt::Vector vehicleVelocity;
+ mVehicleOwner->GetVelocity( &vehicleVelocity );
+ // Certain animations and particle systems are scaled by the velocity
+ // Calculate what % the scalar should be
+ float animationSpeedBias = vehicleVelocity.Magnitude() / PARTICLE_SYSTEM_MAX_VELOCITY;
+ animationSpeedBias = animationSpeedBias > 1.0f ? 1.0f : animationSpeedBias;
+ animationSpeedBias = animationSpeedBias < 0.0f ? 0.0f : animationSpeedBias;
+
+ if ( mVariableEmissionParticleSystem != NULL )
+ {
+ mVariableEmissionParticleSystem->SetBias( p3dParticleSystemConstants::EmitterBias::EMISSION, animationSpeedBias );
+ }
+
+
+ //
+ // Vary prof frink's refraction shader every frame
+ //
+
+ const tName& name = mVehicleOwner->GetNameObject();
+ if( name == "frink_v" )
+ {
+ float topSpeed = mVehicleOwner->GetTopSpeed();
+ float percentOfTopSpeed = vehicleVelocity.Magnitude() / topSpeed;
+ float refraction = percentOfTopSpeed;
+ refraction = rmt::Clamp( refraction, 0.0f, 1.0f );
+
+ /*
+ //we adjust the refractive index based on distance from the camera
+ rmt::Vector pos = mVehicleOwner->GetPosition ();
+ rmt::Vector cameraPosition;
+ unsigned int index = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCamIndex();
+ SuperCam* camera = GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( index );
+ camera->GetPosition( &cameraPosition );
+ float distance = ( pos - cameraPosition ).Magnitude();
+
+ float refractionAmount = refractiveIndex;
+ //if( distance > 20 )
+ {
+ refractionAmount *= 5;
+ refractionAmount /= distance;
+ }
+ */
+
+
+ //
+ // special case for the windshield shader
+ //
+ if( mRefractionShader[ 0 ] != NULL )
+ {
+ #ifdef RAD_GAMECUBE
+ float adjustedRefraction = refraction;
+ #else
+ float adjustedRefraction = 0.5f + (0.5f * refraction);
+ #endif
+
+ #ifndef RAD_WIN32
+ mRefractionShader[ 0 ]->SetFloat( PDDI_SP_REFRACTBLEND, adjustedRefraction );
+ mRefractionShader[ 0 ]->SetFloat( PDDI_SP_REFRACTINDEX, refractiveIndex );
+ #endif
+ }
+
+ //
+ // all other shaders
+ //
+ int i;
+ for( i = 1; i < MAX_REFRACTION_SHADERS; ++i )
+ {
+ if( mRefractionShader[ i ] != NULL )
+ {
+ #ifndef RAD_WIN32
+ mRefractionShader[ i ]->SetFloat( PDDI_SP_REFRACTBLEND, refraction );
+ mRefractionShader[ i ]->SetFloat( PDDI_SP_REFRACTINDEX, refractiveIndex );
+ #endif
+ }
+ }
+ }
+
+ // Update the collectible's position
+ if ( m_Collectible != NULL )
+ {
+ rmt::Matrix collectibleTransform;
+ collectibleTransform.Mult( m_CollectibleTransform, mVehicleOwner->mTransform );
+ m_Collectible->SetTransform( collectibleTransform );
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetTrafficBodyColour
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( pddiColour colour )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetTrafficBodyColour( pddiColour colour )
+{
+ if( mVehicleOwner->mVehicleType == VT_TRAFFIC || mVehicleOwner->mVehicleType == VT_USER )
+ {
+ if( mTrafficBodyDrawable != NULL )
+ {
+ mTrafficBodyDrawable->SetDesiredColour( colour );
+ }
+ if( mTrafficDoorDrawable != NULL )
+ {
+ mTrafficDoorDrawable->SetDesiredColour( colour );
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetP3DPose
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tPose
+//
+//=============================================================================
+tPose* GeometryVehicle::GetP3DPose()
+{
+ if(mCompositeDrawable)
+ {
+ return mCompositeDrawable->GetPose();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetShadowAdjustments
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float Adjustments[ 4 ][ 2 ] )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] )
+{
+ for( int i = 0; i < 4; ++i )
+ {
+ mShadowPointAdjustments[ i ][ 0 ] = Adjustments[ i ][ 0 ];
+ mShadowPointAdjustments[ i ][ 1 ] = Adjustments[ i ][ 1 ];
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetFadeAlpha
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int fade )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetFadeAlpha( int fade )
+{
+ rAssert( 0 <= fade && fade <= 255 );
+ mFadeAlpha = fade;
+}
+
+// Hack:
+// We hard code the traffic models so we can tell which vehicle
+// needs special treatment in terms of fading... Be sure to add
+// any new traffic models here or they won't fade properly.
+//
+// The problem is that the intbroadcast on all the shaders in
+// this composite drawable is going to reset everyone's blend
+// mode to BLEND_ALPHA... Brakelights, however, need BLEND_ADDITIVE
+// and some cars (cCells, cPolice) have special light effects that
+// have similar problems. Setting these all to BLEND_ALPHA will
+// cause the billboards to not display properly, so we make sure
+// we do an intbroadcast (using ProcessShaders()) only for cars
+// that are using traffic models (including non-traffic cars such
+// as mission AI cars).
+//
+// The alternative to this is to avoid using ProcessShaders() and
+// set the appropriate shader for each composite drawable's element
+// that need to fade independently... Could be messy...
+// This hard-coded list is not so bad..
+//
+//=============================================================================
+// GeometryVehicle::IsTrafficModel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::IsTrafficModel()
+{
+ rAssert( mVehicleOwner );
+
+ VehicleEnum::VehicleID ID = mVehicleOwner->mVehicleID;
+
+ if( ID == VehicleEnum::HUSKA ||
+
+ ID == VehicleEnum::COMPACTA ||
+ ID == VehicleEnum::PICKUPA ||
+ ID == VehicleEnum::MINIVANA ||
+ ID == VehicleEnum::SUVA ||
+ ID == VehicleEnum::SPORTSA ||
+
+ ID == VehicleEnum::SPORTSB ||
+ ID == VehicleEnum::SEDANA ||
+ ID == VehicleEnum::SEDANB ||
+ ID == VehicleEnum::TAXIA ||
+ ID == VehicleEnum::WAGONA ||
+
+ ID == VehicleEnum::COFFIN ||
+ ID == VehicleEnum::HALLO ||
+ ID == VehicleEnum::WITCHCAR ||
+ ID == VehicleEnum::SHIP ||
+ ID == VehicleEnum::AMBUL ||
+
+ ID == VehicleEnum::BURNSARM ||
+ ID == VehicleEnum::FISHTRUC ||
+ ID == VehicleEnum::GARBAGE ||
+ ID == VehicleEnum::ICECREAM ||
+ ID == VehicleEnum::ISTRUCK ||
+
+ ID == VehicleEnum::NUCTRUCK ||
+ ID == VehicleEnum::PIZZA ||
+ ID == VehicleEnum::SCHOOLBU ||
+ ID == VehicleEnum::VOTETRUC ||
+ ID == VehicleEnum::GLASTRUC )
+
+ {
+ return true;
+ }
+ return false;
+}
+
+
+//=============================================================================
+// GeometryVehicle::FadeRoof
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool fade )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FadeRoof( bool fade )
+{
+ rAssert( 0 <= INCAR_ROOF_ALPHA && INCAR_ROOF_ALPHA <= 255 );
+ mRoofTargetAlpha = (fade)? INCAR_ROOF_ALPHA : 255;
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindRoofGeometry
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* model )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindRoofGeometry( const char* model )
+{
+ char opac[64];
+ sprintf(opac, "%sRoofOpacShape", model );
+ tUID opacID = tEntity::MakeUID( opac );
+
+ char alpha[64];
+ sprintf(alpha, "%sRoofAlphaShape", model );
+ tUID alphaID = tEntity::MakeUID( alpha );
+
+ for( int j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop =
+ (tCompositeDrawable::DrawablePropElement*)
+ (mCompositeDrawable->GetDrawableElement(j));
+
+ if( prop )
+ {
+ tUID uid = prop->GetUID();
+ if( uid == opacID )
+ {
+ mRoofOpacShape = prop;
+ }
+ else if( uid == alphaID )
+ {
+ mRoofAlphaShape = prop;
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::EnableLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool enable )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::EnableLights( bool enable )
+{
+ mEnableLights = enable;
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetVehicleColour
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tColour
+//
+//=============================================================================
+tColour GeometryVehicle::GetVehicleColour()const
+{
+ if ( mTrafficBodyDrawable != NULL )
+ {
+ return mTrafficBodyDrawable->GetDesiredColour();
+ }
+ else
+ {
+ const tColour DEFAULT_COLOUR( 128,128,128 );
+ return DEFAULT_COLOUR;
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindAnimationControllers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* multicontrollername )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindAnimationControllers( const char* multicontrollername )
+{
+
+ tMultiController* mc = p3d::find< tMultiController >( multicontrollername );
+ if( mc == NULL )
+ return;
+
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+
+ // Iterate through the frame controllers and add them to our own list
+ for ( unsigned int i = 0 ; i < mc->GetNumTracks() ; i++ )
+ {
+ tFrameController* fc = mc->GetTrack( i )->Clone();
+
+ VehicleFrameController vfc;
+
+ vfc.trigger = eNone;
+ vfc.brakeBias = vfc.gasBias = vfc.speedBias = 0;
+ vfc.timeBias = 1.0f;
+ vfc.frameSetter = false;
+
+ GetSpecialController( fc->GetUID(), &vfc );
+ vfc.frameController = fc;
+ vfc.frameController->AddRef();
+ vfc.frameController->Reset();
+ vfc.frameController->SetCycleMode( FORCE_CYCLIC );
+
+ // What kind of animation controller are we dealing with?
+ // Possibly a pose controller?
+ tPoseAnimationController* poseController = dynamic_cast< tPoseAnimationController* >( vfc.frameController );
+ if ( poseController )
+ {
+ // The compositeDrawble::clone function allocates a brand new pose
+ // set the frame controller that this pose is the one to update upon
+ // Advance()
+ poseController->SetPose( mCompositeDrawable->GetPose() );
+
+ }
+
+ mFrameControllers.push_back( vfc );
+ }
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetSpecialController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tUID name, VehicleFrameController* outSpecialController )
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::GetSpecialController( tUID name, VehicleFrameController* outSpecialController )
+{
+ bool wasFound = false;
+ // iterate through the preset "special" controllers and find any with the same name
+ for ( int i = 0 ; i < NUM_FRAME_CONTROLLERS ; i++ )
+ {
+ if ( name == tName::MakeUID( FRAME_CONTROLLER_DATA[i].name ) )
+ {
+ *outSpecialController = FRAME_CONTROLLER_DATA[i];
+ wasFound = true;
+ break;
+ }
+ }
+ return wasFound;
+}
+
+
+//=============================================================================
+// GeometryVehicle::AdvanceAnimationControllers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float deltaTime )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::AdvanceAnimationControllers( float deltaTime )
+{
+ int numFCs = mFrameControllers.size();
+
+ float gas = mVehicleOwner->GetGas();
+ float speed = mVehicleOwner->GetSpeedKmh();
+ float brake = mVehicleOwner->GetBrake();
+
+ if ( mVehicleOwner->IsMovingBackward() )
+ speed *= -1;
+
+ FCTrigger trigger = eNone;
+
+ if ( mVehicleOwner->GetDeltaGas() > 0.001f )
+ {
+ trigger = eBackfire;
+ }
+ for ( int i = 0 ; i < numFCs ; i++ )
+ {
+ VehicleFrameController& vfc = mFrameControllers[i];
+ if ( vfc.frameSetter )
+ {
+ // This thing doesn't ever run linearly, it only sets the frame
+ // it uses specifically.
+ // Figure out which frame to set based upon vehicle attributes
+ float relativeframe;
+ float numFrames = vfc.frameController->GetNumFrames();
+ relativeframe = vfc.brakeBias * brake + vfc.gasBias * gas +
+ vfc.speedBias * speed;
+
+ vfc.frameController->SetFrame( relativeframe * numFrames );
+ vfc.frameController->Advance( 0.0f );
+ }
+ else
+ {
+ // Is the animation triggered by something?
+ // Check to see if it is or isn't
+ if ( trigger != eNone && trigger == vfc.trigger )
+ {
+ // Its a triggered animation thats been triggered
+ // If the animation isnt playing already then restart it
+ if ( vfc.frameController->GetRelativeSpeed() < 0.001f )
+ {
+ vfc.frameController->Reset();
+ vfc.frameController->SetRelativeSpeed( 1.0f );
+ }
+ if ( vfc.frameController->LastFrameReached() )
+ {
+ vfc.frameController->SetRelativeSpeed( 0 );
+ }
+ }
+ // Standard animation, apply biases and play
+ float advanceTime = vfc.timeBias * deltaTime;
+ if ( advanceTime < 0 ) advanceTime = 0;
+
+ float advanceGas = gas * vfc.gasBias * deltaTime;
+ if ( advanceGas < 0 ) advanceGas = 0;
+
+ float advanceBrake = brake * vfc.brakeBias * deltaTime;
+ if ( advanceBrake < 0 ) advanceBrake = 0;
+
+ float advanceSpeed = speed * vfc.speedBias * deltaTime;
+ if ( advanceSpeed < 0 ) advanceSpeed = 0;
+
+ float totalAdvanceTime = advanceTime + advanceGas - advanceBrake + advanceSpeed;
+
+ vfc.frameController->Advance( totalAdvanceTime );
+ }
+ }
+}
+
diff --git a/game/code/worldsim/redbrick/geometryvehicle.h b/game/code/worldsim/redbrick/geometryvehicle.h
new file mode 100644
index 0000000..4da0a8b
--- /dev/null
+++ b/game/code/worldsim/redbrick/geometryvehicle.h
@@ -0,0 +1,322 @@
+/*===========================================================================
+ geometryvehicle.h
+
+ created Dec 7, 2001
+ by Greg Mayer
+
+ Copyright (c) 2001 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _GEOMETRYVEHICLE_HPP
+#define _GEOMETRYVEHICLE_HPP
+
+class tPose;
+class tGeometry;
+class tTexture;
+class tShader;
+class tPoseAnimationController;
+class tParticleSystem;
+class tAnimation;
+class Vehicle;
+class TrafficBodyDrawable;
+class tBillboardQuadGroup;
+class tFrameController;
+class SkidmarkGenerator;
+class tMultiController;
+class StatePropCollectible;
+
+#include <p3d/anim/compositedrawable.hpp>
+#include <render/particles/vehicleparticleemitter.h>
+#include <worldsim/SkidMarks/SkidMarkGenerator.h>
+#include <constants/particleenum.h>
+#include <vector>
+#include <memory/stlallocators.h>
+
+#define MAX_REFRACTION_SHADERS 16
+
+struct BlobShadowParams;
+
+struct HeadLightParams
+{
+ HeadLightParams( const rmt::Vector& Pos, const rmt::Vector& Normal, const rmt::Vector& Forward, const rmt::Vector& Up ) :
+ GroundPos( Pos ), GroundNormal( Normal ), VehicleHeading( Forward ), VehicleUp( Up ) {};
+ const rmt::Vector& GroundPos;
+ const rmt::Vector& GroundNormal;
+ const rmt::Vector& VehicleHeading;
+ const rmt::Vector& VehicleUp;
+ float LightReach; // A value of 1 is the light right at the car (i.e. car on the ground), a zero is light max reach from the car (i.e. car high in the air)
+};
+
+enum FCTrigger
+{
+ eNone,
+ eBackfire
+};
+
+class GeometryVehicle
+{
+public:
+
+ GeometryVehicle();
+ ~GeometryVehicle();
+
+ bool Init( const char* name, Vehicle* owner, int i);
+
+ void Display();
+ //void DisplaySkids();
+ void UpdateSkids();
+ void SetSkidValues(int wheel, float intensity, rmt::Vector& normal, eTerrainType terrainType );
+
+
+ void Update(float dt);
+
+ tPose* GetP3DPose();
+
+ int CastsShadow();
+ void DisplayShadow( BlobShadowParams* BlobParams = 0 );
+
+ // The additive lights. Headlights, etc.
+ void DisplayLights( const HeadLightParams& LightParams );
+
+ void ShowBrakeLights();
+ void HideBrakeLights();
+
+ void ShowReverseLights();
+ void HideReverseLights();
+
+ void SetTrafficBodyColour( pddiColour colour );
+ void SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] );
+ void SetShininess( unsigned char EnvRef ) { m_EnvRef = EnvRef; }
+
+ void SetFadeAlpha( int fade );
+
+ bool IsTrafficModel();
+
+ void FadeRoof( bool fade );
+
+ void EnableLights( bool enable );
+ void SetLightsOffDueToDamage(bool lightsOffDueToDamage) {mLightsOffDueToDamage = lightsOffDueToDamage;}
+ // Get the vehicle body colour, currently only valid for traffic vehicles
+ tColour GetVehicleColour()const;
+ bool HasVehicleColour()const { return (mTrafficBodyDrawable != NULL); }
+
+ // Structure that holds data on how to manipulate frame controllers
+ // that are tied to vehicle specific things like gas and velocity
+ // The animation frame of such an object is determined by
+ // frame = clamp( gasBias * currGas * brakeBias * brake + speedBias * currSpeed )
+ // controller->SetFrame( frame );
+ // note that brake values act as reverse gas and are in the range [0,1] like gas is
+
+
+ struct VehicleFrameController
+ {
+ const char* name;
+ tFrameController* frameController;
+ bool frameSetter;
+ FCTrigger trigger;
+ float gasBias;
+ float brakeBias;
+ float speedBias;
+ float timeBias;
+ };
+
+ float GetHeadlightScale();
+ void SetHeadlightScale( float scale );
+
+ // Attach a tDrawable collectible to the vehicle
+ // returns true if attached or false if not ( false happens
+ // when a collectible is already attached to the vehicle)
+ bool AttachCollectible( StatePropCollectible* );
+ StatePropCollectible* GetAttachedCollectible();
+ void DetachCollectible( const rmt::Vector& velocity, bool explode = true );
+
+private:
+
+ // TODO - keep like this?
+ friend class Vehicle;
+
+ Vehicle* mVehicleOwner;
+
+ bool GetArt( const char* name); // a bit obscure, but use return value to say true for localized damage textures and shaders present
+ void FindAndTurnOffWheels();
+ void FindAndTurnOffFrontWheelsOnly(); // for rocket car
+ void FindBrakeLightBillboardJoints();
+ void FindHeadLightBillboardJoints();
+ void FindGhostGlowBillboards( const char* model );
+ void FindRoofGeometry( const char* model );
+ void FindNukeGlowBillboards( const char* model );
+
+ void SetCollectibleHardpointPosition( const rmt::Vector& position );
+ void SetCollectibleHardpointTransform( float rotx ,float roty ,float rotz, const rmt::Vector& position );
+
+ tCompositeDrawable* mCompositeDrawable;
+
+ // some debug shit to make the car change colour when you bottom out
+ tGeometry* mChassisGeometry;
+
+
+ tShader* mRefractionShader[ MAX_REFRACTION_SHADERS ];
+
+ //-------------------------------------------------------
+ // damage texture stuff
+ //
+ // get specific test working then make a general solution
+ //
+ //-------------------------------------------------------
+
+ // need pointers to the tShaders so we can swap the texture they use
+ // need pointers to the textures to swap in
+ // need pointers to the textures to swap back?
+
+ // we should find these for type 1 and 2 damage
+
+
+ tShader* mHoodShader;
+ tShader* mTrunkShader;
+ tShader* mDoorPShader;
+ tShader* mDoorDShader;
+
+ tTexture* mHoodTextureDam;
+ tTexture* mTrunkTextureDam;
+ tTexture* mDoorPTextureDam;
+ tTexture* mDoorDTextureDam;
+
+ tTexture* mHoodTextureNorm;
+ tTexture* mTrunkTextureNorm;
+ tTexture* mDoorPTextureNorm;
+ tTexture* mDoorDTextureNorm;
+
+ bool FindDamageShadersAndTextures( const char* name); // only returns true if localized damage textures and shaders are present
+ //void TriggerDamage();
+
+ void DamageTextureDoorD(bool on);
+ void DamageTextureDoorP(bool on);
+ void DamageTextureHood(bool on);
+ void DamageTextureTrunk(bool on);
+
+ void HideFlappingPiece(int jointindex, bool doit);
+
+ void InitParticles();
+
+ void SetEngineSmoke(ParticleEnum::ParticleID pid);
+ void SetWheelSmoke( int wheel, ParticleEnum::ParticleID pid, float bias);
+
+ tShader* mChassisShader;
+ tTexture* mChassisTextureNorm;
+ tTexture* mChassisTextureDam;
+ void DamageTextureChassis(bool on);
+
+
+ VehicleParticleEmitter* mParticleEmitter;
+
+ ParticleAttributes mEngineParticleAttr; // eNull
+ ParticleAttributes mLeftWheelParticleAttr;
+ ParticleAttributes mRightWheelParticleAttr;
+ ParticleAttributes mTailPipeParticleAttr; // eNull
+
+ //eEngineSmokeLight,
+ //eEngineSmokeHeavy,
+
+ // Particle system with variable emission rate depending on speed
+ tParticleSystem* mVariableEmissionParticleSystem;
+
+
+ SkidMarkGenerator* mSkidMarkGenerator;
+ void InitSkidMarks();
+
+ // ?
+ // tPoseAnimationController* mAnimController;
+
+ //tPoseAnimation* mAnim;
+
+
+ //tPoseAnimationController* mAnimController;
+ tAnimation* mAnim;
+
+
+ float mAnimRevPerSecondBase;
+
+ float mRevMult;
+
+ // Special particle effects (only present on a few vehicles, like Frink or
+ // the zombie car)
+ ParticleEnum::ParticleID mSpecialEffect;
+
+ // Cache the last position of the vehicle for
+ // spherical environment map rotation based on distance travelled
+ //
+ rmt::Vector mLastPosition;
+ float mCurEnvMapRotation;
+
+ int mBrakeLightJoints[4];
+ int mReverseLightJoints[4];
+
+ TrafficBodyDrawable* mTrafficBodyDrawable;
+ TrafficBodyDrawable* mTrafficDoorDrawable;
+
+ float mShadowPointAdjustments[ 4 ][ 2 ]; // Nudge around the shadow points. See notes in the shadow display.
+
+ int mFadeAlpha;
+
+ enum {
+ NUM_BRAKELIGHT_BBQS = 4,
+#ifdef RAD_WIN32
+ NUM_FRINKARC_BBQS = 3,
+#endif
+ NUM_GHOSTGLOW_BBQGS = 6,
+ NUM_GHOSTGLOW_BBQS = 6,
+ NUM_NUKEGLOW_BBQGS = 1,
+ NUM_NUKEGLOW_BBQS = 3
+ };
+
+#ifdef RAD_WIN32
+ tBillboardQuadGroup* mFrinkArc;
+ tColour mOriginalFrinkArcColour[ NUM_FRINKARC_BBQS ];
+#endif
+
+ tBillboardQuadGroup* mBrakeLights[NUM_BRAKELIGHT_BBQS];
+ // This relies on each quadgroup containing only one quad
+ // & will store color of that quad
+ tColour mOriginalBrakeLightColours[NUM_BRAKELIGHT_BBQS];
+
+ bool mUsingTrafficModel;
+
+ bool mHasGhostGlow;
+ tBillboardQuadGroup* mGhostGlows[NUM_GHOSTGLOW_BBQGS];
+ tColour mOriginalGhostGlowColours[NUM_GHOSTGLOW_BBQS];
+
+ bool mHasNukeGlow;
+ tBillboardQuadGroup* mNukeGlows[NUM_NUKEGLOW_BBQGS];
+ tColour mOriginalNukeGlowColours[NUM_NUKEGLOW_BBQS];
+
+
+ bool mBrakeLightsOn;
+ float mBrakeLightScale;
+ float mHeadLightScale;
+ bool mEnableLights;
+ bool mLightsOffDueToDamage;
+
+ tCompositeDrawable::DrawablePropElement* mRoofOpacShape;
+ tCompositeDrawable::DrawablePropElement* mRoofAlphaShape;
+ tShader* mRoofShader;
+ int mRoofAlpha;
+ int mRoofTargetAlpha;
+
+
+ std::vector< VehicleFrameController, s2alloc< VehicleFrameController > > mFrameControllers;
+ void FindAnimationControllers( const char* multicontrollername );
+ bool GetSpecialController( tUID name, VehicleFrameController* outSpecialController );
+ void AdvanceAnimationControllers( float deltaTime );
+
+ // A collectible that can be attached to the car
+ // Used for l7m5 mission
+ StatePropCollectible* m_Collectible;
+ rmt::Matrix m_CollectibleTransform;
+
+ unsigned char m_EnvRef;
+};
+
+#endif // _GEOMETRYVEHICLE_HPP \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/physicslocomotion.cpp b/game/code/worldsim/redbrick/physicslocomotion.cpp
new file mode 100644
index 0000000..1ab2899
--- /dev/null
+++ b/game/code/worldsim/redbrick/physicslocomotion.cpp
@@ -0,0 +1,2069 @@
+/*===========================================================================
+ physicslocomotion.cpp
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#include <simcommon/simstatearticulated.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <worldsim/redbrick/physicslocomotion.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/worldphysicsmanager.h>
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <cheats/cheatinputsystem.h>
+
+using namespace sim;
+
+/*
+ data that should move from Vehicle to here...
+
+ struct TerrainIntersectCache
+ {
+ rmt::Vector closestTriPosn;
+ rmt::Vector closestTriNormal;
+ rmt::Vector planePosn;
+ rmt::Vector planeNorm;
+ };
+
+ TerrainIntersectCache mTerrainIntersectCache[4]; // is this wasting too much memory?
+
+
+???
+ float mRollingFrictionForce;
+ float mTireLateralResistance;
+ float mSlipGasModifier;
+???
+
+
+
+
+
+*/
+
+
+//------------------------------------------------------------------------
+PhysicsLocomotion::PhysicsLocomotion(Vehicle* vehicle) : VehicleLocomotion(vehicle)
+{
+ mVehicle = vehicle;
+ //
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ mForceApplicationPoints[i].Clear();
+ mSuspensionPointVelocities[i].Clear();
+
+ mCachedSuspensionForceResults[i] = 0.0f;
+
+ // TODO - initialize better?
+ // everytime control is switched to VL_PHYSICS
+ //mWheelTerrainCollisionFixDepth[i] = 0.0f;
+
+ //mWheelTerrainCollisionNormals[i].x = 0.0f;
+ //mWheelTerrainCollisionNormals[i].y = 1.0f;
+ //mWheelTerrainCollisionNormals[i].z = 0.0f;
+
+ //mWheelTerrainCollisionPoints[i].Clear();
+
+ //mGoodWheelTerrainCollisionValue[i] = false;
+ }
+
+ mCurrentSteeringForce = mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+
+}
+
+
+
+//------------------------------------------------------------------------
+PhysicsLocomotion::~PhysicsLocomotion()
+{
+ //
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::SetTerrainIntersectCachePointsForNewTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::SetTerrainIntersectCachePointsForNewTransform()
+{
+ rmt::Vector tempVec;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ tempVec = mVehicle->mSuspensionWorldSpacePoints[i];
+ tempVec.y -= 2.0f * mVehicle->mWheels[i]->mRadius;
+
+ mTerrainIntersectCache[i].closestTriPosn = tempVec;
+ mTerrainIntersectCache[i].closestTriNormal.Set(0.0f, 1.0f, 0.0f);
+
+ mTerrainIntersectCache[i].planePosn = tempVec;
+ mTerrainIntersectCache[i].planeNorm.Set(0.0f, 1.0f, 0.0f);
+
+ mTerrainIntersectCache[i].mTerrainType = TT_Road;
+ mTerrainIntersectCache[i].mInteriorTerrain = false;
+
+ mIntersectNormalUsed[i].Set(0.0f, 1.0f, 0.0f);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::MoveWheelsToBottomOfSuspension
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::MoveWheelsToBottomOfSuspension()
+{
+
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ // want to call false here because we want the movement of the hierarchy based on animation to also be available for collision
+ // TODO - make sure this is done in the traffic locomotion case as well.
+ mVehicle->mPoseEngine->Begin(false); //Cary Here - I've moved it.
+ //mVehicle->mPoseEngine->Begin(true); //Cary Here - I've moved it.
+ // TODO - even need to do this?
+
+
+ // TODO
+ // note:
+ // are we making the assumption that the animation doesn't move the suspension points around?
+ // for now, yes.
+
+
+ // TODO - only do this if wheelInCollision?
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ //trans.y -= mVehicle->mWheels[i]->mLimit;
+ // TODO - verify that the -= is the thing to do here
+ trans.y -= wheel->mLimit;
+
+ trans.y += mVehicle->mDesignerParams.mDpSuspensionYOffset;
+
+ joint->SetObjectTranslation(trans);
+
+
+
+ // TODO - if this method actually works, make nicer interface with wheel to do this
+
+ //
+ // remember, mYOffset is the offset (upwards) from the bottom of the suspension limit, to fix wheel out of collision (or just barely in collision)
+ wheel->mYOffset = -1.0f * wheel->mLimit;
+
+ wheel->mWheelInCollision = false;
+ wheel->mWheelBottomedOutThisFrame = false;
+
+
+ }
+
+ // just in case
+ //
+ // TODO - review why the hell you're doing this? - this one's pretty important I think
+ CollisionObject* collObj = mVehicle->mSimStateArticulated->GetCollisionObject();
+ collObj->SetHasMoved(true);
+
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::UpdateVehicleGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::UpdateVehicleGroundPlane()
+{
+ // create a new transform for the ground plane sim state and set it.
+
+ rmt::Vector p, n;
+ p.Set(0.0f, 0.0f, 0.0f);
+ n.Set(0.0f, 0.0f, 0.0f);
+
+ /*
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ //if(mFoundPlane[i])
+
+ // try doing this with whatever's in there
+ // old or new
+ //
+ // TODO - is this safe?
+
+ {
+ p.Add(mVehicle->mTerrainIntersectCache[i].planePosn);
+ n.Add(mVehicle->mTerrainIntersectCache[i].planeNorm);
+ }
+
+
+ }
+
+ p.Scale(0.25f);
+ n.Scale(0.25f);
+ */
+
+ // new idea for ground plane
+ // pick the most upright normal, and the lowest height point
+ // ??
+
+ p = mTerrainIntersectCache[0].planePosn;
+ n = mTerrainIntersectCache[0].planeNorm;
+
+ int i;
+ for(i = 1; i < 4; i++)
+ {
+ if(mTerrainIntersectCache[i].planePosn.y < p.y)
+ {
+ p = mTerrainIntersectCache[i].planePosn;
+ }
+
+ if( GetWorldPhysicsManager()->mWorldUp.DotProduct(mTerrainIntersectCache[i].planeNorm) >
+ GetWorldPhysicsManager()->mWorldUp.DotProduct(n) )
+ {
+ n = mTerrainIntersectCache[i].planeNorm;
+ }
+
+ }
+
+
+ // new approach with manual sim state
+
+ mVehicle->mGroundPlaneWallVolume->mPosition = p;
+ mVehicle->mGroundPlaneWallVolume->mNormal = n;
+
+
+ sim::CollisionObject* co = mVehicle->mGroundPlaneSimState->GetCollisionObject();
+ co->PostManualUpdate();
+
+
+
+
+ /*
+ rmt::Matrix groundPlaneTransform;
+ groundPlaneTransform.Identity();
+
+ // TODO - optimize this call since we know what the heading is all the time
+ //rmt::Vector heading(0.0f, 0.0f, 1.0f);
+ rmt::Vector up(0.0f, 0.0f, 1.0f);
+
+ // note:
+ //!!!
+ //
+ // new approach - use our n as heading
+
+ //groundPlaneTransform.FillHeading(heading, n);
+ groundPlaneTransform.FillHeading(n, up);
+
+ groundPlaneTransform.FillTranslate(p);
+
+ //PushSimState(bool inReset=true) = 0; // better name for this would be Sync
+ // ? mSimStateArticulated->SetControl(simAICtrl);
+
+ mVehicle->mGroundPlaneSimState->SetTransform(groundPlaneTransform);
+
+
+ // ? mSimStateArticulated->SetControl(simSimulationCtrl);
+ // TODO - need to do this?
+ // double check with martin!!
+ //mVehicle->mGroundPlaneSimState->GetCollisionObject()->Update(); - don't seem to need this?
+ */
+
+}
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::PreCollisionPrep(bool firstSubstep)
+{
+
+ BEGIN_PROFILE("MoveWheelsToBottomOfSuspension")
+ MoveWheelsToBottomOfSuspension();
+ END_PROFILE("MoveWheelsToBottomOfSuspension")
+
+ // this is in substep, so comment out if we don't want it to happen
+ if(firstSubstep) // only do this once per update, but it must be inside the loop the first time
+ {
+
+ if(mVehicle->mSimStateArticulated->GetControl() != sim::simAICtrl)
+ {
+
+ BEGIN_PROFILE("FetchWheelTerrainCollisionInfo")
+ FetchWheelTerrainCollisionInfo();
+ END_PROFILE("FetchWheelTerrainCollisionInfo")
+ }
+ }
+ else
+ {
+ int stophere = 1;
+ }
+
+ BEGIN_PROFILE("UpdateVehicleGroundPlane")
+ UpdateVehicleGroundPlane();
+ END_PROFILE("UpdateVehicleGroundPlane")
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::CompareNormalAndHeight
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint)
+{
+
+ // important!
+ //
+ // assume this is being called from the collision solver agent _after_ we've already fetched new wheel terrain collision info
+
+ // so....
+ // how to decide?
+
+ // first try?
+ // y value of contact points?
+ rAssert(index >= 0 && index < 4);
+
+
+ if(mFoundPlane[index])
+ {
+
+ if(groundContactPoint.y > mTerrainIntersectCache[index].planePosn.y)
+ {
+
+ //mTerrainIntersectCache[index].planePosn = groundContactPoint;
+ //mTerrainIntersectCache[index].planeNorm = normalPointingAtCar;
+
+ // note this used to be in here??
+ mIntersectNormalUsed[index] = normalPointingAtCar;
+
+ }
+ }
+ else
+ {
+
+ // take the more uprigth normal
+ if(GetWorldPhysicsManager()->mWorldUp.DotProduct(normalPointingAtCar) >
+ GetWorldPhysicsManager()->mWorldUp.DotProduct(mIntersectNormalUsed[index]) )
+
+ {
+ mIntersectNormalUsed[index] = normalPointingAtCar;
+
+
+ }
+
+ }
+/*
+ struct TerrainIntersectCache
+ {
+ rmt::Vector closestTriPosn;
+ rmt::Vector closestTriNormal;
+ rmt::Vector planePosn;
+ rmt::Vector planeNorm;
+ };
+
+ TerrainIntersectCache mTerrainIntersectCache[4]; // is this wasting too much memory?
+
+ // need this because if we're driving on a static collision volume the terrain normal could be way off!
+ rmt::Vector mIntersectNormalUsed[4];
+*/
+
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::PreSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::PreSubstepUpdate()
+{
+
+ //MoveWheelsToBottomOfSuspension();
+
+ //FetchWheelTerrainCollisionInfo();
+
+ //UpdateVehicleGroundPlane();
+
+}
+
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::SetForceApplicationPoints()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // TODO - clean up
+ // for force application points should also add in wheel - nope, tried that, not so good
+
+ // TODO
+ // why do I have copies of thsi stuff here
+ mForceApplicationPoints[i] = mVehicle->mSuspensionWorldSpacePoints[i];
+
+ // try something new:
+
+ if(mVehicle->mVehicleType == VT_USER && (mVehicle->mVehicleState == VS_SLIP || mVehicle->mVehicleState == VS_EBRAKE_SLIP))
+ {
+
+ rmt::Vector tireFix = mVehicle->mVehicleUp;
+
+
+ // new test - decrease depending on % of top speed
+ //tireFix.Scale(mVehicle->mWheels[i]->mRadius * -1.0f * (1.0f - (mVehicle->mPercentOfTopSpeed * 0.5f)));
+
+ //tireFix.Scale(mVehicle->mWheels[i]->mRadius * -1.0f * (1.0f - (mVehicle->mPercentOfTopSpeed * 0.75f)));
+
+
+ //tireFix.Scale(mVehicle->mWheels[i]->mRadius * -1.0f * (1.0f - (mVehicle->mPercentOfTopSpeed * mVehicle->mPercentOfTopSpeed)));
+ tireFix.Scale(mVehicle->mWheels[i]->mRadius * mVehicle->mPercentOfTopSpeed);// * 0.5f);
+
+
+ //mForceApplicationPoints[i].y -= mVehicle->mWheels[i]->mRadius;
+
+
+ mForceApplicationPoints[i].Add(tireFix);
+ }
+
+ mSuspensionPointVelocities[i] = mVehicle->mSuspensionPointVelocities[i];
+
+ }
+
+}
+
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::ApplySuspensionForces(float dt, bool atrest)
+{
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ //rmt::Vector force = mVehicle->mVehicleUp;
+ //rmt::Vector force = mTerrainIntersectCache[i].planeNorm;
+ rmt::Vector force;
+ if(atrest)
+ {
+ force = mVehicle->mVehicleUp;
+ }
+ else
+ {
+ force = mIntersectNormalUsed[i];
+ }
+
+ //??
+ /*
+ // not helping
+ if( GetWorldPhysicsManager()->mWorldUp.DotProduct(mVehicle->mVehicleUp) >
+ GetWorldPhysicsManager()->mWorldUp.DotProduct(force) && mVehicle->mDoingJumpBoost)
+
+ {
+
+ force = mVehicle->mVehicleUp;
+ }
+ */
+
+ // debug
+ //float cos30 = 0.866f;
+ //float cos40 = 0.766f;
+ //if(GetWorldPhysicsManager()->mWorldUp.DotProduct(force) < cos30)
+
+
+ float yVelocityAlongSuspensionAxis = force.DotProduct(mSuspensionPointVelocities[i]);
+
+ //mCachedSuspensionForceResults[i] = mWheels[i]->CalculateSuspensionForce(mSuspensionPointVelocities[i].y, dt);
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ float forceToUse = 0.0f;
+ mCachedSuspensionForceResults[i] = CalculateSuspensionForce(wheel, yVelocityAlongSuspensionAxis, dt, forceToUse);
+
+
+ force.Scale(forceToUse);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(force, &(mForceApplicationPoints[i]));
+ }
+
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::CalculateSuspensionForce
+//=============================================================================
+// Description: Comment
+//
+// note - moving thsi functionality from Wheel class to here because wheel
+// shouldn't do physics calculations, and this class shouldn't hold the
+// info about wheels - like k and c
+//
+//
+// Parameters: (Wheel* wheel, float suspensionPointYVelocity, float dt)
+//
+// Return: float
+//
+//=============================================================================
+float PhysicsLocomotion::CalculateSuspensionForce(Wheel* wheel, float suspensionPointYVelocity, float dt, float& forceToUse)
+{
+
+ // crazy shit goin' on here!
+ // the value we return we'll cachce for slip calculations and stuff
+ //
+ // the value we stuff in forceToUse is the one to make the car bank.
+ //
+ // the one we cache should not include the quadSpringForce
+
+
+ float force = 0.0f;
+
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ float cos85 = 0.087f;
+ float cos80 = 0.17365f;
+ float cos65 = 0.4226f;
+
+ if(wheel->mWheelInCollision && tip > cos65) // minor TODO - should this number match any others?
+ {
+ // test
+ //
+ // make the spring also only push up
+
+ //float springForce = wheel->mk * wheel->mYOffset;
+ float springForce = 0.0f;
+ //if(wheel->mYOffset > 0.0f)
+
+ if(!(mVehicle->mDoingJumpBoost && wheel->mYOffset < 0.0f))
+ {
+ springForce = wheel->mk * wheel->mYOffset;
+ }
+
+
+
+ float quadSpringForce = 0.0f;
+ if(wheel->mYOffset > 0.0f)
+ {
+ quadSpringForce = wheel->mqk * wheel->mYOffset * wheel->mYOffset;
+ //springForce += quadSpringForce;
+ }
+
+
+ //
+ // or... topping out!
+ //
+ if((wheel->mLimit - rmt::Fabs(wheel->mYOffset)) < wheel->mLimit * 0.25f)
+ {
+ //springForce *= 3.0f;
+ }
+
+ // for now, only add damping if chassis is trying to compress suspension - ie. y velocity is down, -ve
+ float damperForce = 0.0f;
+
+
+ if(suspensionPointYVelocity > 0.0f)// this would make the dampe pull down
+ {
+ if((mVehicle->mDamperShouldNotPullDown[wheel->mWheelNum]) || mVehicle->mDoingJumpBoost)
+ {
+ // no suspension force down
+ }
+ else
+ {
+ // regular
+ damperForce = wheel->mc * -1.0f * suspensionPointYVelocity;
+ }
+
+ }
+ else
+ {
+ // no worries
+ damperForce = wheel->mc * -1.0f * suspensionPointYVelocity;
+ }
+
+
+
+ force = springForce + damperForce;
+ forceToUse = springForce + damperForce + quadSpringForce;
+ }
+ else
+ {
+ // need to relax spring somehow
+
+ // this is essentially useless....
+
+ float relaxDistance = wheel->mLimit / wheel->mSpringRelaxRate * dt;
+ if(wheel->mYOffset >= relaxDistance)
+ {
+ wheel->mYOffset -= relaxDistance;
+ }
+ else if(wheel->mYOffset <= -relaxDistance)
+ {
+ wheel->mYOffset += relaxDistance;
+ }
+
+ }
+
+ return force;
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::ApplyDragForce
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyDragForce()
+{
+ if(mVehicle->mSpeed > 1.0f)
+ {
+ rmt::Vector force = mVehicle->mVelocityCM;
+
+ // new test -
+ //force.y = 0.0f;
+
+ force.NormalizeSafe();
+
+ float mag = mVehicle->mSpeed * mVehicle->mSpeed * 0.5f * mVehicle->mDragCoeff;
+
+ // new
+ // car slows down too much when you let off gas
+ if(mVehicle->mGas == 0.0f && mVehicle->mSpeedKmh > 50.0f && mVehicle->mBrake == 0.0f)
+ {
+ mag *= 0.25f;
+ }
+
+
+ force.Scale(-1.0f * mag);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(force);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::LowSpeedTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::LowSpeedTest()
+{
+
+ //static float test = 0.8f;
+ static float test = 0.8f;
+
+ static float linearWhittle = 0.6f;
+ static float angularWhittle = 0.1f;
+
+ //const float lowspeed = 1.0f;
+ //static float lowspeed = 2.0f;
+ float lowspeed = 2.0f;
+ float reallylowspeed = 0.05f;
+
+ // only apply if at least 2 wheels in contact with the ground:
+
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ if(mVehicle->mWheels[i]->mWheelInCollision)
+ {
+ count++;
+ }
+ }
+
+ if(count > 1)
+ {
+
+ if(mVehicle->mSpeed < lowspeed && mVehicle->mGas < 0.1f /*&& mVehicle->mReverse < 0.1f*/ && mVehicle->mBrake < 0.1f) // new... brake
+ {
+ // whittle away speed, or just reset?
+ //mVehicle->mSimStateArticulated->ResetVelocities();
+ // I knew this was a bad idea
+
+ if(mVehicle->mSpeed < reallylowspeed)
+ {
+ mVehicle->mSimStateArticulated->ResetVelocities();
+ }
+ else
+ {
+
+ rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ linearVel.Scale(linearWhittle);
+ angularVel.Scale(angularWhittle);
+ }
+ }
+ }
+
+
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::ApplyAngularDrag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyAngularDrag()
+{
+ if(mVehicle->mPercentOfTopSpeed > 0.01f)
+ {
+ const float magicshit = 3.0f;
+
+ rmt::Vector angularDragForce = mVehicle->mSimStateArticulated->GetAngularVelocity();
+ angularDragForce.Scale(-1.0f * magicshit * mVehicle->mDesignerParams.mDpMass);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(angularDragForce);
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::ApplyRollingFriction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyRollingFriction()
+{
+ // to start with simplest possible model - constant?
+ //static float test = 1.0f;
+ //static float test2 = 2000.0f;
+
+ //static float test = 0.9f;
+ //static float test = 0.8f;
+ static float test = 0.8f;
+
+ //const float lowspeed = 1.0f;
+ //static float lowspeed = 2.0f;
+ static float lowspeed = 2.0f;
+ static float reallylowspeed = 0.05f;
+
+ static float torquemod = 0.001f;
+
+ // only apply if at least 2 wheels in contact with the ground:
+
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ if(mVehicle->mWheels[i]->mWheelInCollision)
+ {
+ count++;
+ }
+ }
+
+ if(count > 1)
+ {
+
+ if(mVehicle->mSpeed > reallylowspeed && mVehicle->mGas == 0.0f)
+ {
+ rmt::Vector force = mVehicle->mVelocityCM;
+ force.Normalize();
+
+ force.Scale(-1.0f * mVehicle->mRollingFrictionForce * mVehicle->mDesignerParams.mDpMass);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(force);
+
+
+ }
+
+ else if(0)//mVehicle->mGas < 0.1f && mVehicle->mReverse < 0.1f && mVehicle->mBrake < 0.1f) // new... brake
+ {
+ // whittle away speed, or just reset?
+ //mVehicle->mSimStateArticulated->ResetVelocities();
+ // I knew this was a bad idea
+
+ //if(1)//mVehicle->mSpeed < reallylowspeed)
+ /*
+ if(mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->IsAtRest())
+ {
+
+ mVehicle->mSimStateArticulated->ResetVelocities();
+ mVehicle->mSimStateArticulated->SetControl(sim::simAICtrl);
+
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->ResetAppliedForces();
+
+ }
+ else
+ {
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->ResetAppliedForces();
+
+
+ //rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ //rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ //linearVel.Scale(test);
+ //angularVel.Scale(test);
+ }
+ */
+
+ }
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::HighSpeedInstabilityTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::HighSpeedInstabilityTest()
+{
+ const float threshold = 0.5f;
+
+ // from threshold to 100%, increase y cm offset
+
+ if(mVehicle->mPercentOfTopSpeed > threshold)
+ {
+ // should do ...
+ if(mVehicle->mInstabilityOffsetOn)
+ {
+ // already set
+ //
+ // nothing to do here
+ }
+ else
+ {
+ // set it
+
+ rmt::Vector unstableAdjust = mVehicle->mCMOffset;
+
+ //static float weeblemagic = 1.0f;
+ /*static*/ float magic = 1.0f;
+
+ // scale by point in range
+ // set range of 0 to 1 for start to limit
+ /*
+ float range = 1.0f - ((tip - end) / (start - end));
+
+ if(range < 0.0f)
+ {
+ range = 0.0f;
+ //rAssert(0);
+ }
+ if(range > 1.0f)
+ {
+ range = 1.0f;
+ }
+
+ float modifier = weeblemagic * range;
+ */
+ //weeble.y -= modifier;
+
+ unstableAdjust.y += magic;
+
+ //weeble.y -= 3.0f;
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(unstableAdjust);
+
+ mVehicle->mInstabilityOffsetOn = true;
+ }
+ }
+ else
+ {
+ // if it's on, shut it off
+ if(mVehicle->mInstabilityOffsetOn)
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+
+ mVehicle->mInstabilityOffsetOn = false;
+ }
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::Weeble
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::Weeble()
+{
+ bool doit = false;
+
+ // only do this for sideways tipping
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ float cos35 = 0.8192f;
+ float cos45 = 0.7071f;
+ float cos55 = 0.5736f;
+ float cos40 = 0.766f;
+
+ float start = cos40;
+ float end = cos55;
+
+
+ // new test -
+ // if we are completely airborn - set cmoffset to 0,0,0
+
+ //if(mVehicle->mAirBorn)
+ //{
+ // if(!(mVehicle->mCMOffsetSetToOriginal))
+ // {
+ // //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ // mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mOriginalCMOffset);
+ // mVehicle->mCMOffsetSetToOriginal = true;
+ // }
+ //}
+ //else
+ {
+ /*
+ if(mVehicle->mCMOffsetSetToOriginal)
+ {
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mOriginalCMOffset);
+ mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ mVehicle->mCMOffsetSetToOriginal = false;
+ }
+ */
+
+ //if(!mVehicle->mAirBorn && tip < start)
+ if(1)//tip < start)
+ {
+ bool slowdowntipping = false;
+ if( !(mVehicle->mWheels[0]->mWheelInCollision) && !(mVehicle->mWheels[3]->mWheelInCollision) )
+ {
+ doit = true;
+
+ if(mVehicle->mWheels[1]->mWheelInCollision && mVehicle->mWheels[2]->mWheelInCollision)
+ {
+ slowdowntipping = true;
+ }
+ }
+
+
+ if( !(mVehicle->mWheels[1]->mWheelInCollision) && !(mVehicle->mWheels[2]->mWheelInCollision) )
+ {
+ doit = true;
+
+ if(mVehicle->mWheels[0]->mWheelInCollision && mVehicle->mWheels[3]->mWheelInCollision)
+ {
+ slowdowntipping = true;
+ }
+ }
+
+ if(0)//(slowdowntipping)
+ {
+ // only resist tipping UP!
+
+
+
+ //static float magicshit = 3.0f;
+ const float magicshit = 2.5f;
+
+ float facingAngVel = (mVehicle->mSimStateArticulated->GetAngularVelocity()).DotProduct(mVehicle->mVehicleFacing);
+
+ // a positive value means tipping left.
+ //
+ // so.......
+ // we want to apply the torque if the transverse.dot.worldup is positive
+
+ // or... if tipping right and transverse.dot.worldup is negative
+
+ if( (facingAngVel > 0.0f && mVehicle->mVehicleTransverse.DotProduct(up) > 0.0f) ||
+ (facingAngVel < 0.0f && mVehicle->mVehicleTransverse.DotProduct(up) < 0.0f) )
+ {
+ rmt::Vector torque = mVehicle->mVehicleFacing;
+
+
+ torque.Scale(-1.0f * magicshit * mVehicle->mDesignerParams.mDpMass * facingAngVel);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(torque);
+ }
+ }
+
+ }
+ if(mVehicle->mAirBorn)
+ {
+ doit = true;
+ }
+
+
+ if(doit)
+ {
+
+ if(!(mVehicle->mWeebleOn))
+ {
+
+ // weeble
+ rmt::Vector weeble = mVehicle->mCMOffset;
+
+ //static float weeblemagic = 1.0f;
+ float weeblemagic = 1.0f;
+
+ // scale by point in range
+ // set range of 0 to 1 for start to limit
+ /*
+ float range = 1.0f - ((tip - end) / (start - end));
+
+ if(range < 0.0f)
+ {
+ range = 0.0f;
+ //rAssert(0);
+ }
+ if(range > 1.0f)
+ {
+ range = 1.0f;
+ }
+
+ float modifier = weeblemagic * range;
+ */
+ //weeble.y -= modifier;
+
+ //weeble.y -= weeblemagic;
+ weeble.y += mVehicle->mDesignerParams.mDpWeebleOffset;
+
+ //weeble.y -= 3.0f;
+
+ rmt::Vector current = ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->GetExternalCMOffset();
+
+ if(current.Equals(weeble))
+ {
+ int stophere = 1;
+ }
+ else
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(weeble);
+ }
+
+ mVehicle->mWeebleOn = true;
+ }
+
+ }
+ else
+ {
+ if(mVehicle->mWeebleOn)
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ }
+ mVehicle->mWeebleOn = false;
+ }
+
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::PreUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::PreUpdate()
+{
+ //mVehicle->CalculateSuspensionLocationAndVelocity();
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::Update(float dt)
+{
+
+
+ // recall
+ // this is the set of function calls to physicsvehicle from vehicle in the old
+ // system
+
+ UseWheelTerrainCollisionInfo(dt);
+
+ CorrectWheelYPositions(); // old name: PreUpdate(dt);
+
+ mVehicle->CalculateSuspensionLocationAndVelocity();
+
+
+
+ //mVehicle->mSimStateArticulated->SetControl(sim::simSimulationCtrl); -- move this into SelfRestTest
+
+
+ //bool rest = mVehicle->SelfRestTest();
+
+
+ bool rest = false;
+ rest = mVehicle->SelfRestTest();
+
+
+ if(mVehicle->mSimStateArticulated->GetControl() != sim::simAICtrl)
+ {
+
+
+ // TODO - this call, and UpdateJointState, should be in the same place
+ // mVehicle->mSimStateArticulated->StoreJointState(dt);
+
+
+ // the update guts from physicsvehicle
+ //
+ // kind of a fucking mess
+ //mVehicle->CalculateSuspensionLocationAndVelocity();
+
+ SetForceApplicationPoints();
+
+
+ ApplySuspensionForces(dt, rest);
+
+ if(!(mVehicle->mAirBorn) && !rest)
+ {
+ ApplyControllerForces2(dt);
+ }
+ else
+ {
+ // at least do this
+ mVehicle->mBurnoutLevel = 0.0f;
+
+ }
+
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_NO_TOP_SPEED) && mVehicle->mVehicleType == VT_USER)
+ {
+ // no drag for you!
+ }
+ else
+ {
+ if(!(mVehicle->mSteeringWheelsOutOfContact) && !(mVehicle->mDoSpeedBurst) && !(mVehicle->mDoingJumpBoost))
+ {
+ ApplyDragForce();
+ }
+ }
+
+ if(!(mVehicle->mDoingJumpBoost))
+ {
+ ApplyRollingFriction(); // move low speed portion of this into a separate function to follow update...
+ }
+
+ //TipTest();
+
+ if(mVehicle->mAirBorn && mVehicle->mVehicleType == VT_USER )
+ {
+ ApplyAngularDrag();
+ }
+
+ Weeble();
+
+ if(mVehicle->mVehicleType == VT_USER)
+ {
+ //HighSpeedInstabilityTest();
+ }
+
+ if(mVehicle->mVehicleType == VT_USER)
+ {
+ StuckOnSideTest(dt);
+ }
+
+
+ if(mVehicle->mAirBorn)// && mVehicle->mVehicleType == VT_USER)
+ {
+ DurangoStyleStabilizer(dt);
+ }
+
+
+
+ //
+ // ****
+ //
+ // THE Update
+ //
+ // ****
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->Update(dt);
+
+ //LowSpeedTest();
+
+ //rest = mVehicle->SelfRestTest();
+ }
+
+
+
+ //Update(dt);
+ //PostUpdate(dt);
+
+ // maybe for first pass implementation, implement methods with the same name?
+
+
+ // ???????
+ //
+ // do the pose driver updates here!!!! or back in vehicle <- for some reason
+ // the second way seems much nicer to me.
+
+
+
+ // the net effect on Vehicle result when this function returns is to have
+ // modified the joint-space y values in the wheel joints to correct
+ // the wheel positions, and to have applied forces and torques to the
+ // simstate and had it update itself so there is a new world space transform
+ // AND make sure the world space transform of joint 0 is in sync with this
+
+ // also - make sure wheels have enough info for suspensionjointdriver to
+ // do it's thing
+ //
+ // ie - cumulative rot
+ // wheel turn angle
+ // that's about it?
+
+ // are we gonna have to make physicslocomotion a friend of the Vehicle?
+ // is that the correct thing to do - make all the locomotions friends??
+
+
+
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::DurangoStyleStabilizer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::DurangoStyleStabilizer(float dt)
+{
+ // assume airborn
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ // we want a fix torque proportional to 1.0 - tip.
+
+ // 'direction' of the toruqe is vehicleup crossed into world up
+ float cos10 = 0.9848f;
+ if(tip < cos10)
+ {
+
+
+ rmt::Vector fixTorque;
+ fixTorque.CrossProduct(mVehicle->mVehicleUp, up);
+
+ // need this?
+ fixTorque.NormalizeSafe();
+
+ const float hackmagicshit = 200.0f;
+
+ float fixscale = 1.0f - tip;
+
+ fixTorque.Scale(hackmagicshit * mVehicle->mDesignerParams.mDpMass * fixscale);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(fixTorque);
+
+
+ // try some damping too
+ const float dampingShit = 7.0f;
+
+ rmt::Vector angularDragForce = mVehicle->mSimStateArticulated->GetAngularVelocity();
+ angularDragForce.Scale(-1.0f * dampingShit * mVehicle->mDesignerParams.mDpMass);
+
+ phobj->AddTorque(angularDragForce);
+
+
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::StuckOnSideTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::StuckOnSideTest(float dt)
+{
+ // only for user vehicle in physics locomotion
+
+ const float lowspeed = 1.0f;
+ if(mVehicle->mSpeed < lowspeed)
+ {
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ //float cos85 = 0.0872f;
+ float test = 0.2f;
+ if(tip < test)
+ {
+ //static float magicshit = 1.0f;
+ const float magicshit = 130.0f;
+
+ float side = up.DotProduct(mVehicle->mVehicleTransverse);
+ if(side > 0.0f)
+ {
+ // lying on it's left side - right side pointint to sky
+ //if(mVehicle->mUnmodifiedInputWheelTurnAngle > 0.0f)
+ if(mVehicle->mUnmodifiedInputWheelTurnAngle < 0.0f)
+ {
+ // apply some torque
+ rmt::Vector torque = mVehicle->mVehicleFacing;
+ //torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * -1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+ torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * 1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(torque);
+
+ }
+
+
+ }
+ else
+ {
+ // lying on it's right side - left side pointint to sky
+ //if(mVehicle->mUnmodifiedInputWheelTurnAngle < 0.0f)
+ if(mVehicle->mUnmodifiedInputWheelTurnAngle > 0.0f)
+ {
+ // apply some torque
+ rmt::Vector torque = mVehicle->mVehicleFacing;
+ //torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * -1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+ torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * 1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(torque);
+
+ }
+
+
+ }
+
+ }
+
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::TipTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::TipTest()
+{
+
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ float cos45 = 0.7071f;
+ float cos80 = 0.17365f;
+ float cos65 = 0.4226f;
+ // need to figure out if the angular velocity is trying to tip us over more, or right us
+
+ float start = cos45;
+ float end = cos65;
+
+ //static float tipRecoverPercentage = 75.0f;
+ float tipRecoverPercentage = 75.0f;
+
+ if(tip < start && tip > end)
+ {
+ // set range of 0 to 1 for start to limit
+ float range = 1.0f - ((tip - end) / (start - end));
+
+
+ float lean = mVehicle->mVehicleTransverse.DotProduct(up);
+
+ if(lean > cos45)
+ {
+ // tipping left
+
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ float proj = angularVel.DotProduct(mVehicle->mVehicleFacing);
+
+ //if(proj > 0.0f)
+ if(proj > 1.0f)
+ {
+ // the velocity is trying to tip us more
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage);
+
+ //angularVel.Add(recover);
+
+ //angularVel.Scale(tipRecoverPercentage);
+ //angularVel.Scale(1.0f - range);
+ angularVel.Scale(0.0f);
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage * mVehicle->mDesignerParams.mDpMass * range);
+
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(recover);
+
+
+
+ }
+ }
+ else if(lean < -cos45)
+ {
+
+ //rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ float proj = angularVel.DotProduct(mVehicle->mVehicleFacing);
+
+
+ // tipping over right
+ //if(proj < 0.0f)
+ if(proj < -1.0f)
+ {
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage);
+
+ //angularVel.Add(recover);
+
+ //angularVel.Scale(tipRecoverPercentage);
+ //angularVel.Scale(1.0f - range);
+ angularVel.Scale(0.0f);
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage * mVehicle->mDesignerParams.mDpMass * range);
+
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(recover);
+
+ }
+
+ }
+
+ // else don't do shit
+
+
+
+
+
+
+ // what is our tendency to hold and recover from tipping?
+
+
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::PostUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::PostUpdate()
+{
+ // this whole method is kind of annyoing
+ //
+ // TODO
+ //??? shoudl be in Vehicle
+
+ mVehicle->mTransform = mVehicle->mSimStateArticulated->GetTransform(-1);
+
+
+ mVehicle->mVelocityCM = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ mVehicle->mSpeed = mVehicle->mVelocityCM.Magnitude();
+ mVehicle->mPercentOfTopSpeed = mVehicle->mSpeed / (mVehicle->mDesignerParams.mDpTopSpeedKmh / 3.6f);
+ if(mVehicle->mPercentOfTopSpeed > 1.0f)
+ {
+ mVehicle->mPercentOfTopSpeed = 1.0f;
+ }
+
+ // Roll up the terrain type and interior flag;
+ // As soon as we get two tires or more of a terrain type, that's the type we'll consider the car on.
+ // This isn't very exact since the rear two tires will have precidence over the other tires.
+ int terrainCounts = 0;
+ int interiorCount = 0;
+ eTerrainType newType = TT_Road;
+ for( int i = 0; i < 4; i++ )
+ {
+ int index = i ^ 2; // We do this to change i from 0, 1, 2, 3 into 2, 3, 0, 1 so that the rear wheels are processed last.
+ interiorCount += mTerrainIntersectCache[ index ].mInteriorTerrain ? 1 : 0;
+//? Do we want to mask out roads? int mask = ( 1 << (int)mTerrainIntersectCache[ index ].mTerrainType ) & ~(int)TT_Road; // Mask out roads so everything else takes precidence over it.
+ int mask = 1 << (int)mTerrainIntersectCache[ index ].mTerrainType;
+ if( ( terrainCounts & mask ) )
+ {
+ // We've already got one, so this is number two.
+ newType = mTerrainIntersectCache[ index ].mTerrainType;
+ }
+ else
+ {
+ terrainCounts |= mask;
+ }
+ }
+ mVehicle->mTerrainType = newType;
+ mVehicle->mInterior = ( interiorCount >= 2 );
+
+}
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::CorrectWheelYPositions()
+{
+
+
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // we don't want yOffset, we want the correction value!!
+
+ // now just use mYOffset
+ //float yOffset = mWheels[i]->mYOffset; fuck no!!!
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ float yCorrectionValue = wheel->GetYCorrectionValue();
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ // these trans.y should still be at the bottom of their suspension range
+
+ // new test so wheels don't hang so low
+ if(wheel->mWheelInCollision)
+ {
+ trans.y += yCorrectionValue;
+ }
+ else if(mVehicle->mAirBorn) // add this here to remove the snap/bounce when turning hard and two wheels come out of contact
+ {
+ trans.y += wheel->mLimit;
+ }
+
+ // test
+ //trans.y += mVehicle->mDesignerParams.mDpSuspensionYOffset;
+
+ joint->SetObjectTranslation(trans);
+
+ wheel->ResolveOffset();
+
+ }
+
+
+}
+
+
+//------------------------------------------------------------------------
+// temp...
+//------------------------------------------------------------------------
+inline void TempGetTerrainIntersects( rmt::Vector& trans,
+ float radius,
+ bool& foundtri,
+ rmt::Vector& closestTriNormal,
+ rmt::Vector& closestTriPosn,
+ bool& foundplane,
+ rmt::Vector& planeNormal,
+ rmt::Vector& planePosn)
+{
+ const float y = 3.0f;
+
+
+ foundtri = false;
+ foundplane = true;
+ planeNormal.Set(0.0f, 1.0f, 0.0f);
+ planePosn.x = trans.x;
+ planePosn.y = y;
+ planePosn.z = trans.z;
+
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::FetchWheelTerrainCollisionInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::FetchWheelTerrainCollisionInfo()
+{
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ BEGIN_PROFILE("PreInter")
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ // for each wheel, based on the world xz, get a y and a normal, ..... and a ground type
+
+ //? could also do based on suspension points, no?
+ // I guess this is more accurate
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetWorldTranslation();
+
+
+ rmt::Vector closestTriNormal, closestTriPosn;
+ rmt::Vector planeNormal, planePosn;
+ int terrainType;
+ //bool bFoundTri, bFoundPlane;
+
+ mFoundTri[i] = false;
+ mFoundPlane[i] = false;
+ END_PROFILE("PreInter")
+
+ BEGIN_PROFILE("GetIntersectManager()->FindIntersection")
+
+ terrainType = GetIntersectManager()->FindIntersection( trans,
+ mFoundPlane[i],
+ planeNormal,
+ planePosn );
+
+
+ /*
+ TempGetTerrainIntersects( trans,
+ wheel->mRadius,
+ mFoundTri[i],
+ closestTriNormal,
+ closestTriPosn,
+ mFoundPlane[i],
+ planeNormal,
+ planePosn);
+ */
+
+ END_PROFILE("GetIntersectManager()->FindIntersection")
+
+
+ BEGIN_PROFILE("PostInter")
+ // fill in pieces of cache
+ if(mFoundPlane[i])
+ {
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ if(up.DotProduct(planeNormal) < 0.0f)
+ {
+ //rAssert(0);
+ }
+
+
+ mTerrainIntersectCache[i].planePosn = planePosn;
+ mTerrainIntersectCache[i].planeNorm = planeNormal;
+
+ mIntersectNormalUsed[i] = mTerrainIntersectCache[i].planeNorm;
+ }
+ else
+ {
+ // no plane
+ //rAssert(0);
+ }
+
+
+
+
+ //Devin says this is not used. Cleaning up the warnings.
+// if(mFoundTri[i])
+// {
+// mTerrainIntersectCache[i].closestTriPosn = closestTriPosn;
+// mTerrainIntersectCache[i].closestTriNormal = closestTriNormal;
+// }
+
+ mTerrainIntersectCache[ i ].mTerrainType = (eTerrainType)( terrainType & ~0x80 );
+ mTerrainIntersectCache[ i ].mInteriorTerrain = ( terrainType & 0x80 ) == 0x80;
+
+ // stuff in value for later comparison
+ //mIntersectNormalUsed[i] = mTerrainIntersectCache[i].planeNorm;
+ END_PROFILE("PostInter")
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::UseWheelTerrainCollisionInfo
+//=============================================================================
+// Description: Comment
+//
+// per-wheel, calculate and call Wheel::SetYOffsetFromCurrentPosition
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::UseWheelTerrainCollisionInfo(float dt)
+{
+
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ // take the highest value for this from all 4 wheels
+ float bottomOutFix = 0.0f;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+
+ // make copies 'cause we may want to modify local result, but not cached value
+ bool foundPlane = mFoundPlane[i];
+ bool foundTri = mFoundTri[i];
+
+ float fixHeight = 0.0f;
+ bool thisWheelInCollision = false;
+ rmt::Vector fixHeightNormal(0.0f, 1.0f, 0.0f);
+
+ float fixAlongSuspensionAxis = 0.0f;
+
+ Wheel* wheel = mVehicle->mWheels[i];
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetWorldTranslation();
+
+
+ if(!mFoundPlane[i] && !mFoundTri[i])
+ {
+ // we didn't get any good data back, so we need
+ // to use last cached value
+ //rAssert(0); // curious to see if we hit this - yes, a lot
+
+ // just use plane y
+ foundPlane = true;
+ }
+
+ if(foundPlane)
+ {
+ float y = mTerrainIntersectCache[i].planePosn.y;
+
+ // first check
+ // the pathologically bad case:
+ if(mVehicle->mSuspensionWorldSpacePoints[i].y < y)
+ {
+ // this means the suspension rest point on the car is below the terrain
+ // this is fucking bad.
+
+ // TODO: take this out?
+ //rAssert(0);
+
+ // ?? actually need to do anything here?
+
+ // TODO - bump car up?
+
+ }
+
+ //else
+
+ {
+ // hopefully sane case
+
+ // in planePosn, we only really care about the y
+
+ float penetratingDepth = y - (trans.y - wheel->mRadius);
+
+ if(penetratingDepth > 0.0f)
+ {
+ thisWheelInCollision = true;
+ fixHeight = penetratingDepth;
+
+ // fixHeight is going pure up
+ fixHeightNormal.Set(0.0f, 1.0f, 0.0f);
+
+ fixAlongSuspensionAxis = fixHeight / rmt::Fabs( (mVehicle->mVehicleUp).DotProduct(fixHeightNormal));
+
+ }
+ }
+
+ }
+
+
+ // TODO! revisit this!
+
+ if(0)//foundTri)
+ {
+ // calculate fixAlongSuspensionAxis based on this also, and if it's greater than above, use it instead
+
+ rmt::Vector penetrationVector;
+ //penetrationVector.Sub(trans, mWheelTerrainCollisionPoints[i]);
+
+ penetrationVector = trans;
+ penetrationVector.Sub(mTerrainIntersectCache[i].closestTriPosn);
+
+ // TODO - is this safe?
+ float penMag = penetrationVector.Magnitude();
+
+ bool checkBump = false;
+
+ if(trans.y < mTerrainIntersectCache[i].closestTriPosn.y)
+ {
+ // definately need to fix!
+ fixHeight = wheel->mRadius + penMag;
+ thisWheelInCollision = true;
+ fixHeightNormal = mTerrainIntersectCache[i].closestTriNormal;
+
+ float tempFixAlongSuspensionAxis = fixHeight / rmt::Fabs( (mVehicle->mVehicleUp).DotProduct(fixHeightNormal));
+
+ if(tempFixAlongSuspensionAxis > fixAlongSuspensionAxis)
+ {
+ fixAlongSuspensionAxis = tempFixAlongSuspensionAxis;
+ }
+
+ checkBump = true;
+
+ }
+ else if(penMag < wheel->mRadius) // normal case
+ {
+ fixHeight = wheel->mRadius - penMag;
+ thisWheelInCollision = true;
+ fixHeightNormal = mTerrainIntersectCache[i].closestTriNormal;
+
+ float tempFixAlongSuspensionAxis = fixHeight / rmt::Fabs( (mVehicle->mVehicleUp).DotProduct(fixHeightNormal));
+
+ if(tempFixAlongSuspensionAxis > fixAlongSuspensionAxis)
+ {
+ fixAlongSuspensionAxis = tempFixAlongSuspensionAxis;
+ }
+
+ checkBump = true;
+
+ }
+
+ if(checkBump)
+ {
+
+ // if this is too horizontal, we need to deal with it specially
+ float sin10 = 0.1736f;
+ float sin20 = 0.342f;
+ float sin30 = 0.5f;
+
+ // TODO - what angle?
+ if(rmt::Fabs(fixHeightNormal.DotProduct(mVehicle->mVehicleUp)) < sin20)// && fixHeight > wheel->mRadius)
+ {
+ // just bump the whole car back by penMag?
+
+ rmt::Vector bump = fixHeightNormal;
+ bump.NormalizeSafe();
+ bump.Scale(fixHeight);
+
+ // TODO - true or false or tapered down or what?
+ //mVehicle->mSimStateArticulated->DynamicPositionAdjustment(bump, dt, true);
+ //mVehicle->mSimStateArticulated->DynamicPositionAdjustment(bump, dt, false);
+
+ mVehicle->mBottomedOutThisFrame = true;
+
+ continue;
+
+
+ }
+ }
+ }
+
+
+ if(thisWheelInCollision)
+ {
+
+ // perhaps this method should be renamed
+ // it sets the wheelInCollision flag to true!
+ float bottomedOut = wheel->SetYOffsetFromCurrentPosition(fixAlongSuspensionAxis);
+
+ if(bottomedOut > bottomOutFix)
+ {
+ bottomOutFix = bottomedOut;
+ wheel->mWheelBottomedOutThisFrame = true; // not sure if we'll use yet.
+
+ //char buffy[128];
+ //sprintf(buffy, "wheel %d bottomed out this frame\n", i);
+ //rDebugPrintf(buffy);
+ }
+
+ }
+
+ } // end for(i
+
+
+ if(bottomOutFix > 0.0f)
+ {
+
+ // this is the amount the suspension couldn't deal with.
+ //
+ // what to do?
+ // in srr1 we just bumped the whole chassis up by this amount - seemed to work fairly well actually.
+ //rmt::Vector inDeltaPos = mVehicle->mVehicleUp;
+
+ // Note!
+ // we hav to be careful how to deal with this 'cause we can get some weird ass results that make
+ // the car go flying after seemingly minor crashes
+
+ // hack, but might work well - only correct the vehicle straight up!
+
+ //rmt::Vector inDeltaPos = fixHeightNormal;
+
+ rmt::Vector inDeltaPos;
+
+ inDeltaPos = GetWorldPhysicsManager()->mWorldUp;
+
+
+ // sanity test?
+ if(bottomOutFix > (mVehicle->mWheels[0]->mRadius * 0.5f))
+ {
+ bottomOutFix = (mVehicle->mWheels[0]->mRadius * 0.5f);
+ }
+
+
+
+
+ if(inDeltaPos.y > 0.0f) // this test is pointless right now
+ {
+
+ inDeltaPos.Scale(bottomOutFix);
+
+ // TODO - revisit this
+
+
+
+///you are here
+//take this out - see what f1 car does
+
+// no this was not the problem.
+
+// get kevin to raise the bv
+
+ mVehicle->mSimStateArticulated->DynamicPositionAdjustment(inDeltaPos, dt, false);
+
+
+
+ // ?
+ // how 'bout just applying some impulse to the sucker?
+
+ /*
+ if(0)//mVehicle->mOkToCrashLand)
+ {
+ rmt::Matrix groundPlaneMatrix = mVehicle->mGroundPlaneSimState->GetTransform(-1);
+ rmt::Vector groundPlaneUp = groundPlaneMatrix.Row(2); // recall - ground plane is oriented weird
+
+ rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+
+ static float test = 10.0f;
+ groundPlaneUp.Scale(test);
+
+ linearVel.Add(groundPlaneUp);
+
+ mVehicle->mOkToCrashLand = false;
+
+ }
+ */
+
+ }
+
+ mVehicle->mBottomedOutThisFrame = true;
+ }
+
+
+
+}
+
+
+
diff --git a/game/code/worldsim/redbrick/physicslocomotion.h b/game/code/worldsim/redbrick/physicslocomotion.h
new file mode 100644
index 0000000..446e42a
--- /dev/null
+++ b/game/code/worldsim/redbrick/physicslocomotion.h
@@ -0,0 +1,135 @@
+/*===========================================================================
+ physicslocomotion.h
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _PHYSICSLOCOMOTION_H
+#define _PHYSICSLOCOMOTION_H
+
+#include <worldsim/redbrick/vehiclelocomotion.h>
+#include <render/intersectmanager/intersectmanager.h> // For the terrain type enumeration.
+#include <radmath/radmath.hpp>
+
+class Vehicle;
+class Wheel;
+
+class PhysicsLocomotion : public VehicleLocomotion
+{
+public:
+
+ PhysicsLocomotion(Vehicle* vehicle);
+ ~PhysicsLocomotion();
+
+ virtual void PreSubstepUpdate();
+ virtual void PreCollisionPrep(bool firstSubstep);
+
+ virtual void UpdateVehicleGroundPlane();
+
+ virtual void PreUpdate();
+ virtual void Update(float dt);
+ virtual void PostUpdate();
+
+
+ // call this from Vehicle::SetTransform
+ void SetTerrainIntersectCachePointsForNewTransform();
+
+ virtual void CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint);
+
+//private: for the purposes of debug printout
+
+ void MoveWheelsToBottomOfSuspension();
+
+ void FetchWheelTerrainCollisionInfo();
+ void UseWheelTerrainCollisionInfo(float dt);
+
+ void TipTest();
+ void ApplyAngularDrag();
+
+ void StuckOnSideTest(float dt);
+
+ void DurangoStyleStabilizer(float dt);
+
+
+ // delete all this crap soon
+
+ //float mWheelTerrainCollisionFixDepth[4];
+ //rmt::Vector mWheelTerrainCollisionPoints[4];
+ //rmt::Vector mWheelTerrainCollisionNormals[4];
+ //bool mGoodWheelTerrainCollisionValue[4];
+
+ bool mFoundTri[4];
+ bool mFoundPlane[4];
+
+
+ void CorrectWheelYPositions(); // used to be preupdate
+
+ void SetForceApplicationPoints();
+ rmt::Vector mForceApplicationPoints[4];
+ rmt::Vector mSuspensionPointVelocities[4];
+
+ void ApplySuspensionForces(float dt, bool atrest);
+ float mCachedSuspensionForceResults[4];
+ float CalculateSuspensionForce(Wheel* wheel, float suspensionPointYVelocity, float dt, float& forceToUse);
+
+ void ApplyControllerForces2(float dt);
+ void GasForce2(rmt::Vector& forceResult, int wheelNum, float dt);
+ void BrakeForce2(rmt::Vector& forceResult, int wheelNum, float dt);
+ void ReverseForce2(rmt::Vector& forceResult, int wheelNum);
+ void SteeringForce2(rmt::Vector& forceResult, int wheelNum, float dt);
+ //void LimitTireForce(rmt::Vector& totalForce, int wheelNum);
+ void TestControllerForces(rmt::Vector* totalForce);
+ void SetSkidLevel();
+
+ void UpdateSteeringForce(float dt);
+ float mCurrentSteeringForce;
+
+ void ApplyTerrainFriction(rmt::Vector* totalForce);
+
+
+ void TestSteeringForce(rmt::Vector& force, int index);
+
+ void EBrakeEffect(rmt::Vector& forceResult, int wheelNum);
+ void DoughnutTest();
+
+ void ApplyDragForce();
+ void Weeble();
+
+ void HighSpeedInstabilityTest();
+
+ void ApplyRollingFriction();
+
+ void LowSpeedTest();
+
+ // just for convenience in internal methods
+ Vehicle* mVehicle;
+
+ struct TerrainIntersectCache
+ {
+ rmt::Vector closestTriPosn;
+ rmt::Vector closestTriNormal;
+ rmt::Vector planePosn;
+ rmt::Vector planeNorm;
+ eTerrainType mTerrainType;
+ bool mInteriorTerrain;
+ };
+
+ TerrainIntersectCache mTerrainIntersectCache[4]; // is this wasting too much memory?
+
+ // need this because if we're driving on a static collision volume the terrain normal could be way off!
+ rmt::Vector mIntersectNormalUsed[4];
+
+ friend class Vehicle;
+
+
+
+};
+
+
+#endif // _PHYSICSLOCOMOTION_H
diff --git a/game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp b/game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp
new file mode 100644
index 0000000..00ae0aa
--- /dev/null
+++ b/game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp
@@ -0,0 +1,1686 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: physicslocomotioncontrollerforces.cpp
+//
+// Description: moved some methods into a different cpp file 'cause it was getting
+// too big
+//
+// History: May 13, 2001 + Created -- gmayer
+//
+//=============================================================================
+
+
+//========================================
+// System Includes
+//========================================
+
+#include <simcommon/simstate.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <simphysics/physicsobject.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+
+#include <raddebug.hpp>
+
+#include <gameflow/gameflow.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/avatarmanager.h>
+
+
+#include <cheats/cheatinputsystem.h>
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/redbrick/physicslocomotion.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#ifdef WORKING_ON_GREG_PHIZTEST_RIGHT_NOW
+#include "../../../../users/greg/phiztest/code/worldsim/redbrick/phizsim.h" // just for a temp hack using world up
+#else
+#include <worldsim/worldphysicsmanager.h>
+#endif
+
+#include <render/IntersectManager/IntersectManager.h>
+
+using namespace sim;
+
+
+// test of terrain friction
+ // enum value
+const float TF_Road = 1.0f; // 0
+const float TF_Grass = 0.7f; // 1
+const float TF_Sand = 0.6f; // 2
+const float TF_Gravel = 0.6f; // 3
+const float TF_Water = 0.6f; // 4
+const float TF_Wood = 1.0f; // 5
+const float TF_Metal = 1.0f; // 6
+const float TF_Dirt = 0.6f; // 7
+
+
+
+
+
+
+//=============================================================================
+// PhysicsLocomotion::ApplyControllerForces2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyControllerForces2(float dt)
+{
+ // for each wheel, calculate the total requested force, and then
+ // chop it based on the force circle?
+
+ mVehicle->mBurnoutLevel = 0.0f;
+
+ int i;
+ rmt::Vector totalForce[4];
+ for(i = 0; i < 4; i++)
+ {
+ totalForce[i].Clear();
+
+
+ if(/*mVehicle->mWheels[i]->mWheelInCollision &&*/ !(mVehicle->mWheels[i]->mWheelBottomedOutThisFrame))
+ {
+
+ // TODO
+ // only do loop if wheel in collision?
+ // else reset some shit like the wheel state
+
+ // TODO
+ // how about wheel bottomed out this frame?
+ // only affects steering? or all?
+
+ //rmt::Vector totalForce(0.0f, 0.0f, 0.0f);
+
+ GasForce2(totalForce[i], i, dt);
+ // TODO - make sure you set skid level and renderspinuprate and shit like that.
+
+ BrakeForce2(totalForce[i], i, dt);
+ // TODO - somewhere else add code to bring car to a standstill
+
+ ReverseForce2(totalForce[i], i);
+
+ EBrakeEffect(totalForce[i], i);
+
+ SteeringForce2(totalForce[i], i, dt);
+
+
+
+ }
+ //else
+ {
+ // reset wheel state?
+ }
+
+ }
+
+ // DoughnutTest(); I think we need a state for this.
+
+
+ // test if we should change vehicle state
+ // TODO - test for airborn etc.. before calling this?
+
+ TestControllerForces(totalForce); // <- mVehicleState only changed in here!
+
+ // new - Mar 12, 2003
+ //
+ // use state to set "target" steering force.
+ //
+ // move towards target based on time rate
+ //
+ //
+
+ // next step - make let-off gas get you out of slip state
+
+ // how does gasreducinggrip thing fit into all of this?
+
+ // next step - make skid level 0 to 1 based on have we gotten to our target level yet?
+
+ //UpdateSteeringForce(dt);
+
+
+ // not doing this anymore - only step is really to set good skid level
+
+
+
+ SetSkidLevel();
+
+ // TODO - actually limit locomotive and other forces??????
+
+ // cheap and easy test of terrain friction:
+ //ApplyTerrainFriction(totalForce);
+
+
+ for(i = 0; i < 4; i++)
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(totalForce[i], &(mForceApplicationPoints[i]));
+ }
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::UpdateSteeringForce
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+/*
+
+ // I don't think plum wants this now
+
+void PhysicsLocomotion::UpdateSteeringForce(float dt)
+{
+ // default changerate: 50 units / second
+ //static float changeRate = 10.0f;
+ //static float changeRate = 5.0f;
+ static float changeRate = 1.0f;
+
+ float changethisframe = changeRate * dt;
+
+ float target = 80.0f; // just some default - should never use
+
+
+ // if we are not VT_USER, switch immediately.
+
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ // "target" - mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+
+ target = mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+
+ }
+ else if(mVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ // "target" - mVehicle->mDesignerParams.mDpTireLateralResistanceSlip
+
+ target = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip;
+
+ }
+ else
+ {
+ // regular slip
+ // "target" - mVehicle->mDesignerParams.mDpTireLateralResistanceSlipNoEBrake
+
+ target = mVehicle->mDesignerParams.mDpTireLateralResistanceSlipNoEBrake;
+
+ }
+
+ if(mCurrentSteeringForce > target)
+ {
+ if( (mCurrentSteeringForce - changethisframe) > target)
+ {
+ mCurrentSteeringForce -= changethisframe;
+ }
+ else
+ {
+ mCurrentSteeringForce = target;
+ }
+ }
+ else if(mCurrentSteeringForce < target)
+ {
+ if((mCurrentSteeringForce + changethisframe) < target)
+ {
+ mCurrentSteeringForce += changethisframe;
+ }
+ else
+ {
+ mCurrentSteeringForce = target;
+ }
+ }
+
+
+
+
+}
+
+*/
+
+//=============================================================================
+// PhysicsLocomotion::ApplyTerrainFriction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector* totalForce)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyTerrainFriction(rmt::Vector* totalForce)
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ switch (mTerrainIntersectCache[i].mTerrainType)
+ {
+
+ case TT_Road:
+ totalForce[i].Scale(TF_Road);
+ break;
+
+ case TT_Grass:
+ totalForce[i].Scale(TF_Grass);
+ break;
+
+ case TT_Dirt:
+ totalForce[i].Scale(TF_Dirt);
+ break;
+
+ case TT_Water:
+ totalForce[i].Scale(TF_Water);
+ break;
+
+ case TT_Gravel:
+ totalForce[i].Scale(TF_Gravel);
+ break;
+
+ case TT_Wood:
+ totalForce[i].Scale(TF_Wood);
+ break;
+
+ case TT_Sand:
+ totalForce[i].Scale(TF_Sand);
+ break;
+
+ case TT_Metal:
+ totalForce[i].Scale(TF_Metal);
+ break;
+
+ default:
+ break;
+
+ }
+
+ }
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::GasForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, float dt, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::GasForce2(rmt::Vector& forceResult, int wheelNum, float dt)
+{
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel)
+ {
+ float desiredForceMag = 0.0f;
+
+ // burnout check
+ //
+ // TODO
+ // only high powered vehicles should do this? or do for longer?
+ //if(mVehicle->mDesignerParams.mDpGasScale >
+
+ // maybe the % topspeed for which you do it is greater for high powered vehicles
+
+ // try mapping 5.0 (average value) to %20 topspeed
+ //static float burnouttunebullshit = 0.04f;
+ const float burnouttunebullshit = 0.03f;
+
+ //static float gasdecrease = 0.5f;
+ const float gasdecrease = 0.75f;
+
+ //static float burnoutsound = 0.7f;
+ const float gasvalue = 0.99f;
+
+ //float burnoutRange = mVehicle->mDesignerParams.mDpGasScale * burnouttunebullshit;
+
+ float burnoutRange = mVehicle->mDesignerParams.mDpBurnoutRange;
+
+ // need new designer param for this percent of top speed
+ //
+ // use same value for doing doughnut?
+
+ // !! TODO - get hard/soft press on ps2!
+ //
+ // this is not gonna happen
+ // if you put the ps2 button into analog mode, you gotta mash it to get _anything_!
+
+ if(mVehicle->mPercentOfTopSpeed < burnoutRange && mVehicle->mGas > gasvalue && mVehicle->mVehicleType == VT_USER)
+ {
+ // burnout
+ //
+ // skid, smoke, spin and non loco
+
+ // the regular calculation would give this:
+ //desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ //desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * /*mVehicle->mGas*/ 1.0f * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+ desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ desiredForceMag *= gasdecrease;
+
+ //mVehicle->mBurnoutLevel = burnoutsound;
+ mVehicle->mBurnoutLevel = (1.0f - (0.5f * (mVehicle->mPercentOfTopSpeed / burnoutRange)));
+ if(mVehicle->mBurnoutLevel < 0.0f)
+ {
+ mVehicle->mBurnoutLevel = 0.0f;
+ }
+
+
+
+ // additional test for donut?
+ // here?
+ if(rmt::Fabs(mVehicle->mUnmodifiedInputWheelTurnAngle) > 0.6)
+ {
+ //const float magicshit = 4.0f;
+ //float magicshit = 9.0f;
+ float magicshit = mVehicle->mDesignerParams.mDpDonutTorque;
+
+ rmt::Vector etorque = mVehicle->mVehicleUp;
+ float dir = 0.0f;
+ if(mVehicle->mWheelTurnAngle > 0.0f)
+ {
+ dir = 1.0f;
+ }
+ if(mVehicle->mWheelTurnAngle < 0.0f)
+ {
+ dir = -1.0f;
+ }
+
+ //float speedeffect = mVehicle->mPercentOfTopSpeed * 2.0f;
+ //if(speedeffect > 1.0f)
+ //{
+ // speedeffect = 1.0f;
+ //}
+
+ float speedmodifier = 1.0f - mVehicle->mPercentOfTopSpeed;
+
+ etorque.Scale(magicshit * dir * mVehicle->mDesignerParams.mDpMass * speedmodifier);
+
+ //mVehicle->mPhObj->AddTorque(etorque);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(etorque);
+
+ }
+ else
+ {
+ // speed burst build up here....
+
+ if(mVehicle->mEBrake > 0.5f)
+ {
+ mVehicle->mSpeedBurstTimer += dt;
+ mVehicle->mBuildingUpSpeedBurst = true;
+ mVehicle->mDoSpeedBurst = false;
+
+ //if(mVehicle->mSpeedBurstTimer > 3.0f)
+ if(mVehicle->mSpeedBurstTimer > mVehicle->mDesignerParams.mDpMaxSpeedBurstTime)
+ {
+ mVehicle->mSpeedBurstTimer = mVehicle->mDesignerParams.mDpMaxSpeedBurstTime;
+ }
+ }
+ else
+ {
+ if(mVehicle->mBuildingUpSpeedBurst)
+ {
+ rAssert(mVehicle->mDoSpeedBurst == false);
+ // just let off the button
+ mVehicle->mBuildingUpSpeedBurst = false;
+ mVehicle->mDoSpeedBurst = true;
+
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC(0);
+ SuperCam* sc = scc->GetActiveSuperCam();
+
+ mVehicle->mFOVToRestore = sc->GetFOV();
+
+ mVehicle->mSpeedBurstTimerHalf = mVehicle->mSpeedBurstTimer * 0.5f;
+
+
+ }
+ }
+
+ }
+
+
+ }
+ else
+ {
+
+ //!
+ // TODO
+ //
+ // take analog gas usage out of here?
+
+ if(mVehicle->mGas > 0.0f)
+ {
+
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ if(mVehicle->mVehicleType == VT_USER && mVehicle->mPercentOfTopSpeed > mVehicle->mDesignerParams.mDpGasScaleSpeedThreshold)
+ {
+ // use high speed gas scale
+ desiredForceMag = mVehicle->mDesignerParams.mDpHighSpeedGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ }
+ else
+ {
+ desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ }
+
+ }
+ else
+ {
+ // slip
+
+ desiredForceMag = mVehicle->mDesignerParams.mDpSlipGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas* mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ }
+ }
+
+ // if we get out of speedburst range we should probably null out
+
+
+ //mVehicle->mBuildingUpSpeedBurst = false;
+ //mVehicle->mSpeedBurstTimer = 0.0f;
+ //mVehicle->mDoSpeedBurst = false;
+
+ }
+
+
+ // regardless of burnout or normal
+ // test for wheelie down here
+ if(mVehicle->mPercentOfTopSpeed < mVehicle->mDesignerParams.mDpWheelieRange && mVehicle->mGas > gasvalue && mVehicle->mVehicleType == VT_USER)
+ {
+ // set wheelie offsets
+
+ if(!(mVehicle->mDoingWheelie) && !(mVehicle->mWeebleOn))
+ {
+ rmt::Vector wheelie = mVehicle->mCMOffset;
+
+ wheelie.y += mVehicle->mDesignerParams.mDpWheelieYOffset;
+ wheelie.z += mVehicle->mDesignerParams.mDpWheelieZOffset;
+
+ //mVehicle->mPhObj->SetExternalCMOffset(wheelie);
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(wheelie);
+
+ mVehicle->mDoingWheelie = true;
+ }
+
+
+ }
+ else if(mVehicle->mDoingWheelie)
+ {
+ //mVehicle->mPhObj->SetExternalCMOffset(mVehicle->mCMOffset);
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ mVehicle->mDoingWheelie = false;
+ }
+
+
+
+ rmt::Vector force = mVehicle->mVehicleTransverse;
+ //force.CrossProduct(mTerrainIntersectCache[wheelNum].planeNorm);
+ force.CrossProduct(mIntersectNormalUsed[wheelNum]);
+
+ // just in case
+ force.NormalizeSafe();
+
+
+ if(mVehicle->mDoSpeedBurst)
+ {
+ desiredForceMag *= 2.0f;
+ mVehicle->mSpeedBurstTimer -= dt;
+
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC(0);
+ SuperCam* sc = scc->GetActiveSuperCam();
+
+
+ if(mVehicle->mSpeedBurstTimer <= 0.0f)
+ {
+ mVehicle->mSpeedBurstTimer = 0.0f;
+ mVehicle->mDoSpeedBurst = false;
+
+ //sc->SetFOV(mVehicle->mFOVToRestore); - this shoudl all be moved to super cam
+ }
+ else
+ {
+ //static float camhack = 1.0f;
+ //static float camhack = 0.015f;
+ float camhack = 0.015f;
+ float fov;
+
+ if(mVehicle->mSpeedBurstTimer > (mVehicle->mSpeedBurstTimerHalf * 1.5f))
+ {
+ fov = sc->GetFOV();
+ fov = fov + camhack;
+ //sc->SetFOV(fov); this shoudl all be moved to super cam
+ }
+ else if(mVehicle->mSpeedBurstTimer < (mVehicle->mSpeedBurstTimerHalf * 0.5f))
+ {
+ fov = sc->GetFOV();
+ fov = fov - camhack;
+ //sc->SetFOV(fov); this shoudl all be moved to super cam
+ }
+ // else do nothing in the mid-range
+ }
+
+ }
+
+ if(mVehicle->mDoingJumpBoost)
+ {
+ // want to boost the jump
+ // proportioal to the cars power, and current speed, and should have gas held down
+ if(mVehicle->mGas > 0.5f && mVehicle->mPercentOfTopSpeed > 0.3f && mVehicle->GetSpeedKmh() < 140.0f)
+ {
+ //static float gasscaleboosteffect = 0.1f;
+ //const float gasscaleboosteffect = 0.08f;
+ float boostEffect = 0.0f;
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT )
+ {
+ // do special boost only for AI in supersprint
+ rAssert( mVehicle->mVehicleType == VT_AI );
+ boostEffect = 1.4f;
+ }
+ else
+ {
+ boostEffect = 0.07f;
+ }
+ const float gasscaleboosteffect = boostEffect;
+
+ //float multiplier = 1.0f + /* mVehicle->mPercentOfTopSpeed */ + (mVehicle->mDesignerParams.mDpGasScale * gasscaleboosteffect);
+ const float avgGasScale = 7.0f;
+
+ float multiplier = 1.0f + /* mVehicle->mPercentOfTopSpeed */ + (avgGasScale * gasscaleboosteffect);
+
+ desiredForceMag *= multiplier;
+ }
+
+ }
+
+
+ //rmt::Vector force = mVehicle->mVehicleFacing;
+
+ // just in case
+ // you only get speed burst of buildup if you hold down gas
+ if(mVehicle->mGas == 0.0f && (mVehicle->mDoSpeedBurst || mVehicle->mSpeedBurstTimer > 0.0f))
+ {
+ mVehicle->mSpeedBurstTimer = 0.0f;
+ mVehicle->mDoSpeedBurst = false;
+ }
+
+ if(GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() == mVehicle)
+ {
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_HIGH_ACCELERATION))
+ {
+ desiredForceMag *= 2.0f;
+ }
+ }
+
+
+
+ force.Scale(desiredForceMag);
+
+ forceResult.Add(force);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::DoughnutTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int wheelNum)
+//
+// Return: float
+//
+//=============================================================================
+void PhysicsLocomotion::DoughnutTest()
+{
+ // already know this is a drive wheel
+/*
+ static float test1 = 0.3f;
+ static float test2 = 0.8f;
+ static float gastest = 0.9f;
+ static float amount = 18.0f;
+
+ if( mVehicle->mPercentOfTopSpeed < test1 && rmt::Fabs(mVehicle->mUnmodifiedInputWheelTurnAngle) > test2 && mVehicle->mGas > gastest)
+ {
+ rmt::Vector doughnutTorque = mVehicle->mVehicleUp;
+
+ // nasty hardcoded logic
+ // -ve is left turn
+
+ if(mVehicle->mUnmodifiedInputWheelTurnAngle < 0.0f)
+ {
+ doughnutTorque.Scale(-amount * mVehicle->mDesignerParams.mDpMass);
+ }
+ else
+ {
+ doughnutTorque.Scale(amount* mVehicle->mDesignerParams.mDpMass);
+ }
+
+ mVehicle->mPhObj->AddTorque(doughnutTorque);
+
+ }
+*/
+}
+
+//=============================================================================
+// PhysicsLocomotion::BrakeForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::BrakeForce2(rmt::Vector& forceResult, int wheelNum, float dt)
+{
+
+ //mBrakeTimer = 0.0f;
+ //mBrakeActingAsReverse = false;
+
+ // this is exactly reverse force
+
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel)
+ {
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ // if we're coming to a stop we want to use brakescale
+ // if we're going backwards want to use gasscale
+
+ float desiredForceMag = 0.0f;
+
+
+ float proj = mVehicle->mVelocityCM.DotProduct(mVehicle->mVehicleFacing);
+
+ // don't want just any slight backwards motion to trigger
+ const float cos120 = -0.5f;
+
+ //if(proj < cos120)
+ if(proj < 0.0f)
+ {
+ // going backwards
+
+ // same accel and top speed reverse as forward
+
+ if(mVehicle->mVehicleType == VT_USER && mVehicle->mPercentOfTopSpeed > mVehicle->mDesignerParams.mDpGasScaleSpeedThreshold)
+ {
+ desiredForceMag = mVehicle->mDesignerParams.mDpHighSpeedGasScale * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass;
+ }
+ else
+ {
+ desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass;
+ }
+
+ if(mVehicle->mSpeedKmh < 50.0f)
+ {
+ mVehicle->mBurnoutLevel = mVehicle->mBrake;
+ }
+ }
+ else
+ {
+ if(mVehicle->mGas > 0.1f)
+ {
+ desiredForceMag = (mVehicle->mDesignerParams.mDpGasScale) * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass * 0.75f;// magic number is gasdecrease when doing burnout
+ }
+ else
+ {
+ desiredForceMag = (mVehicle->mDesignerParams.mDpBrakeScale + 3.0f) * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+ }
+ }
+
+
+
+
+
+
+
+
+
+ rmt::Vector force = mVehicle->mVehicleFacing;
+ force.Scale(-1.0f * desiredForceMag);
+
+ // TODO - make this a state?
+ // so that we can reduce the grip on the rear wheels and totally up the front?
+
+ forceResult.Add(force);
+
+ // if we are actually going in reverse
+ //if(mVehicle->mVehicleFacing.DotProduct(mVehicle->mVelocityCM) < 0.0f)
+ //{
+ // mVehicle->mBurnoutLevel = mVehicle->mBrake;
+ //}
+
+ }
+
+
+
+ /*
+ //static float test = 0.8f;
+ const float test = 0.8f;
+
+ //const float lowspeed = 1.0f;
+ const float lowspeed = 1.0f;
+
+
+
+
+ // copy of test from rolling friction -
+ // holding brake down should hold car perfectly still....
+ if(mVehicle->mBrakeActingAsReverse)
+ {
+ if(mVehicle->mSpeed > lowspeed)
+ {
+ float desiredForceMag = mVehicle->mBrake * mVehicle->mDesignerParams.mDpBrakeScale * mVehicle->mDesignerParams.mDpMass;
+
+ rmt::Vector force = mSuspensionPointVelocities[wheelNum];
+
+ force.NormalizeSafe();
+ force.Scale(-1.0f * desiredForceMag);
+
+ forceResult.Add(force);
+ }
+ else if(mVehicle->mBrake > 0.7f)
+ {
+ // TODO - this may have to change with brakestands!
+
+ // whittle away speed
+ rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ linearVel.Scale(test);
+ angularVel.Scale(test);
+
+
+ }
+ }
+ */
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::ReverseForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ReverseForce2(rmt::Vector& forceResult, int wheelNum)
+{
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel)
+ {
+ float desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mReverse * mVehicle->mDesignerParams.mDpMass;
+
+ rmt::Vector force = mVehicle->mVehicleFacing;
+ force.Scale(-1.0f * desiredForceMag);
+
+ // TODO - make this a state?
+ // so that we can reduce the grip on the rear wheels and totally up the front?
+
+ forceResult.Add(force);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::EBrakeEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::EBrakeEffect(rmt::Vector& forceResult, int wheelNum)
+{
+ // in addition to reducing the rear grip, we want to apply (weak?) brakes too?
+
+ // since people are using the handbrake like a parking brake, I guess I better acknowledge...
+ static const float reallylowspeed = 0.5f;
+ if(mVehicle->mSpeed < reallylowspeed && mVehicle->mEBrake == 1.0f)
+ {
+ // someone's trying to use it to hold the car in place, so just let the rest test do it's thing
+ return;
+ }
+
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel && mVehicle->mWheels[wheelNum]->mWheelTurnAngle == 0.0f)
+ {
+ // just lock up steer/drive wheels?????
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ //float desiredForceMag = mVehicle->mEBrake * mVehicle->mDesignerParams.mDpBrakeScale * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+ const float defaultebrakebrakescalethatplumishappywith = 3.0f;
+ float desiredForceMag = mVehicle->mEBrake * defaultebrakebrakescalethatplumishappywith /*mVehicle->mDesignerParams.mDpBrakeScale*/ * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ rmt::Vector force = mSuspensionPointVelocities[wheelNum];
+
+ force.NormalizeSafe();
+ force.Scale(-1.0f * desiredForceMag);
+
+ forceResult.Add(force);
+
+ }
+
+
+
+
+ //mVehicle->mVehicleState = VS_SLIP;
+
+ // instead of adding another state variable, just put this calculation right in steering code?
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip * (1.0f - mVehicle->mEBrake);
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::SteeringForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::SteeringForce2(rmt::Vector& forceResult, int wheelNum, float dt)
+{
+ rmt::Vector lateralDirection = mVehicle->mVehicleTransverse;
+
+
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ rmt::Matrix steeringMatrix; // TODO - make this a class member?
+ steeringMatrix.Identity();
+ steeringMatrix.FillRotateY(mVehicle->mWheels[wheelNum]->mWheelTurnAngle);
+
+ lateralDirection.Transform(steeringMatrix);
+ }
+
+ //float lateralVelocityProjection = lateralDirection.DotProduct(mSuspensionPointVelocities[wheelNum]);
+
+
+
+ rmt::Vector temp = mSuspensionPointVelocities[wheelNum];
+
+ //rmt::Vector temp = mVehicle->mVelocityCM;
+
+
+ float desiredLateralResistanceForce = 0.0f;
+
+ //const float threshold = 0.15f;
+ //static float threshold = 0.15f;
+ const float threshold = 0.15f;
+ if(mVehicle->mPercentOfTopSpeed > threshold)
+ {
+ //-------
+ // normal
+ //-------
+
+ // this is the normal thing to do for regular and high-speed driving
+ temp.NormalizeSafe();
+
+ float lateralVelocityProjection = lateralDirection.DotProduct(temp);
+ // the higher this is the more we're asking this tire to do for us!
+
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection *
+ mVehicle->mDesignerParams.mDpTireLateralResistanceNormal) /*mCurrentSteeringForce)*/ * 0.25f;
+ }
+ else if(mVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ // slip
+ {
+
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection *
+ mVehicle->mDesignerParams.mDpTireLateralResistanceSlip) /*mCurrentSteeringForce)*/ * 0.25f;
+
+
+
+ }
+ }
+ else // just VS_SLIP
+ {
+ // slip without ebrake!
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection *
+ mVehicle->mDesignerParams.mDpTireLateralResistanceSlipNoEBrake /*mCurrentSteeringForce*/) * 0.25f;
+
+
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mDesignerParams.mDpSlipEffectNoEBrake);
+
+ }
+ else
+ {
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mDesignerParams.mDpSlipEffectNoEBrake);
+ }
+
+ }
+
+
+/*
+
+ // put ebrake effect outside of any state
+
+ if(mVehicle->mEBrake > 0.1) // note: we can be in this state without always holding the button down
+ {
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ //desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+ else
+ {
+ // decrease resistance on rear
+ //desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mDesignerParams.mDpEBrakeEffect);
+ }
+
+ }
+
+*/
+
+
+
+ // rockford...?
+
+
+ // this is the condition for beginning a rockford
+ if( mVehicle->mVehicleFacing.DotProduct(mVehicle->mVelocityCM) < 0.0f && mVehicle->mEBrake < 0.1 && rmt::Fabs(mVehicle->mUnmodifiedInputWheelTurnAngle) > 0.5f && mVehicle->mGas == 0.0f )//||
+ //mVehicle->mDoingRockford )
+ {
+
+ mVehicle->mDoingRockford = true;
+ // rockford in proportion to ebrake setting
+
+ // note - the + and - are inverted from regular ebrake effect
+
+
+ //static float magicshit = 20.0f;
+ //static float mDpRockfordEffect = 0.5f;
+ float mDpRockfordEffect = 0.5f;
+
+
+
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ //desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f - mDpRockfordEffect);
+ }
+ else
+ {
+ // decrease resistance on rear
+ //desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f + mDpRockfordEffect);
+ }
+
+
+ float hackbullshit = 10.0f;
+ float magicshit = hackbullshit * mDpRockfordEffect;
+
+ rmt::Vector rockfordtorque = mVehicle->mVehicleUp;
+ float dir = 0.0f;
+ if(mVehicle->mWheelTurnAngle > 0.0f)
+ {
+ dir = -1.0f;
+ }
+ if(mVehicle->mWheelTurnAngle < 0.0f)
+ {
+ dir = 1.0f;
+ }
+
+
+ float speedeffect = mVehicle->mPercentOfTopSpeed;
+
+ rockfordtorque.Scale(magicshit * dir * mVehicle->mDesignerParams.mDpMass * speedeffect);
+
+ //mVehicle->mPhObj->AddTorque(rockfordtorque);
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(rockfordtorque);
+
+
+ mVehicle->mBurnoutLevel = 1.0f;
+
+
+
+
+ //hmmm....
+ //static float resdrop = 0.001f;
+ float resdrop = 0.001f;
+ //desiredLateralResistanceForce *= resdrop;
+
+ // new place to set this
+ mVehicle->mCollisionLateralResistanceDropFactor = resdrop;
+
+ }
+
+
+ // new - reduce traction on drive wheels if givin 'er gas
+ const float gasmodifierthreshold = 0.7f;
+ const float absoluteSpeedThreshold = 90.0f;
+ if( mVehicle->mWheels[wheelNum]->mDriveWheel && mVehicle->mVehicleType == VT_USER && mVehicle->mGas > 0.0f &&
+ mVehicle->mPercentOfTopSpeed > gasmodifierthreshold && rmt::Fabs(mVehicle->mWheelTurnAngleInputValue) > 0.1f &&
+ mVehicle->mSpeedKmh > absoluteSpeedThreshold)
+ {
+ const float maxPenalty = 0.2f;
+ float modifier = 1.0f - (maxPenalty * mVehicle->mGas); // ? also include speed??
+
+ desiredLateralResistanceForce *= modifier;
+
+ mVehicle->mDrawWireFrame = true;
+ mVehicle->mLosingTractionDueToAccel = true; // for plum
+ //mVehicle->mBurnoutLevel = 1.0f;
+
+
+ }
+
+
+
+
+ }
+ else
+ {
+ //----------
+ // low speed
+ //----------
+
+ temp.NormalizeSafe();
+
+ float lateralVelocityProjection = lateralDirection.DotProduct(temp);
+
+ // at low speeds we want to account for the (small) magnitude of the velocity
+ lateralVelocityProjection *= (mVehicle->mPercentOfTopSpeed / threshold);
+
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection * mVehicle->mDesignerParams.mDpTireLateralResistanceNormal) * 0.25f;// * gravFactor;
+ }
+ else
+ {
+ // slip
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection * mVehicle->mDesignerParams.mDpTireLateralResistanceSlip) * 0.25f;// * gravFactor;
+
+ /*
+ if(mVehicle->mEBrake > 0.0)
+ {
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+ else
+ {
+ // decrease resistance on rear
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+
+ }
+ */
+
+
+ }
+
+ }
+
+
+
+ // put ebrake effect outside of any state
+ if(mVehicle->mPercentOfTopSpeed > 0.02f)
+ {
+ if(mVehicle->mEBrake > 0.1) // note: we can be in this state without always holding the button down
+ {
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ //desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+ else
+ {
+ // decrease resistance on rear
+ //desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mDesignerParams.mDpEBrakeEffect);
+ }
+
+ }
+ }
+
+
+ //rmt::Vector force = lateralDirection;
+
+ // assumign these are good?
+
+ //mTerrainIntersectCache[i].planePosn = planePosn;
+ //mTerrainIntersectCache[i].planeNorm = planeNormal;
+
+
+ //rmt::Vector force = mTerrainIntersectCache[wheelNum].planeNorm;
+
+ rmt::Vector force = mIntersectNormalUsed[wheelNum];
+ force.CrossProduct(mVehicle->mVehicleFacing);
+
+ // just in case
+ force.NormalizeSafe();
+
+ //rmt::Vector force = mVehicle->mVehicleTransverse; // I think this is better!
+
+
+
+
+
+ /// new quick terrain based friction test
+ // only affect steering
+
+ switch (mTerrainIntersectCache[wheelNum].mTerrainType)
+ {
+ case TT_Road:
+ desiredLateralResistanceForce *= TF_Road;
+ break;
+
+ case TT_Grass:
+ desiredLateralResistanceForce *= TF_Grass;
+ break;
+
+ case TT_Dirt:
+ desiredLateralResistanceForce *= TF_Dirt;
+ break;
+
+ case TT_Water:
+ desiredLateralResistanceForce *= TF_Water;
+ break;
+
+ case TT_Gravel:
+ desiredLateralResistanceForce *= TF_Gravel;
+ break;
+
+ case TT_Wood:
+ desiredLateralResistanceForce *= TF_Wood;
+ break;
+
+ case TT_Sand:
+ desiredLateralResistanceForce *= TF_Sand;
+ break;
+
+ case TT_Metal:
+ desiredLateralResistanceForce *= TF_Metal;
+ break;
+
+ default:
+ break;
+
+ }
+
+
+ force.Scale(-1.0f * desiredLateralResistanceForce);
+
+
+ if(1)//mVehicle->mBurnoutLevel == 0.0f)
+ {
+ // test
+ // scale lateral resistance way down if we have just had a collision
+ force.Scale(mVehicle->mCollisionLateralResistanceDropFactor);
+ }
+
+
+ // test before or after the above drop?
+ //TestSteeringForce(force, wheelNum);
+
+
+
+ forceResult.Add(force);
+
+
+
+ // new test
+ // faked high speed instability
+ /*
+ const float highSpeedInstabilityThreshold = 0.6f;
+ if(mVehicle->mPercentOfTopSpeed > highSpeedInstabilityThreshold)
+ {
+ // add some magic torque depending on the input wheel turn angle.
+ if(mVehicle->mWheelTurnAngleInputValue != 0.0f && !(mVehicle->mAirBorn))
+ {
+ static float magicshit = 10.0f;
+
+ rmt::Vector instabilityTorque = mVehicle->mVehicleFacing;
+ instabilityTorque.Scale(magicshit * mVehicle->mDesignerParams.mDpMass * mVehicle->mPercentOfTopSpeed * mVehicle->mWheelTurnAngleInputValue);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(instabilityTorque);
+
+
+ }
+ }
+ */
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::TestSteeringForce
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& force)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::TestSteeringForce(rmt::Vector& force, int index)
+{
+ //mVehicleState not changed here - this is just used to limit the lateral force
+
+ //static float forceCap = 1.0f;
+
+
+ rAssert(0);
+
+
+/*
+ static float rearWheelMult = 1.0f;
+
+ if(mVehicle->mVehicleState == VS_SLIP)
+ {
+
+ float suspensionEffect = mCachedSuspensionForceResults[index];
+
+ float suspensionMinEffect = mVehicle->mSuspensionRestValue;
+
+ if(suspensionEffect < suspensionMinEffect)
+ {
+ suspensionEffect = suspensionMinEffect;
+ }
+
+ float maxTireForce = mVehicle->mDesignerParams.mDpTireLateralStaticGrip * suspensionEffect;
+
+
+ // cap all forces
+ int j;
+ for(j = 0; j < 4; j++)
+ {
+
+ //if(totalForce[j].Magnitude() > forceCap)
+ //{
+ // totalForce[j].NormalizeSafe();
+ // totalForce[j].Scale(forceCap);
+ //}
+
+ if(j < 2)
+ {
+
+ if(force.Magnitude() > maxTireForce * rearWheelMult)
+ {
+ force.NormalizeSafe();
+ force.Scale(maxTireForce * rearWheelMult);
+ }
+ }
+ else
+ {
+ if(force.Magnitude() > maxTireForce)
+ {
+ force.NormalizeSafe();
+ force.Scale(maxTireForce);
+ }
+
+ }
+
+ }
+
+ }
+
+*/
+}
+
+//=============================================================================
+// PhysicsLocomotion::TestControllerForces
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector* totalForce) -- points to array of 4
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::TestControllerForces(rmt::Vector* totalForce)
+{
+
+ // mVehicleState only changed in here!
+
+
+ // find wheel with highest down force
+ int i;
+ int index = 0;
+ for(i = 1; i < 4; i++)
+ {
+ if(mCachedSuspensionForceResults[i] > mCachedSuspensionForceResults[index])
+ {
+ index = i;
+ }
+ }
+
+ // TODO - fix this for airborn!
+ /*
+ float suspensionEffect = mCachedSuspensionForceResults[index];
+
+ float suspensionMinEffect = mVehicle->mSuspensionRestValue;
+
+ if(suspensionEffect < suspensionMinEffect)
+ {
+ suspensionEffect = suspensionMinEffect;
+ }
+ */
+
+ // new test
+ float suspensionEffect = mVehicle->mSuspensionRestValue;
+
+
+ float maxTireForce = mVehicle->mDesignerParams.mDpTireLateralStaticGrip * suspensionEffect;
+
+ /*
+ // new Mar 12, 2003
+ // affect the maxTireForce calculation by gas
+ const float gasmodifierthreshold = 0.7f;
+ const float absoluteSpeedThreshold = 90.0f;
+
+ if(mVehicle->mVehicleType == VT_USER && mVehicle->mGas > 0.0f &&
+ mVehicle->mPercentOfTopSpeed > gasmodifierthreshold && rmt::Fabs(mVehicle->mWheelTurnAngleInputValue) > 0.1f &&
+ mVehicle->mSpeedKmh > absoluteSpeedThreshold)
+ {
+ //const float maxPenalty = 0.2f;
+ static float maxPenalty = 0.4f;
+ float modifier = 1.0f - (maxPenalty * mVehicle->mGas); // ? also include speed??
+
+ // *** desiredLateralResistanceForce *= modifier;
+ maxTireForce *= modifier;
+
+ mVehicle->mDrawWireFrame = true;
+ mVehicle->mLosingTractionDueToAccel = true; // for plum
+ //mVehicle->mBurnoutLevel = 1.0f;
+
+
+ }
+ */
+
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ // if the wheel with the greatest down force is slipping
+ // make whole car slip
+
+ if(mVehicle->mEBrake > 0.1f && mVehicle->mPercentOfTopSpeed > 0.05f)
+ {
+ // change state to controlled skid
+ mVehicle->mVehicleState = VS_EBRAKE_SLIP;
+ }
+ else
+ {
+ // now if wheel index is slipping, whole fucking car is slipping
+ float totalForceMag = totalForce[index].Magnitude();
+ //float maxTireForce = mVehicle->mDesignerParams.mDpTireLateralStaticGrip * suspensionEffect;
+ if(totalForceMag > maxTireForce)
+ {
+ // fucker is sliding
+ mVehicle->mVehicleState = VS_SLIP;
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip;
+
+ }
+ }
+
+ }
+ else
+ {
+ if( !mVehicle->mOutOfControl )
+ {
+ // need heruistic to go back to normal
+ rmt::Vector velDir = mVehicle->mVelocityCM;
+ velDir.NormalizeSafe();
+
+ float cos5 = 0.9962f;
+ float cos10 = 0.9848f;
+ float cos20 = 0.9397f;
+ float cos15 = 0.9659f;
+
+ // in addition to the alignment test, should not go into VS_SLIP if you're going very slow,
+ // ebrake or not.
+
+ if( (velDir.DotProduct(mVehicle->mVehicleFacing) > cos15 && rmt::Fabs(mVehicle->mWheelTurnAngleInputValue) < 0.1f) ||
+ mVehicle->mSpeed < 1.0f ||
+ mVehicle->mGas == 0.0f)
+ {
+ mVehicle->mVehicleState = VS_NORMAL;
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+ }
+
+ /*
+ // duplicate this here in case we only got in here cause the ebrake is held down.
+ if(mVehicle->mEBrake > 0.1f && mVehicle->mSpeed > 1.0f)
+ {
+ mVehicle->mVehicleState = VS_SLIP;
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip;
+ // return;
+ }
+ */
+ }
+ }
+
+
+
+
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::SetSkidLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::SetSkidLevel()
+{
+ mVehicle->mSkidLevel = 0.0f;
+ mVehicle->mSlipGasModifier = 1.0f;
+
+ //static float hack = 0.7f;
+ //mVehicle->mSkidLevel = hack;
+
+
+ float skid = 0.0f;
+ float speedfactor = mVehicle->mPercentOfTopSpeed;
+
+
+
+ rmt::Vector velDir = mVehicle->mVelocityCM;
+ velDir.NormalizeSafe();
+
+
+ float projectionOnly = rmt::Fabs(velDir.DotProduct(mVehicle->mVehicleTransverse));
+
+
+ if(mVehicle->mVehicleState == VS_SLIP || mVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ // static float hack = 0.7f;
+ // mVehicle->mSkidLevel = hack;
+
+
+ if(1)//projectionOnly > 0.7f)
+ {
+ // this actually works quite well
+ skid = projectionOnly;
+ }
+ else
+ {
+ skid = projectionOnly * speedfactor;
+ }
+
+
+ // if they're using the ebrake, we want whichever is loudest:
+ if(mVehicle->mEBrake * 0.5f > skid)
+ {
+ skid = mVehicle->mEBrake * 0.5f;
+ }
+
+ }
+
+ if(mVehicle->mBrake > 0.05f && speedfactor > 0.07f)
+ {
+ /*
+ rmt::Vector velDir = mVehicle->mVelocityCM;
+ velDir.NormalizeSafe();
+
+ float proj = velDir.DotProduct(mVehicle->mVehicleFacing);
+
+ // hmm..
+ if(mVehicle->mBrake * 0.5f > skid && proj > 0.0f)
+ {
+ skid = mVehicle->mBrake * 0.5f;
+ }
+ */
+
+ // not sure if values lower than 0.5 are working...
+ //
+ //
+ float forward = velDir.DotProduct(mVehicle->mVehicleFacing);
+ if(forward > 0.0f && speedfactor < 0.5f)
+ {
+ skid = 1.0f;
+ }
+
+
+ }
+
+ if(mVehicle->mEBrake > 0.85f && speedfactor > 0.07f)// && mVehicle->mWheelTurnAngle == 0.0f)
+ {
+ // hmm..
+ if(mVehicle->mEBrake * 0.5f > skid)
+ {
+ skid = mVehicle->mEBrake * 0.5f;
+ }
+ }
+
+
+ // compare to burnout level
+ if(mVehicle->mBurnoutLevel > skid)
+ {
+ mVehicle->mSkidLevel = mVehicle->mBurnoutLevel;
+ }
+ else
+ {
+ mVehicle->mSkidLevel = skid;
+ }
+
+
+ // still too quiet
+ if(mVehicle->mSkidLevel > 0.0f)
+ {
+ if(mVehicle->mSkidLevel < 0.5f)
+ {
+ mVehicle->mSkidLevel = 0.5f;
+ }
+ }
+
+
+/*
+
+ // override any of the above shit if wheels are not in contact with ground
+ int i;
+ if(mBurnoutLevel > 0.0f)
+ {
+ if(mWheels[0]->mWheelInCollision
+
+
+ for(i = 0; i < 2; i++)
+ {
+ if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
+ {
+ mGeometryVehicle->SetSkidValues(i, mSkidLevel);
+ }
+ }
+ }
+ else
+ {
+ for(i = 0; i < 4; i++)
+ {
+ if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
+ {
+ mGeometryVehicle->SetSkidValues(i, mSkidLevel);
+ }
+ }
+ }
+
+
+*/
+
+
+}
+
+
+
+
+
+
+
+
+
diff --git a/game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp
new file mode 100644
index 0000000..4543079
--- /dev/null
+++ b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp
@@ -0,0 +1,927 @@
+/*===========================================================================
+ redbrickcollisionsolveragent.cpp
+
+ created Jan 28, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+===========================================================================*/
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <mission/gameplaymanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <events/eventmanager.h>
+#include <sound/soundcollisiondata.h>
+
+#ifdef RAD_XBOX
+#include <float.h>
+#endif
+
+
+//------------------------------------------------------------------------
+RedBrickCollisionSolverAgent::RedBrickCollisionSolverAgent()
+{
+ //
+ mBottomedOut = false;
+
+ //mBottomedOutScale = 1.0e-5f;
+ //mBottomedOutScale = 1.0f;
+ //mBottomedOutScale = 0.0002f;
+ mBottomedOutScale = 0.0003f;
+
+
+ mWheelSidewaysHit = false;
+
+ mWheelSidewaysHitScale = 0.002f;
+
+}
+
+
+//------------------------------------------------------------------------
+RedBrickCollisionSolverAgent::~RedBrickCollisionSolverAgent()
+{
+ //
+}
+
+
+//------------------------------------------------------------------------------------------------
+void RedBrickCollisionSolverAgent::ResetCollisionFlags()
+{
+ // TODO - how to properly tie this to rest of vehicle module????
+
+ // TODO - need these flags on a car by car basis
+
+ mBottomedOut = false;
+ mWheelSidewaysHit = false;
+
+}
+
+
+//------------------------------------------------------------------------------------------------
+Solving_Answer RedBrickCollisionSolverAgent::TestImpulse(rmt::Vector& impulse, Collision& inCollision)
+{
+
+ // currently this method does nothing...
+ //
+ // test some collision results...
+
+
+
+
+
+ // TODO -
+ // something more efficient here so we don't have to test this shit every time
+
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ //
+ // SOUND EVENT HERE?
+ //
+
+ // need to convert the impulse to a nice friendly float between 0.0 and 1.0
+ //
+ // just looking at some empirical data, the range of impulse magnitude seems to be from around
+ // 0.0 to 100000.0 - at the high end would be a heavy vehicle (3000 kg) hitting a static wall at 122kmh
+ // a 2000kg car hitting a wall at 150kmh gave 78706.7
+
+
+
+
+
+#ifdef RAD_DEBUG
+ if(rmt::IsNan(impulse.x) || rmt::IsNan(impulse.y) || rmt::IsNan(impulse.z))
+ {
+ rAssert(0);
+ }
+#endif
+
+
+
+
+
+ float impulseMagnitude = impulse.Magnitude();
+
+ if(impulseMagnitude > 100000.0f)
+ {
+ int stophere = 1;
+ }
+
+
+
+ const float maxIntensity = 100000.0f;
+
+ float soundScale = impulseMagnitude / maxIntensity;
+ if(soundScale > 1.0f)
+ {
+ soundScale = 1.0f;
+ }
+ if(soundScale < 0.0f)
+ {
+ rAssert(0);
+ }
+
+// SoundCollisionData soundData( soundScale, simStateA->mAIRefPointer, simStateB->mAIRefPointer );
+// GetEventManager()->TriggerEvent( EVENT_COLLISION, &soundData );
+ // up to this point is pretty generic for sound stuff - ie. the guts of PostReactToCollision might look a lot like this?
+
+
+ // here is more vehicle intensive
+
+ //if(simStateA->mAIRefIndex == Vehicle::GetAIRef() && simStateB->mAIRefIndex == Vehicle::GetAIRef())
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ // car on car
+
+ //static float magic = 1.0f;
+
+ // fun test
+ //impulse.Scale(magic);
+
+ //int stophere = 1;
+
+ if( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_TRAFFIC )
+ {
+
+ }
+
+ }
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle || simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ //PhysicsVehicle* physicsVehicle;
+ Vehicle* vehicle;
+
+ float test = impulse.Magnitude();
+
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
+ {
+ vehicle = (Vehicle*)(simStateA->mAIRefPointer);
+ }
+ else
+ {
+ vehicle = (Vehicle*)(simStateB->mAIRefPointer);
+ }
+
+
+ // TODO - more detailed test...
+
+ // TODO - I'm not sure I like this system...
+
+ if(mBottomedOut) // todo - more detailed test for if we are dealing with a wheel
+ {
+ //mImpulse.Scale(mBottomedOutScale);
+ }
+ if(mWheelSidewaysHit)
+ {
+ //mImpulse.Scale(mWheelSidewaysHitScale);
+
+ //mImpulse.y = 0.0f; // TODO - expand to below later...
+
+ }
+
+ }
+
+ return Solving_Continue;
+}
+
+//=============================================================================
+// RedBrickCollisionSolverAgent::CarOnCarAction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Collision& inCollision)
+//
+// Return: Solving_Answer
+//
+//=============================================================================
+Solving_Answer RedBrickCollisionSolverAgent::CarOnCarPreTest(Collision& inCollision, int inPass)
+{
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ Vehicle* vehicleA = (Vehicle*)(simStateA->mAIRefPointer);
+ Vehicle* vehicleB = (Vehicle*)(simStateB->mAIRefPointer);
+
+ int jointIndexA = inCollision.mIndexA;
+ int jointIndexB = inCollision.mIndexB;
+
+
+
+ //vehicleA->TriggerDamage();
+ //vehicleB->TriggerDamage(); - move the damage triggering to Vehicle::PostReactToCollision
+
+// vehicleA->SetHitJoint(jointIndexA);
+// vehicleB->SetHitJoint(jointIndexB);
+
+
+ // for now...
+ if(1)//vehicleA->mUsingInCarPhysics)
+ {
+ if(vehicleA->IsJointAWheel(jointIndexA) || vehicleB->IsJointAWheel(jointIndexB))
+ {
+ return Solving_Aborted;
+ }
+
+ if(vehicleA->IsAFlappingJoint(jointIndexA) || vehicleB->IsAFlappingJoint(jointIndexB))
+ {
+ // TODO - try taking this out?
+ return Solving_Aborted;
+ }
+ }
+
+ //hmmm
+ // is this the place to switch loco?
+ if(vehicleA->GetLocomotionType() == VL_TRAFFIC)
+ {
+ vehicleA->SetLocomotion(VL_PHYSICS);
+ }
+
+ if(vehicleB->GetLocomotionType() == VL_TRAFFIC)
+ {
+ vehicleB->SetLocomotion(VL_PHYSICS);
+ }
+
+ // just in case it has come to rest....
+
+ if(simStateA->GetControl() == sim::simAICtrl)
+ {
+ simStateA->SetControl(sim::simSimulationCtrl);
+ }
+
+ if(simStateB->GetControl() == sim::simAICtrl)
+ {
+ simStateB->SetControl(sim::simSimulationCtrl);
+ }
+
+
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+}
+
+//=============================================================================
+// RedBrickCollisionSolverAgent::PreCollisionEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Collision& inCollision, int inPass)
+//
+// Return: Solving_Answer
+//
+//=============================================================================
+Solving_Answer RedBrickCollisionSolverAgent::PreCollisionEvent(Collision& inCollision, int inPass)
+{
+ // have a look at objects A and B and see if we care about them:
+
+ // TODO
+ // also, depending how steeply we've hit something, we also want to let the solver do it's thing...
+ // ? mabye cancel out the y component?
+
+ // also may skip this depending how tipped we are
+
+
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle || simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ //--------------------------------------
+ // deal specially with car on car action
+ //--------------------------------------
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ // test for hitting a wheel or a flapping joint in here
+ //
+ return CarOnCarPreTest(inCollision, inPass);
+ }
+
+ Vehicle* vehicle;
+
+ int jointIndex;
+
+ rmt::Vector groundContactPoint;
+ rmt::Vector normalPointingAtCar;
+
+ bool carisA = true;
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ vehicle = (Vehicle*)(simStateA->mAIRefPointer);
+ jointIndex = inCollision.mIndexA;
+
+ groundContactPoint = inCollision.GetPositionB();
+ normalPointingAtCar = inCollision.mNormal;
+
+ }
+ else
+ {
+ vehicle = (Vehicle*)(simStateB->mAIRefPointer);
+ jointIndex = inCollision.mIndexB;
+
+ groundContactPoint = inCollision.GetPositionA();
+ normalPointingAtCar = inCollision.mNormal;
+ normalPointingAtCar.Scale(-1.0f);
+ carisA = false;
+ }
+
+
+ // temp hack cause I think we're hitting our driver every frame:
+/*
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable)
+ {
+ return Solving_Aborted;
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable)
+ {
+ return Solving_Aborted;
+ }
+
+ }
+*/
+
+
+ /*
+
+ this now encompassed below...
+
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateB );
+ }
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateA );
+ }
+ }
+ }
+ */
+
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable || simStateB->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+ sim::SimControlEnum control = simStateB->GetControl();
+ bool ishit = ((DynaPhysDSG*)(simStateB->mAIRefPointer))->mIsHit;
+ enClasstypeID classid = (enClasstypeID)((DynaPhysDSG*)(simStateB->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid();
+
+
+ if(simStateB->GetControl() == sim::simAICtrl && ((DynaPhysDSG*)(simStateB->mAIRefPointer))->mIsHit &&
+ ((DynaPhysDSG*)(simStateB->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE)
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateB );
+ }
+ }
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable || simStateA->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+
+ sim::SimControlEnum control = simStateA->GetControl();
+ bool ishit = ((DynaPhysDSG*)(simStateA->mAIRefPointer))->mIsHit;
+ enClasstypeID classid = (enClasstypeID)((DynaPhysDSG*)(simStateA->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid();
+
+
+
+ if(simStateA->GetControl() == sim::simAICtrl && ((DynaPhysDSG*)(simStateA->mAIRefPointer))->mIsHit &&
+ ((DynaPhysDSG*)(simStateA->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE) // safe cast?
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateA );
+ }
+ }
+
+ }
+
+ }
+
+ if(vehicle->mUsingInCarPhysics)
+ {
+ if(vehicle->IsAFlappingJoint(jointIndex))
+ {
+
+ // vehicle->SetHitJoint(jointIndex);
+
+ return Solving_Aborted;
+ }
+ }
+
+
+
+ if(vehicle->IsJointAWheel(jointIndex))
+ {
+
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizFence)
+ {
+ int stophere = 1;
+
+ // the old tip test
+ rmt::Vector worldUp = GetWorldPhysicsManager()->mWorldUp;
+
+ float cos10 = 0.9848f;
+ //if(worldUp.DotProduct(vehicle->mVehicleUp) < cos10)
+ {
+ // will this work??
+ inCollision.mIndexA = 0;
+ return Solving_Continue;
+
+ }
+
+ }
+
+
+
+ if(simStateB->mAIRefIndex != PhysicsAIRef::redBrickPhizStatic) // redBrickPhizMoveableAnim? -> don't think we need to
+ {
+ if(vehicle->mUsingInCarPhysics)
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ return Solving_Continue;
+ }
+ }
+
+
+ }
+ else
+ {
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizFence)
+ {
+ // the old tip test
+ rmt::Vector worldUp = GetWorldPhysicsManager()->mWorldUp;
+
+ float cos10 = 0.9848f;
+ //if(worldUp.DotProduct(vehicle->mVehicleUp) < cos10)
+ {
+ // will this work??
+ inCollision.mIndexB = 0;
+ return Solving_Continue;
+
+ }
+
+ int stophere = 1;
+ }
+
+
+ if(simStateA->mAIRefIndex != PhysicsAIRef::redBrickPhizStatic)
+ {
+ if(vehicle->mUsingInCarPhysics)
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ return Solving_Continue;
+ }
+ }
+ }
+
+
+ /*
+ // if this is a wheel, but the thing we hit is a vehicle ground plane, then we want to ignore it.
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == vehicle->GetGroundPlaneAIRef() ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable) // new - do we want to keep this?
+ {
+ return Solving_Aborted;
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == vehicle->GetGroundPlaneAIRef() ||
+ simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable) // new - do we want to keep this?
+ {
+ return Solving_Aborted;
+ }
+
+ }
+ */
+
+ // recall:
+ // mPositionA = mPositionB + mNormal * mDistance
+
+ // SG::phizSim.mCollisionDistanceCGS;
+
+ // amount we want to keep in collision
+ // TODO - even want to do this?
+ //float slightlyColliding = (SG::phizSim.mCollisionDistanceCGS * 0.1f) * 0.01f; // last factor to make sure we're in meters...
+
+ //float slightlyColliding = (SG::phizSim.mCollisionDistanceCGS * 1.0f) * 0.01f; // last factor to make sure we're in meters...
+
+ // TODO
+ // hack to fix for no phizsim
+ float collisionDistanceCGS = 2.0f;
+
+ //float slightlyColliding = (SG::phizSim.mCollisionDistanceCGS * 1.0f) * 0.01f; // last factor to make sure we're in meters...
+ float slightlyColliding = (collisionDistanceCGS * 1.0f) * 0.01f; // last factor to make sure we're in meters...
+
+ // TODO - choose right value here based on coefficient of restituion blah blah
+ // initial random guessof 0.1 was so low it was never used since the impulses
+ // kept shit apart.
+ //
+ // hmmmm... maybe want to use that? but then how does suspension compress?
+ //
+
+
+ // if we hit something sideways with the wheel, just apply impulse
+ float cos80 = 0.1736f;
+ float cos70 = 0.342f;
+
+ // TODO - this isn't nearly adequate!
+
+ // TODO
+ // temp fix
+
+ rmt::Vector worldUp = GetWorldPhysicsManager()->mWorldUp;
+
+ // new test
+ //if(vehicle->mPercentOfTopSpeed > 0.3f)
+ if(vehicle->mPercentOfTopSpeed > 0.1f)
+ {
+ int wheelIndex = vehicle->mJointIndexToWheelMapping[jointIndex];
+ rAssert(wheelIndex >= 0);
+ rAssert(wheelIndex < 5);
+
+ rmt::Vector normVel = vehicle->mSuspensionPointVelocities[wheelIndex];
+
+ normVel.NormalizeSafe();
+ float cos45 = 0.7071f;
+ if(normalPointingAtCar.DotProduct(normVel) > cos45)
+ //if(normalPointingAtCar.DotProduct(normVel) > cos70)
+ {
+ // the collision is pointing the way we're going so fuck it
+ //char buffy[128];
+ //sprintf(buffy, "aborting wheel-static collision fix - pointing too much along velocity\n");
+ //rDebugPrintf(buffy);
+
+
+ vehicle->SetNoDamperDownFlagOnWheel(wheelIndex);
+
+ return Solving_Aborted;
+ }
+ }
+
+
+
+ float cos55 = 0.5736f;
+
+ //if( rmt::Fabs((inCollision.mNormal).DotProduct(vehicle->mVehicleUp)) < cos70 || // recall - this is the new line
+ if( rmt::Fabs((inCollision.mNormal).DotProduct(vehicle->mVehicleUp)) < cos55 || // recall - this is the new line
+ worldUp.DotProduct(vehicle->mVehicleUp) < cos80) // leave this on at 80
+ {
+
+ //char buffy[128];
+ //sprintf(buffy, "aborting wheel-static collision fix - too horizontal\n");
+ //rDebugPrintf(buffy);
+
+ int wheelIndex = vehicle->mJointIndexToWheelMapping[jointIndex];
+ rAssert(wheelIndex >= 0);
+ rAssert(wheelIndex < 5);
+
+ vehicle->SetNoDamperDownFlagOnWheel(wheelIndex);
+
+ // temp
+ // TODO - something?
+ return Solving_Aborted;
+
+ // let collision solver just apply some impulse
+ //
+ // TODO - scale down impulse??
+
+ mWheelSidewaysHit = true;
+
+ if(carisA)
+ {
+ inCollision.mIndexA = 0;
+ }
+ else
+ {
+ inCollision.mIndexB = 0;
+ }
+
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+
+ }
+
+
+
+
+ if(inCollision.mDistance < slightlyColliding)
+ {
+ // need to move along normal by:
+ float fixAlongCollisionNormal = slightlyColliding - inCollision.mDistance;
+
+ float fixAlongSuspensionAxis = fixAlongCollisionNormal / rmt::Fabs( (vehicle->mVehicleUp).DotProduct(inCollision.mNormal) );
+
+ // now fixAlongSuspensionAxis is the object space y offset that we should correct to
+
+ // this will return true for bottom out
+ if(vehicle->SetWheelCorrectionOffset(jointIndex, fixAlongSuspensionAxis, normalPointingAtCar, groundContactPoint))
+ //if(fixAlongSuspensionAxis > 2.0f * physicsVehicle->GetWheelByJoint(jointIndex)->GetSuspensionLimit() )
+ {
+ // we've bottomed out
+ //
+ // so also let solver apply impulse
+ mBottomedOut = true; // TODO - should also store the joint! - ...later...why?
+
+ // if we've bottomed out, we only want to transfer impulse to the chassis if
+ // it's more or less up
+
+ const float cos20 = 0.9397f;
+
+ //if( rmt::Fabs((inCollision.mNormal).DotProduct(vehicle->mVehicleUp)) > cos20 )
+ if(normalPointingAtCar.DotProduct(vehicle->mVehicleUp) > cos20)
+ {
+ /*
+ if(carisA)
+ {
+ inCollision.mIndexA = 0;
+ }
+ else
+ {
+ inCollision.mIndexB = 0;
+ }
+
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+
+
+ april 11, 2003
+ this makes an absolute world of difference in jumps!!!!
+
+
+ */
+
+ return Solving_Aborted;
+ }
+ else
+ {
+ return Solving_Aborted;
+ }
+
+ }
+
+ return Solving_Aborted;
+
+ }
+
+
+
+ } // end of the IsJointAWheel block...
+
+
+ // if we got here we are the chassis hitting a static or our own groudn plane
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane)
+ {
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = vehicle->mVehicleUp.DotProduct(up);
+
+ //float cos10 = 0.9848f;
+ float cos16 = 0.9613f;
+
+ if(vehicle->mAirBorn && tip > cos16)
+ {
+ //int stophere = 1;
+ return Solving_Aborted;
+ }
+
+
+ //vehicle->mChassisHitGroundPlaneThisFrame = true;
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane)
+ {
+
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = vehicle->mVehicleUp.DotProduct(up);
+
+ //float cos10 = 0.9848f;
+ float cos16 = 0.9613f;
+
+ if(vehicle->mAirBorn && tip > cos16)
+ {
+ //int stophere = 1;
+ return Solving_Aborted;
+ }
+
+
+
+ //vehicle->mChassisHitGroundPlaneThisFrame = true;
+ }
+
+ }
+
+
+
+
+ if(inCollision.mDistance < -0.5f)
+ //if(inCollision.mDistance < -0.25f)
+ //if(inCollision.mDistance < -0.1f)
+ {
+ //if(vehicle->mVehicleID == VehicleEnum::FAMIL_V)
+ //{
+ // int stophere = 1; // motherfucking goddamn data conditions on breakpoints not working!!
+ //}
+
+ // greg
+ // jan 15, 2003
+
+ // did you know, that to penetrate something 0.5m in 32ms, you only have to be going 56 kmh ?
+
+ // live and learn
+
+ //return Solving_Aborted;
+ //inCollision.mDistance = -0.1f; // fuck
+ }
+
+ // this is just chassis hitting something here...
+
+ // test - if all wheels are out of collision, transffer impulse to root...?
+
+
+ }
+ return Solving_Continue;
+}
+
+
+
+Solving_Answer RedBrickCollisionSolverAgent::EndObjectCollision(SimState* inSimState, int inIndex)
+{
+
+ if(inSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ Vehicle* vehicle = (Vehicle*)(inSimState->mAIRefPointer);
+
+ if(vehicle->IsAFlappingJoint(inIndex))
+ {
+ //vehicle->mSimStateArticulated->GetSimulatedObject()->ResetCache();
+ }
+ }
+
+ return CollisionSolverAgent::EndObjectCollision(inSimState, inIndex);
+}
+
+
+
+Solving_Answer RedBrickCollisionSolverAgent::TestCache(SimState* inSimState, int inIndex)
+{
+ // this is called everytime an impulse is added on an object. Getting the cache give information
+
+/*
+ if(inSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ // car on car
+
+ //static float magic = 1.0f;
+
+ // fun test
+ //impulse.Scale(magic);
+
+ //int stophere = 1;
+
+ if( ((Vehicle*)(inSimState->mAIRefPointer))->mVehicleType == VT_TRAFFIC )
+ {
+ static float magic = 5.0f;
+
+ // fun test
+ impulse.Scale(magic);
+
+ SimulatedObject* simobj = inSimState->GetSimulatedObject(-1);
+
+ rmt::Vector cachev, cachew;
+ simobj->GetCollisionCache(cachev, cachew, -1); //retrieve the collision cache of the physical object.
+
+
+ if (cachev.MagnitudeSqr() > maxDv2)
+ {
+ bool fuck = true;
+ }
+
+
+ }
+ }
+
+*/
+
+
+#if 0
+ SimulatedObject* simobj = inSimState->GetSimulatedObject(-1);
+
+
+ if ( simobj && (simobj->Type() == RigidObjectType || simobj->Type() == ArticulatedObjectType) )
+ {
+ /*
+ static float thresholdfactor1 = 100.0f;
+ Vector currentv, vcmv, cachev, cachew;
+ currentv = inSimState->GetLinearVelocity(); //current speed of the object
+ if (inSimState->GetVirtualCM()==NULL)
+ vcmv.Clear();
+ else
+ vcmv = inSimState->GetVirtualCM()->GetVelocity(); //The current speed of the vcm of the object
+ simobj->GetCollisionCache(cachev, cachew, -1); //retrieve the collision cache of the physical object.
+ cachev.Add(vcmv); //The combined vcm's speed and the cached speed.
+ float testSpeed = Max(currentv.DotProduct(currentv),Sqr(simobj->GetMinimumLinSpeed()));
+ if ( cachev.DotProduct(cachev) > thresholdfactor1*testSpeed )
+ {
+ //inSimState->SetControl(simSimulationCtrl);
+ return Solving_Aborted;
+ }
+ */
+ Vector cachev, cachew;
+ simobj->GetCollisionCache(cachev, cachew, -1); //retrieve the collision cache of the physical object.
+
+ static float maxDv2 = 27.0f*27.0f;
+ if (cachev.MagnitudeSqr() > maxDv2)
+ {
+ bool fuck = true;
+ }
+ }
+#endif
+
+ /*
+ if (simobj && simobj->Type() == ArticulatedObjectType)
+ {
+ //Looks if there is a vcm installed on this joint:
+ SimStateArticulated *simStateArt = (SimStateArticulated*)inSimState;
+ JointVCMpArray *vcma = &simStateArt->mVirtualCMList;
+ for (int i=0 ; i<vcma->GetSize() ; i++)
+ {
+ JointVirtualCM *vcm = vcma->GetAt(i);
+ if (vcm->GetIndex() == inIndex)
+ {
+ static float thresholdfactor2=100.0f;
+ Vector currentv, vcmv, cachev, cachew;
+ simStateArt->GetVelocity(vcm->GetPosition(), currentv, inIndex);
+ vcmv = vcm->GetVelocity();
+ simobj->GetCollisionCache(cachev, cachew, inIndex);
+ if ( cachev.DotProduct(cachev) > thresholdfactor2*currentv.DotProduct(currentv) )
+ {
+ simStateArt->SetControl(simSimulationCtrl);
+ return Solving_Aborted;
+ }
+ }
+ }
+ }
+ */
+ return Solving_Continue;
+}
+
diff --git a/game/code/worldsim/redbrick/redbrickcollisionsolveragent.h b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.h
new file mode 100644
index 0000000..ed88531
--- /dev/null
+++ b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.h
@@ -0,0 +1,57 @@
+/*===========================================================================
+ redbrickcollisionsolveragent.h
+
+ created Jan 28, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+===========================================================================*/
+
+#ifndef _REDBRICKCOLLISIONSOLVERAGENT_H
+#define _REDBRICKCOLLISIONSOLVERAGENT_H
+
+#include <worldsim/worldcollisionsolveragent.h>
+
+#include <simcommon/tlist.hpp>
+#include <simcollision/collisionanalyser.hpp>
+#include <simcollision/collisionanalyserdata.hpp>
+
+using namespace sim;
+
+// TODO - looking for the best place to put these
+// a necessary part of the RedBrick interface
+
+// TBJ moved 'em to <worldsim/physicsairef.h>
+//
+//enum RedBrickPhizAITypes { redBrickPhizDefault = 0, redBrickVehicle, redBrickPhizVehicleGroundPlane, redBrickPhizStatic, redBrickPhizMoveable, redBrickPhizMoveableAnim, redBrickPhizLast }; // phizGround, phizStatic, phizMoveableAnim, phizMoveable, phizCamera, phizLast };
+
+class RedBrickCollisionSolverAgent : public WorldCollisionSolverAgent
+{
+public:
+
+ RedBrickCollisionSolverAgent();
+ ~RedBrickCollisionSolverAgent();
+
+ // the key method to override
+ Solving_Answer PreCollisionEvent(Collision& inCollision, int inPass);
+ Solving_Answer TestImpulse(rmt::Vector& mImpulse, Collision& inCollision);
+ Solving_Answer TestCache(SimState* inSimState, int inIndex);
+
+ Solving_Answer EndObjectCollision(SimState* inSimState, int inIndex);
+
+ Solving_Answer CarOnCarPreTest(Collision& inCollision, int inPass);
+
+ virtual void ResetCollisionFlags();
+
+ bool mBottomedOut;
+ float mBottomedOutScale;
+
+ bool mWheelSidewaysHit;
+ float mWheelSidewaysHitScale;
+
+
+};
+
+#endif // _REDBRICKCOLLISIONSOLVERAGENT_H
diff --git a/game/code/worldsim/redbrick/rootmatrixdriver.cpp b/game/code/worldsim/redbrick/rootmatrixdriver.cpp
new file mode 100644
index 0000000..ccaf57e
--- /dev/null
+++ b/game/code/worldsim/redbrick/rootmatrixdriver.cpp
@@ -0,0 +1,29 @@
+/*===========================================================================
+ rootmatrixdriver.cpp
+
+ created Jan 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+#include <poser/joint.hpp>
+
+#include <worldsim/redbrick/rootmatrixdriver.h>
+
+//------------------------------------------------------------------------
+void RootMatrixDriver::Update(poser::Pose* pose)
+{
+ poser::Joint* j = pose->GetJoint(0);
+ //rmt::Matrix m = j->GetObjectMatrix();
+ //m.Mult(*mRootMatrix);
+ j->SetWorldMatrix(*mRootMatrix); // recall, mRootMatrix is a pointer to mTransform of the physics vehicle
+}
+
diff --git a/game/code/worldsim/redbrick/rootmatrixdriver.h b/game/code/worldsim/redbrick/rootmatrixdriver.h
new file mode 100644
index 0000000..bbebab8
--- /dev/null
+++ b/game/code/worldsim/redbrick/rootmatrixdriver.h
@@ -0,0 +1,77 @@
+/*===========================================================================
+ rootmatrixdriver.h
+
+ created Jan 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _ROOTMATRIXDRIVER_H
+#define _ROOTMATRIXDRIVER_H
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+/*
+class PoseDriver: public tEntity
+{
+public:
+
+ PoseDriver();
+
+ virtual int GetMinimumJointIndex() const
+ { return 0; }
+ virtual int GetPriority() const
+ { return PRIORITY_DEFAULT; }
+
+ bool IsEnabled() const
+ { return m_IsEnabled; }
+ void SetIsEnabled(bool isEnabled)
+ { m_IsEnabled = isEnabled; }
+
+ virtual void Advance(float dt) = 0;
+ virtual void Update(Pose* pose) = 0;
+
+protected:
+
+ virtual ~PoseDriver();
+
+private:
+
+ bool m_IsEnabled;
+};
+*/
+
+
+class RootMatrixDriver : public poser::PoseDriver
+{
+public:
+ RootMatrixDriver(rmt::Matrix* inRootMatrix) : mRootMatrix(inRootMatrix) {}
+ virtual int GetMinimumJointIndex() const { return 0; }
+ virtual int GetPriority() const { return 0; }
+ virtual void Advance(float dt) {}
+ virtual void Update(poser::Pose* pose);
+
+ // move definition to cpp file
+ /*
+ {
+ poser::Joint* j = pose->GetJoint(0);
+ rmt::Matrix m = j->GetObjectMatrix();
+ m.Mult(*mRootMatrix);
+ j->SetWorldMatrix(m);
+ }
+ */
+
+private:
+ rmt::Matrix* mRootMatrix;
+};
+
+#endif // #ifndef _ROOTMATRIXDRIVER_H
+
+
diff --git a/game/code/worldsim/redbrick/suspensionjointdriver.cpp b/game/code/worldsim/redbrick/suspensionjointdriver.cpp
new file mode 100644
index 0000000..d9bca77
--- /dev/null
+++ b/game/code/worldsim/redbrick/suspensionjointdriver.cpp
@@ -0,0 +1,130 @@
+/*===========================================================================
+ suspensionjointdriver.cpp
+
+ created Feb 1, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+#include <poser/joint.hpp>
+
+#include <worldsim/redbrick/suspensionjointdriver.h>
+#include <worldsim/redbrick/wheel.h>
+
+
+
+//------------------------------------------------------------------------
+SuspensionJointDriver::SuspensionJointDriver(Wheel* wheel, int jointIndex)
+{
+ mWheel = wheel;
+ mJointIndex = jointIndex;
+
+}
+
+
+//------------------------------------------------------------------------
+SuspensionJointDriver::~SuspensionJointDriver()
+{
+ //
+}
+
+
+
+//------------------------------------------------------------------------
+void SuspensionJointDriver::Update(poser::Pose* pose)
+{
+ // process results of last time's collision detection and solving...
+
+
+ //mWheel->ResolveOffset(); // I think this call is redundant - bug
+
+ // now just use mYOffset
+ //float yOffset = mWheel->mYOffset; // need to reapply this because we called PoseEngine::Begin and overwrote the suspension results...
+
+
+ //!
+ // BUT
+ //
+ // this mYOffset is from the bottomed out location set in precollisionprep, not the neutral point
+ //
+ // try not calling PoseEngine::Begin in PostUpdate
+
+
+
+
+
+
+
+ poser::Joint* joint = pose->GetJoint(mJointIndex);
+
+ // TODO - is this call inefficient?
+ // I'm gonna have to make a copy of the matrix at some point so I can change some values....
+ rmt::Matrix matrix = joint->GetObjectMatrix();
+
+
+
+
+//Matrix& FillRotateXYZ(float anglex, float angley, float anglez);
+
+ /*
+
+
+ rmt::Matrix rot;
+ rot.Identity();
+ rot.FillRotateX(mWheel->mRotAngle * 100.0f);
+
+ matrix.Mult(rot);
+
+
+ */
+
+
+ //matrix.m[3][1] += yOffset;
+
+ // !! TODO - safe to just apply this?
+ //matrix.m[3][1] += mWheel->mPhysicsVehicleOwner->mGravitySettleYCorrection; // this is insignificant
+
+ if(mWheel->mSteerWheel)
+ {
+ // this is one of the two front wheels, so turn it
+ float angle = mWheel->mWheelTurnAngle;
+
+ // should this be done before the addition of the yOffset?
+ // shouldn't matter too much....
+ matrix.FillRotateXYZ(mWheel->mCumulativeRot, mWheel->mWheelTurnAngle, 0.0f);
+
+ }
+ else
+ {
+ matrix.FillRotateX(mWheel->mCumulativeRot);
+ }
+
+ joint->SetObjectMatrix(matrix);
+
+ /*
+
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ trans.y += yOffset;
+
+ joint->SetObjectTranslation(trans);
+
+ if(mWheel->GetNum() < 1)
+ {
+ // this is one of the two front wheels, so rotate it
+
+ const rmt::Matrix& GetObjectMatrix() const
+ { return GetObjectTransform().GetMatrix(); }
+ void SetObjectMatrix(const rmt::Matrix& matrix);
+
+
+ }
+ */
+}
diff --git a/game/code/worldsim/redbrick/suspensionjointdriver.h b/game/code/worldsim/redbrick/suspensionjointdriver.h
new file mode 100644
index 0000000..43d0902
--- /dev/null
+++ b/game/code/worldsim/redbrick/suspensionjointdriver.h
@@ -0,0 +1,71 @@
+/*===========================================================================
+ suspensionjointdriver.h
+
+ created Feb 1, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _SUSPENSIONJOINTDRIVER_H
+#define _SUSPENSIONJOINTDRIVER_H
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+class Wheel;
+
+/*
+class PoseDriver: public tEntity
+{
+public:
+
+ PoseDriver();
+
+ virtual int GetMinimumJointIndex() const
+ { return 0; }
+ virtual int GetPriority() const
+ { return PRIORITY_DEFAULT; }
+
+ bool IsEnabled() const
+ { return m_IsEnabled; }
+ void SetIsEnabled(bool isEnabled)
+ { m_IsEnabled = isEnabled; }
+
+ virtual void Advance(float dt) = 0;
+ virtual void Update(Pose* pose) = 0;
+
+protected:
+
+ virtual ~PoseDriver();
+
+private:
+
+ bool m_IsEnabled;
+};
+*/
+
+
+class SuspensionJointDriver : public poser::PoseDriver
+{
+public:
+ SuspensionJointDriver(Wheel* wheel, int jointIndex);
+ ~SuspensionJointDriver();
+
+ virtual void Advance(float dt) {}
+ virtual void Update(poser::Pose* pose);
+
+private:
+ Wheel* mWheel;
+ int mJointIndex;
+
+};
+
+#endif // #ifndef _SUSPENSIONJOINTDRIVER_H
+
+
diff --git a/game/code/worldsim/redbrick/trafficbodydrawable.cpp b/game/code/worldsim/redbrick/trafficbodydrawable.cpp
new file mode 100644
index 0000000..56725ef
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficbodydrawable.cpp
@@ -0,0 +1,82 @@
+#include <worldsim/redbrick/trafficbodydrawable.h>
+#include <p3d/shader.hpp>
+#include <debug/profiler.h>
+
+TrafficBodyDrawable::TrafficBodyDrawable()
+{
+ mBodyPropDrawable = NULL;
+ mBodyShader = NULL;
+ mDesiredColour.Set( 255, 255, 255, 255 );
+ mFading = false;
+}
+
+TrafficBodyDrawable::~TrafficBodyDrawable()
+{
+ if( mBodyPropDrawable != NULL )
+ {
+ mBodyPropDrawable->Release();//delete mBodyPropDrawable;
+ mBodyPropDrawable = NULL;
+ }
+ if( mBodyShader != NULL )
+ {
+ mBodyShader->Release();
+ mBodyShader = NULL;
+ }
+}
+void TrafficBodyDrawable::SetBodyPropDrawable( tDrawable* drawable )
+{
+ tRefCounted::Assign( mBodyPropDrawable, drawable );
+}
+void TrafficBodyDrawable::SetBodyShader( tShader* shader )
+{
+ tRefCounted::Assign( mBodyShader, shader );
+}
+
+///////////////////////////////////////////////////
+// Implementing tDrawable
+void TrafficBodyDrawable::Display()
+{
+ BEGIN_PROFILE("TrafficBodyDrawable::Display")
+ rAssert( mBodyPropDrawable != NULL );
+ if( mBodyPropDrawable != NULL )
+ {
+ if( mBodyShader != NULL )
+ {
+ // display with desired colour first, then we'll go over it with a gloss
+ // put the old settings back
+ if(!mFading)
+ {
+ mBodyShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
+ }
+ else
+ {
+ mBodyShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ }
+ mBodyShader->SetColour( PDDI_SP_DIFFUSE, mDesiredColour );
+ mBodyShader->SetInt( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ mBodyPropDrawable->Display();
+
+ pddiColour white( 255,255,255,255 );
+ mBodyShader->SetColour( PDDI_SP_DIFFUSE, white );
+ mBodyShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ mBodyShader->SetInt( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ mBodyShader->SetInt( PDDI_SP_ALPHATEST, 1 );
+ mBodyShader->SetFloat( PDDI_SP_ALPHACOMPARE_THRESHOLD, (250.0f * (float(mFadeAlpha) / 255.0f)) / 255.0f );
+ mBodyPropDrawable->Display();
+
+
+ mBodyShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ }
+ else
+ {
+ mBodyPropDrawable->Display();
+ }
+ }
+ END_PROFILE("TrafficBodyDrawable::Display")
+}
+
+void TrafficBodyDrawable::ProcessShaders(ShaderCallback& callback)
+{
+ rAssert( mBodyPropDrawable != NULL );
+ mBodyPropDrawable->ProcessShaders(callback);
+}
diff --git a/game/code/worldsim/redbrick/trafficbodydrawable.h b/game/code/worldsim/redbrick/trafficbodydrawable.h
new file mode 100644
index 0000000..4ee7594
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficbodydrawable.h
@@ -0,0 +1,39 @@
+#ifndef TRAFFICBODYDRAWABLE_H
+#define TRAFFICBODYDRAWABLE_H
+
+#include <p3d/drawable.hpp>
+
+class tShader;
+
+class TrafficBodyDrawable :
+ public tDrawable
+{
+public:
+ TrafficBodyDrawable();
+ ~TrafficBodyDrawable();
+
+ void SetBodyPropDrawable( tDrawable* drawable );
+ void SetBodyShader( tShader* shader );
+ void SetDesiredColour( pddiColour colour )
+ {
+ mDesiredColour = colour;
+ }
+ pddiColour GetDesiredColour() const { return mDesiredColour; }
+
+ int mFadeAlpha;
+ bool mFading;
+
+public:
+ ///////////////////////////////////////////////////
+ // Implementing tDrawable
+ virtual void Display();
+ virtual void ProcessShaders(ShaderCallback&);
+
+private:
+ tDrawable* mBodyPropDrawable;
+ tShader* mBodyShader;
+ pddiColour mDesiredColour;
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/trafficlocomotion.cpp b/game/code/worldsim/redbrick/trafficlocomotion.cpp
new file mode 100644
index 0000000..20bd77c
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficlocomotion.cpp
@@ -0,0 +1,1421 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficlocomotion.cpp
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Locomote through intersections -- Dusit Eakkachaichanvet
+// 04/24/2002 + Created -- Greg Mayer
+//
+//=============================================================================
+#include <poser/poseengine.hpp>
+#include <raddebugwatch.hpp>
+#include <radtime.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <debug/profiler.h>
+#include <worldsim/worldphysicsmanager.h>
+
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/roadsegmentdata.h>
+#include <roads/lane.h>
+#include <roads/intersection.h>
+#include <roads/geometry.h>
+
+const float TrafficLocomotion::SECONDS_BETW_HISTORY_UPDATES = 0.005f;
+static const float SECONDS_BETW_CHECKS_FOR_FREE_LANE = 5.0f;
+static const float TRAFFIC_WHEEL_GAP_HACK = 0.1f;
+
+#define FACING_HISTORY
+// Need to use pos history to smooth out motion along curve
+// Fortunately, this also causes the actual (apparent) position
+// to be located further back from the calculated position, so
+// turning looks better.
+// Unfortunately, averaging means we can't stop on a dime,
+// like we want to ...
+#define POS_HISTORY
+
+//------------------------------------------------------------------------
+TrafficLocomotion::TrafficLocomotion(Vehicle* vehicle) :
+ VehicleLocomotion(vehicle),
+ mMyAI( NULL )
+{
+
+ mMyAI = new TrafficAI( vehicle );
+ mMyAI->AddRef();
+
+#ifdef DEBUGWATCH
+ if( vehicle && vehicle->mVehicleType == VT_TRAFFIC )
+ {
+ mMyAI->RegisterAI();
+ }
+#endif
+
+
+ mVehicle = vehicle;
+ mIsInIntersection = false;
+ mCurrWay = -1;
+ mCurrPathLength = 0.0f;
+ mCurrPathLocation = 0.0f;
+ mActualSpeed = 0.0f;
+ mWays = NULL;
+ mNumWays = 0;
+ mSecondsTillCheckForFreeLane = SECONDS_BETW_CHECKS_FOR_FREE_LANE;
+
+ ////////////////////////////////////////////////
+ // HISTORY stuff
+ ////////////////////////////////////////////////
+#ifdef FACING_HISTORY
+ rmt::Vector heading( 0.0f, 0.0f, 0.0f );
+ /*
+ if( mVehicle != NULL )
+ {
+ mVehicle->GetHeading( &heading );
+ }
+ */
+ mFacingHistory.Init( heading );
+#endif
+
+#ifdef POS_HISTORY
+ rmt::Vector pos( 0.0f, 0.0f, 0.0f );
+ mPosHistory.Init( pos );
+#endif
+ ////////////////////////////////////////////////
+
+ mLaneChangeProgress = 0.0f;
+ mLaneChangeDist = 0.0f;
+ mLaneChangingFrom = 0;
+ mOutLaneT = 0.0f;
+
+ mSecondsSinceLastAddToHistory = 0.0f;
+}
+
+
+//------------------------------------------------------------------------
+TrafficLocomotion::~TrafficLocomotion()
+{
+ mMyAI->ReleaseVerified();
+}
+
+void TrafficLocomotion::InitPos( const rmt::Vector& pos )
+{
+#ifdef POS_HISTORY
+ mPosHistory.Init( pos );
+#endif
+
+ mSecondsSinceLastAddToHistory = 0.0f;
+
+ /////////////////////////////////////////////
+ // Adjust ground height (unfortunately we need to do this
+ // because we use pos averaging (so our y value is off)
+
+ mPrevPos = pos;
+
+ rmt::Vector groundPosition, outnorm;
+ bool bFoundPlane = false;
+
+ groundPosition = pos;
+ outnorm.Set( 0.0f, 1.0f, 0.0f );
+
+ GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition // OUT
+ );
+
+ if( bFoundPlane )
+ {
+ mPrevPos.y = groundPosition.y;
+ }
+ ///////////////////////////////////////////////
+
+}
+
+
+void TrafficLocomotion::InitFacing( const rmt::Vector& facing )
+{
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+#ifdef FACING_HISTORY
+ mFacingHistory.Init( facing );
+#endif
+ mSecondsSinceLastAddToHistory = 0.0f;
+
+}
+//=============================================================================
+// TrafficLocomotion::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::Init()
+{
+ mMyAI->Init();
+}
+
+//=============================================================================
+// TrafficLocomotion::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TrafficAI::Behaviour behaviour,
+// unsigned int behaviourModifiers,
+// Vehicle* vehicle,
+// Lane* lane,
+// unsigned int laneIndex,
+// RoadSegment* segment,
+// unsigned int segmentIndex,
+// float t,
+// float kmh )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::Init( Vehicle* vehicle,
+ Lane* lane,
+ unsigned int laneIndex,
+ RoadSegment* segment,
+ unsigned int segmentIndex,
+ float t,
+ float mps )
+{
+ mMyAI->Init( mVehicle, lane, laneIndex, segment, segmentIndex, t, mps );
+}
+
+//=============================================================================
+// TrafficLocomotion::InitVehicleAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Behaviour behaviour, unsigned int behaviourModifiers, Vehicle* vehicle )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::InitVehicleAI( Vehicle* vehicle )
+{
+ mMyAI->SetVehicle( vehicle );
+}
+
+//=============================================================================
+// TrafficLocomotion::InitLane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Lane* lane, unsigned int laneIndex, float mps )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::InitLane( Lane* lane, unsigned int laneIndex, float mps )
+{
+ mMyAI->SetLane( lane );
+ mMyAI->SetLaneIndex( laneIndex );
+ mMyAI->SetAISpeed( mps );
+}
+
+//=============================================================================
+// TrafficLocomotion::InitSegment
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RoadSegment* segment, unsigned int segmentIndex, float t )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::InitSegment( RoadSegment* segment, unsigned int segmentIndex, float t )
+{
+ mMyAI->SetSegment( segment );
+ mMyAI->SetSegmentIndex( segmentIndex );
+ mMyAI->SetLanePosition( t );
+ mMyAI->SetIsInIntersection( false );
+ mIsInIntersection = false;
+}
+
+
+//=============================================================================
+// TrafficLocomotion::UpdateVehicleGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::UpdateVehicleGroundPlane()
+{
+ rmt::Vector p, n;
+ p.Set(0.0f, 0.0f, 0.0f);
+ n.Set(0.0f, 1.0f, 0.0f);
+
+ p = mVehicle->GetPosition();
+ p.y -= 5.0f; // just to be safe
+
+
+ mVehicle->mGroundPlaneWallVolume->mPosition = p;
+ mVehicle->mGroundPlaneWallVolume->mNormal = n;
+
+
+ sim::CollisionObject* co = mVehicle->mGroundPlaneSimState->GetCollisionObject();
+ co->PostManualUpdate();
+
+
+}
+
+
+
+//------------------------------------------------------------------------
+void TrafficLocomotion::PreCollisionPrep(bool firstSubstep)
+{
+ UpdateVehicleGroundPlane();
+}
+
+//=============================================================================
+// TrafficLocomotion::PreSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (mVehicle* mVehicle)
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::PreSubstepUpdate()
+{
+ if( !mMyAI->mIsActive )
+ {
+ return;
+ }
+
+ // perform whatever update here
+}
+
+
+//=============================================================================
+// TrafficLocomotion::PreUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::PreUpdate()
+{
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ mVehicle->mPoseEngine->Begin( false );
+ //mVehicle->mPoseEngine->Begin(true);
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ //trans.y -= mVehicle->mWheels[i]->mLimit;
+ // TODO - verify that the -= is the thing to do here
+ //trans.y -= wheel->mLimit;
+ trans.y += TRAFFIC_WHEEL_GAP_HACK;
+
+ joint->SetObjectTranslation(trans);
+
+ }
+
+
+}
+
+
+//=============================================================================
+// TrafficLocomotion::UpdateAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::UpdateAI(unsigned int ms)
+{
+ // TrafficAI::Update takes timestep in seconds
+ mMyAI->Update(float(ms)/1000.0f);
+}
+
+
+
+void TrafficLocomotion::StopSuddenly( rmt::Vector& pos, rmt::Vector& facing )
+{
+#ifdef POS_HISTORY
+ // re-init history with pos
+ mPosHistory.GetAverage( pos );
+ mPosHistory.Init( pos );
+#else
+ pos = mPrevPos;
+#endif
+
+#ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ mFacingHistory.Init( facing );
+#endif
+
+ mSecondsSinceLastAddToHistory = 0.0f;
+
+ // TODO:
+ // When we stop suddenly, our t value is slightly ahead of
+ // us (because we've clobbered our pos history with the
+ // vehicle's current position). When we accelerate again,
+ // we'll dump the t position into the history, causing
+ // the vehicle to lurch forward inertialessly, throwing off
+ // the average.
+ //
+ // So we reset our t value at this point to where we are.
+ // This is rather tricky to do: we could be anywhere...
+ // - If we're on a road segment, that's perfect.
+ // - If we're not on a road segment, but we're in
+ // an intersection, there's no way to guarantee that
+ // our t value is still on the spline (we could already
+ // be in the OUT lane). We need to create a new spline
+ // that still takes us from one road to another, but
+ // we didn't store all this info!! Arrgh...
+ //
+ // A BETTER design (from the ground up), would have
+ // just stored splines from the beginning (even if you're
+ // traversing roads), making sure that this spline is
+ // long enough to contain our vehicle's actual position.
+ // As our t value crawls along a roadsegment, we add it to
+ // our spline. This way when we need to recompute t, we
+ // can just search on our spline for the closest segment
+ // to our current position. The other plus side to this
+ // is that we don't traverse segments differently from
+ // intersections. Everything gets stored in our spline.
+ //
+
+}
+
+
+
+//=============================================================================
+// TrafficLocomotion::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float seconds)
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::Update(float seconds)
+{
+ if( !mMyAI->mIsActive )
+ {
+ return;
+ }
+
+ mSecondsSinceLastAddToHistory += seconds;
+
+BEGIN_PROFILE( "Traffic Locomotion" );
+
+ //mVehicle->mPoseEngine->Begin( false );
+
+ rmt::Vector pos, facing;
+
+ TrafficAI::State state = mMyAI->GetState();
+
+ switch ( state )
+ {
+ case TrafficAI::DEAD:
+ {
+ pos = mPrevPos;
+ #ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ #endif
+ }
+ break;
+ case TrafficAI::DRIVING: // fall through
+ case TrafficAI::WAITING_AT_INTERSECTION:
+ {
+ if( mMyAI->mNeedToSuddenlyStop )
+ {
+ StopSuddenly( pos, facing );
+ break;
+ }
+
+ bool stayDoggyStay = false;
+
+ //================================================
+ // DETERMINE t AND CURRENT WAYPOINT OR ROAD SEGMENT
+ // (whichever's applicable)
+ //================================================
+ float t = 0.0f;
+ float pathLength = 0.0f;
+
+ if( !mIsInIntersection )
+ {
+ pathLength = mMyAI->GetLaneLength();
+ t = mMyAI->GetLanePosition();
+ }
+ else
+ {
+ pathLength = mCurrPathLength;
+ t = mCurrPathLocation;
+ }
+
+ float adjustedDt = (GetAISpeed() * seconds) / pathLength;
+ t += adjustedDt;
+
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+
+ if( !mIsInIntersection ) // we're not inside an intersection
+ {
+ const Road* road = mMyAI->GetLane()->GetRoad();
+ unsigned int segmentIndex = mMyAI->GetSegmentIndex();
+
+ // If there are more segments on this road
+ if ( segmentIndex < (road->GetNumRoadSegments()-1) )
+ {
+ // we have to move ahead a segment
+ segmentIndex++;
+ mMyAI->SetSegmentIndex( segmentIndex );
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ mMyAI->SetLanePosition(t);
+ pathLength = newLength;
+
+ // ========================
+ // We just updated mMyAI:
+ // - segment & segment index updated to look at next segment
+ // - lane & lane index not updated
+ // - lane position updated with the new segment's t
+ // ========================
+ }
+ // Else we must be at an intersection
+ else
+ {
+ // set up cubic spline
+ bool succeeded = EnterIntersection();
+ // ========================
+ // We just updated mMyAI:
+ // - segment & segment index updated to look at the OUT segment
+ // - lane & lane index updated to look at the OUT lane
+ // - lane position not updated
+ // ========================
+
+ // Set info of first path on spline
+ if( succeeded )
+ {
+ rAssert( mWays != NULL );
+
+ mMyAI->SetIsInIntersection( true );
+ mIsInIntersection = true;
+
+ mCurrWay = 0;
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ mCurrPathLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ t *= pathLength / mCurrPathLength;
+ pathLength = mCurrPathLength;
+ }
+ else
+ {
+ stayDoggyStay = true;
+ break;
+ }
+
+ }
+ }
+ else // we ARE inside an intersection
+ {
+ // Since we never spawn in an intersection, we never
+ // get into this case unless we entered via EnterIntersection(),
+ // and t is > 1.0f...
+ // Meaning that at this point, mWays has been populated and
+ // mCurrWay, mCurrPath, and mCurrPathLength are set.
+
+ rAssert( mCurrWay < mNumWays );
+ rAssert( mCurrWay >= 0 );
+
+ // if we still got waypoints to traverse in intersection
+ if( mCurrWay < (mNumWays - 2) )
+ {
+ mCurrWay++;
+
+ rAssert( mWays != NULL );
+
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ float newLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ mCurrPathLength = newLength;
+ }
+ else // Else going out of the intersection now
+ {
+ mIsInIntersection = false;
+ mMyAI->SetIsInIntersection( false );
+
+ // mMyAI information has already been set to look
+ // at the out lane/segment/etc.
+ // Must move within the lane...
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ }
+ }
+ }
+
+ // if t is not a valid number, it means that we encountered
+ // an error inside EnterIntersection()
+ if( stayDoggyStay )
+ {
+ pos = mPrevPos;
+ #ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ #endif
+ break; // exit this case
+ }
+
+
+ //================================================
+ // KNOWING t, DETERMINE pos AND facing
+ //================================================
+ rAssert( 0 <= t && t <= 1.0f );
+
+ // Depending on whether we're inside an intersection,
+ // we have different methods for determining pos & facing
+ if( !mIsInIntersection )
+ {
+ mMyAI->SetLanePosition( t );
+ RoadSegment* segment = mMyAI->GetSegment();
+ rAssert( segment );
+
+ segment->GetLaneLocation( mMyAI->GetLanePosition(), mMyAI->GetLaneIndex(), pos, facing );
+ if( !rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) )
+ {
+ facing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ }
+ else
+ {
+ mCurrPathLocation = t;
+ rmt::Vector temp = facing = mCurrPath;
+ facing.Scale( 1.0f / mCurrPathLength );
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ temp.Scale(t);
+
+ rAssert( mWays != NULL );
+ pos.Add( mWays[mCurrWay], temp );
+ }
+ }
+ break;
+
+ case TrafficAI::WAITING_FOR_FREE_LANE:
+ {
+ mSecondsTillCheckForFreeLane -= seconds;
+ if( mSecondsTillCheckForFreeLane < seconds )
+ {
+ mSecondsTillCheckForFreeLane = SECONDS_BETW_CHECKS_FOR_FREE_LANE;
+
+ // Transit back to driving next frame, so we check again for
+ // lane availability
+ mMyAI->SetState( TrafficAI::DRIVING );
+ }
+
+ // keep old heading & facing for now (complete and instantaneous stop)
+ pos = mPrevPos;
+ #ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ #endif
+ }
+ break;
+
+ case TrafficAI::SPLINING: // fall thru
+ case TrafficAI::LANE_CHANGING:
+ {
+ rAssert( !mIsInIntersection );
+
+ // In TrafficAI, if we're SPLINING, we can be told to stop for something.
+ // (when LANE_CHANGING we don't stop for anything... why? Not sure...)
+ // What happens if we need to suddenly stop here?
+ if( mMyAI->mNeedToSuddenlyStop )
+ {
+ StopSuddenly( pos, facing );
+ break;
+ }
+
+
+ //================================================
+ // DETERMINE t AND CURRENT WAYPOINT OR ROAD SEGMENT
+ // (whichever's applicable)
+ //================================================
+ float pathLength = mCurrPathLength;
+ float t = mCurrPathLocation;
+
+ float adjustedDt = (GetAISpeed() * seconds) / pathLength;
+ t += adjustedDt;
+
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+
+ // In case we got fed a long "seconds" value and go WAY over
+ // the lane change spline. Then we're not lane changing
+ // anymore (we're on normal road now)
+ if( mMyAI->GetState() != state )
+ {
+ const Road* road = mMyAI->GetLane()->GetRoad();
+ unsigned int segmentIndex = mMyAI->GetSegmentIndex();
+
+ if( segmentIndex < (road->GetNumRoadSegments()-1) )
+ {
+ // we have to move ahead a segment
+ segmentIndex++;
+ mMyAI->SetSegmentIndex( segmentIndex );
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ mMyAI->SetLanePosition(t);
+ pathLength = newLength;
+
+ // ========================
+ // We just updated mMyAI:
+ // - segment & segment index updated to look at next segment
+ // - lane & lane index not updated
+ // - lane position updated with the new segment's t
+ // ========================
+ }
+ else // if we're out of segments.. we are at an intersection...
+ {
+ // TODO:
+ // we should include intersection code here and deal with
+ // the t-overflow properly... but I'm thinking that's a lot
+ // of code repeat... so I'll get around to it later...
+ t = 0.999f;
+ mMyAI->SetLanePosition(t);
+ }
+ }
+ else // else we are in middle of lane changing...
+ {
+ // At this point, mWays has been populated and
+ // mCurrWay, mCurrPath, and mCurrPathLength are set.
+
+ rAssert( mCurrWay < mNumWays );
+ rAssert( mCurrWay >= 0 );
+
+ // if we still got waypoints to traverse...
+ if( mCurrWay < (mNumWays - 2) )
+ {
+ mCurrWay++;
+
+ rAssert( mWays != NULL );
+
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ float newLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ mCurrPathLength = newLength;
+ }
+ else // Else done lane changing now...
+ {
+ mMyAI->SetState( TrafficAI::DRIVING );
+
+ // mMyAI information has already been set to look
+ // at the target lane/segment/etc.
+ // Must move within the lane...
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ t += mOutLaneT;
+ pathLength = newLength;
+ }
+ }
+ }
+
+ //================================================
+ // KNOWING t, DETERMINE pos AND facing
+ //================================================
+ rAssert( 0 <= t && t <= 1.0f );
+
+ // Depending on whether we're still lane changing...
+ // we have different methods for determining pos & facing
+ if( mMyAI->GetState() != state )
+ {
+ mMyAI->SetLanePosition( t );
+ RoadSegment* segment = mMyAI->GetSegment();
+ rAssert( segment );
+
+ segment->GetLaneLocation( mMyAI->GetLanePosition(), mMyAI->GetLaneIndex(), pos, facing );
+ if( !rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) )
+ {
+ facing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ }
+ else
+ {
+ mCurrPathLocation = t;
+ rmt::Vector temp = facing = mCurrPath;
+ facing.Scale( 1.0f / mCurrPathLength );
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ temp.Scale(t);
+
+ rAssert( mWays != NULL );
+ pos.Add( mWays[mCurrWay], temp );
+ }
+ }
+ break;
+
+ case TrafficAI::SWERVING:
+ {
+ // we should be updating TrafficLocomotion if we're swerving because
+ // we should be in PhysicsLocomotion
+ rAssert( false );
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ break;
+ }
+
+
+
+ //================================================
+ // KNOWING pos AND facing, UPDATE VEHICLE
+ //================================================
+
+ // only need mPrevPos for the transition into Intersection
+ // since we can't rely on Vehicle->GetPosition to return us
+ // the RIGHT-ON-THE-GROUND values (Vehicle class does its
+ // own adjustments to bring the car up from ground level)
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+
+#if defined(FACING_HISTORY) || defined(POS_HISTORY)
+
+ // we need to add this many entries
+ int maxCount = (int)(mSecondsSinceLastAddToHistory / SECONDS_BETW_HISTORY_UPDATES);
+
+ if( maxCount > 0 )
+ {
+ float secondsConsumed = (float)(maxCount)*SECONDS_BETW_HISTORY_UPDATES;
+ float tmpT = (secondsConsumed/mSecondsSinceLastAddToHistory);
+
+ // figure out the starting and ending elements to interpolate between
+ #ifdef POS_HISTORY
+ rmt::Vector posStart = mPosHistory.GetLastEntry();
+ rmt::Vector posDir = pos - posStart;
+ rmt::Vector posEnd = posStart + posDir * tmpT;
+ posDir = posEnd - posStart;
+ #endif
+ #ifdef FACING_HISTORY
+ rmt::Vector faceStart = mFacingHistory.GetLastEntry();
+ rmt::Vector faceDir = facing - faceStart;
+ rmt::Vector faceEnd = faceStart + faceDir * tmpT;
+ faceDir = faceEnd - faceStart;
+ #endif
+
+
+ float tIncrement = 1.0f / (float)(maxCount);
+ tmpT = 0.0f;
+ for( int count = 1; count <= maxCount; count++ )
+ {
+ tmpT += tIncrement;
+ #ifdef POS_HISTORY
+ mPosHistory.UpdateHistory( posStart + posDir * tmpT );
+ #endif
+ #ifdef FACING_HISTORY
+ mFacingHistory.UpdateHistory( faceStart + faceDir * tmpT );
+ #endif
+
+ }
+ mSecondsSinceLastAddToHistory -= secondsConsumed;
+
+ }
+
+#endif
+
+ // Histories are updated, now we get the average out of them.
+
+#ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+#endif
+#ifdef POS_HISTORY
+ mPosHistory.GetAverage( pos );
+#endif
+
+ /////////////////////////////////////////////
+ // Adjust ground height (unfortunately we need to do this
+ // because we use pos averaging (so our y value is off)
+ rmt::Vector groundPosition, outnorm;
+ bool bFoundPlane = false;
+
+ groundPosition = pos;
+ outnorm.Set( 0.0f, 1.0f, 0.0f );
+
+ GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition // OUT
+ );
+
+ if( bFoundPlane )
+ {
+ pos.y = groundPosition.y;
+ }
+ ///////////////////////////////////////////////
+
+ // compute actual speed
+ rmt::Vector oldPos;
+ oldPos = mPrevPos;
+ mActualSpeed = (pos - oldPos).Magnitude() / seconds;
+
+ // Update vehicle transform...
+ rmt::Matrix newTransform;
+ newTransform.Identity();
+
+ rmt::Vector target;
+ target = pos;
+ target.Add( facing );
+ rmt::Vector up = UpdateVUP( pos, target );
+ newTransform.FillTranslate(pos);
+ newTransform.FillHeading(facing, up);
+ mVehicle->TrafficSetTransform(newTransform);
+
+ // Pivot the front wheels...
+ PivotFrontWheels( facing );
+
+ mPrevPos = pos;
+
+END_PROFILE( "Traffic Locomotion" );
+}
+
+//=============================================================================
+// TrafficLocomotion::PostUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::PostUpdate()
+{
+ if( !mMyAI->mIsActive )
+ {
+ return;
+ }
+ // make sure values are set for wheel rendering info...
+
+ // set 'artificial suspension point velocities
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ rmt::Vector velocity = mVehicle->mVehicleFacing;
+ velocity.Scale( mActualSpeed );
+ mVehicle->mSuspensionPointVelocities[i] = velocity;
+ }
+ mVehicle->mVelocityCM = mVehicle->mVehicleFacing;
+ mVehicle->mVelocityCM.Scale( mActualSpeed );
+
+ // update mSimStateArticulated speed here?
+ // hmmm...
+ rmt::Vector& linearVelocity = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ linearVelocity = mVehicle->mVelocityCM;
+
+ mVehicle->mSpeed = mActualSpeed;
+
+
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////// PRIVATES ////////////////////////////////
+
+void TrafficLocomotion::FindOutLane (
+ const Lane* inLane,
+ unsigned int inLaneIndex,
+ Lane*& outLane,
+ unsigned int& outLaneIndex )
+{
+ rAssert( inLane != NULL );
+
+ const Road* inRoad = inLane->GetRoad();
+ rAssert( inRoad != NULL );
+
+ Intersection* intersection =
+ (Intersection*) inRoad->GetDestinationIntersection();
+ rAssert( intersection );
+
+ Road* outRoad = NULL;
+ outLane = NULL;
+ outLaneIndex = 0;
+
+ switch( mMyAI->DecideTurn() )
+ {
+ case TrafficAI::LEFT:
+ {
+ intersection->GetLeftTurnForTraffic(
+ *inRoad, inLaneIndex, outRoad, outLane, outLaneIndex );
+ }
+ break;
+ case TrafficAI::RIGHT:
+ {
+ intersection->GetRightTurnForTraffic(
+ *inRoad, inLaneIndex, outRoad, outLane, outLaneIndex );
+ }
+ break;
+ case TrafficAI::STRAIGHT:
+ {
+ intersection->GetStraightForTraffic(
+ *inRoad, inLaneIndex, outRoad, outLane, outLaneIndex );
+ }
+ break;
+ default:
+ {
+ rAssert( false );
+ }
+ }
+}
+
+
+void TrafficLocomotion::BuildCurve (
+ RoadSegment* inSegment,
+ unsigned int inLaneIndex,
+ RoadSegment* outSegment,
+ unsigned int outLaneIndex )
+{
+ rAssert( inSegment != NULL );
+ rAssert( outSegment != NULL );
+
+ // get the start pos & dir
+ rmt::Vector startPos, startDir;
+ inSegment->GetLaneLocation( 1.0f, inLaneIndex, startPos, startDir );
+ //startDir.y = 0.0f;
+ if( !rmt::Epsilon( startDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ startDir.Normalize(); // *** SQUARE ROOT! ***
+ }
+
+ // get end pos & dir
+ rmt::Vector endPos, endDir;
+ outSegment->GetLaneLocation( 0.0f, outLaneIndex, endPos, endDir );
+ //endDir.y = 0.0f;
+ if( !rmt::Epsilon( endDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ endDir.Normalize(); // *** SQUARE ROOT! ***
+ }
+
+ // get intermediate points
+ rmt::Vector startEndTemp, p2, p4;
+ startEndTemp.Sub( endPos, startPos );
+ float distFromStartOrEnd = startEndTemp.Length() / 3.0f; // *** SQUARE ROOT! ***
+ p2 = startPos + startDir * distFromStartOrEnd;
+ p4 = endPos - endDir * distFromStartOrEnd;
+
+ // Now we are ready to gather together our points and send it
+ // to our "curve" finder.
+ rmt::Vector pts [4];
+ pts[0] = startPos;
+ pts[1] = p2;
+ pts[2] = p4;
+ pts[3] = endPos;
+
+ mSplineMaker.SetControlPoint( pts[0], 0 );
+ mSplineMaker.SetControlPoint( pts[1], 1 );
+ mSplineMaker.SetControlPoint( pts[2], 2 );
+ mSplineMaker.SetControlPoint( pts[3], 3 );
+
+/*
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+
+ const Road* inRoad = inSegment->GetRoad();
+ rAssert( inRoad );
+
+ Intersection* intersection =
+ (Intersection*) inRoad->GetDestinationIntersection();
+ rAssert( intersection );
+
+ // NOTE:
+ // If you hate this assert, you can comment it out locally... FOR NOW...
+ // Put it back in once all y-value mismatch errors are gone.
+ // In other words, once hell freezes over.
+ rmt::Vector intersectionLoc;
+ intersection->GetLocation( intersectionLoc );
+ char baseMsg[1000];
+ sprintf( baseMsg,
+ "\nMismatching y-values at intersection (%f,%f,%f).\n"
+ " Check if y values are same for all IN & OUT road segments attached\n"
+ " to this intersection. Check hypergraph to see if the roadnodes leading\n"
+ " IN and OUT of this intersection contain all the proper roadsegments.\n"
+ " If you skip this, Traffic cars will \"hop\" when they transit through\n"
+ " the intersection between the road segments with mismatching y values.\n"
+ " Better to report error to me (Dusit) or Sheik. Preferrably Sheik.\n\n",
+ intersectionLoc.x,
+ intersectionLoc.y,
+ -1*intersectionLoc.z );
+ rAssert( strlen(baseMsg) < 1000 );
+
+ float ep = 0.001f;
+ if( !( rmt::Epsilon( pts[0].y, pts[1].y, ep ) &&
+ rmt::Epsilon( pts[0].y, pts[2].y, ep ) &&
+ rmt::Epsilon( pts[0].y, pts[3].y, ep ) ) )
+ {
+ rTunePrintf( baseMsg );
+ }
+#endif
+*/
+
+ mSplineMaker.GetCubicBezierCurve( mWays, mNumWays );
+ rAssert( mWays != NULL );
+ rAssert( mNumWays == CubicBezier::MAX_CURVE_POINTS );
+}
+
+
+
+bool TrafficLocomotion::EnterIntersection()
+{
+ const Lane* inLane = mMyAI->GetLane();
+ rAssert( inLane != NULL );
+ RoadSegment* inSegment = mMyAI->GetSegment();
+ rAssert( inSegment != NULL );
+ const unsigned int inLaneIndex = mMyAI->GetLaneIndex();
+
+ // ==============================================
+ // Find OUT LANE and OUT LANE INDEX
+ // ==============================================
+
+ Lane* outLane = NULL;
+ unsigned int outLaneIndex = 0;
+
+ FindOutLane( inLane, inLaneIndex, outLane, outLaneIndex );
+
+ // if absolutely can't find a road, transit to waiting state
+ if( outLane == NULL )
+ {
+ // TODO: Should we slow down?
+ // Right now we have complete and instantaneous stop,
+ // which is needed because we are transiting to a diff state
+ // and must maintain old position & facing for when we transit
+ // back...
+ mMyAI->SetState( TrafficAI::WAITING_FOR_FREE_LANE );
+
+ return false;
+ }
+
+ rAssert( outLane != NULL );
+
+
+ // ================================================
+ // Update IN & OUT lanes' list of traffic vehicles
+ // ================================================
+ UpdateLanes( mVehicle->mTrafficVehicle, (Lane*)inLane, outLane );
+
+
+ // ==============================================
+ // Build Cubic Spline to navigate intersection
+ // ==============================================
+ const Road* outRoad = outLane->GetRoad();
+ rAssert( outRoad != NULL );
+ unsigned int outSegmentIndex = 0;
+ RoadSegment* outSegment = outRoad->GetRoadSegment(outSegmentIndex);
+ rAssert( outSegment != NULL );
+
+ // create control points, then the spline... store all in mWays
+ BuildCurve( inSegment, inLaneIndex, outSegment, outLaneIndex );
+
+
+ // ==================================================
+ // Update mMyAI:
+ // - to look at the OUT segment & segment index
+ // - to look at the OUT lane & lane index
+ // - lane position not updated
+ // ===================================================
+ mMyAI->SetLane( outLane );
+ mMyAI->SetLaneIndex( outLaneIndex );
+ mMyAI->SetSegment( outSegment );
+ mMyAI->SetSegmentIndex( outSegmentIndex );
+
+ return true;
+}
+
+void TrafficLocomotion::PivotFrontWheels( rmt::Vector facing )
+{
+ float cosAlpha = 0.0f;
+ rmt::Vector outPos, outFacing;
+ if( mIsInIntersection )
+ {
+ // when we're in the intersection, the lane is the OUTLANE, so get the t=0.0f
+ mMyAI->GetSegment()->GetLaneLocation( 0.0f, (int)mMyAI->GetLaneIndex(), outPos, outFacing );
+ outFacing.y = 0.0f;
+ outFacing.Normalize(); // *** SQUARE ROOT! ***
+ facing.y = 0.0f;
+ facing.Normalize(); // *** SQUARE ROOT! ***
+ cosAlpha = facing.Dot( outFacing );
+ }
+ else
+ {
+ // fake wheel turning for lane change...
+ if( mMyAI->GetState() == TrafficAI::LANE_CHANGING )
+ {
+ float progress = mLaneChangeProgress / mLaneChangeDist;
+ if( progress < 0.65f )
+ {
+ cosAlpha = 0.9396926f; // cos20
+ }
+ else
+ {
+ cosAlpha = -0.9396926f; // cos20
+ }
+ }
+ else
+ {
+ // when we're not in the intersection, the lane is the
+ // current lane, so get t=1.0f
+ mMyAI->GetSegment()->GetLaneLocation( 1.0f,
+ (int)mMyAI->GetLaneIndex(), outPos, outFacing );
+ outFacing.y = 0.0f;
+ outFacing.Normalize(); // *** SQUARE ROOT! ***
+ facing.y = 0.0f;
+ facing.Normalize(); // *** SQUARE ROOT! ***
+ cosAlpha = facing.Dot( outFacing );
+
+ }
+ }
+
+ // ensure we are in bounds
+ if( cosAlpha < -1.0f )
+ {
+ cosAlpha = -1.0f;
+ }
+ else if( cosAlpha > 1.0f )
+ {
+ cosAlpha = 1.0f;
+ }
+ float alpha = rmt::ACos( cosAlpha ); // *** COSINE! ***
+ rAssert( !rmt::IsNan( alpha ) );
+
+ // pivot left or right
+ if( mMyAI->GetState() != TrafficAI::LANE_CHANGING )
+ {
+ rmt::Vector rightOfFacing = Get90DegreeRightTurn( facing );
+ if( rightOfFacing.Dot( outFacing ) < 0.0f )
+ {
+ alpha *= -1.0f;
+ }
+ }
+ // Who am I? I'm Spiderman! No, I'm Dusit.
+ mVehicle->SetWheelTurnAngleDirectlyInRadiansForDusitOnly( alpha );
+
+}
+
+
+
+void TrafficLocomotion::UpdateLanes( TrafficVehicle* tv, Lane* oldLane, Lane* newLane )
+{
+ rAssert( tv != NULL );
+ rAssert( tv->GetIsActive() );
+ rAssert( oldLane != NULL );
+ rAssert( newLane != NULL );
+
+ // Remove vehicle from IN lane
+ bool found = false;
+ for( int i=0; i<oldLane->mTrafficVehicles.mUseSize; i++ )
+ {
+ if( tv == oldLane->mTrafficVehicles[i] )
+ {
+ oldLane->mTrafficVehicles.Remove( i );
+ found = true;
+ break;
+ }
+ }
+ //rAssert( found );
+
+ // Add vehicle to OUT lane.
+ // What happens if AddLast returns -1 because you can't add
+ // another vehicle to that lane? It WON'T! Because in our
+ // Intersection::Get<blah>ForTraffic, we make sure we either
+ // return a lane that can take this car or NULL. If NULL, then
+ // we shouldn't be at this point in the code.
+ //
+ rAssert( newLane->mTrafficVehicles.mUseSize < newLane->mTrafficVehicles.mSize );
+ newLane->mTrafficVehicles.Add( tv );
+ tv->SetLane( newLane );
+}
+
+Lane* TrafficLocomotion::GetAIPrevLane()
+{
+ return mMyAI->mPrevLane;
+}
+
+// will return if there's not enough room to lanechange
+bool TrafficLocomotion::BuildLaneChangeCurve(
+ RoadSegment* oldSegment,
+ const float oldT,
+ unsigned int oldLaneIndex,
+ unsigned int newLaneIndex,
+ const float dist)
+{
+ rAssert( oldSegment != NULL );
+
+ // We have to create control points
+ rmt::Vector pts[4];
+
+ // First figure out entry point
+ rmt::Vector entryFacing;
+ oldSegment->GetLaneLocation( oldT, oldLaneIndex, pts[0], entryFacing );
+ if( !rmt::Epsilon( entryFacing.MagnitudeSqr(), 1.0f, 0.0001f ) )
+ {
+ entryFacing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( entryFacing.MagnitudeSqr(), 1.0f, 0.0001f ) );
+
+ // Second, figure out the next point in line with facing,
+ // some dist ahead (percentage of lanechange dist)
+ float entryOffset = 0.2f * dist;
+ pts[1] = pts[0] + entryFacing * entryOffset;
+
+ // Third, figure out the exit point.
+ // we'll have to follow the road for the given lanechange distance
+ RoadSegment* endSegment = oldSegment;
+
+ float oldLaneLength = oldSegment->GetLaneLength( oldLaneIndex );
+ float t = oldT;
+ float adjustedDt = dist / oldLaneLength;
+ t += adjustedDt;
+
+ unsigned int endSegIndex = endSegment->GetSegmentIndex();
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+ // move on to next segment (there must be one)
+ unsigned int numSegs = endSegment->GetRoad()->GetNumRoadSegments();
+ endSegIndex++;
+
+ // if we're out of bounds, it means that there weren't enough segments
+ // to complete lane-change. Abort!
+ if( endSegIndex < 0 || endSegIndex >= numSegs )
+ {
+ return false;
+ }
+
+ endSegment = endSegment->GetRoad()->GetRoadSegment( endSegIndex );
+ float nextSegLen = endSegment->GetLaneLength( oldLaneIndex );
+
+ t *= oldLaneLength / nextSegLen;
+
+ oldLaneLength = nextSegLen;
+ }
+
+ rAssert( t <= 1.0f );
+
+ // since the last point of spline won't be the START of the
+ // segment when we come out of LANE_CHANGING state, we need to
+ // store away the t so we add it later...
+ mOutLaneT = t;
+
+ rmt::Vector exitFacing;
+ endSegment->GetLaneLocation( t, newLaneIndex, pts[3], exitFacing );
+ if( !rmt::Epsilon( exitFacing.MagnitudeSqr(), 1.0f, 0.0001f ) )
+ {
+ exitFacing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( exitFacing.MagnitudeSqr(), 1.0f, 0.0001f ) );
+ exitFacing.Scale( -1.0f );
+
+ // Lastly, figure out the previous point, in line with facing,
+ // some dist ahead (percentage of lanechange dist)
+ float exitOffset = dist * 0.5f;//0.3f;
+ pts[2] = pts[3] + exitFacing * exitOffset;
+
+ //
+ // update AI's segment info: segment, segment index, lane length
+ //
+ mMyAI->SetSegmentIndex( endSegment->GetSegmentIndex() );
+
+ mSplineMaker.SetControlPoint( pts[0], 0 );
+ mSplineMaker.SetControlPoint( pts[1], 1 );
+ mSplineMaker.SetControlPoint( pts[2], 2 );
+ mSplineMaker.SetControlPoint( pts[3], 3 );
+
+ // use mWays to store the spline
+ // (since we're not in an intersection anyway)
+ mSplineMaker.GetCubicBezierCurve( mWays, mNumWays );
+ rAssert( mWays != NULL );
+ rAssert( mNumWays == CubicBezier::MAX_CURVE_POINTS );
+
+ // update the currway stuff needed to work with mWays
+ mCurrWay = 0;
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ mCurrPathLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ mCurrPathLocation = 0.0f;
+
+ return true;
+}
+
+bool TrafficLocomotion::BuildArbitraryCurve(
+ const rmt::Vector& startPos,
+ const rmt::Vector& startDir, // is normalized to 1
+ const rmt::Vector& endPos,
+ const rmt::Vector& endDir )// is normalized to 1
+{
+ rAssert( rmt::Epsilon( startDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+ rAssert( rmt::Epsilon( endDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ float distScale = (startPos - endPos).Length(); // *** SQUARE ROOT! ***
+ distScale *= 0.3f;
+
+ rmt::Vector pts[4];
+ pts[0] = startPos;
+ pts[1] = startPos + startDir * distScale;
+ pts[2] = endPos + endDir * -1 * distScale;
+ pts[3] = endPos;
+
+ mSplineMaker.SetControlPoint( pts[0], 0 );
+ mSplineMaker.SetControlPoint( pts[1], 1 );
+ mSplineMaker.SetControlPoint( pts[2], 2 );
+ mSplineMaker.SetControlPoint( pts[3], 3 );
+
+ // use mWays to store the spline
+ mSplineMaker.GetCubicBezierCurve( mWays, mNumWays );
+ rAssert( mWays != NULL );
+ rAssert( mNumWays == CubicBezier::MAX_CURVE_POINTS );
+
+ // update the currway stuff needed to work with mWays
+ mCurrWay = 0;
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ mCurrPathLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ mCurrPathLocation = 0.0f;
+
+ return true;
+}
+
+void TrafficLocomotion::GetSplineCurve( rmt::Vector*& ways, int& npts, int& currWay )
+{
+ if( mWays == NULL )
+ {
+ ways = NULL;
+ npts = 0;
+ currWay = -1;
+ }
+ else
+ {
+ ways = mWays;
+ npts = mNumWays;
+ currWay = mCurrWay;
+ }
+}
diff --git a/game/code/worldsim/redbrick/trafficlocomotion.h b/game/code/worldsim/redbrick/trafficlocomotion.h
new file mode 100644
index 0000000..350f5d0
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficlocomotion.h
@@ -0,0 +1,270 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficlocomotion.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Locomote through intersections -- Dusit Eakkachaichanvet
+// 07/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRAFFICLOCOMOTION_H
+#define TRAFFICLOCOMOTION_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <worldsim/redbrick/vehiclelocomotion.h>
+#include <radmath/radmath.hpp>
+#include <ai/vehicle/trafficai.h>
+#include <roads/geometry.h>
+
+//========================================
+// Forward References
+//========================================
+class Lane;
+class RoadSegment;
+
+//
+// A lightweight statically allocated, singly-linked list
+//
+
+const int MAX_ITEMS = 30;
+
+struct Item
+{
+ rmt::Vector p;
+ int next;
+};
+
+class StaticList
+{
+public:
+ Item mItems[MAX_ITEMS];
+
+ void Clear()
+ {
+ int i=0;
+ for(i; i<MAX_ITEMS; i++)
+ {
+ mItems[i].next = -1;
+ }
+ mFirstFree = 0;
+ }
+
+ StaticList()
+ {
+ Clear();
+ }
+
+ int GetNumItems()
+ {
+ return mFirstFree;
+ }
+
+ Item* InsertItem( rmt::Vector p, Item* prevItem )
+ {
+ if( mFirstFree >= MAX_ITEMS )
+ {
+ return NULL;
+ }
+ int freeIndex = mFirstFree;
+ mItems[freeIndex].p = p;
+ mItems[freeIndex].next = (prevItem != NULL)? prevItem->next : -1;
+
+ if( prevItem != NULL )
+ {
+ prevItem->next = freeIndex;
+ }
+ else if( freeIndex > 0 )
+ {
+ mItems[freeIndex-1].next = freeIndex;
+ }
+ mFirstFree++;
+ return &(mItems[freeIndex]);
+ }
+
+private:
+
+ int mFirstFree;
+};
+
+
+
+
+
+
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TrafficLocomotion : public VehicleLocomotion
+{
+public:
+
+ enum {
+ ON_ROAD_HISTORY_SIZE = 66
+ };
+
+ static const float SECONDS_BETW_HISTORY_UPDATES;
+
+ TrafficLocomotion(Vehicle* vehicle);
+ virtual ~TrafficLocomotion();
+
+ void Init();
+ void Init( Vehicle* vehicle,
+ Lane* lane,
+ unsigned int laneIndex, //0 is curbside
+ RoadSegment* segment,
+ unsigned int segmentIndex,
+ float t,
+ float kmh );
+
+ void InitPos( const rmt::Vector& pos );
+ void InitFacing( const rmt::Vector& facing );
+ void InitVehicleAI( Vehicle* vehicle );
+ void InitLane( Lane* lane, unsigned int laneIndex, float mps );
+ void InitSegment( RoadSegment* segment, unsigned int segmentIndex, float t );
+
+ //////////////////// VEHICLELOCOMOTION STUFF ////////////////////
+ virtual void PreSubstepUpdate();
+ virtual void PreCollisionPrep(bool firstSubstep);
+ virtual void UpdateVehicleGroundPlane();
+ virtual void PreUpdate();
+ virtual void Update(float seconds);
+ virtual void PostUpdate();
+ virtual void CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint) {};
+ ///////////////////////////////////////////////////////////////////
+
+ TrafficAI* GetAI();
+ Lane* GetAILane();
+ Lane* GetAIPrevLane();
+ void SetAISpeed( float mps );
+ float GetAISpeed();
+ bool IsInIntersection() const;
+ void UpdateAI(unsigned int ms);
+ void SetActive( bool isActive );
+ bool GetActive() const;
+ void SetAIState( enum TrafficAI::State state );
+
+ void UpdateLanes( TrafficVehicle* tv, Lane* oldLane, Lane* newLane );
+
+ bool BuildLaneChangeCurve(
+ RoadSegment* oldSegment,
+ const float oldT,
+ unsigned int oldLaneIndex,
+ unsigned int newLaneIndex,
+ const float dist);
+
+ bool BuildArbitraryCurve(
+ const rmt::Vector& startPos,
+ const rmt::Vector& startDir,
+ const rmt::Vector& endPos,
+ const rmt::Vector& endDir );
+
+ void GetSplineCurve( rmt::Vector*& ways, int& npts, int& currWay );
+
+public:
+ float mLaneChangeProgress; // progress in meters while in lane change state
+ float mLaneChangeDist; // total distance over which we want to lane change
+ unsigned int mLaneChangingFrom; // index of lane we are lane changing FROM
+ float mOutLaneT;
+
+ float mCurrPathLocation;
+
+ float mActualSpeed;
+
+private:
+ Vehicle* mVehicle;
+ TrafficAI* mMyAI;
+
+ ///// For building splines at intersections //////
+ CubicBezier mSplineMaker;
+ rmt::Vector* mWays;
+ int mNumWays;
+ //////////////////////////////////////////////////
+
+ rmt::Vector mCurrPath;
+ float mCurrPathLength;
+ int mCurrWay;
+
+ bool mIsInIntersection;
+
+ rmt::Vector mPrevPos;
+
+ float mSecondsTillCheckForFreeLane;
+
+ ////////////// History ///////////////
+ VectorHistory<ON_ROAD_HISTORY_SIZE> mFacingHistory;
+ VectorHistory<ON_ROAD_HISTORY_SIZE> mPosHistory;
+ /////////////////////////////////////////////
+
+ float mSecondsSinceLastAddToHistory;
+
+private:
+ // Helpers
+ void FindOutLane( const Lane* inLane,
+ unsigned int inLaneIndex,
+ Lane*& outLane,
+ unsigned int& outLaneIndex );
+
+ void BuildCurve ( RoadSegment* inSegment,
+ unsigned int inLaneIndex,
+ RoadSegment* outSegment,
+ unsigned int outLaneIndex );
+
+ bool EnterIntersection();
+
+ void PivotFrontWheels( rmt::Vector facing );
+
+ void StopSuddenly( rmt::Vector& pos, rmt::Vector& facing );
+
+ ////////////// WASTEFUL CONSTRUCTORS ////////////////////
+ TrafficLocomotion();
+ TrafficLocomotion( const TrafficLocomotion& trafficlocomotion );
+ TrafficLocomotion& operator=( const TrafficLocomotion& trafficlocomotion );
+ ////////////////////////////////////////////////////////////
+};
+
+inline bool TrafficLocomotion::IsInIntersection() const
+{
+ return mIsInIntersection;
+}
+inline bool TrafficLocomotion::GetActive() const
+{
+ return mMyAI->mIsActive;
+}
+inline void TrafficLocomotion::SetActive( bool isActive )
+{
+ mMyAI->mIsActive = isActive;
+}
+inline void TrafficLocomotion::SetAISpeed( float mps )
+{
+ mMyAI->SetAISpeed( mps );
+}
+inline float TrafficLocomotion::GetAISpeed()
+{
+ return mMyAI->GetAISpeed();
+ }
+inline Lane* TrafficLocomotion::GetAILane()
+{
+ return mMyAI->GetLane();
+}
+inline void TrafficLocomotion::SetAIState( enum TrafficAI::State state )
+{
+ mMyAI->SetState(state);
+}
+inline TrafficAI* TrafficLocomotion::GetAI()
+{
+ return mMyAI;
+}
+
+#endif //TRAFFICLOCOMOTION_H
+
+
diff --git a/game/code/worldsim/redbrick/vehicle.cpp b/game/code/worldsim/redbrick/vehicle.cpp
new file mode 100644
index 0000000..5abc941
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicle.cpp
@@ -0,0 +1,6483 @@
+//=============================================================================
+// 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 );
+} \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehicle.h b/game/code/worldsim/redbrick/vehicle.h
new file mode 100644
index 0000000..928e639
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicle.h
@@ -0,0 +1,989 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicle.h
+//
+// Description: the car
+//
+// History: Nov 16, 2001 + Created, gmayer
+//
+//=============================================================================
+
+
+#ifndef _VEHICLE_H
+#define _VEHICLE_H
+
+
+//========================================
+// Nested System Includes
+//========================================
+
+#include <camera/isupercamtarget.h>
+#include <constants/vehicleenum.h>
+#include <presentation/gui/utility/hudmap.h>
+#include <radmath/radmath.hpp>
+#include <render/DSG/InstDynaPhysDSG.h>
+#include <render/intersectmanager/intersectmanager.h> // For terrain type enumeration.
+
+//========================================
+// Forward References
+//========================================
+
+class Character;
+class EventLocator;
+class GeometryVehicle;
+class PhysicsLocomotion;
+class RectTriggerVolume;
+class RootMatrixDriver;
+class StatePropCollectible;
+class SuspensionJointDriver;
+class TrafficLocomotion;
+class TrafficVehicle;
+class VehicleEventListener;
+class VehicleLocomotion;
+class Wheel;
+
+namespace poser
+{
+ class PoseEngine;
+}
+
+namespace sim
+{
+ class PhysicsJointInertialEffector;
+ class PhysicsJointMatrixModifier;
+ class SimStateArticulated;
+ class ArticulatedPhysicsObject;
+ class PhysicsProperties;
+}
+
+enum VehicleLocomotionType {VL_PHYSICS, VL_TRAFFIC};
+enum VehicleState {VS_NORMAL, VS_SLIP, VS_EBRAKE_SLIP};
+
+enum VehicleType {VT_USER, VT_AI, VT_TRAFFIC, VT_LAST};
+
+//=============================================================================
+//
+// The Vehicle.
+//
+//=============================================================================
+class Vehicle : public DynaPhysDSG, public ISuperCamTarget, public IHudMapIconLocator
+{
+public:
+
+ //-----
+ // core
+ //-----
+
+ Vehicle();
+ virtual ~Vehicle();
+
+ /*
+ void AddRef();
+ void Release();
+ */
+
+ bool Init( const char* name, sim::SimEnvironment* se, VehicleLocomotionType loco, VehicleType vt = VT_USER, bool startoutofcar = true);
+ void Reset( bool ResetDamage = true, bool clearTraffic = false );
+ void ResetOnSpot( bool resetDamage=true, bool moveCarOntoRoad = true );
+
+ // make sure, for gui reasons, we don't call repeatedly
+ bool mAlreadyCalledAutoResetOnSpot;
+
+ void SetPosition(rmt::Vector* newPos);
+
+ void TransitToAI();
+
+ // utility
+ float FacingIntoRad(rmt::Vector facing);
+
+ // reset has come to be the one that uses the initial position & facing
+ // make a different reset that will just reset the flags and state - set transform can be called in
+ // conjunction with this
+ void ResetFlagsOnly(bool resetDamage);
+
+ VehicleType mVehicleType;
+
+ int mVehicleCentralIndex;
+
+ //
+ // Dlong: Proposed new methods (works better!)
+ //
+ void SetInitialPositionGroundOffsetAutoAdjust( rmt::Vector* newPos );
+ void SetInitialPosition( rmt::Vector* newPos );
+ void SetResetFacingInRadians( float rotation );
+ float GetFacingInRadians();
+
+ void SetTransform( rmt::Matrix &m );
+ void TrafficSetTransform(rmt::Matrix &m);
+
+ // for debugging and a cheat
+ void JumpOnHorn(float test);
+
+ void TurboOnHorn();
+ float mSecondsTillNextTurbo;
+ int mNumTurbos;
+
+ // Implements CollisionEntityDSG
+ //
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+ // drawable vehicle encapsulation:
+ GeometryVehicle* mGeometryVehicle;
+
+ rmt::Matrix mTransform;
+
+ // TODO - this is temporary?
+ const rmt::Vector& GetPosition() { return *((rmt::Vector*)mTransform.m[3]); }
+ const rmt::Vector& GetFacing() { return mVehicleFacing; }
+ const rmt::Matrix& GetTransform();
+ const rmt::Vector& GetExtents() { return mExtents; }
+
+ char* mName;
+ rmt::Vector mInitialPosition;
+ float mResetFacingRadians;
+
+ //This is which type of vehicle it is.
+ VehicleEnum::VehicleID mVehicleID;
+ void AssignEnumBasedOnName();
+
+
+ // vehicle event stuff
+ VehicleEventListener* mVehicleEventListener;
+
+ void EnteringJumpBoostVolume();
+ void ExitingJumpBoostVolume();
+
+ bool mDoingJumpBoost;
+
+
+ void ActivateTriggers( bool activate );
+ /*
+
+ in pure3d lhc, top-down view of vehicle:
+
+
+
+ ^
+ |
+ |
+ |
+ +Z
+ |
+ |
+ front
+ _____________
+ wheel 2 | | wheel 3
+ | |
+ | |
+ | |
+ | |
+ | |
+ | | ---+X----->
+ | |
+ | |
+ | |
+ | |
+ | |
+ wheel 1 ------------- wheel 0
+ rear
+
+
+
+ */
+
+ rmt::Vector mVehicleFacing;
+ rmt::Vector mVehicleUp;
+ rmt::Vector mVehicleTransverse;
+
+
+ rmt::Vector mVelocityCM;
+ float mSpeed;
+ float mPercentOfTopSpeed;
+ float mSpeedKmh;
+ float mLastSpeedKmh;
+ float mAccelMss;
+
+ rmt::Vector mOriginalCMOffset;
+ rmt::Vector mCMOffset; // this one modified by designer input
+
+ //----------------
+ // Shadow stuff
+ //----------------
+
+ virtual int CastsShadow();
+ bool IsSimpleShadow( void ) { return m_IsSimpleShadow; }
+ void SetSimpleShadow( bool IsSimpleShadow ) { m_IsSimpleShadow = IsSimpleShadow; }
+ void SetShadow( tShadowMesh* pShadowMesh ){};
+ void DisplayShadow();
+ void DisplaySimpleShadow( void );
+
+ //----------------
+ // Terrain type stuff
+ //----------------
+
+ // This is a 'rollup' from the four wheels.
+ eTerrainType mTerrainType;
+ bool mInterior;
+
+
+ float GetGroundY();
+
+
+ //----------------
+ // locomotion shit
+ //----------------
+
+ VehicleState mVehicleState; //{VS_NORMAL, VS_SLIP};
+
+ VehicleLocomotionType GetLocomotionType() {return mLoco;}
+
+ VehicleLocomotion* mVehicleLocomotion;
+ VehicleLocomotionType mLoco;
+
+
+ PhysicsLocomotion* mPhysicsLocomotion;
+
+
+ // *** For Traffic *** //
+ TrafficVehicle* mTrafficVehicle;
+
+ TrafficLocomotion* mTrafficLocomotion;
+
+ void CreateLocomotions();
+ void SetLocomotion( VehicleLocomotionType loco );
+
+
+ friend class PhysicsLocomotion;
+ friend class TrafficLocomotion;
+
+ bool mLocoSwitchedToPhysicsThisFrame;
+
+ //---------------------------
+ // world simulation interface
+ //---------------------------
+
+ void PreSubstepUpdate(float dt); // only thing this does right now is reset a flag
+ void PostSubstepUpdate(float dt);
+ void PreCollisionPrep(float dt, bool firstSubstep);
+ void Update(float dt);
+
+ void SetVehicleSimEnvironment(sim::SimEnvironment* se); // don't actually need this anymore
+
+ void GetCollisionAreaIndexAndAddSelf();
+ void RemoveSelfAndFreeCollisionAreaIndex();
+
+ void AddSelfToCollisionManager();
+ void AddToOtherCollisionArea(int index);
+ void RemoveSelfFromCollisionManager();
+
+ int mCollisionAreaIndex; // index into the collision area this instance will use.
+ RenderEnums::LayerEnum mRenderLayerEnum;
+ void SetRenderLayerEnum(RenderEnums::LayerEnum renderLayerEnum) {mRenderLayerEnum = renderLayerEnum;}
+ RenderEnums::LayerEnum GetRenderLayerEnum() {return mRenderLayerEnum;}
+
+ void DebugDisplay();
+ void CarDisplay(bool doit);
+ bool mOkToDrawSelf;
+
+ void DrawVehicle( bool draw ) { mDrawVehicle = draw; };
+ bool mDrawVehicle; //This is the same thing as above, but Greg uses the above for debugging.
+
+ //---------------------
+ // controller interface
+ //---------------------
+
+ void SetGas(float gas);
+ void SetBrake(float brake);
+ void SetWheelTurnAngle(float wheelTurnAngle, bool doNotModifyInputValue, float dt);
+ void SetReverse(float reverse);
+ void SetEBrake(float ebrake, float dt); // new timing thing for plum
+
+ void SetWheelTurnAngleDirectlyInRadiansForDusitOnly(float rad);
+
+ // Vehicle's cached values
+ float mGas;
+ float mLastGas;
+ float mDeltaGas;
+ float mBrake;
+ float mWheelTurnAngle;
+ float mWheelTurnAngleInputValue; // for Plum's low speed lag
+ float mReverse;
+ float mEBrake;
+ float mEBrakeTimer;
+ bool mBrakeLightsOn;
+ bool mReverseLightsOn;
+
+ float mBrakeTimer;
+ bool mBrakeActingAsReverse;
+
+
+ float mSteeringInputThreshold; // my own values to modify the h/w input value
+ float mSteeringPreSlope;
+
+ // the designers will have their own value to max the dropoff
+
+ float mUnmodifiedInputWheelTurnAngle; // just for ease of looking at the pure input
+
+
+ bool mDoingRockford;
+
+ float mSpeedBurstTimer;
+ bool mBuildingUpSpeedBurst;
+ bool mDoSpeedBurst;
+ float mFOVToRestore;
+ float mSpeedBurstTimerHalf;
+
+ //to disable player control of vehicle
+ void SetDisableGasAndBrake(bool tf) { mGasBrakeDisabled = tf; }
+ bool mGasBrakeDisabled;
+
+ //----------------------------------
+ // RenderManager/EntityDSG Interface
+ //----------------------------------
+ void Display();
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+
+ sim::SimState* GetSimState() const {return (sim::SimState*)mSimStateArticulated;}
+ sim::SimState* mpSimState() const { return GetSimState(); }
+
+ void DSGUpdateAndMove();
+
+ // just in case
+ virtual void OnSetSimState( sim::SimState* ipSimState ) {};
+
+ // stub out?
+ //virtual int FetchGroundPlane();
+ //virtual void FreeGroundPlane();
+
+ //virtual bool IsAtRest();
+
+ //-----------------
+ // camera interface
+ //-----------------
+
+ virtual void GetPosition( rmt::Vector* position );
+ virtual void GetHeading( rmt::Vector* heading );
+ virtual void GetVUP( rmt::Vector* vup );
+ virtual void GetVelocity( rmt::Vector* velocity );
+ virtual unsigned int GetID();
+ virtual bool IsCar() const;
+ virtual bool IsAirborn();
+ virtual bool IsUnstable();
+ virtual bool IsQuickTurn();
+ virtual bool IsInReverse();
+ virtual void GetFirstPersonPosition( rmt::Vector* position ) {};
+ virtual void GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const;
+
+
+ void CameraShakeTest(float impulseMagnitude, sim::Collision& inCollision);
+
+ bool IsMovingBackward(); // for Darren - don't have to have 'brake' input held down
+
+ bool mSteeringWheelsOutOfContact; // note:
+ // use for adjusting locomotive force appliction points when cresting hills, so turn this on
+ // when wheels 2,3 leave ground
+
+ bool mAirBorn;
+
+ bool mWeebleOn;
+ bool mCMOffsetSetToOriginal;
+
+ bool mInstabilityOffsetOn;
+
+ // ------------
+ // ai interface
+ // ------------
+ void SetDriverName(const char*);
+ Character* GetDriver()const{ return mpDriver; }
+ const char* GetDriverName(void);
+
+ bool HasDriver( void ) const // note! - this only determines if the car has a character model in the driver seat
+ { // nothing to do with whether or not there is a 'user' driving the car
+ return (mpDriver != NULL) || mPhantomDriver;
+ }
+
+ void SetDriver(Character*);
+
+ void SetPhantomDriver(bool b) { mPhantomDriver = b;}
+
+ char mDriverName[32];
+ Character* mpDriver;
+ bool mPhantomDriver;
+
+ void BounceCharacters(float dt);
+ void RecordRestSeatingPositionsOnEntry();
+ rmt::Vector mOurRestSeatingPosition;
+ rmt::Vector mNPCRestSeatingPosition;
+ static bool sDoBounce;
+
+ float mYAccelForSeatingOffset;
+
+ float mMaxBounceDisplacementPerSecond;
+ float mBounceAccelThreshold; // below this value just try and move back to rest
+ // if accel is above this value then we move opposite accel direction
+
+ rmt::Vector mVelocityCMLag;
+ rmt::Vector mPositionCMLag;
+
+ float mBounceLimit;
+ void ApplyDisplacementToCharacters(float displacement);
+ void MoveCharactersTowardsRestPosition(float dt);
+
+ virtual const char* const GetName();
+
+
+ bool mUserDrivingCar;
+ bool IsUserDrivingCar()const { return mUserDrivingCar; } // Sorry, I just really like to use accessor functions.
+ void SetUserDrivingCar(bool b);
+
+
+ virtual int GetAIRef() {return PhysicsAIRef::redBrickVehicle;}
+ /*
+ static int GetAIRef( void )
+ {
+ return PhysicsAIRef::redBrickVehicle;
+ }
+ */
+
+ virtual int GetGroundPlaneAIRef() {return PhysicsAIRef::redBrickPhizVehicleGroundPlane;}
+
+ /*
+ static int GetGroundPlaneAIRef( void )
+ {
+ return PhysicsAIRef::redBrickPhizVehicleGroundPlane;
+ }
+ */
+
+ const rmt::Vector& GetPassengerLocation( void ) const;
+ const rmt::Vector& GetDriverLocation( void ) const;
+
+ //--------------
+ // sfx interface
+ //--------------
+
+ float GetSpeedKmh();
+ float GetAccelMss();
+ float GetRPM();
+ float GetGas()const { return mGas; }
+ float GetDeltaGas()const { return mDeltaGas; }
+ float GetBrake()const { return mBrake; }
+ float GetSkidLevel(); // overall amount amongst the (currently) 4 wheels
+ // both the skidding - tire locked
+ // and slip - tire rotating really fast.
+ int GetGear(); // -1 is reverse, 0 neutral
+ // TODO - how to do the gearshift interface?
+
+ bool IsSafeToUpShift();
+
+ // gearbox stuff
+ float mRPM;
+ float mRPMUpRate;
+ float mRPMDownRate;
+
+ float mBaseRPM;
+ float mMaxRpm;
+ float mShiftPointHigh;
+ float mShiftPointLow;
+ int mGear; // -1 for reverse, 0 neutral
+
+ float mSkidLevel;
+ float mBurnoutLevel; // use for sound and smoke level?
+ bool mDoingBurnout;
+ void SetGeometryVehicleWheelSmokeLevel();
+
+ bool mNoSkid; // just to flag frinks hovering vehilces...
+ bool mNoFrontSkid; // For the rocket car and other vehicles that can only skid using
+ // the back two wheels
+
+ bool mDoingWheelie; //need this?
+
+ int mNumGears;
+ float* mGearRatios;
+ float mFinalDriveRatio;
+ void InitGears();
+
+ void UpdateGearAndRPM();
+
+ void SparksTest(float impulseMagnitude, sim::Collision& inCollision); // should this also be wrapped in mUserDrivingCar??
+
+ //--------------------
+ // the designer params
+ //--------------------
+
+ struct DesignerParams
+ {
+ // from physicsvehicle
+
+ float mDpGasScale; // proportional to mass
+ float mDpSlipGasScale;
+ float mDpHighSpeedGasScale;
+ float mDpGasScaleSpeedThreshold;
+ float mDpBrakeScale; // proportional to mass
+ float mDpTopSpeedKmh;
+
+ float mDpMass; // think of it as kg
+
+ float mDpMaxWheelTurnAngle; // in degrees
+ float mDpHighSpeedSteeringDrop; // 0.0 to 1.0
+
+ float mDpTireLateralStaticGrip;
+ float mDpTireLateralResistanceNormal;
+ float mDpTireLateralResistanceSlip;
+
+ float mDpTireLateralResistanceSlipNoEBrake; // this one's for more out of control driving
+ float mDpSlipEffectNoEBrake;
+
+ float mDpEBrakeEffect;
+
+ float mDpSuspensionLimit;
+ float mDpSpringk;
+ float mDpDamperc;
+
+ float mDpSuspensionYOffset;
+
+ float mHitPoints;
+
+ float mDpBurnoutRange;
+
+
+ float mDpWheelieRange;
+ float mDpWheelieYOffset;
+ float mDpWheelieZOffset;
+
+ float mDpMaxSpeedBurstTime;
+
+ float mDpDonutTorque;
+
+ // new tunable value for plum
+ float mDpWeebleOffset;
+
+ float mDpGamblingOdds;
+
+ rmt::Vector mDpCMOffset;
+
+ };
+
+ DesignerParams mDesignerParams;
+
+ // simple accessors that script functions call
+ //
+ // note - you have to call CalculateValuesBasedOnDesignerParams after using these.
+ //
+ // make sure to name script methods based on watcher names
+ // these names can match the data.
+
+ //Chuck: Added these so cars now have odds for gambling races.
+ void SetGamblingOdds(float odds) {mDesignerParams.mDpGamblingOdds = odds;}
+ float GetGamblingOdds() {return mDesignerParams.mDpGamblingOdds;}
+
+ //Chuck:using this flag for forced car mission, since forced cars dont belong to the player,but the NPC
+ //Forced cars should respawn and do not check the check to or update the charactersheet. If Flag is true then this
+ //car is owned by the player, if false then its owned by the NPC
+ bool mbPlayerCar;
+
+ void SetGasScale(float gs) {mDesignerParams.mDpGasScale = gs;}
+ void SetSlipGasScale(float value) {mDesignerParams.mDpSlipGasScale = value;}
+
+ void SetHighSpeedGasScale(float gs) {mDesignerParams.mDpHighSpeedGasScale = gs;}
+ void SetGasScaleSpeedThreshold(float t) {mDesignerParams.mDpGasScaleSpeedThreshold = t;}
+
+ void SetBrakeScale(float bs) {mDesignerParams.mDpBrakeScale = bs;}
+ void SetTopSpeedKmh(float ts) {mDesignerParams.mDpTopSpeedKmh = ts;}
+ float GetTopSpeed() const;
+ void SetMass(float m) {mDesignerParams.mDpMass = m;}
+ void SetMaxWheelTurnAngle(float mwta) {mDesignerParams.mDpMaxWheelTurnAngle = mwta;}
+ void SetHighSpeedSteeringDrop(float value) {mDesignerParams.mDpHighSpeedSteeringDrop = value;}
+ void SetTireLateralStaticGrip(float g) {mDesignerParams.mDpTireLateralStaticGrip = g;}
+ void SetTireLateralResistanceNormal(float n) {mDesignerParams.mDpTireLateralResistanceNormal = n;}
+ void SetTireLateralResistanceSlip(float s) {mDesignerParams.mDpTireLateralResistanceSlip = s;}
+ void SetEBrakeEffect(float s) {mDesignerParams.mDpEBrakeEffect = s;}
+
+ void SetTireLateralResistanceSlipWithoutEBrake(float s) {mDesignerParams.mDpTireLateralResistanceSlipNoEBrake = s;}
+ void SetSlipEffectWithoutEBrake(float s) {mDesignerParams.mDpSlipEffectNoEBrake = s;}
+
+ void SetCMOffsetX(float s) {mDesignerParams.mDpCMOffset.x = s;}
+ void SetCMOffsetY(float s) {mDesignerParams.mDpCMOffset.y = s;}
+ void SetCMOffsetZ(float s) {mDesignerParams.mDpCMOffset.z = s;}
+
+ void SetSuspensionLimit(float s) {mDesignerParams.mDpSuspensionLimit = s;}
+ void SetSuspensionSpringK(float s) {mDesignerParams.mDpSpringk = s;}
+ void SetSuspensionDamperC(float s) {mDesignerParams.mDpDamperc = s;}
+
+ void SetSuspensionYOffset(float s) {mDesignerParams.mDpSuspensionYOffset = s;}
+
+ void SetHitPoints(float h) {mDesignerParams.mHitPoints = h;}
+ void SetBurnoutRange(float h) {mDesignerParams.mDpBurnoutRange = h;}
+ void SetMaxSpeedBurstTime(float h) {mDesignerParams.mDpMaxSpeedBurstTime = h;}
+ void SetDonutTorque(float h) {mDesignerParams.mDpDonutTorque = h;}
+
+ void SetWeebleOffset(float w) {mDesignerParams.mDpWeebleOffset = w;}
+
+ void SetWheelieRange(float w) {mDesignerParams.mDpWheelieRange = w;}
+ void SetWheelieYOffset(float y) {mDesignerParams.mDpWheelieYOffset = y;}
+ void SetWheelieZOffset(float z) {mDesignerParams.mDpWheelieZOffset = z;}
+
+ void SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] );
+ void SetShininess( unsigned char EnvRef );
+
+ void CalculateValuesBasedOnDesignerParams();
+
+
+ //------------
+ // wheel stuff
+ //------------
+
+ void UpdateWheelRenderingInfo(float dt);
+ bool IsJointAWheel(int jointIndex);
+ //bool SetWheelCorrectionOffset(int jointNum, float objectSpaceYOffsetFromCurrentPosition);
+ bool SetWheelCorrectionOffset(int jointNum, float objectSpaceYOffsetFromCurrentPosition, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint);
+
+ void SetNoDamperDownFlagOnWheel(int jointIndex); // joint corresponds to a wheel
+ bool mDamperShouldNotPullDown[4];
+
+ SuspensionJointDriver* mSuspensionJointDrivers[4];
+
+ int mWheelToJointIndexMapping[4];
+ int* mJointIndexToWheelMapping;
+
+ rmt::Vector mSuspensionRestPointsFromFile[4];
+ rmt::Vector mSuspensionRestPoints[4];
+ float mSuspensionRestValue;
+ float mSuspensionMaxValue;
+
+ void CalculateSuspensionLocationAndVelocity();
+ rmt::Vector mSuspensionWorldSpacePoints[4];
+ rmt::Vector mSuspensionPointVelocities[4];
+
+ float mWheelBase;
+ float GetWheelBase() {return mWheelBase;}
+
+ float GetMass()const { return mDesignerParams.mDpMass; }
+
+ float GetRestHeightAboveGround();
+
+ void FetchWheelMapping();
+
+ Wheel* mWheels[4];
+ void InitWheelsAndLinkSuspensionJointDrivers();
+
+ friend class Wheel;
+
+
+ //--------------
+ // damage states
+ //--------------
+
+/*
+
+ here is what damage states have evolved too:
+
+ there will be 3 types of damage states on vehicles:
+
+ 1.
+ On "user" level vehicles there will be:
+
+ normal
+ texture damage (localized front, back, driver side, passenger side)
+ flapping joints
+ [flapping joints breaking off] <--- this might not be done for this milestone (or ever); I see it as very low priority right now
+ smoke level 1
+ smoke level 2
+ smoke level 3
+ disabled
+
+
+ 2.
+ On "ai" level vehicles there will be
+
+ normal
+ texture damage (localized front, back, driver side, passenger side)
+ smoke level 1
+ smoke level 2
+ smoke level 3
+ disabled
+
+
+ 3.
+ On "traffic" there will be
+
+ normal
+ simultaneous BRIEF paricle system Poof! and overall damage texture - disabled.
+
+
+
+
+
+
+*/
+
+ enum VehicleDamageType { VDT_UNSET, VDT_USER, VDT_AI, VDT_TRAFFIC };
+ //int mDamageType; // 1, 2, or 3 - see above.
+ VehicleDamageType mDamageType;
+
+
+ bool IsAFlappingJoint(int index);
+ bool InitFlappingJoints(); // returns true if least 1 flapping joint
+ bool AddFlappingJoint(const char* transJointName, const char* rotJointName, rmt::Vector& axis, rmt::Vector& rotAxis, int count, int* collisionJointIndex);
+
+ sim::PhysicsJointInertialEffector* mInertialJointDrivers[4];
+ int* mJointIndexToInertialJointDriverMapping;
+ // just for easier delete
+ sim::PhysicsJointMatrixModifier* mPhysicsJointMatrixModifiers[4];
+
+ int mDoorDJoint; // recall set to -1 if not present
+ int mDoorPJoint;
+ int mHoodJoint;
+ int mTrunkJoint;
+
+ // logical state of pieces:
+ // 0 undamaged
+ // 1 textured
+ // 2 flapping
+ // 3 gone
+
+ int mDoorDDamageLevel;
+ int mDoorPDamageLevel;
+ int mHoodDamageLevel;
+ int mTrunkDamageLevel;
+
+
+
+ enum DamageLocation{ dl_hood, dl_trunk, dl_driverside, dl_passengerside };
+
+ DamageLocation TranslateCollisionIntoLocation(sim::Collision& inCollision);
+
+ //void DamageJoint(int joint, int damlevel);
+ //void DamageAllJoints();
+
+ //void BreakOffFlappingPiece(int index);
+
+ bool CarOnCarDamageLogic(bool thisIsA, sim::SimState* simStateA, sim::SimState* simStateB);
+ void SwitchOnDamageTypeAndApply(float normalizedMagnitude, sim::Collision& inCollision);
+
+ // =========================================================
+ bool mCollidedWithVehicle; // when we are involved in collision with a vehicle
+ void DusitsStunTest( float normalizedMagnitude );
+
+ // store data about the last thing that hit me
+ bool mOutOfControl;
+ float mNormalizedMagnitudeOfVehicleHit;
+ bool mWasHitByVehicle; // when we try to distinguish who hit who, if somebody hit us, this is true
+ VehicleType mWasHitByVehicleType;
+ rmt::Vector mSwerveNormal; // if somebody hit us, this tells us direction of swerve
+ void TestWhoHitWhom( sim::SimState* simA, sim::SimState* simB, float normalizedMagnitude, const sim::Collision & inCollision );
+ // =========================================================
+
+
+ void VisualDamageType1(float percentageDamage, DamageLocation dl);
+ void VisualDamageType2(float percentageDamage, DamageLocation dl);
+ void VisualDamageType3(float percentageDamage);
+ void SyncVisualDamage( float Health ); // Note that this is the health of the car 0.0f -husk, 1.0f -nice and new.
+
+ rmt::Vector mSmokeOffset;
+ rmt::Vector& GetSmokeOffset() {return mSmokeOffset;}
+ rmt::Vector GetWheel0Offset();
+ rmt::Vector GetWheel1Offset();
+
+ void DebugInflictDamageHood();
+ void DebugInflictDamageBack();
+ void DebugInflictDamageDriverSide();
+ void DebugInflictDamagePassengerSide();
+
+
+ //void TriggerDamage(float amount, int hitJoint);
+ float TriggerDamage(float amount, bool clamp = true); // returns new percentage
+ bool IsVehicleDestroyed()const { return mVehicleDestroyed; }
+
+ bool mVehicleDestroyed;
+ bool mDontShowBrakeLights;
+
+ bool mAlreadyPlayedExplosion;
+
+ float mDamageOutResetTimer;
+
+ float mNoDamageTimer; // to make a clean distinction between the second last and the last hit
+
+
+ float mHitPoints;
+ bool mVehicleCanSustainDamage; // defaults to false
+ void SetVehicleCanSustainDamage(bool trueOrFalse) {mVehicleCanSustainDamage = trueOrFalse;}
+ float GetVehicleLifePercentage(float testvalue);
+
+ bool mIsADestroyObjective;
+ void VehicleIsADestroyObjective(bool b) {mIsADestroyObjective = b;}
+
+
+ void ResetDamageState();
+
+ void BeefUpHitPointsOnTrafficCarsWhenUserDriving();
+
+ //----------
+ // sim model
+ //----------
+
+ sim::SimStateArticulated* mSimStateArticulated;
+ sim::ArticulatedPhysicsObject* mPhObj;
+ sim::PhysicsProperties* mPhysicsProperties;
+
+ // new - swap in model for character to jump on top of
+ sim::SimStateArticulated* mSimStateArticulatedOutOfCar;
+
+ sim::SimStateArticulated* mSimStateArticulatedInCar;
+
+ bool mUsingInCarPhysics;
+ void SetInCarSimState();
+ void SetOutOfCarSimState();
+
+ //bool mWaitingToSwitchToOutOfCar;
+ //float mOutOfCarSwitchTimer; // brutal fucking hack
+
+ void InitSimState(sim::SimEnvironment* se);
+ void SetupPhysicsProperties();
+
+ void InitGroundPlane();
+ sim::ManualSimState* mGroundPlaneSimState;
+ sim::WallVolume* mGroundPlaneWallVolume;
+ sim::PhysicsProperties* mGroundPlanePhysicsProperties;
+
+
+ void RestTest(void);
+ bool SelfRestTest(void);
+ bool mAtRestAsFarAsTriggersAreConcerned;
+ void ZeroOutXZVelocity();
+
+ bool mCreatedByParkedCarManager;
+
+ void CreatePoseEngine();
+ poser::PoseEngine* mPoseEngine;
+ RootMatrixDriver* mRootMatrixDriver;
+
+ //void CreatePoseEngineOutOfCar();
+ //poser::PoseEngine* mPoseEngineOutOfCar;
+ //RootMatrixDriver* mRootMatrixDriverOutOfCar;
+
+
+ float mDragCoeff; // TODO - do we want regular, quadratic drag?
+ void CalculateDragCoeffBasedOnTopSpeed();
+
+ float mRollingFrictionForce;
+ float mTireLateralResistance;
+ float mSlipGasModifier;
+
+ float mCollisionLateralResistanceDropFactor;
+
+ //bool mOkToCrashLand;
+ //-----------
+ // other crap
+ //-----------
+
+ bool mBottomedOutThisFrame; // for debug rendering
+ bool mWasAirborn; // used to help decide when to fire a bottomed out event for esan
+ float mWasAirbornTimer;
+
+ float mBottomOutSpeedMaintenance;
+
+ float mStuckOnSideTimer;
+
+ bool mDrawWireFrame; // also debug rendering
+ bool mLosingTractionDueToAccel; // for plum
+
+ void SetupRadDebugWatchStuff();
+
+ // unfortunately vehicle will now have a dependency on this shyte, but
+ // I still think this is the cleanest way to do it.
+ RectTriggerVolume* mpTriggerVolume;
+ EventLocator* mpEventLocator;
+ bool mTriggerActive;
+ void InitEventLocator();
+
+ int mDriverInit;
+
+ bool mHijackedByUser;
+
+ // Attach a tDrawable collectible to the vehicle
+ // returns true if attached or false if not ( false happens
+ // when a collectible is already attached to the vehicle)
+ bool AttachCollectible( StatePropCollectible* );
+ StatePropCollectible* GetAttachedCollectible();
+ // The collectible is detached and free to move around the world under physics control
+ void DetachCollectible( const rmt::Vector& velocity, bool explode = true );
+ float mForceToDetachCollectible;
+
+ int mCharacterSheetCarIndex;
+
+ //----------------------
+ // door opening/closing
+ //----------------------
+public:
+ enum Door
+ {
+ DOOR_DRIVER,
+ DOOR_PASSENGER
+ };
+
+ enum DoorAction
+ {
+ DOORACTION_NONE,
+ DOORACTION_OPEN,
+ DOORACTION_CLOSE
+ };
+
+ // move the door to the given "position'
+ // positions are in 0 to 1, DoorAction should be open or close, not none
+ void MoveDoor(Door, DoorAction, float position);
+
+ // do we actually need to open or close the door (test's physics state, current position, etc)
+ bool NeedToOpenDoor(Door); // returns false if door is already open
+ bool NeedToCloseDoor(Door); // returns false if door is already closed
+ bool HasActiveDoor(Door); // is there and active door (i.e. exists, and isn't flapping)
+
+ void UpdateDoorState(void);
+ void ReleaseDoors(void);
+
+ void PlayExplosionEffect();
+
+ virtual void AddToSimulation();
+ virtual void ApplyForce( const rmt::Vector& direction, float force );
+
+ bool mHasDoors : 1;
+ bool mVisibleCharacters : 1;
+ bool mIrisTransition : 1;
+ bool mAllowSlide : 1;
+ bool mHighRoof : 1;
+ float mCharacterScale;
+
+ // Calculates the s_ForceToDamage variable when given
+ // the number of hitpoints that should be removed when a vehicle explodes
+ static void SetPercentDamageFromExplosion( float percent );
+ // Calculates the s_ForceToDamagePlayer variable when given
+ // the number of hitpoints that should be removed when a vehicle explodes
+ static void SetPercentDamageFromExplosionPlayer( float percent );
+
+private:
+
+ float mDesiredDoorPosition[2];
+ DoorAction mDesiredDoorAction[2];
+
+ void CalcDoor(unsigned index, unsigned joint, float scale);
+ void CalcDoors(void);
+
+ //----------------------
+
+private:
+ rmt::Vector mPassengerLocation;
+ rmt::Vector mDriverLocation;
+ rmt::Vector mExtents;
+ bool m_IsSimpleShadow;
+
+
+ // The number of hitpoints that are removed (via TriggerDamage)
+ //
+ static float s_DamageFromExplosion;
+ static float s_DamageFromExplosionPlayer;
+};
+
+
+#endif // _VEHICLE_H
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp
new file mode 100644
index 0000000..d452954
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp
@@ -0,0 +1,167 @@
+#include <worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h>
+#include <main/game.h>
+#include <memory/classsizetracker.h>
+#include <radmath/radmath.hpp>
+
+class Behaviour
+{
+public:
+ virtual rmt::Vector Tick( Vehicle* pVehicle ) = 0;
+};
+
+class VehicleWander
+:
+public Behaviour
+{
+public:
+
+ VehicleWander( void );
+ virtual ~VehicleWander( void );
+
+ rmt::Vector& GetTargetPoint( void );
+ void SetTargetPoint( rmt::Vector& targetPoint );
+
+ rmt::Vector& GetPosition( void );
+ void SetPosition( rmt::Vector& position );
+
+ float GetTargetRadius( void ) const;
+ void SetTargetRadius( float radius );
+
+ float GetSteeringRadius( void ) const;
+ void SetSteeringRadius( float radius );
+
+ rmt::Vector Tick( Vehicle* pVehicle );
+private:
+ rmt::Vector mTargetPoint;
+ rmt::Vector mPosition;
+ float mfTargetCircleRadius;
+ float mfSteeringCircleRadius;
+};
+
+VehicleWander::VehicleWander( void )
+:
+mTargetPoint( 0.0f, 0.0f, 1.0f ),
+mPosition( 0.0f, 0.0f, 0.1f ),
+mfTargetCircleRadius( 1.0f ),
+mfSteeringCircleRadius( 0.003f )
+{
+ mTargetPoint.z = mfTargetCircleRadius;
+}
+
+VehicleWander::~VehicleWander( void )
+{
+}
+
+rmt::Vector VehicleWander::Tick( Vehicle* pVehicle )
+{
+ static rmt::Randomizer r( Game::GetRandomSeed () );
+ static rmt::Vector point( 0.0f, 0.0f, 0.0f );
+
+ point.x = r.FloatSigned( );
+ point.y = 0.0f;
+ point.z = r.FloatSigned( );
+
+ // Make a point on the new circle.
+ //
+ point.Normalize( );
+ point.Scale( mfSteeringCircleRadius );
+
+ // Add to point on circle.
+ //
+ mTargetPoint.Add( point );
+ // Project back to unit circle.
+ //
+ mTargetPoint.Normalize( );
+ // Scale to actual Target Circle.
+ //
+ mTargetPoint.Scale( mfTargetCircleRadius );
+
+ rmt::Vector retVector;
+ retVector.Add( mPosition, mTargetPoint );
+ retVector.Normalize( );
+
+ return retVector;
+}
+
+AiVehicleController::AiVehicleController( Vehicle* pVehicle )
+{
+ //CLASSTRACKER_CREATE( AiVehicleController );
+ SetVehicle( pVehicle );
+}
+
+AiVehicleController::~AiVehicleController( void )
+{
+ //CLASSTRACKER_DESTROY( AiVehicleController );
+}
+
+void AiVehicleController::Update( float timeins )
+{
+ /*
+ static Behaviour* spBehaviour = 0;
+ if ( !spBehaviour )
+ {
+ spBehaviour = new VehicleWander;
+ }
+ rmt::Vector direction = spBehaviour->Tick( GetVehicle( ) );
+ mSteering.SetValue( direction.x );
+ mGas.SetValue( direction.z );
+ */
+ mHandBrake.SetValue( 1.0f );
+
+ mGas.SetValue( 0.0f );
+ mSteering.SetValue( 0.0f );
+ mBrake.SetValue( 0.0f );
+ mReverse.SetValue( 0.0f );
+ mHorn.SetValue( 0.0f );
+
+}
+
+float AiVehicleController::GetGas( void ) const
+{
+ // ai shoudln't really use this?
+ return mGas.GetValue();
+}
+
+float AiVehicleController::GetThrottle( void ) const
+{
+ return mGas.GetValue();
+}
+
+float AiVehicleController::GetBrake( void ) const
+{
+ return mBrake.GetValue();
+}
+
+float AiVehicleController::GetSteering( bool& isWheel ) const
+{
+ isWheel = false;
+ return mSteering.GetValue();
+}
+
+
+// AI shouldn't really call these
+float AiVehicleController::GetSteerLeft( void ) const
+{
+ return 0.0f;
+}
+
+
+float AiVehicleController::GetSteerRight( void ) const
+{
+ return 0.0f;
+}
+
+float AiVehicleController::GetHandBrake( void ) const
+{
+ return mHandBrake.GetValue();
+}
+
+float AiVehicleController::GetReverse( void ) const
+{
+ return mReverse.GetValue();
+}
+
+float AiVehicleController::GetHorn( void ) const
+{
+ return mHorn.GetValue();
+} \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h
new file mode 100644
index 0000000..2f2a463
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h
@@ -0,0 +1,40 @@
+#ifndef AIVEHICLECONTROLLER_H_
+#define AIVEHICLECONTROLLER_H_
+
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+#include <input/button.h>
+
+class Vehicle;
+
+class AiVehicleController
+:
+public VehicleController
+{
+public:
+ AiVehicleController( Vehicle* pVehicle );
+ virtual ~AiVehicleController( void );
+
+ virtual void Update( float timeins );
+
+ virtual float GetGas( void ) const;
+ virtual float GetThrottle( void ) const;
+ virtual float GetBrake( void ) const;
+ virtual float GetSteering( bool& isWheel ) const;
+
+ virtual float GetSteerLeft( void ) const;
+ virtual float GetSteerRight( void ) const;
+
+ virtual float GetHandBrake( void ) const;
+ virtual float GetReverse( void ) const;
+ virtual float GetHorn( void ) const;
+protected:
+ Button mGas;
+ Button mBrake;
+ Button mSteering;
+ Button mHandBrake;
+ Button mReverse;
+ Button mHorn;
+private:
+};
+
+#endif //AIVEHICLECONTROLLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp
new file mode 100644
index 0000000..517da35
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp
@@ -0,0 +1,4 @@
+#include <worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp>
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp>
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp
new file mode 100644
index 0000000..3e9ae01
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp
@@ -0,0 +1,807 @@
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+#include <worldsim/redbrick/vehiclecontroller/../vehicle.h>
+#include <presentation/gui/guisystem.h>
+#include <raddebugwatch.hpp>
+
+#include <input/inputmanager.h>
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+#include <main/commandlineoptions.h>
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+#include <input/basedamper.h>
+#include <input/steeringspring.h>
+#include <input/constanteffect.h>
+#include <input/wheelrumble.h>
+#endif
+
+#ifdef RAD_WIN32
+const short DEFAULT_SPRING_COEFF = 3000;
+#else
+const short DEFAULT_SPRING_COEFF = 50;
+#endif
+const char DEFAULT_SPRING_OFFSET = 0;
+const unsigned char DEFAULT_SPRING_DEADBAND = 0;
+const unsigned char DEFAULT_SPRING_SAT = 0;
+
+const char DEFAULT_DAMPER_OFFSET = 0;
+const unsigned char DEFAULT_DAMPER_DEADBAND = 5;
+const unsigned char DEFAULT_DAMPER_SAT = 0;
+const short DEFAULT_DAMPER_COEFF = 0;
+
+const short DEFAULT_CONSTANT_MAGNITUDE = 0;
+const unsigned short DEFAULT_CONSTANT_DIRECTION = 0;
+
+#ifdef DEBUGWATCH
+short gDamperSlip = -100;
+short gDamperMax = 100;
+float gDamperSpeed = 200.0f;
+
+unsigned char gSpringSlip = 20;
+unsigned char gSpringMax = 200;
+float gSpringSpeed = 80.0f;
+#else
+const float gDamperSpeed = 200.0f;
+
+#ifdef RAD_WIN32
+const u16 gSpringMax = 1000;
+const unsigned char gSpringSlip = 20;
+const short gDamperMax = 100;
+const short gDamperSlip = -600;
+#else
+const unsigned char gSpringMax = 200;
+const unsigned char gSpringSlip = 20;
+const short gDamperMax = 100;
+const short gDamperSlip = -100;
+#endif
+
+const float gSpringSpeed = 80.0f;
+
+const short gConstantMag = 0;
+const unsigned short gConstantDirection = 0;
+
+#endif
+
+#ifdef RAD_GAMECUBE
+const float GC_STEERING_FUDGE = 1.25f;
+#endif
+
+HumanVehicleController::HumanVehicleController( void )
+:
+mpMappable( 0 ),
+mControllerId( -1 ),
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+mSpring( NULL ),
+mDamper( NULL ),
+mConstantEffect( NULL ),
+mWheelRumble( NULL ),
+mHeavyWheelRumble( NULL ),
+#endif
+mDisableReset( false )
+{
+#ifdef RAD_PS2
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ mpWheel[ i ] = NULL;
+ }
+#endif
+}
+
+void HumanVehicleController::Create( Vehicle* pVehicle, VehicleMappable* pMappable, int controllerId )
+{
+ SetVehicle( pVehicle );
+ tRefCounted::Assign( mpMappable, pMappable );
+ mControllerId = controllerId;
+}
+
+#ifdef RAD_PS2
+void HumanVehicleController::SetWheel( VehicleMappable* pMappable, unsigned int wheelNum )
+{
+ rAssert( wheelNum < Input::MaxUSB );
+
+ tRefCounted::Assign( mpWheel[ wheelNum ], pMappable );
+}
+#endif
+
+HumanVehicleController::~HumanVehicleController( void )
+{
+ ReleaseVehicleMappable();
+
+#ifdef RAD_PS2
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ mpWheel[ i ]->Release();
+ mpWheel[ i ] = NULL;
+ }
+ }
+#endif
+}
+
+void HumanVehicleController::ReleaseVehicleMappable( void )
+{
+ if ( mpMappable )
+ {
+ mpMappable->Release();
+ mpMappable = 0;
+ }
+}
+
+
+float HumanVehicleController::GetGas( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::Gas )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Gas )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::Gas )->GetValue();
+#endif
+}
+
+float HumanVehicleController::GetThrottle( void ) const
+{
+ //No throttle on the wheels.
+ return mpMappable->GetButton( VehicleMappable::Throttle )->GetValue();
+}
+
+
+float HumanVehicleController::GetBrake( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::Brake )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Brake )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::Brake )->GetValue();
+#endif
+}
+
+float HumanVehicleController::GetSteering( bool& isWheel ) const
+{
+#ifdef RAD_PS2
+ isWheel = false;
+
+ float value = mpMappable->GetButton( VehicleMappable::Steer )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ isWheel = true;
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Steer )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ isWheel = true;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ //How do I tell if this is from a wheel or a stick?
+#ifdef RAD_GAMECUBE
+ //This is cheating.
+ isWheel = mpMappable->IsWheel();
+ return rmt::Clamp( mpMappable->GetButton( VehicleMappable::Steer )->GetValue() * GC_STEERING_FUDGE, -1.0f, 1.0f );
+#else
+#ifdef RAD_WIN32
+ isWheel = GetInputManager()->GetController(mControllerId)->IsWheel();
+#endif
+ return mpMappable->GetButton( VehicleMappable::Steer )->GetValue();
+#endif
+
+#endif
+}
+
+float HumanVehicleController::GetSteerLeft( void ) const
+{
+ //No steerleft on the wheels
+ return mpMappable->GetButton( VehicleMappable::SteerLeft )->GetValue();
+}
+
+float HumanVehicleController::GetSteerRight( void ) const
+{
+ //No steer right on the wheels
+ return mpMappable->GetButton( VehicleMappable::SteerRight )->GetValue();
+}
+
+float HumanVehicleController::GetReverse( void ) const
+{
+ //No reverse mapped.
+ return 0.0f;
+}
+
+
+float HumanVehicleController::GetHandBrake( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::HandBrake )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::HandBrake )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::HandBrake )->GetValue();
+#endif
+}
+
+float HumanVehicleController::GetHorn( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::Horn )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Horn )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::Horn )->GetValue();
+#endif
+}
+
+void HumanVehicleController::Reset( void )
+{
+ if( !mDisableReset )
+ {
+ Vehicle* vehicle = GetVehicle();
+ GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >( vehicle ) );
+ }
+}
+
+/*
+==============================================================================
+HumanVehicleController::GetMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: VehicleMappable
+
+=============================================================================
+*/
+VehicleMappable *HumanVehicleController::GetMappable( void ) const
+{
+ return mpMappable;
+}
+
+//=============================================================================
+// HumanVehicleController::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::Update( float timeins )
+{
+ //This is where the output points will be set.
+
+ Vehicle* vehicle = GetVehicle();
+ float speed = vehicle->GetSpeedKmh();
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ UserController* uc = NULL;
+
+ //Set up the output points to default settings.
+#ifdef RAD_PS2
+ //TODO: Make this only set up the active wheel.
+ if ( mpWheel[ 0 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB0 );
+ }
+ else if ( mpWheel[ 1 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB1 );
+ }
+ else
+ {
+ uc = GetInputManager()->GetController( mControllerId );
+ }
+#elif defined RAD_GAMECUBE || defined(RAD_WIN32)
+ uc = GetInputManager()->GetController( mControllerId );
+#endif
+
+ SetupRumbleFeatures( uc );
+
+ if ( mSpring || mDamper || mConstantEffect )
+ {
+ rAssert( mSpring && mDamper );
+ //Update these effects.
+
+ if ( vehicle->IsAirborn() ||
+ vehicle->mVehicleState == VS_SLIP ||
+ vehicle->mVehicleState == VS_EBRAKE_SLIP )
+ {
+#ifdef RAD_WIN32
+ if( vehicle->IsAirborn() )
+ {
+ mSpring->SetSpringStrength( 0 );
+ mSpring->SetSpringCoefficient( 0 );
+
+ mDamper->SetDamperCoefficient( 0 );
+ mDamper->SetDamperStrength( 0 );
+ }
+ else
+ {
+ mDamper->SetDamperCoefficient( gDamperSlip );
+ mDamper->SetDamperStrength( 255 );
+
+ mSpring->SetSpringStrength( static_cast<u16>(50*speed) );
+ mSpring->SetSpringCoefficient( gDamperSlip*20 );
+
+ mConstantEffect->SetDirection( static_cast<u16>(90*speed) );
+ mConstantEffect->SetMagnitude( static_cast<s16>(-200*speed) );
+ }
+#else
+ //Kill the damper
+ mDamper->SetDamperCoefficient( gDamperSlip );
+ mDamper->SetDamperStrength( 0 );
+
+
+ mSpring->SetSpringStrength( gSpringSlip );
+ mSpring->SetSpringCoefficient( gDamperSlip );
+#endif
+ }
+ else
+ {
+
+ if ( speed > gSpringSpeed )
+ {
+ speed = gSpringSpeed;
+ }
+
+#ifdef RAD_WIN32
+ u16 springSat = static_cast<u16>(rmt::FtoL(gSpringMax * ( speed / gSpringSpeed )));
+ if( speed < 5.0f )
+ {
+ if( rmt::Epsilon( 0.0f, speed, 0.05f ) ) springSat = gSpringMax;
+ else springSat = static_cast<u16>((gSpringMax)/(speed));
+ }
+ springSat*=50;
+
+#else
+ unsigned char springSat = static_cast<unsigned char>(rmt::FtoL(gSpringMax * ( speed / gSpringSpeed )));
+#endif
+ mSpring->SetSpringStrength( springSat );
+ mSpring->SetSpringCoefficient( DEFAULT_SPRING_COEFF );
+
+ if ( speed > gDamperSpeed )
+ {
+ speed = gDamperSpeed;
+ }
+
+ short dampercoeff = gDamperMax - static_cast<short>(rmt::FtoL(rmt::LtoF(gDamperMax) * ( speed / gDamperSpeed )));
+ mDamper->SetDamperCoefficient( dampercoeff );
+ mDamper->SetDamperStrength( DEFAULT_DAMPER_SAT );
+ }
+ }
+#endif
+ //Update the rumble effect.
+ switch ( vehicle->mTerrainType )
+ {
+ case TT_Grass: // Grass type terrain most everything else which isn't road or sidewalk.
+ case TT_Dirt: // Dirt type terrain.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND2, 250 );
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+// mWheelRumble->SetMagDir( 200, 90 );
+// mWheelRumble->SetPPO( 120 - (80 * (speed / 80.0f)), 0, 0 );
+ }
+#endif
+ }
+ }
+ case TT_Water: // Water on surface type terrain.
+ case TT_Gravel: // Loose gravel type terrain.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND4, 250 );
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+// mWheelRumble->SetMagDir( 200, 90 );
+// mWheelRumble->SetPPO( 120 - (60 * (speed / 80.0f)), 0, 0 );
+ }
+#endif
+ }
+ }
+ case TT_Sand: // Sand type terrain.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND3, 250 );
+ }
+ }
+ case TT_Metal: // Powerplant and other structurs.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND2, 250 );
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+// mWheelRumble->SetMagDir( 255, 90 );
+// mWheelRumble->SetPPO( 120 - (60 * (speed / 80.0f)), 0, 0 );
+ }
+#endif
+ }
+ }
+ case TT_Road:
+ case TT_Wood:
+ default:
+ {
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) //|| defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( 0, 0 );
+ mWheelRumble->SetPPO( 0, 0, 0 );
+ }
+#endif
+ break;
+ }
+ }
+
+// if ( vehicle->mSkidLevel > 0.0f )
+// {
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::PEELOUT, 250 );
+// }
+}
+
+//=============================================================================
+// HumanVehicleController::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::Init()
+{
+ if ( mControllerId == -1 )
+ {
+ return;
+ }
+
+ UserController* uc = NULL;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ //Set up the output points to default settings.
+#ifdef RAD_PS2
+ //TODO: Make this only set up the active wheel.
+ if ( mpWheel[ 0 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB0 );
+ }
+ else if ( mpWheel[ 1 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB1 );
+ }
+ else
+ {
+ uc = GetInputManager()->GetController( mControllerId );
+ }
+#elif defined RAD_GAMECUBE || defined(RAD_WIN32)
+ uc = GetInputManager()->GetController( mControllerId );
+#endif
+
+ if ( uc )
+ {
+ SetupRumbleFeatures( uc );
+ }
+#endif
+
+ GetEventManager()->AddListener( this, EVENT_BIG_CRASH );
+ GetEventManager()->AddListener( this, EVENT_BIG_VEHICLE_CRASH );
+ GetEventManager()->AddListener( this, EVENT_MINOR_CRASH );
+ GetEventManager()->AddListener( this, EVENT_MINOR_VEHICLE_CRASH );
+ GetEventManager()->AddListener( this, EVENT_RUMBLE_COLLISION );
+}
+
+//=============================================================================
+// HumanVehicleController::Shutdown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::Shutdown()
+{
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ //Stop the vehicle output point settings
+
+ if ( mSpring )
+ {
+ mSpring = NULL;
+ }
+
+ if ( mDamper )
+ {
+ mDamper = NULL;
+ }
+
+ if ( mConstantEffect )
+ {
+ mConstantEffect = NULL;
+ }
+
+#endif
+
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// HumanVehicleController::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::HandleEvent( EventEnum id, void* pEventData )
+{
+ Vehicle* vehicle = GetVehicle();
+ float speed = vehicle->GetSpeedKmh();
+ switch ( id )
+ {
+ case EVENT_BIG_CRASH:
+ case EVENT_BIG_VEHICLE_CRASH:
+ {
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE) || defined(RAD_WIN32)
+ if ( mHeavyWheelRumble )
+ {
+#ifdef RAD_WIN32
+ mHeavyWheelRumble->SetMagDir( static_cast<u16>(speed*20), 90 );
+ mHeavyWheelRumble->SetPPO( 20, 0, 0 );
+ if( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( static_cast<u16>(speed*20), 90 );
+ mWheelRumble->SetPPO( 20, 0, 0 );
+ }
+#else
+ mHeavyWheelRumble->SetMagDir( 180, 90 );
+ mHeavyWheelRumble->SetPPO( 20, 0, 0 );
+#endif
+ }
+#endif
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::HARD1, 250 );
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::HARD2, 250 );
+ break;
+ }
+ case EVENT_MINOR_CRASH:
+ case EVENT_MINOR_VEHICLE_CRASH:
+ {
+#ifdef RAD_WIN32
+ if ( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( 100, 90 );
+ mWheelRumble->SetPPO( 20, 0, 0 );
+ }
+#endif
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::LIGHT, 250 );
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::HARD2, 250 );
+ break;
+ }
+ case EVENT_RUMBLE_COLLISION:
+ {
+ RumbleCollision* rc = static_cast<RumbleCollision*>(pEventData);
+ if ( rc->vehicle == GetVehicle() && mControllerId != -1 && rc->vehicle->mSpeedKmh > 1.0f )
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyDynaEffect( RumbleEffect::COLLISION1, 333, rc->normalizedForce );
+ GetInputManager()->GetController( mControllerId )->ApplyDynaEffect( RumbleEffect::COLLISION2, 333, rc->normalizedForce );
+
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE) || defined(RAD_WIN32)
+ if ( mConstantEffect )
+ {
+ if ( mWheelRumble && rc->normalizedForce > 0.02f )
+ {
+ rmt::Vector thisPosition;
+ GetVehicle()->GetPosition( &thisPosition );
+
+ rmt::Vector X( 1.0f, 0.0f, 0.0f );
+ rmt::Matrix mat = GetVehicle()->GetTransform();
+ mat.IdentityTranslation();
+
+ X.Transform( mat );
+
+ rmt::Vector carToPoint;
+ carToPoint.Sub( rc->point, thisPosition );
+ carToPoint.NormalizeSafe();
+
+ float dot = carToPoint.DotProduct( X );
+ bool left = ( dot < 0.0f );
+ u16 direction = rmt::FtoL( rmt::Fabs(dot) * 90.0f );
+ if ( left )
+ {
+ direction = (90 + 180) + ( 90 - (direction) );
+ }
+
+ mConstantEffect->SetDirection( direction );
+ mConstantEffect->SetMagnitude( 200 );
+ }
+ }
+
+ if ( mWheelRumble && rc->normalizedForce > 0.008f )
+ {
+ mWheelRumble->SetMagDir( 180, 90 );
+ mWheelRumble->SetPPO( static_cast<u16>( rmt::FtoL(rmt::Clamp( rc->normalizedForce, 0.5f, 1.0f ) * 200.0f ) ),
+ 0, 0 );
+ }
+#endif
+ }
+ }
+ default:
+ break;
+ }
+}
+
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+//=============================================================================
+// HumanVehicleController::SetupRumbleFeatures
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( UserController* uc )
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::SetupRumbleFeatures( UserController* uc )
+{
+ if ( !mSpring )
+ {
+ mSpring = uc->GetSpring();
+ if ( mSpring )
+ {
+ //Set default settings for the spring
+ mSpring->SetCenterPoint( DEFAULT_SPRING_OFFSET, DEFAULT_SPRING_DEADBAND );
+ mSpring->SetSpringCoefficient( DEFAULT_SPRING_COEFF );
+ mSpring->SetSpringStrength( DEFAULT_SPRING_SAT );
+ }
+ }
+
+ if ( !mDamper )
+ {
+ mDamper = uc->GetDamper();
+ if ( mDamper )
+ {
+ //Set default settings for the damper
+ mDamper->SetCenterPoint( DEFAULT_SPRING_OFFSET, DEFAULT_SPRING_DEADBAND );
+ mDamper->SetDamperCoefficient( DEFAULT_SPRING_COEFF );
+ mDamper->SetDamperStrength( DEFAULT_SPRING_SAT );
+ }
+ }
+
+ if ( !mConstantEffect )
+ {
+ mConstantEffect = uc->GetConstantEffect();
+ if ( mConstantEffect )
+ {
+ //Default
+ mConstantEffect->SetMagnitude( DEFAULT_CONSTANT_MAGNITUDE );
+ mConstantEffect->SetDirection( DEFAULT_CONSTANT_DIRECTION );
+ }
+ }
+
+ if ( !mWheelRumble )
+ {
+ mWheelRumble = uc->GetWheelRumble();
+ if ( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( 0, 0 );
+ mWheelRumble->SetPPO( 0, 0, 0 );
+ }
+ }
+
+ if ( !mHeavyWheelRumble )
+ {
+ mHeavyWheelRumble = uc->GetHeavyWheelRumble();
+ if ( mHeavyWheelRumble )
+ {
+ mHeavyWheelRumble->SetMagDir( 0, 0 );
+ mHeavyWheelRumble->SetPPO( 0, 0, 0 );
+ }
+ }
+}
+#endif
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h
new file mode 100644
index 0000000..8e7feb8
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h
@@ -0,0 +1,78 @@
+#ifndef HUMANVEHICLECONTROLLER_H_
+#define HUMANVEHICLECONTROLLER_H_
+
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+
+#include <input/controller.h>
+#include <events/eventlistener.h>
+
+class VehicleMappable;
+class BaseDamper;
+class SteeringSpring;
+class ConstantEffect;
+class WheelRumble;
+class UserController;
+
+class HumanVehicleController
+:
+public VehicleController,
+public EventListener
+{
+public:
+ HumanVehicleController( void );
+ virtual ~HumanVehicleController( void );
+
+ void ReleaseVehicleMappable( void );
+
+ void Create( Vehicle* pVehicle, VehicleMappable* pMappable, int controllerId );
+#ifdef RAD_PS2
+ void SetWheel( VehicleMappable* pMappable, unsigned int wheelNum );
+#endif
+ virtual float GetGas( void ) const;
+ virtual float GetThrottle( void ) const;
+ virtual float GetBrake( void ) const;
+ virtual float GetSteering( bool& isWheel ) const;
+
+ // for dpad
+ virtual float GetSteerLeft( void ) const;
+ virtual float GetSteerRight( void ) const;
+
+ virtual float GetHandBrake( void ) const;
+ virtual float GetReverse( void ) const;
+ virtual float GetHorn( void ) const;
+
+ void Reset( void );
+ void SetDisableReset( bool tf ) { mDisableReset = tf; }
+
+ VehicleMappable* GetMappable( void ) const;
+
+ virtual void Update( float timeins );
+ virtual void Init();
+ virtual void Shutdown();
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+private:
+
+ VehicleMappable* mpMappable;
+
+#ifdef RAD_PS2
+ VehicleMappable* mpWheel[ Input::MaxUSB ];
+#endif
+
+ int mControllerId;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ SteeringSpring* mSpring;
+ BaseDamper* mDamper;
+ ConstantEffect* mConstantEffect;
+ WheelRumble* mWheelRumble;
+ WheelRumble* mHeavyWheelRumble;
+
+ void SetupRumbleFeatures( UserController* uc );
+#endif
+ bool mDisableReset;
+};
+
+#endif //HUMANVEHICLECONTROLLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp
new file mode 100644
index 0000000..87942e2
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp
@@ -0,0 +1,16 @@
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+
+VehicleController::VehicleController( void )
+:
+mpVehicle( 0 )
+{
+}
+
+VehicleController::~VehicleController( void )
+{
+}
+
+Vehicle* VehicleController::GetVehicle( void ) const
+{
+ return mpVehicle;
+}
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h
new file mode 100644
index 0000000..9a7ce5f
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h
@@ -0,0 +1,49 @@
+#ifndef VEHICLECONTROLER_H_
+#define VEHICLECONTROLER_H_
+
+#include <p3d/refcounted.hpp>
+#include <input/button.h>
+
+class Vehicle;
+
+/*
+// Skeleton class for example.
+//
+struct IButton
+{
+ virtual float GetValue( void ) const = 0;
+ virtual float GetHeldTime( void ) const = 0;
+};
+*/
+class VehicleController
+:
+public tRefCounted
+{
+public:
+ VehicleController( void );
+ virtual ~VehicleController( void );
+
+ virtual void Update( float timeins ) {};
+ virtual void Init() {};
+ virtual void Shutdown() {};
+
+ void SetVehicle( Vehicle* pVehicle ) { mpVehicle = pVehicle; }
+
+ virtual float GetGas( void ) const = 0;
+ virtual float GetThrottle( void ) const = 0;
+
+ virtual float GetBrake( void ) const = 0;
+ virtual float GetSteering( bool& isWheel ) const = 0;
+ virtual float GetSteerLeft( void ) const = 0;
+ virtual float GetSteerRight( void ) const = 0;
+
+ virtual float GetHandBrake( void ) const = 0;
+ virtual float GetReverse( void ) const = 0;
+ virtual float GetHorn( void ) const = 0;
+
+ Vehicle* GetVehicle( void ) const;
+private:
+ Vehicle* mpVehicle;
+};
+
+#endif // VEHICLECONTROLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp
new file mode 100644
index 0000000..94c7d87
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp
@@ -0,0 +1,263 @@
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+
+#include <worldsim/worldphysicsmanager.h>
+
+// Temp.
+//
+#include <worldsim/avatarmanager.h>
+
+#ifdef RAD_WIN32
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+#endif
+
+// Constructor.
+//
+VehicleMappable::VehicleMappable( void )
+:
+Mappable(Input::ACTIVE_GAMEPLAY),
+mpHumanVehicleController( 0 ),
+mIsWheel( false ),
+mIsWheelA( false )
+{
+}
+
+// Destructor
+//
+VehicleMappable::~VehicleMappable( void )
+{
+ ReleaseController();
+}
+
+void VehicleMappable::ReleaseController( void )
+{
+ if ( mpHumanVehicleController )
+ {
+ mpHumanVehicleController->Release();
+ mpHumanVehicleController = 0;
+ }
+}
+
+
+
+// This method is called when ever a button state changes.
+//void
+void VehicleMappable::OnButton( int controllerId, int id, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Pressed" to "Released".
+//
+void VehicleMappable::OnButtonUp( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Released" to "Pressed".
+//
+void VehicleMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ switch ( buttonId )
+ {
+ case Reset:
+ {
+#ifdef RAD_GAMECUBE
+ if ( pButton->GetValue() > 0.5f )
+ {
+ GetController( )->Reset();
+ }
+#else
+ GetController( )->Reset();
+#endif
+ break;
+ }
+ case Horn:
+ {
+ GetWorldPhysicsManager()->ToggleTimerState();
+ }
+ default:
+ {
+ // Do nothing. That's cool.
+ //
+ break;
+ }
+ }
+}
+
+// This is how we create our controller device mappings to logical game mappings.
+// The mappings set up in this method are platform specific.
+//
+// The basic format of the calls is to "Map" a input, to a enumerated output id.
+// The output of the specified input will be contained in the Button[] array.
+// This id will also be sent as a the second parameter in the OnButton... messages.
+//
+void VehicleMappable::LoadControllerMappings( unsigned int controllerId )
+{
+ // The names used in parameter 1 of the Map method are
+ // Platform Specific.
+ //
+
+ // Configuration 1.
+ //
+
+ // MS7
+ // #ifdef this for different platforms
+ #ifdef RAD_XBOX
+
+ ClearMap(0);
+ Map( "LeftStickX", Steer, 0, controllerId );
+ Map( "DPadLeft", SteerLeft, 0, controllerId );
+ Map( "DPadRight", SteerRight, 0, controllerId );
+
+ // The misnamed "Throttle" controls gas (up) and brake (down).
+ //
+ //Map( "RightStickY", Throttle, 0, controllerId ); Not on XBOX, please
+
+ Map( "RightTrigger", Gas, 0, controllerId );
+ Map( "A", Gas, 0, controllerId );
+
+ Map( "B", Brake, 0, controllerId );
+ Map( "LeftTrigger", Brake, 0, controllerId );
+
+ Map( "X", HandBrake, 0, controllerId );
+
+ Map( "White", Horn, 0, controllerId );
+ Map( "LeftThumb", Horn, 0, controllerId );
+
+ Map( "Back", Reset, 0, controllerId );
+ #endif
+
+
+ #ifdef RAD_PS2
+
+ ClearMap(0);
+
+ if ( controllerId >= Input::MaxControllers - Input::MaxUSB )
+ {
+ mIsWheel = true;
+
+ //This is a wheel!
+ //And for the wheels!
+ Map( "Wheel", Steer, 0, controllerId );
+ Map( "Gas", Gas, 0, controllerId );
+ Map( "Brake", Brake, 0, controllerId );
+
+ if ( !Map( "R2", Gas, 0, controllerId ) )
+ {
+ mIsWheelA = true;
+
+ //This is the GT wheel.
+ Map( "LGR1", Gas, 0, controllerId );
+ Map( "LGA", Brake, 0, controllerId );
+ Map( "LGL1", HandBrake, 0, controllerId );
+ Map( "LGX", Horn, 0, controllerId );
+ Map( "LGB", Reset, 0, controllerId );
+ }
+ else
+ {
+ //This is the DPad Wheel
+ Map( "X", Gas, 0, controllerId );
+ Map( "R1", Brake, 0, controllerId );
+ Map( "Circle", Brake, 0, controllerId );
+ Map( "Square", HandBrake, 0, controllerId );
+ Map( "L1", HandBrake, 0, controllerId );
+ Map( "DPadUp", Horn, 0, controllerId );
+ Map( "DPadDown", Horn, 0, controllerId );
+ Map( "DPadLeft", Horn, 0, controllerId );
+ Map( "DPadRight", Horn, 0, controllerId );
+ Map( "Select", Reset, 0, controllerId );
+ }
+ }
+ else
+ {
+ //This is a DUALSHOCK!
+ Map( "LeftStickX", Steer, 0, controllerId );
+ Map( "DPadLeft", SteerLeft, 0, controllerId );
+ Map( "DPadRight", SteerRight, 0, controllerId );
+
+ // The misnamed "Throttle" controls gas (up) and brake (down).
+ //
+ Map( "RightStickY", Throttle, 0, controllerId );
+
+ Map( "X", Gas, 0, controllerId );
+
+ Map( "Circle", Brake, 0, controllerId );
+
+ Map( "R1", HandBrake, 0, controllerId );
+ Map( "Square", HandBrake, 0, controllerId );
+
+ Map( "L3", Horn, 0, controllerId );
+
+ Map( "Select", Reset, 0, controllerId );
+ }
+
+ #endif
+
+ #ifdef RAD_GAMECUBE
+
+ ClearMap(0);
+ Map( "LeftStickX", Steer, 0, controllerId );
+// Map( "DPadLeft", SteerLeft, 0, controllerId );
+// Map( "DPadRight", SteerRight, 0, controllerId );
+
+ Map( "AnalogTriggerR", Gas, 0, controllerId );
+ Map( "A", Gas, 0, controllerId );
+
+ Map( "X", Brake, 0, controllerId );
+ Map( "AnalogTriggerL", Brake, 0, controllerId );
+
+ Map( "B", HandBrake, 0, controllerId );
+
+ Map( "TriggerZ", Horn, 0, controllerId );
+ Map( "DPadUp", Reset, 0, controllerId );
+ Map( "DPadDown", Reset, 0, controllerId );
+ Map( "DPadLeft", Reset, 0, controllerId );
+ Map( "DPadRight", Reset, 0, controllerId );
+
+ if ( Map( "Wheel", Steer, 0, controllerId ) )
+ {
+ mIsWheel = true;
+ //This is a wheel!
+ Map( "Gas", Gas, 0, controllerId );
+ Map( "Brake", Brake, 0, controllerId );
+ }
+ else
+ {
+//
+ }
+
+ #endif
+
+ #ifdef RAD_WIN32
+
+ ClearMap(0);
+ if ( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT &&
+ controllerId == 3 )
+ {
+ Map( "P1_KBD_Left", SteerLeft, 0, controllerId );
+ Map( "P1_KBD_Right", SteerRight, 0, controllerId );
+ Map( "P1_KBD_Gas", Gas, 0, controllerId );
+ Map( "P1_KBD_Brake", Brake, 0, controllerId );
+ Map( "P1_KBD_EBrake", HandBrake, 0, controllerId );
+ Map( "P1_KBD_Nitro", Horn, 0, controllerId );
+ }
+
+ Map( "SteerLeft", SteerLeft, 0, controllerId );
+ Map( "SteerRight", SteerRight, 0, controllerId );
+
+ Map( "Accelerate", Gas, 0, controllerId );
+ Map( "Reverse", Brake, 0, controllerId );
+
+ Map( "HandBrake", HandBrake, 0, controllerId );
+ Map( "Horn", Horn, 0, controllerId );
+
+ Map( "ResetCar", Reset, 0, controllerId );
+
+ #endif
+
+}
+
+void VehicleMappable::SetController( HumanVehicleController* pHumanVehicleController )
+{
+ tRefCounted::Assign( mpHumanVehicleController, pHumanVehicleController );
+}
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h
new file mode 100644
index 0000000..e63d6e0
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h
@@ -0,0 +1,60 @@
+#ifndef VEHICLEMAPPABLE_H_
+#define VEHICLEMAPPABLE_H_
+
+#include <input/mappable.h>
+
+class HumanVehicleController;
+
+class VehicleMappable
+:
+public Mappable
+{
+public:
+ VehicleMappable( void );
+ ~VehicleMappable( void );
+
+ void ReleaseController( void );
+
+ void OnButton( int controllerId, int id, const IButton* pButton );
+ void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+ void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+ void LoadControllerMappings( unsigned int controllerId );
+
+ void SetController( HumanVehicleController* pHumanVehicleController );
+
+ bool IsWheel() const { return mIsWheel; };
+ bool IsWheelA() const { return mIsWheelA; };
+
+ enum
+ {
+ Steer = 0,
+ SteerLeft,
+ SteerRight,
+ Throttle,
+ Gas,
+ Brake,
+ Horn,
+ Reset,
+ Pause,
+ Reverse,
+ HandBrake,
+ Burnout,
+ GetOutofCar,
+ LookBack,
+ Spring,
+ Damper,
+ Rumble,
+ RightStickX,
+ RightStickY
+ };
+protected:
+ HumanVehicleController* GetController( void ) const
+ { return mpHumanVehicleController; }
+private:
+ HumanVehicleController* mpHumanVehicleController;
+
+ bool mIsWheel;
+ bool mIsWheelA;
+};
+
+#endif //VEHICLEMAPPABLE_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehicleeventlistener.cpp b/game/code/worldsim/redbrick/vehicleeventlistener.cpp
new file mode 100644
index 0000000..34838aa
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicleeventlistener.cpp
@@ -0,0 +1,304 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleeventlistener.cpp
+//
+// Description:
+//
+// History: created, Nov 29, 2002, gmayer
+//
+//=============================================================================
+
+
+#include <worldsim/redbrick/vehicleeventlistener.h>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <meta/triggervolumetracker.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <gameflow/gameflow.h>
+
+#include <mission/objectives/missionobjective.h>
+#include <mission/objectives/destroyobjective.h>
+
+#include <mission/gameplaymanager.h>
+#include <supersprint/supersprintmanager.h>
+
+//-----------------------------------------------------------------------------
+VehicleEventListener::VehicleEventListener()
+{
+ mVehicleOwner = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+VehicleEventListener::VehicleEventListener(Vehicle* owner)
+{
+ mVehicleOwner = owner;
+ GetEventManager()->AddListener( this, EVENT_DEATH_VOLUME_SCREEN_BLANK );
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::WHACKY_GRAVITY));
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH) );
+}
+
+
+//-----------------------------------------------------------------------------
+VehicleEventListener::~VehicleEventListener()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+
+//-----------------------------------------------------------------------------
+void VehicleEventListener::HandleEvent( EventEnum id, void* pEventData )
+{
+ ////////////////////////////////////////////////////////////////
+ // WHACKY GRAVITY (FOR JUMP VOLUMES)
+
+ if( id == EVENT_LOCATOR + LocatorEvent::WHACKY_GRAVITY )
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+
+ Vehicle* testVehicle = NULL;
+ if ( pLocator->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //This is a player vehicle
+ testVehicle = GetAvatarManager()->GetAvatarForPlayer( pLocator->GetPlayerID() )->GetVehicle();
+ }
+ else
+ {
+ testVehicle = GetTriggerVolumeTracker()->GetAI( pLocator->GetPlayerID() );
+ }
+
+ if ( testVehicle != mVehicleOwner )
+ {
+ //If this isn't for me, ignore it.
+ return;
+ }
+
+ // if in supersprint, ignore jump boost for player
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT &&
+ mVehicleOwner->mVehicleType == VT_USER )
+ {
+ return;
+ }
+
+ if ( pLocator->GetPlayerEntered() )
+ {
+ int stophere = 1;
+ // only apply this if we are moderately aligned to it
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+ rmt::Vector facing = mat.Row(2);
+
+ if(mVehicleOwner->mVehicleFacing.DotProduct(facing) > 0.0f)
+ {
+ mVehicleOwner->EnteringJumpBoostVolume();
+ }
+ }
+ else
+ {
+ // should be leaving
+ int stophere = 1;
+ mVehicleOwner->ExitingJumpBoostVolume();
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // DEATH VOLUMES
+
+ else if( id == EVENT_LOCATOR + LocatorEvent::DEATH )
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+ if( pLocator == NULL )
+ {
+ return;
+ }
+
+ Vehicle* testVehicle = NULL;
+ if ( pLocator->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //This is a player vehicle
+ testVehicle = GetAvatarManager()->GetAvatarForPlayer( pLocator->GetPlayerID() )->GetVehicle();
+ }
+ else
+ {
+ testVehicle = GetTriggerVolumeTracker()->GetAI( pLocator->GetPlayerID() );
+ }
+
+ if ( testVehicle != mVehicleOwner )
+ {
+ //If this isn't for me, ignore it.
+ return;
+ }
+
+ // Ok, it is for me...
+
+ GameplayManager::GameTypeEnum gameType = GetGameplayManager()->GetGameType();
+
+ //
+ // In supersprint, we want to handle DEATH event for AI & player vehicles,
+ // but if in other contexts, we handle DEATH event only for AI (player vehicles
+ // will need to listen to the fade to DEATH_VOLUME_SCREEN_BLANK event because
+ // we don't want death to be instantaneous)
+ //
+ VehicleType type = mVehicleOwner->mVehicleType;
+ if( ( gameType != GameplayManager::GT_SUPERSPRINT ) &&
+ ( type != VT_AI ) )
+ {
+ // there's some stuff that we want to do right away
+ if ( pLocator->GetPlayerEntered() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_DEATH_VOLUME_SOUND, mVehicleOwner );
+ }
+ return;
+ }
+
+ /*
+ //
+ // If this is a destroy objective, we want to kill the target AI vehicle
+ // (thus triggering the destroyed condition, completing the mission...
+ //
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT )
+ {
+ MissionStage* ms = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+ if( ms )
+ {
+ MissionObjective* mobj = ms->GetObjective();
+ if( mobj->GetObjectiveType() == MissionObjective::OBJ_DESTROY )
+ {
+ DestroyObjective* dobj = (DestroyObjective*) mobj;
+ if( dobj->GetTargetVehicle() == mVehicleOwner )
+ {
+ mVehicleOwner->mHitPoints = 0.0f;
+ mVehicleOwner->mVehicleDestroyed = true;
+ return;
+ }
+ }
+ }
+ }
+ */
+
+ rAssert( mVehicleOwner->mVehicleCentralIndex != -1 );
+
+ // Only do something upon entry
+ if ( pLocator->GetPlayerEntered() )
+ {
+ // We're here if we're a car that the player is driving
+ // or if we're an AI car...
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+
+ //Let's turn the car around in specific cases.
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ if ( GetSSM()->IsTrackReversed() )
+ {
+ rmt::Vector heading;
+ mat.GetHeading( heading );
+ heading *= -1.0f;
+ mat.FillHeadingXZ( heading ); //Turn, turn around
+ }
+
+ //
+ // Dusit said I should do this here
+ //
+ GetEventManager()->TriggerEvent( EVENT_DEATH_VOLUME_SOUND, mVehicleOwner );
+ }
+
+ // John McLean here,
+ // locator is snapped to ground, so we need to spawn at some rest
+ // height above ground. Yippe ka yay!
+ rmt::Vector loc;
+ pLocator->GetLocation(&loc);
+ float yAug = mVehicleOwner->GetRestHeightAboveGround();
+ loc.y += yAug;
+ mat.Row(3).y = loc.y;
+
+ mVehicleOwner->SetTransform(mat);
+
+ // do reset flags, do reset AI info (if an AI car)
+ // don't reset pos (already done manually be setting transform),
+ // don't reset damage states
+ mVehicleOwner->ResetFlagsOnly(false);
+ }
+ }
+ else if( id == EVENT_DEATH_VOLUME_SCREEN_BLANK )
+ {
+ // Only handle the player vehicle triggering this. This includes:
+ // - player default vehicle (could be under AI control in Demo) in Demo or Normal modes
+ // - any traffic car or parked car or husk that the player is driving
+
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+ if( pLocator == NULL )
+ {
+ return;
+ }
+
+ rAssert( pLocator->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) );
+
+ //This is a player vehicle
+ Vehicle* testVehicle = GetAvatarManager()->GetAvatarForPlayer(
+ pLocator->GetPlayerID() )->GetVehicle();
+
+ if( testVehicle != mVehicleOwner )
+ {
+ //If this isn't for me, ignore it.
+ return;
+ }
+
+ // Ok, it is for me...
+ rAssert( mVehicleOwner->mVehicleCentralIndex != -1 );
+ rAssert( GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT );
+
+ // Only do something upon entry
+ if ( pLocator->GetPlayerEntered() )
+ {
+ // We're here if we're a car that the player is driving
+ // or if we're an AI-controller player car (in Demo mode)
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+
+ // John McLean here,
+ // locator is snapped to ground, so we need to spawn at some rest
+ // height above ground. Yippe ka yay!
+ rmt::Vector loc;
+ pLocator->GetLocation(&loc);
+ float yAug = mVehicleOwner->GetRestHeightAboveGround();
+ loc.y += yAug;
+ mat.Row(3).y = loc.y;
+
+ //
+ // what if there is already a car at the location we want to put htis car
+ //
+ const rmt::Vector& position = mat.Row( 3 );
+ rmt::Sphere s;
+ s.centre = position;
+ s.radius = 4.0;
+ TrafficManager::GetInstance()->ClearTrafficInSphere( s );
+ GetGameplayManager()->DumpCurrentCarIfInSphere( s );
+ GetPCM().RemoveFreeCarIfClose( position );
+
+ mVehicleOwner->SetTransform(mat);
+
+ // do reset flags, do reset AI info (if an AI car)
+ // don't reset pos (already done manually be setting transform),
+ // don't reset damage states
+ mVehicleOwner->ResetFlagsOnly(false);
+ }
+ }
+}
+
diff --git a/game/code/worldsim/redbrick/vehicleeventlistener.h b/game/code/worldsim/redbrick/vehicleeventlistener.h
new file mode 100644
index 0000000..30f0668
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicleeventlistener.h
@@ -0,0 +1,45 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleeventlistener.h
+//
+// Description:
+//
+// History: created, Nov 29, 2002, gmayer
+//
+//=============================================================================
+
+
+
+#ifndef VEHICLEEVENTLISTENER_H
+#define VEHICLEEVENTLISTENER_H
+
+#include <events/eventlistener.h>
+
+class Vehicle;
+
+class VehicleEventListener : public EventListener
+{
+public:
+
+ VehicleEventListener();
+ VehicleEventListener(Vehicle* owner);
+
+ virtual ~VehicleEventListener();
+
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+private:
+
+ // Declared but not defined to prevent copying and assignment.
+ VehicleEventListener( const VehicleEventListener& );
+ VehicleEventListener& operator=( const VehicleEventListener& );
+
+ Vehicle* mVehicleOwner;
+};
+
+
+
+
+#endif // VEHICLEEVENTLISTENER_H
diff --git a/game/code/worldsim/redbrick/vehicleinit.cpp b/game/code/worldsim/redbrick/vehicleinit.cpp
new file mode 100644
index 0000000..79ee9fe
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicleinit.cpp
@@ -0,0 +1,2309 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleinit.cpp
+//
+// Description: init stuff for the vehicle, that is only called once!
+//
+// History: Aug 2, 2002 + Created -- gmayer
+//
+//=============================================================================
+
+
+//========================================
+// System Includes
+//========================================
+
+#include <simcommon/skeletoninfo.hpp>
+#include <simcommon/simstate.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <simphysics/physicsjoint.hpp>
+#include <simphysics/physicsobject.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <simcollision/collisiondisplay.hpp>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <p3d/anim/drawablepose.hpp>
+#include <p3d/shadow.hpp>
+
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/worldobject.h>
+
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/rootmatrixdriver.h>
+#include <worldsim/redbrick/suspensionjointdriver.h>
+#include <worldsim/redbrick/vehicleeventlistener.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
+
+#include <worldsim/redbrick/physicslocomotion.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+
+#include <worldsim/character/charactermanager.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 <worldsim/character/character.h>
+
+#include <gameflow/gameflow.h>
+
+using namespace sim;
+
+// The force required to knock a collectible off
+const float MIN_FORCE_DETACH_COLLECTIBLES = 30000.0f;
+
+
+//=============================================================================
+// Vehicle::Vehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle::Vehicle() :
+ mGeometryVehicle( NULL ),
+ mName( NULL ),
+ mVehicleID( VehicleEnum::INVALID ),
+ mTerrainType( TT_Road),
+ mInterior( false ),
+ mpEventLocator( NULL ),
+ mCharacterSheetCarIndex( -1 ),
+ m_IsSimpleShadow( true )
+{
+ mTranslucent = true;
+ mDrawVehicle = true;
+
+ mTransform.Identity();
+
+ mVehicleCentralIndex = -1;
+
+ // wtf?
+ mInitialPosition.Set(0.0f, 8.0f, 20.0f);
+ mResetFacingRadians = 0.0f;
+
+ mVehicleFacing.Set(0.0f, 0.0f, 1.0f);
+ mVehicleUp.Set(0.0f, 1.0f, 0.0f);
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ GameMemoryAllocator allocator = GetGameplayManager()->GetCurrentMissionHeap();
+ HeapMgr()->PushHeap( allocator );
+#endif
+ mGeometryVehicle = new GeometryVehicle;
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( allocator );
+#endif
+
+
+ //mPhysicsVehicle = new PhysicsVehicle;
+ //mPhysicsVehicle = 0; test this motherfucker
+
+ // should this be here or in Init?
+ // shouldn't really be in this class at all
+ // TODO - take this shit out
+ //mVehicleRender = new VehicleRender(this);
+
+
+ mSimStateArticulated = 0;
+ mPoseEngine = 0;
+ mRootMatrixDriver = 0;
+
+ //mPoseEngineOutOfCar = 0;
+ //mRootMatrixDriverOutOfCar = 0;
+
+
+ mSimStateArticulatedOutOfCar = 0;
+ mSimStateArticulatedInCar = 0;
+
+ mGas = 0.0f;
+ mLastGas = 0.0f;
+ mDeltaGas = 0.0f;
+ mBrake = 0.0f;
+ mWheelTurnAngle = 0.0f;
+ mReverse = 0.0f;
+ mEBrake = 0.0f;
+
+ mBrakeTimer = 0.0f;
+ mBrakeActingAsReverse = false;
+
+ mGasBrakeDisabled = false;
+
+ mUnmodifiedInputWheelTurnAngle = 0.0f;
+
+ mVelocityCM.Set(0.0f, 0.0f, 0.0f);
+ mSpeed = 0.0f;
+ mSpeedKmh = 0.0f;
+ mLastSpeedKmh = 0.0f;
+ mAccelMss = 0.0f;
+ mPercentOfTopSpeed = 0.0f;
+
+ mRollingFrictionForce = 2.0f; // need to tune? I don't think so
+ //mRollingFrictionForce = 1.5f; // need to tune? I don't think so
+
+ mRPM = 0.0f;
+ mBaseRPM = 500.0f;
+ mMaxRpm = 6500.0f;
+
+ mRPMUpRate = 10.0f;
+ mRPMDownRate = 50.0f;
+ //mShiftPointHigh = 3500.0f;
+ //mShiftPointLow = 1000.0f;
+
+ //mShiftPointHigh = 4000.0f;
+ mShiftPointHigh = 4500.0f;
+ //mShiftPointLow = 1500.0f;
+ mShiftPointLow = 2000.0f;
+
+
+ mGear = 0; // neutral?
+
+ mNumGears = -1; // unset
+ mGearRatios = 0;
+ mFinalDriveRatio = -1.0f; // unset
+
+ mSkidLevel = 0.0f;
+ mBurnoutLevel = 0.0f;
+ mSlipGasModifier = 1.0f;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ mInertialJointDrivers[i] = 0;
+ mPhysicsJointMatrixModifiers[i] = 0;
+ }
+ mJointIndexToInertialJointDriverMapping = 0;
+
+ mCollisionAreaIndex = -1;
+
+ mDoorDJoint = -1;
+ mDoorPJoint = -1;
+ mHoodJoint = -1;
+ mTrunkJoint = -1;
+
+ mDoorDDamageLevel = 0;
+ mDoorPDamageLevel = 0;
+ mHoodDamageLevel = 0;
+ mTrunkDamageLevel = 0;
+
+ mOkToDrawSelf = true;
+ mSteeringWheelsOutOfContact = false;
+ mAirBorn = false;
+ mWasAirborn = false;
+ mWasAirbornTimer = 0.0f;
+ mWeebleOn = false;
+ mInstabilityOffsetOn = false;
+
+ mSpeedBurstTimer = 0.0f;
+ mBuildingUpSpeedBurst = false;
+ mDoSpeedBurst = false;
+ mFOVToRestore = 1.57f;
+ mSpeedBurstTimerHalf = 0.0f;
+
+ //mDamageType = 0; // 0 is unset - normally 1, 2, or 3
+ mDamageType = VDT_UNSET;
+
+ mVehicleDestroyed = false;
+ mVehicleCanSustainDamage = true;
+ //mVehicleCanSustainDamage = false;
+
+ mIsADestroyObjective = false;
+
+ mHitPoints = 2.0f;
+
+ mDamageOutResetTimer = 0.0f;
+
+ mSmokeOffset.Set(0.0f, -0.5f, 1.5f);
+
+// mHitJoint = 0;
+
+ mTrafficVehicle = NULL;
+
+ mCollisionLateralResistanceDropFactor = 1.0f;
+
+ mUserDrivingCar = false;
+
+ mDesiredDoorPosition[0] = mDesiredDoorPosition[1] = 0.0f;
+ mDesiredDoorAction[0] = mDesiredDoorAction[1] = DOORACTION_NONE;
+
+ mpTriggerVolume = NULL;
+
+ mpDriver = NULL;
+
+ mDoingRockford = false;
+ mDoingWheelie = false;
+
+
+ strcpy(mDriverName,"phantom");
+// mDriverName[0] = 0;
+ mPhantomDriver = false;
+
+
+ // vehicle event stuff
+ mVehicleEventListener = 0;
+
+ mDoingJumpBoost = false;
+
+ mNoSkid = false; // only true for frink's hovering vehicles (also the ghost ship)
+ mNoFrontSkid = false; // only true for the rocket car
+
+ mBrakeLightsOn = false;
+ mReverseLightsOn = false;
+ mDontShowBrakeLights = false;
+
+ mCMOffsetSetToOriginal = false;
+
+ mOutOfControl = false;
+ mCollidedWithVehicle = false;
+
+ mNormalizedMagnitudeOfVehicleHit = 0.0f;
+ mWasHitByVehicle = false;
+ mWasHitByVehicleType = VT_LAST;
+
+ mDamperShouldNotPullDown[0] = false;
+ mDamperShouldNotPullDown[1] = false;
+ mDamperShouldNotPullDown[2] = false;
+ mDamperShouldNotPullDown[3] = false;
+
+
+ mNumTurbos = 3; // default to 3 for now
+ mSecondsTillNextTurbo = 0.0f;
+
+ mUsingInCarPhysics = true;
+
+ //mWaitingToSwitchToOutOfCar = false;
+ //mOutOfCarSwitchTimer = 0.0f; // brutal fucking hack
+
+ mNoDamageTimer = 0.0f;
+
+ mAlreadyPlayedExplosion = false;
+
+ mEBrakeTimer = 0.0f;
+
+ mWheelTurnAngleInputValue = 0.0f;
+
+ mDrawWireFrame = false;
+ mLosingTractionDueToAccel = false; // for plum - just debug output
+
+ mHijackedByUser = false;
+
+
+
+ mDesignerParams.mDpGamblingOdds= 1.0f; //Chuck: set the Gambling Odds to 1.0 or 1:1 by default.
+ mbPlayerCar = false;
+
+ mOurRestSeatingPosition.Set(0.0f, 0.0f, 0.0f);
+ mNPCRestSeatingPosition.Set(0.0f, 0.0f, 0.0f);
+ mYAccelForSeatingOffset = 0.0f;
+
+ mVelocityCMLag.Set(0.0f, 0.0f, 0.0f);
+ mPositionCMLag.Set(0.0f, 0.0f, 0.0f);
+
+ // value... fresh from my ass:
+ //mBounceLimit = 0.5f;
+ //mBounceLimit = 0.25f;
+ mBounceLimit = 0.11f;
+
+
+ //mMaxBounceDisplacementPerSecond = 1.0f;
+ mMaxBounceDisplacementPerSecond = 2.0f;
+
+ mBounceAccelThreshold = 1.0f; // below this value just try and move back to rest
+ //mBounceAccelThreshold = 2.0f; // below this value just try and move back to rest
+
+
+ mForceToDetachCollectible = MIN_FORCE_DETACH_COLLECTIBLES;
+
+ mHasDoors = true;
+ mVisibleCharacters = true;
+ mIrisTransition = false;
+ mAllowSlide = true;
+ mHighRoof = false;
+ mCharacterScale = 1.0f;
+
+
+ // Lets set the default to inflict half damage on this vehicle when they explode
+ s_DamageFromExplosion = 0.5f;
+ // And no damage on player vehicles
+ s_DamageFromExplosionPlayer = 0;
+
+ mStuckOnSideTimer = 0.0f;
+ mAlreadyCalledAutoResetOnSpot = false;
+
+
+ mOriginalCMOffset.x = 0.0f;
+ mOriginalCMOffset.y = 0.0f;
+ mOriginalCMOffset.z = 0.0f;
+
+ mBottomOutSpeedMaintenance = 0.0f;
+
+ mTriggerActive = false;
+
+ mAtRestAsFarAsTriggersAreConcerned = true;
+
+ mLocoSwitchedToPhysicsThisFrame = false;
+
+ mCreatedByParkedCarManager = false;
+}
+
+
+//=============================================================================
+// Vehicle::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name, int num, SimEnvironment* se, VehicleLocomotionType loco)
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::Init( const char* name, SimEnvironment* se, VehicleLocomotionType loco, VehicleType vt, bool startoutofcar)
+{
+ if(mbPlayerCar ==true)
+ {
+ mCharacterSheetCarIndex = GetCharacterSheetManager()->GetCarIndex( name );
+ }
+ else
+ {
+ mCharacterSheetCarIndex = -1;
+ }
+
+ mDrawVehicle = true;
+
+ mVehicleType = vt;
+
+ rAssert( mName == NULL );
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mName = new(gma)char[strlen(name)+1];
+ strcpy( mName, name );
+
+ CollisionEntityDSG::SetName(mName);
+
+ mLoco = loco;
+
+ AssignEnumBasedOnName();
+
+ //++++++++++++++++++++
+ // the designer params
+ //++++++++++++++++++++
+
+
+ //mDesignerParams.mDpGasScale = 8.0f; // proportional to mass
+ //mDesignerParams.mDpGasScale = 6.0f; // proportional to mass
+ //mDesignerParams.mDpGasScale = 3.0f; // proportional to mass
+ mDesignerParams.mDpGasScale = 4.0f; // proportional to mass
+
+ mDesignerParams.mDpSlipGasScale = 4.0f;
+
+ mDesignerParams.mDpHighSpeedGasScale = 2.0f;
+ //mDesignerParams.mDpGasScaleSpeedThreshold = 0.5f;
+ mDesignerParams.mDpGasScaleSpeedThreshold = 1.0f; // make this the default so it is not used for the time being
+
+
+ mDesignerParams.mDpBrakeScale = 3.0f; // proportional to mass
+
+ //mDesignerParams.mDpTopSpeedKmh = 300.0f;
+ //mDesignerParams.mDpTopSpeedKmh = 250.0f;
+ mDesignerParams.mDpTopSpeedKmh = 220.0f;
+
+ mDesignerParams.mDpMass = 1000.0f; // think of it as kg
+
+ mDesignerParams.mDpMaxWheelTurnAngle = 35.0f; // in degrees
+ //mDesignerParams.mDpMaxWheelTurnAngle = 30.0f; // in degrees
+ //mDesignerParams.mDpMaxWheelTurnAngle = 24.0f; // in degrees
+ //mDesignerParams.mDpHighSpeedSteeringDrop = 0.25f; // 0.0 to 1.0
+ mDesignerParams.mDpHighSpeedSteeringDrop = 0.0f; // 0.0 to 1.0
+
+ // for wheel
+ //mDesignerParams.mDpTireLateralStaticGrip = 2.5f;
+ //mDesignerParams.mDpTireLateralStaticGrip = 5.0f;
+ mDesignerParams.mDpTireLateralStaticGrip = 4.0f;
+
+
+ mDesignerParams.mDpTireLateralResistanceNormal = 110.0f;
+ mDesignerParams.mDpTireLateralResistanceSlip = 35.0f;
+
+ mDesignerParams.mDpEBrakeEffect = 0.5f;
+
+ //mDesignerParams.mDpTireLateralResistanceSlipNoEBrake = 20.0f; // this one's for more out of control driving
+ mDesignerParams.mDpTireLateralResistanceSlipNoEBrake = 50.0f; // this one's for more out of control driving
+ mDesignerParams.mDpSlipEffectNoEBrake = 0.1f;
+
+ mDesignerParams.mDpCMOffset.x = 0.0f;
+ mDesignerParams.mDpCMOffset.y = 0.0f;
+ mDesignerParams.mDpCMOffset.z = 0.0f;
+
+ // don't think they need to touch these for now
+ mDesignerParams.mDpSuspensionLimit = 0.4f;
+ mDesignerParams.mDpSpringk = 0.5f;
+ mDesignerParams.mDpDamperc = 0.2f;
+
+ mDesignerParams.mDpSuspensionYOffset = 0.0f;
+
+ mDesignerParams.mHitPoints = 2.0f;
+ //mDesignerParams.mHitPoints = 0.5f;
+
+ //mDesignerParams.mDpBurnoutRange = 0.2f;
+ mDesignerParams.mDpBurnoutRange = 0.3f;
+
+ mDesignerParams.mDpWheelieRange = 0.0f;
+ mDesignerParams.mDpWheelieYOffset = 0.0f;
+ mDesignerParams.mDpWheelieZOffset = 0.0f;
+
+ mDesignerParams.mDpMaxSpeedBurstTime = 1.0f;
+ mDesignerParams.mDpDonutTorque = 10.0f;
+
+ mDesignerParams.mDpWeebleOffset = -1.0f;
+
+
+ mVehicleState = VS_NORMAL;
+ mTireLateralResistance = mDesignerParams.mDpTireLateralResistanceNormal;
+
+ mSteeringInputThreshold = 0.7f;
+ mSteeringPreSlope = 0.5f;
+
+
+ bool localizedDamage;
+ //localizedDamage = mGeometryVehicle->Init(name, this, num);
+ localizedDamage = mGeometryVehicle->Init(name, this, 0);
+
+ // change the logic so that localizedDamage is true if there as at
+ // least one localized damage texture
+
+
+ if(!localizedDamage)
+ {
+ //mDamageType = 3; // traffic
+ mDamageType = VDT_TRAFFIC;
+ }
+
+ InitSimState(se);
+
+ InitGroundPlane();
+
+ FetchWheelMapping();
+ InitWheelsAndLinkSuspensionJointDrivers();
+
+
+ mGeometryVehicle->InitParticles();
+ //if(mVehicleType == VT_USER)
+ if(1)// mVehicleType == VT_USER || mVehicleType == VT_AI) change this to init for all, only draw for USER - that way it is totally safe to switch vehicle type on the fly
+ {
+ mGeometryVehicle->InitSkidMarks();
+ }
+
+
+
+ bool flappingJointPresent = InitFlappingJoints();
+ if(flappingJointPresent)
+ {
+ //if(mDamageType == 3)
+ if(mDamageType == VDT_TRAFFIC)
+ {
+ //rAssertMsg(0, "Fink, what are you doing?");
+
+ }
+
+ // else
+
+ //mDamageType = 1;
+ mDamageType = VDT_USER;
+
+ }
+ else if(mDamageType == VDT_UNSET) // still unset
+ {
+ mDamageType = VDT_AI;
+ }
+
+ // should be set for sure at this point
+ rAssertMsg(mDamageType != VDT_UNSET, "damage type not set?");
+
+ InitGears();
+
+ //CalculateDragCoeffBasedOnTopSpeed();
+
+ CalculateValuesBasedOnDesignerParams();
+ // does this:
+ // SetupPhysicsProperties();
+ // CalculatedDragCoeffBasedOnTopSpeed
+ // SetDesignerParams on Wheels
+
+
+ CreateLocomotions();
+ mLocoSwitchedToPhysicsThisFrame = false;
+
+
+
+ // need this call here?
+ Reset( false );
+
+ SetupRadDebugWatchStuff();
+
+ // moved here from VehicleCentral
+ InitEventLocator();
+
+
+ // vehicle event stuff
+ mVehicleEventListener = new VehicleEventListener(this);
+
+
+
+
+ //mUsingInCarPhysics
+
+ // default at this point in creation is to be using the incar physics.
+ //
+ // perhaps there should be a paramter to say which, passed into
+
+
+ if(startoutofcar)
+ {
+ SetOutOfCarSimState();
+ }
+
+ /*
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_DEMO )
+ {
+ mWaitingToSwitchToOutOfCar = true;
+ }
+ else
+ {
+ mWaitingToSwitchToOutOfCar = false;
+ }
+ */
+
+ // actually any use in this result?
+ // might as well just assert.
+ //return result;
+ return true;
+}
+
+
+
+//=============================================================================
+// Vehicle::InitEventLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitEventLocator()
+{
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ mpEventLocator = new(gma)EventLocator();
+ mpEventLocator->SetName( mName );
+ mpEventLocator->AddRef();
+ mpEventLocator->SetEventType( LocatorEvent::CAR_DOOR );
+ int id = -1;
+
+ ActionButton::GetInCar* pABHandler = static_cast<ActionButton::GetInCar*>( ActionButton::GetInCar::NewAction( mpEventLocator ) );
+ rAssert( dynamic_cast<ActionButton::GetInCar*>( pABHandler ) != NULL );
+ rAssert( pABHandler );
+ bool bResult = GetActionButtonManager()->AddAction( pABHandler, id );
+ rAssert( bResult );
+
+ // this part is done in VehicleCentral::AddVehicleToActiveList
+ //pABHandler->SetVehicleId( mNumActiveVehicles );
+
+ mpEventLocator->SetData( id );
+
+ // grab the car bounding box
+ mExtents = GetSimState()->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ // slap down a trigger volume for getting intop car
+ const float edge = 1.0f; // how big? TODO : tunable?
+ mpTriggerVolume = new(gma) RectTriggerVolume( mTransform.Row(3),
+ mTransform.Row(0),
+ mTransform.Row(1),
+ mTransform.Row(2),
+ mExtents.x + edge,
+ mExtents.y + edge,
+ mExtents.z + edge);
+
+ // add volume to event trigger
+ mpEventLocator->SetNumTriggers( 1, gma );
+ mpEventLocator->AddTriggerVolume( mpTriggerVolume );
+ mpTriggerVolume->SetLocator( mpEventLocator );
+ mpTriggerVolume->SetName( "get_in" );
+
+}
+
+//=============================================================================
+// Vehicle::AssignEnumBasedOnName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::AssignEnumBasedOnName()
+{
+ /*
+ enum VehicleID
+ {
+ BART_V = 0, // bart_v 0
+ APU_V, // apu_v 1
+ SNAKE_V, // snake_v 2
+ HOMER_V, // homer_v 3
+ FAMIL_V, // famil_v 4
+ GRAMP_V, // gramp_v 5
+ CLETU_V, // cletu_v 6
+ WIGGU_V, // wiggu_v 7
+ KRUSTYKAR_NOTYETIN, //
+ MARGE_V, // marge_v 9
+ OTTOBUS_NOTYETIN,
+ MOESDUFFTRUCK_NOTYETIN,
+ SMITH_V, // smith_v 12
+ FLANDERSMOTORHOME_NOTYETIN,
+ MCBAINHUMMER_NOTYETIN,
+ ALIEN_NOTYETIN,
+ ZOMBI_V, // zombi_v 16
+ MUNSTERS_NOTYETIN,
+ HUMMER2_NOTYETIN,
+
+ CVAN, // cVan 19
+ COMPACTA, // compactA 20
+
+ COMIC_V, // comic_v 21
+ SKINN_V, // skinn_v 22
+
+
+
+ // some more new ai cars
+ CCOLA, // 23
+ CSEDAN,
+ CPOLICE,
+ CCELLA,
+ CCELLB,
+ CCELLC,
+ CCELLD,
+
+ // some more new traffic cars
+ MINIVANA, // 30
+ PICKUPA,
+ TAXIA,
+ SPORTSA,
+ SPORTSB,
+ SUVA,
+ WAGONA, // 36
+
+ // some more new cars: nov 12, 2002
+ HBIKE_V, // 37
+ BURNS_V, // 38
+ HONOR_V, // 39
+
+ CARMOR, // 40
+ CCURATOR, // 41
+ CHEARS, // 42
+ CKLIMO, // 43
+ CLIMO, // 44
+ CNERD, // 45
+
+ FRINK_V, // 46
+
+ // some more new cars: dec 18, 2002
+ CMILK, // 47
+ CDONUT, // 48
+ BBMAN_V, // 49
+ BOOKB_V, // 50
+ CARHOM_V, // 51
+ ELECT_V, // 52
+ FONE_V, // 53
+ GRAMR_V, // 54
+ MOE_V, // 55
+ MRPLO_V, // 56
+ OTTO_V, // 57
+ PLOWK_V, // 58
+ SCORP_V, // 59
+ WILLI_V, // 60
+
+ // new traffic cars: Jan 11, 2003
+ SEDANA, // 61
+ SEDANB, // 62
+
+ // new Chase cars: Jan 11, 2003
+ CBLBART, // 63
+ CCUBE, // 64
+ CDUFF, // 65
+ CNONUP, // 66
+
+ // new Driver cars: Jan 11, 2003
+ LISA_V, // 67
+ KRUST_V, // 68
+
+ // new Traffic cars: Jan 13, 2003
+ COFFIN, // 69
+ HALLO, // 70
+ SHIP, // 71
+ WITCHCAR, // 72
+
+ // new Traffic husk: Feb 10, 2003
+ HUSKA, // 73
+
+ // new Driver cars: Feb 11, 2003
+ ATV_V, // 74
+ DUNE_V, // 75
+ HYPE_V, // 76
+ KNIGH_V, // 77
+ MONO_V, // 78
+ OBLIT_V, // 79
+ ROCKE_V, // 80
+
+ // new Traffic cars: Feb 24, 2003
+ AMBUL, // 81
+ BURNSARM, // 82
+ FISHTRUC, // 83
+ GARBAGE, // 84
+ ICECREAM, // 85
+ ISTRUCK, // 86
+ NUCTRUCK, // 87
+ PIZZA, // 88
+ SCHOOLBU, // 89
+ VOTETRUC, // 90
+
+ // new traffic cars: April 2, 2003
+ GLASTRUC, // 91
+ CFIRE_V, // 92
+
+ CBONE,
+ REDBRICK, // 94
+
+ NUM_VEHICLES,
+ INVALID
+
+ };
+
+ */
+
+ if(strcmp(mName, "bart_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BART_V;
+ }
+
+ if(strcmp(mName, "apu_v") == 0)
+ {
+ mVehicleID = VehicleEnum::APU_V;
+ }
+
+ if(strcmp(mName, "snake_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SNAKE_V;
+ }
+
+ if(strcmp(mName, "homer_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HOMER_V;
+ }
+
+ if(strcmp(mName, "famil_v") == 0)
+ {
+ mVehicleID = VehicleEnum::FAMIL_V;
+ }
+
+ if(strcmp(mName, "gramp_v") == 0)
+ {
+ mVehicleID = VehicleEnum::GRAMP_V;
+ }
+
+ if(strcmp(mName, "cletu_v") == 0)
+ {
+ mVehicleID = VehicleEnum::CLETU_V;
+ }
+
+ if(strcmp(mName, "wiggu_v") == 0)
+ {
+ mVehicleID = VehicleEnum::WIGGU_V;
+ }
+
+ if(strcmp(mName, "marge_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MARGE_V;
+ }
+
+ if(strcmp(mName, "smith_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SMITH_V;
+ }
+
+ if(strcmp(mName, "zombi_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ZOMBI_V;
+ }
+
+ if(strcmp(mName, "cVan") == 0)
+ {
+ mVehicleID = VehicleEnum::CVAN;
+ }
+
+ if(strcmp(mName, "compactA") == 0)
+ {
+ mVehicleID = VehicleEnum::COMPACTA;
+ }
+
+ if(strcmp(mName, "comic_v") == 0)
+ {
+ mVehicleID = VehicleEnum::COMIC_V;
+ }
+
+ if(strcmp(mName, "skinn_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SKINN_V;
+ }
+
+ if(strcmp(mName, "cCola") == 0)
+ {
+ mVehicleID = VehicleEnum::CCOLA;
+ }
+
+ if(strcmp(mName, "cSedan") == 0)
+ {
+ mVehicleID = VehicleEnum::CSEDAN;
+ }
+
+ if(strcmp(mName, "cPolice") == 0)
+ {
+ mVehicleID = VehicleEnum::CPOLICE;
+ }
+
+ if(strcmp(mName, "cCellA") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLA;
+ }
+
+ if(strcmp(mName, "cCellB") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLB;
+ }
+
+ if(strcmp(mName, "cCellC") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLC;
+ }
+
+ if(strcmp(mName, "cCellD") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLD;
+ }
+
+ if(strcmp(mName, "minivanA") == 0)
+ {
+ mVehicleID = VehicleEnum::MINIVANA;
+ }
+
+ if(strcmp(mName, "pickupA") == 0)
+ {
+ mVehicleID = VehicleEnum::PICKUPA;
+ }
+
+ if(strcmp(mName, "taxiA") == 0)
+ {
+ mVehicleID = VehicleEnum::TAXIA;
+ }
+
+ if(strcmp(mName, "sportsA") == 0)
+ {
+ mVehicleID = VehicleEnum::SPORTSA;
+ }
+
+ if(strcmp(mName, "sportsB") == 0)
+ {
+ mVehicleID = VehicleEnum::SPORTSB;
+ }
+
+ if(strcmp(mName, "SUVA") == 0)
+ {
+ mVehicleID = VehicleEnum::SUVA;
+ }
+
+ if(strcmp(mName, "wagonA") == 0)
+ {
+ mVehicleID = VehicleEnum::WAGONA;
+ }
+
+ if(strcmp(mName, "hbike_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HBIKE_V;
+ mNoSkid = true;
+ }
+
+
+ if(strcmp(mName, "burns_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BURNS_V;
+ }
+
+ if(strcmp(mName, "honor_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HONOR_V;
+ mNoFrontSkid = true;
+ }
+
+ if(strcmp(mName, "cArmor") == 0)
+ {
+ mVehicleID = VehicleEnum::CARMOR;
+ }
+
+ if(strcmp(mName, "cCurator") == 0)
+ {
+ mVehicleID = VehicleEnum::CCURATOR;
+ }
+
+ if(strcmp(mName, "cHears") == 0)
+ {
+ mVehicleID = VehicleEnum::CHEARS;
+ }
+
+ if(strcmp(mName, "cKlimo") == 0)
+ {
+ mVehicleID = VehicleEnum::CKLIMO;
+ }
+
+ if(strcmp(mName, "cLimo") == 0)
+ {
+ mVehicleID = VehicleEnum::CLIMO;
+ }
+
+ if(strcmp(mName, "cNerd") == 0)
+ {
+ mVehicleID = VehicleEnum::CNERD;
+ }
+
+ if(strcmp(mName, "frink_v") == 0)
+ {
+ mVehicleID = VehicleEnum::FRINK_V;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "cMilk") == 0)
+ {
+ mVehicleID = VehicleEnum::CMILK;
+ }
+
+ if(strcmp(mName, "cDonut") == 0)
+ {
+ mVehicleID = VehicleEnum::CDONUT;
+ }
+
+ if(strcmp(mName, "bbman_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BBMAN_V;
+ }
+
+ if(strcmp(mName, "bookb_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BOOKB_V;
+ }
+
+ if(strcmp(mName, "carhom_v") == 0)
+ {
+ mVehicleID = VehicleEnum::CARHOM_V;
+ }
+
+ if(strcmp(mName, "elect_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ELECT_V;
+ }
+
+ if(strcmp(mName, "fone_v") == 0)
+ {
+ mVehicleID = VehicleEnum::FONE_V;
+ }
+
+ if(strcmp(mName, "gramR_v") == 0)
+ {
+ mVehicleID = VehicleEnum::GRAMR_V;
+ }
+
+ if(strcmp(mName, "moe_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MOE_V;
+ }
+
+ if(strcmp(mName, "mrplo_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MRPLO_V;
+ }
+
+ if(strcmp(mName, "otto_v") == 0)
+ {
+ mVehicleID = VehicleEnum::OTTO_V;
+ }
+
+ if(strcmp(mName, "plowk_v") == 0)
+ {
+ mVehicleID = VehicleEnum::PLOWK_V;
+ }
+
+ if(strcmp(mName, "scorp_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SCORP_V;
+ }
+
+ if(strcmp(mName, "willi_v") == 0)
+ {
+ mVehicleID = VehicleEnum::WILLI_V;
+ }
+
+ if(strcmp(mName, "sedanA") == 0)
+ {
+ mVehicleID = VehicleEnum::SEDANA;
+ }
+
+ if(strcmp(mName, "sedanB") == 0)
+ {
+ mVehicleID = VehicleEnum::SEDANB;
+ }
+
+ if(strcmp(mName, "cBlbart") == 0)
+ {
+ mVehicleID = VehicleEnum::CBLBART;
+ }
+
+ if(strcmp(mName, "cCube") == 0)
+ {
+ mVehicleID = VehicleEnum::CCUBE;
+ }
+
+ if(strcmp(mName, "cDuff") == 0)
+ {
+ mVehicleID = VehicleEnum::CDUFF;
+ }
+
+ if(strcmp(mName, "cNonup") == 0)
+ {
+ mVehicleID = VehicleEnum::CNONUP;
+ }
+
+ if(strcmp(mName, "lisa_v") == 0)
+ {
+ mVehicleID = VehicleEnum::LISA_V;
+ }
+
+ if(strcmp(mName, "krust_v") == 0)
+ {
+ mVehicleID = VehicleEnum::KRUST_V;
+ }
+
+ if(strcmp(mName, "coffin") == 0)
+ {
+ mVehicleID = VehicleEnum::COFFIN;
+ }
+
+ if(strcmp(mName, "hallo") == 0)
+ {
+ mVehicleID = VehicleEnum::HALLO;
+ }
+
+ if(strcmp(mName, "ship") == 0)
+ {
+ mVehicleID = VehicleEnum::SHIP;
+ m_IsSimpleShadow = false;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "witchcar") == 0)
+ {
+ mVehicleID = VehicleEnum::WITCHCAR;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "huskA") == 0)
+ {
+ mVehicleID = VehicleEnum::HUSKA;
+ }
+
+ if(strcmp(mName, "atv_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ATV_V;
+ }
+
+ if(strcmp(mName, "dune_v") == 0)
+ {
+ mVehicleID = VehicleEnum::DUNE_V;
+ }
+
+ if(strcmp(mName, "hype_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HYPE_V;
+ }
+
+ if(strcmp(mName, "knigh_v") == 0)
+ {
+ mVehicleID = VehicleEnum::KNIGH_V;
+ }
+
+ if(strcmp(mName, "mono_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MONO_V;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "oblit_v") == 0)
+ {
+ mVehicleID = VehicleEnum::OBLIT_V;
+ }
+
+ if(strcmp(mName, "rocke_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ROCKE_V;
+ mNoFrontSkid = true;
+ }
+
+ if(strcmp(mName, "ambul") == 0)
+ {
+ mVehicleID = VehicleEnum::AMBUL;
+ }
+
+ if(strcmp(mName, "burnsarm") == 0)
+ {
+ mVehicleID = VehicleEnum::BURNSARM;
+ }
+
+ if(strcmp(mName, "fishtruc") == 0)
+ {
+ mVehicleID = VehicleEnum::FISHTRUC;
+ }
+
+ if(strcmp(mName, "garbage") == 0)
+ {
+ mVehicleID = VehicleEnum::GARBAGE;
+ }
+
+ if(strcmp(mName, "icecream") == 0)
+ {
+ mVehicleID = VehicleEnum::ICECREAM;
+ }
+
+ if(strcmp(mName, "IStruck") == 0)
+ {
+ mVehicleID = VehicleEnum::ISTRUCK;
+ }
+
+ if(strcmp(mName, "nuctruck") == 0)
+ {
+ mVehicleID = VehicleEnum::NUCTRUCK;
+ }
+
+ if(strcmp(mName, "pizza") == 0)
+ {
+ mVehicleID = VehicleEnum::PIZZA;
+ }
+
+ if(strcmp(mName, "schoolbu") == 0)
+ {
+ mVehicleID = VehicleEnum::SCHOOLBU;
+ }
+
+ if(strcmp(mName, "votetruc") == 0)
+ {
+ mVehicleID = VehicleEnum::VOTETRUC;
+ }
+
+ if(strcmp(mName, "glastruc") == 0)
+ {
+ mVehicleID = VehicleEnum::GLASTRUC;
+ }
+
+ if(strcmp(mName, "cFire_v") == 0)
+ {
+ mVehicleID = VehicleEnum::CFIRE_V;
+ }
+
+ if(strcmp(mName, "cBone") == 0)
+ {
+ mVehicleID = VehicleEnum::CBONE;
+ }
+
+
+ // the best for last!
+ if(strcmp(mName, "redbrick") == 0)
+ {
+ mVehicleID = VehicleEnum::REDBRICK;
+ }
+
+
+}
+
+
+
+// call back for debug watcher:
+//typedef void (*RADDEBUGWATCH_CALLBACK)( void* userData );
+void DebugWatchVehicleTuningCallback(void* userData)
+{
+ ((Vehicle*)userData)->CalculateValuesBasedOnDesignerParams();
+}
+
+
+void InflictDamageHoodCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamageHood();
+}
+
+void InflictDamageBackCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamageBack();
+}
+
+void InflictDamageDriverSideCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamageDriverSide();
+}
+
+void InflictDamagePassengerSideCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamagePassengerSide();
+}
+
+
+//=============================================================================
+// Vehicle::SetupRadDebugWatchStuff
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetupRadDebugWatchStuff()
+{
+ radDbgWatchAddFloat(&mSpeedKmh, "speed kmh", mName, NULL, (void*)this, 0.0f, 1000.0f); // just want to observe this
+ radDbgWatchAddFloat(&mForceToDetachCollectible, "Detach Collectible Force", mName, DebugWatchVehicleTuningCallback, (void*)this, 5000.0f, 50000.0f );
+
+ // Add the static explosion damage variables to the watcher. Make sure to only add them once
+ // under group "Vehicle"
+ static bool staticVariablesInitialized = false;
+ if ( !staticVariablesInitialized)
+ {
+ radDbgWatchAddFloat(&Vehicle::s_DamageFromExplosion, "Damage from Explosion (other)", "Vehicle", NULL, NULL, 0, 10.0f );
+ radDbgWatchAddFloat(&Vehicle::s_DamageFromExplosionPlayer, "Damage from Explosion (player)", "Vehicle", NULL, NULL, 0, 10.0f );
+ staticVariablesInitialized = true;
+ }
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpMass), "mass", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 10000.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpGasScale), "gas scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSlipGasScale), "slip gas scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpHighSpeedGasScale), "high speed gas scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpGasScaleSpeedThreshold), "gas scale threshold", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpBrakeScale), "brake scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTopSpeedKmh), "top speed kmh", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 300.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpMaxWheelTurnAngle), "max wheel turn angle", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 55.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpHighSpeedSteeringDrop), "high speed steering drop", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralStaticGrip), "tire grip", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 15.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralResistanceNormal), "normal steering", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 500.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralResistanceSlip), "slip steering", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 500.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpEBrakeEffect), "ebrake effect", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralResistanceSlipNoEBrake), "slip steering without ebrake", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 500.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSlipEffectNoEBrake), "slip effect without ebrake", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpCMOffset.x), "cmoffset x", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpCMOffset.y), "cmoffset y", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpCMOffset.z), "cmoffset z", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSuspensionLimit), "suspension limit", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSpringk), "spring k", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpDamperc), "damper c", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSuspensionYOffset), "suspension Y Offset", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpBurnoutRange), "burnout range", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpMaxSpeedBurstTime), "max speed burst time", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 10.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpDonutTorque), "donut torque", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 20.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWeebleOffset), "weeble offset", mName, DebugWatchVehicleTuningCallback, (void*)this, -3.0f, 3.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWheelieRange), "wheelie range", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWheelieYOffset), "wheelie Y offset", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWheelieZOffset), "wheelie Z offset", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, -2.0f );
+
+ radDbgWatchAddFunction( "Inflict Damage Hood", &InflictDamageHoodCallback, (void*)this, mName );
+ radDbgWatchAddFunction( "Inflict Damage Back", &InflictDamageBackCallback, (void*)this, mName );
+ radDbgWatchAddFunction( "Inflict Damage Driver Side", &InflictDamageDriverSideCallback, (void*)this, mName );
+ radDbgWatchAddFunction( "Inflict Damage Passenger Side", &InflictDamagePassengerSideCallback, (void*)this, mName );
+
+}
+
+//=============================================================================
+// Vehicle::~Vehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle::~Vehicle()
+{
+ if(GetGameplayManager()->GetCurrentVehicle() == this)
+ {
+ GetGameplayManager()->UnregisterVehicleHUDIcon();
+ }
+
+ delete mGeometryVehicle;
+
+ delete mPhysicsLocomotion;
+ delete mTrafficLocomotion;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ delete mWheels[i];
+ mSuspensionJointDrivers[i]->Release();
+
+ // TODO - need to wrap these somehow in case they don't exist?
+ if(mInertialJointDrivers[i])
+ {
+ mInertialJointDrivers[i]->Release();
+ }
+ if(mPhysicsJointMatrixModifiers[i])
+ {
+ mPhysicsJointMatrixModifiers[i]->Release();
+ }
+
+ }
+
+ delete[] mGearRatios;
+ delete[] mJointIndexToWheelMapping;
+ if(mJointIndexToInertialJointDriverMapping)
+ {
+ delete[] mJointIndexToInertialJointDriverMapping;
+ }
+
+ // these moved to a method that can be called from VehicleCentral::RemoveVehicleFromActiveList
+ //RemoveSelfFromCollisionManager();
+ //GetWorldPhysicsManager()->FreeCollisionAreaIndex(mCollisionAreaIndex);
+ //mCollisionAreaIndex = -1;
+
+ mGroundPlaneWallVolume->Release();
+ mGroundPlanePhysicsProperties->Release();
+ mPhysicsProperties->Release();
+
+ if(mRootMatrixDriver)
+ {
+ mRootMatrixDriver->Release();
+ }
+
+ mPoseEngine->Release();
+
+ mGroundPlaneSimState->Release();
+
+ //p3d::inventory->SelectSection("Level");
+ //p3d::inventory->Remove(mSimStateArticulated->GetCollisionObject());
+ //p3d::inventory->Remove(mSimStateArticulated->GetSimulatedObject());
+
+
+ tRefCounted::Release(mSimStateArticulatedInCar);
+ tRefCounted::Release(mSimStateArticulatedOutOfCar);
+
+
+ int id = mpEventLocator->GetData();
+ GetActionButtonManager()->RemoveActionByIndex( id );
+
+
+ mpEventLocator->Release();
+
+ if(mpDriver)
+ {
+ // DO NOT remove a pedestrian manually!
+ // We need them for recycling within the level.
+ if( mpDriver->GetRole() != Character::ROLE_PEDESTRIAN )
+ {
+ GetCharacterManager()->RemoveCharacter(mpDriver);
+ }
+ tRefCounted::Release(mpDriver);
+ }
+
+ GetCharacterManager()->ClearTargetVehicle(this);
+
+ rAssert( mName != NULL );
+ delete[] mName;
+
+ if(mVehicleEventListener)
+ {
+ delete mVehicleEventListener;
+ }
+}
+
+
+
+//=============================================================================
+// Vehicle::CreateLocomotions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CreateLocomotions()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ // is this vehicle being setup for A or B?
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPhysicsLocomotion = new(gma)PhysicsLocomotion(this);
+ mTrafficLocomotion = new(gma)TrafficLocomotion(this);
+
+ //?
+ SetLocomotion( mLoco );
+MEMTRACK_POP_GROUP( "Vehicle" );
+}
+
+
+
+
+//=============================================================================
+// Vehicle::InitWheelsAndLinkSuspensionJointDrivers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitWheelsAndLinkSuspensionJointDrivers()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ CollisionObject* collObj = mSimStateArticulated->GetCollisionObject();
+ CollisionVolume* collVol = collObj->GetCollisionVolume();
+ rAssert(collVol);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ CollisionVolume* sub = collVol->GetSubCollisionVolume(mWheelToJointIndexMapping[i], true); // true because we only care about the local sub volume
+
+ // sub should be of type SphereVolumeType
+ rAssert(sub->Type() == SphereVolumeType);
+
+ float radius = ((SphereVolume*)sub)->GetRadius();
+
+ mWheels[i] = new(gma)Wheel;
+
+ // now we can init the goddamn wheel
+ if(i < 2) // todo - way to NOT hardcode this for 4 wheels????
+ {
+ mWheels[i]->Init(this, i, radius, false, true);
+ }
+ else
+ {
+ mWheels[i]->Init(this, i, radius, true, false);
+ // try all wheel drive
+ //mWheels[i]->Init(this, i, radius, true, true);
+ }
+
+ mSuspensionJointDrivers[i] = new(gma)SuspensionJointDriver(mWheels[i], mWheelToJointIndexMapping[i]);
+
+ mSuspensionJointDrivers[i]->AddRef();
+ mPoseEngine->AddPoseDriver(1, mSuspensionJointDrivers[i]);
+ }
+MEMTRACK_POP_GROUP("Vehicle");
+}
+
+
+//=============================================================================
+// Vehicle::InitFlappingJoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+bool Vehicle::InitFlappingJoints()
+{
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mJointIndexToInertialJointDriverMapping = new(gma) int[p3dPose->GetNumJoint()];
+
+ // TODO ?
+ // ok to use memset?
+ memset(mJointIndexToInertialJointDriverMapping, -1, (sizeof(int) * p3dPose->GetNumJoint()) );
+
+
+ int count = 0;
+
+ rmt::Vector axis;
+ rmt::Vector rotAxis;
+
+ bool atLeastOneFlappingJointPresent = false;
+
+
+ //-----------------
+ // driver-side door
+ //-----------------
+ axis.Set(0.0f, 0.0f, -1.0f);
+ rotAxis.Set(0.0f, 1.0f, 0.0f);
+ if(AddFlappingJoint("DoorDTrans", "DoorDRot", axis, rotAxis, count, &mDoorDJoint))
+ {
+ // override default settings
+
+ mInertialJointDrivers[count]->SetSpeedRate(15.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(0.5f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ //--------------------
+ // passenger-side door
+ //--------------------
+ count++;
+ rotAxis.Set(0.0f, -1.0f, 0.0f);
+ if(AddFlappingJoint("DoorPTrans", "DoorPRot", axis, rotAxis, count, &mDoorPJoint))
+ {
+ mInertialJointDrivers[count]->SetSpeedRate(15.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(0.5f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ //-----
+ // hood
+ //-----
+ count++;
+ axis.Set(0.0f, 0.0f, 1.0f);
+ rotAxis.Set(-1.0f, 0.0f, 0.0f);
+ if(AddFlappingJoint("HoodTrans", "HoodRot", axis, rotAxis, count, &mHoodJoint))
+ {
+ mInertialJointDrivers[count]->SetSpeedRate(3.0f);
+ mInertialJointDrivers[count]->SetAccelRate(300.0f);
+ mInertialJointDrivers[count]->SetGravityRate(1.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(1.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ //------
+ // trunk
+ //------
+ count++;
+ axis.Set(0.0f, 0.0f, -1.0f);
+ rotAxis.Set(1.0f, 0.0f, 0.0f);
+ if(AddFlappingJoint("TrunkTrans", "TrunkRot", axis, rotAxis, count, &mTrunkJoint))
+ {
+ mInertialJointDrivers[count]->SetSpeedRate(12.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(1.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(1.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ return atLeastOneFlappingJointPresent;
+
+}
+
+
+//=============================================================================
+// Vehicle::AddFlappingJoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const char* transJointName, const char* rotJointName, rmt::Vector& axis, rmt::Vector& rotAxis, int count)
+//
+// Return: void
+//
+//=============================================================================
+bool Vehicle::AddFlappingJoint(const char* transJointName, const char* rotJointName, rmt::Vector& axis, rmt::Vector& rotAxis, int count, int* collisionJointIndex)
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+
+ //SkeletonInfo* skelInfo = p3d::find<SkeletonInfo>(mName);
+ SkeletonInfo* skelInfo = mPhObj->GetSkeletonInfo ();
+ rAssert(skelInfo);
+
+ // release debugging:
+ if(skelInfo == 0)
+ {
+ char buffy[64];
+ sprintf(buffy, "can't find skelInfo for %s\n", mName);
+ rReleaseString(buffy);
+ }
+
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+
+ int indexTrans;
+ int indexRot;
+
+ indexTrans = p3dPose->FindJointIndex(transJointName);
+ indexRot = p3dPose->FindJointIndex(rotJointName);
+
+ *collisionJointIndex = indexRot;
+
+ if(indexTrans == -1 || indexRot == -1)
+ {
+ MEMTRACK_POP_GROUP( "Vehicle" );
+ return false;
+ }
+
+ #ifdef RAD_DEBUG // wrap in define?
+ tPose::Joint* transJoint = p3dPose->GetJoint(indexTrans);
+ tPose::Joint* rotJoint = p3dPose->GetJoint(indexRot);
+ rAssert(rotJoint->parent == transJoint);
+ #endif
+
+ skelInfo->SetJointAxis(indexTrans, axis, 1.0f);
+ skelInfo->SetJointAxis(indexRot, axis, 1.0f);
+
+ skelInfo->SetJointRotAxis(indexRot, rotAxis);
+
+ // add new driver...
+ mJointIndexToInertialJointDriverMapping[indexRot] = count;
+
+ PhysicsJoint* phizJoint = mPhObj->GetJoint(indexRot);
+ rAssert(phizJoint);
+
+ // test
+ //phizJoint->SetInvStiffness(0.5f);
+
+
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mInertialJointDrivers[count] = new(gma) PhysicsJointInertialEffector(phizJoint);
+
+ mInertialJointDrivers[count]->AddRef();
+
+ // defaults
+ // override shortly after this...
+
+ mInertialJointDrivers[count]->SetSpeedRate(15.0f);
+ //mInertialJointDrivers[count]->SetAccelRate(20.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(1.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+
+ // new
+ // start out disabled
+
+ mPhObj->GetJoint(indexRot)->SetInvStiffness(0.0f);
+ mPhObj->GetJoint(indexRot)->ResetDeformation();
+
+ mInertialJointDrivers[count]->SetIsEnabled(false);
+
+ // debug
+ //mInertialJointDrivers[count]->SetIsEnabled(true);
+
+
+ /*
+ mInertialJointDrivers[count]->SetSpeedRate(0.0f);
+ //mInertialJointDrivers[count]->SetAccelRate(20.0f);
+ mInertialJointDrivers[count]->SetAccelRate(0.0f);
+ mInertialJointDrivers[count]->SetGravityRate(0.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+ */
+
+
+ // TODO - which pose engine pass?
+ mPoseEngine->AddPoseDriver(2, mInertialJointDrivers[count]);
+
+ // also need this other jointmatrixmodifier thing...
+ mPhysicsJointMatrixModifiers[count] = new(gma) PhysicsJointMatrixModifier(phizJoint);
+
+ mPhysicsJointMatrixModifiers[count]->AddRef();
+
+ mPoseEngine->AddPoseDriver(2, mPhysicsJointMatrixModifiers[count]);
+
+ MEMTRACK_POP_GROUP("Vehicle");
+ return true;
+}
+
+
+
+//=============================================================================
+// Vehicle::InitGears
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitGears()
+{
+ mNumGears = 6; // TODO - not sure we actually want designers to worry about this?
+ // maybe, they can just choose number of gears and there will be pre-defined ratios??
+ //
+ // recall: all this gear shit is just for sound and does not affect performance
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mGearRatios = new(gma) float[mNumGears];
+
+ mFinalDriveRatio = 3.42f;
+
+ mGearRatios[0] = 2.97f; // 1st
+ //mGearRatios[1] = 2.07f; // 2nd
+ mGearRatios[1] = 1.8f; // 2nd
+ mGearRatios[2] = 1.43f; // 3rd
+ mGearRatios[3] = 1.00f; // 4th
+ mGearRatios[4] = 0.84f; // 5th
+ mGearRatios[5] = 0.56f; // 6th
+
+
+
+/* some stuff fromt trav for a corvette
+
+ Transmission..........6-speed manual
+ Final-drive ratio..........3.42:1, limited slip
+ Gear..........Ratio..........Mph/1000 rpm..........Max. test speed
+ I..........2.97..........7.4..........48 mph (6500 rpm)
+ II..........2.07..........10.6..........69 mph (6500 rpm)
+ III..........1.43..........15.3..........100 mph (6500 rpm)
+ IV..........1.00..........21.9..........143 mph (6500 rpm)
+ V..........0.84..........26.1..........168 mph (6450 rpm)
+ VI..........0.56..........39.2..........155 mph (4000 rpm)
+
+*/
+
+
+}
+
+
+//=============================================================================
+// Vehicle::SetupPhysicsProperties
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetupPhysicsProperties()
+{
+ // called when we first create the sim state, as well as when
+ // we set new designer paramters.
+
+ //float volume = mPhObj->GetVolume();
+
+ float volume = ((ArticulatedPhysicsObject*)(mSimStateArticulatedInCar->GetSimulatedObject(-1)))->GetVolume();
+
+ // TODO - verify this is coming back in the correct units
+
+ // can't set mass directly
+ float density = mDesignerParams.mDpMass / volume;
+
+ // TODO - which of these do we actually want to allow designers to modify?
+ //mPhysicsProperties->SetFrictCoeffCGS(0.8f);
+ //mPhysicsProperties->SetRestCoeffCGS(1.05f);
+ //mPhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ // these values slide you along walls a little nicer
+ mPhysicsProperties->SetFrictCoeffCGS(0.3f);
+ mPhysicsProperties->SetRestCoeffCGS(1.15f);
+ mPhysicsProperties->SetTangRestCoeffCGS(-0.6f);
+
+
+ //mPhysicsProperties->SetDensityCGS(density / 1000000.0f); // our density is in g / m^3
+ mPhysicsProperties->SetDensityCGS(density); // our density is in g / m^3
+
+ //mSimStateArticulated->SetPhysicsProperties(mPhysicsProperties);
+
+ mSimStateArticulatedInCar->SetPhysicsProperties(mPhysicsProperties);
+ if(mSimStateArticulatedOutOfCar)
+ {
+ mSimStateArticulatedOutOfCar->SetPhysicsProperties(mPhysicsProperties);
+ }
+
+ mPhObj->SetInvTWDissip(0);
+ mPhObj->SetDissipationInternalRate(0);
+ mPhObj->SetDissipationDeformationRate(0); //no good Martin
+
+
+ // TODO - where to put this!
+ // should i even use it?
+
+ // TODO
+ // temp hack to fix lack of phizsim
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();// this or CGS ?
+
+ //float gravity_y = 9.81f;
+ float gravity_y = -1.0f * gravity.y;
+
+ //rmt::Vector gravity = SG::phizSim.mSimEnvironment->Gravity();
+ //mSuspensionRestValue = mDesignerParams.mDpMass * rmt::Fabs(gravity.y) * 0.25f;
+ mSuspensionRestValue = mDesignerParams.mDpMass * gravity_y * 0.25f;
+
+ // new thing aimed at minimizing strage whipping effects on the car when the wheels bottom out
+ // and you get insane values calculated for suspension force
+ mSuspensionMaxValue = mDesignerParams.mDpMass * gravity_y * 2.5f; // TODO - find the right value
+
+
+ mCMOffset = mOriginalCMOffset;
+
+ mCMOffset.Add(mDesignerParams.mDpCMOffset);
+
+ ((ArticulatedPhysicsObject*)(mSimStateArticulatedInCar->GetSimulatedObject(-1)))->SetExternalCMOffset(mCMOffset);
+
+ //mPhObj->SetExternalCMOffset(mCMOffset);
+
+
+}
+
+
+//=============================================================================
+// Vehicle::InitGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitGroundPlane()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ rmt::Vector p(0.0f, 0.0f, 0.0f);
+ rmt::Vector n(0.0f, 1.0f, 0.0f);
+
+ HeapMgr()->PushHeap (gma);
+
+ mGroundPlaneWallVolume = new WallVolume(p, n);
+ mGroundPlaneWallVolume->AddRef();
+
+ mGroundPlaneSimState = (sim::ManualSimState*)(SimState::CreateManualSimState(mGroundPlaneWallVolume));
+ mGroundPlaneSimState->AddRef();
+
+ mGroundPlaneSimState->GetCollisionObject()->SetManualUpdate(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetAutoPair(false);
+ mGroundPlaneSimState->GetCollisionObject()->SetIsStatic(true);
+
+ char buffy[128];
+ sprintf(buffy, "%s_groundplane", mName);
+
+ mGroundPlaneSimState->GetCollisionObject()->SetName(buffy);
+
+ mGroundPlaneSimState->mAIRefIndex = this->GetGroundPlaneAIRef();
+ mGroundPlaneSimState->mAIRefPointer = (void*)this;
+
+ mGroundPlanePhysicsProperties = new PhysicsProperties;
+ mGroundPlanePhysicsProperties->AddRef();
+
+ mGroundPlanePhysicsProperties->SetFrictCoeffCGS(0.8f);
+ mGroundPlanePhysicsProperties->SetRestCoeffCGS(1.15f);
+ mGroundPlanePhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ mGroundPlaneSimState->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+
+ HeapMgr()->PopHeap (gma);
+
+/*
+
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ rmt::Vector p, n;
+ p.Set(0.0f, 0.0f, 0.0f);
+ //n.Set(0.0f, 1.0f, 0.0f);
+ n.Set(0.0f, 0.0f, 1.0f);
+
+ mGroundPlaneWallVolume = new(gma) WallVolume(p, n);
+
+ mGroundPlaneWallVolume->AddRef();
+
+ mGroundPlaneSimState = SimState::CreateSimState(mGroundPlaneWallVolume);
+ mGroundPlaneSimState->AddRef();
+ //mGroundPlaneSimState->GetCollisionObject()->SetIsStatic(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetAutoPair(false); // TODO - does not work yet
+ // TODO - name?
+
+ char buffy[128];
+ sprintf(buffy, "%s_groundplane", mName);
+
+ mGroundPlaneSimState->GetCollisionObject()->SetName(buffy);
+
+ mGroundPlaneSimState->mAIRefIndex = this->GetGroundPlaneAIRef();
+ mGroundPlaneSimState->mAIRefPointer = (void*)this;
+
+ mGroundPlanePhysicsProperties = new(gma) PhysicsProperties;
+ mGroundPlanePhysicsProperties->AddRef();
+
+ mGroundPlanePhysicsProperties->SetFrictCoeffCGS(0.8f);
+ mGroundPlanePhysicsProperties->SetRestCoeffCGS(1.05f);
+ mGroundPlanePhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ mGroundPlaneSimState->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+
+
+*/
+
+
+
+MEMTRACK_POP_GROUP("Vehicle");
+}
+
+
+
+
+//=============================================================================
+// Vehicle::FetchWheelMapping
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::FetchWheelMapping()
+{
+ // should have already done these asserts, but just in case things
+ // get moved around a bit...
+
+ rAssert(mGeometryVehicle);
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mJointIndexToWheelMapping = new(gma) int[p3dPose->GetNumJoint()];
+
+ memset(mJointIndexToWheelMapping, -1, (sizeof(int) * p3dPose->GetNumJoint()) );
+
+ //
+ // naming convention so far:
+ //
+ // should have joints named:
+ //
+ // w0, w1, w2, w3
+
+ char buffy[8];
+ memset(buffy, 0, sizeof(buffy));
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // TODO!
+ // safe to use sprintf ??
+ sprintf(buffy, "w%d", i);
+ mWheelToJointIndexMapping[i] = p3dPose->FindJointIndex(buffy);
+
+ // for convenience we can look up either way....
+ mJointIndexToWheelMapping[mWheelToJointIndexMapping[i]] = i;
+
+ tPose::Joint* joint = p3dPose->GetJoint(mWheelToJointIndexMapping[i]);
+
+ // fill mSuspensionRestPoints[4] with the transform row of the joint object space
+ // matrix
+
+ mSuspensionRestPointsFromFile[i] = joint->objectMatrix.Row(3);
+
+ mSuspensionRestPoints[i] = mSuspensionRestPointsFromFile[i];
+
+ mSuspensionRestPoints[i].y += mDesignerParams.mDpSuspensionYOffset;
+
+ // used to be other stuff in here for radius, and Wheels, and suspensionjointdrivers
+
+ }
+
+ // passenger/driver locations
+
+ int passengerIndex = p3dPose->FindJointIndex( "pl" );
+ int driverIndex = p3dPose->FindJointIndex( "dl" );
+
+ if(passengerIndex != -1)
+ {
+ tPose::Joint* joint = p3dPose->GetJoint( passengerIndex );
+ mPassengerLocation = joint->objectMatrix.Row(3);
+ }
+ else
+ {
+ mPassengerLocation.Set( 0.5f, -0.6f, 0.01f );
+ }
+
+ if(driverIndex != -1)
+ {
+ tPose::Joint* joint = p3dPose->GetJoint( driverIndex );
+ mDriverLocation = joint->objectMatrix.Row(3);
+ }
+ else
+ {
+ mDriverLocation.Set( -0.5f, -0.6f, 0.01f );
+ }
+
+
+ mWheelBase = mSuspensionRestPoints[3].z - mSuspensionRestPoints[1].z;
+
+ // smoke location
+
+ //int smokeIndex = p3dPose->FindJointIndex("smoke");
+ int smokeIndex = p3dPose->FindJointIndex("sl");
+ tPose::Joint* joint = p3dPose->GetJoint(smokeIndex);
+ if (joint)
+ {
+ mSmokeOffset = joint->objectMatrix.Row(3);
+ }
+
+}
+
+
+//=============================================================================
+// Vehicle::GetWheel0Offset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector Vehicle::GetWheel0Offset()
+{
+ // this is not accurate as wheel is lurching around but should be close enough for smoke effects?
+ rmt::Vector temp = mSuspensionRestPoints[0];
+ temp.y -= mWheels[0]->mRadius;
+ return temp;
+}
+
+
+//=============================================================================
+// Vehicle::GetWheel1Offset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector Vehicle::GetWheel1Offset()
+{
+ rmt::Vector temp = mSuspensionRestPoints[1];
+ temp.y -= mWheels[1]->mRadius;
+ return temp;
+}
+
+//=============================================================================
+// Vehicle::InitSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitSimState(SimEnvironment* se)
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ CreatePoseEngine();
+ rAssert(mPoseEngine);
+
+ // The section stuff here is a hack.
+ // Tracked to ATG as bug 1259.
+ //
+ p3d::inventory->PushSection ();
+ p3d::inventory->AddSection (SKELCACHE);
+ p3d::inventory->SelectSection (SKELCACHE);
+ //mSimStateArticulated = SimStateArticulated::CreateSimStateArticulated(mName, mPoseEngine->GetPose(), SimStateAttribute_Default, NULL);
+
+ SimStateArticulated* newSimState = SimStateArticulated::CreateSimStateArticulated(mName, mPoseEngine->GetPose(), SimStateAttribute_Default, NULL);
+ rAssert( mSimStateArticulatedInCar == NULL );
+ tRefCounted::Assign( mSimStateArticulatedInCar, newSimState );
+ rAssert( mSimStateArticulatedInCar != NULL );
+
+
+ mSimStateArticulatedInCar->mAIRefIndex = PhysicsAIRef::redBrickVehicle;
+
+ mSimStateArticulatedInCar->mAIRefPointer = (void*)this;
+
+
+ ((ArticulatedPhysicsObject*)(mSimStateArticulatedInCar->GetSimulatedObject(-1)))->SetSimEnvironment(se);
+
+
+
+
+ // new test:
+ char buffy[128];
+ sprintf(buffy, "%sBV", mName);
+ rAssert( mSimStateArticulatedOutOfCar == NULL );
+ mSimStateArticulatedOutOfCar = SimStateArticulated::CreateSimStateArticulated(buffy, mPoseEngine->GetPose(), SimStateAttribute_Default, NULL);
+
+ if(mSimStateArticulatedOutOfCar)
+ {
+
+ mSimStateArticulatedOutOfCar->AddRef();
+ mSimStateArticulatedOutOfCar->mAIRefIndex = PhysicsAIRef::redBrickVehicle;
+
+ mSimStateArticulatedOutOfCar->mAIRefPointer = (void*)this;
+ ((ArticulatedPhysicsObject*)(mSimStateArticulatedOutOfCar->GetSimulatedObject(-1)))->SetSimEnvironment(se);
+
+ }
+
+
+ p3d::inventory->PopSection ();
+
+ // finish initialization with this... then switch back
+ mSimStateArticulated = mSimStateArticulatedInCar;
+
+
+
+
+
+
+ mPhObj = (ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1));
+
+ // fetch once only ever
+ // seven fucking days
+ //mOriginalCMOffset = mPhObj->GetExternalCMOffset();
+
+ mOriginalCMOffset.x = 0.0f;
+ mOriginalCMOffset.y = 0.0f;
+ mOriginalCMOffset.z = 0.0f;
+
+
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPhysicsProperties = new(gma) PhysicsProperties; // TODO - who owns - ie. who is responsible to delete
+
+ mPhysicsProperties->AddRef();
+
+/*
+ for (int i=0; i<mPhObj->NumSimJoints(); i++)
+ {
+ PhysicsJoint* joint = mPhObj->GetSimJoint(i);
+ P3DASSERT(joint);
+ mPoseEngine->AddPoseDriver(2, new PhysicsJointMatrixModifier(joint));
+ }
+
+ sim::PhysicsJointMatrixModifier mPhysicsJointMatrixModifiers[4];
+*/
+MEMTRACK_POP_GROUP( "Vehicle" );
+}
+
+
+//=============================================================================
+// Vehicle::CreatePoseEngineOutOfCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+/*
+void Vehicle::CreatePoseEngineOutOfCar()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ // TODO - wtf?
+ //const int PoseEngineSimPass = 1;
+ const int PoseEngineSimPass = 2;
+
+ rAssert(mGeometryVehicle);
+
+
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPoseEngine = new(gma) poser::PoseEngine(p3dPose, PoseEngineSimPass+1, p3dPose->GetNumJoint());
+ mRootMatrixDriver = new(gma) RootMatrixDriver(&mTransform);
+
+ mPoseEngine->AddRef();
+ mRootMatrixDriver->AddRef();
+ mPoseEngine->AddPoseDriver(1, mRootMatrixDriver); // 0 is the pass
+
+MEMTRACK_POP_GROUP();
+
+}
+*/
+//=============================================================================
+// Vehicle::CreatePoseEngine
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CreatePoseEngine()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ // TODO - wtf?
+ //const int PoseEngineSimPass = 1;
+ const int PoseEngineSimPass = 2;
+
+ rAssert(mGeometryVehicle);
+
+
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPoseEngine = new(gma) poser::PoseEngine(p3dPose, PoseEngineSimPass+1, p3dPose->GetNumJoint());
+ mRootMatrixDriver = new(gma) RootMatrixDriver(&mTransform);
+
+ mPoseEngine->AddRef();
+ mRootMatrixDriver->AddRef();
+ //mPoseEngine->AddPoseDriver(1, mRootMatrixDriver); // 0 is the pass
+ mPoseEngine->AddPoseDriver(0, mRootMatrixDriver); // 0 is the pass
+
+MEMTRACK_POP_GROUP("Vehicle");
+}
diff --git a/game/code/worldsim/redbrick/vehiclelocomotion.cpp b/game/code/worldsim/redbrick/vehiclelocomotion.cpp
new file mode 100644
index 0000000..4cf7f2b
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclelocomotion.cpp
@@ -0,0 +1,46 @@
+/*===========================================================================
+ vehiclelocomotion.cpp
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+
+#include <worldsim/redbrick/vehiclelocomotion.h>
+
+
+
+/*
+//------------------------------------------------------------------------
+VehicleLocomotion::VehicleLocomotion()
+{
+ //
+}
+
+
+
+//------------------------------------------------------------------------
+VehicleLocomotion::~VehicleLocomotion()
+{
+ //
+}
+
+
+//------------------------------------------------------------------------
+void VehicleLocomotion::PreCollisionPrep(Vehicle* vehicle)
+{
+
+}
+
+
+//------------------------------------------------------------------------
+void VehicleLocomotion::Update(Vehicle* vehicle, float dt)
+{
+
+}
+*/ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclelocomotion.h b/game/code/worldsim/redbrick/vehiclelocomotion.h
new file mode 100644
index 0000000..a700dc6
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclelocomotion.h
@@ -0,0 +1,41 @@
+/*===========================================================================
+ vehiclelocomotion.h
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _VEHICLELOCOMOTION_H
+#define _VEHICLELOCOMOTION_H
+
+#include <radmath/radmath.hpp>
+
+class Vehicle;
+
+class VehicleLocomotion
+{
+public:
+
+ VehicleLocomotion(Vehicle* vehicle) {};
+ virtual ~VehicleLocomotion() {};
+
+ virtual void PreSubstepUpdate() = 0;
+
+ virtual void PreCollisionPrep(bool firstSubstep) = 0;
+ virtual void UpdateVehicleGroundPlane() = 0;
+
+ virtual void PreUpdate() = 0;
+ virtual void Update(float dt) = 0;
+ virtual void PostUpdate() = 0;
+
+ virtual void CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint) = 0;
+
+};
+
+
+#endif // _VEHICLELOCOMOTION_H
diff --git a/game/code/worldsim/redbrick/wheel.cpp b/game/code/worldsim/redbrick/wheel.cpp
new file mode 100644
index 0000000..f889909
--- /dev/null
+++ b/game/code/worldsim/redbrick/wheel.cpp
@@ -0,0 +1,533 @@
+/*===========================================================================
+ wheel.cpp
+
+ created Jan 29, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#include <worldsim/redbrick/wheel.h>
+#include <radmath/radmath.hpp>
+#include <worldsim/worldphysicsmanager.h>
+
+
+//------------------------------------------------------------------------
+Wheel::Wheel()
+{
+ mRadius = 0.0f;
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+
+ mWheelNum = -1; // unset flag
+ mYOffset = 0.0f;
+
+ mLimit = 0.0f;
+ mk = 0.0f;
+ mc = 0.0f;
+
+ mWheelInCollision = false; // TODO - is this method adequate!?
+ mWheelBottomedOutThisFrame = false;
+
+ mSpringRelaxRate = 0.5f; // second to move from limit to 0 when not in collision
+
+ mWheelTurnAngle = 0.0f;
+
+ mRotAngle = 0.0f;
+ mCumulativeRot = 0.0f;
+ mTotalTime = 0.0f;
+
+ mWheelRenderSpeedRadPerSecond = 0.0f;
+ //mSpinUpRate = 10.0f; //? - I think this is in (rad/s)/s
+
+ //mRenderingSpinUpRateBase = 0.1f; // this is actually in units of rad/s -
+ // it's a direct setting of the increment amount, for now
+
+ mRenderingSpinUpRateBase = 100.0f; // back to rad/s
+
+ mRenderingSpinUpRate = 0.0f; // this one is scaled by the gas input
+
+ // % of top speed at which mode goes back to NORMAL, if we were slipping 'cause we floored it initially
+ mDpRenderingSpinUpSpeedThresholdPercentage = 0.3f;
+ //mDpRenderingSpinUpSpeedThresholdPercentage = 0.2f;
+
+
+ mSteerWheel = false;
+ mDriveWheel = false;
+
+
+ mVehicle = 0;
+
+ //----------------
+ // designer values
+ //----------------
+
+ mWheelState = WS_NORMAL;
+
+
+ //---------
+ // redbrick normal
+ //---------
+
+ // NOTE: these are all overwritten shortly after init
+ // these are just default values that are ok for the ferrari
+
+ mDpSuspensionLimit = 0.4f; // try this for ferrari
+
+ mDpSpringk = 0.5f; // prett good value for redbrick
+ mDpDamperc = 0.2f; // scale on critical damping temp for debugging
+
+
+}
+
+
+//------------------------------------------------------------------------
+Wheel::~Wheel()
+{
+ //
+}
+
+
+//------------------------------------------------------------------------
+void Wheel::Init(Vehicle* vehicle, int wheelNum, float radius, bool steer, bool drive)
+{
+ mVehicle = vehicle;
+
+ mWheelNum = wheelNum;
+ mRadius = radius;
+
+ mSteerWheel = steer;
+ mDriveWheel = drive;
+
+ //
+ // calculate 'real' values from designer params
+ //
+
+ mLimit = mDpSuspensionLimit * mRadius;
+
+ // TODO
+ // temp hack for lack of phizsim
+ //float gravity_y = 9.81f;
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();// this or CGS ?
+
+ //float gravity_y = 9.81f;
+ float gravity_y = -1.0f * gravity.y;
+
+ //rmt::Vector gravity = SG::phizSim.mSimEnvironment->Gravity();
+ //float force = mVehicle->mDesignerParams.mDpMass * rmt::Fabs(gravity.y);
+ float force = mVehicle->mDesignerParams.mDpMass * gravity_y;
+ mk = force / mLimit;
+ mk *= mDpSpringk;
+
+ //mqk = rmt::Sqrt(mk) * 2.0f; // first guess at quad spring
+ mqk = mk * 5.0f; // first guess at quad spring
+ //mqk = mk * 10.0f; // first guess at quad spring
+
+ float criticalDamping = 2.0f * (float)rmt::Sqrt(mk * mVehicle->mDesignerParams.mDpMass);
+ mc = criticalDamping * mDpDamperc;
+
+
+ // these will get recalculated wheh SetDesignParams is called
+
+}
+
+//------------------------------------------------------------------------
+void Wheel::SetDesignerParams(Vehicle::DesignerParams* dp)
+{
+
+ mDpSuspensionLimit = dp->mDpSuspensionLimit;
+
+ mDpSpringk = dp->mDpSpringk;
+ mDpDamperc = dp->mDpDamperc;
+
+
+ //----------
+
+ mLimit = mDpSuspensionLimit * mRadius;
+
+ // TODO
+ // temp hack for lack of phizsim
+ //float gravity_y = 9.81f;
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();// this or CGS ?
+
+ //float gravity_y = 9.81f;
+ float gravity_y = -1.0f * gravity.y;
+
+ //rmt::Vector gravity = SG::phizSim.mSimEnvironment->Gravity();
+ //float force = mPhysicsVehicleOwner->mDpMass * rmt::Fabs(gravity.y);
+ float force = mVehicle->mDesignerParams.mDpMass * gravity_y;
+ mk = force / mLimit;
+ mk *= mDpSpringk;
+
+ //mqk = rmt::Sqrt(mk) * 2.0f; // first guess at quad spring
+ mqk = mk * 5.0f; // first guess at quad spring
+ //mqk = mk * 10.0f; // first guess at quad spring
+
+
+ float criticalDamping = 2.0f * (float)rmt::Sqrt(mk * mVehicle->mDesignerParams.mDpMass);
+ mc = criticalDamping * mDpDamperc;
+
+
+ CalculateRenderingSpinUpRateBase(dp->mDpTopSpeedKmh);
+
+}
+
+//------------------------------------------------------------------------
+float Wheel::CalculateSuspensionForce(float suspensionPointYVelocity, float dt)
+{
+
+ rAssert(0);
+
+ float force = 0.0f;
+
+ if(mWheelInCollision)
+ {
+
+ float springForce = mk * mYOffset;
+
+ // apply extra force if we're close to bottoming out
+ //
+ // TODO
+ // hmmmm..... revisit this...
+ // replace with quadratic
+
+ //
+ // or... topping out!
+ //
+ if((mLimit - rmt::Fabs(mYOffset)) < mLimit * 0.25f)
+ {
+ springForce *= 3.0f;
+ }
+
+ // for now, only add damping if chassis is trying to compress suspension - ie. y velocity is down, -ve
+ float damperForce = 0.0f;
+
+ if(suspensionPointYVelocity < 0.0f)
+ {
+ damperForce = mc * -1.0f * suspensionPointYVelocity;
+ }
+ else
+ {
+ // like the havok guys, only let damper pull down at about 1/3 of push up
+ damperForce = mc * -1.0f * suspensionPointYVelocity * 0.3f;
+
+ }
+
+ force = springForce + damperForce;
+ }
+ else
+ {
+ // need to relax spring somehow
+ float relaxDistance = mLimit / mSpringRelaxRate * dt;
+ if(mYOffset >= relaxDistance)
+ {
+ mYOffset -= relaxDistance;
+ }
+ else if(mYOffset <= -relaxDistance)
+ {
+ mYOffset += relaxDistance;
+ }
+
+ }
+ return force;
+
+
+}
+
+
+
+//------------------------------------------------------------------------------------------------
+void Wheel::CalculateRenderingSpinUpRateBase(float topSpeedKmh)
+{
+ // mDpRenderingSpinUpSpeedThreshold;
+
+ // at what rad/s is the wheel going % of top speed
+
+ float topspeedmps = topSpeedKmh / 3.6f;
+
+ float thresholdmps = topspeedmps * mDpRenderingSpinUpSpeedThresholdPercentage;
+
+ float linearDistancePerRev = rmt::PI * mRadius * 2.0f;
+
+ // rev/s = rev/dist * dist/s
+
+ // ! fuck you idiot
+ // we need rads not revs
+ float linearDistancePerRad = linearDistancePerRev / (2.0f * rmt::PI);
+
+
+ mRenderingSpinUpRateBase = (1.0f / linearDistancePerRad) * thresholdmps;
+
+
+
+}
+
+
+//------------------------------------------------------------------------
+void Wheel::CalculateRotAngle(float dt)
+{
+ if(mVehicle->mEBrake > 0.0f && mWheelInCollision)
+ {
+ // now, if no gas, don't rotate
+ if(mVehicle->mGas == 0.0f && this->mDriveWheel == true)
+ {
+ mRotAngle = 0.0f;
+ return;
+ }
+
+ }
+
+
+ float speedAlongFacing;
+
+ rmt::Vector wheelDirection = mVehicle->mVehicleFacing;
+
+ if(mSteerWheel)
+ {
+ rmt::Matrix steeringMatrix; // TODO - make this a class member?
+ steeringMatrix.Identity();
+ steeringMatrix.FillRotateY(mWheelTurnAngle);
+
+ wheelDirection.Transform(steeringMatrix);
+ }
+
+ speedAlongFacing = wheelDirection.DotProduct(mVehicle->mSuspensionPointVelocities[mWheelNum]);
+
+ //float linearDistance = speedAlongFacing * dt;
+
+ float linearDistancePerRev = rmt::PI * mRadius * 2.0f;
+
+ float revPerTime = speedAlongFacing / linearDistancePerRev;
+
+ float radiansPerTime = revPerTime * 2.0f * rmt::PI;
+
+ // ?
+ // see if vehicle's doin' a burnout
+ if(mVehicle->mBurnoutLevel > 0.0f && mDriveWheel)
+ {
+ if(mVehicle->mGas > mVehicle->mBrake)
+ {
+ radiansPerTime = mRenderingSpinUpRateBase;
+ }
+ else
+ {
+ radiansPerTime = -1.0f * mRenderingSpinUpRateBase;
+ }
+ }
+
+
+ /*
+ if(mWheelState == WS_LOCOMOTIVE_SLIDE || mWheelState == WS_LOCOMOTIVE_FREE_SPIN)
+ {
+ if(radiansPerTime >= mRenderingSpinUpRate)
+ {
+ // kick out of thid mode and use the calculated value
+ //
+ // TODO - how to then easily tune the mRenderingSpinUpRateBase?
+ //
+ // should be able to calculate based on some % of top speed
+ // yeah - no fucking timer needed!
+
+ mWheelState = WS_NORMAL;
+
+ }
+ else
+ {
+ //mRotAngle *= 1.5f;
+ //otAngle = mRenderingSpinUpRate;
+ radiansPerTime = mRenderingSpinUpRate;
+ }
+
+ }
+ */
+
+ mRotAngle = radiansPerTime * dt;
+
+ //if(rmt::Fabs(radiansPerTime - (2.0f * rmt::PI)) < 0.1f)
+ //{
+ // int stophere = 1;
+ // }
+
+
+ // fucking hack to (temporarily?) fix wheel rot on ps2
+ /*
+ mTotalTime += dt;
+
+ if(mTotalTime > 30.0f) // value is in seconds
+ {
+ mTotalTime = 0.0f;
+ mCumulativeRot = 0.0f;
+ }
+ */
+
+ mCumulativeRot += mRotAngle;
+
+ if(mCumulativeRot > rmt::PI_2)
+ {
+ mCumulativeRot -= rmt::PI_2;
+
+ //safeguard - higly unlikely we'd get here...
+ if(mCumulativeRot > rmt::PI_2)
+ {
+ // still?
+ // something strange must have happened
+ mCumulativeRot = 0.0f;
+ }
+
+ }
+
+
+
+
+ // TODO
+ // what we might want to try here is always calculate the wheel rotation to render based on desired forces
+ // not the actual forces we apply.
+ //
+ // add booleans to the Cap methods?
+ //
+ // also need to throw in some consideration for braking - draw wheels locked.
+ //
+ // furthermore, this should be done before the vehicle PostUpdate where the pose drivers actually change the joint matrices
+
+
+}
+
+//=============================================================================
+// Wheel::SetYOffsetFromCurrentPosition
+//=============================================================================
+// Description:
+//
+// Parameters: (float yoffset)
+//
+// Return: float
+//
+// note - if we've bottomed out, this will return the overflow
+// amount that the suspension could not absorb
+// if we didn't bottom out it will return 0.0f;
+//
+//
+//=============================================================================
+float Wheel::SetYOffsetFromCurrentPosition(float yoffset)
+{
+
+ // the parameter mObjectSpaceYOffsetFromCurrentPosition is a bit verbose at this
+ // point since before doing wheel collision we set the wheels to the bottom of the
+ // suspension limit, so at this poitn the currentposition is always the same
+
+ if(yoffset > mObjectSpaceYOffsetFromCurrentPosition)
+ {
+ mObjectSpaceYOffsetFromCurrentPosition = yoffset;
+ }
+ // TODO - make sure this gets reset to 0.0 at the right place
+
+
+ // this call got propagated to us from the collision solver, so I think this is the place to set the flag
+ mWheelInCollision = true;
+
+ // temp
+ // TODO - remove
+ //return 0.0f;
+
+
+ if(mObjectSpaceYOffsetFromCurrentPosition > 2.0f * mLimit)
+ {
+ // suspension has bottomed out
+ //
+ // TODO - rename this method?
+ //
+ // TODO - double check this
+
+ float overflow = mObjectSpaceYOffsetFromCurrentPosition - (2.0f * mLimit);
+ mObjectSpaceYOffsetFromCurrentPosition = 2.0f * mLimit;
+
+ return overflow;
+ //return true;
+ }
+
+ // ??
+ // TODO
+ // is this ok?
+ // should be if we always started out at the bottom of the suspension limit
+ if(mObjectSpaceYOffsetFromCurrentPosition < 0.0f)
+ {
+ rAssert(0);
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+ }
+
+ return 0.0f;
+
+}
+
+
+//------------------------------------------------------------------------
+void Wheel::ResolveOffset()
+{
+ // we assume at this point anyone who wants to has called SetYOffsetFromCurrentPosition
+ mYOffset += mObjectSpaceYOffsetFromCurrentPosition;
+
+
+ // temp
+ // TODO - remove
+ //mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+ //return;
+
+ if(mYOffset > mLimit)
+ {
+ mYOffset = mLimit;
+ }
+ if(mYOffset < -mLimit)
+ {
+ mYOffset = -mLimit;
+ }
+
+ // !
+ //
+ // mYOffset is only of use internally to this class
+ //
+ // this is NOT the value other shit should use to move suspension points around and what have you.
+
+
+
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+ //mWheelInCollision = false; // TODO - is this method adequate!?
+
+}
+
+
+//------------------------------------------------------------------------
+float Wheel::GetYCorrectionValue()
+{
+ return mObjectSpaceYOffsetFromCurrentPosition;
+}
+
+
+
+
+//------------------------------------------------------------------------
+void Wheel::Reset()
+{
+ mYOffset = 0.0f;
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+
+ mRotAngle = 0.0f;
+
+ mCumulativeRot = 0.0f;
+ mTotalTime = 0.0f;
+
+ mWheelInCollision = false;
+ mWheelBottomedOutThisFrame = false;
+
+ mWheelState = WS_NORMAL;
+
+ mWheelRenderSpeedRadPerSecond = 0.0f;
+
+}
+
+
+
+
+
diff --git a/game/code/worldsim/redbrick/wheel.h b/game/code/worldsim/redbrick/wheel.h
new file mode 100644
index 0000000..832a052
--- /dev/null
+++ b/game/code/worldsim/redbrick/wheel.h
@@ -0,0 +1,107 @@
+/*===========================================================================
+ wheel.h
+
+ created Jan 29, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _WHEEL_H
+#define _WHEEL_H
+
+#include <radmath/radmath.hpp>
+
+#include <worldsim/redbrick/vehicle.h>
+
+class SuspensionJointDriver;
+
+enum WheelState{ WS_NORMAL, WS_SLIP, WS_LOCOMOTIVE_SLIDE, WS_LOCOMOTIVE_FREE_SPIN };
+
+class Wheel
+{
+private:
+
+ friend class SuspensionJointDriver;
+ friend class Vehicle;
+ friend class PhysicsLocomotion;
+ friend class GeometryVehicle; // everyone's friends with the Wheel :)
+ friend class RedBrickCollisionSolverAgent;
+
+
+ Wheel();
+ ~Wheel();
+
+ Vehicle* mVehicle;
+
+ // TODO - initialize k and c from vehicle too...easier to tune?
+ void Init(Vehicle* vehicle, int wheelNum, float radius, bool steer, bool drive);
+ void Reset();
+
+ void SetDesignerParams(Vehicle::DesignerParams* dp);
+
+ // this will check the one we're passing in against the one stored
+ float SetYOffsetFromCurrentPosition(float yoffset);
+ void ResolveOffset(); // modify mYOffset by the final thing left in mObjectSpaceYOffsetFromCurrentPosition
+
+ float GetYCorrectionValue();
+
+ float CalculateSuspensionForce(float suspensionPointYVelocity, float dt);
+ void CalculateRenderingSpinUpRateBase(float topSpeedKmh);
+
+ bool mSteerWheel;
+ bool mDriveWheel;
+
+ int mWheelNum;
+ float mRadius;
+ float mObjectSpaceYOffsetFromCurrentPosition; // set frame to frame by collision agent
+
+
+ float mYOffset; // object space offset from suspension rest point
+
+ void CalculateRotAngle(float dt);
+ float mRotAngle;
+ float mCumulativeRot;
+ float mTotalTime;
+
+ float mLimit;
+ float mk; // spring constant
+ float mc; // damper constant
+
+ float mqk; // new test - quadratic spring
+
+ bool mWheelInCollision;
+ bool mWheelBottomedOutThisFrame;
+
+ float mSpringRelaxRate;
+
+ float mWheelTurnAngle;
+
+
+
+ WheelState mWheelState;
+
+ float mWheelRenderSpeedRadPerSecond;
+
+ float mRenderingSpinUpRate;
+ float mRenderingSpinUpRateBase;
+
+
+ //---------------------
+ // designer parameters:
+ //---------------------
+
+ float mDpSuspensionLimit; // multiplier on radius
+ float mDpSpringk;
+ float mDpDamperc; // normalized values
+
+ float mDpRenderingSpinUpSpeedThresholdPercentage;
+
+
+};
+
+
+#endif // _WHEEL_H \ No newline at end of file
diff --git a/game/code/worldsim/skidmarks/SkidMarkGenerator.cpp b/game/code/worldsim/skidmarks/SkidMarkGenerator.cpp
new file mode 100644
index 0000000..df7d860
--- /dev/null
+++ b/game/code/worldsim/skidmarks/SkidMarkGenerator.cpp
@@ -0,0 +1,272 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidMarkGenerator
+//
+// Description: A per-vehicle class that generates skidmarks
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <stdlib.h>
+
+#include <worldsim/SkidMarks/SkidMarkGenerator.h>
+#include <worldsim/SkidMarks/skidmarkmanager.h>
+#include <worldsim/SkidMarks/skidmark.h>
+
+#include <radmath/util.hpp>
+#include <float.h>
+#include <debug/profiler.h>
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#ifdef RAD_RELEASE
+const float SKID_INTENSITY_DAMPENING = 0.5f;
+#else
+static float SKID_INTENSITY_DAMPENING = 0.5f;
+#endif
+
+
+SkidMarkGenerator::SkidMarkGenerator()
+{
+#ifndef RAD_RELEASE
+ static bool s_AddedDebugging = false;
+ if ( s_AddedDebugging == false )
+ {
+ radDbgWatchAddFloat( &SKID_INTENSITY_DAMPENING, "Skid mark dampening", "Skidmarks" );
+ s_AddedDebugging = true;
+ }
+#endif
+}
+
+SkidMarkGenerator::~SkidMarkGenerator()
+{
+ for(int i = 0; i < 4; i++)
+ {
+ if(m_CurrentSkidData[i].currentSkid)
+ {
+ GetSkidmarkManager()->ReturnUsedSkidmark( m_CurrentSkidData[i].currentSkid );
+ }
+ m_CurrentSkidData[i].currentSkid = NULL;
+ }
+}
+
+// Height in meters to raise the skids above the ground to avoid z fighting
+
+// Keep pointers to the tShaders
+static tShader* spPavementShader = NULL;
+static tShader* spDirtShader = NULL;
+static tShader* spGrassShader = NULL;
+
+// The shader names for use in searching for skid mark shaders in the inventory
+const char* PAVEMENT_SKIDMARK_SHADERNAME = "skid_m";
+const char* DIRT_SKIDMARK_SHADERNAME = "dirt_skidmark_shader";
+const char* GRASS_SKIDMARK_SHADERNAME = "grass_skidmark_shader";
+
+// enables z test, disables z test, required for correct
+// skid mark appearance
+static void SetShaderSkidSettings( tShader* pShader )
+{
+ pShader->SetInt(PDDI_SP_ALPHAOP1, PDDI_BLEND_ALPHA);
+ pShader->SetInt(PDDI_SP_TEXTUREOP1, PDDI_TEXBLEND_MODULATE);
+ pShader->SetInt(PDDI_SP_COLOUROP1, PDDI_BLEND_SUBTRACT);
+}
+
+// Find the shaders in the inventory, set zwrite/ztest (since the artists can't
+// export these shader settings directly and addref them
+// SkidMarkGenerator::ReleaseShaders() must be called afterwards
+void SkidMarkGenerator::InitShaders()
+{
+
+ // Just so that we never do an accidental double addref
+ ReleaseShaders();
+
+ spPavementShader = p3d::find< tShader >( PAVEMENT_SKIDMARK_SHADERNAME );
+ if ( spPavementShader != NULL )
+ {
+ spPavementShader->AddRef();
+ SetShaderSkidSettings( spPavementShader );
+ }
+ rTuneWarningMsg( spPavementShader != NULL, "Pavement skid shader not found in inventory! Some skids will not appear!");
+
+ spDirtShader = p3d::find< tShader >( DIRT_SKIDMARK_SHADERNAME );
+ if ( spDirtShader != NULL )
+ {
+ spDirtShader->AddRef();
+ SetShaderSkidSettings( spDirtShader );
+ }
+ rTuneWarningMsg( spDirtShader != NULL, "Dirt skid shader not found in inventory! Some skids will not appear!");
+
+ spGrassShader = p3d::find< tShader >( GRASS_SKIDMARK_SHADERNAME );
+ if ( spGrassShader != NULL )
+ {
+ spGrassShader->AddRef();
+ SetShaderSkidSettings( spGrassShader );
+ }
+
+ rTuneWarningMsg( spGrassShader != NULL, "Grass skid shader not found in inventory! Some skids will not appear!");
+
+
+}
+// Fetch the appropriate shader for the terrain type
+tShader* SkidMarkGenerator::GetShader( eTerrainType terrainType )
+{
+ tShader* shader = NULL;
+
+ switch (terrainType)
+ {
+ case TT_Dirt:
+ shader = spDirtShader;
+ break;
+ case TT_Grass:
+ shader = spGrassShader;
+ break;
+ case TT_Road:
+ default:
+ shader = spPavementShader;
+ break;
+ };
+ return shader;
+}
+
+
+// Releases all the shaders that were addrefed in InitShaders()
+void SkidMarkGenerator::ReleaseShaders()
+{
+ if ( spPavementShader != NULL )
+ {
+ spPavementShader->Release();
+ spPavementShader = NULL;
+ }
+ if ( spDirtShader != NULL )
+ {
+ spDirtShader->Release();
+ spDirtShader = NULL;
+ }
+ if ( spGrassShader != NULL )
+ {
+ spGrassShader->Release();
+ spGrassShader = NULL;
+ }
+}
+
+
+
+
+void SkidMarkGenerator::GenerateSkid( int wheel, const SkidMarkGenerator::SkidData& skidData )
+{
+ if ( skidData.velocityDirection.MagnitudeSqr() < 0.01f ||
+ skidData.intensity < 0.01f )
+ return;
+
+
+ float dampenedIntensity = skidData.intensity * SKID_INTENSITY_DAMPENING;
+
+
+ rAssert( wheel >=0 && wheel < 4 );
+ TireGeneratorData& generatorData = m_CurrentSkidData[ wheel ];
+ // Is the tire currently skidding?
+ // if so, continue it
+ // otherwise start a new skid
+
+ rmt::Vector alignedOffset;
+ skidData.transform.RotateVector( generatorData.offset, &alignedOffset );
+ rmt::Vector skidCenter = skidData.transform.Row(3) + alignedOffset;
+
+ rmt::Vector skidDirection = skidData.velocityDirection;
+ skidDirection.Normalize();
+
+ // Check to see if the terrain type changed
+ if ( generatorData.currentSkid != NULL )
+ {
+ const float ANGLE_TOO_SHARP = 0.7f;
+ bool angleTooSharp = generatorData.currentSkid->GetCurrentDirection().Dot( skidDirection ) < ANGLE_TOO_SHARP;
+ bool terrainTypeChanged = generatorData.terrainType != skidData.terrainType;
+
+ if ( angleTooSharp || terrainTypeChanged )
+ {
+ // Finish off the current skid and start a new one
+ generatorData.currentSkid->FadeOutTrailingVertices();
+ GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
+ generatorData.currentSkid = NULL;
+ GenerateSkid( wheel, skidData );
+ }
+ }
+ // Check to see if
+
+ if ( generatorData.currentSkid != NULL )
+ {
+ // Is there space left for more vertices?
+ if ( generatorData.currentSkid->SpaceLeft() )
+ {
+ generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
+ generatorData.skidExtendedThisFrame = true;
+ }
+ else
+ {
+ // No space left. We must continue this skid with a new skidmark
+ Skidmark* skidMark = GetSkidmarkManager()->GetUnusedSkidmark();
+ if ( skidMark != NULL )
+ {
+ generatorData.currentSkid->ContinueSkidmark( skidMark );
+ GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
+ generatorData.currentSkid = skidMark;
+ generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
+ generatorData.skidExtendedThisFrame = true;
+ }
+ else
+ {
+ // No space available!
+ // Tell the current skid to fade out, we are done. Pretend the skid is finished
+ generatorData.currentSkid->FadeOutTrailingVertices();
+ GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
+ generatorData.currentSkid = NULL;
+ generatorData.skidExtendedThisFrame = false;
+ }
+ }
+ }
+ else
+ {
+ // Fetch a skidmark thats not being used right now
+ generatorData.currentSkid = GetSkidmarkManager()->GetUnusedSkidmark();
+ if ( generatorData.currentSkid != NULL )
+ {
+ generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
+ generatorData.skidExtendedThisFrame = true;
+ generatorData.terrainType = skidData.terrainType;
+ generatorData.currentSkid->SetShader( GetShader( skidData.terrainType ) );
+ generatorData.currentSkid->FadeInVertices();
+ }
+ }
+}
+
+
+
+void
+SkidMarkGenerator::Update( )
+{
+ for ( int i = 0 ; i < 4 ; i++ )
+ {
+ if ( m_CurrentSkidData[i].skidExtendedThisFrame == false)
+ {
+ if ( m_CurrentSkidData[i].currentSkid != NULL )
+ {
+ m_CurrentSkidData[i].currentSkid->FadeOutTrailingVertices();
+ GetSkidmarkManager()->ReturnUsedSkidmark( m_CurrentSkidData[i].currentSkid );
+ m_CurrentSkidData[i].currentSkid = NULL;
+ }
+ }
+ else
+ {
+ m_CurrentSkidData[i].skidExtendedThisFrame = false;
+ }
+ }
+}
+
+
+
+
diff --git a/game/code/worldsim/skidmarks/SkidMarkGenerator.h b/game/code/worldsim/skidmarks/SkidMarkGenerator.h
new file mode 100644
index 0000000..ebea931
--- /dev/null
+++ b/game/code/worldsim/skidmarks/SkidMarkGenerator.h
@@ -0,0 +1,187 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidMarkGenerator
+//
+// Description: A per-vehicle class that generates skidmarks
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef SKIDMARKGENERATOR_H
+#define SKIDMARKGENERATOR_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/IntersectManager/IntersectManager.h>
+
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Skidmark;
+class tShader;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+//
+//
+//
+//
+// Constraints:
+//
+//===========================================================================
+
+
+
+class SkidMarkGenerator
+{
+ public:
+ // Tries and finds the shaders the skidmarks in the inventory, retrieves
+ // them so that skidmarkgenerator doesn't need to call p3d::find<>
+ // and also sets the zwrite/ztest properties
+ static void InitShaders();
+ // Releases the shaders that were addrefed in initshaders
+ static void ReleaseShaders();
+
+ public:
+ SkidMarkGenerator();
+ ~SkidMarkGenerator();
+
+ enum Constants
+ {
+ MAX_NUM_WHEELS = 4
+ };
+
+ // Data for a single tire
+ struct TireData
+ {
+ // Tire dimensions as they would look when pressed evenly flat against the road
+ // A tank tread, or a snowboard, for instance would be much longer than it is wide
+ float width;
+ float length;
+ // offset of the tire, relative to the vehicle center
+ rmt::Vector offset;
+ };
+
+ struct SkidData
+ {
+ SkidData() : groundPlaneNormal( 0 , 1 ,0 ) { }
+
+ // Direction that the vehicle is moving
+ rmt::Vector velocityDirection;
+ // Orientation and position of the vehicle
+ rmt::Matrix transform;
+ float intensity; // 1 most intense, 0 nonexistent skid
+ // Ground plane normal, used to raise the skid mark over the ground
+ // to avoid Z figthing.
+ // Constraint : this variable must be normalized before use
+ rmt::Vector groundPlaneNormal;
+
+ // Terrain type underneath the tire. Used to determine which texture to use.
+ eTerrainType terrainType;
+ };
+
+ // Generate a skidmark, starts a new strip if necessary
+
+
+ /*
+ ^
+ |
+ |
+ |
+ +Z
+ |
+ |
+ front
+ _____________
+ wheel 2 | | wheel 3
+ | |
+ | |
+ | |
+ | |
+ | |
+ | | ---+X----->
+ | |
+ | |
+ | |
+ | |
+ | |
+ wheel 1 ------------- wheel 0
+ rear
+
+ */
+
+ // Sets the translation offset for the specified wheel relative to the
+ // vehicle's origin
+ void SetWheelOffset( int wheel, const rmt::Vector& offset )
+ {
+ m_CurrentSkidData[ wheel ].offset = offset;
+ }
+
+ // Sets the length and width of the wheel as it presses against the ground
+ // Determines the dimensions of the skid
+ void SetWheelDimensions( int wheel, float width, float length )
+ {
+ m_CurrentSkidData[ wheel ].halfWidth = width / 2.0f;
+ }
+ void GenerateSkid( int wheel, const SkidData& );
+
+ void Update( );
+
+ protected:
+
+ private:
+
+ tShader* GetShader( eTerrainType terrainType );
+ void LayDownVertices( int wheel, const SkidMarkGenerator::SkidData& skidData );
+
+ struct TireGeneratorData
+ {
+ TireGeneratorData():
+ halfWidth( 0.25f ),
+ currentSkid( NULL ),
+ skidExtendedThisFrame( false ),
+ offset( 0,0,0 ),
+ wasLastVertexLaidTemporary( false ),
+ terrainType( TT_NumTerrainTypes )
+ {
+
+ }
+ float halfWidth;
+ Skidmark* currentSkid;
+ bool skidExtendedThisFrame;
+ rmt::Vector offset;
+ bool wasLastVertexLaidTemporary;
+ eTerrainType terrainType;
+ };
+
+ TireGeneratorData m_CurrentSkidData[4];
+
+
+
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow SkidMarkGenerator from being copied and assigned.
+ SkidMarkGenerator( const SkidMarkGenerator& );
+ SkidMarkGenerator& operator=( const SkidMarkGenerator& );
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/skidmarks/allskidmarks.cpp b/game/code/worldsim/skidmarks/allskidmarks.cpp
new file mode 100644
index 0000000..e178a4a
--- /dev/null
+++ b/game/code/worldsim/skidmarks/allskidmarks.cpp
@@ -0,0 +1,3 @@
+#include <worldsim/SkidMarks/SkidMarkGenerator.cpp>
+#include <worldsim/SkidMarks/SkidMarkmanager.cpp>
+#include <worldsim/skidmarks/skidmark.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/skidmarks/skidmark.cpp b/game/code/worldsim/skidmarks/skidmark.cpp
new file mode 100644
index 0000000..8cfd239
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmark.cpp
@@ -0,0 +1,442 @@
+#include <worldsim/SkidMarks/skidmark.h>
+#include <camera/supercammanager.h>
+#include <debug/profiler.h>
+
+const float METERS_PER_TEX_UNIT = 0.5f;
+const float TEX_UNITS_PER_METER = 1.0f / METERS_PER_TEX_UNIT;
+
+
+
+Skidmark::Skidmark():
+m_NumSegments( 0 ),
+mpShader( NULL ),
+m_IsInDSG( false ),
+m_LastU( 0.0f ),
+m_TextureDirection( 1.0f ),
+m_FadedOut( false ),
+m_FadingIn( false )
+{
+ // Skidmarks are always translucent
+ mTranslucent = true;
+ SetName( "Skidmark" );
+}
+
+Skidmark::~Skidmark()
+{
+ if ( mpShader != NULL )
+ {
+ mpShader->Release();
+ mpShader = NULL;
+ }
+ if ( m_IsInDSG )
+ {
+ RemoveFromDSG();
+ m_IsInDSG = false;
+ }
+}
+
+bool Skidmark::IsVisible()const
+{
+ if ( m_NumSegments < 2 || m_FadedOut )
+ return false;
+
+ rmt::Sphere boundingSphere = m_BoundingBox.GetBoundingSphere();
+
+ if (GetSuperCamManager()->GetSCC( 0 )->GetCamera()->SphereVisible( boundingSphere.centre, boundingSphere.radius ) )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void Skidmark::Display()
+{
+ if ( m_NumSegments < 2 || mpShader == NULL )
+ return;
+
+ BEGIN_PROFILE("Skidmark::Display")
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+
+ pddiPrimStream* pStream = p3d::pddi->BeginPrims( mpShader->GetShader(), PDDI_PRIM_TRISTRIP, PDDI_V_CT, m_NumSegments * 2 );
+ for ( int i = 0 ; i < m_NumSegments ; i++ )
+ {
+ tColour colour( m_Segments[i].intensity, m_Segments[i].intensity, m_Segments[i].intensity, m_Segments[i].intensity );
+ pStream->Vertex( &m_Segments[ i ].left.position, colour, &m_Segments[ i ].left.uv );
+ pStream->Vertex( &m_Segments[ i ].right.position, colour, &m_Segments[ i ].right.uv );
+ }
+ p3d::pddi->EndPrims( pStream );
+
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ END_PROFILE("Skidmark::Display")
+}
+
+void Skidmark::Extend( const rmt::Vector& position, const rmt::Vector& forward, const rmt::Vector groundNormal, float halfWidth, float intensity )
+{
+ const float Z_FIGHTING_OFFSET = 0.1f;
+
+ rAssert( SpaceLeft () );
+ rmt::Vector elevatedPosition = position + (groundNormal * Z_FIGHTING_OFFSET);
+
+
+
+ rmt::Box3D oldBoundingBox;
+ GetBoundingBox( &oldBoundingBox );
+
+ m_CurrentDirection = forward;
+
+ unsigned char ucIntensity = static_cast< unsigned char >( intensity * 255.0f );
+
+ // if vertices have been laid already
+ // find texture break position
+ // it is on the straight line vector from the last position to the current one
+ // extend the last two vertices laid to the texture break position
+ // the texture break position is recorded as the last position written
+ // lay down two more at the current (given position)
+ if ( m_NumSegments > 0 )
+ {
+ // Check to see that we have travelled the minimum distance from the last
+ // point that we laid down a segment
+ if ( ( elevatedPosition - m_LastPositionWritten ).MagnitudeSqr() < 0.05f )
+ return;
+
+ rmt::Vector toPosition = elevatedPosition - m_LastPositionWritten;
+ toPosition.Normalize();
+ rmt::Vector right;
+ right.CrossProduct( toPosition, groundNormal );
+ right.Normalize();
+
+ rmt::Vector textureBreakPosition;
+ float breakPointU;
+ float distanceToTextureBreakPoint;
+ float distanceToGivenPoint;
+ if ( FindTextureBreakPosition( elevatedPosition, &textureBreakPosition, &breakPointU, &distanceToTextureBreakPoint, &distanceToGivenPoint ) )
+ {
+ float distanceFromStart = distanceToTextureBreakPoint + m_Segments[ m_NumSegments - 1].distanceFromStart;
+ // ExtendVertices( textureBreakPosition, right, halfWidth, ucIntensity, breakPointU, distanceFromStart );
+ m_LastPositionWritten = textureBreakPosition;
+ // Reverse texture direction
+ m_TextureDirection *= -1.0f;
+ // Calculate the texture coordinate of the elevated position
+ // u = distance( tbp, ep ) * TEX_UNITS_PER_METER * texturedirection
+ //float distance = (textureBreakPosition - elevatedPosition).Magnitude();
+ float currentU = breakPointU + distanceToTextureBreakPoint * TEX_UNITS_PER_METER * m_TextureDirection;
+ if ( currentU > 1.0f )
+ currentU = 1.0f;
+ else if ( currentU < 0 )
+ currentU = 0;
+ m_LastU = currentU;
+
+ distanceFromStart = distanceToGivenPoint + m_Segments[ m_NumSegments - 1].distanceFromStart;
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ m_LastPositionWritten = elevatedPosition;
+ }
+ else
+ {
+ // We haven't crossed a texture break point boundary
+ // extend the last two vertices written to the current position
+ // then write two more vertices, also at the current position for use in
+ // the next extension
+ float distance = (m_LastPositionWritten - elevatedPosition).Magnitude();
+
+ float currentU = m_LastU + distance * TEX_UNITS_PER_METER * m_TextureDirection;
+
+ float distanceFromStart = m_Segments[ m_NumSegments - 1 ].distanceFromStart + distance;
+
+ ExtendVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ m_LastPositionWritten = elevatedPosition;
+ m_LastU = currentU;
+ }
+
+ }
+ else
+ {
+ rmt::Vector right;
+ right.CrossProduct( forward, groundNormal );
+ right.Normalize();
+
+ // Brand new strip
+ // write 4 vertices, first two are fixed, 2nd two are temporaries
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, 0, 0 );
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, 0, 0 );
+ m_TextureDirection = 1.0f;
+ m_LastU = 0;
+ m_LastPositionWritten = elevatedPosition;
+ }
+ // Toss it in the dsg if its not there already
+ if ( m_IsInDSG == false )
+ {
+ AddToDSG( RenderEnums::LevelSlot );
+ }
+ else
+ {
+ MoveInDSG( oldBoundingBox );
+ }
+
+}
+
+void Skidmark::ClearVertices()
+{
+ // Reset the bounding box
+
+ // Which direction are we laying our coordinates, either 1.0 or -1.0
+ m_LastU = 0.0f;
+
+ RemoveFromDSG();
+ m_NumSegments = 0;
+ m_FadedOut = false;
+ m_FadingIn = false;
+
+ m_BoundingBox.low = rmt::Vector(FLT_MAX,FLT_MAX,FLT_MAX);
+ m_BoundingBox.high = rmt::Vector(-FLT_MAX,-FLT_MAX,-FLT_MAX);
+
+}
+
+void Skidmark::ContinueSkidmark( Skidmark* newSkidmark )
+{
+ tRefCounted::Assign( newSkidmark->mpShader, mpShader );
+ newSkidmark->ClearVertices();
+ newSkidmark->m_LastPositionWritten = m_LastPositionWritten;
+ newSkidmark->m_Segments[ 0 ] = m_Segments[ m_NumSegments - 1 ];
+ newSkidmark->m_Segments[ 1 ] = m_Segments[ m_NumSegments - 2 ];
+ newSkidmark->m_NumSegments = 2;
+ newSkidmark->m_LastU = m_LastU;
+ newSkidmark->m_TextureDirection = m_TextureDirection;
+ newSkidmark->m_CurrentDirection = m_CurrentDirection;
+ rmt::Box3D oldBox;
+ GetBoundingBox( &oldBox );
+
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 0 ].left.position );
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 0 ].right.position );
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 1 ].left.position );
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 1 ].right.position );
+
+ m_NumSegments--;
+
+}
+
+void Skidmark::FadeInVertices()
+{
+ const float DISTANCE_TO_FADE_IN = 1.0f;
+
+ // We want to fade in the last few vertices, but we don't want to make the fade greater in intensity than
+ // existing ones, so check before writing new values
+
+ if ( m_NumSegments < 1 )
+ return;
+
+ for ( int i = 0 ; i < m_NumSegments ; i++ )
+ {
+ int intensity = static_cast< int >( m_Segments[ i ].distanceFromStart / DISTANCE_TO_FADE_IN * 255.0f );
+ if ( m_Segments[ i ].intensity > intensity )
+ m_Segments[ i ].intensity = intensity;
+ }
+
+}
+
+void Skidmark::FadeOutTrailingVertices()
+{
+ const float DISTANCE_TO_FADE_OUT = 1.0f;
+
+ rAssert(m_NumSegments != 0); // happened to me once, probably bad, you can click
+ // through okay now, but we might want to fix it at some point
+ // nbrooke, apr 11, 2003
+ if(m_NumSegments == 0)
+ return;
+
+ // We want to fade out the last few vertices, but we don't want to make the fade greater in intensity than
+ // existing ones, so check before writing new values
+
+ // Last segment is zero
+ // next to last is -1.dist - this.dist
+
+ float distFadedOutSoFar = 0;
+ m_Segments[ m_NumSegments - 1 ].intensity = 0;
+ int intensity = 0;
+
+ for ( int i = m_NumSegments - 2 ; i >= 0 ; i-- )
+ {
+ distFadedOutSoFar += m_Segments[ i + 1 ].distanceFromStart - m_Segments[ i ].distanceFromStart;
+ intensity = static_cast< int >( distFadedOutSoFar / DISTANCE_TO_FADE_OUT * 255.0f );
+ if ( m_Segments[ i ].intensity > intensity )
+ m_Segments[ i ].intensity = intensity;
+ }
+
+}
+
+void Skidmark::FadeOut( float deltaAlpha )
+{
+ int iDeltaAlpha = static_cast< unsigned char > ( deltaAlpha * 255.0f );
+
+ bool stillVisible = false;
+ for ( int i = 0 ; i < m_NumSegments ; i++ )
+ {
+ int newAlpha = m_Segments[i].intensity - iDeltaAlpha;
+ if ( newAlpha <= 0 )
+ {
+ newAlpha = 0;
+ }
+ else
+ {
+ stillVisible = true;
+ }
+ m_Segments[ i ].intensity = newAlpha;
+ }
+ if ( stillVisible == false )
+ m_FadedOut = true;
+}
+
+void Skidmark::AddToDSG( RenderEnums::LayerEnum renderLayer )
+{
+ if ( m_IsInDSG )
+ RemoveFromDSG();
+
+ m_RenderLayer = renderLayer;
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Add( this );
+ m_IsInDSG = true;
+
+}
+
+void Skidmark::RemoveFromDSG()
+{
+
+ if ( m_IsInDSG )
+ {
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Remove( this );
+ m_IsInDSG = false;
+ }
+}
+
+void Skidmark::SetShader( tShader* pShader )
+{
+ tRefCounted::Assign( mpShader, pShader );
+}
+
+void Skidmark::WriteVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& right,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distanceFromStart )
+{
+
+
+ rmt::Vector leftVertex = elevatedPosition - (right * halfWidth);
+ rmt::Vector rightVertex = elevatedPosition + (right * halfWidth);
+
+ // Increase bounding box
+ m_BoundingBox.Expand( rightVertex );
+ m_BoundingBox.Expand( leftVertex );
+
+
+ // Set position data
+ m_Segments[ m_NumSegments ].left.position = leftVertex;
+ m_Segments[ m_NumSegments ].right.position = rightVertex;
+
+
+ // Intensity
+ m_Segments[ m_NumSegments ].intensity = intensity;
+
+ // Tex coordinates
+ m_Segments[ m_NumSegments ].left.uv = pddiVector2( 0.0f, u );
+ m_Segments[ m_NumSegments ].right.uv = pddiVector2( 1.0f, u );
+
+ m_Segments[ m_NumSegments ].distanceFromStart = distanceFromStart;
+
+ m_NumSegments++;
+}
+
+void Skidmark::ExtendVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& right,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distance )
+{
+
+ rmt::Vector leftVertex = elevatedPosition - (right * halfWidth);
+ rmt::Vector rightVertex = elevatedPosition + (right * halfWidth);
+
+ // Increase bounding box
+ m_BoundingBox.Expand( rightVertex );
+ m_BoundingBox.Expand( leftVertex );
+
+ // Set position data
+ m_Segments[ m_NumSegments - 1 ].left.position = leftVertex;
+ m_Segments[ m_NumSegments - 1 ].right.position = rightVertex;
+
+ // Intensity
+ m_Segments[ m_NumSegments - 1 ].intensity = intensity;
+
+ // Tex coordinates
+ m_Segments[ m_NumSegments -1 ].left.uv = pddiVector2( 0.0f, u );
+ m_Segments[ m_NumSegments -1 ].right.uv = pddiVector2( 1.0f, u );
+
+ m_Segments[ m_NumSegments - 1].distanceFromStart = distance;
+}
+
+bool Skidmark::FindTextureBreakPosition( const rmt::Vector& position, rmt::Vector* texBreakPosition, float* u, float* distanceToTextureBreakPoint, float* distanceToGivenPoint )
+{
+ // Calculate the point on the vector from the last vertex written
+ rmt::Vector toPosition = position - m_LastPositionWritten;
+ toPosition.Normalize();
+ float deltaU;
+ float breakTexCoordinate;
+ if ( m_TextureDirection > 0 )
+ {
+ deltaU = 1.0f - m_LastU;
+ breakTexCoordinate = 1.0f;
+ }
+ else
+ {
+ deltaU = m_LastU - 0.0f;
+ breakTexCoordinate = 0.0f;
+ }
+
+ float distance = METERS_PER_TEX_UNIT * deltaU;
+ // Check to see that the break position is actually between the last position and
+ // the given position
+ *texBreakPosition = m_LastPositionWritten + toPosition * distance;
+ *u = breakTexCoordinate;
+
+ float distanceToBreakPointSqr = ( *texBreakPosition - m_LastPositionWritten ).MagnitudeSqr();
+ float distanceToGivenPointSqr = ( position - m_LastPositionWritten ).MagnitudeSqr();
+ if ( distanceToGivenPointSqr > distanceToBreakPointSqr )
+ {
+ *distanceToTextureBreakPoint = rmt::Sqrt ( distanceToBreakPointSqr );
+ *distanceToGivenPoint = rmt::Sqrt ( distanceToGivenPointSqr );
+ return true;
+ }
+ else
+ return false;
+
+}
+
+void Skidmark::MoveInDSG( rmt::Box3D& oldBox )
+{
+ if ( m_IsInDSG )
+ {
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+ }
+}
+
+
diff --git a/game/code/worldsim/skidmarks/skidmark.h b/game/code/worldsim/skidmarks/skidmark.h
new file mode 100644
index 0000000..5803913
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmark.h
@@ -0,0 +1,85 @@
+#ifndef SKIDMARK_H
+#define SKIDMARK_H
+
+#include <render/DSG/StaticEntityDSG.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/RenderManager/RenderManager.h>
+
+
+// A contigous skidmark
+class Skidmark : public StaticEntityDSG
+{
+public:
+ Skidmark();
+ ~Skidmark();
+ virtual void Display();
+ virtual void GetBoundingBox( rmt::Box3D* box ){ *box = m_BoundingBox; }
+ virtual void GetBoundingSphere( rmt::Sphere* sphere ) { *sphere = m_BoundingBox.GetBoundingSphere(); }
+ bool SpaceLeft()const { return (NUM_SEGMENTS - m_NumSegments) >= 3; }
+ bool IsVisible()const;
+ void Extend( const rmt::Vector& position, const rmt::Vector& forward, const rmt::Vector groundNormal, float halfWidth, float intensity );
+ void ClearVertices();
+ // Continue a skidmark by copying the last two vertices + texture information to the new
+ // skidmark
+ void ContinueSkidmark( Skidmark* newSkidmark );
+ void FadeInVertices();
+ void FadeOutTrailingVertices();
+ void FadeOut( float deltaAlpha );
+ void AddToDSG( RenderEnums::LayerEnum renderLayer );
+ void RemoveFromDSG();
+ bool IsInDSG()const{ return m_IsInDSG; }
+ void SetShader( tShader* pShader );
+ void SetFadeIn( bool useFadeIn ){ m_FadingIn = useFadeIn; }
+ const rmt::Vector& GetCurrentDirection()const { return m_CurrentDirection; }
+
+private:
+
+ void WriteVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& rightVector,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distance );
+
+ void ExtendVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& rightVector,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distance );
+ void MoveInDSG( rmt::Box3D& oldBox );
+
+
+ bool FindTextureBreakPosition( const rmt::Vector& position, rmt::Vector* texBreakPosition, float* u, float* distanceToTBP, float* distanceToGivenPoint );
+
+ struct Vertex
+ {
+ pddiVector position;
+ pddiVector2 uv;
+ };
+
+ struct Segment
+ {
+ Vertex left,right;
+ unsigned char intensity;
+ float distanceFromStart;
+ };
+ enum { NUM_SEGMENTS = 33 };
+ int m_NumSegments;
+ Segment m_Segments[ NUM_SEGMENTS ];
+
+ tShader* mpShader;
+ rmt::Box3D m_BoundingBox;
+ bool m_IsInDSG;
+ RenderEnums::LayerEnum m_RenderLayer;
+ // The midpoint of the last position written, needed for texture coordinate generation
+ rmt::Vector m_LastPositionWritten;
+ float m_LastU;
+ float m_TextureDirection;
+ bool m_FadedOut;
+ // Is this the start of the skid? If so, fade it in
+ bool m_FadingIn;
+ rmt::Vector m_CurrentDirection;
+};
+
+#endif
diff --git a/game/code/worldsim/skidmarks/skidmarkmanager.cpp b/game/code/worldsim/skidmarks/skidmarkmanager.cpp
new file mode 100644
index 0000000..d1e5391
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmarkmanager.cpp
@@ -0,0 +1,178 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidmarkManager
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <worldsim/SkidMarks/skidmarkmanager.h>
+#include <memory/srrmemory.h>
+#include <worldsim/SkidMarks/skidmark.h>
+#include <radtime.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float ALPHA_FADE_PER_MILLISECOND = 0.001f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+SkidmarkManager* SkidmarkManager::spInstance = NULL;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+SkidmarkManager::ManagedSkidmark::ManagedSkidmark():
+age( 0 ),
+inUse( false )
+{
+ skidmark = new Skidmark();
+ skidmark->AddRef();
+}
+
+SkidmarkManager::ManagedSkidmark::~ManagedSkidmark()
+{
+ skidmark->RemoveFromDSG();
+ skidmark->Release();
+}
+
+
+SkidmarkManager::SkidmarkManager():
+m_SkidMarks( NULL ),
+m_UseTimedFadeouts( false )
+{
+
+}
+
+SkidmarkManager::~SkidmarkManager()
+{
+ Destroy();
+}
+
+
+
+SkidmarkManager* SkidmarkManager::CreateInstance()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ rAssert( spInstance == NULL );
+ spInstance = new SkidmarkManager();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ return spInstance;
+}
+
+SkidmarkManager* SkidmarkManager::GetInstance()
+{
+ return spInstance;
+}
+
+void SkidmarkManager::DestroyInstance()
+{
+ delete spInstance;
+}
+
+void SkidmarkManager::Update( unsigned int timeInMS )
+{
+
+ const unsigned int TIME_TO_WAIT_BEFORE_FADE = 5000;
+ unsigned int currentTime = radTimeGetMilliseconds();
+ if ( m_UseTimedFadeouts )
+ {
+
+ float deltaAlpha = static_cast< float >( timeInMS ) * ALPHA_FADE_PER_MILLISECOND;
+ // Iterate through all skidmarks, fading out the old ones that aren't in use
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++ )
+ {
+ if ( m_SkidMarks[i]->age + TIME_TO_WAIT_BEFORE_FADE < currentTime )
+ {
+ if ( m_SkidMarks[i]->skidmark->IsInDSG() && m_SkidMarks[i]->inUse == false )
+ {
+ m_SkidMarks[i]->skidmark->FadeOut( deltaAlpha );
+ }
+ }
+ }
+ }
+}
+
+Skidmark* SkidmarkManager::GetUnusedSkidmark()
+{
+
+ Skidmark* availSkidmark = NULL;
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++ )
+ {
+ if ( m_SkidMarks[ i ]->inUse == false && m_SkidMarks[ i ]->skidmark->IsVisible() == false )
+ {
+ availSkidmark = m_SkidMarks[i]->skidmark;
+ availSkidmark->ClearVertices();
+ m_SkidMarks[ i ]->inUse = true;
+
+ break;
+ }
+ }
+ return availSkidmark;
+}
+
+void SkidmarkManager::ReturnUsedSkidmark( Skidmark* skidmark )
+{
+ rAssert(m_SkidMarks);
+ if(!m_SkidMarks)
+ {
+ return;
+ }
+
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++ )
+ {
+ if ( m_SkidMarks[ i ]->skidmark == skidmark )
+ {
+ rAssert( m_SkidMarks[i]->inUse == true );
+ m_SkidMarks[ i ]->inUse = false;
+ m_SkidMarks[ i ]->age = radTimeGetMilliseconds();
+ break;
+ }
+ }
+}
+
+void SkidmarkManager::Init( int numSkids )
+{
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ rAssert( m_SkidMarks == NULL );
+ m_NumSkidMarks = numSkids;
+ rAssert( m_SkidMarks == NULL );
+ m_SkidMarks = new ManagedSkidmark*[ numSkids ];
+
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++)
+ {
+ m_SkidMarks[i] = new ManagedSkidmark();
+ }
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+void SkidmarkManager::Destroy()
+{
+
+ if ( m_SkidMarks != NULL )
+ {
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++)
+ {
+ delete m_SkidMarks[i];
+ m_SkidMarks[i] = NULL;
+ }
+ delete [] m_SkidMarks;
+ m_SkidMarks = NULL;
+ }
+
+}
+
diff --git a/game/code/worldsim/skidmarks/skidmarkmanager.h b/game/code/worldsim/skidmarks/skidmarkmanager.h
new file mode 100644
index 0000000..057be54
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmarkmanager.h
@@ -0,0 +1,94 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidmarkManager
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef SKIDMARKMANAGER_H
+#define SKIDMARKMANAGER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Skidmark;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+const int DEFAULT_NUM_SKID_MARKS = 30;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+//
+//===========================================================================
+class SkidmarkManager
+{
+ public:
+
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the TriStripDSGManager)
+ static SkidmarkManager* CreateInstance();
+ static SkidmarkManager* GetInstance();
+ static void DestroyInstance();
+ static SkidmarkManager* spInstance;
+
+ void Update( unsigned int timeInMS );
+ Skidmark* GetUnusedSkidmark();
+ void ReturnUsedSkidmark( Skidmark* );
+
+ void SetTimedFadeouts( bool useFadeouts ) { m_UseTimedFadeouts = useFadeouts; }
+ void Init( int numSkids = DEFAULT_NUM_SKID_MARKS );
+ void Destroy();
+
+ private:
+ SkidmarkManager();
+ ~SkidmarkManager();
+ SkidmarkManager( const SkidmarkManager& );
+ SkidmarkManager& operator=( const SkidmarkManager& );
+
+ struct ManagedSkidmark
+ {
+ ManagedSkidmark();
+ ~ManagedSkidmark();
+
+ Skidmark* skidmark;
+ unsigned int age;
+ bool inUse;
+ };
+
+ ManagedSkidmark** m_SkidMarks;
+ // Returns a preallocated skidmark thats not currently in use at the moment
+ bool m_UseTimedFadeouts;
+ int m_NumSkidMarks;
+
+};
+// A little syntactic sugar for getting at this singleton.
+inline SkidmarkManager* GetSkidmarkManager()
+{
+ return( SkidmarkManager::GetInstance() );
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/spawn/allspawn.cpp b/game/code/worldsim/spawn/allspawn.cpp
new file mode 100644
index 0000000..443b1d7
--- /dev/null
+++ b/game/code/worldsim/spawn/allspawn.cpp
@@ -0,0 +1 @@
+#include <worldsim/spawn/spawnmanager.cpp>
diff --git a/game/code/worldsim/spawn/spawnmanager.cpp b/game/code/worldsim/spawn/spawnmanager.cpp
new file mode 100644
index 0000000..357a791
--- /dev/null
+++ b/game/code/worldsim/spawn/spawnmanager.cpp
@@ -0,0 +1,107 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: spawnmanager.cpp
+//
+// Description: SpawnManager Class Implementation
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#include <worldsim/spawn/spawnmanager.h>
+
+void SpawnManager::Init()
+{
+ //////////////////////////////////////////////////////////////////
+ // Sanity check subclass implementations
+ rAssert( GetAbsoluteMaxObjects() >= 0 );
+ rAssert( GetMaxModels() >= 0 );
+ rAssert( GetSecondsBetwAdds() >= 0.0f );
+ rAssert( GetSecondsBetwRemoves() >= 0.0f );
+ rAssert( GetSecondsBetwUpdates() >= 0.0f );
+
+ mSecondsSinceLastAdd = 0.0f;
+ mSecondsSinceLastRemove = 0.0f;
+ mSecondsSinceLastUpdate = 0.0f;
+
+ mAddEnabled = true;
+ mRemoveEnabled = true;
+ mUpdateEnabled = true;
+}
+
+
+void SpawnManager::Update( float seconds )
+{
+ rAssert( seconds >= 0.0f );
+
+ if( !IsActive() )
+ {
+ return;
+ }
+
+ ///////////////////////////////////////////////////////
+ // Do some local sanity checks
+ rAssert( mSpawnRadius >= 0.0f );
+ rAssert( mRemoveRadius >= 0.0f );
+
+ ///////////////////////////////////////////////////////
+ // Do some enforcement of subclass implementations
+ rAssert( 0 <= mNumObjects && mNumObjects <= GetAbsoluteMaxObjects() );
+ rAssert( 0 <= GetMaxObjects() && GetMaxObjects() <= GetAbsoluteMaxObjects() );
+ rAssert( 0 < GetNumRegisteredModels() && GetNumRegisteredModels() <= GetMaxModels() );
+
+ ///////////////////////////////////////////////////////
+ // Do things in this order:
+ // - Remove everything that's outside the radius
+ // - Add at the fringes of the radius
+ // - Update as necessary.
+
+ /////////////////
+ // REMOVE PHASE
+ float secondsBetwRemoves = GetSecondsBetwRemoves();
+ rAssert( secondsBetwRemoves >= 0.0f );
+ if( mRemoveEnabled && mSecondsSinceLastRemove >= secondsBetwRemoves )
+ {
+ mSecondsSinceLastRemove = 0.0f;
+ RemoveObjects( seconds );
+ }
+ else
+ {
+ mSecondsSinceLastRemove += seconds;
+ }
+ rAssert( 0 <= mNumObjects && mNumObjects <= GetAbsoluteMaxObjects() );
+ /////////////////
+
+ ////////////////
+ // ADD PHASE
+ float secondsBetwAdds = GetSecondsBetwAdds();
+ rAssert( secondsBetwAdds >= 0.0f );
+ if( mAddEnabled && mSecondsSinceLastAdd >= secondsBetwAdds )
+ {
+ mSecondsSinceLastAdd = 0.0f;
+ AddObjects( seconds );
+ }
+ else
+ {
+ mSecondsSinceLastAdd += seconds;
+ }
+ rAssert( 0 <= mNumObjects && mNumObjects <= GetAbsoluteMaxObjects() );
+ ////////////////
+
+ ////////////////
+ // UPDATE PHASE
+ float secondsBetwUpdates = GetSecondsBetwUpdates();
+ rAssert( secondsBetwUpdates >= 0.0f );
+ if( mUpdateEnabled && mSecondsSinceLastUpdate >= secondsBetwUpdates )
+ {
+ mSecondsSinceLastUpdate = 0.0f;
+ UpdateObjects( seconds );
+ }
+ else
+ {
+ mSecondsSinceLastUpdate += seconds;
+ }
+ rAssert( (0 <= mNumObjects) && (mNumObjects <= GetAbsoluteMaxObjects() ) );
+ ////////////////
+}
diff --git a/game/code/worldsim/spawn/spawnmanager.h b/game/code/worldsim/spawn/spawnmanager.h
new file mode 100644
index 0000000..4a9ccc4
--- /dev/null
+++ b/game/code/worldsim/spawn/spawnmanager.h
@@ -0,0 +1,154 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: spawnmanager.h
+//
+// Description: SpawnManager Class Declaration
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef SPAWNMANAGER_H
+#define SPAWNMANAGER_H
+
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp>
+
+class SpawnManager
+{
+public:
+
+ SpawnManager();
+ virtual ~SpawnManager();
+ void Update( float seconds ); // don't allow overwriting of Update, which enforces structure
+
+ // PURE VIRTUALS
+ virtual void Init();
+ virtual void ClearAllObjects() = 0;
+ virtual void ClearObjectsInsideRadius( rmt::Vector center, float radius ) = 0;
+ virtual void ClearObjectsOutsideRadius( rmt::Vector center, float radius ) = 0;
+ virtual int GetAbsoluteMaxObjects() const = 0;
+ virtual int GetMaxObjects() const = 0;
+ virtual void SetMaxObjects( int maxObjects ) = 0;
+ virtual int GetMaxModels() const = 0;
+ virtual int GetNumRegisteredModels() const = 0;
+ virtual bool RegisterModel( const char* name, int spawnFreq ) = 0;
+ virtual bool UnregisterModel( const char* name ) = 0;
+
+ // ACCESSORS
+ bool IsActive() const;
+
+ // turning on will make use of current enable flags (all default to true)
+ // turning off will not doing anything (no adding, removing, updating whatsoever)
+ void SetActive( bool activeFlag );
+
+ void EnableAdd( bool enable );
+ void EnableRemove( bool enable );
+ void EnableUpdate( bool enable );
+
+ float GetSpawnRadius() const;
+ void SetSpawnRadius( float radius );
+ float GetRemoveRadius() const;
+ void SetRemoveRadius( float radius );
+
+ int GetNumObjects() const;
+
+
+protected:
+
+ // PURE VIRTUALS
+ // return # objects added/removed
+ virtual void AddObjects( float seconds ) = 0;
+ virtual void RemoveObjects( float seconds ) = 0;
+ virtual void UpdateObjects( float seconds ) = 0;
+
+ // subclass accessors
+ virtual float GetSecondsBetwAdds() const = 0;
+ virtual float GetSecondsBetwRemoves() const = 0;
+ virtual float GetSecondsBetwUpdates() const = 0;
+
+
+ bool mIsActive;
+ float mSpawnRadius; // in meters
+ float mRemoveRadius; // in meters
+ // Betw 0 and AbsoluteMax (defined by subclass)...
+ // NOTE: Not betw 0 and mMaxObjects because mMaxObjects can be changed
+ // to a lower number while there are mNumObjects > mMaxObjects
+ // still within radius (they don't get removed till an explicit
+ // call to ClearObjectsOutsideRadius takes place)
+ int mNumObjects;
+
+ float mSecondsSinceLastAdd;
+ float mSecondsSinceLastRemove;
+ float mSecondsSinceLastUpdate;
+
+ bool mAddEnabled;
+ bool mRemoveEnabled;
+ bool mUpdateEnabled;
+private:
+
+};
+
+inline SpawnManager::SpawnManager() :
+mIsActive( true ),
+mSpawnRadius( -1.0f ),
+mRemoveRadius( -1.0f ),
+mNumObjects( 0 ),
+mSecondsSinceLastAdd( 0.0f ),
+mSecondsSinceLastRemove( 0.0f ),
+mSecondsSinceLastUpdate( 0.0f ),
+mAddEnabled( true ),
+mRemoveEnabled( true ),
+mUpdateEnabled( true )
+{
+}
+
+inline SpawnManager::~SpawnManager()
+{
+}
+
+inline bool SpawnManager::IsActive() const
+{
+ return mIsActive;
+}
+inline void SpawnManager::SetActive( bool activeFlag )
+{
+ mIsActive = activeFlag;
+}
+inline float SpawnManager::GetSpawnRadius() const
+{
+ return mSpawnRadius;
+}
+inline void SpawnManager::SetSpawnRadius( float radius )
+{
+ rAssert( radius >= 0.0f );
+ mSpawnRadius = radius;
+}
+inline float SpawnManager::GetRemoveRadius() const
+{
+ return mRemoveRadius;
+}
+inline void SpawnManager::SetRemoveRadius( float radius )
+{
+ rAssert( radius >= 0.0f );
+ mRemoveRadius = radius;
+}
+inline int SpawnManager::GetNumObjects() const
+{
+ return mNumObjects;
+}
+inline void SpawnManager::EnableAdd( bool enable )
+{
+ mAddEnabled = enable;
+}
+inline void SpawnManager::EnableRemove( bool enable )
+{
+ mRemoveEnabled = enable;
+}
+inline void SpawnManager::EnableUpdate( bool enable )
+{
+ mUpdateEnabled = enable;
+}
+
+#endif
diff --git a/game/code/worldsim/traffic/alltraffic.cpp b/game/code/worldsim/traffic/alltraffic.cpp
new file mode 100644
index 0000000..f75d366
--- /dev/null
+++ b/game/code/worldsim/traffic/alltraffic.cpp
@@ -0,0 +1 @@
+#include <worldsim/traffic/trafficmanager.cpp>
diff --git a/game/code/worldsim/traffic/trafficmanager.cpp b/game/code/worldsim/traffic/trafficmanager.cpp
new file mode 100644
index 0000000..45ae929
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficmanager.cpp
@@ -0,0 +1,2169 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TrafficManager.cpp
+//
+// Description: Implement TrafficManager
+//
+// History: 09/09/2002 + Spawning/Removing vehicles -- Dusit Eakkachaichanvet
+// 04/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <stdlib.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/gameplaymanager.h>
+
+#include <camera/supercammanager.h>
+#include <worldsim/traffic/TrafficManager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <memory/srrmemory.h>
+#include <debug/profiler.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <ai/vehicle/trafficai.h>
+
+#include <roads/roadmanager.h>
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/roadsegmentdata.h>
+#include <roads/lane.h>
+#include <roads/intersection.h>
+#include <roads/roadrendertest.h>
+
+#include <render/Culling/ReserveArray.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+TrafficManager* TrafficManager::mInstance = NULL;
+
+//#define TRAFFIC_TEST
+#ifdef TRAFFIC_TEST
+ const float TrafficManager::FADE_RADIUS = 10.0f;
+ const float TrafficManager::ADD_RADIUS = 30.0f;
+ const float TrafficManager::CENTER_OFFSET = 20.0f;
+ const float TrafficManager::REMOVE_RADIUS = 35.0f;
+ const float TrafficManager::INITIAL_ADD_RADIUS = 20.0f;
+#else
+ const float TrafficManager::FADE_RADIUS = 85.0;
+ const float TrafficManager::CENTER_OFFSET = 40.0f;
+ const float TrafficManager::ADD_RADIUS = 65.0f;
+ const float TrafficManager::REMOVE_RADIUS = 75.0f;
+ const float TrafficManager::INITIAL_ADD_RADIUS = 100.0f;
+#endif
+
+const unsigned int TrafficManager::MILLISECONDS_BETWEEN_REMOVE = 200;
+const unsigned int TrafficManager::MILLISECONDS_BETWEEN_ADD = 200;
+const unsigned int TrafficManager::MILLISECONDS_POPULATE_WORLD = 3000;
+
+const unsigned int MILLISECONDS_STUNNED_AFTER_DEACTIVATED = 3000;
+
+const int MAX_TRAFFIC = 5;
+
+// Define all the swatch colours here...
+TrafficManager::SwatchColour TrafficManager::sSwatchColours[] =
+{
+ {148, 45, 12},
+ {133, 124, 4},
+ {110, 84, 145},
+ {170, 43, 74},
+ {48, 11, 102},
+
+ {110, 30, 32},
+ {140, 125, 207},
+ {195, 98, 31},
+ {122, 50, 69},
+ {148, 191, 229},
+
+ {0, 159, 123},
+ {0, 75, 132},
+ {43, 177, 166},
+ {250, 166, 23},
+ {172, 81, 127},
+
+ {185, 162, 232},
+ {229, 222, 33},
+ {163, 203, 60},
+ {213, 142, 210},
+ {255, 239, 158},
+
+ {122, 43, 103},
+ {181, 133, 70},
+ {68, 106, 171},
+ {59, 149, 36},
+ {205, 94, 0}
+};
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+void TrafficManager::InitDefaultModelGroups()
+{
+ for( int i=0; i<TrafficManager::MAX_TRAFFIC_MODEL_GROUPS; i++ )
+ {
+ // WORKING ONES
+ //mTrafficModelGroups[i].AddTrafficModel( "minivanA", 5 );
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "compactA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "pickupA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "sportsA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "SUVA", 1 );
+ */
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "coffin", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "hallo",1 );
+ mTrafficModelGroups[i].AddTrafficModel( "ship", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "witchcar", 1 );
+ */
+
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "sportsB", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "wagonA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "minivanA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "taxiA", 2 );
+ */
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "sedanA", 3 );
+ mTrafficModelGroups[i].AddTrafficModel( "sedanB", 2 );
+ */
+ //mTrafficModelGroups[i].AddTrafficModel( "ambul", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "burnsarm", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "fishtruc", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "garbage", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "icecream", 5 );
+
+ //mTrafficModelGroups[i].AddTrafficModel( "IStruck", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "nuctruck", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "pizza", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "schoolbu", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "votetruc", 5 );
+
+ // NOT TESTED
+ //mTrafficModelGroups[i].AddTrafficModel( "compactA", 5 );
+
+ // DEFAULTS
+ mTrafficModelGroups[i].AddTrafficModel( "compactA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "pickupA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "sportsA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "SUVA", 2 );
+ }
+ mCurrTrafficModelGroup = 0;
+}
+
+
+ITrafficSpawnController* TrafficManager::GetSpawnController()
+{
+ return (ITrafficSpawnController*) TrafficManager::GetInstance();
+}
+
+
+TrafficManager* TrafficManager::GetInstance()
+{
+ MEMTRACK_PUSH_GROUP( "Traffic Manager" );
+ if ( !mInstance )
+ {
+ mInstance = new(GMA_LEVEL_OTHER) TrafficManager();
+ }
+ MEMTRACK_POP_GROUP( "Traffic Manager" );
+ return mInstance;
+}
+
+void TrafficManager::DestroyInstance()
+{
+ delete mInstance;
+ mInstance = NULL;
+}
+
+void TrafficManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ if( id == EVENT_VEHICLE_DESTROYED )
+ {
+ // ignore it if it's not one of our vehicles...
+ Vehicle* v = (Vehicle*) pEventData;
+ SwapInTrafficHusk( v );
+ }
+
+ else if( id == EVENT_REPAIR_CAR )
+ {
+ //
+ // NOTE: When we want to support other cars than the user car
+ // picking up Wrench/repair icons in the main game, we should pass in
+ // the vehicle pointer when we trigger this event.
+ //
+ Vehicle* currVehicle = GetGameplayManager()->GetCurrentVehicle();
+ if( currVehicle == NULL )
+ {
+ return; // no current vehicle to repair.. oh well...
+ }
+ TrafficVehicle* tv = FindTrafficVehicle( currVehicle );
+ if( tv == NULL )
+ {
+ return; // not one of ours, don't worry...
+ }
+
+
+ // repair this vehicle..
+ if( currVehicle->mVehicleDestroyed )
+ {
+ bool usingHusk = false;
+ Vehicle* husk = tv->GetHusk();
+ if( husk )
+ {
+ usingHusk = true;
+ // damage is extensive... gotta replace husk with original vehicle
+
+ // obtain info from the husk
+ rmt::Vector initPos, initDir;
+ husk->GetPosition( &initPos );
+ initDir = husk->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // remove husk
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( husk );
+ rAssert( succeeded );
+ /*
+ GetVehicleCentral()->SetVehicleController( tv->mActiveListIndex, NULL );
+ tv->mActiveListIndex = -1;
+ */
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( husk );
+ // restore fade alpha if we set it, so other vehicles don't get confused
+ husk->mGeometryVehicle->SetFadeAlpha( 255 );
+ husk->Release();
+ husk = NULL;
+ tv->SetHusk( NULL );
+ tv->SetHasHusk( false );
+
+ currVehicle->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ currVehicle->SetResetFacingInRadians( angle );
+ currVehicle->Reset();
+ //currVehicle->SetLocomotion( VL_PHYSICS );
+
+ }
+
+ // put in original vehicle
+ int res = GetVehicleCentral()->AddVehicleToActiveList( currVehicle );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ //tv->mActiveListIndex = res;
+ //GetEventManager()->TriggerEvent( EVENT_TRAFFIC_SPAWN, currVehicle ); // tell sound
+
+ if( usingHusk )
+ {
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar );
+
+ // if the avatar is inside a vehicle the vehicle
+ // is probably a husk, update this vehicle to be the original
+ // vehicle and place the character in this new vehicle
+ //
+ if( avatar->IsInCar() )
+ {
+ rAssert( avatar->GetVehicle() );
+ rAssert( GetVehicleCentral()->mHuskPool.IsHuskType( avatar->GetVehicle()->mVehicleID ) );
+
+ avatar->SetVehicle( currVehicle );
+
+ Character* character = avatar->GetCharacter();
+ GetAvatarManager()->PutCharacterInCar( character, currVehicle );
+ }
+ }
+
+ // fire off event so Esan can know when we switch the vehicle on him.
+ GetEventManager()->TriggerEvent( EVENT_VEHICLE_DESTROYED_SYNC_SOUND, currVehicle );
+
+ }
+ // repair any damage to original vehicle
+ bool resetDamage = true;
+ currVehicle->ResetFlagsOnly( resetDamage );
+ }
+
+ // If the player honks his horn, we trigger horn honking too for nearby traffic
+ else if( id == EVENT_PLAYER_VEHICLE_HORN )
+ {
+ // if queue for the last time we honked horn hasn't been cleared yet,
+ // don't do more...
+ if( mQueuedTrafficHorns.mUseSize > 0 )
+ {
+ return;
+ }
+
+ Vehicle* playerVehicle = (Vehicle*) pEventData;
+ rAssert( playerVehicle );
+
+ rmt::Vector playerPos;
+ playerVehicle->GetPosition( &playerPos );
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ if( mQueuedTrafficHorns.mUseSize >= MAX_QUEUED_TRAFFIC_HORNS )
+ {
+ break;
+ }
+
+ rmt::Vector traffPos;
+ traffV->GetVehicle()->GetPosition( &traffPos );
+
+ // within n meters; make sure n is less than traffic remove radius
+ const float CLOSE_ENOUGH_TO_HONK_BACK_SQR = 625.0f;
+
+ float distSqr = (traffPos - playerPos).MagnitudeSqr();
+ if( distSqr < CLOSE_ENOUGH_TO_HONK_BACK_SQR )
+ {
+ int coinflip = rand() % 10; // 1 in n chance of honking back
+ if( coinflip == 0 )
+ {
+ int delayInMilliseconds = rand() % 300 + 500;
+
+ TrafficHornQueue tmp;
+ tmp.delayInMilliseconds = static_cast<unsigned int>( delayInMilliseconds );
+ tmp.vehicle = traffV->GetVehicle();
+
+ mQueuedTrafficHorns.Add( tmp );
+ }
+ }
+ }
+
+ }
+}
+
+
+
+void TrafficManager::Init()
+{
+ //////////////////////////////////////////
+ // Do normal cleaning up...
+ Cleanup();
+
+ //////////////////////////////////////////
+ // Initialize some members
+ mNumTraffic = 0;
+ mMillisecondsBetweenRemove = TrafficManager::MILLISECONDS_BETWEEN_REMOVE;
+ mMillisecondsBetweenAdd = 0;
+ mMillisecondsPopulateWorld = TrafficManager::MILLISECONDS_POPULATE_WORLD;
+ mDesiredTrafficSpeedKph = DetermineDesiredSpeedKph();
+
+ Vehicle* newV = NULL; // vehicle
+ Vehicle* newH = NULL; // vehicle husk
+
+ //////////////////////////////////////////
+ // Initialize Traffic cars
+ for( int i=0 ; i<MAX_TRAFFIC ; i++ )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ newV = InitRandomVehicle();
+ rAssert( newV != NULL );
+ newV->AddRef();
+ newV->SetLocomotion( VL_TRAFFIC );
+ newV->mTrafficVehicle = tv;
+
+ tv->SetVehicle( newV );
+ tv->SetHusk( NULL );
+ }
+
+#ifdef DEBUGWATCH
+ char nameSpace[64];
+ sprintf( nameSpace, "Traffic Manager" );
+
+ radDbgWatchAddFloat( &mDesiredTrafficSpeedKph,
+ "Global Traffic Target Speed",
+ nameSpace,
+ NULL,
+ NULL,
+ 10.0f,
+ 200.0f );
+#endif
+
+}
+
+
+void TrafficManager::ClearOutOfSightTraffic()
+{
+ static const unsigned int MAX_MILLISECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL = 5000;
+
+ Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ // if the player is using this vehicle, goddammit don't remove the thing
+ if( traffV->GetVehicle() == playerVehicle )
+ {
+ continue;
+ }
+
+ if( traffV->mOutOfSight &&
+ traffV->mMillisecondsOutOfSight > MAX_MILLISECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL )
+ {
+ RemoveTraffic( i ); // remove this car
+ }
+ }
+}
+
+
+void TrafficManager::UpdateQueuedTrafficHorns( unsigned int milliseconds )
+{
+ // update our queue of traffic horns
+ int i=0;
+ while( i < mQueuedTrafficHorns.mUseSize )
+ {
+ if( mQueuedTrafficHorns[i].delayInMilliseconds <= milliseconds )
+ {
+ // trigger the event already, sheesh!
+ GetEventManager()->TriggerEvent( EVENT_TRAFFIC_HORN,
+ mQueuedTrafficHorns[i].vehicle );
+ mQueuedTrafficHorns.Remove( i );
+ }
+ else
+ {
+ mQueuedTrafficHorns[i].delayInMilliseconds -= milliseconds;
+ i++;
+ }
+ }
+}
+
+
+
+// ******************************
+//
+// BIG UPDATE METHOD
+//
+// ******************************
+
+void TrafficManager::Update( unsigned int milliseconds )
+{
+ rAssert( 0 <= mNumTraffic && mNumTraffic <= MAX_TRAFFIC );
+
+BEGIN_PROFILE( "Traffic Man" );
+
+ if( mMillisecondsPopulateWorld > milliseconds )
+ {
+ mMillisecondsPopulateWorld -= milliseconds;
+ }
+ else
+ {
+ mMillisecondsPopulateWorld = 0;
+ }
+
+ // play the queued up traffic horn sounds, if the time is right
+ UpdateQueuedTrafficHorns( milliseconds );
+
+ //
+ // ====================================
+ // Getting information about the player
+ // ====================================
+ //
+
+ Vehicle* v = NULL;
+
+ rmt::Vector pPos, pVel;//, pDir;
+ float pSpeed;
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ // Avatar::GetPosition() will return position of vehicle if avatar inside
+ // vehicle. Same deal with Avatar::GetVelocity() & GetSpeedMps()
+ // Remember that we should use VELOCITY rather than facing because
+ // the player can face one way and travel in reverse.
+ //
+ avatar->GetPosition(pPos);
+ avatar->GetVelocity(pVel);
+ pSpeed = avatar->GetSpeedMps();
+
+ // Get the camera for Player 1.
+ // It's what we need to apply the center offset to our spawn & remove radii
+ SuperCam* pCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rmt::Vector camTarget;
+ pCam->GetHeadingNormalized( &camTarget );
+ rAssert( rmt::Epsilon(camTarget.MagnitudeSqr(), 1.0f, 0.0005f) );
+
+ rmt::Vector center;
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ center = pPos;
+ }
+ else
+ {
+ center = pPos + camTarget * TrafficManager::CENTER_OFFSET;
+ }
+
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+ superCam->GetPosition( &pPos );
+ superCam->GetVelocity( &pVel );
+ pSpeed = pVel.Magnitude(); // *** SQUARE ROOT! ***
+ */
+ /*
+ if( pSpeed > 0.001f )
+ {
+ pDir = pVel;
+ pDir.Scale(1.0f / pSpeed);
+ }
+ else
+ {
+ avatar->GetHeading( pDir );
+ }
+ rAssert( rmt::Epsilon( pDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+ */
+
+ // for sphere intersection/containment testing
+ float minDistSqr = 0.0f;
+ rmt::Vector distVec;
+
+
+BEGIN_PROFILE( "Traffic Man: Remove" );
+
+ if( mMillisecondsBetweenRemove < milliseconds )
+ {
+ mMillisecondsBetweenRemove = MILLISECONDS_BETWEEN_REMOVE;
+
+ // don't remove if populating world
+ if( mMillisecondsPopulateWorld == 0 )
+ {
+ //
+ // ==================================
+ // Remove vehicles
+ // ===================================
+ // "Remove" from consideration vehicles that:
+ // - are no longer in the player's Traffic Radius, and/or
+ // - haven't been in player's view for some time
+ //
+ rmt::Sphere traffSphere;
+ traffSphere.Set( center, TrafficManager::REMOVE_RADIUS );
+ ClearTrafficOutsideSphere( traffSphere );
+
+ ClearOutOfSightTraffic();
+ }
+ }
+ else
+ {
+ mMillisecondsBetweenRemove -= milliseconds;
+ }
+
+END_PROFILE( "Traffic Man: Remove" );
+
+
+BEGIN_PROFILE( "Traffic Man: Add" );
+
+ //
+ // ==============================
+ // Adding cars to lanes as needed
+ // ==============================
+ // Imagine two overlapping circles. Circle1 is the traffic radius of your
+ // current position (center, radius), and Circle2 (center2, radius)
+ // is the traffic radius of your future position (based on velocity and
+ // assumption that it'll be just as long to reach the next loop as it took
+ // to reach the current loop).
+ //
+ if( mTrafficEnabled )
+ {
+
+ if( mMillisecondsBetweenAdd < milliseconds )
+ {
+ mMillisecondsBetweenAdd = MILLISECONDS_BETWEEN_ADD;
+
+ float addRadius = TrafficManager::ADD_RADIUS;
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ addRadius = TrafficManager::INITIAL_ADD_RADIUS;
+ }
+
+ rmt::Sphere pSphere( center, addRadius );
+
+ // FindRoadSegmentElems returns to us a list of RoadSegments whose bounding spheres
+ // come in contact with the player's traffic radius. These contact points give us
+ // the segments on the FRINGE of the traffic radius, where can we add cars.
+ //
+ // For each point of intersection, we only place down a car IF:
+ // a) the candidate car's predicted position in the NEXT frame lies within Circle 2
+ // (player's traffic zone in the NEXT frame)
+ // AND
+ // b) the lane containing this segment has fewer cars on it than the desired
+ // density value.
+ //
+ // The reason for FindRoadSegmentElems (which consequently forced us to build DSG
+ // Tree out of RoadSegments, involving a lot of work) is that a lane isn't a
+ // straight line. It's comprised of meandering segments. A lane can enter and exit
+ // the player's Traffic Radius as many times as it desires.
+ //
+ ReserveArray<RoadSegment*> orList;
+ ::GetIntersectManager()->FindRoadSegmentElems( center, addRadius, orList );
+
+ bool noMoreFreeTrafficVehicles = false;
+
+ for( int i=0; i<orList.mUseSize; i++ )
+ {
+ RoadSegment* segment;
+ unsigned int numLanes;
+
+ segment = orList.mpData[i];
+ rAssert( segment != NULL );
+
+ numLanes = segment->GetNumLanes();
+ rAssert( numLanes >= 1 );
+
+ // loop through all the lanes for this segment
+ for( unsigned int j=0; j<numLanes; j++ )
+ {
+
+ Road* road;
+ Lane* lane;
+ unsigned int nDesiredDensity;
+
+ road = segment->GetRoad();
+ rAssert( road != NULL );
+
+ lane = road->GetLane( j );
+ nDesiredDensity = lane->GetDensity();
+
+ // HACK:
+ // We only have 5 traffic cars man c'mon...
+ if(nDesiredDensity > 2 )
+ {
+ nDesiredDensity = 2;
+ }
+
+ // we add vehicle only if number on the lane < expected density
+ // AND if projected next position of the car is in the projected
+ // next position of the player's Traffic Radius
+
+ if( lane->mTrafficVehicles.mUseSize < (int)(nDesiredDensity) )
+ {
+ // Here we're determining where in the world to place the car.
+ // We try to place it at the lane location where it intersects
+ // with our traffic zone (pSphere).
+
+ rmt::Vector startPos, startDir, endPos, endDir;
+ segment->GetLaneLocation( 0.0f, j, startPos, startDir );
+ segment->GetLaneLocation( 1.0f, j, endPos, endDir );
+
+ rmt::Vector intPts[2];
+ int numIntersections = IntersectLineSphere( startPos, endPos, pSphere, intPts );
+ if(numIntersections <= 0)
+ {
+ continue;
+ }
+
+ rmt::Vector cPos, cDir;
+ cDir.Sub( endPos, startPos );
+ cDir.Normalize(); // *** SQUARE ROOT! ***
+
+ // for each intersection point found, plant a vehicle
+ for( int k=0; k<numIntersections; k++ )
+ {
+ // HACK:
+ // Designers want maxtraffic to be always 5 if you're on foot
+ // unless mMaxTraffic is actually 0, in which case, we leave at
+ // zero.
+ int maxTrafficToUse = mMaxTraffic;
+ if( mMaxTraffic > 0 && !avatar->IsInCar())
+ {
+ maxTrafficToUse = MAX_TRAFFIC_MODEL_GROUPS;
+ }
+
+ if( mNumTraffic < maxTrafficToUse )
+ {
+ // set cPos;
+ cPos = intPts[k];
+
+ // see if we got any more vehicles...
+ TrafficVehicle* tv = this->GetFreeTrafficVehicle();
+ if( tv == NULL )
+ {
+ noMoreFreeTrafficVehicles = true;
+ break;
+ }
+ v = tv->GetVehicle();
+ rAssert( v != NULL );
+
+
+ //
+ // We should detect if we're placing this car on top of another car
+ // We need to search the entire ActiveVehicles list, not just the
+ // traffic cars in our lane, to take into account vehicles that
+ // are lying around on the road in VL_PHYSICS
+ //
+ int nActiveVehicles = 0;
+ Vehicle** activeVehicles = NULL;
+
+ VehicleCentral* vc;
+ vc = ::GetVehicleCentral();
+ vc->GetActiveVehicleList( activeVehicles, nActiveVehicles );
+
+
+ rmt::Sphere cSphere;
+ v->GetBoundingSphere( &cSphere );
+
+
+ bool tryNextPoint = false;
+ int vCount = 0;
+ for( int i=0; i<vc->GetMaxActiveVehicles(); i++ )
+ {
+ if( vCount >= nActiveVehicles )
+ {
+ break;
+ }
+
+ Vehicle* aCar = activeVehicles[i];
+ if( aCar == NULL )
+ {
+ continue;
+ }
+ vCount++;
+
+ rmt::Vector aPos;
+ rmt::Sphere aSphere;
+ aCar->GetPosition( &aPos );
+ aCar->GetBoundingSphere( &aSphere );
+
+ float distSqr = (aPos - cPos).MagnitudeSqr();
+
+ // if same as our lane, make sure they're at least some
+ // lookahead distance away...
+ float minDist = 5.0f; // initial buffer...
+ if( aCar->mTrafficLocomotion->GetAILane() == lane )
+ {
+ /*
+ float lookAhead = aCar->mTrafficLocomotion->GetAISpeed() *
+ TrafficAI::SECONDS_LOOKAHEAD;
+ */
+ float lookAhead = aCar->mTrafficLocomotion->mActualSpeed *
+ TrafficAI::SECONDS_LOOKAHEAD;
+ if( lookAhead < TrafficAI::LOOKAHEAD_MIN )
+ {
+ lookAhead = TrafficAI::LOOKAHEAD_MIN;
+ }
+ minDist += lookAhead;
+ }
+ else
+ {
+ minDist += aSphere.radius + cSphere.radius;
+ }
+
+ float minDistSqr = minDist * minDist;
+ if( distSqr < minDistSqr )
+ {
+ // if we're too near another car, don't spawn here...
+ tryNextPoint = true;
+ break;
+ }
+ }
+ if( tryNextPoint )
+ {
+ continue;
+ }
+
+
+ //
+ // Check if in NEXT few seconds, vehicle will still be in
+ // player's traffic zone.
+ //
+ const float SECONDS_LOOK_AHEAD = 1.5f;
+
+ // next frame player pos & traffic zone
+ rmt::Vector center2 = center + pVel * SECONDS_LOOK_AHEAD;
+
+ float speedLimit = lane->GetSpeedLimit();
+ rmt::Vector cVel = cDir * speedLimit;
+ rmt::Vector cPos2 = cPos + cVel * SECONDS_LOOK_AHEAD;
+
+ minDistSqr = TrafficManager::REMOVE_RADIUS * TrafficManager::REMOVE_RADIUS;
+ distVec.Sub( cPos2, center2 );
+
+
+ if( distVec.MagnitudeSqr() < minDistSqr )
+ {
+
+ bool succeeded = this->AddTraffic(lane, tv);
+ if( !succeeded )
+ {
+ // can't add more traffic to this lane, do next lane
+ continue;
+ }
+
+
+ // Determine which point on the segment we are at...
+ // Since we know that cPos is on the line between startPos & endPos
+ // of the segment, we only need to test one coordinate (that didn't
+ // remain the same, of course)... This should give us a good enough
+ // approximation (within 0.00001)
+ //
+ float segmentT = GetLineSegmentT( startPos, endPos, cPos );
+ rAssert( 0.0f <= segmentT && segmentT <= 1.0f );
+
+ // INITIALIZE
+
+ // Get a random color for this vehicle
+ pddiColour randomColour;
+ GenerateRandomColour( randomColour );
+ v->mGeometryVehicle->SetTrafficBodyColour( randomColour );
+
+ // initialize vehicle's position & facing
+ v->SetInitialPosition( &cPos );
+ float angle = GetRotationAboutY( cDir.x, cDir.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+ v->mHijackedByUser = false;
+
+ // set up TrafficLocomotion/TrafficAI info
+ v->mTrafficLocomotion->Init();
+ v->mTrafficLocomotion->InitPos( cPos );
+ v->mTrafficLocomotion->InitFacing( cDir );
+ v->mTrafficLocomotion->InitVehicleAI( v );
+ v->mTrafficLocomotion->InitLane( lane, j, lane->GetSpeedLimit() );
+ v->mTrafficLocomotion->InitSegment( segment, segment->GetSegmentIndex(), segmentT );
+ v->mTrafficLocomotion->SetActive( true );
+
+ // determine initial speed for traffic.
+ // if far from road's end, go at desired speed
+ float speed = 0.0f;
+ rmt::Vector segEnd, segFacing;
+ RoadSegment* lastSeg = road->GetRoadSegment( road->GetNumRoadSegments()-1 );
+ lastSeg->GetLaneLocation( 1.0f, 0, segEnd, segFacing );
+ const float MIN_DIST_FROM_ROAD_END_SQR = 100.0f;
+ if( (cPos-segEnd).MagnitudeSqr() > MIN_DIST_FROM_ROAD_END_SQR )
+ {
+ speed = GetDesiredTrafficSpeed();
+ }
+ v->mTrafficLocomotion->SetAISpeed( speed );
+
+ v->mVehicleType = VT_TRAFFIC;
+ v->SetLocomotion(VL_TRAFFIC);
+
+ } // end if vehicle's next pos lies in radius of player's next traffic zone
+ }
+ }
+ if( noMoreFreeTrafficVehicles )
+ {
+ break;
+ }
+ } // end if(lane density < desired density)
+
+ } // end for-loop through all the lanes in a particular segment
+
+ if( noMoreFreeTrafficVehicles )
+ {
+ break;
+ }
+
+ } // end for-loop through all segments returned by DSGFind
+ }
+ else
+ {
+ mMillisecondsBetweenAdd -= milliseconds;
+ }
+ }
+
+END_PROFILE( "Traffic Man: Add" );
+
+BEGIN_PROFILE( "Traffic Man: Update AI & Intersections" );
+
+ //
+ // ============================================================================
+ // Go through the vehicles, updating
+ // ============================================================================
+ //
+
+ // at first, no intersection has been updated in this update call.
+ for( int i=0; i<MAX_INTERSECTIONS; i++ )
+ {
+ mpIntersections[i] = NULL;
+ }
+ int nIntersectionsUpdated = 0;
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ if( traffV->HasHusk() )
+ {
+ v = traffV->GetHusk();
+ }
+ else
+ {
+ v = traffV->GetVehicle();
+ }
+ rAssert( v );
+
+ // Test for out of sight of player 0... If so, increment timer, if not reset timer
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+ traffV->mOutOfSight = !GetGameplayManager()->TestPosInFrustrumOfPlayer( vPos, 0 );
+
+ traffV->mMillisecondsOutOfSight += milliseconds;
+ if( !traffV->mOutOfSight )
+ {
+ traffV->mMillisecondsOutOfSight = 0;
+ }
+
+ // Determine fade alpha.. All active (in the world) traffic vehicles
+ // fade. Whether or not they're in VL_TRAFFIC or VL_PHYSICS is irrelevant
+ SetVehicleFadeAlpha( v, pPos );
+
+ // figure out if we should allow entering the traffic vehicle
+ const float GETIN_SPEED_THRESHOLD_MPS = 4.5f; // this is as fast as character can run
+ if( !avatar->IsInCar() && !v->mHijackedByUser )
+ {
+ rAssert( v->mVehicleType != VT_AI );
+ if( v->mSpeed > GETIN_SPEED_THRESHOLD_MPS )
+ {
+ v->ActivateTriggers( false );
+ }
+ else
+ {
+ v->ActivateTriggers( true );
+ }
+ }
+
+ if( v->GetLocomotionType() == VL_PHYSICS && !v->mHijackedByUser )
+ {
+ traffV->mMillisecondsDeactivated += milliseconds;
+
+ // Check through vehicles VL_PHYSICS to see if we can bring any of them
+ // back to life:
+ // - check health
+ // - check distance from last segment
+ // - check if we've been deactivated long enough
+ AttemptResurrection( traffV );
+ }
+
+ if( v->GetLocomotionType() == VL_TRAFFIC )
+ {
+ // Update Vehicle AI
+ //v->mTrafficLocomotion->UpdateAI(milliseconds);
+
+ // Update intersection
+ UpdateIntersection( milliseconds, v, nIntersectionsUpdated );
+ }
+ }
+END_PROFILE( "Traffic Man: Update AI & Intersections" );
+
+
+
+END_PROFILE( "Traffic Man" );
+}
+
+void TrafficManager::ClearTrafficOutsideSphere( const rmt::Sphere& s )
+{
+ rmt::Vector distVec;
+ rmt::Vector vPos;
+ rmt::Sphere vSphere;
+ float minDistSqr = 0.0f;
+
+ Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();//GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ rAssert( traffV != NULL );
+
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ Vehicle* v = NULL;
+ if( traffV->HasHusk() )
+ {
+ v = traffV->GetHusk();
+ }
+ else
+ {
+ v = traffV->GetVehicle();
+ }
+ rAssert( v != NULL );
+
+ // if the player is driving this traffic vehicle, don't take it away from him!
+ if( traffV->GetVehicle() == playerVehicle ) // get original vehicle to compare, not husk
+ {
+ continue;
+ }
+
+ v->GetPosition( &vPos );
+
+ v->GetBoundingSphere( &vSphere );
+ vSphere.centre = vPos;
+
+ minDistSqr = s.radius * s.radius;
+ distVec.Sub( vSphere.centre, s.centre );
+ if( distVec.MagnitudeSqr() < minDistSqr )
+ {
+ // BAH! vehicle still in traffic zone... leave it alone
+ continue;
+ }
+
+ // At this point, we want to kill the vehicle
+ // because it is now outside our traffic zone
+ RemoveTraffic( i );
+ }
+}
+
+void TrafficManager::ClearTrafficInSphere( const rmt::Sphere& s )
+{
+ rmt::Vector distVec;
+ rmt::Vector vPos;
+ rmt::Sphere vSphere;
+ float minDistSqr = 0.0f;
+
+ Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();//GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ rAssert( traffV != NULL );
+
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ Vehicle* v = NULL;
+ if( traffV->HasHusk() )
+ {
+ v = traffV->GetHusk();
+ }
+ else
+ {
+ v = traffV->GetVehicle();
+ }
+ rAssert( v != NULL );
+
+ // if the player is driving this traffic vehicle, don't take it away from him!
+ if( traffV->GetVehicle() == playerVehicle )
+ {
+ continue;
+ }
+
+ v->GetPosition( &vPos );
+
+ v->GetBoundingSphere( &vSphere );
+ vSphere.centre = vPos;
+
+ minDistSqr = s.radius * s.radius;
+ distVec.Sub( vSphere.centre, s.centre );
+
+ // If vehicle still in sphere... kill it
+ if( distVec.MagnitudeSqr() < minDistSqr )
+ {
+ // At this point, we want to kill the vehicle
+ // because it is now outside our traffic zone
+ RemoveTraffic( i );
+ }
+ }
+}
+
+
+void TrafficManager::RemoveTraffic( Vehicle* vehicle )
+{
+ rAssert( vehicle != NULL );
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = NULL;
+ if( GetVehicleCentral()->mHuskPool.IsHuskType( vehicle->mVehicleID ) )
+ {
+ v = tv->GetHusk();
+ }
+ else
+ {
+ v = tv->GetVehicle();
+ }
+
+ // found it.
+ if( vehicle == v )
+ {
+ RemoveTrafficVehicle( tv );
+ }
+ }
+}
+
+
+void TrafficManager::EnableTraffic()
+{
+ if( !CommandLineOptions::Get(CLO_NO_TRAFFIC) && !DISABLETRAFFIC )
+ {
+ mTrafficEnabled = true;
+ }
+}
+
+void TrafficManager::DisableTraffic()
+{
+ mTrafficEnabled = false;
+}
+
+void TrafficManager::AddCharacterToStopFor( Character* character )
+{
+ rAssert( character != NULL );
+
+ bool assigned = false;
+
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ if( mCharactersToStopFor[i] == character )
+ {
+ mCharactersToStopFor[i]->Release();
+ mCharactersToStopFor[i] = NULL;
+ }
+ if( !assigned && mCharactersToStopFor[i] == NULL )
+ {
+ tRefCounted::Assign( mCharactersToStopFor[i], character );
+ assigned = true;
+ mNumCharsToStopFor++;
+ }
+ }
+}
+
+void TrafficManager::RemoveCharacterToStopFor( Character* character )
+{
+ rAssert( character != NULL );
+
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ if( mCharactersToStopFor[i] == character )
+ {
+ mCharactersToStopFor[i]->Release();
+ mCharactersToStopFor[i] = NULL;
+ mNumCharsToStopFor--;
+ }
+ }
+}
+
+void TrafficManager::GenerateRandomColour( pddiColour& colour )
+{
+ int alpha = 255;
+ int red, green, blue;
+
+ int index = rand() % TrafficManager::NUM_SWATCH_COLOURS;
+ red = sSwatchColours[index].red;
+ green = sSwatchColours[index].green;
+ blue = sSwatchColours[index].blue;
+ /*
+ red = rand() % 256;
+ green = rand() % 256;
+ blue = rand() % 256;
+ */
+ colour.Set( red, green, blue, alpha );
+}
+
+void TrafficManager::Deactivate( Vehicle* vehicle )
+{
+ rAssert( vehicle != NULL );
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = tv->GetVehicle();
+
+ // found it.
+ if( vehicle == v )
+ {
+ rAssert( v != NULL );
+ v->mTrafficLocomotion->SetActive( false );
+ //
+ // Remove the vehicle from the lane
+ Lane* lane = tv->GetLane();
+ if( lane )
+ {
+ for( int j=0; j<lane->mTrafficVehicles.mUseSize; j++ )
+ {
+ if( tv == lane->mTrafficVehicles[j] )
+ {
+ lane->mTrafficVehicles.Remove(j);
+ break;
+ }
+ }
+ }
+
+ // NOTE:
+ // hold off NULLing out the lane variable.
+ // We could use it in case we want to resurrect it
+ //tv->SetLane( NULL );
+
+ // NOTE:
+ // DO NOT SET TrafficVehicle::mIsActive TO FALSE HERE,
+ // We want traffic manager to consider it as being
+ // active still (in the sense that it's still in the world and is
+ // subject to removal). We just want to remove it from the lane and
+ // deactivate its AI...
+ //tv->SetIsActive( false );
+
+ // start the deactivation timer
+ tv->mMillisecondsDeactivated = 0;
+ tv->mCanBeResurrected = true;
+ }
+ }
+
+}
+
+void TrafficManager::SwapInTrafficHusk( Vehicle* vehicle )
+{
+ rAssert( vehicle );
+
+ TrafficVehicle* tv = FindTrafficVehicle( vehicle );
+ if( tv == NULL )
+ {
+ return;
+ }
+
+ // obtain info from the vehicle
+ rmt::Vector initPos, initDir;
+ vehicle->GetPosition( &initPos );
+ initDir = vehicle->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ //
+ // Deactivate traffic vehicle, take it out of the world, swap in a free husk...
+ // that sort of thing
+ //
+ Deactivate( vehicle );
+ bool succeeded = ::GetVehicleCentral()->RemoveVehicleFromActiveList( vehicle );
+ rAssert( succeeded );
+ //GetVehicleCentral()->SetVehicleController( tv->mActiveListIndex, NULL );
+ GetEventManager()->TriggerEvent( EVENT_TRAFFIC_REMOVE, vehicle );
+
+ //
+ // Now we grab husk and put it in place of the original vehicle
+ //
+ Vehicle* husk = InitRandomHusk( vehicle );
+ if( husk == NULL )
+ {
+ // by returning here, we have removed the traffic car from the
+ // manager, the lane, and world scene... But not entirely! (RemoveTrafficVehicle
+ // will be called on it when it goes out of range later...) Good job!
+ return;
+ }
+ int res = ::GetVehicleCentral()->AddVehicleToActiveList( husk );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ //tv->mActiveListIndex = res;
+ GetVehicleCentral()->SetVehicleController( res, husk->mTrafficLocomotion->GetAI() );
+
+
+ husk->AddRef();
+ husk->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ husk->SetResetFacingInRadians( angle );
+ husk->Reset();
+ husk->SetLocomotion( VL_PHYSICS );
+
+ /*
+ // tell the avatar that it's now in a husk
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ avatar->SetVehicle(husk);
+ */
+
+ tv->SetHusk( husk );
+
+ // tell the TrafficVehicle that it is destroyed
+ tv->SetHasHusk( true );
+
+}
+
+void TrafficManager::SetMaxTraffic( int n )
+{
+ if( n < 0 )
+ {
+ n = 0;
+ }
+ else if( n > MAX_TRAFFIC )
+ {
+ n = MAX_TRAFFIC;
+ }
+
+ mMaxTraffic = n;
+}
+
+int TrafficManager::GetMaxTraffic()
+{
+ return mMaxTraffic;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+TrafficManager::TrafficManager()
+{
+ // start listening for events
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_REPAIR_CAR );
+ GetEventManager()->AddListener( this, EVENT_PLAYER_VEHICLE_HORN );
+
+ mVehicles = new TrafficVehicle[ MAX_TRAFFIC ];
+
+ mCurrTrafficModelGroup = -1;
+ mMaxTraffic = MAX_TRAFFIC;
+
+ // initialize NULL list.
+ mNumCharsToStopFor = 0;
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ mCharactersToStopFor[i] = NULL;
+ }
+
+ mTrafficEnabled = !DISABLETRAFFIC;
+ if( CommandLineOptions::Get(CLO_NO_TRAFFIC) )
+ {
+ mTrafficEnabled = false;
+ }
+
+ mQueuedTrafficHorns.Allocate( MAX_QUEUED_TRAFFIC_HORNS );
+
+}
+TrafficManager::~TrafficManager()
+{
+ GetEventManager()->RemoveAll( this );
+ Cleanup();
+
+ mQueuedTrafficHorns.Clear();
+ delete[] mVehicles;
+}
+
+Vehicle* TrafficManager::InitRandomVehicle()
+{
+ rAssert( mCurrTrafficModelGroup >= 0 );
+
+ Vehicle* newV = NULL;
+
+ // randomly choose a model index
+ int numModels = mTrafficModelGroups[mCurrTrafficModelGroup].GetNumModels();
+ int modelIndex = rand() % numModels;
+
+ TrafficModel* tm = NULL;
+
+ // if there are already too many instances of traffic cars under this model
+ // we go to the next model... and so on until we're out of models.
+ int count = 0;
+ while( count < numModels )
+ {
+ tm = mTrafficModelGroups[mCurrTrafficModelGroup].GetTrafficModel( modelIndex );
+ if( tm->mNumInstances >= tm->mMaxInstances )
+ {
+ modelIndex = (modelIndex+1) % numModels;
+ count++;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if( count >= numModels )
+ {
+ char message[256];
+ sprintf( message, "DOH! Can't initialize a traffic car \"%s\" because\n"
+ "we already have %d of max %d instances allowed.\n"
+ "See leveli.mfk to increase the max allowed for this model\n",
+ tm->mModelName, tm->mNumInstances, tm->mMaxInstances );
+
+ rTuneAssertMsg( false, message );
+ return NULL;
+ }
+
+ newV = ::GetVehicleCentral()->InitVehicle( tm->mModelName, false, 0, VT_TRAFFIC );
+ rTuneAssert( newV != NULL );
+ tm->mNumInstances++;
+
+ return newV;
+}
+
+void TrafficManager::Cleanup()
+{
+ // Clean up list of characters to stop for...
+ //
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ if( mCharactersToStopFor[i] != NULL )
+ {
+ mCharactersToStopFor[i]->Release();
+ mCharactersToStopFor[i] = NULL;
+ mNumCharsToStopFor--;
+ }
+ }
+ rAssert( mNumCharsToStopFor == 0 );
+
+ // Clean up swap array
+ //
+ mQueuedTrafficHorns.ClearUse();
+
+ // Clean up traffic vehicles
+ //
+ for( int i=0 ; i<MAX_TRAFFIC ; i++ )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = tv->GetVehicle();
+ if( tv->GetIsActive() )
+ {
+ // tell trafficlocomotion to be inactive,
+ // clear out traffic vehicle from a lane,
+ // reset trafficvehicle members (except vehicle pointer member) to indeterminate values,
+ // decrement numtraffic count by 1,
+ // remove from activelist
+ RemoveTraffic( i );
+ }
+
+ rAssert( tv->GetHusk() == NULL );
+
+ if( v != NULL )
+ {
+ v->ReleaseVerified();
+ tv->SetVehicle( NULL );
+ }
+ }
+
+ // clean up intersections
+ for( int j=0; j<MAX_INTERSECTIONS; j++ )
+ {
+ mpIntersections[j] = NULL;
+ }
+
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mDesiredTrafficSpeedKph );
+#endif
+
+}
+
+bool TrafficManager::AddTraffic(Lane* lane, TrafficVehicle* tv)
+{
+ rAssert( tv != NULL );
+ rAssert( tv->GetIsActive() == false );
+ rAssert( tv->GetLane() == NULL );
+
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v != NULL );
+ rAssert( v->mTrafficLocomotion->GetActive() == false );
+
+ // add vehicle to the lane
+ // if there was some error in adding (say if list was full)
+ // then just abort
+ if( lane->mTrafficVehicles.mUseSize >= lane->mTrafficVehicles.mSize )
+ {
+ return false;
+ }
+
+ // add vehicle to ActiveList
+ if( ::GetVehicleCentral()->ActiveVehicleListIsFull() )
+ {
+ return false;
+ }
+
+ v->mVehicleType = VT_TRAFFIC;
+
+ int res = ::GetVehicleCentral()->AddVehicleToActiveList( v );
+ if( res == -1 )
+ {
+ // not supposed to happen since we already eliminated the
+ // safe failure condition (activelistisfull)
+ rAssert( false );
+ return false;
+ }
+
+ /////// NO MORE BAILING OUT AFTER THIS POINT /////////
+
+ lane->mTrafficVehicles.Add( tv );
+
+ // now add the AI to active vehicle controller list
+ GetVehicleCentral()->SetVehicleController( res, v->mTrafficLocomotion->GetAI() );
+
+ // set trafficVehicle fields
+ tv->SetLane( lane );
+ tv->SetIsActive( true );
+ tv->SetHasHusk( false );
+ //tv->mActiveListIndex = res;
+ tv->mMillisecondsDeactivated = 0;
+ tv->mMillisecondsOutOfSight = 0;
+ tv->mOutOfSight = true;
+
+ ::GetEventManager()->TriggerEvent( EVENT_TRAFFIC_SPAWN, v );
+
+ mNumTraffic++;
+ return true;
+}
+
+void TrafficManager::RemoveTraffic( int vIndex )
+{
+ int i = vIndex;
+ TrafficVehicle* tv = &mVehicles[i];
+ RemoveTrafficVehicle( tv );
+}
+
+void TrafficManager::RemoveTrafficVehicle( TrafficVehicle* tv )
+{
+ rAssert( tv != NULL );
+ rAssert( tv->GetIsActive() == true );
+
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v != NULL );
+ v->mTrafficLocomotion->SetActive( false );
+
+ // Remove the vehicle from the lane, if it hasn't been removed
+ // already (if the car was Deactivated, it will have already been
+ // removed from the lane.. the lane pointer will not be NULL cuz
+ // it's needed later for resurrection, but the traffic index will
+ // be -1.)
+ Lane* lane = tv->GetLane();
+ if( lane != NULL )
+ {
+ for( int i=0; i<lane->mTrafficVehicles.mUseSize; i++ )
+ {
+ if( tv == lane->mTrafficVehicles[i] )
+ {
+ lane->mTrafficVehicles.Remove(i);
+ break;
+ }
+ }
+ }
+
+ // remove Vehicle (or its husk if it's destroyed) from ActiveList
+ if( tv->HasHusk() )
+ {
+ Vehicle* husk = tv->GetHusk();
+ rAssert( husk );
+
+ bool succeeded = ::GetVehicleCentral()->RemoveVehicleFromActiveList( husk );
+ rAssert( succeeded );
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( husk );
+ // restore fade alpha if we set it, so other vehicles don't get confused
+ husk->mGeometryVehicle->SetFadeAlpha( 255 );
+ husk->Release(); // don't verify destruction cuz huskpool has final ownership
+ tv->SetHusk( NULL );
+ }
+ else
+ {
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v );
+ rAssert( tv->GetHusk() == NULL );
+
+ GetVehicleCentral()->RemoveVehicleFromActiveList( v );
+ GetEventManager()->TriggerEvent( EVENT_TRAFFIC_REMOVE, v );
+ }
+
+ // remove from our traffic system
+ //GetVehicleCentral()->SetVehicleController( tv->mActiveListIndex, NULL );
+ tv->SetLane( NULL );
+ tv->SetIsActive( false );
+ tv->SetHasHusk( false );
+ //tv->mActiveListIndex = -1;
+ tv->mMillisecondsDeactivated = 0;
+ tv->mMillisecondsOutOfSight = 0;
+ tv->mOutOfSight = true;
+
+ mNumTraffic--;
+
+ // search through queued traffic horns list and remove self...
+ Vehicle* traffVehicle = tv->GetVehicle();
+ for( int i=0; i<mQueuedTrafficHorns.mUseSize; i++ )
+ {
+ if( mQueuedTrafficHorns[i].vehicle == traffVehicle )
+ {
+ mQueuedTrafficHorns.Remove( i );
+ break;
+ }
+ }
+}
+
+TrafficVehicle* TrafficManager::GetFreeTrafficVehicle()
+{
+ // gotta start at some random index, so we don't recycle
+ // the same car (and thus same car model) over and over again
+ // when maxtraffic is set to a small number
+
+ int randIndex = rand() % MAX_TRAFFIC;
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ if( !mVehicles[randIndex].GetIsActive() )
+ {
+ return &mVehicles[randIndex];
+ }
+ randIndex = (randIndex + 1) % MAX_TRAFFIC;
+ }
+ return NULL;
+}
+
+float TrafficManager::DetermineDesiredSpeedKph()
+{
+ float speedKph = 0.0f;
+ switch( GetGameplayManager()->GetCurrentLevelIndex() )
+ {
+ case RenderEnums::L1:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L2:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L3:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L4:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L5:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L6:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L7:
+ speedKph = 60.0f;
+ break;
+ default:
+ rAssert( false );
+ break;
+ }
+ return speedKph;
+}
+
+TrafficVehicle* TrafficManager::FindTrafficVehicle( Vehicle* vehicle )
+{
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = tv->GetVehicle();
+ if( vehicle == v )
+ {
+ return tv;
+ }
+ }
+ return NULL;
+}
+
+bool TrafficManager::IsVehicleTrafficVehicle( Vehicle* vehicle )
+{
+ return FindTrafficVehicle( vehicle ) != NULL;
+}
+
+
+Vehicle* TrafficManager::InitRandomHusk( Vehicle* v )
+{
+ Vehicle* husk = ::GetVehicleCentral()->mHuskPool.RequestHusk( VT_TRAFFIC, v );
+ return husk;
+}
+
+void TrafficManager::UpdateIntersection( unsigned int milliseconds, Vehicle* v, int& nIntersectionsUpdated )
+{
+ rAssert( v->GetLocomotionType() == VL_TRAFFIC );
+ rAssert( v != NULL );
+
+ Intersection* intersection = NULL;
+ if( !v->mTrafficLocomotion->IsInIntersection() )
+ {
+ intersection = (Intersection*) v->mTrafficLocomotion->
+ GetAILane()->GetRoad()->GetDestinationIntersection();
+ }
+ else
+ {
+ intersection = (Intersection*) v->mTrafficLocomotion->
+ GetAILane()->GetRoad()->GetSourceIntersection();
+ }
+
+ // update the intersection if it hasn't already been updated this frame
+ bool foundIntersection = false;
+ for( int i=0; i<nIntersectionsUpdated; i++ )
+ {
+ if( mpIntersections[i] == intersection )
+ {
+ foundIntersection = true;
+ break;
+ }
+ }
+ if( !foundIntersection )
+ {
+ if( nIntersectionsUpdated < MAX_INTERSECTIONS )
+ {
+ mpIntersections[nIntersectionsUpdated] = intersection;
+ nIntersectionsUpdated++;
+ intersection->Update(milliseconds);
+ }
+ }
+}
+
+void TrafficManager::SetVehicleFadeAlpha( Vehicle* v, const rmt::Vector& pPos )
+{
+ rAssert( v );
+
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+ float distFromPlayer = (pPos - vPos).Length(); // *** SQUARE ROOT! ***
+
+ float fadeMinLimit = TrafficManager::FADE_RADIUS;
+ float fadeMaxLimit = TrafficManager::ADD_RADIUS + TrafficManager::CENTER_OFFSET;
+ int fadeAlpha = 255;
+ if( fadeMinLimit <= distFromPlayer && distFromPlayer <= fadeMaxLimit )
+ {
+ // if we're in the fading zone, gotta change fade alpha
+ float fadeRatio = (distFromPlayer - fadeMinLimit)/(fadeMaxLimit - fadeMinLimit);
+ fadeAlpha = (int) (255.0f * (1.0f - fadeRatio));
+ }
+ else if( distFromPlayer > fadeMaxLimit )
+ {
+ fadeAlpha = 0;
+ }
+
+ v->mGeometryVehicle->SetFadeAlpha( fadeAlpha );
+}
+
+bool TrafficManager::AttemptResurrection( TrafficVehicle* tv )
+{
+ rAssert( tv );
+ rAssert( tv->GetIsActive() );
+
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v );
+ rAssert( v->mTrafficLocomotion );
+ rAssert( v->mTrafficLocomotion->GetActive() == false );
+
+ // if has been hijacked by user, don't resurrect
+ if( v->mHijackedByUser )
+ {
+ tv->mCanBeResurrected = false;
+ return false;
+ }
+
+ // if we have determined that it can't be resurrect, no point trying
+ if( !tv->mCanBeResurrected )
+ {
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // don't allow resurrection right away... cool down... cool down... cool down...
+ if( tv->mMillisecondsDeactivated < MILLISECONDS_STUNNED_AFTER_DEACTIVATED )
+ {
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ if( v->mVehicleDestroyed )
+ {
+ // reset it to 0 so we don't check again every frame
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // Check through vehicles VL_PHYSICS to see if we can bring any of them
+ // back to life:
+ // - check health
+ // - check distance from last segment
+ // - check if we've been deactivated long enough
+
+ // if not at rest yet, don't resurrect...
+ if( !v->IsAtRest() )
+ {
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // If the lane won't support us anymore, wait for awhile
+ Lane* oldLane = tv->GetLane();
+ rAssert( oldLane != NULL );
+ if( oldLane->mTrafficVehicles.mUseSize >= oldLane->GetDensity() )
+ {
+ // reset it to 0 so there's a lull before we check again..
+ tv->mMillisecondsDeactivated = 0;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ //
+ // check if there's anything ahead ...
+ //
+ TrafficAI::ObstacleType foundSOMETHING = TrafficAI::OT_NOTHING;
+ float distFromSOMETHINGSqr = 100000.0f;
+ void* SOMETHING = NULL;
+ bool SOMETHINGOnMyRight = false;
+
+ v->mTrafficLocomotion->GetAI()->CheckForObstacles(
+ foundSOMETHING, distFromSOMETHINGSqr, SOMETHING, SOMETHINGOnMyRight );
+
+ if( foundSOMETHING != TrafficAI::OT_NOTHING )
+ {
+ // reset it to 0 so there's a lull before we check again..
+ tv->mMillisecondsDeactivated = 0;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+
+
+ rmt::Vector vPos, vDir, vUp;
+ v->GetPosition( &vPos );
+
+ vDir = v->GetFacing();
+ rAssert( rmt::Epsilon( vDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ v->GetVUP( &vUp );
+ rAssert( rmt::Epsilon( vUp.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // ACTION:
+ // - if we were in an intersection, just build spline straight to the
+ // target segment (no problem!). Remain in DRIVING state.
+ // - else
+ // - Make sure we're not too far from closest point on last segment
+ // - Make sure the line from us to closest point on last segment does not
+ // cross over the segment (meaning that we were outside the segment...
+ // e.g. on the sidewalk... in which case we don't recover)
+ // - Build spline to a position 5 meters ahead of the closestPt on segment
+ // flowing onto a new segment as necessary... do not flow over into an
+ // intersection. Store out segment's t value and transit to RECOVERING
+ // state. When done RECOVERING state, add t value to the t.
+
+ TrafficAI* ai = v->mTrafficLocomotion->GetAI();
+ rAssert( ai );
+
+ RoadSegment* seg = ai->GetSegment();
+ rAssert( seg );
+
+ int laneIndex = (int)(ai->GetLaneIndex());
+
+ const float TOLERANCE_DIST_SQR = 64.0f;
+ const float UP_COSALPHA = 0.9848077f;
+ const float FACING_RESURRECT_COSAPLHA = 0.0f;
+
+ rmt::Vector targetPos, targetDir;
+
+ if( v->mTrafficLocomotion->IsInIntersection() ||
+ ai->GetState() == TrafficAI::LANE_CHANGING )
+ {
+ // TODO:
+ // The assumption that the intersection's UP vector is simply 0,1,0 is
+ // no longer valid since we don't enforce the intersection to be completely
+ // horizontal anymore. So... we have to determine the up vector ourselves...
+ // *shudder*....
+
+ // If vehicle's up vector isn't anywhere close to the horizontal up vector
+ // (0,1,0), don't resurrect (it might be tipped over or laying on its side)
+ rmt::Vector testUp( 0.0f, 1.0f, 0.0f );
+ if( vUp.Dot( testUp ) < UP_COSALPHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // Just build a spline to the end of the intersection, if we're
+ // close enough to the original intersection spline
+ rmt::Vector* ways;
+ int nPts, currPt;
+ v->mTrafficLocomotion->GetSplineCurve( ways, nPts, currPt );
+
+ rAssert( ways ); // we should have mWays populated since we're in the intersection
+
+ rmt::Vector tmpStart, tmpEnd, tmpClosestPt;
+ tmpStart = ways[0];
+ tmpEnd = ways[nPts-1];
+ FindClosestPointOnLine( tmpStart, tmpEnd, vPos, tmpClosestPt );
+
+ const float CLOSE_ENOUGH_TO_ORIG_SPLINE_SQR = 16.0f;
+ float distSqr = (vPos - tmpClosestPt).MagnitudeSqr();
+ if( distSqr > CLOSE_ENOUGH_TO_ORIG_SPLINE_SQR )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // now it's time to get the target of our new spline...
+ seg->GetLaneLocation( 0.0f, laneIndex, targetPos, targetDir );
+
+ // if our pos is too close to the end, don't do it;
+ // it will look too strange...
+ const float US_FAR_ENOUGH_FROM_TARGET_POS_SQR = 25.0f;
+ distSqr = (vPos - targetPos).MagnitudeSqr();
+ if( distSqr < US_FAR_ENOUGH_FROM_TARGET_POS_SQR )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // if the closest point on original spline is too close to
+ // the end, don't do it; it will look too strange...
+ const float PROJ_FAR_ENOUGH_FROM_TARGET_POS_SQR = 25.0f;
+ distSqr = (tmpClosestPt - targetPos).MagnitudeSqr();
+ if( distSqr < PROJ_FAR_ENOUGH_FROM_TARGET_POS_SQR )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ if( !rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ targetDir.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // If vehicle's facing is at too great an angle from target dir,
+ // and our lateral distance is not that far, don't do it...
+ // (it will look too strange)
+ if( targetDir.Dot( vDir ) < FACING_RESURRECT_COSAPLHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ ////////////////////// OK, NO ABORTING NOW //////////////////////
+ if( ai->GetState() == TrafficAI::LANE_CHANGING )
+ {
+ ai->SetState( TrafficAI::SPLINING );
+ }
+ }
+ else
+ {
+ // Here we're neither lane-changing nor in an intersection
+
+ // Find the closest seg along road (rather than use the last seg we were on)
+ const Road* road = seg->GetRoad();
+ rAssert( road );
+
+ float closestDistSqr = TOLERANCE_DIST_SQR;
+ RoadSegment* closestSeg = NULL;
+ rmt::Vector closestPt, start, end, closestPtOnSeg;
+
+ unsigned int numSegs = road->GetNumRoadSegments();
+ for( unsigned int i=0; i<numSegs; i++ )
+ {
+ RoadSegment* aSeg = road->GetRoadSegment( i );
+ rAssert( aSeg );
+
+ rmt::Vector tmpStart, tmpEnd, tmpDir;
+ aSeg->GetLaneLocation( 0.0f, laneIndex, tmpStart, tmpDir );
+ aSeg->GetLaneLocation( 1.0f, laneIndex, tmpEnd, tmpDir );
+
+ FindClosestPointOnLine( tmpStart, tmpEnd, vPos, closestPtOnSeg );
+
+ float distSqr = (vPos - closestPtOnSeg).MagnitudeSqr();
+ if( distSqr < closestDistSqr )
+ {
+ closestDistSqr = distSqr;
+ closestSeg = aSeg;
+ closestPt = closestPtOnSeg;
+ start = tmpStart;
+ end = tmpEnd;
+ }
+ }
+ if( closestSeg == NULL )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ seg = closestSeg;
+
+ // If vehicle's up vector isn't anywhere close to the segment's up vector,
+ // don't resurrect (it might be tipped over or laying on its side)
+ rmt::Vector testUp;
+ seg->GetSegmentNormal( testUp );
+ rAssert( rmt::Epsilon( testUp.MagnitudeSqr(), 1.0f, 0.001f ) );
+ if( vUp.Dot( testUp ) < UP_COSALPHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+
+ // determine starting t
+ float startT = GetLineSegmentT( start, end, closestPt );
+ rAssert( 0.0f <= startT && startT <= 1.0f );
+
+ // TODO:
+ // Maybe make sure line from us to closestPt doesn't cross over a segment bound
+ // (test against line segments---v0,v1 and v2,v3---to make sure that
+ // closestPt and vPos are both on the left of v2,v3 and right of v0,v1)
+
+ // find a new lane position, 5 meters ahead...
+ unsigned int segmentIndex = seg->GetSegmentIndex();
+ float pathLength = seg->GetLaneLength( laneIndex );
+ float t = startT;
+ float distAhead = 10.0f;
+ t += distAhead / pathLength;
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+
+ if( segmentIndex < (numSegs-1) )
+ {
+ // move ahead a segment
+ segmentIndex++;
+ seg = road->GetRoadSegment( segmentIndex );
+ float newLength = seg->GetLaneLength( laneIndex );
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ }
+ else // if we're out of segments.. we are at an intersection...
+ {
+ // TODO:
+ // Aborting here keeps us from being able to resurrect properly if
+ // we're closestDistSqr meters or less from the intersection (on approach).
+ // We need to be able to deal effectively with this case..
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+ }
+
+ rAssert( 0.0f <= t && t <= 1.0f );
+
+ seg->GetLaneLocation( t, laneIndex, targetPos, targetDir );
+ if( !rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ targetDir.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // If vehicle's facing is at too great an angle from target dir,
+ // and our lateral distance is not that far, don't do it...
+ // (it will look too strange)
+ if( targetDir.Dot( vDir ) < FACING_RESURRECT_COSAPLHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ //////////////////////////// OK NO ABORTING NOW ////////////////////////
+ // Update TrafficAI to look at the new segment, segIndex, and outT
+ ai->SetSegmentIndex( segmentIndex );
+ ai->SetState( TrafficAI::SPLINING );
+
+ v->mTrafficLocomotion->mOutLaneT = t;
+ }
+
+ float restHeightAboveGround = v->GetRestHeightAboveGround();
+
+ /////////////////////////////////////////////
+ // Adjust ground height (unfortunately we need to do this
+ // because we use pos averaging (so our y value is off)
+ rmt::Vector groundPosition, outnorm;
+ bool bFoundPlane = false;
+
+ groundPosition = vPos;
+ outnorm.Set( 0.0f, 1.0f, 0.0f );
+
+ GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition // OUT
+ );
+
+ if( bFoundPlane )
+ {
+ vPos.y = groundPosition.y + restHeightAboveGround;
+ }
+
+ ///////////////////////////////////
+ // build spline
+ vPos.y -= restHeightAboveGround;
+ //vDir.y = 0.0f;
+ if( !rmt::Epsilon( vDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ vDir.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( vDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ v->mTrafficLocomotion->BuildArbitraryCurve( vPos, vDir, targetPos, targetDir );
+
+
+ // bring vehicle back into traffic:
+ // - set loco back to VL_TRAFFIC
+ // - put it back in the same lane, on the closest point on the closestSeg
+ // - TrafficLomotion::SetIsActive(true)
+
+ /* NOTE:
+ Don't need to do this sheeyatsu... the vehicle is already where
+ we want it to be and the simstate is up to date.
+ v->SetInitialPosition( &vPos );
+ float angle = GetRotationAboutY( vDir.x, vDir.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+ */
+
+ // Just a test to see if it's ever anything else... if it is, this could be
+ // a problem.. Please notify Dusit.
+ rAssert( v->mVehicleType == VT_TRAFFIC );
+
+ v->mVehicleType = VT_TRAFFIC;
+ v->SetLocomotion( VL_TRAFFIC );
+ v->mTrafficLocomotion->SetActive( true );
+ v->mTrafficLocomotion->SetAISpeed( 0.0f );
+ v->mTrafficLocomotion->InitPos( vPos );
+ v->mTrafficLocomotion->InitFacing( vDir );
+
+ // Readd the vehicle to the lane
+ oldLane->mTrafficVehicles.Add( tv );
+
+ return true;
+}
+
+
diff --git a/game/code/worldsim/traffic/trafficmanager.h b/game/code/worldsim/traffic/trafficmanager.h
new file mode 100644
index 0000000..0979e87
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficmanager.h
@@ -0,0 +1,312 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Spawning/Removing vehicles -- Dusit Eakkachaichanvet
+// 04/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRAFFICMANAGER_H
+#define TRAFFICMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <worldsim/traffic/trafficvehicle.h>
+#include <worldsim/traffic/trafficmodelgroup.h>
+#include <ai/vehicle/trafficai.h>
+#include <events/eventlistener.h>
+#include <roads/intersection.h>
+
+#include <render/culling/swaparray.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class Lane;
+class Character;
+
+
+//********************************************
+// USED TO CONTROL WHETHER OR NOT TRAFFIC CARS GET ADDED TO THE WORLD
+// FOR THE PURPOSES OF TESTING.
+//
+const bool DISABLETRAFFIC = false;
+//
+//********************************************
+
+
+struct ITrafficSpawnController
+{
+ virtual void SetMaxTraffic( int n ) = 0;
+ virtual void EnableTraffic() = 0;
+ virtual void DisableTraffic() = 0;
+ virtual void ClearTrafficInSphere( const rmt::Sphere& s ) = 0;
+};
+
+/*
+These are the constraints Traffic lives by.
+
+Road Segment
+============
+- Try not to make angles of the edge normals deviate too greatly from
+ the segment's "direction". i.e Avoid this:
+
+ |
+ / |
+ / |
+ / |
+ / |
+ / |
+ | |
+ |__________|
+
+- a single road segment is a flat polygon (of coplanar vertices).
+- road world builder data must conform to the art/physics.
+- road segments should end BEFORE they cross over the (art-side)
+ crosswalks. Otherwise traffic cars will run over people crossing the street.
+- no road segment is overlapping another
+- the road segments that belong to the same road must lie within half a meter
+ apart from one another to be considered attached to that road.
+
+
+Intersections
+=========
+- plane of the intersection (formed by in & out road segments) must be
+ FLAT FLAT FLAT... This means completely X-Z horizontal.
+
+- For each intersection "x", for every IN road "y" belonging to "x", if "y"
+ has more than 1 OUT road ("z"?) other than a U-turn OUT road going back
+ the way it came ("w"?), then it needs to be type N_WAY (type value of 1
+ for the world builders). Otherwise its type is NO_STOP (type value 0). This
+ goes for all intersections, including the "fake" ones we use to join
+ Zones/Rails.
+
+- The road data for IN & OUT segments of "fake" intersections (used to
+ join zones/rails) need to leave a small (1 meter or less) "gap" between
+ one another. Otherwise the traffic cars will appear to "flip". More
+ generally: no 2 roads going in or out of any intersection are
+ touching/overlapping one another.
+
+- the centre of any intersection (hand-placed) is more or less at the
+ physical centre of the intersection.
+
+- There is an appreciable/reasonable distance between one intersection and
+ another. This translates to something like 10 meters or more.
+
+General
+======
+- The world is not round or overlapping. In other words, if I were to extend
+ an infinite line, parallel to y-axis down through the world, it will only
+ cut through only one single road segment or intersection plane. This is a
+ precautionary step only. Not sure what drastic effects failing to conform
+ to this constraint will produce, but I imagine that most of the code
+ assumes we are in 2.5D, not 3D.
+
+
+
+Might have missed some.. but there you are.
+*/
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TrafficManager :
+ ITrafficSpawnController,
+ public EventListener
+{
+public:
+
+ static TrafficManager* mInstance;
+
+ static const float FADE_RADIUS;
+ static const float ADD_RADIUS;
+ static const float CENTER_OFFSET;
+ static const float INITIAL_ADD_RADIUS;
+ static const float REMOVE_RADIUS;
+ static const unsigned int MILLISECONDS_BETWEEN_REMOVE;
+ static const unsigned int MILLISECONDS_BETWEEN_ADD;
+ static const unsigned int MILLISECONDS_POPULATE_WORLD;
+
+ enum {
+ MAX_CHARS_TO_STOP_FOR = 10 // Max # characters traffic will brake for
+ };
+
+ static ITrafficSpawnController* GetSpawnController();
+ static TrafficManager* GetInstance();
+ static void DestroyInstance();
+
+ ///////////////////////////////////
+ // EVENTLISTENTER STUFF
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ ///////////////////////////////////
+
+ void Init();
+ void InitDefaultModelGroups();
+ void Update( unsigned int milliseconds );
+
+ void Cleanup();
+
+ void AddCharacterToStopFor( Character* character );
+ void RemoveCharacterToStopFor( Character* character );
+ int GetNumCharsToStopFor();
+ Character* GetCharacterToStopFor( int i ) const;
+
+ // Given its pointer, find it in our static list of TrafficVehicles and
+ // take it out of lane traffic and out of active list. Also invalidate
+ // TrafficVehicle-specific fields.
+ void RemoveTraffic( Vehicle* vehicle ); //Take this out of traffic.
+ void Deactivate( Vehicle* v );
+
+ void SetMaxTraffic( int n );
+ int GetMaxTraffic();
+
+ void EnableTraffic();
+ void DisableTraffic();
+ void ClearTrafficInSphere( const rmt::Sphere& s );
+ void ClearTrafficOutsideSphere( const rmt::Sphere& s );
+
+ void ClearOutOfSightTraffic();
+
+ TrafficModelGroup* GetTrafficModelGroup( int i );
+ void SetCurrTrafficModelGroup( int i );
+
+ float GetDesiredTrafficSpeed();
+ void GenerateRandomColour( pddiColour& colour );
+ void SwapInTrafficHusk( Vehicle* vehicle ); // Remove traffic vehicle, swap in a husk...
+
+ bool IsVehicleTrafficVehicle( Vehicle* vehicle );
+
+//MEMBERS
+private:
+ float mDesiredTrafficSpeedKph;
+
+ enum {
+ MAX_TRAFFIC_MODEL_GROUPS = 10, // Max # different groups of traffic models
+ MAX_INTERSECTIONS = 5, // Max # intersections we'll be keeping track of (calling Update to)
+ NUM_SWATCH_COLOURS = 25, // # artist-defined swatch colours
+ MAX_QUEUED_TRAFFIC_HORNS = 3
+ };
+ int mMaxTraffic; // value is betw 0 and MAX_TRAFFIC
+ int mNumTraffic; // value changes between 0 and MAX_TRAFFIC as vehicles are added/removed
+
+ // keep repository of all the vehicles we'll ever need.
+ TrafficVehicle* mVehicles;
+
+ Character* mCharactersToStopFor[ MAX_CHARS_TO_STOP_FOR ];
+ int mNumCharsToStopFor;
+
+ // keep list of intersections that will need to be updated in this loop
+ Intersection* mpIntersections[ MAX_INTERSECTIONS ];
+
+ unsigned int mMillisecondsBetweenRemove;
+ unsigned int mMillisecondsBetweenAdd;
+ unsigned int mMillisecondsPopulateWorld;
+
+ bool mTrafficEnabled;
+
+ TrafficModelGroup mTrafficModelGroups[ MAX_TRAFFIC_MODEL_GROUPS ];
+ int mCurrTrafficModelGroup;
+
+ struct SwatchColour
+ {
+ int red;
+ int green;
+ int blue;
+ };
+ static SwatchColour sSwatchColours[ NUM_SWATCH_COLOURS ];
+
+ struct TrafficHornQueue
+ {
+ Vehicle* vehicle;
+ unsigned int delayInMilliseconds;
+ };
+ SwapArray<TrafficHornQueue> mQueuedTrafficHorns;
+ int mNumQueuedTrafficHorns;
+
+//METHODS
+private:
+
+ float DetermineDesiredSpeedKph();
+
+ // Add a traffic vehicle to the lane and to active list and Initialize
+ // some TrafficVehicle-specific fields
+ bool AddTraffic( Lane* lane, TrafficVehicle* tv );
+
+ // Given its index in our static list of TrafficVehicles, take vehicle out
+ // of lane traffic and out of active list. Also invalidate TrafficVehicle-
+ // specific fields.
+ void RemoveTraffic( int vIndex );
+
+ void RemoveTrafficVehicle( TrafficVehicle* tv );
+
+ TrafficVehicle* FindTrafficVehicle( Vehicle* vehicle );
+ TrafficVehicle* GetFreeTrafficVehicle();
+ Vehicle* InitRandomVehicle();
+ Vehicle* InitRandomHusk( Vehicle* v );
+
+
+ // update the intersections to let cars go in turn (if NWAY)
+ void UpdateIntersection(
+ unsigned int milliseconds,
+ Vehicle* v,
+ int& nIntersectionsUpdated );
+
+ void SetVehicleFadeAlpha( Vehicle* v, const rmt::Vector& pPos );
+
+ bool AttemptResurrection( TrafficVehicle* tv );
+
+ void UpdateQueuedTrafficHorns( unsigned int milliseconds );
+
+ // constructors/destructors we wish to hide so we can implement singleton
+ TrafficManager();
+ virtual ~TrafficManager();
+
+ //Prevent wasteful constructor creation.
+ TrafficManager( const TrafficManager& trafficmanager );
+ TrafficManager& operator=( const TrafficManager& trafficmanager );
+};
+
+
+
+
+// ************************************ INLINES *******************************************
+
+
+inline int TrafficManager::GetNumCharsToStopFor()
+{
+ return mNumCharsToStopFor;
+}
+inline Character* TrafficManager::GetCharacterToStopFor( int i ) const
+{
+ rAssert( 0 <= i && i < MAX_CHARS_TO_STOP_FOR );
+ return mCharactersToStopFor[i];
+}
+inline TrafficModelGroup* TrafficManager::GetTrafficModelGroup( int i )
+{
+ rTuneAssert( 0 <= i && i < MAX_TRAFFIC_MODEL_GROUPS );
+ return &mTrafficModelGroups[i];
+}
+inline void TrafficManager::SetCurrTrafficModelGroup( int i )
+{
+ rTuneAssert( 0 <= i && i < MAX_TRAFFIC_MODEL_GROUPS );
+ mCurrTrafficModelGroup = i;
+}
+inline float TrafficManager::GetDesiredTrafficSpeed()
+{
+ return mDesiredTrafficSpeedKph * KPH_2_MPS;
+}
+#endif //TRAFFICMANAGER_H
+
+
+
+
diff --git a/game/code/worldsim/traffic/trafficmodelgroup.h b/game/code/worldsim/traffic/trafficmodelgroup.h
new file mode 100644
index 0000000..01c1559
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficmodelgroup.h
@@ -0,0 +1,145 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficvehicle.h
+//
+// Description: Blahblahblah
+//
+// History: 02/01/2003 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef TRAFFICMODELGROUP_H
+#define TRAFFICMODELGROUP_H
+
+#include <string.h>
+#include <raddebug.hpp>
+
+//******************************************************************************
+//
+// TRAFFIC MODEL
+//
+//******************************************************************************
+
+class TrafficModel
+{
+public:
+ enum
+ {
+ MAX_STRING_LEN = 64
+ };
+
+ // TRACKING
+ int mNumInstances;
+
+ // DATA
+ char mModelName[MAX_STRING_LEN+1];
+ int mMaxInstances;
+
+public:
+ TrafficModel();
+ void ClearData();
+ void Init( const char* name, int num );
+
+private:
+
+};
+
+inline void TrafficModel::ClearData()
+{
+ mModelName[0] = '\0';
+ mMaxInstances = 0;
+ mNumInstances = 0;
+ // TODO:
+ // We may not need to clear mNumInstances if we're going to initialize traffic vehicles
+ // on the fly. Imagine loading a new model group that shares this same model as
+ // the previous group. We don't want to lose the count.
+}
+
+inline TrafficModel::TrafficModel()
+{
+ ClearData();
+}
+
+inline void TrafficModel::Init( const char* name, int num )
+{
+ rTuneAssert( name != NULL );
+ rTuneAssert( num > 0 );
+
+ int nameLen = strlen( name );
+
+ rTuneAssert( nameLen <= TrafficModel::MAX_STRING_LEN );
+
+ strncpy( mModelName, name, TrafficModel::MAX_STRING_LEN );
+ mModelName[ nameLen ] = '\0';
+ mMaxInstances = num;
+}
+
+//******************************************************************************
+//
+// TRAFFIC MODEL GROUP
+//
+//******************************************************************************
+
+class TrafficModelGroup
+{
+public:
+ TrafficModelGroup();
+ void ClearGroup();
+ void AddTrafficModel( const char* name, int num );
+ TrafficModel* GetTrafficModel( int i );
+ int GetNumModels();
+
+private:
+ enum
+ {
+ MAX_TRAFFIC_MODELS = 5
+ };
+ TrafficModel mTrafficModels[MAX_TRAFFIC_MODELS];
+ int mNumModels;
+};
+
+inline TrafficModelGroup::TrafficModelGroup()
+{
+ mNumModels = 0;
+}
+inline void TrafficModelGroup::ClearGroup()
+{
+ for( int i=0; i<mNumModels; i++ )
+ {
+ mTrafficModels[i].ClearData();
+ }
+ mNumModels = 0;
+}
+inline void TrafficModelGroup::AddTrafficModel( const char* name, int num )
+{
+ rTuneAssert( name != NULL );
+ rTuneAssert( strlen(name) <= TrafficModel::MAX_STRING_LEN );
+ rTuneAssert( num > 0 );
+
+ bool found = false;
+ for( int i=0; i<mNumModels; i++ )
+ {
+ if( strcmp( mTrafficModels[i].mModelName, name )==0 )
+ {
+ found = true;
+ mTrafficModels[i].mNumInstances = num;
+ }
+ }
+
+ if( !found )
+ {
+ mTrafficModels[mNumModels].Init( name, num );
+ mNumModels++;
+ }
+}
+inline TrafficModel* TrafficModelGroup::GetTrafficModel( int i )
+{
+ rTuneAssert( 0 <= i && i < TrafficModelGroup::MAX_TRAFFIC_MODELS );
+ return &mTrafficModels[i];
+}
+inline int TrafficModelGroup::GetNumModels()
+{
+ return mNumModels;
+}
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/traffic/trafficvehicle.h b/game/code/worldsim/traffic/trafficvehicle.h
new file mode 100644
index 0000000..e7259d4
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficvehicle.h
@@ -0,0 +1,135 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficvehicle.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Added members that aid Traffic management -- Dusit Eakkachaichanvet
+// 04/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef TRAFFICVEHICLE_H
+#define TRAFFICVEHICLE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class Lane;
+
+
+//=============================================================================
+//
+// TrafficVehicle
+//
+//=============================================================================
+
+class TrafficVehicle
+{
+public:
+ TrafficVehicle();
+ virtual ~TrafficVehicle();
+
+ Vehicle* GetVehicle() const;
+ void SetVehicle( Vehicle* vehicle );
+
+ Lane* GetLane() const;
+ void SetLane( Lane* lane );
+
+ bool GetIsActive() const;
+ void SetIsActive( bool active );
+
+ bool HasHusk() const;
+ void SetHasHusk( bool yes );
+
+ Vehicle* GetHusk();
+ void SetHusk( Vehicle* husk );
+
+ //int mActiveListIndex;
+ unsigned int mMillisecondsDeactivated;
+ bool mCanBeResurrected;
+
+ unsigned int mMillisecondsOutOfSight;
+ bool mOutOfSight;
+
+
+
+private:
+
+ Vehicle* mVehicle;
+ Vehicle* mHusk;
+
+ bool mIsActive; // Active = being used as traffic car
+ Lane* mLane; // Pointer to Lane to whose list of vehicles this
+ // traffic vehicle has been added.
+ bool mHasHusk;
+
+private:
+ //Prevent wasteful constructor creation.
+ TrafficVehicle( const TrafficVehicle& trafficvehicle );
+ TrafficVehicle& operator=( const TrafficVehicle& trafficvehicle );
+};
+inline TrafficVehicle::TrafficVehicle() :
+ //mActiveListIndex( -1 ),
+ mMillisecondsDeactivated( 0 ),
+ mCanBeResurrected( true ),
+ mMillisecondsOutOfSight( 0 ),
+ mOutOfSight( true ),
+ mVehicle( NULL ),
+ mHusk( NULL ),
+ mIsActive( false ),
+ mLane( NULL ),
+ mHasHusk( false )
+{
+}
+inline TrafficVehicle::~TrafficVehicle()
+{
+}
+inline Vehicle* TrafficVehicle::GetVehicle() const
+{
+ return mVehicle;
+}
+inline void TrafficVehicle::SetVehicle( Vehicle* vehicle )
+{
+ mVehicle = vehicle;
+}
+inline Lane* TrafficVehicle::GetLane() const
+{
+ return mLane;
+}
+inline void TrafficVehicle::SetLane( Lane* lane )
+{
+ mLane = lane;
+}
+inline bool TrafficVehicle::GetIsActive() const
+{
+ return mIsActive;
+}
+inline void TrafficVehicle::SetIsActive( bool active )
+{
+ mIsActive = active;
+}
+inline bool TrafficVehicle::HasHusk() const
+{
+ return mHasHusk;
+}
+inline void TrafficVehicle::SetHasHusk( bool yes )
+{
+ mHasHusk = yes;
+}
+inline Vehicle* TrafficVehicle::GetHusk()
+{
+ return mHusk;
+}
+inline void TrafficVehicle::SetHusk( Vehicle* husk )
+{
+ mHusk = husk;
+}
+
+#endif //TRAFFICVEHICLE_H
diff --git a/game/code/worldsim/vehiclecentral.cpp b/game/code/worldsim/vehiclecentral.cpp
new file mode 100644
index 0000000..f9dc05f
--- /dev/null
+++ b/game/code/worldsim/vehiclecentral.cpp
@@ -0,0 +1,2278 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclecentral.cpp
+//
+// Description: bleek
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <raddebug.hpp>
+
+#include <stdlib.h> // for atof
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/avatarmanager.h>
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+
+#include <debug/debuginfo.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/missionscriptloader.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+
+#include <gameflow/gameflow.h>
+#include <console/console.h>
+#include <cheats/cheatinputsystem.h>
+
+#include <p3d/billboardobject.hpp>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+VehicleCentral* VehicleCentral::spInstance = 0;
+AiVehicleController* VehicleCentral::spGenericAI = NULL;
+
+//
+// Dusit here:
+// First, keep MAX_HUSKS defined in the .cpp so we can tweak without
+// recompiling the whole game...
+// Now, how many husks can we possibly need in the WORST case??
+// traffic 5
+// parked cars 5
+// harass 5
+// player 1
+// mission ai 4
+// main ai 1
+// free (moment) cars 1-2 (?)
+// This is too many (23). At 17KB per Vehicle instance + 35KB Husk model,
+// it would total between 420 and 500 KB... Unacceptable.
+// So we have forced the other systems to deal safely with not having
+// husks available when they need it (RequestHusk may return NULL)
+//
+const int MAX_HUSKS = 5;
+
+
+//=============================================================================
+// VehicleCentral::VehicleCentral
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral::VehicleCentral() :
+ mCurrentVehicleUnderContructionHead( 0 ),
+ mCurrentVehicleUnderConstructionTail( 0 ),
+ mbVehicleTriggersActive(true),
+ mSuppressedDriverCount(0)
+{
+ spGenericAI = new AiVehicleController( NULL );
+ spGenericAI->AddRef();
+
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ mActiveVehicleList[i] = 0;
+
+ //mActiveVehicleControllerList[ i ] = NULL;
+ mActiveVehicleControllerList[ i ] = spGenericAI;
+ mActiveVehicleControllerList[ i ]->AddRef();
+
+ mVehicleUnderConstruction[ i ] = NULL;
+ //mDoorTriggerList[ i ] = 0;
+ }
+
+ mNumActiveVehicles = 0;
+
+ // perhaps this would be a good place to setup the console functions for the vehicle
+ SetupConsoleFunctionsForVehicleTuning();
+
+ for( i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ mHeadLights[i] = NULL;
+ }
+}
+
+//==============
+// console hooks
+//==============
+
+static void ConsoleHookSetGasScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetGasScale(value);
+}
+
+static void ConsoleHookSetSlipGasScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSlipGasScale(value);
+}
+
+static void ConsoleHookSetHighSpeedGasScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetHighSpeedGasScale(value);
+}
+
+
+static void ConsoleHookSetGasScaleSpeedThreshold(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetGasScaleSpeedThreshold(value);
+}
+
+
+static void ConsoleHookSetBrakeScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetBrakeScale(value);
+}
+
+static void ConsoleHookSetTopSpeedKmh(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTopSpeedKmh(value);
+}
+
+static void ConsoleHookSetMass(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetMass(value);
+}
+
+static void ConsoleHookSetMaxWheelTurnAngle(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetMaxWheelTurnAngle(value);
+}
+
+
+static void ConsoleHookSetHighSpeedSteeringDrop(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetHighSpeedSteeringDrop(value);
+}
+
+
+static void ConsoleHookSetTireLateralStaticGrip(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralStaticGrip(value);
+}
+
+
+static void ConsoleHookSetTireLateralResistanceNormal(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralResistanceNormal(value);
+}
+
+
+static void ConsoleHookSetTireLateralResistanceSlip(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralResistanceSlip(value);
+}
+
+
+static void ConsoleHookSetEBrakeEffect(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetEBrakeEffect(value);
+}
+
+
+static void ConsoleHookSetCMOffsetX(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetCMOffsetX(value);
+}
+
+
+static void ConsoleHookSetCMOffsetY(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetCMOffsetY(value);
+}
+
+
+static void ConsoleHookSetCMOffsetZ(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetCMOffsetZ(value);
+}
+
+
+static void ConsoleHookSetSuspensionLimit(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionLimit(value);
+}
+
+
+static void ConsoleHookSetSpringK(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionSpringK(value);
+}
+
+
+static void ConsoleHookSetDamperC(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionDamperC(value);
+}
+
+
+static void ConsoleHookSetHitPoints(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetHitPoints(value);
+}
+
+
+static void ConsoleHookSetSuspensionYOffset(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionYOffset(value);
+}
+
+static void ConsoleHookSetBurnoutRange(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetBurnoutRange(value);
+}
+
+
+static void ConsoleHookSetMaxSpeedBurstTime(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetMaxSpeedBurstTime(value);
+}
+
+
+static void ConsoleHookSetDonutTorque(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetDonutTorque(value);
+}
+
+
+static void ConsoleHookSetSlipSteeringNoEBrake(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralResistanceSlipWithoutEBrake(value);
+}
+
+static void ConsoleHookSetSlipEffectNoEBrake(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSlipEffectWithoutEBrake(value);
+}
+
+
+static void ConsoleHookSetWeebleOffset(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWeebleOffset(value);
+}
+
+
+static void ConsoleHookSetWheelieRange(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWheelieRange(value);
+}
+
+
+static void ConsoleHookSetWheelieOffsetY(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWheelieYOffset(value);
+}
+
+static void ConsoleHookSetWheelieOffsetZ(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWheelieZOffset(value);
+}
+
+static void ConsoleHookSetShininess( int argc, char** argv )
+{
+ if( argc != 2 )
+ {
+ return;
+ }
+ float shininess = static_cast<float>( atof( argv[ 1 ] ) );
+ unsigned char ref = rmt::Clamp( int( 0xff * shininess ), 0, 0xff );
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetShininess( ref );
+}
+
+static void ConsoleHookSetShadowAdjustments( int argc, char** argv )
+{
+ if( argc != 9 )
+ {
+ return;
+ }
+ float Adjustments[ 4 ][ 2 ];
+ for( int i = 0; i < 4; ++i )
+ {
+ Adjustments[ i ][ 0 ] = static_cast<float>( atof( argv[ 1 + ( i * 2 ) ] ) );
+ Adjustments[ i ][ 1 ] = static_cast<float>( atof( argv[ 2 + ( i * 2 ) ] ) );
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetShadowAdjustments( Adjustments );
+}
+
+static void ConsoleHookSetDriverName(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetDriverName(argv[1]);
+}
+
+static void ConsoleHookSetHasDoors(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mHasDoors = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetCharactersVisible(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mVisibleCharacters = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetIrisTransition(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mIrisTransition = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetAllowSlide(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mAllowSlide = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetHighRoof(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mHighRoof = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetCharacterScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mCharacterScale = static_cast< float >( atof(argv[1]) );
+}
+
+//Chuck: added this for Gambling Races.
+static void ConsoleHookSetGamblingOdds(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetGamblingOdds(value);
+}
+
+static void ConsoleHookSuppressDriver(int argc, char** argv)
+{
+ GetVehicleCentral()->AddSuppressedDriver(argv[1]);
+}
+
+
+//=============================================================================
+// VehicleCentral::SetupConsoleFunctionsForVehicleTuning
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SetupConsoleFunctionsForVehicleTuning()
+{
+
+ GetConsole()->AddFunction("SetMass", ConsoleHookSetMass, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetGasScale", ConsoleHookSetGasScale, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetHighSpeedGasScale", ConsoleHookSetHighSpeedGasScale, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetGasScaleSpeedThreshold", ConsoleHookSetGasScaleSpeedThreshold, "help your goddamn self", 1, 1);
+
+
+ GetConsole()->AddFunction("SetSlipGasScale", ConsoleHookSetSlipGasScale, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetBrakeScale", ConsoleHookSetBrakeScale, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetTopSpeedKmh", ConsoleHookSetTopSpeedKmh, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetMaxWheelTurnAngle", ConsoleHookSetMaxWheelTurnAngle, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetHighSpeedSteeringDrop", ConsoleHookSetHighSpeedSteeringDrop, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetTireGrip", ConsoleHookSetTireLateralStaticGrip, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetNormalSteering", ConsoleHookSetTireLateralResistanceNormal, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetSlipSteering", ConsoleHookSetTireLateralResistanceSlip, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetEBrakeEffect", ConsoleHookSetEBrakeEffect, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetCMOffsetX", ConsoleHookSetCMOffsetX, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCMOffsetY", ConsoleHookSetCMOffsetY, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCMOffsetZ", ConsoleHookSetCMOffsetZ, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetSuspensionLimit", ConsoleHookSetSuspensionLimit, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetSpringK", ConsoleHookSetSpringK, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetDamperC", ConsoleHookSetDamperC, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetSuspensionYOffset", ConsoleHookSetSuspensionYOffset, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetHitPoints", ConsoleHookSetHitPoints, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetBurnoutRange", ConsoleHookSetBurnoutRange, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetMaxSpeedBurstTime", ConsoleHookSetMaxSpeedBurstTime, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetDonutTorque", ConsoleHookSetDonutTorque, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetSlipSteeringNoEBrake", ConsoleHookSetSlipSteeringNoEBrake, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetSlipEffectNoEBrake", ConsoleHookSetSlipEffectNoEBrake, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetWeebleOffset", ConsoleHookSetWeebleOffset, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetWheelieRange", ConsoleHookSetWheelieRange, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetWheelieOffsetY", ConsoleHookSetWheelieOffsetY, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetWheelieOffsetZ", ConsoleHookSetWheelieOffsetZ, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction( "SetShadowAdjustments", ConsoleHookSetShadowAdjustments, "Move the shadow points around", 8, 8 );
+ GetConsole()->AddFunction( "SetShininess", ConsoleHookSetShininess, "Set the environmental reflection 0 to 1", 1, 1 );
+
+ GetConsole()->AddFunction("SetDriver", ConsoleHookSetDriverName, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetGamblingOdds",ConsoleHookSetGamblingOdds,"Set Gambling Odds",1,1);
+
+ GetConsole()->AddFunction("SetHasDoors", ConsoleHookSetHasDoors, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCharactersVisible", ConsoleHookSetCharactersVisible, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetIrisTransition", ConsoleHookSetIrisTransition, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetAllowSeatSlide", ConsoleHookSetAllowSlide, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetHighRoof", ConsoleHookSetHighRoof, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCharacterScale", ConsoleHookSetCharacterScale, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SuppressDriver", ConsoleHookSuppressDriver, "help your goddamn self", 1, 1);
+}
+
+//=============================================================================
+// VehicleCentral::~VehicleCentral
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral::~VehicleCentral()
+{
+ ClearSuppressedDrivers();
+
+
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i])
+ {
+ // TODO - will the mission manager delete these instead??
+ //
+ // for now, only delete the ones that were left in there...
+ delete mActiveVehicleList[i];
+ }
+
+ if ( this->mActiveVehicleControllerList[ i ] )
+ {
+ mActiveVehicleControllerList[ i ]->Release();
+ mActiveVehicleControllerList[ i ] = NULL;
+ }
+/*
+ if ( mDoorTriggerList[ i ] )
+ {
+ mDoorTriggerList[ i ]->Release( );
+ mDoorTriggerList[ i ] = 0;
+ }
+*/
+ }
+
+ // create a static vehicle AI and put it in limbo...
+ if( spGenericAI )
+ {
+ spGenericAI->ReleaseVerified();
+ }
+
+ for( i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ if( mHeadLights[i] )
+ {
+ mHeadLights[i]->Release();
+ mHeadLights[i] = NULL;
+ }
+ }
+
+}
+
+
+//=============================================================================
+// VehicleCentral::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral* VehicleCentral::GetInstance()
+{
+ rAssert(spInstance);
+ return spInstance;
+}
+
+
+//=============================================================================
+// VehicleCentral::CreateInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral* VehicleCentral::CreateInstance()
+{
+ rAssert(spInstance == 0);
+
+ spInstance = new(GMA_PERSISTENT) VehicleCentral;
+ rAssert(spInstance);
+
+ return spInstance;
+
+}
+
+
+//=============================================================================
+// VehicleCentral::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::DestroyInstance()
+{
+ rAssert(spInstance);
+
+ delete(GMA_PERSISTENT, spInstance);
+ spInstance = NULL;
+
+
+}
+
+
+//=============================================================================
+// VehicleCentral::InitHuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::InitHuskPool()
+{
+}
+
+
+//=============================================================================
+// VehicleCentral::FreeHuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::FreeHuskPool()
+{
+
+
+}
+
+
+//=============================================================================
+// VehicleCentral::PreLoad
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PreLoad()
+{
+ // bunch of actual loading moved to frontend startup to reduce load times - nbrooke 3/7/2003
+
+ this->mHuskPool.Init(MAX_HUSKS);
+
+ rAssert( mHeadLights[0] == NULL && mHeadLights[1] == NULL && mHeadLights[2] == NULL );
+
+ // also might as well load the vehicle commons here..
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Global" );
+ bool oldCurrSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ tRefCounted::Assign( mHeadLights[0], p3d::find<tBillboardQuadGroup>(tEntity::MakeUID("headlightShape8")) );
+ tRefCounted::Assign( mHeadLights[1], p3d::find<tBillboardQuadGroup>(tEntity::MakeUID("headlight2Shape")) );
+ tRefCounted::Assign( mHeadLights[2], p3d::find<tBillboardQuadGroup>(tEntity::MakeUID("glowGroupShape2")) );
+
+ p3d::inventory->SetCurrentSectionOnly( oldCurrSectionOnly );
+ p3d::inventory->PopSection();
+
+ // grab the colours from the bbqgs
+ int count = 0;
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( mHeadLights[i] );
+ for( int j=0; j<mHeadLights[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mHeadLights[i]->GetQuad( j );
+ rAssert( quad );
+ mOriginalHeadLightColours[count] = quad->GetColour();
+ count++;
+ }
+ }
+ rAssert( mHeadLights[0] && mHeadLights[1] && mHeadLights[2] );
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::Unload
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Unload()
+{
+ // empty out husk manager here also
+ this->mHuskPool.Empty();
+
+ ClearSuppressedDrivers();
+
+ int count = 0;
+
+ // restore prev states and release all preloaded headlights bbqgs
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( mHeadLights[i] );
+ for( int j=0; j<mHeadLights[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mHeadLights[i]->GetQuad( j );
+ rAssert( quad );
+ quad->SetColour( mOriginalHeadLightColours[count] );
+ count++;
+ }
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ mHeadLights[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+
+ mHeadLights[i]->Release();
+ mHeadLights[i] = NULL;
+ }
+
+ rAssert(mNumActiveVehicles == 0);
+
+}
+
+//=============================================================================
+// VehicleCentral::InitVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+Vehicle* VehicleCentral::InitVehicle( const char* name, bool addToActiveVehicleList, char* confile, VehicleType vt, DriverInit driver, bool playercar, bool startoutofcar)
+{
+ HeapMgr()->PushHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+ MEMTRACK_PUSH_GROUP( "VehicleCentral" );
+//#ifdef RAD_GAMECUBE
+// Vehicle* vehicle = new( GMA_GC_VMM ) Vehicle;
+//#else
+ Vehicle* vehicle = new(GetGameplayManager()->GetCurrentMissionHeap()) Vehicle;
+//#endif
+
+ //if this car is owned by the player then set the flag.
+ if(playercar ==true)
+ {
+ vehicle->mbPlayerCar=true;
+ }
+
+ // note: moved the car open-door locator to the vehicle init
+ bool ok = vehicle->Init( name, GetWorldPhysicsManager()->mSimEnvironment, VL_PHYSICS, vt, startoutofcar);
+ rAssert(ok);
+
+ // so the script hooks can get at it
+ rAssert( mVehicleUnderConstruction[mCurrentVehicleUnderConstructionTail] == NULL );
+ mVehicleUnderConstruction[mCurrentVehicleUnderConstructionTail] = vehicle;
+ mCurrentVehicleUnderConstructionTail = (mCurrentVehicleUnderConstructionTail + 1) % MAX_ACTIVE_VEHICLES;
+
+ //If this happens, we're in big shit. We'll need to up the number of active vehicles or look closer at the loading.
+ rAssert( mCurrentVehicleUnderContructionHead != mCurrentVehicleUnderConstructionTail );
+
+ // see TODO in comment blocks above
+ // DL: now loads the car based on name
+
+ char scriptname[64];
+ strcpy( scriptname, "scripts/cars/" );
+
+ if(confile != NULL && confile[0] != '\0')
+ {
+ strcat( scriptname, confile );
+
+ }
+ else
+ {
+ strcat( scriptname, name );
+ strcat( scriptname, ".con" );
+ }
+
+ vehicle->mDriverInit = driver;
+ GetMissionScriptLoader()->LoadScriptAsync( scriptname, this );
+
+ //vehicle->CalculateValuesBasedOnDesignerParams();
+
+ if(addToActiveVehicleList)
+ {
+ // return slot ignored here...
+ int dummy = AddVehicleToActiveList(vehicle);
+ }
+
+ MEMTRACK_POP_GROUP( "VehicleCentral" );
+
+ HeapMgr()->PopHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+ return vehicle;
+}
+
+
+//=============================================================================
+// VehicleCentral::AddVehicleToActiveList
+//=============================================================================
+// Description: Comment
+//
+// returns index if successful, otherwise -1
+//
+// Parameters: VehicleCentral::AddVehicleToActiveList
+//
+// Return: int
+//
+//=============================================================================
+int VehicleCentral::AddVehicleToActiveList(Vehicle* vehicle)
+{
+
+
+ //==============================================================================
+ //
+ // let's be clear here:
+ //
+ // an 'active' vehicle is one that will be added to the dsg
+ // it will also request a collision area index and put itself in there,
+ // but it won't do it's own query to fill that area unless it's under VL_PHYSICS
+ //
+ //==============================================================================
+
+ // find this vehicle a slot
+ int slot = -1;
+
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ // debug test
+ if(mActiveVehicleList[i])
+ {
+ if(mActiveVehicleList[i] == vehicle)
+ {
+ // this is already in our list!!!!
+ //rAssertMsg( false, "This vehicle is already in the active list\n");
+ return i;
+
+ //chuck: draw the driver
+ if (vehicle->GetDriver() !=NULL)
+ {
+ vehicle->GetDriver()->AddToWorldScene();
+ }
+
+ }
+ }
+
+
+ }
+
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i] == 0)
+ {
+ // got it.
+ slot = i;
+ break;
+ }
+ }
+
+
+ if(slot == -1)
+ {
+ // couldn't find room for it.
+ //?
+ rAssertMsg(0,"Trying to add too many active vehicles!");
+ return -1;
+ }
+
+ //----------
+ // add it in
+ //----------
+
+ mActiveVehicleList[slot] = vehicle;
+
+ if (vehicle->GetDriver() !=NULL)
+ {
+ vehicle->GetDriver()->AddToWorldScene();
+ }
+
+
+
+ // this is perhaps totally redundant right now
+ mNumActiveVehicles++;
+
+ if(mNumActiveVehicles > MAX_ACTIVE_VEHICLES)
+ {
+ rAssertMsg(0,"Too many active vehicles!");
+ return -1;
+ }
+
+
+ //--------------------
+ // trigger volume shit
+ //--------------------
+
+ if( vehicle->mVehicleType == VT_AI &&
+ ::GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT &&
+ !GetGameplayManager()->mIsDemo )
+ {
+ GetTriggerVolumeTracker()->RegisterAI( vehicle );
+ }
+
+
+
+ int id = vehicle->mpEventLocator->GetData();
+ ActionButton::GetInCar* pABHandler = static_cast<ActionButton::GetInCar*>( GetActionButtonManager()->GetActionByIndex(id) );
+ rAssert( dynamic_cast<ActionButton::GetInCar*>( pABHandler ) != NULL );
+ rAssert( pABHandler );
+ pABHandler->SetVehicleId(slot);
+
+
+ // leave AI untouched! Their triggers remain OFF!
+ vehicle->ActivateTriggers(true);
+
+ //-----------
+ // add to dsg
+ //-----------
+ GetRenderManager()->pWorldScene()->Add((DynaPhysDSG*)vehicle);
+
+ vehicle->GetCollisionAreaIndexAndAddSelf();
+
+ int curWorldRenderLayer = GetRenderManager()->rCurWorldRenderLayer();
+ vehicle->SetRenderLayerEnum((RenderEnums::LayerEnum)curWorldRenderLayer);
+
+
+ // temp - for car on car collisions
+ // TODO - remove
+
+ // until the vehicles are moving around in the scenegraph, we need to make sure the cars have a collision pair for each other
+
+ /*
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i] != 0 && i != slot)
+ {
+ vehicle->AddToOtherCollisionArea(mActiveVehicleList[i]->mCollisionAreaIndex);
+ }
+ }
+ */
+
+ //
+ // Notify the world about new user vehicles
+ //
+ if( vehicle->mVehicleType == VT_USER )
+ {
+ GetEventManager()->TriggerEvent( EVENT_USER_VEHICLE_ADDED_TO_WORLD, vehicle );
+ }
+
+ // april 22, 2003
+ // new
+ // vehicle will hold it's slot.
+
+ vehicle->mVehicleCentralIndex = slot;
+
+ vehicle->AddRef();
+
+ return slot;
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::RemoveVehicleFromActiveList
+//=============================================================================
+// Description: Comment
+//
+// returns whether or not it was even there in the first place
+//
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: bool
+//
+//=============================================================================
+bool VehicleCentral::RemoveVehicleFromActiveList(Vehicle* vehicle)
+{
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i] == vehicle)
+ {
+
+ //Chuck: If car getting dumped was the last car used by the player
+ //set the mCurrentVehicle to NULL
+
+
+ // greg
+ // jan 4, 2003
+ //
+ // TODO
+ // not sure if we want this code
+ // mCurrentVehicle should be updated elsewhere
+
+ // PHONEBOOTH CAR SHOULD REPLACE DEFAULT VEHICLE.
+ // this change is going in rsn
+
+ //if (GetGameplayManager()->GetCurrentVehicle() == vehicle)
+ //{
+ // Vehicle* p_vehicle = GetGameplayManager()->GetVehicle(GetGameplayManager()->mDefaultVehicle);
+ // GetGameplayManager()->SetCurrentVehicle(p_vehicle);
+ //}
+
+ // later in the same day
+ //
+ // GameplayManager::DumpCurrentCar ()
+ // now sets mCurrentVehicle to NULL as the name would imply
+
+
+ mActiveVehicleList[i] = 0;
+ mNumActiveVehicles--;
+
+ // make sure controller slot doesn't have junk left lying around in it.
+ if( mActiveVehicleControllerList[i] )
+ {
+ mActiveVehicleControllerList[i]->Release();
+ }
+ //mActiveVehicleControllerList[i] = NULL;
+ mActiveVehicleControllerList[i] = spGenericAI;
+ mActiveVehicleControllerList[i]->AddRef();
+
+ // !! need to remove this from any other vehicles mCurrentDynamics list
+ //
+ // what an ugly pain in the ass:
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(vehicle);
+
+ vehicle->RemoveSelfAndFreeCollisionAreaIndex();
+
+
+ //----------------
+ // remove from DSG
+ //----------------
+
+ int& curRenderLayer = GetRenderManager()->rCurWorldRenderLayer();
+ int orgRenderLayer = curRenderLayer;
+
+ // swtich to the vehicle's render layer
+ curRenderLayer = vehicle->GetRenderLayerEnum();
+
+ //Chuck: if we're removing the car lets check if there is a AI NPC driver in that car
+ if(vehicle->GetDriver() != NULL)
+ {
+ vehicle->GetDriver()->RemoveFromWorldScene();
+ }
+ GetRenderManager()->pWorldScene()->Remove(/*(DynaPhysDSG*)*/ vehicle);
+
+
+ // restore the orignal render layer
+ curRenderLayer = orgRenderLayer;
+
+ // hack
+ //vehicle->Release();
+
+ // leave AI untouched! Their triggers remain OFF!
+ vehicle->ActivateTriggers(false);
+
+ //
+ // Notify the world about deleted user vehicles
+ //
+ if( vehicle->mVehicleType == VT_USER )
+ {
+ GetEventManager()->TriggerEvent( EVENT_USER_VEHICLE_REMOVED_FROM_WORLD, vehicle );
+ }
+
+ // stop tracking AI...
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT )
+ {
+ GetTriggerVolumeTracker()->UnregisterAI( vehicle );
+ }
+
+ vehicle->mVehicleCentralIndex = -1;
+
+ vehicle->Release();
+
+ return true;
+ }
+ /*
+ else if(mActiveVehicleList[i])
+ {
+ // this isn't super-efficient but it's only temporary and should stop Darryl's whining
+
+ // try and remove from all other collision areas just in case it's in there...
+ GetWorldPhysicsManager()->mCollisionManager->
+ RemoveCollisionObject(vehicle->mSimStateArticulated->GetCollisionObject(), mActiveVehicleList[i]->mCollisionAreaIndex);
+
+
+ }
+ */
+
+ }
+
+
+ return false;
+}
+
+
+//=============================================================================
+// VehicleCentral::KillEmAll
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::KillEmAll()
+{
+ // not sure if I should keep this method around, because whoever creates the
+ // vehicle should destroy it
+ //
+ // but the method name is so fucking cool....
+
+
+}
+
+/*
+//=============================================================================
+// VehicleCentral::Suspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Suspend()
+{
+ mSuspended = true;
+}
+
+
+//=============================================================================
+// VehicleCentral::Resume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Resume()
+{
+ mSuspended = false;
+}
+*/
+
+//=============================================================================
+// VehicleCentral::GetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* VehicleCentral::GetVehicle( int id ) const
+{
+ //rAssert( id < this->mNumActiveVehicles );
+ return this->mActiveVehicleList[ id ];
+}
+
+
+//=============================================================================
+// VehicleCentral::SetVehicleController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id, VehicleController* pVehicleController )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SetVehicleController( int id, VehicleController* pVehicleController )
+{
+
+ if ( id >= 0 && id < MAX_ACTIVE_VEHICLES )
+ {
+ //rAssert( id < this->mNumActiveVehicles ); // don't think this is necessary anymore
+ if ( pVehicleController )
+ {
+ pVehicleController->Init();
+ }
+ else
+ {
+ if ( mActiveVehicleControllerList[ id ] )
+ {
+ mActiveVehicleControllerList[ id ]->Shutdown();
+ }
+ pVehicleController = spGenericAI;
+ }
+
+ tRefCounted::Assign( mActiveVehicleControllerList[ id ], pVehicleController );
+ }
+ else
+ {
+ rAssertMsg( 0, "SetVehicleController - id out of range" );
+ }
+}
+
+//Triage hack, only for demo mode, or until Greg actually
+//addrefs and releases --dm 12/01/02
+VehicleController* VehicleCentral::RemoveVehicleController( int mAIIndex )
+{
+ if ( mActiveVehicleControllerList[ mAIIndex ] )
+ {
+ VehicleController* FoundVehicleController = mActiveVehicleControllerList[ mAIIndex ];
+ mActiveVehicleControllerList[ mAIIndex ] = 0;
+ return FoundVehicleController;
+ }
+ return NULL;
+}
+//=============================================================================
+// VehicleCentral::GetVehicleController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: VehicleController
+//
+//=============================================================================
+VehicleController* VehicleCentral::GetVehicleController( int id ) const
+{
+ if( 0 <= id && id < MAX_ACTIVE_VEHICLES )
+ {
+ if( mActiveVehicleControllerList[ id ] == spGenericAI )
+ {
+ return NULL;
+ }
+ return mActiveVehicleControllerList[ id ];
+ }
+ return NULL;
+}
+
+
+//=============================================================================
+// VehicleCentral::GetVehicleId
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* pVehicle )
+//
+// Return: int
+//
+//=============================================================================
+int VehicleCentral::GetVehicleId( Vehicle* pVehicle, bool checkStrict ) const
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if(pVehicle == mActiveVehicleList[i])
+ {
+ return i;
+ }
+ }
+
+ if(checkStrict)
+ {
+ rAssertMsg( false, "Vehicle not found in Vehicle Database!\n" );
+ }
+
+ return (int)-1;
+}
+
+//=============================================================================
+// VehicleCentral::PreSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PreSubstepUpdate( float dt )
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ Vehicle* vehicle = mActiveVehicleList[i];
+ if( vehicle ) // TODO - wrap with render layer test
+ {
+ vehicle->PreSubstepUpdate(dt);
+
+ // Update controller
+ VehicleController* vController = mActiveVehicleControllerList[i];
+ if( vController != 0 )
+ {
+ vController->Update( dt );
+
+ float test = vController->GetGas();
+ //?
+ // safe to just apply these as-is to vehicle?
+ vehicle->SetGas(test);
+
+
+ test = vController->GetBrake( );
+ vehicle->SetBrake( test );
+
+
+ test = vController->GetThrottle();
+ if(test > 0.1f)
+ {
+ vehicle->SetGas( test );
+ }
+ if(test < -0.1f)
+ {
+ vehicle->SetBrake( -test );
+ }
+
+
+ //This sucks.
+ bool isWheel = false;
+ test = vController->GetSteering( isWheel );
+
+
+ // test stick and dpad and set highest fabs
+ float left = 0.0f;
+ if( vController->GetSteerLeft() )
+ {
+ left = vController->GetSteerLeft( );
+ }
+
+ float right = 0.0f;
+ if( vController->GetSteerRight() )
+ {
+ right = vController->GetSteerRight( );
+ }
+
+ if(rmt::Fabs(test) > left && rmt::Fabs(test) > right)
+ {
+ // use stick value
+ vehicle->SetWheelTurnAngle(test, isWheel, dt);
+
+ }
+ else if(right > left)
+ {
+ vehicle->SetWheelTurnAngle(right, false, dt);
+ }
+ else
+ {
+ vehicle->SetWheelTurnAngle(-left, false, dt);
+ }
+
+
+
+ /*
+ // only human controlled vehicles will return a pointer here
+ if(vController->GetSteerLeft())
+ {
+ float left = vController->GetSteerLeft( );
+ if(test <= 0.0f && left > rmt::Fabs(test))
+ {
+ vehicle->SetWheelTurnAngle(-left, false, dt);
+ }
+ }
+ if(vController->GetSteerRight())
+ {
+ float right = vController->GetSteerRight( );
+ if(test >= 0.0f && right > test)
+ {
+ vehicle->SetWheelTurnAngle(right, false, dt);
+ }
+ }
+ */
+
+ test = vController->GetReverse( );
+ vehicle->SetReverse(test);
+
+ test = vController->GetHandBrake();
+ vehicle->SetEBrake(test, dt);
+
+ if( GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_CAR_JUMP_ON_HORN) &&
+ vehicle == GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() ) // vehicle->mVehicleType == VT_USER )
+ {
+ test = vController->GetHorn( );
+ if(test > 0.1f)
+ {
+ vehicle->JumpOnHorn(test);
+ }
+ }
+
+ if( vController->GetHorn() > 0.0f )
+ {
+ // Only the player vehicle's controller uses Set/GetHorn.
+ // AI cars never at any time set the Horn value.
+ if( vehicle == GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() ||
+ ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ // if in a certain game type, use the horn button for speedboost
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ // may or may not do turbo depending on if turbo is available..
+ vehicle->TurboOnHorn();
+ }
+ else
+ {
+ // outside of Supersprint context, we want to fire off an event
+ // so that traffic can honk back and peds can panick.
+ GetEventManager()->TriggerEvent( EVENT_PLAYER_VEHICLE_HORN, vehicle );
+ }
+ }
+ }
+
+
+ }
+ else
+ {
+ vehicle->SetGas(0.0f);
+ vehicle->SetBrake(0.0f);
+ vehicle->SetWheelTurnAngle(0.0f, false, dt);
+ vehicle->SetReverse(0.0f);
+ vehicle->SetEBrake(0.0f, dt);
+ }
+
+ //
+ // [Dusit Matthew Eakkachaichanvet: Dec 10, 2002]
+ // ok, the controller has been updated and knows of
+ // last frame's collision with a vehicle, so
+ // we can clear this value now.
+ //
+ vehicle->mCollidedWithVehicle = false;
+ vehicle->mNormalizedMagnitudeOfVehicleHit = 0.0f;
+ vehicle->mWasHitByVehicle = false;
+
+ }
+ }
+}
+
+//=============================================================================
+// VehicleCentral::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Update(float dt)
+{
+
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ //rAssert( mActiveVehicleList[ i ] );
+ if( mActiveVehicleList[i] &&
+ (GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum()))
+ {
+ BEGIN_PROFILE("ActiveVehicle->Update")
+ mActiveVehicleList[i]->Update( dt );
+ END_PROFILE("ActiveVehicle->Update")
+
+ //rAssertMsg( mActiveVehicleList[i] != NULL, "VehicleCentral::Update - trying to add a null vehicle* pointer!" );
+
+ if ( mActiveVehicleList[i] != NULL )
+ {
+ if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS)
+ {
+ BEGIN_PROFILE("WorldPhysManager()->UpdateDyna")
+ GetWorldPhysicsManager()->UpdateDynamicObjects(dt, mActiveVehicleList[i]->mCollisionAreaIndex);
+ GetWorldPhysicsManager()->UpdateAnimCollisions(dt, mActiveVehicleList[i]->mCollisionAreaIndex);
+ END_PROFILE("WorldPhysManager()->UpdateDyna")
+ // Temp removed this because objects were being updated twice.
+ // more important (for me) to have the character update anim objects.
+ //
+ // TBJ [8/27/2002]
+ //
+ //GetWorldPhysicsManager()->UpdateAnimCollisions(dt, mActiveVehicleList[i]->mCollisionAreaIndex);
+
+ }
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// VehicleCentral::ClearSpot
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& point, float radius)
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::ClearSpot(rmt::Vector& point, float radius, Vehicle* skipCar)
+{
+ rAssert(0); // don't call this
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum() &&
+ mActiveVehicleList[i] != skipCar)
+ {
+
+ rmt::Vector carPosition = mActiveVehicleList[i]->rPosition();
+
+ rmt::Vector vectorToCar = carPosition;
+ vectorToCar.Sub(point);
+
+ float carsWheelBase = mActiveVehicleList[i]->GetWheelBase();
+
+ float dist = vectorToCar.Magnitude();
+ if(dist - carsWheelBase < radius)
+ {
+ // this car has to move back
+
+ float amount = radius - (dist - carsWheelBase);
+
+ // first test
+ // blast it back by some ratio of amount?
+
+ rmt::Vector fix = vectorToCar;
+ fix.NormalizeSafe();
+ fix.Scale(amount);
+
+ carPosition.Add(fix);
+
+ // quick test for fun and safety
+ carPosition.y += 2.0f;
+
+ float ang = mActiveVehicleList[i]->GetFacingInRadians();
+
+ rmt::Matrix m;
+ m.Identity();
+ m.FillRotateXYZ( 0.0f, ang, 0.0f );
+ m.FillTranslate(carPosition);
+
+ mActiveVehicleList[i]->SetTransform(m);
+
+
+ /*
+ rmt::Vector& linearVelocity = mActiveVehicleList[i]->mSimStateArticulated->GetLinearVelocity();
+
+ vectorToCar.NormalizeSafe();
+
+ static float magicshit = 1.0f;
+
+ vectorToCar.Scale(amount * magicshit);
+
+ linearVelocity = vectorToCar;
+ */
+
+ }
+
+
+
+ }
+ }
+
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::PostSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PostSubstepUpdate(float dt)
+{
+
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ //rAssert( mActiveVehicleList[ i ] );
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ mActiveVehicleList[i]->PostSubstepUpdate(dt);
+ }
+ }
+}
+
+
+//=============================================================================
+// VehicleCentral::PreCollisionPrep
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PreCollisionPrep(float dt, bool firstSubstep)
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ mActiveVehicleList[ i ]->PreCollisionPrep(dt, firstSubstep);
+ }
+ }
+}
+
+
+//=============================================================================
+// VehicleCentral::SubmitStatics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SubmitStatics()
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ if(1)
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ // at 200kmh, we cover 55.5 m/s
+ // a long frame would be 50ms...
+ //
+ // at that rate we'd cover 2.775 m
+
+ // hmm.... ferrini wheel base is approx 3m
+
+ //float radius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ Vehicle* playerVehicle = 0;
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+ }
+
+ if(playerVehicle && mActiveVehicleList[i] == playerVehicle)
+ {
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ }
+ else
+ {
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+ }
+
+ // TODO - should probably only do this one if it's a human driver or AI ... ie. not traffic bouncing around
+ //GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+ }
+ else
+ {
+ /*
+ // an active vehicle, but not VL_PHYSICS
+ // so we want to do a DSG query and cleanup of our collision area if necessary
+
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+ GetWorldPhysicsManager()->CleanOnlyStaticsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+
+ // TODO - should probably only do this one if it's a human driver or AI ... ie. not traffic bouncing around
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+ */
+
+ }
+
+ }
+ }
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::SubmitDynamics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SubmitDynamics()
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ // new
+ // call this for all (one) VT_USER cars first
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS && mActiveVehicleList[i]->mVehicleType == VT_USER)
+ if(mActiveVehicleList[i]->mVehicleType == VT_USER)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ //float radius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ Vehicle* playerVehicle = 0;
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+ }
+
+ if(playerVehicle && mActiveVehicleList[i] == playerVehicle)
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ }
+ else
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ }
+ }
+ }
+ }
+
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS && mActiveVehicleList[i]->mVehicleType != VT_USER)
+ if(mActiveVehicleList[i]->mVehicleType != VT_USER)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ //float radius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ Vehicle* playerVehicle = 0;
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+ }
+
+ if(playerVehicle && mActiveVehicleList[i] == playerVehicle)
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ }
+ else
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ }
+
+ //GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ }
+ }
+ }
+
+}
+
+
+//=============================================================================
+// VehicleCentral::SubmitAnimCollisions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SubmitAnimCollisions()
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ if(1)
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ //float collradius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float collradius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+ GetWorldPhysicsManager()->SubmitAnimCollisionsPseudoCallback(position, collradius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+
+ float updateradius = collradius * 1.0f; //? TODO - what magic number
+ GetWorldPhysicsManager()->SubmitAnimCollisionsForUpdateOnly(position, updateradius, collisionAreaIndex);
+ }
+ }
+ }
+
+
+
+}
+
+//=============================================================================
+// VehicleCentral::GetVehicleByName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* VehicleCentral::GetVehicleByName( const char* name ) const
+{
+ return GetVehicleByUID( tEntity::MakeUID( name ) );
+}
+
+/*
+==============================================================================
+VehicleCentral::GetVehicleByUID
+==============================================================================
+Description: Comment
+
+Parameters: ( tUID uid )
+
+Return: Vehicle
+
+=============================================================================
+*/
+Vehicle* VehicleCentral::GetVehicleByUID( tUID uid ) const
+{
+ Vehicle* vehicle = 0;
+ for( int i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if ( mActiveVehicleList[ i ] != 0 )
+ {
+ if( uid == tEntity::MakeUID( mActiveVehicleList[ i ]->GetName() ) )
+ {
+ vehicle = mActiveVehicleList[ i ];
+ break;
+ }
+ }
+ }
+ return vehicle;
+}
+
+
+//=============================================================================
+// VehicleCentral::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::OnProcessRequestsComplete( void* pUserData )
+{
+ Character* theDriver;
+
+ Vehicle* vehicle = GetCurrentVehicleUnderConstruction();
+ rAssert( vehicle );
+
+ vehicle->CalculateValuesBasedOnDesignerParams();
+
+ SetupDriver(vehicle);
+
+ theDriver = vehicle->GetDriver();
+ if( theDriver != NULL )
+ {
+ //
+ // Despite the name, send this for every vehicle load. The dialogue
+ // system keeps a flag to determine which ones come from phone booths,
+ // and throws the rest of 'em out. Not pretty, but we're going final.
+ // -- Esan
+ //
+ GetEventManager()->TriggerEvent( EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED, theDriver );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_PHONE_BOOTH_CANCEL_RIDEREPLY_LINE );
+ }
+
+ //Clear the head.
+ mVehicleUnderConstruction[mCurrentVehicleUnderContructionHead] = NULL;
+ mCurrentVehicleUnderContructionHead = (mCurrentVehicleUnderContructionHead + 1) % MAX_ACTIVE_VEHICLES;
+
+ if ( mCurrentVehicleUnderContructionHead == mCurrentVehicleUnderConstructionTail )
+ {
+ rAssert( mVehicleUnderConstruction[mCurrentVehicleUnderContructionHead] == NULL );
+ }
+}
+
+void VehicleCentral::ActivateVehicleTriggers(bool active)
+{
+ if(active == mbVehicleTriggersActive)
+ {
+ return;
+ }
+
+ mbVehicleTriggersActive = active;
+
+ Vehicle* v = NULL;
+
+ if( mbVehicleTriggersActive )
+ {
+ for(int i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ v = mActiveVehicleList[i];
+ if(v)
+ {
+ v->ActivateTriggers(true);
+ }
+ }
+ }
+ else
+ {
+ for(int i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ v = mActiveVehicleList[i];
+ if( v )
+ {
+ v->ActivateTriggers(false);
+ }
+ }
+ }
+
+}
+
+void VehicleCentral::ClearSuppressedDrivers(void)
+{
+ for(unsigned i = 0; i < mSuppressedDriverCount; i++)
+ {
+ mSuppressedDrivers[i].SetText(NULL);
+ }
+
+ mSuppressedDriverCount = 0;
+}
+
+void VehicleCentral::AddSuppressedDriver(const char* name)
+{
+ rAssert(mSuppressedDriverCount < MAX_SuppressED_DRIVERS);
+ mSuppressedDrivers[mSuppressedDriverCount++].SetText(name);
+}
+
+void VehicleCentral::RemoveSuppressedDriver(const char* name)
+{
+ tUID uid = tEntity::MakeUID(name);
+ for(unsigned i = 0; i < mSuppressedDriverCount; i++)
+ {
+ if(mSuppressedDrivers[i].GetUID() == uid)
+ {
+ mSuppressedDrivers[i].SetUID((tUID)0);
+ return;
+ }
+ }
+}
+
+bool VehicleCentral::IsDriverSuppressed(const char* name)
+{
+ tUID uid = tEntity::MakeUID(name);
+ for(unsigned i = 0; i < mSuppressedDriverCount; i++)
+ {
+ if(mSuppressedDrivers[i].GetUID() == uid)
+ return true;
+ }
+ return false;
+}
+
+void VehicleCentral::SetupDriver(Vehicle* vehicle)
+{
+ if(vehicle->GetDriver())
+ {
+ if((vehicle->GetDriver() != GetCharacterManager()->GetCharacter(0)) && (vehicle->GetDriver()->GetRole() != Character::ROLE_PEDESTRIAN))
+ {
+ GetCharacterManager()->RemoveCharacter(vehicle->GetDriver());
+ vehicle->SetDriver(NULL);
+ }
+ }
+
+ if(vehicle->mDriverInit != FORCE_NO_DRIVER)
+ {
+ if((vehicle->GetDriverName()[0] != 0))
+ {
+ if(strcmp(vehicle->GetDriverName(),"phantom") == 0)
+ {
+ vehicle->SetPhantomDriver(true);
+ }
+ else
+ {
+ if((strcmp(vehicle->GetDriverName(),"none") != 0) &&
+ ((vehicle->mDriverInit == FORCE_DRIVER) || !IsDriverSuppressed(vehicle->GetDriverName())))
+ {
+ char uniqueName[16];
+ sprintf(uniqueName, "d_%s", vehicle->GetDriverName());
+
+ Character* character = NULL;
+ character = GetCharacterManager()->GetCharacterByName(uniqueName);
+ if(character && ((character->GetTargetVehicle() == vehicle) || (character->GetTargetVehicle() == NULL)))
+ {
+ GetCharacterManager()->RemoveCharacter(character);
+ }
+
+ character = GetCharacterManager()->AddCharacter(CharacterManager::NPC, uniqueName, vehicle->GetDriverName(), "npd", "");
+
+ static_cast<NPCController*>(character->GetController())->TransitToState(NPCController::NONE);
+ character->SetTargetVehicle( vehicle );
+ character->AddToWorldScene();
+ character->GetStateManager()->SetState<CharacterAi::InCar>();
+
+ vehicle->SetDriver(character);
+
+ character->SetRole(Character::ROLE_DRIVER);
+ }
+ }
+ }
+ }
+}
+
+
+//chuck adding this method so we can determine if car is still underconstruction.
+
+bool VehicleCentral::IsCarUnderConstruction(const char* name)
+{
+ //unsigned int pListPtr = mCurrentVehicleUnderContructionHead;
+
+ for(int i=0;i<MAX_ACTIVE_VEHICLES;i++)
+ {
+ if (mVehicleUnderConstruction[i] != NULL)
+ {
+ if( strcmp(mVehicleUnderConstruction[i]->GetName(),name) == 0)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool VehicleCentral::IsCarUnderConstruction(const Vehicle* vehicle)
+{
+ for(int i=0;i<MAX_ACTIVE_VEHICLES;i++)
+ {
+ if (mVehicleUnderConstruction[i] != NULL)
+ {
+ if( mVehicleUnderConstruction[i] == vehicle )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+VehicleAI* VehicleCentral::GetVehicleAI( Vehicle* vehicle )
+{
+ if( vehicle )
+ {
+ //////////////////////////
+ // if Demo context, it's ok to fetch AI for
+ // non VT_AI types. Otherwise, we must boot!!
+ //
+ // Note:
+ // Supersprint doesn't worry about these, the AI cars should
+ // be VT_AI...
+ //
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_DEMO &&
+ vehicle->mVehicleType != VT_AI )
+ {
+ return NULL;
+ }
+
+ int id = GetVehicleCentral()->GetVehicleId( vehicle );
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController( id );
+ if( controller )
+ {
+ rAssert( dynamic_cast<VehicleAI*>( controller ) );
+ return static_cast<VehicleAI*>( controller );
+ }
+ }
+ return NULL;
+}
+
+
+void VehicleCentral::DetachAllCollectibles()
+{
+ for ( int i = 0 ; i < GetNumVehicles() ; i++ )
+ {
+ Vehicle* v = GetVehicle( i );
+ if ( v != NULL )
+ {
+ v->DetachCollectible( rmt::Vector(0,0,0), false );
+ }
+ }
+}
diff --git a/game/code/worldsim/vehiclecentral.h b/game/code/worldsim/vehiclecentral.h
new file mode 100644
index 0000000..488846f
--- /dev/null
+++ b/game/code/worldsim/vehiclecentral.h
@@ -0,0 +1,205 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclecentral.h
+//
+// Description: holds a list of all the vehicles in the game and provides
+// easy access for the rest of the game objects that care
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+#ifndef VEHICLECENTRAL_H
+#define VEHICLECENTRAL_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+#include <loading/loadingmanager.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/huskpool.h>
+
+
+
+//========================================
+// Forward References
+//========================================
+class VehicleAI;
+class AiVehicleController;
+class VehicleController;
+class EventLocator;
+class tBillboardQuadGroup;
+
+//=============================================================================
+//
+// Synopsis: they lived happily ever after....
+//
+//=============================================================================
+
+
+
+class VehicleCentral : public LoadingManager::ProcessRequestsCallback
+{
+ public:
+ enum DriverInit
+ {
+ ALLOW_DRIVER,
+ FORCE_NO_DRIVER,
+ FORCE_DRIVER
+ };
+
+ // Static Methods for accessing this singleton.
+ static VehicleCentral* GetInstance();
+ static VehicleCentral* CreateInstance();
+ static void DestroyInstance();
+
+ void PreLoad();
+ void Unload();
+
+ Vehicle* InitVehicle( const char* name, bool addToActiveVehicleList = true, char* confile = 0, VehicleType vt = VT_USER,
+ DriverInit s = ALLOW_DRIVER, bool playercar = false, bool startoutofcar = true);
+
+ // returns index if successful, otherwise -1
+ int AddVehicleToActiveList(Vehicle* vehicle);
+ // also make this put vehicles in the dsg.
+ //
+ // this is also where the vehicle should ask for a collision index, and insert itself
+
+ // returns whether or not it was even there in the first place
+ bool RemoveVehicleFromActiveList(Vehicle* vehicle);
+ // remove from dsg
+
+ //Triage hack, only for demo mode, or until Greg actually
+ //addrefs and releases --dm 12/01/02
+ VehicleController* RemoveVehicleController( int mAIIndex );
+
+ void KillEmAll();
+
+ void ClearSpot(rmt::Vector& point, float radius, Vehicle* skipCar);
+
+ //void Suspend(); // temporarily freeze all action - leave vehicles in the active list
+ //void Resume(); // unfreeze
+
+ int GetNumVehicles() const { return mNumActiveVehicles; }
+ Vehicle* GetVehicle( int id ) const;
+
+ void SetupConsoleFunctionsForVehicleTuning();
+
+ void SetVehicleController( int id, VehicleController* pVehicleController );
+ VehicleController* GetVehicleController( int id ) const;
+
+ int GetVehicleId( Vehicle* pVehicle, bool checkStrict = true ) const;
+
+ void SubmitStatics();
+ void SubmitDynamics();
+ void SubmitAnimCollisions();
+
+
+ void PreSubstepUpdate(float dt);
+ void PostSubstepUpdate(float dt);
+ void Update(float dt);
+ void PreCollisionPrep(float dt, bool firstSubstep);
+
+ enum { MAX_ACTIVE_VEHICLES = 30 };
+
+ static int GetMaxActiveVehicles() {return MAX_ACTIVE_VEHICLES;}
+
+ // *** //
+ void GetActiveVehicleList(Vehicle** &vList, int& nVehicles);
+ bool ActiveVehicleListIsFull() const;
+ // *** //
+
+ // hmmm... is there a nicer way to do this?
+ // need this so we have object to call script hooks on
+ Vehicle* mVehicleUnderConstruction[MAX_ACTIVE_VEHICLES];
+ unsigned int mCurrentVehicleUnderContructionHead;
+ unsigned int mCurrentVehicleUnderConstructionTail;
+ Vehicle* GetCurrentVehicleUnderConstruction() { return mVehicleUnderConstruction[ mCurrentVehicleUnderContructionHead ]; };
+ void OnProcessRequestsComplete( void* pUserData );
+
+
+ Vehicle* GetVehicleByName( const char* name ) const;
+ Vehicle* GetVehicleByUID( tUID uid ) const;
+
+ void ActivateVehicleTriggers(bool);
+
+ HuskPool mHuskPool; // just use default constructor
+
+ void InitHuskPool();
+ void FreeHuskPool();
+
+ void ClearSuppressedDrivers(void);
+ void AddSuppressedDriver(const char* name);
+ void RemoveSuppressedDriver(const char* name);
+ bool IsDriverSuppressed(const char* name);
+
+ void SetupDriver(Vehicle*);
+
+ //
+ bool IsCarUnderConstruction(const char* name);
+ bool IsCarUnderConstruction(const Vehicle* vehicle);
+
+ bool GetVehicleTriggersActive(void) {return mbVehicleTriggersActive;}
+
+ VehicleAI* GetVehicleAI( Vehicle* vehicle );
+
+ // Removes all collectibles attached to various vehicles
+ void DetachAllCollectibles();
+
+ // store the headlights here!
+ enum
+ {
+ NUM_HEADLIGHT_BBQGS = 3,
+ NUM_HEADLIGHT_BBQS = 7 // combined total of BBQs of all headlight BBQGs
+ };
+ tBillboardQuadGroup* mHeadLights[NUM_HEADLIGHT_BBQGS];
+ tColour mOriginalHeadLightColours[NUM_HEADLIGHT_BBQS];
+
+ private:
+
+ // No public access to these, use singleton interface.
+ VehicleCentral();
+ ~VehicleCentral();
+
+
+ // pointer to the single instance
+ static VehicleCentral* spInstance;
+
+ Vehicle* mActiveVehicleList[MAX_ACTIVE_VEHICLES];
+ VehicleController* mActiveVehicleControllerList[ MAX_ACTIVE_VEHICLES ];
+ //EventLocator* mDoorTriggerList[ MAX_ACTIVE_VEHICLES ];
+ int mNumActiveVehicles;
+
+ //bool mSuspended;
+
+ bool mbVehicleTriggersActive;
+
+ static const unsigned int MAX_SuppressED_DRIVERS = 32;
+ unsigned int mSuppressedDriverCount;
+ tName mSuppressedDrivers[MAX_SuppressED_DRIVERS];
+
+ static AiVehicleController* spGenericAI;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline VehicleCentral* GetVehicleCentral() { return( VehicleCentral::GetInstance() ); }
+
+
+
+// *** //
+inline void VehicleCentral::GetActiveVehicleList(Vehicle** &vList, int& nVehicles)
+{
+ vList = mActiveVehicleList;
+ nVehicles = mNumActiveVehicles;
+}
+inline bool VehicleCentral::ActiveVehicleListIsFull() const
+{
+ return (mNumActiveVehicles>=MAX_ACTIVE_VEHICLES);
+}
+// *** //
+
+#endif //VEHICLECENTRAL_H
diff --git a/game/code/worldsim/worldcollisionsolveragent.cpp b/game/code/worldsim/worldcollisionsolveragent.cpp
new file mode 100644
index 0000000..3362c34
--- /dev/null
+++ b/game/code/worldsim/worldcollisionsolveragent.cpp
@@ -0,0 +1,365 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class WorldCollisionSolverAgentManager
+//
+// History: 6/14/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/worldcollisionsolveragent.h>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
+#include <worldsim/physicsairef.h>
+#include <memory/srrmemory.h>
+
+#include <camera/supercamcentral.h>
+
+#include <render/DSG/collisionentitydsg.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WorldCollisionSolverAgentManager::WorldCollisionSolverAgentManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldCollisionSolverAgentManager::WorldCollisionSolverAgentManager()
+{
+ mpCollisionSolverAgentArray[ 0 ] = new(GMA_PERSISTENT) RedBrickCollisionSolverAgent;
+ mpCollisionSolverAgentArray[ 0 ]->AddRef();
+}
+
+//==============================================================================
+// WorldCollisionSolverAgentManager::~WorldCollisionSolverAgentManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldCollisionSolverAgentManager::~WorldCollisionSolverAgentManager()
+{
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ if ( mpCollisionSolverAgentArray[ i ] )
+ {
+ mpCollisionSolverAgentArray[ i ]->Release();
+ mpCollisionSolverAgentArray[ i ] = 0;
+ }
+ }
+}
+
+
+
+
+//=============================================================================
+// WorldCollisionSolverAgentManager::CollisionEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (SimState* inSimStateA, int indexA, SimState* inSimStateB, int indexB, const rmt::Vector& inPos, float inDvN, float inDvT, SimulatedObject** simA, SimulatedObject** simB)
+//
+// Return: Solving_Answer
+//
+//=============================================================================
+Solving_Answer WorldCollisionSolverAgentManager::CollisionEvent(SimState* inSimStateA, int indexA, SimState* inSimStateB, int indexB, const rmt::Vector& inPos, float inDvN, float inDvT, SimulatedObject** simA, SimulatedObject** simB)
+{
+ return Solving_Continue;
+}
+
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::PreCollisionEvent
+==============================================================================
+Description: Comment
+
+Parameters: (Collision& inCollision, int inPass)
+
+Return: Solving_Answer
+
+=============================================================================
+*/
+Solving_Answer WorldCollisionSolverAgentManager::PreCollisionEvent(Collision& inCollision, int inPass)
+{
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+
+ if(inCollision.mCollisionVolumeA->Type() == sim::BBoxVolumeType || inCollision.mCollisionVolumeB->Type() == sim::BBoxVolumeType)
+ {
+ return Solving_Aborted;
+ }
+
+
+
+
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->PreCollisionEvent( inCollision, inPass ) )
+ {
+ return Solving_Aborted;
+ }
+ }
+
+
+
+ // this is a bit messy, but I don't want to have to derive supercamcentral from CollisionEntityDSG
+ if(simStateA && simStateA->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ rmt::Vector fixOffset = inCollision.mNormal;
+ float dist = inCollision.mDistance;
+
+ if(dist < 0.0f)
+ {
+ dist *= -1.0f;
+ }
+
+ fixOffset.Scale(dist);
+
+ SuperCamCentral* scc = (SuperCamCentral*)(simStateA->mAIRefPointer);
+ scc->AddCameraCollisionOffset(fixOffset);
+
+ return Solving_Aborted;
+
+ }
+ if(simStateB && simStateB->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ // normal always points B to A
+ // distance -ve means interpenetration
+
+ rmt::Vector fixOffset = inCollision.mNormal;
+
+
+ float dist = inCollision.mDistance;
+
+ if(dist < 0.0f)
+ {
+ dist *= -1.0f;
+ }
+
+ fixOffset.Scale(-1.0f * dist);
+
+ SuperCamCentral* scc = (SuperCamCentral*)(simStateB->mAIRefPointer);
+ scc->AddCameraCollisionOffset(fixOffset);
+
+ return Solving_Aborted;
+
+ }
+
+
+
+ /*
+ if( simStateA && simStateB && ( simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane) )
+ {
+ //char buffy[128];
+ rReleaseString("something collided with a moveableobject groundplane\n");
+
+ }
+ */
+
+ CollisionEntityDSG* pObjA = static_cast<CollisionEntityDSG*>( simStateA->mAIRefPointer );
+ CollisionEntityDSG* pObjB = static_cast<CollisionEntityDSG*>( simStateB->mAIRefPointer );
+
+ //bool bSolveA = false;
+ //bool bSolveB = false;
+ Solving_Answer answerA = Solving_Continue;
+ Solving_Answer answerB = Solving_Continue;
+
+ if ( pObjA )
+ {
+ //bSolveA = pObjA->ReactToCollision( simStateB, inCollision );
+ answerA = pObjA->PreReactToCollision( simStateB, inCollision );
+ }
+ if ( pObjB )
+ {
+ //bSolveB = pObjB->ReactToCollision( simStateA, inCollision );
+ answerB = pObjB->PreReactToCollision( simStateA, inCollision );
+ }
+ //if ( bSolveA || bSolveB )
+ if(answerA == Solving_Aborted || answerB == Solving_Aborted)
+ {
+ return Solving_Aborted;
+ }
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+}
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::TestImpulse
+==============================================================================
+Description: Comment
+
+Parameters: (rmt::Vector& mImpulse, Collision& inCollision)
+
+Return: Solving_Answer
+
+=============================================================================
+*/
+Solving_Answer WorldCollisionSolverAgentManager::TestImpulse(rmt::Vector& impulse, Collision& inCollision)
+{
+ int i;
+
+ // vehicle is special and will be dealt with in the redbrick collision solver agent
+
+ // other ordinary shit will get the PostReactToCollision call here.
+
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->TestImpulse( impulse, inCollision ) )
+ {
+ return Solving_Aborted;
+ }
+ }
+
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ CollisionEntityDSG* pObjA = static_cast<CollisionEntityDSG*>( simStateA->mAIRefPointer );
+ CollisionEntityDSG* pObjB = static_cast<CollisionEntityDSG*>( simStateB->mAIRefPointer );
+
+ Solving_Answer answerA = Solving_Continue;
+ Solving_Answer answerB = Solving_Continue;
+
+ if(pObjA)
+ {
+ answerA = pObjA->PostReactToCollision(impulse, inCollision);
+ }
+ if(pObjB)
+ {
+ answerB = pObjB->PostReactToCollision(impulse, inCollision);
+ }
+
+ if(answerA == Solving_Aborted || answerB == Solving_Aborted)
+ {
+ return Solving_Aborted;
+ }
+
+ /*
+ if( simStateA && simStateB && ( simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane) )
+ {
+ //char buffy[128];
+ rReleaseString("something is testing impulse with a moveableobject groundplane\n");
+
+ }
+ */
+
+
+ return CollisionSolverAgent::TestImpulse(impulse, inCollision);
+
+
+}
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::TestCache
+==============================================================================
+Description: Comment
+
+Parameters: (SimState* inSimState, int inIndex)
+
+Return: Solving_Answer
+
+=============================================================================
+*/
+Solving_Answer WorldCollisionSolverAgentManager::TestCache(SimState* inSimState, int inIndex)
+{
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->TestCache( inSimState, inIndex ) )
+ {
+ return Solving_Aborted;
+ }
+ }
+ return Solving_Continue;
+}
+
+
+
+
+Solving_Answer WorldCollisionSolverAgentManager::EndObjectCollision(SimState* inSimState, int inIndex)
+{
+
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->EndObjectCollision(inSimState, inIndex) )
+ {
+ return Solving_Aborted;
+ }
+ }
+ return Solving_Continue;
+
+}
+
+
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::ResetCollisionFlags
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void WorldCollisionSolverAgentManager::ResetCollisionFlags()
+{
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ mpCollisionSolverAgentArray[ i ]->ResetCollisionFlags();
+ }
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/worldcollisionsolveragent.h b/game/code/worldsim/worldcollisionsolveragent.h
new file mode 100644
index 0000000..c624e01
--- /dev/null
+++ b/game/code/worldsim/worldcollisionsolveragent.h
@@ -0,0 +1,80 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldcollisionsolveragent.h
+//
+// Description: Blahblahblah
+//
+// History: 6/14/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef WORLDCOLLISIONSOLVERAGENT_H
+#define WORLDCOLLISIONSOLVERAGENT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <simcollision/impulsebasedcollisionsolver.hpp>
+
+//========================================
+// Forward References
+//========================================
+class RedBrickCollisionSolverAgent;
+
+using namespace sim;
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+class WorldCollisionSolverAgent
+:
+public CollisionSolverAgent
+{
+public:
+ virtual void ResetCollisionFlags() {};
+};
+
+class WorldCollisionSolverAgentManager
+:
+public CollisionSolverAgent
+{
+public:
+ WorldCollisionSolverAgentManager();
+ ~WorldCollisionSolverAgentManager();
+
+ // the key method to override
+ Solving_Answer PreCollisionEvent(Collision& inCollision, int inPass);
+ Solving_Answer TestImpulse(rmt::Vector& mImpulse, Collision& inCollision);
+ Solving_Answer TestCache(SimState* inSimState, int inIndex);
+
+
+ Solving_Answer EndObjectCollision(SimState* inSimState, int inIndex);
+
+
+ // need to override this so that the sim library version doesn't automatically
+ // switch from ai to sim ctrl when objects are hit
+
+ // this method allows to trigger sounds, animation and modify the objects state.
+ Solving_Answer CollisionEvent( SimState* inSimStateA, int indexA,
+ SimState* inSimStateB, int indexB,
+ const rmt::Vector& inPos, float inDvN, float inDvT,
+ SimulatedObject** simA, SimulatedObject** simB);
+
+
+
+
+ void ResetCollisionFlags();
+private:
+
+ //Prevent wasteful constructor creation.
+ WorldCollisionSolverAgentManager( const WorldCollisionSolverAgent& worldcollisionsolveragent );
+ WorldCollisionSolverAgentManager& operator=( const WorldCollisionSolverAgentManager& worldcollisionsolveragent );
+
+ static const int NUM_SOLVERS = 1;
+ WorldCollisionSolverAgent* mpCollisionSolverAgentArray[ NUM_SOLVERS ];
+};
+
+
+#endif //WORLDCOLLISIONSOLVERAGENT_H
diff --git a/game/code/worldsim/worldobject.cpp b/game/code/worldsim/worldobject.cpp
new file mode 100644
index 0000000..e5726f6
--- /dev/null
+++ b/game/code/worldsim/worldobject.cpp
@@ -0,0 +1,125 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldobject.cpp
+//
+// Description: Implement WorldObject
+//
+// History: 14/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/worldobject.h>
+#include <debug/profiler.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WorldObject::WorldObject
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldObject::WorldObject( tDrawable* drawable )
+{
+ if( drawable != NULL )
+ {
+ mDrawable = drawable;
+ mDrawable->AddRef();
+ }
+ else
+ {
+ mDrawable = NULL;
+ }
+
+ mTransform.Identity();
+}
+
+//==============================================================================
+// WorldObject::~WorldObject
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldObject::~WorldObject()
+{
+ if( mDrawable != NULL )
+ {
+ mDrawable->Release();
+ }
+}
+
+//=============================================================================
+// WorldObject::SetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector &position )
+//
+// Return: void
+//
+//=============================================================================
+void WorldObject::SetPosition( rmt::Vector &position )
+{
+ mTransform.FillTranslate( position );
+}
+
+//=============================================================================
+// WorldObject::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldObject::Display()
+{
+ BEGIN_PROFILE("WorldObject::Display")
+ if( mDrawable != NULL )
+ {
+ p3d::stack->PushMultiply( mTransform );
+
+ mDrawable->Display();
+
+ p3d::stack->Pop();
+ }
+ END_PROFILE("WorldObject::Display")
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/worldobject.h b/game/code/worldsim/worldobject.h
new file mode 100644
index 0000000..3cbc3a7
--- /dev/null
+++ b/game/code/worldsim/worldobject.h
@@ -0,0 +1,60 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 14/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef WORLDOBJECT_H
+#define WORLDOBJECT_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+#include <radmath/radmath.hpp>
+#include <p3d/drawable.hpp>
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WorldObject : public tDrawable
+{
+ public:
+ WorldObject( tDrawable* drawable );
+ virtual ~WorldObject();
+
+ void SetPosition( rmt::Vector &position );
+
+ virtual void Display();
+
+ tDrawable* GetDrawable() { return( mDrawable ); }
+ private:
+ //Prevent wasteful constructor creation.
+ WorldObject( const WorldObject& worldObject );
+ WorldObject& operator=( const WorldObject& worldObject );
+
+ tDrawable* mDrawable;
+ rmt::Matrix mTransform;
+};
+
+
+// Hack name for inventory section to hold sim library caches of SkeletonInfo.
+// Tracked to ATG as bug 1259.
+//
+const char SKELCACHE[] = "SkeletonInfoCache";
+
+
+#endif // WORLDOBJECT_H
+
diff --git a/game/code/worldsim/worldphysicsmanager.cpp b/game/code/worldsim/worldphysicsmanager.cpp
new file mode 100644
index 0000000..9a3eb24
--- /dev/null
+++ b/game/code/worldsim/worldphysicsmanager.cpp
@@ -0,0 +1,2921 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldphysicsmanager.cpp
+//
+// Description: bleek
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <raddebug.hpp>
+
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionmanager.hpp>
+#include <simcommon/physicsproperties.hpp>
+
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisiondisplay.hpp>
+#include <simcommon/simutility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/animcollisionentitydsg.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/worldcollisionsolveragent.h>
+
+#include <worldsim/groundplanepool.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <memory/srrmemory.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/physicslocomotion.h> // just for debug prinout
+
+#include <camera/supercammanager.h>
+
+#include <debug/profiler.h>
+#include <debug/debuginfo.h>
+
+
+#include <render/Culling/ReserveArray.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/StatePropDSG.h>
+#include <render/DSG/FenceEntityDSG.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <constants/maxplayers.h>
+#include <constants/maxnpccharacters.h>
+
+#include <sound/soundmanager.h>
+
+#include <mission/gameplaymanager.h>
+
+// TODO
+// can remove
+// just here to hack in a light so I can see the texture damage states better
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+WorldPhysicsManager* WorldPhysicsManager::spInstance = 0;
+
+//=============================================================================
+// WorldPhysicsManager::WorldPhysicsManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager::WorldPhysicsManager()
+{
+ mCollisionDistanceCGS = 2.0f;
+ mWorldUp.Set(0.0f, 1.0f, 0.0f);
+ mTotalTime = 0.0f;
+ updateFrame = 0;
+ mLoopTime = 0.0f;
+
+ sim::InitializeSimulation(MetersUnits);
+
+ //SetSimUnits();
+
+ // move to bootupcontext onstart
+ //Init();
+ mLastTime = 30;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::~WorldPhysicsManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager::~WorldPhysicsManager()
+{
+ mSimEnvironment = 0;
+
+ int i, j;
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ for( j = 0; j < mMaxFencePerArea; j++ )
+ {
+ mFences[i][j].mFenceSimState->Release();
+ }
+
+ delete [] mCurrentStatics[i];
+ delete [] mCurrentAnimCollisions[i];
+ delete [] mCurrentUpdateAnimCollisions[i];
+ delete [] mFences[i];
+ delete [] mCurrentDynamics[i];
+ }
+ delete [] mCurrentStatics;
+ delete [] mCurrentAnimCollisions;
+ delete [] mCurrentUpdateAnimCollisions;
+ delete [] mFences;
+ delete [] mFencesInEachArea;
+ delete [] mCurrentDynamics;
+ delete mGroundPlanePool;
+
+ for( i = 0; i < mMaxFencePerArea; i++ )
+ {
+ mFenceDSGResults[i]->Release();
+ }
+ delete[] mFenceDSGResults;
+
+ mFencePhysicsProperties->Release();
+
+ mCollisionManager->Release();
+
+ mpWorldCollisionSolverAgentManager->Release();
+
+ sim::CleanupLineDrawing ();
+
+ delete [] mCollisionAreaAllocated;
+ delete [] mCollisionAreaActive;
+
+ sim::ResetSimulation ();
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager* WorldPhysicsManager::GetInstance()
+{
+ rAssert(spInstance);
+ return spInstance;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::CreateInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager* WorldPhysicsManager::CreateInstance()
+{
+ rAssert(spInstance == 0);
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ spInstance = new WorldPhysicsManager;
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ spInstance = new WorldPhysicsManager;
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+#endif
+ rAssert(spInstance);
+
+ return spInstance;
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::Init
+//=============================================================================
+// Description:
+// TODO - need this? - where to call it from?
+// ok to call from CreateSingletons
+//
+// no - it would be called from the a context OnStart if necessary
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::Init()
+{
+ mSimEnvironment = sim::SimEnvironment::GetDefaultSimEnvironment();
+
+ rAssert(mSimEnvironment);
+ //mSimEnvironment->AddRef(); // TODO
+ mSimEnvironment->SetCollisionDistanceCGS(mCollisionDistanceCGS);
+
+ //mSimEnvironment->SetGravityCGS(0.0f, -981.0f, 0.0f);
+ //mSimEnvironment->SetGravityCGS(0.0f, -2000.0f, 0.0f);
+ mSimEnvironment->SetGravityCGS(0.0f, -1600.0f, 0.0f); // plum's setting
+ //mSimEnvironment->SetGravityCGS(0.0f, -2500.0f, 0.0f);
+ mTimerTime = 0.0f;
+ mTimerOn = false;
+
+ InitCollisionManager();
+
+ sim::SetupLineDrawing ();
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::StartTimer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::StartTimer()
+{
+ mTimerOn = true;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::StopTimer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::StopTimer()
+{
+ mTimerOn = false;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::ResetTimer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::ResetTimer()
+{
+ mTimerTime = 0.0f;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::ToggleTimerState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::ToggleTimerState()
+{
+ if(mTimerOn)
+ {
+ mTimerOn = false;
+ }
+ else if(mTimerTime > 0.0f)
+ {
+ mTimerTime = 0.0f;
+ }
+ else
+ {
+ mTimerOn = true;
+ }
+}
+
+int WorldPhysicsManager::ApplyForceToDynamics( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ const rmt::Vector& direction,
+ float force,
+ WorldPhysicsManager::NumObjectsHit* pObjectsHit,
+ CollisionEntityDSGList* pCollisionEntityList )
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ int i;
+ int numObjectsAffected = 0;
+ float radiusSqr = radius * radius;
+
+ if( pCollisionEntityList != NULL )
+ {
+ //
+ // Initialize entity list
+ //
+ for( i = 0; i < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES; i++ )
+ {
+ pCollisionEntityList->collisionEntity[i] = NULL;
+ }
+ }
+
+ for (i = 0 ; i < mMaxDynamics ; i++)
+ {
+ DynaPhysDSG* pDSG = mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG;
+ if ( pDSG != NULL )
+ {
+ rmt::Vector objPosition;
+ rmt::Box3D boundingBox;
+ pDSG->GetBoundingBox( &boundingBox );
+ rmt::Sphere boundingSphere( position, radius );
+ pDSG->GetPosition(&objPosition);
+ if ( boundingBox.Intersects( boundingSphere ) )
+ {
+ if ( pDSG->IsCollisionEnabled() )
+ {
+ pDSG->ApplyForce( direction, force );
+
+ if( ( pCollisionEntityList != NULL )
+ && ( numObjectsAffected < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES ) )
+ {
+ pCollisionEntityList->collisionEntity[numObjectsAffected] = pDSG;
+ }
+ ++numObjectsAffected;
+ }
+ }
+ }
+ }
+ return numObjectsAffected;
+}
+
+int WorldPhysicsManager::ApplyForceToDynamicsSpherical( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ float force,
+ CollisionEntityDSGList* pCollisionEntityList )
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ int i;
+ int numObjectsAffected = 0;
+ float radiusSqr = radius * radius;
+
+ if( pCollisionEntityList != NULL )
+ {
+ //
+ // Initialize entity list
+ //
+ for( i = 0; i < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES; i++ )
+ {
+ pCollisionEntityList->collisionEntity[i] = NULL;
+ }
+ }
+
+ for (i = 0 ; i < mMaxDynamics ; i++)
+ {
+ DynaPhysDSG* pDSG = mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG;
+ if ( pDSG != NULL )
+ {
+ rmt::Vector objPosition;
+ rmt::Box3D boundingBox;
+ pDSG->GetBoundingBox( &boundingBox );
+ rmt::Sphere boundingSphere( position, radius );
+ pDSG->GetPosition(&objPosition);
+ if ( boundingBox.Intersects( boundingSphere ) )
+ {
+ if ( pDSG->IsCollisionEnabled() )
+ {
+ // Calculate the vector from the position to the object
+ rmt::Vector direction = boundingBox.Mid() - position;
+ direction.Normalize();
+ pDSG->ApplyForce( direction, force );
+ if( ( pCollisionEntityList != NULL )
+ && ( numObjectsAffected < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES ) )
+ {
+ pCollisionEntityList->collisionEntity[numObjectsAffected] = pDSG;
+ }
+
+ ++numObjectsAffected;
+ }
+ }
+ }
+ }
+ return numObjectsAffected;
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::InitCollisionManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::InitCollisionManager()
+{
+MEMTRACK_PUSH_GROUP( "WorldPhysicsManager" );
+
+ mCollisionManager = CollisionManager::GetInstance();
+ mCollisionManager->AddRef();
+
+ //mCollisionManager->SetCollisionDistanceCGS(mCollisionDistanceCGS);
+
+ //mCollisionManager->SetCollisionManagerAttributes(CM_DetectIfMoving); // TODO - which attribute should this be?
+ mCollisionManager->SetCollisionManagerAttributes(CM_DetectAll); // TODO - should be able to use IfMoving
+
+
+
+ //mReservedCollisionAreas = 2;
+ mReservedCollisionAreas = 0;
+
+ // setup areas:
+ //
+ // total number of areas = max allowed number of active vehicles
+ // + max active (human) characters {always 1 I think}
+ // + cameras for active cars or characters
+ //
+ // (new) + initial reserved slots
+
+ //mMaxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+
+ //mMaxChars = GetCharacterManager()->GetMaxCharacters();
+
+ // new
+ // jan 12, 2003
+ // test
+ // values for vehicles and especially characters are now way too high
+ //
+ // only need slots for shit that is in existence simultaneously
+
+ // 10 is probably enough
+ mMaxVehicles = 15;
+
+ // 12 - 15 would probably be enough
+ mMaxChars = 18;
+
+
+
+ mMaxCameras = MAX_PLAYERS;
+
+ // this total is the number of collision areas we need + reserved
+
+ // following indexing standard will be used into the collision areas array:
+
+ /*
+
+ reserved:
+
+ 0
+ 1
+ 2
+ .
+ .
+ .
+ mReservedCollisionAreas - 1
+
+ vehicles:
+
+ mReservedCollisionAreas
+ .
+ .
+ .
+ .
+ .
+ mReservedCollisionAreas + maxVehicle - 1
+
+
+ characters:
+
+ mReservedCollisionAreas + maxVehicle
+ .
+ .
+ mReservedCollisionAreas+ maxVehicle + maxChars - 1
+
+ cameras:
+
+ mReservedCollisionAreas + maxVehicle + maxChars
+ .
+ .
+ mReservedCollisionAreas + maxVehicle + maxChars + maxCameras - 1
+
+
+ */
+
+ mNumCollisionAreas = mReservedCollisionAreas + mMaxVehicles + mMaxChars + mMaxCameras;
+ mCollisionManager->SetNbArea(mNumCollisionAreas);
+
+ // goddamn, goddamn!!
+ mCollisionManager->SetUseExclusiveAutoPair(true);
+
+ mCollisionManager->ActivateAllAreas();
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ mCollisionAreaAllocated = new bool[mNumCollisionAreas];
+ mCollisionAreaActive = new bool[mNumCollisionAreas];
+
+ //--------
+ // statics
+ //--------
+
+ // TODO - find the right number here
+ // TODO - different depending on thing?
+ mMaxStatics = 30;
+
+ mCurrentStatics = new StaticsInCollisionDetection*[mNumCollisionAreas];
+
+ int i,j;
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCollisionAreaAllocated[i] = false;
+ mCollisionAreaActive[i] = false;
+
+ mCurrentStatics[i] = new StaticsInCollisionDetection[mMaxStatics];
+
+ for(j = 0; j < mMaxStatics; j++)
+ {
+ mCurrentStatics[i][j].mStaticPhysDSG = 0;
+ mCurrentStatics[i][j].clean = false;
+ }
+ }
+
+
+ //----------------
+ // anim collisions
+ //----------------
+
+ // Todo: TBJ - make this value smaller.
+ // It has to be larger because of the large radius set in CharacterManager.
+ // When anim objects move in the DSG, we can reduce this to a smaller number.
+ //
+ mMaxAnimCollisions = 20;
+
+ mCurrentAnimCollisions = new AnimCollisionInCollisionDetection*[mNumCollisionAreas];
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCurrentAnimCollisions[i] = new AnimCollisionInCollisionDetection[mMaxAnimCollisions];
+
+ for(j = 0; j < mMaxAnimCollisions; j++)
+ {
+ mCurrentAnimCollisions[i][j].mAnimCollisionEntityDSG = 0;
+ mCurrentAnimCollisions[i][j].clean = false;
+ }
+ }
+
+
+ mMaxUpdateAnimCollisions = 64;
+
+ mCurrentUpdateAnimCollisions = new AnimCollisionInCollisionDetection*[mNumCollisionAreas];
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCurrentUpdateAnimCollisions[i] = new AnimCollisionInCollisionDetection[mMaxUpdateAnimCollisions];
+
+ for(j = 0; j < mMaxUpdateAnimCollisions; j++)
+ {
+ mCurrentUpdateAnimCollisions[i][j].mAnimCollisionEntityDSG = 0;
+ mCurrentUpdateAnimCollisions[i][j].clean = false;
+ }
+ }
+
+ //-------
+ // fences
+ //-------
+
+ // TODO - find the right number here!
+ mMaxFencePerArea = 8;
+
+ mFences = new FencePieces*[mNumCollisionAreas];
+
+ mFencesInEachArea = new int[mNumCollisionAreas];
+ // keep a total for efficient submission/removal
+
+
+ // new - to save memory
+ // May 7, 2003
+ //
+ // make all fences and ground planes in the pool use the same physics properties
+ // (perhaps later change so that one for fences and one for ground planes)
+
+ mFencePhysicsProperties = new sim::PhysicsProperties;
+ mFencePhysicsProperties->AddRef();
+
+
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mFences[i] = new FencePieces[mMaxFencePerArea];
+
+ for(j = 0; j < mMaxFencePerArea; j++)
+ {
+ // need to make collision volumes here!
+ rmt::Vector center(0.0f, 0.0f, 0.0f);
+ rmt::Vector o0(1.0f, 0.0f, 0.0f);
+ rmt::Vector o1(0.0f, 1.0f, 0.0f);
+ rmt::Vector o2(0.0f, 0.0f, 1.0f);
+
+ sim::OBBoxVolume* tempOBBox = new OBBoxVolume(center, o0, o1, o2, 1.0f, 1.0f, 1.0f);
+
+
+ mFences[i][j].mFenceSimState = (sim::ManualSimState*)(SimState::CreateManualSimState(tempOBBox));
+ mFences[i][j].mFenceSimState->AddRef();
+
+ mFences[i][j].mFenceSimState->GetCollisionObject()->SetManualUpdate(true);
+ //mFences[i][j].mFenceSimState->GetCollisionObject()->SetAutoPair(true);
+ mFences[i][j].mFenceSimState->GetCollisionObject()->SetAutoPair(false);
+
+ // setting this should free the unnecessarly allocated default one:
+ //mFences[i][j].mFenceSimState->GetCollisionObject()->SetPhysicsProperties(mFencePhysicsProperties);
+ mFences[i][j].mFenceSimState->SetPhysicsProperties(this->mFencePhysicsProperties);
+
+
+ // give this thing a reasonable name for debug purposes
+ char buffy[128];
+ sprintf(buffy, "fence_a%d_n%d", i, j);
+
+ mFences[i][j].mFenceSimState->GetCollisionObject()->SetName(buffy);
+
+ mFences[i][j].mFenceSimState->mAIRefIndex = PhysicsAIRef::redBrickPhizFence;
+ mFences[i][j].mFenceSimState->mAIRefPointer = 0; // only set if object is derived from CollisionEntityDSG
+
+
+ mFences[i][j].mInCollision = false;
+ mFences[i][j].mClean = false;
+
+ }
+
+ mFencesInEachArea[i] = 0;
+
+ }
+
+ // debug drawing
+ mNumDebugFences = 0;
+
+ mFenceDSGResults = new FenceEntityDSG*[mMaxFencePerArea];
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ mFenceDSGResults[i] = new FenceEntityDSG;
+ mFenceDSGResults[i]->AddRef();
+ }
+
+ //---------
+ // dynamics
+ //---------
+
+ mMaxDynamics = 20;
+
+ mCurrentDynamics = new DynamicsInCollisionDetection*[mNumCollisionAreas];
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCurrentDynamics[i] = new DynamicsInCollisionDetection[mMaxDynamics];
+
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ mCurrentDynamics[i][j].mDynamicPhysDSG = 0;
+ mCurrentDynamics[i][j].clean = false;
+ }
+ }
+
+ //--------------
+ // ground planes
+ //--------------
+
+ mGroundPlanePool = new GroundPlanePool(mMaxDynamics * 2); // TODO - ????? how many
+
+
+ //-----------------------
+ // collision solver agent
+ //-----------------------
+ mpWorldCollisionSolverAgentManager = new WorldCollisionSolverAgentManager;
+ mpWorldCollisionSolverAgentManager->AddRef();
+
+ mCollisionManager->GetImpulseBasedCollisionSolver()->SetCollisionSolverAgent(mpWorldCollisionSolverAgentManager);
+
+
+ //enum DrawVolumeMethod {DrawVolumeOutline=0, DrawVolumeShape};
+ //sim::SetDrawVolumeMethod(DrawVolumeOutline);
+ sim::SetDrawVolumeMethod(DrawVolumeShape);
+
+ mInInterior = false;
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+MEMTRACK_POP_GROUP("WorldPhysicsManager");
+}
+
+//=============================================================================
+// WorldPhysicsManager::SuspendForInterior
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SuspendForInterior()
+{
+ mInInterior = true;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::ResumeForOutside
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::ResumeForOutside()
+{
+ mInInterior = false;
+}
+
+
+
+//=============================================================================
+// WorldPhysicsManager::EmptyCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::EmptyCollisionAreaIndex(int index)
+{
+ rAssert( index > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert(mCollisionAreaAllocated[index]); // should not be trying to empty one that is not in use
+
+ mCollisionManager->ResetArea(index); // this will take everything out of that area!
+
+ int i;
+ for(i = 0; i < mMaxStatics; i++)
+ {
+ tRefCounted::Release( mCurrentStatics[index][i].mStaticPhysDSG );
+ mCurrentStatics[index][i].mStaticPhysDSG = 0;
+ mCurrentStatics[index][i].clean = false;
+ }
+
+ for(i = 0; i < mMaxAnimCollisions; i++)
+ {
+ tRefCounted::Release( mCurrentAnimCollisions[index][i].mAnimCollisionEntityDSG );
+ mCurrentAnimCollisions[index][i].mAnimCollisionEntityDSG = 0;
+ mCurrentAnimCollisions[index][i].clean = false;
+ }
+
+ for(i = 0; i < mMaxUpdateAnimCollisions; i++)
+ {
+ tRefCounted::Release( mCurrentUpdateAnimCollisions[index][i].mAnimCollisionEntityDSG );
+ mCurrentUpdateAnimCollisions[index][i].mAnimCollisionEntityDSG = 0;
+ mCurrentUpdateAnimCollisions[index][i].clean = false;
+ }
+
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ // don't need to add ref and release these 'cause they're only internal to the fences.
+ mFences[index][i].mInCollision = false;
+ }
+ //mFencesInEachArea[index] = 0;
+
+
+ for(i = 0; i < mMaxDynamics; i++)
+ {
+ if(mCurrentDynamics[index][i].mDynamicPhysDSG)
+ {
+ //TODO
+ if(mCurrentDynamics[index][i].mDynamicPhysDSG->GetAIRef() != PhysicsAIRef::redBrickVehicle)
+ {
+ mCurrentDynamics[index][i].mDynamicPhysDSG->FreeGroundPlane(); // in case it still has one
+
+ // this is also where the ground plane collision object will be disabled if
+ // there are no more refs
+ }
+
+
+
+
+
+ tRefCounted::Release( mCurrentDynamics[index][i].mDynamicPhysDSG );
+ mCurrentDynamics[index][i].mDynamicPhysDSG = 0;
+ mCurrentDynamics[index][i].clean = false;
+ }
+
+ }
+
+ // TODO - setup some sort of enable/disable interface also?
+}
+
+//=============================================================================
+// WorldPhysicsManager::FreeCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::FreeCollisionAreaIndex(int index)
+{
+ rAssert( index > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert(mCollisionAreaAllocated[index]); // should not be trying to free up one that is not in use
+
+ EmptyCollisionAreaIndex( index );
+
+ mCollisionAreaAllocated[index] = false;
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldPhysicsManager::FreeAllCollisionAreaIndicies()
+{
+ int index = 0;
+ for(; index<mNumCollisionAreas; index++)
+ {
+ if(mCollisionAreaAllocated[index]) // should not be trying to free up one that is not in use
+ {
+ FreeCollisionAreaIndex(index);
+ }
+ }
+}
+
+//=============================================================================
+// WorldPhysicsManager::RemoveVehicleFromAnyOtherCurrentDynamicsList
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(DynaPhysDSG* obj)
+{
+ int i;
+
+ // Vehicle* vehicle = dynamic_cast<Vehicle*>(obj);
+ // Character* character= dynamic_cast<Character*>(obj);
+ Vehicle* vehicle = NULL;
+ Character* character = NULL;
+
+ if ( obj->GetAIRef() == PhysicsAIRef::NPCharacter ||
+ obj->GetAIRef() == PhysicsAIRef::PlayerCharacter )
+ {
+ rAssert( dynamic_cast< Character* >( obj ) );
+ character = static_cast< Character*>( obj ) ;
+ }
+ else if ( obj->GetAIRef() == PhysicsAIRef::redBrickVehicle )
+ {
+ rAssert( dynamic_cast< Vehicle* >( obj ));
+ vehicle = static_cast< Vehicle*>( obj );
+ }
+ else
+ {
+ rAssert( dynamic_cast< Character* >( obj )==false);
+ rAssert( dynamic_cast< Vehicle* >( obj )==false);
+ }
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ if(vehicle && (i == vehicle->mCollisionAreaIndex))
+ {
+ continue;
+ }
+
+ if(character && (i == character->GetCollisionAreaIndex()))
+ {
+ continue;
+ }
+
+ if(!mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+
+ int j;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if(mCurrentDynamics[i][j].mDynamicPhysDSG == obj)
+ {
+ mCollisionManager->RemoveCollisionObject(obj->GetSimState()->GetCollisionObject(), i);
+
+
+ // only deal with ground plane here if the incoming object is not a car
+ if(!(vehicle))
+ {
+ int groundPlaneIndex = mCurrentDynamics[i][j].mDynamicPhysDSG->GetGroundPlaneIndex();
+ if(groundPlaneIndex != -1) // should never be false...
+ {
+ sim::CollisionObject* gpCollObj = mGroundPlanePool->GetSimState(groundPlaneIndex)->GetCollisionObject();
+
+ mCollisionManager->RemoveCollisionObject(gpCollObj, i);
+
+ mCurrentDynamics[i][j].mDynamicPhysDSG->FreeGroundPlane(); // this decrements the groundplaneref, but we still need to take it out of
+ }
+ }
+
+ mCurrentDynamics[i][j].mDynamicPhysDSG = 0;
+ mCurrentDynamics[i][j].clean = false;
+
+ obj->Release();
+
+
+ }
+
+ }
+ }
+}
+
+//=============================================================================
+// WorldPhysicsManager::OnQuitLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::OnQuitLevel()
+{
+ // make sure ground plane pool has been properly free'd up
+ if(!(mGroundPlanePool->FreeAllGroundPlanes()))
+ {
+ // there was some ground planes that were not yet free'd when this was called!
+ rAssertMsg(false, "not all the ground planes were free'd by their dynamic object owners! bad dynamic objects!\n");
+ }
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::GetCameraCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetCameraCollisionAreaIndex()
+{
+ // moved to member data
+ //int maxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+ //int maxChars = MAX_PLAYERS;
+ //int maxCameras = MAX_PLAYERS;
+
+ int start = mReservedCollisionAreas + mMaxVehicles + mMaxChars;
+ int end = mReservedCollisionAreas + mMaxVehicles + mMaxChars + mMaxCameras - 1;
+
+ int i;
+ for(i = start; i <= end; i++)
+ {
+ // look for first free index
+ if(mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+ else
+ {
+ mCollisionAreaAllocated[i] = true;
+ return i;
+ }
+ }
+
+ rReleaseAssertMsg( 0, "not enough camera collision indices\n" );
+ return WorldPhysicsManager::INVALID_COLLISION_AREA;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetVehicleCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetVehicleCollisionAreaIndex()
+{
+ //int maxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+
+ int start = mReservedCollisionAreas;
+ int end = mReservedCollisionAreas + mMaxVehicles - 1;
+
+ int i;
+ for(i = start; i <= end; i++)
+ {
+ // look for first free index
+ if(mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+ else
+ {
+ mCollisionAreaAllocated[i] = true;
+ return i;
+ }
+ }
+
+ rReleaseAssertMsg( 0, "not enough vehicle collision indices\n" );
+ return WorldPhysicsManager::INVALID_COLLISION_AREA;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetCharacterCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetCharacterCollisionAreaIndex()
+{
+ //int maxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+ //int maxChars = MAX_PLAYERS;
+
+ int start = mReservedCollisionAreas + mMaxVehicles;
+ int end = mReservedCollisionAreas + mMaxVehicles + mMaxChars - 1;
+
+ int i;
+ for(i = start; i <= end; i++)
+ {
+ // look for first free index
+ if(mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+ else
+ {
+ mCollisionAreaAllocated[i] = true;
+ return i;
+ }
+ }
+
+ rReleaseAssertMsg( 0, "not enough character collision indices\n" );
+ return WorldPhysicsManager::INVALID_COLLISION_AREA;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DestroyInstance()
+{
+ rAssert(spInstance);
+
+ // TODO - Release() collision manager
+
+ delete(GMA_PERSISTENT, spInstance);
+ spInstance = NULL;
+
+
+
+}
+
+
+
+//=============================================================================
+// WorldPhysicsManager::SetSimUnits
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+/*
+void WorldPhysicsManager::SetSimUnits()
+{
+ float LSCALE = 0.01f;
+ //float MSCALE = 0.001f;
+ float MSCALE = 1.0f;
+ float TSCALE = 1.0f;
+ SimUnitsManager um;
+ um.SetUnits(LSCALE, MSCALE, TSCALE);
+}
+*/
+
+//=============================================================================
+// WorldPhysicsManager::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned int timeDeltaSeconds)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::Update(unsigned int timeDeltaMilliSeconds)
+{
+
+
+ BEGIN_PROFILE("WorldPhysicsManager::Update")
+
+
+ BEGIN_PROFILE("Vehicle Submits")
+ GetVehicleCentral()->SubmitStatics();
+ GetVehicleCentral()->SubmitAnimCollisions();
+ GetVehicleCentral()->SubmitDynamics();
+ END_PROFILE("Vehicle Submits")
+
+
+ BEGIN_PROFILE("Character Submits")
+ GetCharacterManager()->SubmitStatics();
+ GetCharacterManager()->SubmitAnimCollisions(); // both collision and update I assume
+ GetCharacterManager()->SubmitDynamics();
+ END_PROFILE("Character Submits")
+
+ BEGIN_PROFILE("Cam Submits")
+ GetSuperCamManager()->SubmitStatics();
+ END_PROFILE("Cam Submits")
+
+
+ BEGIN_PROFILE("Update Ground")
+ UpdateSimluatingDynamicObjectGroundPlanes();
+ END_PROFILE("Update Ground")
+
+ // new substep logic - nbrooke 12/7/03
+ //
+ // we want to try to minimize it ticking over into two substep mode unless it absolutly neccesary
+ // and try to run a 30fps on the ps2 all the time therefore I have
+ // 1) upped the threshold for going to two substeps to 35 ms from 32ms
+ // 2) only go into multiple substeps if we get two consecutive frames over the threshold or
+ // one frame WAY over the threshold
+ unsigned numSubsteps = 1;
+
+ if(((mLastTime > 35) && (timeDeltaMilliSeconds > 35)) || (timeDeltaMilliSeconds > 50))
+ {
+ numSubsteps = (timeDeltaMilliSeconds + 34) / 35;
+ }
+
+ mLastTime = timeDeltaMilliSeconds;
+
+ float substep = (float(timeDeltaMilliSeconds) * 0.001f) / float(numSubsteps);
+ float dt = static_cast<float>(timeDeltaMilliSeconds) * 0.001f;
+
+ // cap
+ if(numSubsteps > 10)
+ {
+ rReleasePrintf("\nhit 10 substeps!\n");
+ numSubsteps = 10;
+ }
+
+
+ mLoopTime = dt;
+
+ if(mTimerOn)
+ {
+ mTimerTime += dt;
+ }
+
+ BEGIN_PROFILE("WorldPhysicsManager::PreSubstepUpdate")
+
+ GetVehicleCentral()->PreSubstepUpdate(dt);
+
+ END_PROFILE("WorldPhysicsManager::PreSubstepUpdate")
+
+ BEGIN_PROFILE("PreSim")
+ GetCharacterManager( )->PreSimUpdate( dt );
+ END_PROFILE("PreSim")
+
+ bool firstSubstep = true;
+ //while(countDown > timestep)// * 1.5f)
+ for(unsigned i = 0; i < numSubsteps; i++)
+ {
+ // do shit
+ WorldSimSubstepGuts(substep, firstSubstep);
+ firstSubstep = false;
+ }
+
+ BEGIN_PROFILE("VehiclePost")
+ GetVehicleCentral()->PostSubstepUpdate(dt);
+ END_PROFILE("VehiclePost")
+
+ BEGIN_PROFILE("CharPost")
+ GetCharacterManager()->PostSimUpdate( dt );
+ END_PROFILE("CharPost")
+
+ END_PROFILE("WorldPhysicsManager::Update")
+
+ BEGIN_PROFILE("Phys DebugInfo")
+ DebugInfoDisplay();
+ END_PROFILE("Phys DebugInfo")
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::DebugInfoDisplay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DebugInfoDisplay()
+{
+ // maybe "Display" is a bit deceiving.
+
+ #ifdef DEBUGINFO_ENABLED
+
+ DEBUGINFO_PUSH_SECTION( "Vehicle Shit" );
+
+ // just display the vehicles position
+ // whichever vehicle player is driving
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+
+ Vehicle* playerVehicle = 0;
+
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+
+ if(playerVehicle)
+ {
+ float x = 0.0f;
+ float y = 0.0f;
+ float z = 0.0f;
+
+ rmt::Vector pos = playerVehicle->GetPosition();
+ x = pos.x;
+ y = pos.y;
+ z = pos.z;
+
+
+ char buffy[128];
+ sprintf( buffy, "position - x: %.2f y: %.2f z: %.2f", x, y, z);
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ float life = playerVehicle->GetVehicleLifePercentage(playerVehicle->mHitPoints);
+ sprintf( buffy, "damage inc. - %.3f", 1.0f - life);
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ if(playerVehicle->mVehicleState == VS_NORMAL)
+ {
+ sprintf( buffy, "VS_NORMAL");
+ }
+ else if(playerVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ sprintf( buffy, "VS_EBRAKE_SLIP");
+ }
+ else
+ {
+ sprintf( buffy, "VS_SLIP");
+ }
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ /*
+ if(playerVehicle->mLosingTractionDueToAccel)
+ {
+ sprintf(buffy, "LOSING TRACTION DUE TO ACCELERATION!!");
+ }
+ else
+ {
+ sprintf(buffy, "unmodified traction");
+ }
+
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ if(playerVehicle->mLoco == VL_PHYSICS) // I think this will always be the case
+ {
+ sprintf(buffy, "currentSteeringForce: %.2f", playerVehicle->mPhysicsLocomotion->mCurrentSteeringForce);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+ }
+ */
+
+
+ sprintf(buffy, "wheel turn angle unmodified input: %.2f", playerVehicle->mUnmodifiedInputWheelTurnAngle);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "wheel turn angle normalized: %.2f", playerVehicle->mWheelTurnAngleInputValue);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "wheel turn angle: %.2f", playerVehicle->mWheelTurnAngle);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+
+ sprintf(buffy, "kmh: %.2f", playerVehicle->mSpeedKmh);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "ebrake: %.2f", playerVehicle->mEBrake);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "gas: %.2f", playerVehicle->mGas);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+
+ sprintf(buffy, "timer: %.3f", mTimerTime);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ /*
+
+ sprintf(buffy, "speed burst timer: %.3f", playerVehicle->mSpeedBurstTimer);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ if(playerVehicle->mDoSpeedBurst)
+ {
+ sprintf(buffy, "doing speed burst!");
+ }
+ else
+ {
+ sprintf(buffy, "not doing speed burst");
+ }
+ */
+
+
+ }
+
+ }
+
+
+ DEBUGINFO_POP_SECTION();
+
+
+
+
+ if(playerVehicle)
+ {
+ char temp[128];
+ DebugInfo::GetInstance()->Push( "Vehicle Terrain Type" );
+
+
+ sprintf( temp, "average for vehicle: %d. %sside.", playerVehicle->mTerrainType, playerVehicle->mInterior ? "In" : "Out" );
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 0: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[0].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 1: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[1].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 2: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[2].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 3: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[3].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+
+
+ DebugInfo::GetInstance()->Pop();
+
+
+ /*
+ DebugInfo::GetInstance()->Push( "Player Vehicle Collision Info" );
+
+ int colindex = playerVehicle->mCollisionAreaIndex;
+ sprintf( temp, "number of collision pairs in player collision area: %d", this->mCollisionManager->GetCollisionObjectPairList(colindex)->ArraySize());
+
+ DEBUGINFO_ADDSCREENTEXT( temp );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ DebugInfo::GetInstance()->Pop();
+ */
+
+
+ }
+
+
+
+ #endif // DEBUGINFO_ENABLED
+
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::WorldSimSubstepGuts
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::WorldSimSubstepGuts(float dt, bool firstSubstep)
+{
+ BEGIN_PROFILE("WorldSimSubstepGuts")
+
+ mTotalTime += dt;
+ updateFrame++;
+
+ BEGIN_PROFILE("VC::PreCollisionPrep")
+ if(!mInInterior)
+ {
+ GetVehicleCentral()->PreCollisionPrep(dt, firstSubstep);
+ }
+ END_PROFILE("VC::PreCollisionPrep")
+
+ BEGIN_PROFILE("CharMgr::PreSimUpdate")
+ GetCharacterManager()->PreSubstepUpdate( dt );
+ END_PROFILE("CharMgr::PreSimUpdate")
+
+ BEGIN_PROFILE("ABMgr::Update")
+ GetActionButtonManager( )->Update( dt );
+ END_PROFILE("ABMgr::Update")
+
+// BEGIN_PROFILE("SuperCamMgr::PreCollPrep")
+// GetSuperCamManager()->PreCollisionPrep();
+// END_PROFILE("SuperCamMgr::PreCollPrep")
+
+ BEGIN_PROFILE("ResetCollisionFlags")
+ mpWorldCollisionSolverAgentManager->ResetCollisionFlags();
+ END_PROFILE("ResetCollisionFlags")
+
+
+
+BEGIN_PROFILE("DetectCollisions")
+ bool printOut = false;
+ mCollisionManager->ClearCollisions();
+ mCollisionManager->DetectCollision(dt, mTotalTime, printOut );
+ mCollisionManager->SolveCollision(dt, mTotalTime);
+
+END_PROFILE("DetectCollisions")
+
+
+ BEGIN_PROFILE("GetVehicleCentral()->Update")
+ GetVehicleCentral()->Update(dt);
+ END_PROFILE("GetVehicleCentral()->Update")
+ BEGIN_PROFILE("GetCharacterManager()->Update")
+ GetCharacterManager()->Update( dt );
+ END_PROFILE("GetCharacterManager()->Update")
+ BEGIN_PROFILE("GetCharacterManager()->PostSimUpdate")
+ GetCharacterManager()->PostSubstepUpdate(dt);
+ END_PROFILE("GetCharacterManager()->PostSimUpdate")
+
+ //Update the position of the super camers
+ BEGIN_PROFILE("GetSuperCamManager()->Update")
+ GetSuperCamManager()->Update( rmt::FtoL(dt * 1000.0f), firstSubstep );
+ END_PROFILE("GetSuperCamManager()->Update")
+
+ //Detect collisions on the new position of the super camera
+ int area = GetSuperCamManager()->GetSCC( 0 )->mCollisionAreaIndex;
+ mCollisionManager->ClearCollisions();
+ BEGIN_PROFILE("SuperCamMgr::PreCollPrep")
+ GetSuperCamManager()->PreCollisionPrep();
+ END_PROFILE("SuperCamMgr::PreCollPrep")
+
+ mCollisionManager->DetectCollision( area, dt, mTotalTime, printOut );
+ mCollisionManager->SolveCollision( area, dt, mTotalTime );
+
+ //Correct the position of the super camera
+ GetSuperCamManager()->GetSCC( 0 )->UpdateForPhysics( rmt::FtoL(dt * 1000.0f) );
+
+ END_PROFILE("WorldSimSubstepGuts")
+}
+
+
+
+//=============================================================================
+// WorldPhysicsManager::DisplayCollisionObjectsExceptVehicleInArea
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int area)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DisplayFencesInArea(int area)
+{
+ rAssert( area > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ int i;
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ if(mFences[area][i].mInCollision)
+ {
+ sim::ManualSimState* mss = mFences[area][i].mFenceSimState;
+ sim::DrawCollisionObject(mss->GetCollisionObject());
+ }
+
+ }
+ for(i = 0; i < mNumDebugFences; i++)
+ {
+ // debug draw it!
+ mFenceDSGResults[i]->Display();
+ }
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::SubmitStaticsPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitStaticsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ BEGIN_PROFILE( "SubmitStaticsPseudoCallback" );
+
+ ReserveArray<StaticPhysDSG*> staticPhysDSGList;
+
+ int i;
+ for(i = 0; i < mMaxStatics; i++)
+ {
+ mCurrentStatics[collisionAreaIndex][i].clean = false; // probably slower to check if there's
+ // even something pointed to than just
+ // to do this
+ }
+
+ BEGIN_PROFILE( "FindStaticPhysElems" );
+ GetIntersectManager()->FindStaticPhysElems(position, radius, staticPhysDSGList);
+ END_PROFILE( "FindStaticPhysElems" );
+
+ int numResults = staticPhysDSGList.mUseSize;
+
+ // !!!!
+ // temp
+ //
+ // TODO
+ // hmmm... what to do here?
+ // pick different max or try and prioritize further?
+ //
+ // also TODO - need different maxes for different lists
+
+ if(numResults > mMaxStatics)
+ {
+ numResults = mMaxStatics;
+ rDebugPrintf("\n!!! too many statics !!!\n");
+ }
+
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ StaticPhysDSG* curr = staticPhysDSGList[i];
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxStatics; j++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][j].mStaticPhysDSG == curr)
+ {
+ // this one already in list
+ mCurrentStatics[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxStatics; k++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][k].mStaticPhysDSG == 0)
+ {
+ // here is a slot
+ mCurrentStatics[collisionAreaIndex][k].mStaticPhysDSG = curr;
+ mCurrentStatics[collisionAreaIndex][k].mStaticPhysDSG->AddRef();
+ mCurrentStatics[collisionAreaIndex][k].clean = true;
+
+ sim::CollisionObject* currCollObj = curr->mpSimState()->GetCollisionObject();
+
+
+ BEGIN_PROFILE( "AddCollisionObject" );
+
+ if(1)//allowAutoPairing)
+ {
+ currCollObj->SetAutoPair(true);
+ }
+ else
+ {
+ currCollObj->SetAutoPair(false);
+ }
+
+ mCollisionManager->AddCollisionObject(currCollObj, collisionAreaIndex);
+
+ //if(!allowAutoPairing)
+ {
+ mCollisionManager->AddPair(currCollObj, callerSimState->GetCollisionObject(), collisionAreaIndex);
+ }
+
+
+
+
+ END_PROFILE( "AddCollisionObject" );
+ break;
+ }
+ }
+
+ rAssert(k < mMaxStatics);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ for(i = 0; i < mMaxStatics; i++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG != 0 &&
+ mCurrentStatics[collisionAreaIndex][i].clean == false)
+ {
+ BEGIN_PROFILE( "RemoveCollisionObject" );
+ mCollisionManager->RemoveCollisionObject(mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG->mpSimState()->GetCollisionObject(), collisionAreaIndex);
+ END_PROFILE( "RemoveCollisionObject" );
+ mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG->Release();
+ mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG = 0;
+ mCurrentStatics[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+ END_PROFILE( "SubmitStaticsPseudoCallback" );
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::SubmitAnimCollisionsPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitAnimCollisionsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ ReserveArray<AnimCollisionEntityDSG*> animCollisionEntityDSGList;
+
+ int i;
+ for(i = 0; i < mMaxAnimCollisions; i++)
+ {
+ mCurrentAnimCollisions[collisionAreaIndex][i].clean = false;
+ }
+
+ GetIntersectManager()->FindAnimPhysElems(position, radius, animCollisionEntityDSGList);
+
+ int numResults = animCollisionEntityDSGList.mUseSize;
+
+ if(numResults > mMaxAnimCollisions)
+ {
+ numResults = mMaxAnimCollisions;
+ rDebugPrintf("\n!!! too many animating collision objects - goddamn !!!\n");
+ }
+
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ AnimCollisionEntityDSG* curr = animCollisionEntityDSGList[i];
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxAnimCollisions; j++)
+ {
+ if(mCurrentAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG == curr)
+ {
+ // this one already in list
+ mCurrentAnimCollisions[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxAnimCollisions; k++)
+ {
+ if(mCurrentAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG == 0)
+ {
+ // here is a slot
+ mCurrentAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG = curr;
+ mCurrentAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG->AddRef( );
+ mCurrentAnimCollisions[collisionAreaIndex][k].clean = true;
+
+ sim::CollisionObject* currCollObj = curr->GetSimState()->GetCollisionObject();
+
+ currCollObj->SetAutoPair(false);
+ mCollisionManager->AddCollisionObject(currCollObj, collisionAreaIndex);
+ mCollisionManager->AddPair(currCollObj, callerSimState->GetCollisionObject(), collisionAreaIndex);
+
+ break;
+ }
+ }
+
+ rAssert(k < mMaxAnimCollisions);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ for(i = 0; i < mMaxAnimCollisions; i++)
+ {
+ if(mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG != 0 &&
+ mCurrentAnimCollisions[collisionAreaIndex][i].clean == false)
+ {
+ mCollisionManager->RemoveCollisionObject(mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG->GetSimState()->GetCollisionObject(), collisionAreaIndex);
+ mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG->Release( );
+ mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG = 0;
+ mCurrentAnimCollisions[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::SubmitAnimCollisionsForUpdateOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitAnimCollisionsForUpdateOnly(rmt::Vector& position, float radius, int collisionAreaIndex)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ ReserveArray<AnimCollisionEntityDSG*> animCollisionEntityDSGList;
+
+ int i;
+ for(i = 0; i < mMaxUpdateAnimCollisions; i++)
+ {
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].clean = false;
+ }
+
+ GetIntersectManager()->FindAnimPhysElems(position, radius, animCollisionEntityDSGList);
+
+ int numResults = animCollisionEntityDSGList.mUseSize;
+
+ if(numResults > mMaxUpdateAnimCollisions)
+ {
+ numResults = mMaxUpdateAnimCollisions;
+ rDebugPrintf("\n!!! too many animating collision objects - goddamn !!!\n");
+ }
+
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ AnimCollisionEntityDSG* curr = animCollisionEntityDSGList[i];
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxUpdateAnimCollisions; j++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG == curr)
+ {
+ // this one already in list
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxUpdateAnimCollisions; k++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG == 0)
+ {
+ // here is a slot
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG = curr;
+ // Hang on to this object.
+ //
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG->AddRef( );
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][k].clean = true;
+
+ break;
+ }
+ }
+
+ rAssert(k < mMaxUpdateAnimCollisions);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ for(i = 0; i < mMaxUpdateAnimCollisions; i++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG != 0 &&
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].clean == false)
+ {
+ // Let this thing go.
+ //
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG->Release( );
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG = 0;
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+}
+
+//=============================================================================
+// WorldPhysicsManager::SubmitDynamicsPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitDynamicsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ // hmmm...
+ // safe to make this an InstDynaPhysDSG list?? - doesn't compile
+ ReserveArray<DynaPhysDSG*> dynamicsPhysDSGList;
+
+ int i;
+ for(i = 0; i < mMaxDynamics; i++)
+ {
+ mCurrentDynamics[collisionAreaIndex][i].clean = false; // probably slower to check if there's
+ // even something pointed to than just
+ // to do this
+ }
+
+ // this is just commented out until it exists
+ GetIntersectManager()->FindDynaPhysElems(position, radius, dynamicsPhysDSGList);
+
+ int numResults = dynamicsPhysDSGList.mUseSize;
+
+ if(numResults > 1)
+ {
+ int stophere = 1;
+ }
+
+
+ // TODO
+ // hmmm... what to do here?
+ if(numResults > mMaxDynamics)
+ {
+ numResults = mMaxDynamics;
+ rDebugPrintf("\n!!! too many dynamics returned from query !!!\n");
+ }
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ DynaPhysDSG* curr = dynamicsPhysDSGList[i];
+
+ // temp
+ //
+ // this actually might be the way to do it?
+ //if(curr == caller)
+ if((curr->mpSimState() == callerSimState) || (curr->mpSimState() == NULL))
+ {
+ continue;
+ }
+
+ // new test
+ // just for Cary
+ if(callerSimState->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ if( curr->mpSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle &&
+ curr->mpSimState()->mAIRefIndex != PhysicsAIRef::NPCharacter &&
+ curr->mpSimState()->mAIRefIndex != PhysicsAIRef::PlayerCharacter )
+ {
+ continue;
+ }
+
+ }
+
+
+ // not optimization further below
+ //
+ // only gonna pair "submitters" with ourselves
+ //
+ // this logic here is to see if the current "submitter" is trying to pair itself against another "submitter" vehicle we already called earlier, that
+ // already set up this pair
+ if(callerSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle && curr->mpSimState()->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+
+ // April 22, 2003
+ // problem when cars that are calling this are very close to each other
+ //
+ // many duplicate pairs put in more than one collision area
+ //
+ // the fastest, most optimal check we can do to minimize this is:
+ // (first re-order the vehicle calls to this to make sure VT_USER called first)
+ // then, if we are a car, and return a car, and either it's a VT_USER or it's and AI with
+ // a lower vehicle central index than us, then skipt it - they've already submitted us, and autopaird all round!
+ Vehicle* submittingVehicle = (Vehicle*)(callerSimState->mAIRefPointer);
+ Vehicle* submittedVehicle = (Vehicle*)(curr->mpSimState()->mAIRefPointer);
+
+ rTuneAssert(submittingVehicle->mVehicleCentralIndex != -1);
+ rTuneAssert(submittedVehicle->mVehicleCentralIndex != -1);
+
+ if(submittingVehicle->mVehicleType == VT_USER)
+ {
+ if(submittedVehicle->mVehicleType == VT_USER)
+ {
+ if(submittingVehicle->mVehicleCentralIndex > submittedVehicle->mVehicleCentralIndex)
+ {
+ // both user vehicles, but...
+ // the submitted vehicle has been called first, so it wins
+ continue;
+ }
+ }
+ }
+ else
+ {
+ // not a user vehicle
+ if(submittedVehicle->mVehicleType == VT_USER)
+ {
+ // it has already added us
+ continue;
+ }
+ else if(submittingVehicle->mVehicleType == VT_AI)
+ {
+ if(submittingVehicle->mVehicleCentralIndex > submittedVehicle->mVehicleCentralIndex)
+ {
+ // the submitted vehicle has been called first, so it wins
+ continue;
+ }
+ }
+ }
+
+ }
+
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if(mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG == curr)
+ {
+ // this one already in list
+ mCurrentDynamics[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxDynamics; k++)
+ {
+ if(mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG == 0)
+ {
+ // here is a slot
+ mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG = curr;
+ mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG->AddRef( );
+ mCurrentDynamics[collisionAreaIndex][k].clean = true;
+
+ sim::CollisionObject* currCollObj = curr->mpSimState()->GetCollisionObject();
+
+ // final, simplest possible optimization
+ //
+ // don't autopair dynamics period
+ // any dynamic we submit only pairs with us!
+ //
+ // who knows what else this thing will do in it's own area (if it has one), so just in case turn
+ // turn flag back on after
+
+
+ // only pair with us
+ //
+
+ if(allowAutoPairing && curr->mpSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle)
+ {
+ currCollObj->SetAutoPair(true);
+ }
+ else
+ {
+ currCollObj->SetAutoPair(false);
+ }
+
+ mCollisionManager->AddCollisionObject(currCollObj, collisionAreaIndex);
+
+ //if(!allowAutoPairing || curr->mpSimState()->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ mCollisionManager->AddPair(currCollObj, callerSimState->GetCollisionObject(), collisionAreaIndex);
+ }
+
+
+
+ /*
+ // new??
+ // is this gonna work, and work well?
+
+ //!!
+ // only do this for the player character!
+
+
+ int l;
+ for(l = 0; l < this->mMaxStatics; l++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][l].mStaticPhysDSG)
+ {
+ // there's one there...
+ // make sure we are paired with it
+ mCollisionManager->AddPair(currCollObj,
+ mCurrentStatics[collisionAreaIndex][l].mStaticPhysDSG->mpSimState()->GetCollisionObject(),
+ collisionAreaIndex);
+ }
+ }
+ for(l = 0; l < this->mMaxFencePerArea; l++)
+ {
+ if(mFences[collisionAreaIndex][l].mInCollision)
+ {
+ mCollisionManager->AddPair(currCollObj,
+ mFences[collisionAreaIndex][l].mFenceSimState->GetCollisionObject(),
+ collisionAreaIndex);
+
+ }
+
+ }
+ */
+
+
+ // all vehicles and player character have their own groundplanes already
+ if( curr->GetAIRef() != PhysicsAIRef::redBrickVehicle && curr->GetAIRef() != PhysicsAIRef::PlayerCharacter )
+ {
+ int groundPlaneIndex = mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG->FetchGroundPlane();
+
+ if(groundPlaneIndex != -1) // just in case, though this should never be -1
+ {
+ sim::CollisionObject* gpCollObj = mGroundPlanePool->GetSimState(groundPlaneIndex)->GetCollisionObject();
+ gpCollObj->SetRayCastingEnabled( false );
+
+
+
+ // recall, collision is disabled on the ground plane until it is set on by the object using it
+ mCollisionManager->AddCollisionObject( gpCollObj, collisionAreaIndex );
+
+ mCollisionManager->AddPair(currCollObj, gpCollObj, collisionAreaIndex);
+ }
+
+ }
+
+
+ break;
+ }
+ }
+
+ //rAssert(k < mMaxDynamics);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ //
+ // at this stage, we can only remove the ones that are unclean and at rest.
+
+ PurgeDynamics( collisionAreaIndex );
+
+}
+
+
+//=============================================================================
+// static bool IsBreakable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DynaPhysDSG* pObject )
+//
+// Return: static
+//
+//=============================================================================
+static bool IsBreakable( DynaPhysDSG* pObject )
+{
+ bool isBreakable;
+ CollisionAttributes* attr = pObject->GetCollisionAttributes();
+ if ( pObject->GetAIRef() == PhysicsAIRef::StateProp ||
+ pObject->GetAIRef() == PhysicsAIRef::ActorStateProp )
+ {
+ // *some* stateprops can break, but not ones flagged as PROP_ONE_TIME_MOVEABLE
+ if ( pObject->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE )
+ isBreakable = false;
+ else
+ isBreakable = true;
+ }
+ else if ( attr != NULL )
+ {
+ if ( attr->GetClasstypeid() == PROP_BREAKABLE )
+ {
+ isBreakable = true;
+ }
+ else
+ {
+ isBreakable = false;
+ }
+ }
+ else
+ {
+ isBreakable = false;
+ }
+
+ return isBreakable;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::PurgeDynamics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int collisionAreaIndex )
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::PurgeDynamics( int collisionAreaIndex )
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ for(int i = 0; i < mMaxDynamics; i++)
+ {
+ // TODO !
+ // test for vehicles must be different;
+ // they use they're own ground plane!
+
+ if( mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG != 0 &&
+ mCurrentDynamics[collisionAreaIndex][i].clean == false )
+ {
+ DynaPhysDSG* pObject = mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG;
+
+ // need to keep updating when "at rest" if it's a onetime movable that has been hit
+ // so that it can be removed when it goes out of view
+ bool atRest = pObject->IsAtRest();
+
+ if(atRest && pObject->GetCollisionAttributes())
+ {
+ atRest = atRest && !((pObject->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE)
+ && pObject->mIsHit);
+ }
+
+ if ( atRest || IsBreakable( pObject ) )
+ {
+ mCollisionManager->RemoveCollisionObject(pObject->mpSimState()->GetCollisionObject(), collisionAreaIndex);
+
+ // don't do fuck if we are a car - temp TODO!!
+ // this test doesn't seem to work
+ if(pObject->GetAIRef() != PhysicsAIRef::redBrickVehicle)
+ {
+ int groundPlaneIndex = pObject->GetGroundPlaneIndex();
+ if(groundPlaneIndex != -1) // should never be false...
+ {
+ sim::CollisionObject* gpCollObj = mGroundPlanePool->GetSimState(groundPlaneIndex)->GetCollisionObject();
+
+ mCollisionManager->RemoveCollisionObject(gpCollObj, collisionAreaIndex);
+
+ pObject->FreeGroundPlane(); // this decrements the groundplaneref, but we still need to take it out of
+ // there are no more refs
+ }
+ }
+ pObject->Release( );
+ mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG = 0;
+ mCurrentDynamics[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::UpdateSimluatingDynamicObjectGroundPlanes
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::UpdateSimluatingDynamicObjectGroundPlanes()
+{
+ int i;
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ if(!mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+
+
+ int j;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if( mCurrentDynamics[i][j].mDynamicPhysDSG &&
+ mCurrentDynamics[i][j].mDynamicPhysDSG->GetSimState()->GetControl() == sim::simSimulationCtrl &&
+ mCurrentDynamics[i][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle &&
+ mCurrentDynamics[i][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::PlayerCharacter ) // TODO - vehicles use also?
+
+ // TODO - rest test??? or is that covered under sim control?
+
+ {
+ // need to update this ground plane
+ // TODO
+ // is the position of the DSG object being updated correctly???????
+ //
+ // no implementatino of that for dynamics yet!
+
+ rmt::Vector inPosition = mCurrentDynamics[i][j].mDynamicPhysDSG->rPosition();
+ float inRadius = 1.0f;
+
+ // ignore the tri values
+ rmt::Vector triNormal;
+ rmt::Vector triPosition;
+
+ bool foundPlane;
+ rmt::Vector planeNormal;
+ rmt::Vector planePosition;
+
+
+ GetIntersectManager()->FindIntersection(inPosition,
+ foundPlane,
+ planeNormal,
+ planePosition);
+
+
+ if(foundPlane)
+ {
+ mGroundPlanePool->UpdateGroundPlane(mCurrentDynamics[i][j].mDynamicPhysDSG->GetGroundPlaneIndex(), planePosition, planeNormal);
+ }
+ else
+ {
+ //rAssert(0);
+ int stophere = 1; // hopefully not.
+ }
+
+ }
+
+ }
+ }
+
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetNewGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetNewGroundPlane(sim::SimState* simStateOwner)
+{
+ return mGroundPlanePool->GetNewGroundPlane(simStateOwner);
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::FreeGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::FreeGroundPlane(int index)
+{
+ mGroundPlanePool->FreeGroundPlane(index);
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::EnableGroundPlaneCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::EnableGroundPlaneCollision(int index)
+{
+ //char buffy[128];
+ //sprintf(buffy, "enabling ground plane index %d\n", index);
+
+ //rReleaseString(buffy);
+
+ mGroundPlanePool->EnableCollision(index);
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::DisableGroundPlaneCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DisableGroundPlaneCollision(int index)
+{
+ //char buffy[128];
+ //sprintf(buffy, "disabling ground plane index %d\n", index);
+
+ //rReleaseString(buffy);
+
+ mGroundPlanePool->DisableCollision(index);
+}
+
+//=============================================================================
+// WorldPhysicsManager::UpdateDynamicObjects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::UpdateDynamicObjects(float dt, int collisionAreaIndex)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert( mCollisionAreaAllocated[collisionAreaIndex] );
+
+ int j;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if(mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG)
+ {
+ mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->RestTest();
+
+ bool atRest = mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->IsAtRest();
+
+ // need to keep updating when "at rest" if it's a onetime movable that has been hit
+ if(atRest && mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetCollisionAttributes())
+ {
+ atRest = atRest && !((mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE)
+ && mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->mIsHit);
+ }
+
+ // don't update vehicles or characters (they are updated elsewhere),
+ // stuff at rest, or stuff that has already been updated.
+ if((mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle) &&
+ (mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::PlayerCharacter ) &&
+ (!atRest) &&
+ (mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetLastUpdate() != updateFrame))
+ {
+ // set the update tick so we wont update this again
+ mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->SetLastUpdate(updateFrame);
+ mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->Update(dt);
+ }
+ }
+ }
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::UpdateAnimCollisions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::UpdateAnimCollisions(float dt, int collisionAreaIndex)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert(mCollisionAreaAllocated[collisionAreaIndex]);
+
+ int j;
+ for(j = 0; j < mMaxUpdateAnimCollisions; j++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG->GetLastUpdate() != updateFrame)
+ {
+ // set the update tick so we wont update this again
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG->SetLastUpdate(updateFrame);
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG->Update(dt);
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::SubmitFencePiecesPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (position, radius, collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitFencePiecesPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ ReserveArray<FenceEntityDSG*> fenceDSGList;
+
+ BEGIN_PROFILE( "FindFenceElems" );
+ GetIntersectManager()->FindFenceElems(position, radius, fenceDSGList);
+ END_PROFILE( "FindFenceElems" );
+
+ int numResults = fenceDSGList.mUseSize;
+
+ //int numResults = 1;
+ if(numResults > 0)
+ {
+ int stophere = 1;
+ }
+
+ if(numResults > mMaxFencePerArea)
+ {
+ numResults = mMaxFencePerArea;
+ }
+
+ // fill in debug rendering list:
+ int i;
+
+ for(i = 0; i < numResults; i++)
+ {
+ mFenceDSGResults[i]->mStartPoint = fenceDSGList[i]->mStartPoint;
+ mFenceDSGResults[i]->mEndPoint = fenceDSGList[i]->mEndPoint;
+ mFenceDSGResults[i]->mNormal = fenceDSGList[i]->mNormal;
+
+ mFenceDSGResults[i]->mStartPoint.y = position.y;
+ mFenceDSGResults[i]->mEndPoint.y = position.y;
+
+
+ }
+ mNumDebugFences = numResults;
+
+ // reset list
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ mFences[collisionAreaIndex][i].mClean = false;
+ }
+
+ for(i = 0; i < numResults; i++)
+ {
+
+ // i is candidate for submission
+
+ // just in case, do this in function, not here
+ //fenceDSGList[i]->mStartPoint.y = position.y;
+ //fenceDSGList[i]->mEndPoint.y = position.y;
+
+ mFences[collisionAreaIndex][i].start = fenceDSGList[i]->mStartPoint;
+ mFences[collisionAreaIndex][i].end = fenceDSGList[i]->mEndPoint;
+
+ if(UpdateFencePiece(position, mFences[collisionAreaIndex][i].mFenceSimState,
+ fenceDSGList[i]->mStartPoint, fenceDSGList[i]->mEndPoint,
+ fenceDSGList[i]->mNormal,
+ callerSimState))
+ {
+ // ok for this one to be in collision
+ //
+
+ if(mFences[collisionAreaIndex][i].mInCollision == false)
+ {
+ // we need to add this into the collision manager
+ if(allowAutoPairing)
+ {
+ mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject()->SetAutoPair(true);
+ }
+ else
+ {
+ mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject()->SetAutoPair(false);
+ }
+
+ mCollisionManager->AddCollisionObject(mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject(), collisionAreaIndex);
+ mCollisionManager->AddPair(callerSimState->GetCollisionObject(), mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject(), collisionAreaIndex);
+ mFences[collisionAreaIndex][i].mInCollision = true;
+
+ }
+ mFences[collisionAreaIndex][i].mClean = true;
+
+ }
+ }
+
+ // update list
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ if(mFences[collisionAreaIndex][i].mInCollision == true &&
+ mFences[collisionAreaIndex][i].mClean == false)
+ {
+ mCollisionManager->RemoveCollisionObject(mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject(), collisionAreaIndex);
+ mFences[collisionAreaIndex][i].mInCollision = false;
+ }
+
+
+ }
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::UpdateFencePiece
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (SimState* fencePiece, rmt::Vector* end0, rmt::Vector* end1, rmt::Vector* normal)
+//
+// Return: void
+//
+//=============================================================================
+bool WorldPhysicsManager::UpdateFencePiece(rmt::Vector& callerPosition, sim::ManualSimState* fencePiece, rmt::Vector& end0,
+ rmt::Vector& end1, rmt::Vector& normal, sim::SimState* callerSimState)
+{
+
+ // on the collision volume
+ // set position,
+ // mAxis
+ // mLength
+ rmt::Vector o0, o1, o2;
+ float l0, l1, l2;
+
+ // pain in the ass - just get it to fucking work
+ rmt::Vector localEnd0 = end0;
+ rmt::Vector localEnd1 = end1;
+
+ localEnd0.y = callerPosition.y;
+ localEnd1.y = callerPosition.y;
+
+ o1.Set(0.0f, 1.0f, 0.0f); // y is always straight up
+
+ o2 = normal; // safe to assume this is normalized?
+
+ o0.CrossProduct(o1, o2);
+
+ //l1 = 10000.0f; // magic big ass number
+ l1 = 10.0f; // use a smaller number for now so the debug drawn volumes are easier to see
+
+ rmt::Vector segment;
+ segment.Sub(localEnd1, localEnd0);
+
+ float segmentLength = segment.Magnitude();
+
+ l0 = segmentLength * 0.5f;
+
+
+ l2 = 10.0f; // ? whatever? TODO - is this value ok?
+
+ if(callerSimState->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ // Cary!
+ // change this number!
+
+ // this is half the length of the fence piece obbox
+
+ l2 = 2.0f;
+ //l2 = 10.0f;
+ }
+
+ rmt::Vector midpoint;
+ midpoint.Add(localEnd0, localEnd1);
+ midpoint.Scale(0.5f);
+
+
+ // before doing anything else, test if we are pointed at the fucker
+ // TODO - maybe this test should be moved up to the submit part?
+ // maybe not
+ rmt::Vector test;
+ test.Sub(callerPosition, midpoint);
+
+ if(test.DotProduct(normal) < 0.0f && callerSimState->mAIRefIndex != PhysicsAIRef::CameraSphere)
+ {
+
+ fencePiece->GetCollisionObject()->SetCollisionEnabled(false);
+ return false;
+ }
+
+ rmt::Vector position = midpoint;
+ rmt::Vector centerAdjust = o2;
+ centerAdjust.Scale(l2);
+ position.Sub(centerAdjust);
+
+
+ // for convenience:
+ sim::CollisionObject* co = fencePiece->GetCollisionObject();
+ sim::OBBoxVolume* obbox = (OBBoxVolume*)(co->GetCollisionVolume());
+
+ obbox->Set(position, o0, o1, o2, l0, l1, l2);
+
+ co->PostManualUpdate();
+
+ //co->Relocated();
+ //obbox->UpdateBBox();
+
+ // only need to do this once
+ co->SetCollisionEnabled(true);
+
+
+ // on the Object call Relocated
+ //
+ //virtual void UpdateBBox() {}
+ // make sure mUpdated is true
+ // what about dP??
+ //
+ // what about OptimizeAxis
+ // what about SetRotation?
+
+ // GetCollisionObject()->SetCollisionEnabled(true);
+
+
+ //#ifdef RAD_DEBUG
+
+ //sim::DrawCollisionObject(CollisionObject* inObject);
+ //sim::DrawCollisionVolume(obbox);
+
+ //#endif
+
+ return true;
+
+
+}
+
+//----------------------------------------------------------------------------
+// Segment intersection, stolen, as usual, from www.magic-software.com
+//----------------------------------------------------------------------------
+
+// even though we're using 3d vectors (cause that's what we have) this is only a 2d intersection
+// in x & z
+static bool Find (const rmt::Vector& rkP0, const rmt::Vector& rkD0,
+ const rmt::Vector& rkP1, const rmt::Vector& rkD1, rmt::Vector& rkDiff,
+ float& rfD0SqrLen, int& riQuantity, float afT[2])
+{
+ // Intersection is a solution to P0+s*D0 = P1+t*D1. Rewrite as
+ // s*D0 - t*D1 = P1 - P0, a 2x2 system of equations. If D0 = (x0,y0)
+ // and D1 = (x1,y1) and P1 - P0 = (c0,c1), then the system is
+ // x0*s - x1*t = c0 and y0*s - y1*t = c1. The error tests are relative
+ // to the size of the direction vectors, |Cross(D0,D1)| >= e*|D0|*|D1|
+ // rather than absolute tests |Cross(D0,D1)| >= e. The quantities
+ // P1-P0, |D0|^2, and |D1|^2 are returned for use by calling functions.
+
+ float fDet = rkD1.x*rkD0.z - rkD1.z*rkD0.x;
+ rkDiff = rkP1 - rkP0;
+ rfD0SqrLen = rkD0.MagnitudeSqr();
+
+ const float fEpsilon = 1e-06f;
+ if ( fDet*fDet > fEpsilon*rfD0SqrLen*rkD1.MagnitudeSqr() )
+ {
+ // Lines intersect in a single point. Return both s and t values for
+ // use by calling functions.
+ float fInvDet = 1.0f/fDet;
+ riQuantity = 1;
+ afT[0] = (rkD1.x*rkDiff.z - rkD1.z*rkDiff.x)*fInvDet;
+ afT[1] = (rkD0.x*rkDiff.z - rkD0.z*rkDiff.x)*fInvDet;
+ }
+ else
+ {
+ // lines are parallel
+ fDet = rkD0.x*rkDiff.z - rkD0.z*rkDiff.x;
+ if ( fDet*fDet > fEpsilon*rfD0SqrLen*rkDiff.MagnitudeSqr() )
+ {
+ // lines are disjoint
+ riQuantity = 0;
+ }
+ else
+ {
+ // lines are the same
+ riQuantity = 2;
+ }
+ }
+
+ return riQuantity != 0;
+}
+
+//----------------------------------------------------------------------------
+static bool FindIntersection (const rmt::Vector& origin0, const rmt::Vector& direction0,
+ const rmt::Vector& origin1, const rmt::Vector& direction1,
+ int& riQuantity)
+{
+ float afT[2];
+
+ rmt::Vector kDiff;
+ float fD0SqrLen;
+ bool bIntersects = Find(origin0, direction0,
+ origin1,direction1,kDiff,fD0SqrLen,
+ riQuantity,afT);
+
+ if ( bIntersects )
+ {
+ if ( riQuantity == 1 )
+ {
+ if ( afT[0] < 0.0f || afT[0] > 1.0f
+ || afT[1] < 0.0f || afT[1] > 1.0f )
+ {
+ // lines intersect, but segments do not
+ riQuantity = 0;
+ }
+ }
+ else
+ {
+ // segments are on the same line
+ float fDotRS = direction0.Dot(direction1);
+ float fDot0, fDot1;
+ if ( fDotRS > 0.0f )
+
+ {
+ fDot0 = kDiff.Dot(direction0);
+ fDot1 = fDot0 + fDotRS;
+ }
+ else
+ {
+ fDot1 = kDiff.Dot(direction0);
+ fDot0 = fDot1 + fDotRS;
+ }
+
+ // compute intersection of [t0,t1] and [0,1]
+ if ( fDot1 < 0.0f || fDot0 > fD0SqrLen )
+ {
+ riQuantity = 0;
+ }
+ else if ( fDot1 > 0.0f )
+ {
+ if ( fDot0 < fD0SqrLen )
+ {
+ float fInvLen = 1.0f/fD0SqrLen;
+ riQuantity = 2;
+ afT[0] = ( fDot0 < 0.0f ? 0.0f : fDot0*fInvLen );
+ afT[1] = ( fDot1 > fD0SqrLen ? 1.0f : fDot1*fInvLen );
+ }
+ else // fT0 == 1
+ {
+ riQuantity = 1;
+ afT[0] = 1.0f;
+ }
+ }
+ else // fT1 == 0
+ {
+ riQuantity = 1;
+ afT[0] = 0.0f;
+ }
+ }
+ }
+
+ return riQuantity != 0;
+}
+
+bool WorldPhysicsManager::FenceSanityCheck(unsigned collisionAreaIndex, const rmt::Vector lastFrame, const rmt::Vector thisFrame, rmt::Vector* fixPos)
+{
+ rmt::Vector dir, dirTweak;
+ dir.Sub(thisFrame, lastFrame);
+
+ if(dir.MagnitudeSqr() == 0.0f)
+ {
+ return false;
+ }
+
+ dirTweak = dir;
+ dirTweak.Normalize();
+ dirTweak.Scale(0.07f);
+ dir += dirTweak;
+
+ for(int i = 0; i < mMaxFencePerArea; i++)
+ {
+ if(mFences[collisionAreaIndex][i].mClean)
+ {
+ rmt::Vector fenceDir, fenceTweak, fenceStart;
+ fenceDir.Sub(mFences[collisionAreaIndex][i].end, mFences[collisionAreaIndex][i].start);
+
+ fenceTweak = fenceDir;
+ fenceTweak.Normalize();
+ fenceTweak.Scale(0.07f);
+
+ fenceDir += fenceTweak;
+ fenceDir += fenceTweak;
+
+ fenceStart = mFences[collisionAreaIndex][i].start;
+ fenceStart -= fenceTweak;
+
+ int quantity;
+
+ if(FindIntersection(lastFrame, dir, fenceStart, fenceDir, quantity))
+ {
+ if(quantity == 1)
+ {
+ *fixPos = lastFrame;
+ fixPos->y = thisFrame.y;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+float WorldPhysicsManager::GetLoopTime()
+{
+ return mLoopTime;
+}
diff --git a/game/code/worldsim/worldphysicsmanager.h b/game/code/worldsim/worldphysicsmanager.h
new file mode 100644
index 0000000..e8509ce
--- /dev/null
+++ b/game/code/worldsim/worldphysicsmanager.h
@@ -0,0 +1,296 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldphysicsmanager.h
+//
+// Description: manage the world physics
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+#ifndef WORLDPHYSICSMANAGER_H
+#define WORLDPHYSICSMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+class AnimCollisionEntityDSG;
+class CollisionEntityDSG;
+class DynaPhysDSG;
+class WorldCollisionSolverAgentManager;
+class StaticPhysDSG;
+class GroundPlanePool;
+class FenceEntityDSG;
+class Vehicle;
+namespace sim
+{
+ class CollisionManager;
+ class ManualSimState;
+ class SimEnvironment;
+ class SimState;
+ class PhysicsProperties;
+}
+
+//=============================================================================
+//
+// Synopsis: this is the key tie-in point to the gameplay context to
+// run the simulation of the game world
+//
+//=============================================================================
+class WorldPhysicsManager
+{
+ public:
+
+ // Structure thats filled out upon call to ApplyForceToDynamics
+ // indicating the counts for the various types of objects that got hit
+ struct NumObjectsHit
+ {
+ NumObjectsHit() : numNPCsHit(0) {}
+ int numNPCsHit;
+ };
+
+ struct CollisionEntityDSGList
+ {
+ static const int NUM_COLLISION_LIST_ENTITIES = 3;
+
+ CollisionEntityDSG* collisionEntity[NUM_COLLISION_LIST_ENTITIES];
+ };
+
+ // Static Methods for accessing this singleton.
+ static WorldPhysicsManager* GetInstance();
+ static WorldPhysicsManager* CreateInstance();
+ static void DestroyInstance();
+
+ // Important!
+ // this method must be called before we load _anything_!
+ //static void SetSimUnits();
+
+ void Init(); // TODO - need this?
+
+ void Update(unsigned int timeDeltaMilliSeconds);
+
+ // kind of a hack?
+ void SuspendForInterior();
+ void ResumeForOutside();
+
+ void OnQuitLevel();
+
+ sim::SimEnvironment* mSimEnvironment;
+ float mCollisionDistanceCGS;
+ rmt::Vector mWorldUp;
+
+ sim::CollisionManager* mCollisionManager;
+
+ WorldCollisionSolverAgentManager* mpWorldCollisionSolverAgentManager;
+
+ int GetCameraCollisionAreaIndex(); // return the next free one
+ int GetVehicleCollisionAreaIndex();
+ int GetCharacterCollisionAreaIndex();
+
+ void FreeCollisionAreaIndex(int index);
+ void FreeAllCollisionAreaIndicies();
+ void EmptyCollisionAreaIndex(int index);
+ static const int INVALID_COLLISION_AREA = -1;
+ void SubmitStaticsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing = false);
+ void SubmitFencePiecesPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing = false);
+ //void SubmitFencePiecesPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex);
+ void SubmitDynamicsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* caller, bool allowAutoPairing = false);
+
+ //bool WorldPhysicsManager::IsASubmitter(sim::SimState* testSimState);
+
+ void SubmitAnimCollisionsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState);
+
+ void SubmitAnimCollisionsForUpdateOnly(rmt::Vector& position, float radius, int collisionAreaIndex);
+
+ void UpdateAnimCollisions(float dt, int collisionAreaIndex);
+ void UpdateDynamicObjects(float dt, int collisionAreaIndex);
+
+
+ void DisplayFencesInArea(int area);
+
+ // need to do this when we remove a vehicle from an active collision area index...
+ void RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(DynaPhysDSG* vehicle);
+
+ // allow access to groundplanepool
+ int GetNewGroundPlane(sim::SimState* simStateOwner); // user refers to it with the returned index
+ void FreeGroundPlane(int index);
+
+ // add pair to collision
+ // called by object
+ //void SubmitGroundPlaneToCollisionManager(sim::SimState* simStateToPair, int groundPlaneIndex);
+ //void RemoveGroundPlaneFromCollisionManager(int groundPlaneIndex);
+
+ void EnableGroundPlaneCollision(int index);
+ void DisableGroundPlaneCollision(int index);
+
+ void StartTimer();
+ void StopTimer();
+ void ResetTimer();
+
+ void ToggleTimerState();
+
+ // Apply the given force to nearby objects. Returns the number of objects affected
+ int ApplyForceToDynamics( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ const rmt::Vector& direction,
+ float force,
+ WorldPhysicsManager::NumObjectsHit* pObjectsHit = NULL,
+ CollisionEntityDSGList* pCollisionEntityList = NULL );
+
+ int ApplyForceToDynamicsSpherical( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ float force,
+ CollisionEntityDSGList* pCollisionEntityList = NULL );
+ float GetLoopTime();
+
+ //--------
+ // statics
+ //--------
+ struct StaticsInCollisionDetection
+ {
+ StaticPhysDSG* mStaticPhysDSG;
+ bool clean;
+ };
+ int mMaxStatics; // per area
+
+ // for each collision area, need the 'clean' list
+ StaticsInCollisionDetection** mCurrentStatics;
+
+ void PurgeDynamics( int collisionAreaIndex );
+
+ bool FenceSanityCheck(unsigned collisionAreaIndex, const rmt::Vector lastFrame, const rmt::Vector thisFrame, rmt::Vector* fixPos);
+
+ private:
+
+ // No public access to these, use singleton interface.
+ WorldPhysicsManager();
+ ~WorldPhysicsManager();
+
+ void InitCollisionManager();
+
+
+ void WorldSimSubstepGuts(float dt, bool firstSubstep);
+
+ // pointer to the single instance
+ static WorldPhysicsManager* spInstance;
+
+ float mTotalTime;
+ float mTimerTime;
+ bool mTimerOn;
+
+
+ void DebugInfoDisplay();
+
+ //----------------------
+ // anim collision entity
+ //----------------------
+
+ // these are for collision detection only
+ struct AnimCollisionInCollisionDetection
+ {
+ AnimCollisionEntityDSG* mAnimCollisionEntityDSG;
+ bool clean;
+ };
+ int mMaxAnimCollisions;
+
+ AnimCollisionInCollisionDetection** mCurrentAnimCollisions;
+
+ // these ones are for update only - we can and want to update stuff that is too far away to collide with, for appearance sake
+ AnimCollisionInCollisionDetection** mCurrentUpdateAnimCollisions;
+ int mMaxUpdateAnimCollisions;
+
+ //---------
+ // dynamics
+ //---------
+ struct DynamicsInCollisionDetection
+ {
+ //InstDynaPhysDSG* mDynamicPhysDSG;
+ DynaPhysDSG* mDynamicPhysDSG;
+ bool clean;
+ //int groundPlaneIndex; have to hold this in the InstDynaPhysDSG class since object could be in more than one update list
+ };
+ int mMaxDynamics; // per area
+
+ // the 'clean' list
+ //
+ DynamicsInCollisionDetection** mCurrentDynamics;
+
+ // this list will hold all dynamics that should be tested for collision against.
+ // some will be under ai ctrl, and one's that have been collided with will be under
+ // simulation ctrl
+ //
+ // only the simulation ctrl ones need a physics update and ground plane and a rest test
+ //
+ // for now... don't remove from the list until object has come to rest
+
+ GroundPlanePool* mGroundPlanePool;
+ void UpdateSimluatingDynamicObjectGroundPlanes();
+
+ bool* mCollisionAreaAllocated; // has been requested for use by a vehicle, character, or camera
+ bool* mCollisionAreaActive; // for intermediate, temporary disabling
+ int mNumCollisionAreas;
+
+
+ // for debugging?
+ int mReservedCollisionAreas; // reserve 0...mReservedCollisionAreas - 1
+
+ int mMaxVehicles;
+ int mMaxChars;
+ int mMaxCameras;
+
+ //-------------
+ // fence pieces
+ //-------------
+
+ sim::PhysicsProperties* mFencePhysicsProperties;
+
+ // have a pool of these
+
+ struct FencePieces
+ {
+ sim::ManualSimState* mFenceSimState;
+ bool mInCollision;
+ bool mClean;
+ rmt::Vector start, end;
+ };
+
+ FencePieces** mFences;
+
+ // debug drawing:
+ // note this only really works for one thing calling submit fence pieces!
+ int mNumDebugFences;
+ FenceEntityDSG** mFenceDSGResults;
+
+
+ //sim::ManualSimState*** mFenceSimStates; // a 2D array of pointers
+
+ int mMaxFencePerArea;
+ int* mFencesInEachArea; // ? still need this
+
+ bool UpdateFencePiece(rmt::Vector& callerPosition, sim::ManualSimState* fencePiece, rmt::Vector& end0, rmt::Vector& end1, rmt::Vector& normal, sim::SimState* callerSimState);
+
+
+ // ? bit of a hack here for player character going into interiors
+ bool mInInterior;
+
+ // current physics "frame" (for detecting duplicate updating
+ // when an object is in multip[le lists
+ unsigned updateFrame;
+
+ float mLoopTime;
+ unsigned mLastTime;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline WorldPhysicsManager* GetWorldPhysicsManager() { return( WorldPhysicsManager::GetInstance() ); }
+
+
+
+#endif //WORLDPHYSICSMANAGER_H