5403 lines
181 KiB
C++
5403 lines
181 KiB
C++
#include "TaskVehiclePlayer.h"
|
|
|
|
// Framework headers
|
|
#include "fwmaths/vector.h"
|
|
#include "fwmaths/angle.h"
|
|
#include "math/vecMath.h"
|
|
|
|
|
|
// Game headers
|
|
#include "camera/gameplay/GameplayDirector.h"
|
|
#include "camera/gameplay/follow/FollowVehicleCamera.h"
|
|
#include "frontend/PauseMenu.h"
|
|
#include "frontend/MultiplayerChat.h"
|
|
#include "system/ControlMgr.h"
|
|
#include "network/NetworkInterface.h"
|
|
#include "network/Events/NetworkEventTypes.h"
|
|
#include "Peds/ped.h"
|
|
#include "Peds/PedIntelligence.h"
|
|
#include "peds/Ped.h"
|
|
#include "Peds/PlayerInfo.h"
|
|
#include "peds/PlayerSpecialAbility.h"
|
|
#include "audio/planeaudioentity.h"
|
|
#include "control/record.h"
|
|
#include "Vehicles/Metadata/VehicleMetadataManager.h"
|
|
#include "Vehicles/Heli.h"
|
|
#include "Vehicles/AmphibiousAutomobile.h"
|
|
#include "Vehicles/Automobile.h"
|
|
#include "Vehicles/Bike.h"
|
|
#include "Vehicles/Boat.h"
|
|
#include "Vehicles/Planes.h"
|
|
#include "Vehicles/Submarine.h"
|
|
#include "Vehicles/VehicleGadgets.h"
|
|
#include "Vehicles/VehicleFactory.h"
|
|
#include "Vfx/Systems/VfxVehicle.h"
|
|
#include "scene/world/GameWorld.h"
|
|
#include "Physics/PhysicsHelpers.h"
|
|
#include "vehicles/Metadata/VehicleSeatInfo.h"
|
|
#include "vehicles/Metadata/VehicleLayoutInfo.h"
|
|
#include "vehicleAi/task/TaskVehicleGoToHelicopter.h"
|
|
#include "vehicleAi/task/TaskVehicleFlying.h"
|
|
#include "vehicleAi/task/TaskVehicleAnimation.h"
|
|
#include "vehicleAi/task/TaskVehicleGoToAutomobile.h"
|
|
#include "vehicleAi/VehicleIntelligence.h"
|
|
#include "debug/DebugScene.h"
|
|
#include "Task/Vehicle/TaskCar.h"
|
|
#include "Task/Vehicle/TaskExitVehicle.h"
|
|
#include "Task/Vehicle/TaskInVehicle.h"
|
|
#include "Game/ModelIndices.h"
|
|
|
|
AI_OPTIMISATIONS()
|
|
AI_VEHICLE_OPTIMISATIONS()
|
|
|
|
BANK_ONLY( PARAM( steerToCamera, "steertocamera" ) );
|
|
|
|
#if RSG_ORBIS
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_STOPPED = 12.0f;
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_ATSPEED = 6.0f;
|
|
#else
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_STOPPED = 10.0f;
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_ATSPEED = 5.0f;
|
|
#endif
|
|
|
|
bank_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_STOPPED_SPECIAL = 20.0f;
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_STOPPED_QUAD = 5.0f;
|
|
|
|
|
|
bank_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_ATSPEED_SPECIAL = 15.0f;
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_ATSPEED_QUAD = 4.0f;
|
|
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_SMOOTH_RATE_MAXSPEED = 30.0f;
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEERING_CURVE_POW = 1.5f;
|
|
dev_float CTaskVehiclePlayerDriveAutomobile::ms_fCAR_STEER_STATIONARY_AUTOCENTRE_MAX = (DtoR * 10.0f);
|
|
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBIKE_STEER_SMOOTH_RATE_MAXSPEED = 25.0f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBIKE_STD_STEER_SMOOTH_RATE = 7.0f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBIKE_STOPPED_STEER_SMOOTH_RATE = 5.5f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBIKE_STD_LEAN_SMOOTH_RATE = 15.0f;
|
|
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBICYCLE_STEER_SMOOTH_RATE_MAXSPEED = 15.0f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBICYCLE_STD_STEER_SMOOTH_RATE = 4.5f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBICYCLE_STOPPED_STEER_SMOOTH_RATE = 6.0f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBICYCLE_STD_LEAN_SMOOTH_RATE = 12.0f;
|
|
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBICYCLE_MIN_FORWARDNESS_SLOW = 0.35f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBICYCLE_MAX_FORWARDNESS_SLOW = 0.40f;
|
|
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fMOTION_STEER_SMOOTH_RATE_MULT = 3.0f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fMOTION_STEER_DEAD_ZONE = 0.06f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fPAD_STEER_REUCTION_PAD_END = 0.9f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBICYCLE_STEER_REDUCTION_UNDER_LIMIT_MULT = 0.9f;
|
|
dev_float CTaskVehiclePlayerDriveBike::ms_fBIKE_STEER_REDUCTION_UNDER_LIMIT_MULT = 0.6f;
|
|
|
|
dev_float CTaskVehiclePlayerDriveBoat::ms_fBOAT_STEER_SMOOTH_RATE = 8.0f;
|
|
dev_float CTaskVehiclePlayerDriveBoat::ms_fBOAT_PITCH_FWD_SMOOTH_RATE = 10.0f;
|
|
|
|
dev_float CTaskVehiclePlayerDriveRotaryWingAircraft::ms_fHELI_THROTTLE_CONTROL_DAMPING = 0.1f;
|
|
dev_float CTaskVehiclePlayerDriveRotaryWingAircraft::ms_fHELI_AUTO_THROTTLE_FALLOFF = 0.2f;
|
|
dev_float CTaskVehiclePlayerDriveRotaryWingAircraft::ms_fHELI_STEERING_CURVE_POW_MOUSE = 1.5f;
|
|
|
|
dev_float CTaskVehiclePlayerDrivePlane::ms_fPLANE_STEERING_CURVE_POW_MOUSE = 2.0f;
|
|
|
|
float CTaskVehiclePlayerDriveAutomobile::ms_fAUTOMOBILE_MAXSPEEDNOW = 4.0f;
|
|
float CTaskVehiclePlayerDriveAutomobile::ms_fAUTOMOBILE_MAXSPEEDCHANGE = 12.0f;
|
|
float CTaskVehiclePlayerDrivePlane::ms_fPLANE_MAXSPEEDNOW = 14.0f;
|
|
float CTaskVehiclePlayerDriveBike::ms_fBIKE_MAXSPEEDNOW = 4.0f;
|
|
float CTaskVehiclePlayerDriveBike::ms_fBIKE_MAXSPEEDCHANGE = 12.0f;
|
|
|
|
const u32 g_HeliHoverCameraHash = ATSTRINGHASH("FOLLOW_HELI_HOVER_CAMERA", 0xD7020D1F);
|
|
|
|
bank_float CTaskVehiclePlayerDrive::sfSpecialAbilitySteeringTimeStepMult = 1.0f;
|
|
|
|
#if RSG_PC
|
|
const float c_fMouseAdjustmentScale = 4.0f; // undo scaling done in mapper.cpp, can be increased if too slow.
|
|
dev_float c_fMouseTollerance = 0.1f;
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDrive::CTaskVehiclePlayerDrive() :
|
|
CTaskVehicleMissionBase(),
|
|
m_fTimeSincePlayingRecording(999.0f),
|
|
m_bExitButtonUp(false),
|
|
m_bDoorControlButtonUp(false),
|
|
m_uTimeHydraulicsControlPressed(0)
|
|
#if RSG_PC
|
|
, m_fLowriderUpDownInput(0.0f)
|
|
#endif
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDrive::~CTaskVehiclePlayerDrive()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
CTask::FSM_Return CTaskVehiclePlayerDrive::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
|
|
|
|
//player is always dirty
|
|
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
|
|
|
|
FSM_Begin
|
|
//
|
|
FSM_State(State_Drive)
|
|
FSM_OnEnter
|
|
Drive_OnEnter(pVehicle);
|
|
FSM_OnExit
|
|
Drive_OnExit(pVehicle);
|
|
FSM_OnUpdate
|
|
return Drive_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_NoDriver)
|
|
FSM_OnEnter
|
|
NoDriver_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return NoDriver_OnUpdate(pVehicle);
|
|
FSM_End
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//State_Drive
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDrive::Drive_OnEnter(CVehicle* pVehicle)
|
|
{
|
|
// Setup a convertible roof control task if we have an animation clip dictionary
|
|
if( pVehicle->DoesVehicleHaveAConvertibleRoofAnimation() )
|
|
{
|
|
aiTask *pTask = pVehicle->GetIntelligence()->GetTaskManager()->GetTree(VEHICLE_TASK_TREE_SECONDARY)->GetActiveTask();
|
|
|
|
if(!pTask || pTask->GetTaskType() != CTaskTypes::TASK_VEHICLE_CONVERTIBLE_ROOF)
|
|
{
|
|
CTaskVehicleConvertibleRoof *convertibleRoof= rage_new CTaskVehicleConvertibleRoof(false,pVehicle->m_nVehicleFlags.bRoofLowered);
|
|
pVehicle->GetIntelligence()->GetTaskManager()->GetTree(VEHICLE_TASK_TREE_SECONDARY)->SetTask(convertibleRoof, VEHICLE_TASK_SECONDARY_ANIM);
|
|
}
|
|
}
|
|
if( pVehicle->DoesVehicleHaveATransformAnimation() )
|
|
{
|
|
aiTask *pTask = pVehicle->GetIntelligence()->GetTaskManager()->GetTree( VEHICLE_TASK_TREE_SECONDARY )->GetActiveTask();
|
|
if( !pTask || pTask->GetTaskType() != CTaskTypes::TASK_VEHICLE_TRANSFORM_TO_SUBMARINE )
|
|
{
|
|
CTaskVehicleTransformToSubmarine *transformToSubmarine= rage_new CTaskVehicleTransformToSubmarine();
|
|
pVehicle->GetIntelligence()->GetTaskManager()->GetTree(VEHICLE_TASK_TREE_SECONDARY)->SetTask( transformToSubmarine, VEHICLE_TASK_SECONDARY_ANIM );
|
|
}
|
|
}
|
|
|
|
if( pVehicle->GetClearWaitingOnCollisionOnPlayerEnter() )
|
|
{
|
|
pVehicle->m_nVehicleFlags.bShouldFixIfNoCollision = false;
|
|
}
|
|
|
|
ProcessDriverInputsForPlayerOnEnter(pVehicle);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//State_Drive
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDrive::Drive_OnExit(CVehicle* pVehicle)
|
|
{
|
|
ProcessDriverInputsForPlayerOnExit(pVehicle);
|
|
|
|
// if the vehicle has KERS make sure we disable it when the player exits
|
|
if( pVehicle->pHandling->hFlags & HF_HAS_KERS )
|
|
{
|
|
pVehicle->SetKERS( false );
|
|
pVehicle->m_Transmission.DisableKERSEffect();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//State_Drive
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
aiTask::FSM_Return CTaskVehiclePlayerDrive::Drive_OnUpdate (CVehicle* pVehicle)
|
|
{
|
|
const bool bHasDriver = pVehicle->GetDriver() != NULL;
|
|
const bool bDriverInjured = pVehicle->GetDriver() && pVehicle->GetDriver()->IsInjured();
|
|
const bool bDriverArrested = pVehicle->GetDriver() && pVehicle->GetDriver()->GetIsArrested();
|
|
const bool bDriverJumpingOut = false;//pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedIntelligence()->GetQueriableInterface()->GetStateForTaskType(CTaskTypes::TASK_EXIT_VEHICLE_EXIT_SEAT) == CTaskExitVehicleExitSeat::State_JumpOutOfSeat ? true : false;
|
|
|
|
m_fTimeSincePlayingRecording = rage::Min(999.0f, m_fTimeSincePlayingRecording + fwTimer::GetTimeStep());
|
|
if( pVehicle && pVehicle->IsRunningCarRecording() )
|
|
{
|
|
m_fTimeSincePlayingRecording = 0.0f;
|
|
}
|
|
|
|
if(bHasDriver && !bDriverArrested && !bDriverInjured && !bDriverJumpingOut && taskVerifyf(pVehicle->GetDriver()->GetPlayerInfo(), "Invalid player info!"))
|
|
{
|
|
Assert( pVehicle->GetDriver() );
|
|
Assert(pVehicle->GetDriver()->IsPlayer());
|
|
CPed* pPlayerPed = pVehicle->GetDriver();
|
|
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl==NULL)
|
|
return FSM_Continue;
|
|
|
|
ProcessDriverInputsForPlayerOnUpdate(pPlayerPed, pControl, pVehicle);
|
|
|
|
// Dont override the velocity unless the player hasn't been running a recording for at least a second
|
|
if(IsThePlayerControlInactive(pControl) && m_fTimeSincePlayingRecording > 0.1f)
|
|
{
|
|
ProcessDriverInputsForPlayerInactiveOnUpdate(pControl, pVehicle);
|
|
}
|
|
|
|
if( CGameWorld::GetMainPlayerInfo()->AreControlsDisabled() || CControlMgr::GetMainPlayerControl().IsInputDisabled(INPUT_VEH_EXIT) WIN32PC_ONLY( || SMultiplayerChat::GetInstance().ShouldDisableInputSinceChatIsTyping()))
|
|
{
|
|
m_bExitButtonUp = false;
|
|
}
|
|
else if( !m_bExitButtonUp && !pControl->GetVehicleExit().IsDown())
|
|
{
|
|
m_bExitButtonUp = true;
|
|
}
|
|
|
|
// Check if we need to re-add convertible roof animation task (it might get blatted by, e.g. the bring vehicle to halt task).
|
|
if(pVehicle->DoesVehicleHaveAConvertibleRoofAnimation())
|
|
{
|
|
aiTask *pTask = pVehicle->GetIntelligence()->GetTaskManager()->GetTree(VEHICLE_TASK_TREE_SECONDARY)->GetActiveTask();
|
|
if(!pTask)
|
|
{
|
|
CTaskVehicleConvertibleRoof *convertibleRoof= rage_new CTaskVehicleConvertibleRoof(false,pVehicle->m_nVehicleFlags.bRoofLowered);
|
|
pVehicle->GetIntelligence()->GetTaskManager()->GetTree(VEHICLE_TASK_TREE_SECONDARY)->SetTask(convertibleRoof, VEHICLE_TASK_SECONDARY_ANIM);
|
|
}
|
|
}
|
|
if( pVehicle->DoesVehicleHaveATransformAnimation() )
|
|
{
|
|
aiTask *pTask = pVehicle->GetIntelligence()->GetTaskManager()->GetTree( VEHICLE_TASK_TREE_SECONDARY )->GetActiveTask();
|
|
if( !pTask )
|
|
{
|
|
CTaskVehicleTransformToSubmarine *transformToSubmarine= rage_new CTaskVehicleTransformToSubmarine();
|
|
pVehicle->GetIntelligence()->GetTaskManager()->GetTree(VEHICLE_TASK_TREE_SECONDARY)->SetTask( transformToSubmarine, VEHICLE_TASK_SECONDARY_ANIM );
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bHasDriver == false)//if theres no driver goto the no driver state
|
|
SetState(State_NoDriver);
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CTaskVehiclePlayerDrive::IsThePlayerControlInactive(CControl *pControl)
|
|
{
|
|
TUNE_GROUP_BOOL( VEHICLE_PLAYER_DRIVE, playerDrive_forceDisabledControl, false);//used for testing disabled controls
|
|
|
|
// If the player has no control (cut scene for instance) we will hit the brakes.
|
|
if ((!CPauseMenu::IsActive()&&!CLiveManager::IsSystemUiShowing()) || !NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (CControlMgr::IsDisabledControl(pControl) || playerDrive_forceDisabledControl)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDrive::NoDriver_OnEnter (CVehicle* UNUSED_PARAM(pVehicle))
|
|
{
|
|
SetNewTask(rage_new CTaskVehicleNoDriver());
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDrive::NoDriver_OnUpdate (CVehicle* UNUSED_PARAM(pVehicle))
|
|
{
|
|
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_NO_DRIVER))
|
|
{
|
|
return FSM_Quit;
|
|
}
|
|
return FSM_Continue;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// shared for cars and motorbikes
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CTaskVehiclePlayerDrive::ProcessPlayerControlInputsForBrakeAndGasPedal(CControl* pControl, bool bForceBrake, CVehicle *pVehicle)
|
|
{
|
|
//get the vehicles reference frame velocity, so if they are driving on a plane we car work out the relative velocity rather then just using the absolute
|
|
|
|
// Forwardness between -1 and 1
|
|
float fSpeed = DotProduct(pVehicle->GetVelocityIncludingReferenceFrame(), VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetB()));
|
|
ioValue::ReadOptions options = ioValue::DEFAULT_OPTIONS;
|
|
if(CPauseMenu::sm_uLastResumeTime + CPauseMenu::sm_uDisableInputDuration >= fwTimer::GetTimeInMilliseconds())
|
|
{
|
|
options.SetFlags(ioValue::ReadOptions::F_READ_SUSTAINED, true);
|
|
}
|
|
float fAccelButton = pControl->GetVehicleAccelerate().GetNorm01(options);
|
|
float fBrakeButton = pControl->GetVehicleBrake().GetNorm01(options);
|
|
|
|
if( pVehicle->m_bInvertControls )
|
|
{
|
|
float temp = fAccelButton;
|
|
fAccelButton = fBrakeButton;
|
|
fBrakeButton = temp;
|
|
}
|
|
|
|
float fForwardness = fAccelButton - fBrakeButton;
|
|
|
|
if( pVehicle->pHandling->hFlags & HF_HAS_KERS )
|
|
{
|
|
bool bKERS = pControl->GetVehicleKERS().IsPressed();
|
|
if( bKERS )
|
|
{
|
|
static u32 sMinTimeToToggleKERS = 500;
|
|
if( !pVehicle->pHandling->GetCarHandlingData() ||
|
|
!( pVehicle->pHandling->GetCarHandlingData()->aFlags & CF_USE_DOWNFORCE_BIAS ) ||
|
|
pVehicle->GetKERSActive() ||
|
|
fwTimer::GetTimeInMilliseconds() > ( pVehicle->m_kersToggledTime + sMinTimeToToggleKERS ) )
|
|
{
|
|
pVehicle->ToggleKERS();
|
|
pVehicle->m_kersToggledTime = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bReverseBrakeAndThrottleForSecondEngine = false;
|
|
|
|
if( pVehicle->InheritsFromAmphibiousQuadBike() &&
|
|
CPhysics::ms_bInStuntMode &&
|
|
pVehicle->IsInAir() )
|
|
{
|
|
if( fForwardness > 0.0f &&
|
|
pVehicle->GetThrottle() > 0.0f )
|
|
{
|
|
fSpeed = Abs( fSpeed );
|
|
}
|
|
}
|
|
|
|
if( pVehicle->GetAreOutriggersBeingDeployed() )
|
|
{
|
|
bForceBrake = true;
|
|
}
|
|
|
|
if(bForceBrake)
|
|
{
|
|
pVehicle->SetBrake(1.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
else if(!pVehicle->m_nVehicleFlags.bEngineOn && pVehicle->m_nVehicleFlags.bIsDrowning)
|
|
{
|
|
pVehicle->SetBrake(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
else if(pVehicle->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_PuttingOnHelmet)
|
|
/*pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_PUT_ON_HELMET)*/)
|
|
{
|
|
pVehicle->SetBrake(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
else if( pVehicle->GetSpecialFlightModeRatio() == 1.0f &&
|
|
!pVehicle->HasGlider() )
|
|
{
|
|
pVehicle->SetThrottle( fForwardness );
|
|
pVehicle->SetBrake( 0.0f );
|
|
}
|
|
// We've stopped. Go where we want to go.
|
|
else if (rage::Abs(fSpeed) < 0.5f)
|
|
{
|
|
// if both brake and throttle are held down, do a burn out
|
|
if(fAccelButton > 0.6f && fBrakeButton > 0.6f)
|
|
{
|
|
pVehicle->SetThrottle(fAccelButton);
|
|
pVehicle->SetBrake(fBrakeButton);
|
|
}
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( pVehicle->GetSpeedUpBoostDuration() > 0.0f &&
|
|
( !pVehicle->InheritsFromAmphibiousQuadBike() ||
|
|
static_cast< CAmphibiousQuadBike* >( pVehicle )->IsWheelsFullyOut() ||
|
|
static_cast< CAmphibiousQuadBike* >( pVehicle )->IsPropellerSubmerged() ) )
|
|
{
|
|
fForwardness = 1.0f;
|
|
}
|
|
|
|
// if both brake and throttle are held down, do a burn out
|
|
if(fAccelButton > 0.6f && fBrakeButton > 0.6f)
|
|
{
|
|
pVehicle->SetThrottle(fAccelButton);
|
|
pVehicle->SetBrake(fBrakeButton);
|
|
}
|
|
// going forwards
|
|
else if (fSpeed >= 0.0f)
|
|
{
|
|
// we want to go forwards so go faster
|
|
if (fForwardness >= 0.0f)
|
|
{
|
|
if( pVehicle->GetSlowDownBoostDuration() <= 0.0f )
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
}
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
// we don't want to go forwards - so brake
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(0.0f);
|
|
pVehicle->SetBrake(-fForwardness);
|
|
}
|
|
}
|
|
// going backwards
|
|
else
|
|
{
|
|
// want to go forwards
|
|
if (fForwardness >= 0.0f)
|
|
{
|
|
// let us sit on the gas pedal if we're trying to get up a slope but sliding back
|
|
if(pVehicle->GetThrottle() > 0.5f && fSpeed > -10.0f)
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
// oh dear we're sliding back too fast, go for the brakes
|
|
else
|
|
{
|
|
bReverseBrakeAndThrottleForSecondEngine = true;
|
|
|
|
pVehicle->SetThrottle(0.0f);
|
|
pVehicle->SetBrake(fForwardness);
|
|
}
|
|
}
|
|
// we want to go backwards, so apply the gas
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( pVehicle->InheritsFromAmphibiousAutomobile() )
|
|
{
|
|
static_cast<CAmphibiousAutomobile*>(pVehicle)->SetReverseBrakeAndThrottle(bReverseBrakeAndThrottleForSecondEngine);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
#if !__FINAL
|
|
const char * CTaskVehiclePlayerDrive::GetStaticStateName( s32 iState )
|
|
{
|
|
Assert(iState>=State_Drive&&iState<=State_NoDriver);
|
|
static const char* aStateNames[] =
|
|
{
|
|
"State_Drive",
|
|
"State_NoDriver"
|
|
};
|
|
|
|
return aStateNames[iState];
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveAutomobile
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveAutomobile::CTaskVehiclePlayerDriveAutomobile() :
|
|
CTaskVehiclePlayerDrive(),
|
|
m_JustEnteredDiggerMode(false),
|
|
m_JustLeftDiggerMode(false)
|
|
#if RSG_PC
|
|
,m_MouseSteeringInput(false)
|
|
#endif
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_AUTOMOBILE);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveAutomobile::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
|
|
|
|
//player is always dirty
|
|
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
|
|
|
|
FSM_Begin
|
|
//
|
|
FSM_State(State_Drive)
|
|
FSM_OnEnter
|
|
Drive_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return Drive_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_NoDriver)
|
|
FSM_OnEnter
|
|
NoDriver_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return NoDriver_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_DiggerMode)
|
|
FSM_OnEnter
|
|
DiggerMode_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return DiggerMode_OnUpdate(pVehicle);
|
|
FSM_End
|
|
}
|
|
|
|
u32 ms_uTimeToHoldTruckDetachButtonDown = 250;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CTaskVehiclePlayerDriveAutomobile::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
Assert(pPlayerPed && (pPlayerPed->IsControlledByLocalPlayer()));
|
|
|
|
if(pControl==NULL)
|
|
return;
|
|
|
|
// Process whether we should enable Franklin's special ability.
|
|
CPlayerSpecialAbilityManager::ProcessControl(pPlayerPed);
|
|
|
|
bool bForceBrake = false;
|
|
if( pControl->GetVehicleExit().IsDown() && m_bExitButtonUp )
|
|
{
|
|
if( !pVehicle->CanPedJumpOutCar(pPlayerPed) )
|
|
{
|
|
pVehicle->SetHandBrake(true);
|
|
bForceBrake = true;
|
|
if (pVehicle->GetVelocityIncludingReferenceFrame().Mag2() < (0.2f*0.2f)) // If we're already static we don't want the brake light to light up. (causes ugly glitch)
|
|
{
|
|
pVehicle->m_nVehicleFlags.bSuppressBrakeLight = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pVehicle->SetHandBrake(pVehicle->GetIsHandBrakePressed(pControl));
|
|
}
|
|
|
|
if (!pVehicle->m_nVehicleFlags.bEngineOn && !pVehicle->m_nVehicleFlags.bEngineStarting && pVehicle->m_Transmission.GetEngineHealth() > 0.0f
|
|
&& pVehicle->GetVelocityIncludingReferenceFrame().Mag2() < 3.0f*3.0f)
|
|
{
|
|
bForceBrake = true;
|
|
}
|
|
|
|
float fFwdSpeedFrac = DotProduct(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetB()), pVehicle->GetVelocityIncludingReferenceFrame()) / ms_fCAR_STEER_SMOOTH_RATE_MAXSPEED;
|
|
float fStoppedSteerSmoothRate = pVehicle->GetVehicleType() == VEHICLE_TYPE_QUADBIKE || pVehicle->GetVehicleType() == VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE ? ms_fCAR_STEER_SMOOTH_RATE_STOPPED_QUAD : ms_fCAR_STEER_SMOOTH_RATE_STOPPED;
|
|
float fMovingSteerSmoothRate = pVehicle->GetVehicleType() == VEHICLE_TYPE_QUADBIKE || pVehicle->GetVehicleType() == VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE ? ms_fCAR_STEER_SMOOTH_RATE_ATSPEED_QUAD : ms_fCAR_STEER_SMOOTH_RATE_ATSPEED;
|
|
|
|
float timeStep = fwTimer::GetTimeStep();
|
|
// If we are using Franklin's special ability we shouldn't slow down the steering.
|
|
if( pPlayerPed->GetSpecialAbility() && pPlayerPed->GetSpecialAbility()->GetType() == SAT_CAR_SLOWDOWN )
|
|
{
|
|
if(pPlayerPed->GetSpecialAbility()->ShouldApplyFx())
|
|
{
|
|
float fFxStrength = pPlayerPed->GetSpecialAbility()->GetFxStrength();
|
|
timeStep = Lerp(fFxStrength, timeStep, fwTimer::GetNonPausableCamTimeStep() * sfSpecialAbilitySteeringTimeStepMult);// We want to use a time step for the steering which can't be modified by scaling time
|
|
|
|
fMovingSteerSmoothRate = Lerp(fFxStrength, fMovingSteerSmoothRate, ms_fCAR_STEER_SMOOTH_RATE_ATSPEED_SPECIAL);
|
|
fStoppedSteerSmoothRate = Lerp(fFxStrength, fStoppedSteerSmoothRate, ms_fCAR_STEER_SMOOTH_RATE_STOPPED_SPECIAL);
|
|
}
|
|
}
|
|
|
|
float fSmoothFrac = fStoppedSteerSmoothRate;
|
|
if(fFwdSpeedFrac > 1.0f)
|
|
fSmoothFrac = fMovingSteerSmoothRate;
|
|
else if(fFwdSpeedFrac > 0.0f)
|
|
fSmoothFrac = fFwdSpeedFrac*fMovingSteerSmoothRate + (1.0f - fFwdSpeedFrac)*fStoppedSteerSmoothRate;
|
|
|
|
//Slow down steering on rear wheel steer vehicles.
|
|
if(pVehicle->pHandling->hFlags &HF_STEER_REARWHEELS)
|
|
{
|
|
static dev_float sfRearWheelSteerMult = 0.2f;
|
|
fSmoothFrac *= sfRearWheelSteerMult;
|
|
}
|
|
|
|
if (NetworkInterface::IsInCopsAndCrooks())
|
|
{
|
|
// for C&C, the Nitro special ability affects steering while active
|
|
if (pPlayerPed->GetSpecialAbilityType(PSAS_SECONDARY) == SAT_NITRO_COP ||
|
|
pPlayerPed->GetSpecialAbilityType(PSAS_SECONDARY) == SAT_NITRO_CROOK)
|
|
{
|
|
const CPlayerSpecialAbility* pPlayerSpecialAbility = pPlayerPed->GetSpecialAbility(PSAS_SECONDARY);
|
|
if (pPlayerSpecialAbility && pPlayerSpecialAbility->IsActive())
|
|
{
|
|
// get the modifier from the abilities and apply it here
|
|
fSmoothFrac *= pPlayerSpecialAbility->GetSteeringMultiplier();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bCheckSteeringExclusive = true;
|
|
#if KEYBOARD_MOUSE_SUPPORT
|
|
if(pControl->WasKeyboardMouseLastKnownSource() && (pVehicle->GetModelIndex() == MI_TANK_RHINO || (MI_TANK_KHANJALI.IsValid() && pVehicle->GetModelIndex() == MI_TANK_KHANJALI)))
|
|
{
|
|
bCheckSteeringExclusive = false;
|
|
}
|
|
#endif // KEYBOARD_MOUSE_SUPPORT
|
|
|
|
if (bCheckSteeringExclusive)
|
|
{
|
|
pControl->SetVehicleSteeringExclusive();
|
|
}
|
|
float fDesiredSteerInput = -pControl->GetVehicleSteeringLeftRight().GetNorm();
|
|
|
|
if( pVehicle->m_bInvertControls )
|
|
{
|
|
fDesiredSteerInput = -fDesiredSteerInput;
|
|
}
|
|
|
|
#if RSG_PC
|
|
float fLowriderDesiredUpDownInput = pControl->GetVehicleSteeringUpDown().GetNorm(); // For lowrider up/down bounce input.
|
|
|
|
if(pControl->WasKeyboardMouseLastKnownSource())
|
|
{
|
|
if(pControl->GetVehicleSteeringLeftRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
// Undo fixup applied for scaled axis, without affecting scripted minigame inputs.
|
|
fDesiredSteerInput *= c_fMouseAdjustmentScale;
|
|
fLowriderDesiredUpDownInput *= c_fMouseAdjustmentScale;
|
|
m_MouseSteeringInput = true;
|
|
}
|
|
else if(pControl->GetVehicleSteeringLeftRight().GetSource().m_Device == IOMS_MKB_AXIS)// Keyboard steering input
|
|
{
|
|
m_MouseSteeringInput = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_MouseSteeringInput = false;
|
|
}
|
|
|
|
bool bMouseSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_DRIVE) == CControl::eMSM_Vehicle;
|
|
bool bCameraMouseSteering = CControl::GetMouseSteeringMode(PREF_MOUSE_DRIVE) == CControl::eMSM_Camera;
|
|
|
|
if(m_MouseSteeringInput && ((bMouseSteering && !CControlMgr::GetMainPlayerControl().GetDriveCameraToggleOn()) || (bCameraMouseSteering && CControlMgr::GetMainPlayerControl().GetDriveCameraToggleOn())))
|
|
{
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, CAR_STEERING_DEADZONE_CENTERING_SPEED, 2.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, CAR_STEERING_MULTIPLIER, 3.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, CAR_STEERING_CENTER_ALLOWANCE, 0.001f, 0.0f, 1.0f, 0.0001f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, CAR_STEERING_INPUT_DEADZONE, FLT_EPSILON, 0.0f, 1.0f, 0.0001f);
|
|
|
|
float fAutoCenterMult = CPauseMenu::GetMenuPreference(PREF_MOUSE_AUTOCENTER_CAR)/10.0f;
|
|
|
|
if (fabs(fDesiredSteerInput) <= CAR_STEERING_INPUT_DEADZONE)
|
|
{
|
|
if(pVehicle->m_fSteerInput > 0.0f)
|
|
{
|
|
fDesiredSteerInput = Clamp(pVehicle->m_fSteerInput - (CAR_STEERING_DEADZONE_CENTERING_SPEED * fAutoCenterMult) * timeStep, 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fDesiredSteerInput = Clamp(pVehicle->m_fSteerInput + (CAR_STEERING_DEADZONE_CENTERING_SPEED * fAutoCenterMult) * timeStep,-1.0f, 0.0f);
|
|
}
|
|
}
|
|
else if( Sign(fDesiredSteerInput) == Sign(pVehicle->m_fSteerInput) || rage::Abs(pVehicle->m_fSteerInput) <= CAR_STEERING_CENTER_ALLOWANCE)
|
|
{
|
|
fDesiredSteerInput = pVehicle->m_fSteerInput + (fDesiredSteerInput * CAR_STEERING_MULTIPLIER);
|
|
}
|
|
|
|
if( Sign(fLowriderDesiredUpDownInput) == Sign(m_fLowriderUpDownInput))
|
|
{
|
|
fLowriderDesiredUpDownInput = m_fLowriderUpDownInput + (fLowriderDesiredUpDownInput * CAR_STEERING_MULTIPLIER);
|
|
}
|
|
}
|
|
|
|
#else
|
|
float fCarSteeringCurvePow = ms_fCAR_STEERING_CURVE_POW;
|
|
fDesiredSteerInput = Sign(fDesiredSteerInput) * Powf(Abs(fDesiredSteerInput),fCarSteeringCurvePow);
|
|
#endif
|
|
|
|
|
|
|
|
// ORBIS has more fidelity in stick input now, so adjust the inputs to be more aggressive.
|
|
#if RSG_ORBIS
|
|
TUNE_FLOAT(STEER_ORBIS_MULT, 1.0f, 0.0f, 2.0f, 0.01f);
|
|
|
|
fDesiredSteerInput *= STEER_ORBIS_MULT;
|
|
|
|
fDesiredSteerInput = Clamp( fDesiredSteerInput, -1.0f, 1.0f);
|
|
#endif
|
|
|
|
#if RSG_PC
|
|
bool isWheelDevice = pControl->GetVehicleSteeringLeftRight().GetSource().m_Device == IOMS_JOYSTICK_AXIS &&
|
|
ioJoystick::GetStick(pControl->GetVehicleSteeringLeftRight().GetSource().m_DeviceIndex).IsWheel();
|
|
|
|
// If its a steering wheel the use the raw value as this gives us the angle of the steering wheel.
|
|
if(isWheelDevice)
|
|
{
|
|
fDesiredSteerInput = -pControl->GetVehicleSteeringLeftRight().GetNorm(ioValue::NO_DEAD_ZONE);
|
|
}
|
|
#endif // RSG_PC
|
|
|
|
// The player can't steer whilst hotwiring the car
|
|
CTaskMotionInAutomobile* pTask = static_cast<CTaskMotionInAutomobile*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveMotionByType(CTaskTypes::TASK_MOTION_IN_AUTOMOBILE) );
|
|
if(((pTask && pTask->IsHotwiringVehicle()) || pVehicle->m_nVehicleFlags.bCarNeedsToBeHotwired) && !pVehicle->m_nVehicleFlags.bEngineOn )
|
|
{
|
|
bForceBrake = true;
|
|
fDesiredSteerInput = 0.0f;
|
|
#if RSG_PC
|
|
fLowriderDesiredUpDownInput = 0.0f;
|
|
#endif
|
|
}
|
|
|
|
// Or when putting on helmet
|
|
if(pVehicle->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_PuttingOnHelmet))
|
|
{
|
|
fDesiredSteerInput = 0.0f;
|
|
#if RSG_PC
|
|
fLowriderDesiredUpDownInput = 0.0f;
|
|
#endif
|
|
}
|
|
|
|
// Or while being affected by an EMP
|
|
if (pVehicle->GetExplosionEffectEMP())
|
|
{
|
|
TUNE_GROUP_BOOL(VEHICLE_EXPLOSION_EFFECTS, EMP_EFFECT_SHOULD_APPLY_BRAKE, false);
|
|
if (EMP_EFFECT_SHOULD_APPLY_BRAKE)
|
|
{
|
|
bForceBrake = true;
|
|
}
|
|
fDesiredSteerInput = 0.0f;
|
|
#if RSG_PC
|
|
fLowriderDesiredUpDownInput = 0.0f;
|
|
#endif
|
|
}
|
|
|
|
#if RSG_PC
|
|
if(isWheelDevice)
|
|
{
|
|
// If its a steering wheel device we set the value directly as the device is already dt based (like a mouse) and it reduces steering lag which makes
|
|
// wheel more responsive.
|
|
pVehicle->m_fSteerInput = fDesiredSteerInput;
|
|
}
|
|
else
|
|
#endif // RSG_PC
|
|
{
|
|
pVehicle->m_fSteerInput = pVehicle->m_fSteerInput + (fDesiredSteerInput - pVehicle->m_fSteerInput) * fSmoothFrac * timeStep;
|
|
}
|
|
|
|
pVehicle->m_fSteerInput += pVehicle->m_fSteerInputBias;
|
|
pVehicle->m_fSteerInput = Clamp(pVehicle->m_fSteerInput, -1.0f, 1.0f);
|
|
float fSteerAngle = pVehicle->m_fSteerInput * pVehicle->pHandling->m_fSteeringLock;
|
|
|
|
#if __BANK
|
|
TUNE_GROUP_BOOL( VEHICLE_DEBUG_STEER, ENABLE_STEER_TO_CAMERA, PARAM_steerToCamera.Get() );
|
|
TUNE_GROUP_FLOAT( VEHICLE_DEBUG_STEER, STEER_RATE_SCALE, 2.0f, 0.1f, 100.0f, 0.1f );
|
|
|
|
if( ENABLE_STEER_TO_CAMERA )
|
|
{
|
|
float angleToCamForward = -camInterface::GetFront().Dot( VEC3V_TO_VECTOR3( pVehicle->GetTransform().GetRight() ) );
|
|
|
|
if( pVehicle->GetVelocity().Dot( VEC3V_TO_VECTOR3( pVehicle->GetTransform().GetForward() ) ) < 0.0f )
|
|
{
|
|
angleToCamForward *= -1.0f;
|
|
}
|
|
|
|
fSteerAngle = angleToCamForward * STEER_RATE_SCALE;
|
|
fSteerAngle = Clamp( fSteerAngle, -pVehicle->pHandling->m_fSteeringLock, pVehicle->pHandling->m_fSteeringLock );
|
|
|
|
camFollowVehicleCamera::ms_ShouldDisablePullAround = true;
|
|
}
|
|
#endif // #if __BANK
|
|
|
|
// moved this code down to vehicle because it's shared with motorbikes
|
|
ProcessPlayerControlInputsForBrakeAndGasPedal(pControl, bForceBrake, pVehicle);
|
|
|
|
#if __DEV
|
|
static dev_bool USE_THROTTLE_LIMITING_FOR_PLAYER = false;
|
|
if( USE_THROTTLE_LIMITING_FOR_PLAYER )
|
|
{
|
|
static dev_float ENGINE_INCREASE = 1.0f;
|
|
pVehicle->SetCheatPowerIncrease(ENGINE_INCREASE);
|
|
float fMaxThrottle = CTaskVehicleGoToPointAutomobile::CalculateMaximumThrottleBasedOnTraction(pVehicle);
|
|
pVehicle->SetThrottle(rage::Min(pVehicle->GetThrottle(), fMaxThrottle));
|
|
|
|
// If we're slipping at all, remove any power increase
|
|
if( pVehicle->GetCheatPowerIncrease() > 1.0f )
|
|
{
|
|
if( fMaxThrottle < 1.0f )
|
|
{
|
|
pVehicle->SetCheatPowerIncrease(Lerp(fMaxThrottle, 1.0f, pVehicle->GetCheatPowerIncrease()));
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
//Stationary auto-centre damping
|
|
if(IsClose(fDesiredSteerInput, 0.0f, FLT_EPSILON) && rage::Abs(fFwdSpeedFrac) < 0.01f)
|
|
{
|
|
if(bForceBrake && pVehicle->GetStatus()!=STATUS_WRECKED)
|
|
{
|
|
fSteerAngle = pVehicle->GetSteerAngle();
|
|
}
|
|
float fDesiredChange = rage::Clamp(fSteerAngle - pVehicle->GetSteerAngle(), -ms_fCAR_STEER_STATIONARY_AUTOCENTRE_MAX, ms_fCAR_STEER_STATIONARY_AUTOCENTRE_MAX);
|
|
fSteerAngle = fDesiredChange + pVehicle->GetSteerAngle();
|
|
}
|
|
|
|
pVehicle->SetSteerAngle(fSteerAngle);
|
|
|
|
if(pVehicle->pHandling->hFlags &HF_HANDBRAKE_REARWHEELSTEER)
|
|
{
|
|
float fDesired2ndSteerAngle = 0.0f;
|
|
|
|
if(pVehicle->GetHandBrake())
|
|
fDesired2ndSteerAngle = pVehicle->GetSteerAngle();
|
|
|
|
// Progress towards the steer angle
|
|
float fSteerDiff = rage::Clamp(fDesired2ndSteerAngle - pVehicle->GetSecondSteerAngle(),-fSmoothFrac*fwTimer::GetTimeStep(),fSmoothFrac*fwTimer::GetTimeStep());
|
|
pVehicle->SetSecondSteerAngle(pVehicle->GetSecondSteerAngle() + fSteerDiff);
|
|
}
|
|
|
|
//Update hydraulic suspension control
|
|
pVehicle->m_nVehicleFlags.bPlayerModifiedHydraulics = false;
|
|
if(pVehicle->InheritsFromAutomobile() && static_cast<CAutomobile*>(pVehicle)->HasHydraulicSuspension() &&
|
|
pVehicle->m_nVehicleFlags.bEngineOn )
|
|
{
|
|
// Don't allow hydraulic modification if doing a driveby or somebody is entering/exiting/jacking the vehicle.
|
|
if (!pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsDoingDriveby) && !pVehicle->m_bBlockDueToEnterExit)
|
|
{
|
|
float fXAxis = ((-pControl->GetVehicleHydraulicsControlLeft().GetNorm()) + pControl->GetVehicleHydraulicsControlRight().GetNorm());
|
|
float fYAxis = ((-pControl->GetVehicleHydraulicsControlUp().GetNorm()) + pControl->GetVehicleHydraulicsControlDown().GetNorm());
|
|
float fStickX = -fXAxis;
|
|
float fStickY = fYAxis;
|
|
|
|
#if RSG_PC
|
|
m_fLowriderUpDownInput = m_fLowriderUpDownInput + (fLowriderDesiredUpDownInput - m_fLowriderUpDownInput) * fSmoothFrac * timeStep;
|
|
m_fLowriderUpDownInput += pVehicle->m_fSteerInputBias;
|
|
m_fLowriderUpDownInput = Clamp(m_fLowriderUpDownInput, -1.0f, 1.0f);
|
|
|
|
// PC mouse/keyboard: Use mouse input to control lowrider if using mouse steering
|
|
if (pControl->WasKeyboardMouseLastKnownSource())
|
|
{
|
|
if (m_MouseSteeringInput && (CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Vehicle || CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Camera))
|
|
{
|
|
// Set stick X/Y based on cached input values.
|
|
// Using raw mouse input wasn't working as it was returning to 0.0f when not moving it, meaning we couldn't lock the suspension properly.
|
|
fStickX += pVehicle->m_fSteerInput;
|
|
fStickY += m_fLowriderUpDownInput;
|
|
}
|
|
}
|
|
|
|
fStickX = Clamp (fStickX, -1.0f, 1.0f);
|
|
fStickY = Clamp (fStickY, -1.0f, 1.0f);
|
|
#endif
|
|
|
|
ProcessHydraulics( pControl, pVehicle, fStickX, fStickY );
|
|
}
|
|
}
|
|
|
|
if( pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_DROP_SUSPENSION_WHEN_STOPPED ) &&
|
|
pVehicle->m_nVehicleFlags.bEngineOn )
|
|
{
|
|
ProcessSuspensionDrop( pVehicle );
|
|
}
|
|
|
|
for(int i = 0; i < pVehicle->GetNumberOfVehicleGadgets(); i++)
|
|
{
|
|
CVehicleGadget *pVehicleGadget = pVehicle->GetVehicleGadget(i);
|
|
|
|
// Make handBrake2 (A on 360, X on ps3) toggle detaching the trailer
|
|
if(pPlayerPed->GetPedConfigFlag(CPED_CONFIG_FLAG_AllowedToDetachTrailer))
|
|
{
|
|
if(pVehicleGadget->GetType() == VGT_TRAILER_ATTACH_POINT)
|
|
{
|
|
CVehicleTrailerAttachPoint *pTrailerAttachPoint = static_cast<CVehicleTrailerAttachPoint*>(pVehicleGadget);
|
|
|
|
if(pControl->GetVehicleHeadlight().IsDown() && pControl->GetVehicleHeadlight().HistoryHeldDown(ms_uTimeToHoldTruckDetachButtonDown))
|
|
{
|
|
pTrailerAttachPoint->DetachTrailer(pVehicle);
|
|
}
|
|
}
|
|
}
|
|
|
|
float fDesiredSteerInputUpDown = pControl->GetVehicleSteeringUpDown().GetNorm();
|
|
#if RSG_PC
|
|
if(pControl->GetVehicleSteeringUpDown().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
fDesiredSteerInputUpDown *= c_fMouseAdjustmentScale;
|
|
}
|
|
#endif
|
|
|
|
if(pVehicleGadget->GetType() == VGT_DIGGER_ARM && pVehicle->m_nVehicleFlags.bEngineOn)
|
|
{
|
|
CVehicleGadgetDiggerArm *pDiggerArm = static_cast<CVehicleGadgetDiggerArm*>(pVehicleGadget);
|
|
pDiggerArm->SetDesiredAcceleration( -fDesiredSteerInputUpDown );
|
|
}
|
|
|
|
if(pVehicleGadget->GetType() == VGT_TOW_TRUCK_ARM && pVehicle->m_nVehicleFlags.bEngineOn)
|
|
{
|
|
CVehicleGadgetTowArm *pTowArm = static_cast<CVehicleGadgetTowArm*>(pVehicleGadget);
|
|
pTowArm->SetDesiredAcceleration(pVehicle, fDesiredSteerInputUpDown );
|
|
|
|
//check whether the player wants to detach the vehicle
|
|
if(pControl->GetVehicleHeadlight().IsDown() && pControl->GetVehicleHeadlight().HistoryHeldDown(ms_uTimeToHoldTruckDetachButtonDown))
|
|
{
|
|
const CVehicle *pAttachedVehicle = pTowArm->GetAttachedVehicle();
|
|
|
|
if(pAttachedVehicle && pAttachedVehicle->IsNetworkClone())
|
|
{
|
|
// send an event to the owner of the attached entity requesting they detach
|
|
CRequestDetachmentEvent::Trigger(*pAttachedVehicle);
|
|
}
|
|
else
|
|
{
|
|
vehicleDisplayf( "[TOWTRUCK ROPE DEBUG] CTaskVehiclePlayerDriveAutomobile::ProcessDriverInputsForPlayerOnUpdate - Detaching entity." );
|
|
pTowArm->DetachVehicle( );
|
|
}
|
|
}
|
|
}
|
|
|
|
//do we have a forklift
|
|
if(pVehicleGadget->GetType() == VGT_FORKS && pVehicle->m_nVehicleFlags.bEngineOn)
|
|
{
|
|
CVehicleGadgetForks *pForks = static_cast<CVehicleGadgetForks*>(pVehicleGadget);
|
|
|
|
// Allow the forks to be jammed during processing of the forks gadget.
|
|
if(!pForks->CanMoveForksDown() || pForks->AreForksLocked())
|
|
fDesiredSteerInputUpDown = rage::Min(fDesiredSteerInputUpDown, 0.0f);
|
|
if(!pForks->CanMoveForksUp() || pForks->AreForksLocked())
|
|
fDesiredSteerInputUpDown = rage::Max(fDesiredSteerInputUpDown, 0.0f);
|
|
|
|
pForks->SetDesiredAcceleration( -fDesiredSteerInputUpDown );
|
|
|
|
// We require script to call the command to lock the forks every frame. Reset the flag here.
|
|
pForks->UnlockForks();
|
|
}
|
|
|
|
if(pVehicleGadget->GetType() == VGT_HANDLER_FRAME)
|
|
{
|
|
CVehicleGadgetHandlerFrame* pFrame= static_cast<CVehicleGadgetHandlerFrame*>(pVehicleGadget);
|
|
|
|
BANK_ONLY(pFrame->DebugFrameMotion(pVehicle));
|
|
|
|
// Allow the frame to be jammed during processing of the frame gadget.
|
|
if(fDesiredSteerInputUpDown > 0.0f)
|
|
pFrame->SetTryingToMoveFrameDown(true);
|
|
else
|
|
pFrame->SetTryingToMoveFrameDown(false);
|
|
if(!pFrame->CanMoveFrameDown() || pFrame->IsFrameLocked())
|
|
fDesiredSteerInputUpDown = rage::Min(fDesiredSteerInputUpDown, 0.0f);
|
|
if(!pFrame->CanMoveFrameUp() || pFrame->IsFrameLocked())
|
|
fDesiredSteerInputUpDown = rage::Max(fDesiredSteerInputUpDown, 0.0f);
|
|
|
|
pFrame->SetDesiredAcceleration( -fDesiredSteerInputUpDown );
|
|
|
|
// Check whether the player wants to detach the container (INPUT_CONTEXT is used for attaching/detatching, straingly attatching is done in script).
|
|
if(pControl->GetPedContextAction().IsPressed() && pFrame->IsContainerAttached())
|
|
{
|
|
pFrame->DetachContainerFromFrame();
|
|
}
|
|
|
|
// We require script to call the command to lock the frame every frame. Reset the flag here.
|
|
pFrame->UnlockFrame();
|
|
}
|
|
|
|
if(pVehicleGadget->GetType() == VGT_ARTICULATED_DIGGER_ARM && pVehicle->m_nVehicleFlags.bEngineOn)
|
|
{
|
|
CVehicleGadgetArticulatedDiggerArm* pDiggerArm = static_cast<CVehicleGadgetArticulatedDiggerArm*>(pVehicleGadget);
|
|
|
|
switch(pDiggerArm->GetNumberOfSetupDiggerJoint())
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
{
|
|
if(pDiggerArm->GetJointSetup(DIGGER_JOINT_STICK))
|
|
{
|
|
pDiggerArm->SetDesiredAcceleration(DIGGER_JOINT_STICK, fDesiredSteerInputUpDown );
|
|
}
|
|
else if(pDiggerArm->GetJointSetup(DIGGER_JOINT_BOOM))
|
|
{
|
|
pDiggerArm->SetDesiredAcceleration(DIGGER_JOINT_BOOM, fDesiredSteerInputUpDown );
|
|
}
|
|
break;
|
|
}
|
|
case 3:
|
|
case 4:
|
|
{
|
|
const ioValue &ioVal = pControl->GetVehicleHorn();
|
|
|
|
// if stick was clicked then enter "digger" mode
|
|
if(ioVal.IsReleased() && !m_JustLeftDiggerMode)
|
|
{
|
|
m_JustEnteredDiggerMode = true;
|
|
SetState(State_DiggerMode);
|
|
}
|
|
else
|
|
{
|
|
m_JustLeftDiggerMode = false;
|
|
}
|
|
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveAutomobile::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *pVehicle)
|
|
{
|
|
if (pVehicle->m_nVehicleFlags.bLimitSpeedWhenPlayerInactive )
|
|
{
|
|
if(pVehicle->HasContactWheels() || pVehicle->GetTransform().GetC().GetZf() < 0.0f)
|
|
{
|
|
pVehicle->SetBrake(1.0f);
|
|
pVehicle->SetHandBrake(false);
|
|
pVehicle->SetThrottle(0.0f);
|
|
pVehicle->GetDriver()->GetPlayerInfo()->KeepAreaAroundPlayerClear();
|
|
// Limit the velocity
|
|
float Speed = pVehicle->GetVelocity().Mag();
|
|
|
|
if (Speed > ms_fAUTOMOBILE_MAXSPEEDNOW && pVehicle->m_nVehicleFlags.bStopInstantlyWhenPlayerInactive)
|
|
{
|
|
float NewSpeed = rage::Max(ms_fAUTOMOBILE_MAXSPEEDNOW, Speed - ms_fAUTOMOBILE_MAXSPEEDCHANGE);
|
|
pVehicle->SetVelocity( pVehicle->GetVelocity() * (NewSpeed / Speed) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//State_DiggerMode
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CTaskVehiclePlayerDriveAutomobile::DiggerMode_OnEnter (CVehicle* pVehicle)
|
|
{
|
|
for(int i = 0; i < pVehicle->GetNumberOfVehicleGadgets(); i++)
|
|
{
|
|
CVehicleGadget *pVehicleGadget = pVehicle->GetVehicleGadget(i);
|
|
|
|
if(pVehicleGadget->GetType() == VGT_ARTICULATED_DIGGER_ARM)
|
|
{
|
|
static s32 DIGGERARM_JOINTS_MINIMUM = 3;
|
|
CVehicleGadgetArticulatedDiggerArm* pDiggerArm = static_cast<CVehicleGadgetArticulatedDiggerArm*>(pVehicleGadget);
|
|
if(pDiggerArm->GetNumberOfSetupDiggerJoint() >= DIGGERARM_JOINTS_MINIMUM)//if it has less then 3 digger joints then it doesn't need to go into digger mode.
|
|
{
|
|
SetNewTask(rage_new CTaskVehiclePlayerDriveDiggerArm( pDiggerArm ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveAutomobile::DiggerMode_OnUpdate (CVehicle* pVehicle)
|
|
{
|
|
const bool bHasDriver = pVehicle->GetDriver() != NULL;
|
|
const bool bDriverInjured = pVehicle->GetDriver() && pVehicle->GetDriver()->IsInjured();
|
|
const bool bDriverArrested = pVehicle->GetDriver() && pVehicle->GetDriver()->GetIsArrested();
|
|
const bool bDriverJumpingOut = false; //pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedIntelligence()->GetQueriableInterface()->GetStateForTaskType(CTaskTypes::TASK_EXIT_VEHICLE_EXIT_SEAT) == CTaskExitVehicleExitSeat::State_JumpOutOfSeat ? true : false;
|
|
CControl *pControl = NULL;
|
|
|
|
if(bHasDriver && !bDriverArrested && !bDriverInjured && !bDriverJumpingOut)
|
|
{
|
|
Assert( pVehicle->GetDriver() );
|
|
Assert(pVehicle->GetDriver()->IsPlayer());
|
|
CPed* pPlayerPed = pVehicle->GetDriver();
|
|
|
|
pControl = pPlayerPed->GetControlFromPlayer();
|
|
}
|
|
|
|
|
|
if(pControl)//if we have control check whether we should leave "digger" mode
|
|
{
|
|
const ioValue &ioVal = pControl->GetVehicleHorn();
|
|
// if stick was clicked then quit "digger" mode
|
|
if(ioVal.IsReleased() && !m_JustEnteredDiggerMode)
|
|
{
|
|
m_JustLeftDiggerMode = true;
|
|
SetState(State_Drive);
|
|
}
|
|
else
|
|
{
|
|
m_JustEnteredDiggerMode = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetState(State_Drive);//if anythings failed then just go back to the driving state.
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
static bool sb_invertBounceStick = false;
|
|
|
|
void CTaskVehiclePlayerDriveAutomobile::ProcessHydraulics( CControl* pControl, CVehicle* pVehicle, float fStickX, float fStickY )
|
|
{
|
|
int numWheels = pVehicle->GetNumWheels();
|
|
|
|
if( !ProcessHydraulicsTilt( pControl, pVehicle, fStickX, fStickY ) )
|
|
{
|
|
ProcessHydraulicsStateChange( pControl, pVehicle );
|
|
}
|
|
|
|
CAutomobile *pAutomobile = static_cast<CAutomobile*>(pVehicle);
|
|
bool suspensionUpdated = false;
|
|
bool steeringReduced = false;
|
|
bool donkSuspension = pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_HAS_LOWRIDER_DONK_HYDRAULICS );
|
|
|
|
for(int i = 0; i < numWheels; i++)
|
|
{
|
|
if( CWheel *pWheel = pVehicle->GetWheel( i ) )
|
|
{
|
|
eWheelHydraulicState wheelState = pWheel->GetSuspensionHydraulicState();
|
|
eWheelHydraulicState wheelTargetState = pWheel->GetSuspensionHydraulicTargetState();
|
|
|
|
// Flag if the player has modified the suspension of any of the wheels.
|
|
// Needed for triggering lowrider animations and suspension syncing.
|
|
if (!pVehicle->m_nVehicleFlags.bPlayerModifiedHydraulics && (wheelState != WHS_INVALID) && pWheel->GetSuspensionRaiseAmount() != 0.0f)
|
|
{
|
|
pVehicle->m_nVehicleFlags.bPlayerModifiedHydraulics = true;
|
|
}
|
|
|
|
TUNE_GROUP_FLOAT( HYDRAULICS_TUNE, steeringLockReduction, 0.8f, 0.0f, 1.0f, 0.01f);
|
|
|
|
if( !steeringReduced &&
|
|
pWheel->GetConfigFlags().IsFlagSet(WCF_STEER) &&
|
|
pWheel->GetSuspensionRaiseAmount() < 0.0f )
|
|
{
|
|
float currentLoweredAmount = pWheel->GetSuspensionRaiseAmount();
|
|
currentLoweredAmount /= pAutomobile->m_fHydraulicsLowerBound * pWheel->GetSuspensionLength();
|
|
currentLoweredAmount *= steeringLockReduction;
|
|
|
|
pVehicle->SetSteerAngle( pVehicle->GetSteerAngle() * steeringLockReduction );
|
|
|
|
steeringReduced = true;
|
|
}
|
|
|
|
if( wheelState == WHS_FREE ||
|
|
wheelState == WHS_BOUNCE_ACTIVE ||
|
|
wheelState == WHS_BOUNCE_LANDING ||
|
|
wheelState == WHS_BOUNCE_LANDED ||
|
|
( wheelState !=
|
|
wheelTargetState ) )
|
|
{
|
|
TUNE_GROUP_FLOAT(HYDRAULICS_TUNE, offsetScale, 1.0f, 0.0f, 5.0f, 0.1f );
|
|
static float lockOffsetScale = 0.75f;
|
|
float targetRaiseAmount = 0.0f;
|
|
float raiseRate = 1.0f;
|
|
|
|
if( donkSuspension )
|
|
{
|
|
TUNE_GROUP_FLOAT(HYDRAULICS_TUNE, donkSuspensionRate, 0.1f, 0.0f, 1.0f, 0.01f );
|
|
raiseRate = donkSuspensionRate;
|
|
}
|
|
|
|
Vector3 vOffset = CWheel::GetWheelOffset(pAutomobile->GetVehicleModelInfo(), pWheel->GetHierarchyId());
|
|
vOffset.z = 0.0f;
|
|
vOffset.NormalizeSafe(vOffset);
|
|
|
|
switch( wheelTargetState )
|
|
{
|
|
case WHS_IDLE:
|
|
{
|
|
break;
|
|
}
|
|
case WHS_LOCK:
|
|
{
|
|
targetRaiseAmount = pWheel->GetSuspensionTargetRaiseAmount();
|
|
break;
|
|
}
|
|
case WHS_LOCK_UP:
|
|
{
|
|
// scale this so left/right goes slower than up/down
|
|
float rateScale = 0.75f;
|
|
|
|
if( Abs( vOffset.y ) > Abs( vOffset.x ) &&
|
|
Abs( fStickY ) > Abs( fStickX ) )
|
|
{
|
|
rateScale += lockOffsetScale * Abs( vOffset.y );
|
|
}
|
|
else if( Abs( vOffset.x ) > Abs( vOffset.y ) &&
|
|
Abs( fStickX ) > Abs( fStickY ) )
|
|
{
|
|
rateScale += lockOffsetScale * Abs( vOffset.x );
|
|
}
|
|
raiseRate *= rateScale;
|
|
|
|
// just fall through to use the same raise amount as lock up all
|
|
}
|
|
case WHS_LOCK_UP_ALL:
|
|
case WHS_BOUNCE_ALL_ACTIVE:
|
|
case WHS_BOUNCE_ALL_LANDING:
|
|
{
|
|
targetRaiseAmount = pAutomobile->m_fHydraulicsUpperBound;
|
|
break;
|
|
}
|
|
case WHS_LOCK_DOWN_ALL:
|
|
case WHS_LOCK_DOWN:
|
|
{
|
|
targetRaiseAmount = pAutomobile->m_fHydraulicsLowerBound;
|
|
break;
|
|
}
|
|
case WHS_BOUNCE:
|
|
case WHS_BOUNCE_LEFT_RIGHT:
|
|
{
|
|
targetRaiseAmount = pAutomobile->m_fHydraulicsUpperBound;
|
|
break;
|
|
}
|
|
case WHS_BOUNCE_FRONT_BACK:
|
|
{
|
|
float rateScale = 1.0f;
|
|
|
|
// scale this so left/right goes slower than up/down otherwise we get too much bounce
|
|
if( Abs( vOffset.y ) > Abs( vOffset.x ) )
|
|
{
|
|
rateScale += offsetScale * Abs( vOffset.y );
|
|
}
|
|
|
|
targetRaiseAmount = pAutomobile->m_fHydraulicsUpperBound;
|
|
raiseRate = rateScale;
|
|
break;
|
|
}
|
|
case WHS_BOUNCE_ALL:
|
|
{
|
|
targetRaiseAmount = pAutomobile->m_fHydraulicsUpperBound;
|
|
break;
|
|
}
|
|
case WHS_FREE:
|
|
case WHS_BOUNCE_ACTIVE:
|
|
case WHS_BOUNCE_LANDING:
|
|
case WHS_BOUNCE_LANDED:
|
|
{
|
|
float t = ( ( vOffset.x * fStickX ) / Abs( vOffset.x ) ) +
|
|
( ( vOffset.y * fStickY ) / Abs( vOffset.y ) );
|
|
|
|
float m_fHydraulicsT = t >= 0.0f ? (pAutomobile->m_fHydraulicsUpperBound * t) : (pAutomobile->m_fHydraulicsLowerBound * (-t));
|
|
targetRaiseAmount = m_fHydraulicsT;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
suspensionUpdated = true;
|
|
pWheel->SetSuspensionTargetRaiseAmount( targetRaiseAmount, raiseRate );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( suspensionUpdated )
|
|
{
|
|
pVehicle->ActivatePhysics();
|
|
|
|
pAutomobile->SetTimeHyrdaulicsModified( fwTimer::GetTimeInMilliseconds() );
|
|
pVehicle->GetVehicleAudioEntity()->SetHydraulicSuspensionInputAxis( fStickX, fStickY );
|
|
}
|
|
else// if( targetStateReached )
|
|
{
|
|
pVehicle->GetVehicleAudioEntity()->SetHydraulicSuspensionInputAxis(0.f, 0.f);
|
|
}
|
|
|
|
if( pControl->GetVehicleHydraulicsControlToggle().IsDown() )
|
|
{
|
|
pVehicle->ActivatePhysics();
|
|
pVehicle->SetSteerAngle( 0.0f );
|
|
|
|
if( Abs( pVehicle->GetThrottle() ) < 0.1f &&
|
|
pVehicle->GetVelocity().Mag2() < 5.0f )
|
|
{
|
|
pVehicle->SetHandBrake(true);
|
|
pVehicle->SetBrake( 0.1f );
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
TUNE_GROUP_BOOL( HYDRAULICS_TUNE, sb_DrawHydraulicWheelStates, false );
|
|
|
|
if( sb_DrawHydraulicWheelStates )
|
|
{
|
|
char vehDebugString[ 1024 ];
|
|
|
|
static int StartX = 10;
|
|
static int StartY = 27;
|
|
|
|
int y = StartY;
|
|
|
|
sprintf(vehDebugString,"All Raised: %d", (int)pVehicle->m_nVehicleFlags.bAreHydraulicsAllRaised );
|
|
grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
y++;
|
|
|
|
const char* s_StateNames[] = { "Invalid",
|
|
"Idle",
|
|
"Lock",
|
|
"Lock_Up_All",
|
|
"Lock_Up",
|
|
"Lock_Down_All",
|
|
"Lock_Down",
|
|
"Bounce Left Right",
|
|
"Bounce Front Back",
|
|
"Bounce",
|
|
"Bounce_All",
|
|
"Bounce_Active",
|
|
"Bounce_Landing",
|
|
"Bounce_Landed",
|
|
"Bounce_All_Active",
|
|
"Bounce_All_Landing",
|
|
"Free" };
|
|
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
CWheel* pWheel = pVehicle->GetWheel( i );
|
|
|
|
eWheelHydraulicState wheelState = pWheel->GetSuspensionHydraulicState();
|
|
eWheelHydraulicState wheelTargetState = pWheel->GetSuspensionHydraulicTargetState();
|
|
|
|
sprintf(vehDebugString,"Wheel State: %s", s_StateNames[ (int)wheelState + 1 ] );
|
|
grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
y++;
|
|
|
|
sprintf(vehDebugString,"Wheel Target State: %s", s_StateNames[ (int)wheelTargetState + 1 ] );
|
|
grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
y++;
|
|
|
|
sprintf(vehDebugString,"Wheel Raise Amount: %.3f", pWheel->GetSuspensionRaiseAmount() );
|
|
grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
y++;
|
|
|
|
sprintf(vehDebugString,"Wheel Target Raise Amount: %.3f", pWheel->GetSuspensionTargetRaiseAmount() );
|
|
grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
y++;
|
|
|
|
sprintf(vehDebugString,"Wheel Suspension Force Modified: %d", (int)( pWheel->m_bSuspensionForceModified ) );
|
|
grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
y++;
|
|
|
|
//sprintf(vehDebugString,"Wheel Suspension Success Count: %d", (int)( pWheel->m_nHydraulicsSuccessfulBounceCount ) );
|
|
//grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
//y++;
|
|
|
|
sprintf(vehDebugString,"Wheel Raise Rate: %.3f", pWheel->GetSuspensionRaiseRate() );
|
|
grcDebugDraw::PrintToScreenCoors( vehDebugString, StartX, y );
|
|
y++;
|
|
|
|
}
|
|
}
|
|
#endif // #if __BANK
|
|
}
|
|
|
|
void CTaskVehiclePlayerDriveAutomobile::ProcessHydraulicsStateChange( CControl* pControl, CVehicle* pVehicle )
|
|
{
|
|
eWheelHydraulicState result = WHS_INVALID;
|
|
u32 uTimeHydraulicsControlPressed = 0;
|
|
u32 uResetTimeThreshold = 50;
|
|
u32 uPressedTimeThreshold = 200;
|
|
|
|
#if __BANK
|
|
uResetTimeThreshold = Max( uResetTimeThreshold, 17 + fwTimer::GetTimeStepInMilliseconds() );
|
|
uPressedTimeThreshold = Max( uPressedTimeThreshold, 167 + fwTimer::GetTimeStepInMilliseconds() );
|
|
#endif
|
|
|
|
|
|
// toggle the suspension state if the button is up
|
|
// a quick tap resets the suspension.
|
|
bool buttonUp = pControl->GetVehicleHydraulicsControlToggle().IsUp();
|
|
bool buttonReleasedRecently = pControl->GetVehicleHydraulicsControlToggle().HistoryReleased( uResetTimeThreshold, &uTimeHydraulicsControlPressed );
|
|
bool buttonPressedRecently = pControl->GetVehicleHydraulicsControlToggle().HistoryPressed( uPressedTimeThreshold, &uTimeHydraulicsControlPressed );
|
|
bool resetSuspension = buttonUp && buttonPressedRecently;
|
|
bool donkSuspension = pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_HAS_LOWRIDER_DONK_HYDRAULICS );
|
|
|
|
bool toggleState = false;
|
|
static bool stateChanged = false;
|
|
|
|
int numWheels = pVehicle->GetNumWheels();
|
|
|
|
if( resetSuspension )
|
|
{
|
|
toggleState = true;
|
|
stateChanged = false;
|
|
}
|
|
else if( buttonUp ) // the button is up
|
|
{
|
|
stateChanged = false;
|
|
|
|
if( buttonReleasedRecently )// the button has been released recently and all the suspension is locked down
|
|
{
|
|
stateChanged = true;
|
|
|
|
// only do this if the wheels are all locked down
|
|
bool bounceAll = !donkSuspension;
|
|
bool lockPosition = true;
|
|
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
CWheel* pWheel = pVehicle->GetWheel( i );
|
|
|
|
if( pWheel )
|
|
{
|
|
eWheelHydraulicState wheelState = pWheel->GetSuspensionHydraulicState();
|
|
|
|
if( wheelState != WHS_LOCK_DOWN_ALL )
|
|
{
|
|
bounceAll = false;
|
|
}
|
|
|
|
#if RSG_PC
|
|
if( wheelState == WHS_LOCK_UP_ALL ||
|
|
wheelState == WHS_LOCK_DOWN_ALL ||
|
|
wheelState == WHS_IDLE ||
|
|
( wheelState >= WHS_BOUNCE_START &&
|
|
wheelState < WHS_BOUNCE_LAST_ACTIVE ) )
|
|
#else
|
|
if( wheelState == WHS_LOCK_UP_ALL ||
|
|
wheelState == WHS_IDLE )
|
|
#endif
|
|
{
|
|
lockPosition = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bounceAll )
|
|
{
|
|
result = WHS_BOUNCE_ALL;
|
|
}
|
|
else if( lockPosition )
|
|
{
|
|
result = WHS_LOCK;
|
|
}
|
|
}
|
|
}
|
|
else if( !buttonPressedRecently )// the button is still held and was pressed recently
|
|
{
|
|
toggleState = true;
|
|
}
|
|
|
|
if( toggleState &&
|
|
!stateChanged )
|
|
{
|
|
stateChanged = true;
|
|
|
|
if( resetSuspension )
|
|
{
|
|
result = WHS_IDLE;
|
|
}
|
|
else if( !pVehicle->m_nVehicleFlags.bAreHydraulicsAllRaised )
|
|
{
|
|
result = WHS_LOCK_UP_ALL;
|
|
}
|
|
else if( !donkSuspension )
|
|
{
|
|
result = WHS_LOCK_DOWN_ALL;
|
|
}
|
|
}
|
|
|
|
bool anyWheelStateChanged = false;
|
|
|
|
if( result != WHS_INVALID )
|
|
{
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
CWheel* pWheel = pVehicle->GetWheel( i );
|
|
|
|
if( pWheel && result != pWheel->GetSuspensionHydraulicTargetState() )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( result );
|
|
anyWheelStateChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(anyWheelStateChanged)
|
|
{
|
|
switch(result)
|
|
{
|
|
case WHS_BOUNCE_ALL:
|
|
if(numWheels > 0)
|
|
{
|
|
pVehicle->TriggerHydraulicBounceSound();
|
|
}
|
|
break;
|
|
|
|
case WHS_LOCK_UP_ALL:
|
|
{
|
|
u32 numRaisedWheels = 0;
|
|
|
|
if(donkSuspension)
|
|
{
|
|
CAutomobile* pAutomobile = static_cast< CAutomobile* >( pVehicle );
|
|
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
CWheel* pWheel = pVehicle->GetWheel( i );
|
|
|
|
if( pWheel &&
|
|
pWheel->GetSuspensionTargetRaiseAmount() == pWheel->GetSuspensionRaiseAmount() &&
|
|
pWheel->GetSuspensionRaiseAmount() == pAutomobile->m_fHydraulicsUpperBound )
|
|
{
|
|
numRaisedWheels++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(numRaisedWheels < numWheels)
|
|
{
|
|
pVehicle->TriggerHydraulicActivationSound();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WHS_LOCK_DOWN_ALL:
|
|
case WHS_IDLE:
|
|
if(donkSuspension)
|
|
{
|
|
bool allWheelsAtTarget = true;
|
|
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
CWheel* pWheel = pVehicle->GetWheel( i );
|
|
|
|
if( pWheel && pWheel->GetSuspensionTargetRaiseAmount() != pWheel->GetSuspensionRaiseAmount() )
|
|
{
|
|
allWheelsAtTarget = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(allWheelsAtTarget)
|
|
{
|
|
pVehicle->TriggerHydraulicDeactivationSound();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pVehicle->TriggerHydraulicDeactivationSound();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTaskVehiclePlayerDriveAutomobile::ProcessHydraulicsTilt( CControl* pControl, CVehicle* pVehicle, float fStickX, float fStickY )
|
|
{
|
|
bool result = false;
|
|
|
|
static dev_u32 uPressedTimeThreshold = 200;
|
|
u32 uTimeHydraulicsControlPressed = 0;
|
|
|
|
bool buttonHeld = pControl->GetVehicleHydraulicsControlToggle().IsDown();
|
|
bool buttonPressedRecently = pControl->GetVehicleHydraulicsControlToggle().HistoryPressed( uPressedTimeThreshold, &uTimeHydraulicsControlPressed );
|
|
bool donkSuspension = pVehicle->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_HAS_LOWRIDER_DONK_HYDRAULICS );
|
|
|
|
static float fPrevStickX = 0.0f;
|
|
static float fPrevStickY = 0.0f;
|
|
static float sf_deadZone = 0.05f;
|
|
|
|
if( Abs( fStickX ) < sf_deadZone )
|
|
{
|
|
fStickX = 0.0f;
|
|
}
|
|
if(Abs( fStickY ) < sf_deadZone )
|
|
{
|
|
fStickY = 0.0f;
|
|
}
|
|
|
|
static float sfHeldDuration = 0.0f;
|
|
static float sfLockDuration = 0.0f;
|
|
TUNE_GROUP_FLOAT(HYDRAULICS_TUNE, lockDuration, 0.5f, 0.0f, 5.0f, 0.1f );
|
|
TUNE_GROUP_FLOAT(HYDRAULICS_TUNE, unlockDuration, 0.0f, 0.0f, 5.0f, 0.1f );
|
|
float timeStep = fwTimer::GetTimeStep();
|
|
float invTimeStep = fwTimer::GetInvTimeStep();
|
|
u32 numNewWheelsBouncing = 0;
|
|
u32 numInitialWheelsLockedDown = 0;
|
|
u32 numWheelsInTiltLockUpState = 0;
|
|
u32 numWheelsInTiltLockDownState = 0;
|
|
|
|
if( buttonHeld &&
|
|
!buttonPressedRecently )
|
|
{
|
|
CAutomobile* pAutomobile = static_cast< CAutomobile* >( pVehicle );
|
|
float stickDeltaX = ( Abs( fStickX ) - Abs( fPrevStickX ) ) * invTimeStep;
|
|
float stickDeltaY = ( Abs( fStickY ) - Abs( fPrevStickY ) ) * invTimeStep;
|
|
TUNE_GROUP_FLOAT(HYDRAULICS_TUNE, bounceStickDelta, 13.5f, 0.0f, 100.0, 0.5f );
|
|
TUNE_GROUP_FLOAT(HYDRAULICS_TUNE, resetTollerance, 0.2f, 0.0f, 1.0f, 0.01f );
|
|
TUNE_GROUP_FLOAT(HYDRAULICS_TUNE, donkResetTollerance, 0.8f, 0.0f, 1.0f, 0.01f );
|
|
bool bounce = false;
|
|
float fBounceStickX = -fStickX;
|
|
float fBounceStickY = -fStickY;
|
|
|
|
if( !donkSuspension &&
|
|
( stickDeltaX > bounceStickDelta ||
|
|
stickDeltaY > bounceStickDelta ) )
|
|
{
|
|
bounce = true;
|
|
|
|
if( sb_invertBounceStick )
|
|
{
|
|
fBounceStickY *= -1.0f;
|
|
fBounceStickX *= -1.0f;
|
|
}
|
|
}
|
|
|
|
int numWheels = pVehicle->GetNumWheels();
|
|
int halfNumWheels = numWheels / 2;
|
|
const bool isUpsideDownOrOnSide = (pVehicle->IsOnItsSide() || pVehicle->IsUpsideDown());
|
|
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
CWheel *pWheel = pVehicle->GetWheel( i );
|
|
|
|
if( pWheel )
|
|
{
|
|
eWheelHydraulicState currentState = pWheel->GetSuspensionHydraulicState();
|
|
eWheelHydraulicState targetState = pWheel->GetSuspensionHydraulicTargetState();
|
|
|
|
if(donkSuspension)
|
|
{
|
|
if(currentState == targetState && currentState == WHS_IDLE)
|
|
{
|
|
numInitialWheelsLockedDown++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(currentState == targetState)
|
|
{
|
|
if(currentState == WHS_LOCK_DOWN)
|
|
{
|
|
numWheelsInTiltLockDownState++;
|
|
}
|
|
else if(currentState == WHS_LOCK_UP)
|
|
{
|
|
numWheelsInTiltLockUpState++;
|
|
}
|
|
}
|
|
|
|
if(currentState == WHS_LOCK_DOWN_ALL || currentState == WHS_LOCK_DOWN || currentState == WHS_IDLE)
|
|
{
|
|
numInitialWheelsLockedDown++;
|
|
}
|
|
}
|
|
|
|
if( ( currentState < WHS_BOUNCE_START ||
|
|
currentState > WHS_BOUNCE_END ) &&
|
|
( targetState < WHS_BOUNCE_START ||
|
|
targetState > WHS_BOUNCE_END ) )
|
|
{
|
|
Vector3 vOffset = CWheel::GetWheelOffset(pAutomobile->GetVehicleModelInfo(), pWheel->GetHierarchyId());
|
|
vOffset.z = 0.0f;
|
|
vOffset.NormalizeSafe(vOffset);
|
|
|
|
if( donkSuspension )
|
|
{
|
|
if( vOffset.x != 0.0f )
|
|
{
|
|
vOffset.x = vOffset.x > 0.0f ? 1.0f : -1.0f;
|
|
}
|
|
if( vOffset.y != 0.0f )
|
|
{
|
|
vOffset.y = vOffset.y > 0.0f ? 1.0f : -1.0f;
|
|
}
|
|
}
|
|
|
|
if( fStickX != 0.0f ||
|
|
fStickY != 0.0f ||
|
|
bounce )
|
|
{
|
|
if( bounce )
|
|
{
|
|
float t = ( vOffset.x * fBounceStickX ) +
|
|
( vOffset.y * fBounceStickY );
|
|
|
|
eWheelHydraulicState bounceState = WHS_BOUNCE_FRONT_BACK;
|
|
|
|
int oppositeWheelIndex = i;
|
|
if( Abs( fBounceStickX ) > Abs( fBounceStickY ) )
|
|
{
|
|
bounceState = WHS_BOUNCE_LEFT_RIGHT;
|
|
|
|
if( i % 2 == 1 )
|
|
{
|
|
oppositeWheelIndex = i - 1;
|
|
}
|
|
else
|
|
{
|
|
oppositeWheelIndex = i + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( i < halfNumWheels )
|
|
{
|
|
oppositeWheelIndex = i + halfNumWheels;
|
|
}
|
|
else
|
|
{
|
|
oppositeWheelIndex = i - halfNumWheels;
|
|
}
|
|
}
|
|
|
|
CWheel* pOppositeWheel = pVehicle->GetWheel( oppositeWheelIndex );
|
|
|
|
if( t > 0.0f )
|
|
{
|
|
if( pWheel->GetSuspensionRaiseAmount() <= 0.0f &&
|
|
( !pOppositeWheel ||
|
|
pOppositeWheel->GetSuspensionHydraulicState() == WHS_LOCK_UP ) ) // only bounce if both this wheel and the opposite one are in the correct position
|
|
{
|
|
if( pWheel->GetSuspensionHydraulicTargetState() != bounceState )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( bounceState );
|
|
|
|
if(isUpsideDownOrOnSide || pWheel->GetIsTouching())
|
|
{
|
|
numNewWheelsBouncing++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_IDLE );
|
|
}
|
|
}
|
|
else if( t < 0.0f )
|
|
{
|
|
if( pWheel->GetSuspensionHydraulicTargetState() != WHS_LOCK_UP )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_LOCK_UP );
|
|
|
|
if(isUpsideDownOrOnSide || pWheel->GetIsTouching())
|
|
{
|
|
numNewWheelsBouncing++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !sb_invertBounceStick )
|
|
{
|
|
fStickX = -fBounceStickX;
|
|
fStickY = -fBounceStickY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = true;
|
|
|
|
// if we're not bouncing
|
|
// if the wheel is free we need to check to see if we should lock this wheel in position
|
|
bool wheelLocked = ( currentState >= WHS_LOCK_START &&
|
|
currentState <= WHS_LOCK_END ) ||
|
|
( targetState >= WHS_LOCK_START &&
|
|
targetState <= WHS_LOCK_END );
|
|
|
|
float t = ( vOffset.x * fStickX ) +
|
|
( vOffset.y * fStickY );
|
|
|
|
if( !wheelLocked )
|
|
{
|
|
if( sfLockDuration > lockDuration )
|
|
{
|
|
if( t > 0.0f )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_LOCK_UP );
|
|
}
|
|
else if( t < -0.0f )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_LOCK_DOWN );
|
|
}
|
|
}
|
|
else if( currentState != WHS_BOUNCE_ACTIVE &&
|
|
currentState != WHS_BOUNCE_LANDING &&
|
|
currentState != WHS_BOUNCE_LANDED )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_FREE );
|
|
}
|
|
}
|
|
else if( wheelLocked &&
|
|
sfLockDuration > unlockDuration ) // if the wheel is locked then we need to check to see if we unlock it
|
|
{
|
|
if( ( t > resetTollerance ||
|
|
( donkSuspension &&
|
|
t < -donkResetTollerance ) ) &&
|
|
( currentState == WHS_LOCK_DOWN ||
|
|
currentState == WHS_LOCK_DOWN_ALL ) )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_FREE );
|
|
}
|
|
else if( ( t < -resetTollerance ||
|
|
( donkSuspension &&
|
|
t > donkResetTollerance ) ) &&
|
|
( currentState == WHS_LOCK_UP ||
|
|
currentState == WHS_LOCK_UP_ALL ) )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_FREE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we go from fully idle to > 1 wheel being locked, trigger the activate sound
|
|
if((s32)numInitialWheelsLockedDown == numWheels)
|
|
{
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
CWheel *pWheel = pVehicle->GetWheel( i );
|
|
|
|
if(pWheel && pWheel->GetIsTouching())
|
|
{
|
|
if(pWheel->GetSuspensionHydraulicTargetState() == WHS_LOCK_UP || pWheel->GetSuspensionHydraulicTargetState() == WHS_LOCK_UP_ALL || pWheel->GetSuspensionHydraulicTargetState() == WHS_FREE)
|
|
{
|
|
pVehicle->TriggerHydraulicActivationSound();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if the button is no longer pressed lock any wheels that were free
|
|
for( int i = 0; i < pVehicle->GetNumWheels(); i++ )
|
|
{
|
|
if( CWheel *pWheel = pVehicle->GetWheel( i ) )
|
|
{
|
|
eWheelHydraulicState currentState = pWheel->GetSuspensionHydraulicState();
|
|
|
|
if( currentState == WHS_FREE )
|
|
{
|
|
pWheel->SetSuspensionHydraulicTargetState( WHS_LOCK );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Treat going from tilted (ie 2 wheels up, 2 wheels down) to not as a bounce, so that we play a bounce hiss sound
|
|
if(numNewWheelsBouncing == 0 && numWheelsInTiltLockDownState == 2 && numWheelsInTiltLockUpState == 2)
|
|
{
|
|
numWheelsInTiltLockUpState = 0;
|
|
numWheelsInTiltLockDownState = 0;
|
|
|
|
for( int i = 0; i < pVehicle->GetNumWheels(); i++ )
|
|
{
|
|
if( CWheel *pWheel = pVehicle->GetWheel( i ) )
|
|
{
|
|
eWheelHydraulicState wheelTargetState = pWheel->GetSuspensionHydraulicTargetState();
|
|
|
|
if( wheelTargetState == WHS_LOCK_DOWN )
|
|
{
|
|
numWheelsInTiltLockDownState++;
|
|
}
|
|
else if( wheelTargetState == WHS_LOCK_UP )
|
|
{
|
|
numWheelsInTiltLockUpState++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(numWheelsInTiltLockUpState != 2 || numWheelsInTiltLockDownState != 2)
|
|
{
|
|
numNewWheelsBouncing++;
|
|
}
|
|
}
|
|
|
|
// If one or more wheels entered the bounce state this frame, then trigger
|
|
// some hydraulic bounce audio
|
|
if(numNewWheelsBouncing > 0)
|
|
{
|
|
pVehicle->TriggerHydraulicBounceSound();
|
|
}
|
|
|
|
// if the stick position hasn't changed then we increment the lock timer
|
|
if( ( fPrevStickX != 0.0f ||
|
|
fPrevStickY != 0.0f ) &&
|
|
fPrevStickX == fStickX &&
|
|
fPrevStickY == fStickY )
|
|
{
|
|
sfLockDuration += timeStep;
|
|
}
|
|
else
|
|
{
|
|
fPrevStickX = fStickX;
|
|
fPrevStickY = fStickY;
|
|
sfLockDuration = 0.0f;
|
|
}
|
|
|
|
if( result )
|
|
{
|
|
sfHeldDuration += timeStep;
|
|
}
|
|
else
|
|
{
|
|
sfHeldDuration = 0.0f;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CTaskVehiclePlayerDriveAutomobile::ProcessSuspensionDrop( CVehicle* pVehicle )
|
|
{
|
|
static dev_float maxSpeedToLowerSuspension = 1.0f;
|
|
|
|
float targetRaiseAmount = 0.0f;
|
|
audVehicleAudioEntity* vehicleAudioEntity = pVehicle->GetVehicleAudioEntity();
|
|
static dev_float sfMaxStuckTimeForSuspensionRaise = 2000.0f;
|
|
|
|
if( pVehicle->GetVelocity().Mag2() < maxSpeedToLowerSuspension &&
|
|
( !pVehicle->InheritsFromAutomobile() || !static_cast< CAutomobile* >( pVehicle )->m_nAutomobileFlags.bWheelieModeEnabled ) &&
|
|
( ( pVehicle->GetVehicleDamage()->GetStuckTimer( VEH_STUCK_JAMMED ) < sfMaxStuckTimeForSuspensionRaise || pVehicle->GetThrottle() == 0.0f ) ||
|
|
( pVehicle->InheritsFromAutomobile() && static_cast< CAutomobile* >( pVehicle )->m_nAutomobileFlags.bInBurnout ) ) ) // raise the suspension back up if the vehicle is jammed
|
|
{
|
|
if(vehicleAudioEntity)
|
|
{
|
|
vehicleAudioEntity->TriggerStationarySuspensionDrop();
|
|
}
|
|
|
|
targetRaiseAmount = -pVehicle->pHandling->m_fSuspensionRaise;
|
|
}
|
|
else
|
|
{
|
|
if(vehicleAudioEntity)
|
|
{
|
|
vehicleAudioEntity->CancelStationarySuspensionDrop();
|
|
}
|
|
}
|
|
|
|
bool suspensionUpdated = false;
|
|
int numWheels = pVehicle->GetNumWheels();
|
|
static_cast< CAutomobile* >( pVehicle )->SetHydraulicsRate( Max( static_cast< CAutomobile* >( pVehicle )->GetHydraulicsRate(), 0.25f ) );
|
|
static dev_float suspensionLowerRate = 0.4f;
|
|
bool steeringLockReduced = false;
|
|
|
|
for(int i = 0; i < numWheels; i++)
|
|
{
|
|
if( CWheel *pWheel = pVehicle->GetWheel( i ) )
|
|
{
|
|
if( targetRaiseAmount != pWheel->GetSuspensionRaiseAmount() )
|
|
{
|
|
//if( targetRaiseAmount < pWheel->GetSuspensionRaiseAmount() ||
|
|
// pVehicle->GetThrottle() > 0.0f )
|
|
{
|
|
pWheel->SetSuspensionTargetRaiseAmount( targetRaiseAmount, suspensionLowerRate );
|
|
suspensionUpdated = true;
|
|
}
|
|
}
|
|
|
|
float currentRaiseAmount = pWheel->GetSuspensionRaiseAmount();
|
|
if( !steeringLockReduced &&
|
|
pWheel->GetConfigFlags().IsFlagSet( WCF_STEER ) &&
|
|
currentRaiseAmount < 0.0f )
|
|
{
|
|
float currentLoweredAmount = currentRaiseAmount;
|
|
currentLoweredAmount /= pVehicle->pHandling->m_fSuspensionRaise;
|
|
currentLoweredAmount *= 0.8f;
|
|
|
|
pVehicle->SetSteerAngle( pVehicle->GetSteerAngle() * ( 1.0f - Abs( currentLoweredAmount ) ) );
|
|
|
|
steeringLockReduced = true;
|
|
}
|
|
}
|
|
|
|
if( suspensionUpdated )
|
|
{
|
|
pVehicle->ActivatePhysics();
|
|
|
|
for( int i = 0; i < numWheels; i++ )
|
|
{
|
|
if( CWheel *pWheel = pVehicle->GetWheel( i ) )
|
|
{
|
|
pWheel->GetConfigFlags().SetFlag( WCF_UPDATE_SUSPENSION );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
#if !__FINAL
|
|
const char * CTaskVehiclePlayerDriveAutomobile::GetStaticStateName( s32 iState )
|
|
{
|
|
Assert(iState>=State_Drive&&iState<=State_DiggerMode);
|
|
static const char* aStateNames[] =
|
|
{
|
|
"State_Drive",
|
|
"State_NoDriver",
|
|
"State_DiggerMode"
|
|
};
|
|
|
|
return aStateNames[iState];
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveDiggerArm
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveDiggerArm::CTaskVehiclePlayerDriveDiggerArm(CVehicleGadgetArticulatedDiggerArm *diggerGadget) :
|
|
CTaskVehiclePlayerDrive(),
|
|
m_DiggerGadget(diggerGadget)
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_DIGGER_ARM);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveDiggerArm::ProcessDriverInputsForPlayerOnUpdate(CPed *UNUSED_PARAM(pPlayerPed), CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
Assert(m_DiggerGadget);
|
|
|
|
if(pControl==NULL)
|
|
return;
|
|
|
|
//Make sure the digger isn't still moving
|
|
pVehicle->SetBrake(1.0f);
|
|
pVehicle->SetHandBrake(true);
|
|
pVehicle->SetThrottle(0.0f);
|
|
|
|
pControl->SetVehicleSteeringExclusive();
|
|
float fDesiredSteerInputUpDown = pControl->GetVehicleSteeringUpDown().GetNorm();
|
|
float fDesiredSteerInputLeftRight = pControl->GetVehicleSteeringLeftRight().GetNorm();
|
|
float fDesiredGunInputUpDown = pControl->GetVehicleGunUpDown().GetUnboundNorm();
|
|
float fDesiredGunInputLeftRight = pControl->GetVehicleGunLeftRight().GetUnboundNorm();
|
|
|
|
#if RSG_PC
|
|
if(pControl->GetVehicleSteeringLeftRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
fDesiredSteerInputLeftRight *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(pControl->GetVehicleSteeringUpDown().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
fDesiredSteerInputUpDown *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(pControl->GetVehicleGunLeftRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
fDesiredGunInputLeftRight *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(pControl->GetVehicleGunUpDown().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
fDesiredGunInputUpDown *= c_fMouseAdjustmentScale;
|
|
}
|
|
#endif
|
|
|
|
m_DiggerGadget->SetDesiredAcceleration(DIGGER_JOINT_BASE, -fDesiredSteerInputLeftRight );
|
|
m_DiggerGadget->SetDesiredAcceleration(DIGGER_JOINT_BOOM, fDesiredSteerInputUpDown );
|
|
m_DiggerGadget->SetDesiredAcceleration(DIGGER_JOINT_STICK, fDesiredGunInputUpDown );
|
|
m_DiggerGadget->SetDesiredAcceleration(DIGGER_JOINT_BUCKET, fDesiredGunInputLeftRight );
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveBike
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveBike::CTaskVehiclePlayerDriveBike() :
|
|
m_uLastSprintTime(0),
|
|
m_uStartTime(0),
|
|
m_fMaxBicycleForwardnessSlowCycle(ms_fBICYCLE_MAX_FORWARDNESS_SLOW),
|
|
m_bClearedForwardness(false),
|
|
m_bStartFinished(false),
|
|
m_fStartingThrottle(0.0f),
|
|
CTaskVehiclePlayerDrive()
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_BIKE);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//Moving tuning values
|
|
static dev_float sfPlayerBikeSpeedSteerMult = 0.15f;
|
|
static dev_float sfPlayerBicycleSpeedSteerMult = 0.075f;
|
|
|
|
static dev_float sfPlayerBikeSpeedSteerFwdThreshold = 0.15f;
|
|
static dev_float sfPlayerBikeSpeedSteerSideRatioThreshold = 0.1f;
|
|
static dev_bool sbPlayerBikeSpeedSteerAutoCentre = true;
|
|
static dev_float sfPlayerBikeSpeedSteerAutoCentreMax = ( DtoR * 10.0f);
|
|
|
|
static dev_float sfPlayerBicycleTuckSteerMult = 0.05f;
|
|
|
|
//Stationary tuning values
|
|
static dev_bool sbPlayerBikeStationarySteerAutoCentre = true;
|
|
static dev_float sfPlayerBikeNoInputSteerAutoCentreMax = ( DtoR * 5.0f);
|
|
|
|
void CTaskVehiclePlayerDriveBike::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CBike*>(pVehicle));
|
|
CBike *pBike = static_cast<CBike*>(pVehicle);
|
|
float timeStep = fwTimer::GetTimeStep();
|
|
|
|
Assert(pPlayerPed);
|
|
if (!pPlayerPed->IsControlledByLocalPlayer())
|
|
return;
|
|
|
|
if(pControl==NULL)
|
|
return;
|
|
/* //Disable this on bikes for the time being
|
|
// Process whether we should enable Franklin's special ability.
|
|
if( pPlayerPed->GetSpecialAbility() && pPlayerPed->GetSpecialAbility()->GetType() == SAT_CAR_SLOWDOWN )
|
|
{
|
|
if(pControl->GetVehicleHandBrakeAlt().IsDown())
|
|
{
|
|
pPlayerPed->GetSpecialAbility()->Activate();
|
|
}
|
|
else
|
|
{
|
|
pPlayerPed->GetSpecialAbility()->Deactivate();
|
|
}
|
|
}
|
|
*/
|
|
if( pControl->GetVehicleExit().IsDown() && m_bExitButtonUp &&
|
|
!CControlMgr::GetMainPlayerControl().IsInputDisabled( INPUT_VEH_EXIT ) )// we want to get out of the car, so we need to brake
|
|
{
|
|
pBike->SetHandBrake( true );
|
|
}
|
|
else if (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
pBike->SetHandBrake(false);//no handbrake on bicycles
|
|
}
|
|
else
|
|
{
|
|
pBike->SetHandBrake(pBike->GetIsHandBrakePressed(pControl));
|
|
}
|
|
|
|
|
|
float fDesiredSteerInputUpDown = 0.0f;
|
|
float fDesiredSteerInputLeftRight = 0.0f;
|
|
|
|
float fMovingSteerSmoothRate = (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? ms_fBICYCLE_STD_STEER_SMOOTH_RATE : ms_fBIKE_STD_STEER_SMOOTH_RATE;
|
|
float fStoppedSteerSmoothRate = (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? ms_fBICYCLE_STOPPED_STEER_SMOOTH_RATE : ms_fBIKE_STOPPED_STEER_SMOOTH_RATE;
|
|
float fLeanSmoothRate = (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? ms_fBICYCLE_STD_LEAN_SMOOTH_RATE : ms_fBIKE_STD_LEAN_SMOOTH_RATE;
|
|
|
|
float fSteerSmoothRate = fStoppedSteerSmoothRate;
|
|
float fFwdSpeedFrac = DotProduct(VEC3V_TO_VECTOR3(pBike->GetTransform().GetB()), pBike->GetVelocityIncludingReferenceFrame());
|
|
fFwdSpeedFrac /= (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? ms_fBICYCLE_STEER_SMOOTH_RATE_MAXSPEED : ms_fBIKE_STEER_SMOOTH_RATE_MAXSPEED;
|
|
|
|
// Or when putting on helmet
|
|
if(!pVehicle->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_PuttingOnHelmet))
|
|
{
|
|
pBike->GetBikeLeanInputs(fDesiredSteerInputLeftRight, fDesiredSteerInputUpDown);
|
|
|
|
if( abs(fDesiredSteerInputLeftRight) < ms_fMOTION_STEER_DEAD_ZONE )
|
|
{
|
|
fDesiredSteerInputLeftRight = 0.0f;
|
|
}
|
|
|
|
fMovingSteerSmoothRate *= ms_fMOTION_STEER_SMOOTH_RATE_MULT;
|
|
fStoppedSteerSmoothRate *= ms_fMOTION_STEER_SMOOTH_RATE_MULT;
|
|
}
|
|
|
|
|
|
if(fFwdSpeedFrac > 1.0f)
|
|
fSteerSmoothRate = fMovingSteerSmoothRate;
|
|
else if(fFwdSpeedFrac > 0.0f)
|
|
fSteerSmoothRate = fFwdSpeedFrac*fMovingSteerSmoothRate + (1.0f - fFwdSpeedFrac)*fStoppedSteerSmoothRate;
|
|
|
|
// If we are using Franklin's special ability we shouldn't slow down the steering.
|
|
if( pPlayerPed->GetSpecialAbility() && pPlayerPed->GetSpecialAbility()->GetType() == SAT_CAR_SLOWDOWN )
|
|
{
|
|
if( pPlayerPed->GetSpecialAbility()->IsActive() )
|
|
{
|
|
timeStep = fwTimer::GetNonPausableCamTimeStep() * sfSpecialAbilitySteeringTimeStepMult;// We want to use a time step for the steering which can't be modified by scaling time
|
|
}
|
|
}
|
|
|
|
//reduce steering beneath the limits
|
|
if(rage::Abs(fDesiredSteerInputLeftRight) < ms_fPAD_STEER_REUCTION_PAD_END)
|
|
{
|
|
fDesiredSteerInputLeftRight *= (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? ms_fBICYCLE_STEER_REDUCTION_UNDER_LIMIT_MULT : ms_fBIKE_STEER_REDUCTION_UNDER_LIMIT_MULT;;
|
|
}
|
|
|
|
// progressive steering
|
|
float fSteeringChange = (fDesiredSteerInputLeftRight - pBike->m_fSteerInput) * rage::Min(1.0f,fSteerSmoothRate * timeStep);
|
|
|
|
pBike->m_fSteerInput = pBike->m_fSteerInput + fSteeringChange;
|
|
|
|
pBike->m_fSteerInput = Clamp(pBike->m_fSteerInput, -1.0f, 1.0f);
|
|
|
|
// progressive leaning
|
|
fDesiredSteerInputUpDown = Clamp(fDesiredSteerInputUpDown, -1.0f, 1.0f);
|
|
|
|
if( pVehicle->m_bInvertControls )
|
|
{
|
|
fDesiredSteerInputUpDown = -fDesiredSteerInputUpDown;
|
|
}
|
|
|
|
pBike->m_fAnimLeanFwd = pBike->m_fAnimLeanFwd + (fDesiredSteerInputUpDown - pBike->m_fAnimLeanFwd) * rage::Min(1.0f,fLeanSmoothRate * timeStep);
|
|
pBike->m_fAnimLeanFwd = Clamp(pBike->m_fAnimLeanFwd, -1.0f, 1.0f);
|
|
|
|
if (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
bool bForceBrake = false;
|
|
if (pControl->GetVehicleExit().IsDown())
|
|
{
|
|
if (!pVehicle->CanPedJumpOutCar(pPlayerPed))
|
|
{
|
|
pVehicle->SetHandBrake(true);
|
|
bForceBrake = true;
|
|
}
|
|
}
|
|
|
|
ProcessPlayerControlInputsForBrakeAndGasPedalBicycle(pControl, bForceBrake, pBike, pPlayerPed);
|
|
}
|
|
else
|
|
{
|
|
ProcessPlayerControlInputsForBrakeAndGasPedal(pControl, false, pVehicle);
|
|
}
|
|
|
|
// Jumping Bike code
|
|
if(pBike->pHandling && pBike->pHandling->GetBikeHandlingData() && pBike->pHandling->GetBikeHandlingData()->m_fJumpForce > 0.0f)
|
|
{
|
|
//Have to work out whether the button is pressed by using the history as special abilities use the same button and clears the input, this logic clones that of GetPedDuck
|
|
static dev_u32 HISTORY_MS = 300;
|
|
u32 timePressed = 0;
|
|
if(pControl->GetVehicleJump().HistoryPressed(HISTORY_MS, &timePressed))
|
|
{
|
|
if(timePressed > pBike->m_uJumpLastPressedTime)
|
|
{
|
|
pBike->m_uJumpLastPressedTime = timePressed;
|
|
pBike->m_nBikeFlags.bJumpPressed = true;
|
|
pBike->m_nBikeFlags.bJumpReleased = false;
|
|
}
|
|
}
|
|
else if(pBike->m_uJumpLastPressedTime > 0 && pBike->m_uJumpLastReleasedTime == 0 && pControl->GetVehicleJump().HistoryReleased(HISTORY_MS, &timePressed))
|
|
{
|
|
pBike->m_nBikeFlags.bJumpReleased = true;
|
|
pBike->m_nBikeFlags.bJumpPressed = false;
|
|
}
|
|
}
|
|
|
|
|
|
// script influence on steering input
|
|
if( pVehicle->m_bInvertControls )
|
|
{
|
|
pVehicle->m_fSteerInput = -pVehicle->m_fSteerInput;
|
|
}
|
|
pBike->m_fSteerInput += 0.5f*pBike->m_fSteerInputBias;
|
|
pBike->m_fSteerInput = Clamp(pBike->m_fSteerInput, -1.0f, 1.0f);
|
|
|
|
if (pBike->GetIntelligence()->GetRecordingNumber() < 0 || CVehicleRecordingMgr::GetUseCarAI(pBike->GetIntelligence()->GetRecordingNumber())) // Only overwrite the steer angle if this bike doesn't have a recording played on it. (This can happen for the players' bike)
|
|
{
|
|
float fDesiredSteerAngle = pBike->m_fSteerInput * pBike->pHandling->m_fSteeringLock;
|
|
|
|
if(pBike->GetDriver() && pBike->GetDriver()->IsLocalPlayer())// For the local player, do some extra steering correction
|
|
{
|
|
Vector3 vBikeVelocity = pBike->GetVelocityIncludingReferenceFrame();
|
|
|
|
float fFwdSpeed = DotProduct(VEC3V_TO_VECTOR3(pBike->GetTransform().GetB()), vBikeVelocity);
|
|
float fSideSpeed = DotProduct(VEC3V_TO_VECTOR3(pBike->GetTransform().GetA()), vBikeVelocity);
|
|
|
|
float fPlayerBikeSpeedSteerMult = (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? sfPlayerBicycleSpeedSteerMult : sfPlayerBikeSpeedSteerMult;
|
|
|
|
float fPlayerBikeSpeedSteerFwdThreshold = sfPlayerBikeSpeedSteerFwdThreshold * pBike->pHandling->m_fEstimatedMaxFlatVel;
|
|
if(fFwdSpeed > fPlayerBikeSpeedSteerFwdThreshold &&
|
|
!(fSideSpeed * fDesiredSteerAngle < -sfPlayerBikeSpeedSteerSideRatioThreshold*rage::Abs(fFwdSpeed)) )
|
|
{
|
|
fDesiredSteerAngle /= 1.0f + fPlayerBikeSpeedSteerMult * (fFwdSpeed - fPlayerBikeSpeedSteerFwdThreshold);
|
|
}
|
|
|
|
float fSteeringLockReduction = 1.0f;
|
|
if(pBike->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_IsTuckedOnBicycleThisFrame))
|
|
{
|
|
fSteeringLockReduction = sfPlayerBicycleTuckSteerMult;
|
|
}
|
|
|
|
//Steer reduction rates
|
|
float fReductionConst = (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? 0.75f : 0.55f;
|
|
float fStationaryReductionConst = (pBike->GetVehicleType() == VEHICLE_TYPE_BICYCLE) ? 0.80f : 0.70f;
|
|
|
|
bool bSteerReduction = false;
|
|
float fSteerReductionConst = fReductionConst;
|
|
if(fFwdSpeed > fPlayerBikeSpeedSteerFwdThreshold && sbPlayerBikeSpeedSteerAutoCentre)
|
|
{
|
|
TUNE_GROUP_FLOAT(FIRST_PERSON_IN_VEHICLE_TUNE, BICYCLE_STEER_AUTO_CENTRE_MAX, 2.5f, 0.0f, 20.0f, 0.01f);
|
|
float fSteerAutoCentreMax = CTaskMotionInAutomobile::IsFirstPersonDrivingBicycle(*pBike->GetDriver(), *pBike) ? BICYCLE_STEER_AUTO_CENTRE_MAX * DtoR : sfPlayerBikeSpeedSteerAutoCentreMax;
|
|
float fAutoCentreSteerAngle = 0.0f;
|
|
static dev_float sfMinLeanAngleToAutoCentreSteer = 0.01f;
|
|
if(rage::Abs(pBike->GetLeanAngle()) > sfMinLeanAngleToAutoCentreSteer && !pBike->IsInAir())
|
|
{
|
|
fAutoCentreSteerAngle = rage::Atan2f(-fSideSpeed, fFwdSpeed);
|
|
fAutoCentreSteerAngle = rage::Clamp(fAutoCentreSteerAngle, -fSteerAutoCentreMax, fSteerAutoCentreMax);
|
|
}
|
|
|
|
fDesiredSteerAngle = rage::Clamp(fDesiredSteerAngle + fAutoCentreSteerAngle, -pBike->pHandling->m_fSteeringLock*fSteeringLockReduction, pBike->pHandling->m_fSteeringLock*fSteeringLockReduction);
|
|
bSteerReduction = rage::Abs(fDesiredSteerAngle) > rage::Abs(pBike->GetSteerAngle()) && !pBike->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_IsTuckedOnBicycleThisFrame);
|
|
}
|
|
else if(sbPlayerBikeStationarySteerAutoCentre)
|
|
{
|
|
if(IsClose(fDesiredSteerInputLeftRight, 0.0f, FLT_EPSILON))
|
|
{
|
|
TUNE_GROUP_FLOAT(FIRST_PERSON_IN_VEHICLE_TUNE, BICYCLE_NO_INPUT_STEER_AUTO_CENTRE_MAX, 2.5f, 0.0f, 20.0f, 0.01f);
|
|
float fNoInputSteerAutoCentreMax = CTaskMotionInAutomobile::IsFirstPersonDrivingBicycle(*pBike->GetDriver(), *pBike) ? BICYCLE_NO_INPUT_STEER_AUTO_CENTRE_MAX * DtoR : sfPlayerBikeNoInputSteerAutoCentreMax;
|
|
float fDesiredChange = rage::Clamp(fDesiredSteerAngle - pBike->GetSteerAngle(), -fNoInputSteerAutoCentreMax, fNoInputSteerAutoCentreMax);
|
|
fDesiredSteerAngle = fDesiredChange + pBike->GetSteerAngle();
|
|
}
|
|
else
|
|
{
|
|
bSteerReduction = true;
|
|
}
|
|
|
|
fDesiredSteerAngle = rage::Clamp(fDesiredSteerAngle, -pBike->pHandling->m_fSteeringLock*fSteeringLockReduction, pBike->pHandling->m_fSteeringLock*fSteeringLockReduction);
|
|
fSteerReductionConst = fStationaryReductionConst;
|
|
}
|
|
|
|
float fOldSteerAngle = pBike->GetSteerAngle();
|
|
float fSteerDelta = fDesiredSteerAngle-fOldSteerAngle;
|
|
|
|
float fSteerReduction = 0.0f;
|
|
|
|
if(bSteerReduction)
|
|
{
|
|
fSteerReduction = fSteerReductionConst * fSteerDelta;
|
|
}
|
|
else if(pBike->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_IsTuckedOnBicycleThisFrame))// If we're past the steering limits we want to slowly bring the steering back under the limits when in the tucked mode.
|
|
{
|
|
if(rage::Abs(pBike->GetSteerAngle()) > pBike->pHandling->m_fSteeringLock*fSteeringLockReduction)
|
|
{
|
|
fSteerReduction = fSteerReductionConst * fSteerDelta;
|
|
}
|
|
}
|
|
|
|
float fSteerChange = (fSteerDelta - fSteerReduction);
|
|
|
|
if(Sign(fSteerDelta) != Sign(fSteerChange))
|
|
{
|
|
fSteerChange = 0.0f;
|
|
}
|
|
|
|
fDesiredSteerAngle = fOldSteerAngle + fSteerChange;
|
|
}
|
|
|
|
pBike->SetSteerAngle(fDesiredSteerAngle);
|
|
}
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveBike::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *pVehicle)
|
|
{
|
|
if (pVehicle->m_nVehicleFlags.bLimitSpeedWhenPlayerInactive)
|
|
{
|
|
// If the player has no control (cut scene for instance) we will hit the brakes.
|
|
if(pVehicle->HasContactWheels())
|
|
{
|
|
pVehicle->SetBrake(1.0f);
|
|
pVehicle->SetHandBrake(false);
|
|
pVehicle->SetThrottle(0.0f);
|
|
FindPlayerPed()->GetPlayerInfo()->KeepAreaAroundPlayerClear();
|
|
// Limit the velocity
|
|
float Speed = pVehicle->GetVelocity().Mag();
|
|
|
|
if (Speed > ms_fBIKE_MAXSPEEDNOW && pVehicle->m_nVehicleFlags.bStopInstantlyWhenPlayerInactive)
|
|
{
|
|
float NewSpeed = rage::Max(ms_fBIKE_MAXSPEEDNOW, Speed - ms_fBIKE_MAXSPEEDCHANGE);
|
|
pVehicle->SetVelocity( pVehicle->GetVelocity() * (NewSpeed / Speed) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveBike::ProcessPlayerControlInputsForBrakeAndGasPedalBicycle(CControl* pControl, bool bForceBrake, CBike *pBike, CPed* pPlayerPed)
|
|
{
|
|
CVehicleModelInfo* pVehicleModelInfo = pBike->GetVehicleModelInfo();
|
|
CHandlingData* pHandlingData = CHandlingDataMgr::GetHandlingData(pVehicleModelInfo->GetHandlingId());
|
|
//get the vehicles reference frame velocity, so if they are driving on a plane we car work out the relative velocity rather then just using the absolute
|
|
Vector3 vRelativeVelocity = pBike->GetVelocityIncludingReferenceFrame();
|
|
const float fCurrentSpeed = vRelativeVelocity.Mag();
|
|
const float fMaxSpeed = pHandlingData->m_fEstimatedMaxFlatVel;
|
|
|
|
float fSpeedRatio = fCurrentSpeed / fMaxSpeed;
|
|
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, BICYCLE_THROTTLE_RECOVERY_RATE, 0.04f, 0.0f, 1.0f, 0.01f);
|
|
if (m_fMaxBicycleForwardnessSlowCycle < ms_fBICYCLE_MAX_FORWARDNESS_SLOW)
|
|
{
|
|
m_fMaxBicycleForwardnessSlowCycle += fwTimer::GetTimeStep() * BICYCLE_THROTTLE_RECOVERY_RATE;
|
|
m_fMaxBicycleForwardnessSlowCycle = rage::Clamp(m_fMaxBicycleForwardnessSlowCycle, 0.0f, ms_fBICYCLE_MAX_FORWARDNESS_SLOW);
|
|
}
|
|
|
|
CTaskMotionOnBicycle* pBicycleTask = static_cast<CTaskMotionOnBicycle*>(pPlayerPed->GetPedIntelligence()->FindTaskActiveMotionByType(CTaskTypes::TASK_MOTION_ON_BICYCLE));
|
|
static dev_float MIN_SPEED_FOR_MOVE_START = 0.2f;
|
|
if (fSpeedRatio < MIN_SPEED_FOR_MOVE_START && pBicycleTask && pBicycleTask->GetState() == CTaskMotionOnBicycle::State_Still)
|
|
{
|
|
m_uStartTime = 0;
|
|
m_bStartFinished = false;
|
|
}
|
|
else
|
|
{
|
|
m_uStartTime += fwTimer::GetTimeStepInMilliseconds();
|
|
}
|
|
|
|
// Forwardness between -1 and 1
|
|
float fSpeed = DotProduct(vRelativeVelocity, VEC3V_TO_VECTOR3(pBike->GetTransform().GetB()));
|
|
float fAccelButton = 0.0f;
|
|
|
|
float fSprintResult = pPlayerPed->GetPlayerInfo()->ControlButtonSprint(CPlayerInfo::SPRINT_ON_BICYCLE, true);
|
|
//Displayf("Sprint Result: %.2f", fSprintResult);
|
|
|
|
bool bIsTrackStanding = false;
|
|
bool bIsTransitioning = false;
|
|
const CBicycleInfo* pBicycleInfo = pBike->GetLayoutInfo()->GetBicycleInfo();
|
|
const bool bIsFixie = pBicycleInfo && pBicycleInfo->GetIsFixieBike();
|
|
TUNE_GROUP_BOOL(BICYCLE_TUNE, ALLOW_FORCED_BRAKE, true);
|
|
if (ALLOW_FORCED_BRAKE && pBicycleTask && pBicycleInfo)
|
|
{
|
|
if (pBicycleTask->GetState() == CTaskMotionOnBicycle::State_StillToSit && pBicycleTask->GetTimeInState() < pBicycleInfo->GetMinForcedInitialBrakeTime())
|
|
{
|
|
if (vRelativeVelocity.Mag2() <= rage::square(CTaskMotionOnBicycle::ms_Tunables.m_MaxSpeedForStill))
|
|
{
|
|
bForceBrake = true;
|
|
}
|
|
}
|
|
else if (pBicycleTask->GetSubTask() && pBicycleInfo->GetCanTrackStand())
|
|
{
|
|
const s32 iBicycleControllerState = pBicycleTask->GetSubTask()->GetState();
|
|
bIsTrackStanding = iBicycleControllerState == CTaskMotionOnBicycleController::State_TrackStand;
|
|
const bool bIsFixieSkidding = iBicycleControllerState == CTaskMotionOnBicycleController::State_FixieSkid;
|
|
bIsTransitioning = iBicycleControllerState == CTaskMotionOnBicycleController::State_ToTrackStandTransition;
|
|
if (bIsFixie)
|
|
{
|
|
if ((bIsTrackStanding || bIsFixieSkidding || bIsTransitioning))
|
|
{
|
|
bForceBrake = true;
|
|
}
|
|
}
|
|
else if (bIsTrackStanding && fSprintResult == 0.0f)
|
|
{
|
|
bForceBrake = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Script-specified disabling of sprinting gives a max movespeed of 1.0f (running)
|
|
if(pPlayerPed->GetPlayerInfo()->GetPlayerDataPlayerSprintDisabled())
|
|
fSprintResult = Min(fSprintResult, 1.0f);
|
|
|
|
bool bStartingToRide = false;
|
|
|
|
const bool bIgnoreInAirCheck = false;
|
|
const bool bPreparingToBunnyHop = CTaskMotionOnBicycle::WantsToJump(*pPlayerPed, *pBike, bIgnoreInAirCheck);
|
|
bool bCyclingPreventedDueToDriveby = pPlayerPed->GetPedResetFlag(CPED_RESET_FLAG_IsDoingDriveby);
|
|
if (bCyclingPreventedDueToDriveby)
|
|
{
|
|
const CVehicleDriveByInfo* pVehicleDriveByInfo = CVehicleMetadataMgr::GetVehicleDriveByInfoFromPed(pPlayerPed);
|
|
if (pVehicleDriveByInfo && pVehicleDriveByInfo->GetUseSpineAdditive())
|
|
{
|
|
bCyclingPreventedDueToDriveby = false;
|
|
}
|
|
}
|
|
float fFrontBrakeButton = (!bPreparingToBunnyHop && bCyclingPreventedDueToDriveby) ? 1.0f : pControl->GetVehiclePushbikeFrontBrake().GetNorm01();
|
|
float fRearBrakeButton = (!bPreparingToBunnyHop && bCyclingPreventedDueToDriveby) ? 1.0f : pControl->GetVehiclePushbikeRearBrake().GetNorm01();// TODO: Might want to make this a new button rear brake button
|
|
|
|
// We make the fixie only apply the front brake, so when we do the fixie skid we do the rear too
|
|
bool bAbleToReverse = true;
|
|
if (bIsFixie)
|
|
{
|
|
if (pControl->GetVehiclePushbikeFrontBrake().IsDown() && pControl->GetVehicleBrake().IsDown())
|
|
{
|
|
fFrontBrakeButton = 1.0f;
|
|
fRearBrakeButton = 1.0f;
|
|
bAbleToReverse = false;
|
|
}
|
|
else
|
|
{
|
|
if (fRearBrakeButton > fFrontBrakeButton)
|
|
{
|
|
bAbleToReverse = false;
|
|
fFrontBrakeButton = fRearBrakeButton;
|
|
}
|
|
fRearBrakeButton = 0.0f;
|
|
}
|
|
}
|
|
else if (bIsTrackStanding || bIsTransitioning)
|
|
{
|
|
bAbleToReverse = false;
|
|
}
|
|
|
|
float fBrakeButton = fFrontBrakeButton;
|
|
|
|
if (fSprintResult >= 1.0f && fBrakeButton == 0.0f)
|
|
{
|
|
if (bStartingToRide)
|
|
{
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, MIN_SPRINT_RESULT, 1.0f, 0.0f, 5.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, MAX_SPRINT_RESULT, 10.0f, 0.0f, 10.0f, 0.01f);
|
|
fAccelButton = rage::Clamp((fSprintResult - MIN_SPRINT_RESULT) / (MAX_SPRINT_RESULT - MIN_SPRINT_RESULT), 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fAccelButton = 1.0f;
|
|
}
|
|
}
|
|
|
|
//Only go into reverse if both brakes are pressed.
|
|
TUNE_BOOL(ENABLE_DUAL_BRAKE_REVERSE, FALSE);
|
|
|
|
float fReverse = pControl->GetVehicleBrake().GetNorm01();
|
|
float fBackwardness = fRearBrakeButton;
|
|
if(fFrontBrakeButton > fRearBrakeButton)
|
|
{
|
|
fBackwardness = fFrontBrakeButton;
|
|
}
|
|
|
|
if (fSprintResult >= 1.0f && fRearBrakeButton == 1.0f)
|
|
{
|
|
fSprintResult = 0.0f;
|
|
fAccelButton = 0.0f;
|
|
}
|
|
|
|
if(fReverse == 0.0f || bPreparingToBunnyHop)// if reverse is not pressed or we are preparing to bunny hop don't reverse.
|
|
{
|
|
bAbleToReverse = false;
|
|
}
|
|
|
|
float fForwardness = fAccelButton - fBackwardness;
|
|
|
|
if (bCyclingPreventedDueToDriveby || (bPreparingToBunnyHop && fForwardness > 0.0f))
|
|
{
|
|
fForwardness = 0.0f;
|
|
fSprintResult = 0.0f;
|
|
fAccelButton = 0.0f;
|
|
}
|
|
|
|
if (fForwardness > 0.0f && pBicycleTask && pBicycleTask->GetState() == CTaskMotionOnBicycle::State_Still)
|
|
{
|
|
bStartingToRide = true;
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, PUSH_START_STARTING_THROTTLE, 0.30f, 0.0f, 1.0f, 0.01f);
|
|
m_fStartingThrottle = PUSH_START_STARTING_THROTTLE;
|
|
}
|
|
|
|
if (!m_bStartFinished)
|
|
{
|
|
static dev_u32 VEHICLE_START_TIME = 2000;
|
|
if (m_uStartTime >= VEHICLE_START_TIME || fForwardness < 0.0f)
|
|
{
|
|
m_bStartFinished = true;
|
|
}
|
|
}
|
|
|
|
if (NetworkInterface::IsGameInProgress() && fSprintResult > 0.0f && fForwardness == 0.0f)
|
|
{
|
|
fForwardness = fSprintResult;
|
|
}
|
|
|
|
if (fSprintResult > 1.0f)
|
|
{
|
|
m_bClearedForwardness = false;
|
|
m_uLastSprintTime = fwTimer::GetTimeInMilliseconds();
|
|
|
|
if (!bStartingToRide)
|
|
{
|
|
const float fSprintNormalisedResult = fSprintResult / pPlayerPed->GetPlayerInfo()->GetButtonSprintResults(CPlayerInfo::SPRINT_ON_BICYCLE);
|
|
//Displayf("fSprintNormalisedResult: %.2f", fSprintNormalisedResult);
|
|
fForwardness = Sign(fForwardness) * rage::Clamp(fSprintNormalisedResult, 0.0f, 1.0f);
|
|
}
|
|
}
|
|
else if (fForwardness > 0.0f)
|
|
{
|
|
const u32 uLastSprintDiff = fwTimer::GetTimeInMilliseconds() - m_uLastSprintTime;
|
|
static dev_u32 uIgnoreSlownessResetTime = 500;
|
|
|
|
bool bClampForwardness = false;
|
|
|
|
if (uLastSprintDiff >= uIgnoreSlownessResetTime)
|
|
{
|
|
if (fSprintResult > 0.0f)
|
|
{
|
|
if (m_bStartFinished)
|
|
{
|
|
bClampForwardness = true;
|
|
}
|
|
|
|
if (!bStartingToRide)
|
|
{
|
|
const float fSprintNormalisedResult = fSprintResult / pPlayerPed->GetPlayerInfo()->GetButtonSprintResults(CPlayerInfo::SPRINT_ON_BICYCLE);
|
|
//Displayf("fSprintNormalisedResult: %.2f", fSprintNormalisedResult);
|
|
fForwardness = Sign(fForwardness) * rage::Clamp(fSprintNormalisedResult, 0.0f, 1.0f);
|
|
}
|
|
}
|
|
else if (m_bStartFinished)
|
|
{
|
|
bClampForwardness = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bStartingToRide)
|
|
{
|
|
if (pBicycleTask && pBicycleTask->GetState() == CTaskMotionOnBicycle::State_StillToSit)
|
|
{
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, STILL_TO_SIT_FORWARD, 0.2f, 0.0f, 1.0f, 0.01f);
|
|
fForwardness = STILL_TO_SIT_FORWARD;
|
|
}
|
|
else
|
|
{
|
|
fForwardness = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bClampForwardness)
|
|
{
|
|
float fSpeed = pBike->GetVelocityIncludingReferenceFrame().Mag();
|
|
fSpeed /=fMaxSpeed;
|
|
|
|
//fSpeed *= SPEED_MULT;
|
|
//static dev_float MAX_SPEED = 0.6f;
|
|
//if (fSpeed > MAX_SPEED || (!m_bClearedForwardness && m_fMaxBicycleForwardnessSlowCycle >= ms_fBICYCLE_MAX_FORWARDNESS_SLOW))
|
|
|
|
if ((!m_bClearedForwardness && m_fMaxBicycleForwardnessSlowCycle >= ms_fBICYCLE_MAX_FORWARDNESS_SLOW))
|
|
{
|
|
m_fMaxBicycleForwardnessSlowCycle = ms_fBICYCLE_MAX_FORWARDNESS_SLOW;
|
|
m_bClearedForwardness = true;
|
|
}
|
|
|
|
fForwardness = Sign(fForwardness) * rage::Clamp(Abs(fForwardness), 0.0f, ms_fBICYCLE_MAX_FORWARDNESS_SLOW);
|
|
|
|
if (fForwardness >= 0.0f)
|
|
{
|
|
fForwardness = Min(m_fMaxBicycleForwardnessSlowCycle, fForwardness);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Displayf("m_fMaxBicycleForwardnessSlowCycle: %.2f, fForwardness: %.2f", m_fMaxBicycleForwardnessSlowCycle, fForwardness);
|
|
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, PUSH_START_THROTTLE_APPROACH_RATE, 0.6f, 0.0f, 5.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, MAX_THROTTLE_PUSH_START, 0.85f, 0.0f, 1.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, MIN_FORWARDNESS, 0.2f, 0.0f, 1.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(BICYCLE_TUNE, MIN_FORWARDNESS_SETTING_OFF, 0.2f, 0.0f, 1.0f, 0.01f);
|
|
bool bSettingOff = (pBicycleTask && ((pBicycleTask->GetState() == CTaskMotionOnBicycle::State_Still && fForwardness > 0.0f) || pBicycleTask->GetState() == CTaskMotionOnBicycle::State_StillToSit)) ? true : false;
|
|
if (m_bStartFinished)
|
|
{
|
|
bSettingOff = false;
|
|
}
|
|
|
|
if(bForceBrake)
|
|
{
|
|
pBike->SetBrake(1.0f);
|
|
pBike->SetThrottle(0.0f);
|
|
pBike->SetRearBrake(1.0f);
|
|
}
|
|
else if(!pBike->m_nVehicleFlags.bEngineOn && pBike->m_nVehicleFlags.bIsDrowning)
|
|
{
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetThrottle(0.0f);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
else if(pBike->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_PuttingOnHelmet)
|
|
/*pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_PUT_ON_HELMET)*/)
|
|
{
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetThrottle(0.0f);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
// We've stopped. Go where we want to go.
|
|
else if (rage::Abs(fSpeed) < 0.5f)
|
|
{
|
|
// if both brake and throttle are held down, do a burn out
|
|
if(fAccelButton > 0.6f && fBrakeButton > 0.6f && pBike->GetVehicleType() != VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
pBike->SetThrottle(fAccelButton);
|
|
pBike->SetBrake(fBrakeButton);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
else
|
|
{
|
|
if (bSettingOff || fAccelButton > 0.0f)
|
|
{
|
|
if (bSettingOff)
|
|
{
|
|
rage::Approach(m_fStartingThrottle, MAX_THROTTLE_PUSH_START, PUSH_START_THROTTLE_APPROACH_RATE, fwTimer::GetTimeStep());
|
|
fForwardness = m_fStartingThrottle;
|
|
}
|
|
else
|
|
{
|
|
fForwardness = rage::Clamp(fForwardness, MIN_FORWARDNESS, 1.0f);
|
|
}
|
|
}
|
|
|
|
if(!bAbleToReverse && fForwardness <= 0.0f)
|
|
{
|
|
fForwardness = 0.0f;// prevent the player reversing if they are not supposed to.
|
|
}
|
|
|
|
pBike->SetThrottle(fForwardness);
|
|
|
|
if(fForwardness != 0.0f)
|
|
{
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
else//make sure we don't roll backwards.
|
|
{
|
|
pBike->SetBrake(1.0f);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if both brake and throttle are held down, do a burn out
|
|
if(fAccelButton > 0.6f && fBrakeButton > 0.6f && pBike->GetVehicleType() != VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
pBike->SetThrottle(fAccelButton);
|
|
pBike->SetBrake(fBrakeButton);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
// going forwards
|
|
else if (fSpeed >= 0.0f)
|
|
{
|
|
// we want to go forwards so go faster
|
|
if (fForwardness >= 0.0f)
|
|
{
|
|
if (bSettingOff || fAccelButton > 0.0f || (!m_bStartFinished && fForwardness > 0.0f))
|
|
{
|
|
if (bSettingOff || !m_bStartFinished)
|
|
{
|
|
rage::Approach(m_fStartingThrottle, MAX_THROTTLE_PUSH_START, PUSH_START_THROTTLE_APPROACH_RATE, fwTimer::GetTimeStep());
|
|
fForwardness = m_fStartingThrottle;
|
|
}
|
|
else
|
|
{
|
|
fForwardness = rage::Clamp(fForwardness, MIN_FORWARDNESS, 1.0f);
|
|
}
|
|
}
|
|
pBike->SetThrottle(fForwardness);
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
// we don't want to go forwards - so brake
|
|
else
|
|
{
|
|
pBike->SetThrottle(0.0f);
|
|
if(fFrontBrakeButton > 0.0f)
|
|
{
|
|
pBike->SetBrake(-fForwardness);
|
|
pBike->SetRearBrake(fRearBrakeButton);
|
|
}
|
|
else
|
|
{
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetRearBrake(fRearBrakeButton);
|
|
}
|
|
|
|
}
|
|
}
|
|
// going backwards
|
|
else
|
|
{
|
|
// want to go forwards
|
|
static dev_float sfMaxReverseSpeed = -3.0f;
|
|
|
|
if(fSpeed < sfMaxReverseSpeed)
|
|
{
|
|
pBike->SetThrottle(0.0f);
|
|
pBike->SetBrake(1.0f);
|
|
pBike->SetRearBrake(1.0f);
|
|
}
|
|
else if (fForwardness >= 0.0f)
|
|
{
|
|
// let us sit on the gas pedal if we're trying to get up a slope but sliding back
|
|
if(pBike->GetThrottle() > 0.5f && fSpeed > -10.0f)
|
|
{
|
|
pBike->SetThrottle(fForwardness);
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
// oh dear we're sliding back too fast, go for the brakes
|
|
else
|
|
{
|
|
pBike->SetThrottle(0.0f);
|
|
|
|
if(fFrontBrakeButton > 0.0f)
|
|
{
|
|
pBike->SetBrake(fForwardness);
|
|
pBike->SetRearBrake(fRearBrakeButton);
|
|
}
|
|
else
|
|
{
|
|
pBike->SetBrake(1.0f);
|
|
pBike->SetRearBrake(fRearBrakeButton);
|
|
}
|
|
}
|
|
}
|
|
// we want to go backwards, so apply the gas
|
|
else if(bAbleToReverse)
|
|
{
|
|
pBike->SetThrottle(fForwardness);
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetRearBrake(0.0f);
|
|
}
|
|
else
|
|
{
|
|
pBike->SetThrottle(0.0f);
|
|
if(fFrontBrakeButton > 0.0f)
|
|
{
|
|
pBike->SetBrake(-fForwardness);
|
|
pBike->SetRearBrake(fRearBrakeButton);
|
|
}
|
|
else
|
|
{
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetRearBrake(fRearBrakeButton);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveBoat
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveBoat::CTaskVehiclePlayerDriveBoat() :
|
|
CTaskVehiclePlayerDrive()
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_BOAT);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveBoat::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
//Assert(dynamic_cast<CBoat*>(pVehicle));
|
|
//CBoat *pBoat = static_cast<CBoat*>(pVehicle);
|
|
CBoatHandling* pHandling = pVehicle->GetBoatHandling();
|
|
|
|
Assert(pPlayerPed && (pPlayerPed->IsControlledByLocalPlayer()));
|
|
|
|
CAnchorHelper* anchorHelper = NULL;
|
|
if( pVehicle->InheritsFromBoat() )
|
|
{
|
|
anchorHelper = &static_cast< CBoat* >( pVehicle )->GetAnchorHelper();
|
|
}
|
|
else if( pVehicle->InheritsFromAmphibiousAutomobile() )
|
|
{
|
|
anchorHelper = &static_cast< CAmphibiousAutomobile* >( pVehicle )->GetAnchorHelper();
|
|
}
|
|
|
|
|
|
// make sure anchor is cleared in this state
|
|
if( anchorHelper->IsAnchored() && !anchorHelper->ShouldForcePlayerBoatToRemainAnchored())
|
|
anchorHelper->Anchor(false);
|
|
|
|
if( pPlayerPed->GetIsArrested() || pPlayerPed->IsInjured() )
|
|
{
|
|
pVehicle->SetThrottle(0.0f);
|
|
pVehicle->SetBrake(0.0f);
|
|
return;
|
|
}
|
|
|
|
float fForwardness = pControl->GetVehicleAccelerate().GetNorm01() - pControl->GetVehicleBrake().GetNorm01();
|
|
if( !pVehicle->m_nVehicleFlags.bEngineOn && pVehicle->m_nVehicleFlags.bIsDrowning )
|
|
{
|
|
fForwardness = 0.0f;
|
|
}
|
|
|
|
pVehicle->SetThrottle( fForwardness );
|
|
pVehicle->SetBrake( 0.0f );
|
|
|
|
float fSteerMult = 1.0f;
|
|
// handBrake tries to steer twice as tightly
|
|
if( pVehicle->GetIsHandBrakePressed( pControl ) )
|
|
{
|
|
fSteerMult = 1.3f;
|
|
}
|
|
|
|
// progressive steering
|
|
pControl->SetVehicleSteeringExclusive();
|
|
|
|
float fDesiredInput = -pControl->GetVehicleSteeringLeftRight().GetNorm() * fSteerMult;
|
|
float fDesiredPitchInput = pControl->GetVehicleSteeringUpDown().GetNorm();
|
|
float fSteerSmoothRate = ms_fBOAT_STEER_SMOOTH_RATE;
|
|
float fPitchSmoothRate = ms_fBOAT_PITCH_FWD_SMOOTH_RATE;
|
|
#if RSG_PC
|
|
const float c_fMouseAdjustmentScaleForBoats = c_fMouseAdjustmentScale * 3.0f; // boats steering is unresponsive, so turn it up.
|
|
if( pControl->GetVehicleSteeringLeftRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS )
|
|
{
|
|
fDesiredInput *= c_fMouseAdjustmentScaleForBoats;
|
|
}
|
|
if( pControl->GetVehicleSteeringUpDown().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS )
|
|
{
|
|
fDesiredPitchInput *= c_fMouseAdjustmentScaleForBoats;
|
|
}
|
|
#endif
|
|
#if USE_SIXAXIS_GESTURES
|
|
if(CControlMgr::GetPlayerPad() && CPadGestureMgr::GetMotionControlEnabled(CPadGestureMgr::MC_TYPE_BOAT))
|
|
{
|
|
CPadGesture* gesture = CControlMgr::GetPlayerPad()->GetPadGesture();
|
|
if(gesture)
|
|
{
|
|
if(fDesiredInput == 0.0f && pControl->GetVehicleSteeringLeftRight().IsEnabled())
|
|
{
|
|
fDesiredInput = -gesture->GetPadRollInRange(CBoat::MOTION_CONTROL_ROLL_MIN,CBoat::MOTION_CONTROL_ROLL_MAX,true) * fSteerMult;
|
|
fSteerSmoothRate = CVehicle::ms_fDefaultMotionContolSteerSmoothRate;
|
|
|
|
}
|
|
if(fDesiredPitchInput == 0.0f && pControl->GetVehicleSteeringUpDown().IsEnabled())
|
|
{
|
|
fDesiredPitchInput = gesture->GetPadPitchInRange(CBoat::MOTION_CONTROL_PITCH_MIN,CBoat::MOTION_CONTROL_PITCH_MAX,true,true);
|
|
fPitchSmoothRate = CVehicle::ms_fDefaultMotionContolSteerSmoothRate * fPitchSmoothRate/fSteerSmoothRate;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pHandling->SetPitchFwdInput( pHandling->GetPitchFwdInput() +
|
|
( fDesiredPitchInput - pHandling->GetPitchFwdInput()) * rage::Min( 1.0f, fPitchSmoothRate * fwTimer::GetTimeStep() ) );
|
|
|
|
pVehicle->m_fSteerInput += ( fDesiredInput - pVehicle->m_fSteerInput) * rage::Min( 1.0f, fSteerSmoothRate *fwTimer::GetTimeStep() );
|
|
pVehicle->m_fSteerInput = Clamp( pVehicle->m_fSteerInput, -fSteerMult, fSteerMult );
|
|
|
|
// use non-linear steering - squared
|
|
float fVal = 0.0f;
|
|
if( pVehicle->m_fSteerInput >= 0 )
|
|
{
|
|
fVal = pVehicle->m_fSteerInput * pVehicle->m_fSteerInput;
|
|
}
|
|
else
|
|
{
|
|
fVal = -pVehicle->m_fSteerInput * pVehicle->m_fSteerInput;
|
|
}
|
|
|
|
pVehicle->SetSteerAngle( fVal * pVehicle->pHandling->m_fSteeringLock );
|
|
|
|
//Check if heli has light and if it needs turning on/off
|
|
if( pControl->GetVehicleHeadlight().IsPressed() && pPlayerPed->GetPlayerInfo()->GetCanUseSearchLight() )
|
|
{
|
|
CVehicleWeaponMgr* pWeaponMgr = pVehicle->GetVehicleWeaponMgr();
|
|
if(pWeaponMgr)
|
|
{
|
|
for( int i = 0; i < pWeaponMgr->GetNumVehicleWeapons(); i++ )
|
|
{
|
|
CVehicleWeapon* pWeapon = pWeaponMgr->GetVehicleWeapon( i );
|
|
|
|
if( pWeapon && pWeapon->GetType() == VGT_SEARCHLIGHT )
|
|
{
|
|
CSearchLight* pSearchlight = static_cast<CSearchLight*>( pWeapon );
|
|
pSearchlight->Fire( pPlayerPed, pVehicle, VEC3_ZERO );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveBoat::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveBoat::ProcessDriverInputsForPlayerOnExit( CVehicle* pVehicle )
|
|
{
|
|
Assert(pVehicle);
|
|
|
|
// GTAV - B*1784768 - After the player driving the boat died in the mission 'Dry Docking', the boat continued to drive with no way of stopping it.
|
|
// reset the input when the player stops driving the vehicle.
|
|
CBoat *pBoat = static_cast<CBoat*>(pVehicle);
|
|
|
|
pBoat->SetThrottle( 0.0f );
|
|
pBoat->SetBrake( 0.25f );
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveTrain
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveTrain::CTaskVehiclePlayerDriveTrain() :
|
|
CTaskVehiclePlayerDrive()
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_TRAIN);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveTrain::ProcessDriverInputsForPlayerOnUpdate(CPed* /*pPlayerPed*/, CControl* /*pControl*/, CVehicle* /*pVehicle*/)
|
|
{
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveTrain::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveSubmarine
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveSubmarine::CTaskVehiclePlayerDriveSubmarine() :
|
|
CTaskVehiclePlayerDrive()
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_SUBMARINE);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarine::ProcessDriverInputsForPlayerOnEnter(CVehicle* UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarine::ProcessDriverInputsForPlayerOnExit (CVehicle* pVehicle)
|
|
{
|
|
Assert(pVehicle);
|
|
|
|
CSubmarineHandling* pSubHandling = pVehicle->GetSubHandling();
|
|
|
|
// GTAV B*1815137 - crash when choosing to load a quick save while in a submarine
|
|
// The crash is caused by this being called because CVehicle destructor calls CPedIntelligence::FlushImmediately
|
|
// which aborts all the tasks and calls this.
|
|
// This is not the best way to fix this.
|
|
if( pSubHandling )
|
|
{
|
|
//Reset inputs
|
|
pSubHandling->SetPitchControl(0.0f);
|
|
pSubHandling->SetYawControl(0.0f);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarine::ProcessDriverInputsForPlayerOnUpdate(CPed *UNUSED_PARAM(pPlayerPed), CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
CSubmarineHandling* pSubHandling = pVehicle->GetSubHandling();
|
|
|
|
physicsAssertf(pControl,"Unexpected NULL control");
|
|
|
|
// Make sure any anchor is cleared in this state unless specifically requested.
|
|
if(pSubHandling->GetAnchorHelper().IsAnchored() && !pSubHandling->GetAnchorHelper().ShouldForcePlayerBoatToRemainAnchored())
|
|
{
|
|
pSubHandling->GetAnchorHelper().Anchor(false);
|
|
}
|
|
|
|
Vector2 vPlayerInput;
|
|
|
|
pControl->SetVehicleSubSteeringExclusive();
|
|
vPlayerInput.y = pControl->GetVehicleSubPitchUpDown().GetNorm();
|
|
vPlayerInput.x = -pControl->GetVehicleSubTurnLeftRight().GetNorm();
|
|
#if RSG_PC
|
|
if(pControl->GetVehicleSubTurnLeftRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
vPlayerInput.x *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(pControl->GetVehicleSubPitchUpDown().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS)
|
|
{
|
|
vPlayerInput.y *= c_fMouseAdjustmentScale;
|
|
}
|
|
#endif
|
|
|
|
CFlightModelHelper::MakeCircularInputSquare(vPlayerInput);
|
|
|
|
vPlayerInput.x += (pControl->GetVehicleSubTurnHardLeft().GetNorm01() - pControl->GetVehicleSubTurnHardRight().GetNorm01());
|
|
|
|
pSubHandling->SetPitchControl(vPlayerInput.y);
|
|
pSubHandling->SetYawControl(vPlayerInput.x);
|
|
|
|
float fThrottle = pControl->GetVehicleSubThrottleUp().GetNorm01() - pControl->GetVehicleSubThrottleDown().GetNorm01();
|
|
|
|
fThrottle = rage::Clamp(fThrottle,-1.0f,1.0f);
|
|
|
|
pVehicle->SetThrottle(fThrottle);
|
|
|
|
// Don't want to control ascending or descending anymore
|
|
pSubHandling->SetDiveControl(pControl->GetVehicleSubAscend().GetNorm01() - pControl->GetVehicleSubDescend().GetNorm01());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarine::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveSubmarineCar
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveSubmarineCar::CTaskVehiclePlayerDriveSubmarineCar() :
|
|
CTaskVehiclePlayerDriveAutomobile()
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_SUBMARINECAR);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarineCar::ProcessDriverInputsForPlayerOnEnter(CVehicle* UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveSubmarineCar::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
|
|
|
|
//player is always dirty
|
|
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
|
|
|
|
FSM_Begin
|
|
//
|
|
FSM_State(State_Drive)
|
|
FSM_OnEnter
|
|
Drive_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return Drive_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_NoDriver)
|
|
FSM_OnEnter
|
|
NoDriver_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return NoDriver_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_SubmarineMode)
|
|
FSM_OnEnter
|
|
SubmarineMode_OnEnter();
|
|
FSM_OnUpdate
|
|
return SubmarineMode_OnUpdate(pVehicle);
|
|
FSM_End
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarineCar::SubmarineMode_OnEnter ()
|
|
{
|
|
SetNewTask( rage_new CTaskVehiclePlayerDriveSubmarine() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveSubmarineCar::SubmarineMode_OnUpdate( CVehicle* pVehicle )
|
|
{
|
|
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( pVehicle );
|
|
|
|
if( !submarineCar ||
|
|
!submarineCar->IsInSubmarineMode() )
|
|
{
|
|
SetState( State_Drive );
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarineCar::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
// Car stuff
|
|
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( pVehicle );
|
|
|
|
if( !submarineCar->IsInSubmarineMode() )
|
|
{
|
|
CTaskVehiclePlayerDriveAutomobile::ProcessDriverInputsForPlayerOnUpdate( pPlayerPed, pControl, pVehicle );
|
|
}
|
|
else
|
|
{
|
|
SetState( State_SubmarineMode );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveSubmarineCar::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveAmphibiousAutomobile
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveAmphibiousAutomobile::CTaskVehiclePlayerDriveAmphibiousAutomobile() :
|
|
CTaskVehiclePlayerDriveAutomobile()
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_AMPHIBIOUS_AUTOMOBILE);
|
|
}
|
|
|
|
|
|
CTaskVehiclePlayerDriveSubmarineCar::~CTaskVehiclePlayerDriveSubmarineCar()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveAmphibiousAutomobile::ProcessDriverInputsForPlayerOnEnter(CVehicle* UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveAmphibiousAutomobile::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
|
|
|
|
//player is always dirty
|
|
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
|
|
|
|
FSM_Begin
|
|
//
|
|
FSM_State(State_Drive)
|
|
FSM_OnEnter
|
|
Drive_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return Drive_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_NoDriver)
|
|
FSM_OnEnter
|
|
NoDriver_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return NoDriver_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_BoatMode)
|
|
FSM_OnEnter
|
|
BoatMode_OnEnter();
|
|
FSM_OnUpdate
|
|
return BoatMode_OnUpdate(pVehicle);
|
|
FSM_End
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveAmphibiousAutomobile::BoatMode_OnEnter()
|
|
{
|
|
SetNewTask( rage_new CTaskVehiclePlayerDriveBoat() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveAmphibiousAutomobile::BoatMode_OnUpdate( CVehicle* pVehicle )
|
|
{
|
|
CAmphibiousAutomobile* amphibiousVehicle = static_cast< CAmphibiousAutomobile* >( pVehicle );
|
|
|
|
if( !amphibiousVehicle )
|
|
{
|
|
SetState( State_Drive );
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveAmphibiousAutomobile::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
// Car stuff
|
|
CAmphibiousAutomobile* amphibiousVehicle = static_cast< CAmphibiousAutomobile* >( pVehicle );
|
|
CAnchorHelper& anchorHelper = amphibiousVehicle->GetAnchorHelper();
|
|
|
|
// make sure anchor is cleared in this state
|
|
if( anchorHelper.IsAnchored() && !anchorHelper.ShouldForcePlayerBoatToRemainAnchored())
|
|
{
|
|
anchorHelper.Anchor(false);
|
|
}
|
|
|
|
CTaskVehiclePlayerDriveAutomobile::ProcessDriverInputsForPlayerOnUpdate( pPlayerPed, pControl, pVehicle );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveAmphibiousAutomobile::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDrivePlane
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDrivePlane::CTaskVehiclePlayerDrivePlane() :
|
|
CTaskVehiclePlayerDrive()
|
|
#if RSG_PC
|
|
,m_MouseSteeringInput(false)
|
|
#endif
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_PLANE);
|
|
m_TimeOfLastLandingGearToggle = 0;
|
|
m_ThrottleControlHasBeenActive = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDrivePlane::Drive_OnEnter(CVehicle* pVehicle)
|
|
{
|
|
CTaskVehiclePlayerDrive::Drive_OnEnter(pVehicle);
|
|
m_ThrottleControlHasBeenActive = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
u32 ms_uTimeToHoldDoorControlButtonDown = 250;
|
|
#if USE_SIXAXIS_GESTURES
|
|
static dev_float sfZeroMotionThreshold = 0.1f;
|
|
#endif // USE_SIXAXIS_GESTURES
|
|
void CTaskVehiclePlayerDrivePlane::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CPlane*>(pVehicle));
|
|
CPlane* pPlane = static_cast<CPlane*>(pVehicle);
|
|
|
|
// If this is a seaplane it could be anchored. Make sure anchor is cleared in this state.
|
|
if(CSeaPlaneExtension* pSeaPlaneExtension = pPlane->GetExtension<CSeaPlaneExtension>())
|
|
{
|
|
if(pSeaPlaneExtension->GetAnchorHelper().IsAnchored() && !pSeaPlaneExtension->GetAnchorHelper().ShouldForcePlayerBoatToRemainAnchored())
|
|
{
|
|
pSeaPlaneExtension->GetAnchorHelper().Anchor(false);
|
|
}
|
|
}
|
|
|
|
float fSpeed = Abs(DotProduct(VEC3V_TO_VECTOR3(pPlane->GetTransform().GetB()), pVehicle->GetVelocityIncludingReferenceFrame()));
|
|
|
|
Assert(pPlayerPed && (pPlayerPed->IsControlledByLocalPlayer()));
|
|
|
|
float fDesiredThrottleControl = pControl->GetVehicleFlyThrottleUp().GetNorm01() - pControl->GetVehicleFlyThrottleDown().GetNorm01();
|
|
|
|
// GTAV - B*1754978 - If we're in flight we don't want the afterburner effect to cut out
|
|
// for a frame so if the throttle control has never been active set it to the previous stored value
|
|
if( !m_ThrottleControlHasBeenActive )
|
|
{
|
|
m_ThrottleControlHasBeenActive = pControl->GetVehicleFlyThrottleUp().IsEnabled();
|
|
|
|
if( !m_ThrottleControlHasBeenActive )
|
|
{
|
|
fDesiredThrottleControl = pVehicle->GetThrottle();
|
|
}
|
|
}
|
|
|
|
pPlane->SetThrottleControl(fDesiredThrottleControl);
|
|
|
|
TUNE_FLOAT(TEST_PLANE_PERC, 0.5f, 0.0f, 1.0f, 0.01f);
|
|
|
|
float fMotionPitchControl = 0.0f, fMotionRollControl = 0.0f, fMotionYawControl = 0.0f;
|
|
#if USE_SIXAXIS_GESTURES
|
|
if(CControlMgr::GetPlayerPad() && CPadGestureMgr::GetMotionControlEnabled(CPadGestureMgr::MC_TYPE_AIRCRAFT))
|
|
{
|
|
// Static values here can be tweaked in widgets
|
|
|
|
CPadGesture* pGesture = CControlMgr::GetPlayerPad()->GetPadGesture();
|
|
if(pGesture)
|
|
{
|
|
// Rescale the raw pitch input to custom range
|
|
// Values will be clamped below so don't bother getting this function to do it
|
|
if(pControl->GetVehicleFlyPitchUpDown().IsEnabled())
|
|
{
|
|
fMotionPitchControl = pGesture->GetPadPitchInRange(CPlane::MOTION_CONTROL_PITCH_MIN,CPlane::MOTION_CONTROL_PITCH_MAX,false,true);
|
|
if(fMotionPitchControl > -sfZeroMotionThreshold && fMotionPitchControl < sfZeroMotionThreshold)
|
|
{
|
|
fMotionPitchControl = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Rescale the raw roll too
|
|
// Values will be clamped below so don't bother getting this function to do it
|
|
if(pControl->GetVehicleFlyRollLeftRight().IsEnabled())
|
|
{
|
|
fMotionRollControl = pGesture->GetPadRollInRange(CPlane::MOTION_CONTROL_ROLL_MIN,CPlane::MOTION_CONTROL_ROLL_MAX,true);
|
|
if(fMotionRollControl > -sfZeroMotionThreshold && fMotionRollControl < sfZeroMotionThreshold)
|
|
{
|
|
fMotionRollControl = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Not currently used unless Extrapolate yaw is turned on in RAG
|
|
if(pControl->GetVehicleFlyYawRight().IsEnabled() || pControl->GetVehicleFlyYawLeft().IsEnabled())
|
|
{
|
|
fMotionYawControl = pGesture->GetPadYaw() * CPlane::MOTION_CONTROL_YAW_MULT;;
|
|
}
|
|
}
|
|
}
|
|
//else
|
|
#endif // USE_SIXAXIS_GESTURES
|
|
pControl->SetVehicleFlySteeringExclusive();
|
|
|
|
ioValue::ReadOptions readOptions = ioValue::ALWAYS_DEAD_ZONE;
|
|
|
|
#if RSG_PC
|
|
const bool bScaledAxisRollInput = pControl->GetVehicleFlyRollLeftRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS;
|
|
const bool bScaledAxisPitchnput = pControl->GetVehicleFlyPitchUpDown().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS;
|
|
const bool bScaledAxisYawInput = pControl->GetVehicleFlyYawLeft().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS
|
|
|| pControl->GetVehicleFlyYawRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS;
|
|
if(bScaledAxisRollInput || bScaledAxisPitchnput)
|
|
{
|
|
readOptions = ioValue::DEFAULT_OPTIONS;
|
|
}
|
|
#endif
|
|
|
|
float fPitchControl = fMotionPitchControl + (pControl->GetVehicleFlyPitchUpDown().GetNorm(readOptions));
|
|
fPitchControl += pPlane->m_fPitchInputBias;
|
|
|
|
float fRollControl = (fMotionRollControl + pControl->GetVehicleFlyRollLeftRight().GetNorm(readOptions)*(1.0f - TEST_PLANE_PERC*rage::Abs(pPlane->GetPitchControl() ) ) );
|
|
fRollControl += pPlane->m_fRollInputBias;
|
|
|
|
// Allow the script to manipulate yaw control
|
|
// Use Abs on the norm values as mouse mappings can be inverted, this is due to the way the mappings work
|
|
// (ideally we would alter this but its safer to fix the issue this way).
|
|
float fYawControl = fMotionYawControl + Abs(pControl->GetVehicleFlyYawRight().GetNorm()) - Abs(pControl->GetVehicleFlyYawLeft().GetNorm());
|
|
fYawControl += pPlane->m_fSteerInputBias;
|
|
|
|
#if RSG_PC
|
|
float timeStep = fwTimer::GetTimeStep();
|
|
|
|
if(pControl->WasKeyboardMouseLastKnownSource())
|
|
{
|
|
if(bScaledAxisRollInput || bScaledAxisPitchnput || bScaledAxisYawInput)
|
|
{
|
|
if(bScaledAxisRollInput)
|
|
{
|
|
fRollControl *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(bScaledAxisPitchnput)
|
|
{
|
|
fPitchControl *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(bScaledAxisYawInput)
|
|
{
|
|
fYawControl *= c_fMouseAdjustmentScale;
|
|
}
|
|
m_MouseSteeringInput = true;
|
|
}
|
|
else if(pControl->GetVehicleFlyRollLeftRight().GetSource().m_Device == IOMS_MKB_AXIS || pControl->GetVehicleFlyPitchUpDown().GetSource().m_Device == IOMS_MKB_AXIS)// Keyboard steering input
|
|
{
|
|
m_MouseSteeringInput = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// HACK TO FIX GTAV B*2870991 - The input returns the control immediately back to joypad control when playing on mouse, even when there isn't one plugged in
|
|
if( !m_MouseSteeringInput ||
|
|
Abs( fRollControl ) > c_fMouseTollerance ||
|
|
Abs( fPitchControl ) > c_fMouseTollerance ||
|
|
Abs( fYawControl ) > c_fMouseTollerance )
|
|
{
|
|
m_MouseSteeringInput = false;
|
|
}
|
|
}
|
|
|
|
bool bMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Vehicle;
|
|
bool bCameraMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Camera;
|
|
|
|
if(m_MouseSteeringInput && ((bMouseFlySteering && !CControlMgr::GetMainPlayerControl().GetDriveCameraToggleOn()) || (bCameraMouseFlySteering && CControlMgr::GetMainPlayerControl().GetDriveCameraToggleOn())))
|
|
{
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, PLANE_STEERING_DEADZONE_CENTERING_SPEED_ROLL, 4.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, PLANE_STEERING_DEADZONE_CENTERING_SPEED_PITCH, 0.6f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, PLANE_STEERING_DEADZONE_CENTERING_SPEED_YAW, 0.6f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, PLANE_STEERING_MULTIPLIER, 1.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, PLANE_STEERING_CENTER_ALLOWANCE, 0.001f, 0.0f, 1.0f, 0.0001f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, PLANE_STEERING_INPUT_DEADZONE, FLT_EPSILON, 0.0f, 1.0f, 0.0001f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, PLANE_STEERING_DELTA_MULT, 15.0f, 0.0f, 30.0f, 0.1f);
|
|
|
|
float fAutoCenterMult = CPauseMenu::GetMenuPreference(PREF_MOUSE_AUTOCENTER_PLANE)/10.0f;
|
|
|
|
//Roll
|
|
if( bScaledAxisRollInput )
|
|
{
|
|
if (fabs(fRollControl) <= PLANE_STEERING_INPUT_DEADZONE)
|
|
{
|
|
if(pPlane->GetRollControl() > 0.0f)
|
|
{
|
|
fRollControl = Clamp(pPlane->GetRollControl() - PLANE_STEERING_DEADZONE_CENTERING_SPEED_ROLL * fAutoCenterMult * timeStep, 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fRollControl = Clamp(pPlane->GetRollControl() + PLANE_STEERING_DEADZONE_CENTERING_SPEED_ROLL * fAutoCenterMult * timeStep,-1.0f, 0.0f);
|
|
}
|
|
}
|
|
else if( Sign(fRollControl) == Sign(pPlane->GetRollControl()) || rage::Abs(pPlane->GetRollControl()) <= PLANE_STEERING_CENTER_ALLOWANCE)
|
|
{
|
|
fRollControl = pPlane->GetRollControl() + (fRollControl * PLANE_STEERING_MULTIPLIER);
|
|
}
|
|
else
|
|
{
|
|
fRollControl = pPlane->GetRollControl() + (fRollControl - pPlane->GetRollControl()) * PLANE_STEERING_DELTA_MULT * timeStep;
|
|
}
|
|
}
|
|
|
|
//Pitch
|
|
if( bScaledAxisPitchnput )
|
|
{
|
|
if (fabs(fPitchControl) <= PLANE_STEERING_INPUT_DEADZONE)
|
|
{
|
|
if(pPlane->GetPitchControl() > 0.0f)
|
|
{
|
|
fPitchControl = Clamp(pPlane->GetPitchControl() - PLANE_STEERING_DEADZONE_CENTERING_SPEED_PITCH * fAutoCenterMult * timeStep, 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fPitchControl = Clamp(pPlane->GetPitchControl() + PLANE_STEERING_DEADZONE_CENTERING_SPEED_PITCH * fAutoCenterMult * timeStep,-1.0f, 0.0f);
|
|
}
|
|
}
|
|
else if( Sign(fPitchControl) == Sign(pPlane->GetPitchControl()) || rage::Abs(pPlane->GetPitchControl()) <= PLANE_STEERING_CENTER_ALLOWANCE)
|
|
{
|
|
fPitchControl = pPlane->GetPitchControl() + (fPitchControl * PLANE_STEERING_MULTIPLIER);
|
|
}
|
|
else
|
|
{
|
|
fPitchControl = pPlane->GetPitchControl() + (fPitchControl - pPlane->GetPitchControl()) * PLANE_STEERING_DELTA_MULT * timeStep;
|
|
}
|
|
}
|
|
|
|
//Yaw
|
|
if( bScaledAxisYawInput )
|
|
{
|
|
if (fabs(fYawControl) <= PLANE_STEERING_INPUT_DEADZONE)
|
|
{
|
|
if(pPlane->GetYawControl() > 0.0f)
|
|
{
|
|
fYawControl = Clamp(pPlane->GetYawControl() - PLANE_STEERING_DEADZONE_CENTERING_SPEED_YAW * fAutoCenterMult * timeStep, 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fYawControl = Clamp(pPlane->GetYawControl() + PLANE_STEERING_DEADZONE_CENTERING_SPEED_YAW * fAutoCenterMult * timeStep,-1.0f, 0.0f);
|
|
}
|
|
}
|
|
else if( Sign(fYawControl) == Sign(pPlane->GetYawControl()) || rage::Abs(pPlane->GetYawControl()) <= PLANE_STEERING_CENTER_ALLOWANCE)
|
|
{
|
|
fYawControl = pPlane->GetYawControl() + (fYawControl * PLANE_STEERING_MULTIPLIER);
|
|
}
|
|
else
|
|
{
|
|
fYawControl = pPlane->GetYawControl() + (fYawControl - pPlane->GetYawControl()) * PLANE_STEERING_DELTA_MULT * timeStep;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pPlane->SetPitchControl( fPitchControl );
|
|
pPlane->SetRollControl( fRollControl );
|
|
pPlane->SetYawControl( fYawControl );
|
|
|
|
dev_float fPlaneWheelSteeringSpeed = 1.0f;
|
|
float fDesiredSteerAngle = -pPlane->GetYawControl() * pPlane->pHandling->m_fSteeringLock * 0.02f*rage::Max(0.1f, 50.0f - fSpeed);
|
|
float fSteerAngleDiff = fDesiredSteerAngle - pPlane->GetSteerAngle();
|
|
fSteerAngleDiff = Clamp(fSteerAngleDiff, -fPlaneWheelSteeringSpeed * fwTimer::GetTimeStep(), fPlaneWheelSteeringSpeed * fwTimer::GetTimeStep());
|
|
pPlane->SetSteerAngle(pPlane->GetSteerAngle() + fSteerAngleDiff);
|
|
|
|
pPlane->SetYawControl(Clamp(pPlane->GetYawControl(), -1.0f, 1.0f));
|
|
pPlane->SetPitchControl(Clamp(pPlane->GetPitchControl(), -1.0f, 1.0f));
|
|
pPlane->SetRollControl(Clamp(pPlane->GetRollControl(), -1.0f, 1.0f));
|
|
|
|
|
|
// Make new undercarriage input (A on 360, X on ps3) toggle landing gear
|
|
// Only allow toggle when in the air
|
|
if(!pPlane->HasContactWheels())
|
|
{
|
|
if(pControl->GetVehicleFlyUndercarriage().IsPressed())
|
|
{
|
|
u32 currentTime = fwTimer::GetTimeInMilliseconds();
|
|
static bank_u32 minTimeForGearToggle = 500;
|
|
switch(pPlane->GetLandingGear().GetPublicState())
|
|
{
|
|
case CLandingGear::STATE_LOCKED_DOWN:
|
|
case CLandingGear::STATE_DEPLOYING:
|
|
if(currentTime > m_TimeOfLastLandingGearToggle + minTimeForGearToggle)
|
|
{
|
|
pPlane->GetLandingGear().ControlLandingGear(pVehicle,CLandingGear::COMMAND_RETRACT);
|
|
m_TimeOfLastLandingGearToggle = currentTime;
|
|
}
|
|
break;
|
|
case CLandingGear::STATE_LOCKED_UP:
|
|
case CLandingGear::STATE_RETRACTING:
|
|
if(currentTime > m_TimeOfLastLandingGearToggle + minTimeForGearToggle)
|
|
{
|
|
pPlane->GetLandingGear().ControlLandingGear(pVehicle,CLandingGear::COMMAND_DEPLOY);
|
|
m_TimeOfLastLandingGearToggle = currentTime;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bInputActivated = pControl->GetVehicleFlyVerticalFlight().IsPressed();
|
|
bool bOnlyToggleWhenComplete = pPlane->GetVehicleModelInfo()->GetVehicleFlag( CVehicleModelInfoFlags::FLAG_HAS_VERTICAL_FLIGHT_MODE );
|
|
bool bTula = MI_PLANE_TULA.IsValid() && pPlane->GetModelIndex() == MI_PLANE_TULA;
|
|
bool bAvenger = MI_PLANE_AVENGER.IsValid() && pPlane->GetModelIndex() == MI_PLANE_AVENGER;
|
|
if (bTula || bAvenger)
|
|
{
|
|
static u32 uMinTimeForTapMS = 250;
|
|
u32 timeForTapMS = u32( (float)uMinTimeForTapMS * ( (float)fwTimer::GetTimeStepInMilliseconds() / 33.0f ) );
|
|
timeForTapMS = Max( uMinTimeForTapMS, timeForTapMS );
|
|
bInputActivated = pControl->GetVehicleFlyVerticalFlight().IsReleased() && !pControl->GetVehicleFlyVerticalFlight().IsReleasedAfterHistoryHeldDown(timeForTapMS);
|
|
bOnlyToggleWhenComplete = true;
|
|
}
|
|
|
|
if( bAvenger && !pPlane->IsInAir( false ) ) // don't allow the avenger to toggle mode when on the ground
|
|
{
|
|
bInputActivated = false;
|
|
}
|
|
|
|
if(bInputActivated)
|
|
{
|
|
//Process whether the player wants to go to vertical take off mode
|
|
if(pPlane->GetVerticalFlightModeAvaliable() &&
|
|
( !bOnlyToggleWhenComplete ||
|
|
pPlane->GetDesiredVerticalFlightModeRatio() == pPlane->GetVerticalFlightModeRatio() ) )
|
|
{
|
|
if(pPlane->GetDesiredVerticalFlightModeRatio() >= 1.0f)
|
|
{
|
|
pPlane->SetDesiredVerticalFlightModeRatio( 0.0f );
|
|
((audPlaneAudioEntity*)pPlane->GetVehicleAudioEntity())->OnVerticalFlightModeChanged(0.0f);
|
|
}
|
|
else
|
|
{
|
|
pPlane->SetDesiredVerticalFlightModeRatio( 1.0f );
|
|
((audPlaneAudioEntity*)pPlane->GetVehicleAudioEntity())->OnVerticalFlightModeChanged(1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if RSG_PC
|
|
if (m_bExitButtonUp && SMultiplayerChat::GetInstance().ShouldDisableInputSinceChatIsTyping())
|
|
m_bExitButtonUp = false;
|
|
#endif
|
|
|
|
bool bForceBrake = false;
|
|
if( pControl->GetVehicleExit().IsDown() && m_bExitButtonUp )
|
|
{
|
|
pPlane->SetHandBrake(true);
|
|
if( !pPlane->CanPedJumpOutCar(pPlayerPed) )
|
|
{
|
|
bForceBrake = true;
|
|
if (pPlane->GetVelocityIncludingReferenceFrame().Mag2() < (0.2f*0.2f)) // If we're already static we don't want the brake light to light up. (causes ugly glitch)
|
|
{
|
|
pPlane->m_nVehicleFlags.bSuppressBrakeLight = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlane->SetHandBrake(false);
|
|
}
|
|
|
|
//#if 0 // Disable open/close rear door of cargo plane, titan. B* 1369017
|
|
#if __BANK
|
|
|
|
if( CVehicleFactory::ms_rearDoorsCanOpen )
|
|
{
|
|
// Process the rear ramp door
|
|
m_bDoorControlButtonUp |= pControl->GetVehicleRoof().IsUp();
|
|
|
|
if(m_bDoorControlButtonUp && pControl->GetVehicleRoof().HistoryHeldDown(ms_uTimeToHoldDoorControlButtonDown))
|
|
{
|
|
CCarDoor *pDoor = pVehicle->GetDoorFromId(VEH_BOOT);
|
|
if(pDoor == NULL)
|
|
{
|
|
pDoor = pVehicle->GetDoorFromId(VEH_DOOR_DSIDE_R); // Some planes have their rare door tagged with VEH_DOOR_DSIDE_R
|
|
}
|
|
if(pDoor && pDoor->GetIsIntact(pPlane))
|
|
{
|
|
if(pDoor->GetIsLatched(pPlane) || pDoor->GetTargetDoorRatio() < 0.01f)
|
|
{
|
|
pDoor->SetTargetDoorOpenRatio(1.0f, CCarDoor::DRIVEN_NORESET|CCarDoor::DRIVEN_SPECIAL);
|
|
}
|
|
else if(pDoor->GetTargetDoorRatio() > 0.99f)
|
|
{
|
|
pDoor->SetTargetDoorOpenRatio(0.0f, CCarDoor::DRIVEN_AUTORESET|CCarDoor::WILL_LOCK_DRIVEN);
|
|
}
|
|
}
|
|
m_bDoorControlButtonUp = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// GTAV FIX B*1773938 - Need to set the brake when in vertical flight
|
|
// mode so that the game doesn't think we're stuck
|
|
// This is the same as the rotary wing aircraft
|
|
bool bInVerticalFlightMode = false;
|
|
|
|
if( pPlane->GetVerticalFlightModeAvaliable() )
|
|
{
|
|
if( pPlane->GetVerticalFlightModeRatio() == 1.0f )
|
|
{
|
|
bInVerticalFlightMode = true;
|
|
pPlane->SetBrake(1.0f);
|
|
pPlane->SetThrottle(0.0f);
|
|
pPlane->SetHandBrake(false);
|
|
}
|
|
}
|
|
|
|
if( !bInVerticalFlightMode )
|
|
{
|
|
ProcessPlayerControlInputsForBrakeAndGasPedal(pControl,bForceBrake, pVehicle);
|
|
}
|
|
|
|
// process crop duster smoke vfx in MP
|
|
if (NetworkInterface::IsGameInProgress() &&
|
|
pVehicle->GetStatus()==STATUS_PLAYER &&
|
|
pVehicle->GetModelIndex()==MI_PLANE_DUSTER.GetModelIndex() &&
|
|
pControl->GetParachuteSmoke().IsDown())
|
|
{
|
|
g_vfxVehicle.UpdatePtFxPlaneSmoke(pVehicle, Color32(1.0f, 1.0f, 1.0f, 1.0f));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDrivePlane::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *UNUSED_PARAM(pVehicle))
|
|
{
|
|
//The following code prevents the plane from crashing when player brings up the phone internet. Commented it out per Ben's suggestion - NanM
|
|
|
|
// if (pVehicle->m_nVehicleFlags.bLimitSpeedWhenPlayerInactive)
|
|
// {
|
|
// // If the player has no control (cut scene for instance) we will hit the brakes.
|
|
// pVehicle->SetBrake(1.0f);
|
|
// pVehicle->SetHandBrake(true);
|
|
// pVehicle->SetThrottle(0.0f);
|
|
// CGameWorld::FindLocalPlayer()->GetPlayerInfo()->KeepAreaAroundPlayerClear();
|
|
// // Limit the velocity
|
|
// float Speed = pVehicle->GetVelocity().Mag();
|
|
//
|
|
// if (Speed > ms_fPLANE_MAXSPEEDNOW)
|
|
// {
|
|
// pVehicle->SetVelocity( pVehicle->GetVelocity() * (ms_fPLANE_MAXSPEEDNOW / Speed) );
|
|
// }
|
|
// }
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDrivePlane::ProcessPlayerControlInputsForBrakeAndGasPedal(CControl* pControl, bool bForceBrake, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CPlane*>(pVehicle));
|
|
CPlane *pPlane = static_cast<CPlane*>(pVehicle);
|
|
|
|
//get the vehicles reference frame velocity, so if they are driving on a plane we car work out the relative velocity rather then just using the absolute
|
|
// Forwardness between -1 and 1
|
|
float fSpeed = DotProduct(pVehicle->GetVelocityIncludingReferenceFrame(), VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetB()));
|
|
float fAccelButton = pControl->GetVehicleFlyThrottleUp().GetNorm01();
|
|
|
|
// GTAV - B*1754978 - If we're in flight we don't want the afterburner effect to cut out
|
|
// for a frame so if the throttle control has never been active set it to the previous stored value
|
|
if( !m_ThrottleControlHasBeenActive )
|
|
{
|
|
fAccelButton = pVehicle->GetThrottle();
|
|
fAccelButton = Clamp(fAccelButton, -1.0f, 1.0f);
|
|
}
|
|
|
|
float fBrakeButton = pControl->GetVehicleFlyThrottleDown().GetNorm01();
|
|
float fForwardness = fAccelButton - fBrakeButton;
|
|
fForwardness = Clamp(fForwardness, -1.0f, 1.0f);
|
|
|
|
if(bForceBrake)
|
|
{
|
|
pVehicle->SetBrake(1.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
else if(!pVehicle->m_nVehicleFlags.bEngineOn && pVehicle->m_nVehicleFlags.bIsDrowning)
|
|
{
|
|
pVehicle->SetBrake(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
else if(pVehicle->GetDriver()->GetPedResetFlag(CPED_RESET_FLAG_PuttingOnHelmet)
|
|
/*pVehicle->GetDriver() && pVehicle->GetDriver()->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_PUT_ON_HELMET)*/)
|
|
{
|
|
pVehicle->SetBrake(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
// We've stopped. Go where we want to go.
|
|
else if (rage::Abs(fSpeed) < 0.5f)
|
|
{
|
|
// if both brake and throttle are held down, do a burn out
|
|
if(fAccelButton > 0.6f && fBrakeButton > 0.6f)
|
|
{
|
|
pVehicle->SetThrottle(fAccelButton);
|
|
pVehicle->SetBrake(fBrakeButton);
|
|
}
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if both brake and throttle are held down, do a burn out
|
|
if(fAccelButton > 0.6f && fBrakeButton > 0.6f)
|
|
{
|
|
pVehicle->SetThrottle(fAccelButton);
|
|
pVehicle->SetBrake(fBrakeButton);
|
|
}
|
|
// going forwards or in air
|
|
else if (fSpeed >= 0.0f || pPlane->IsInAir())
|
|
{
|
|
// we want to go forwards so go faster
|
|
if (fForwardness >= 0.0f)
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
// we don't want to go forwards - so brake
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(0.0f);
|
|
pVehicle->SetBrake(-fForwardness);
|
|
}
|
|
}
|
|
// going backwards
|
|
else
|
|
{
|
|
// want to go forwards
|
|
if (fForwardness >= 0.0f)
|
|
{
|
|
// let us sit on the gas pedal if we're trying to get up a slope but sliding back
|
|
if(pVehicle->GetThrottle() > 0.5f && fSpeed > -10.0f)
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
// oh dear we're sliding back too fast, go for the brakes
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(0.0f);
|
|
pVehicle->SetBrake(fForwardness);
|
|
}
|
|
}
|
|
// we want to go backwards, so apply the gas
|
|
else
|
|
{
|
|
pVehicle->SetThrottle(fForwardness);
|
|
pVehicle->SetBrake(0.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveAutogyro
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveAutogyro::CTaskVehiclePlayerDriveAutogyro() :
|
|
CTaskVehiclePlayerDriveRotaryWingAircraft()
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_AUTOGYRO);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveAutogyro::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CAutogyro*>(pVehicle));
|
|
CAutogyro *pAutogyro = static_cast<CAutogyro*>(pVehicle);
|
|
|
|
CTaskVehiclePlayerDriveRotaryWingAircraft::ProcessDriverInputsForPlayerOnUpdate(pPlayerPed, pControl, pAutogyro);
|
|
|
|
ProcessPlayerControlInputsForBrakeAndGasPedal(pPlayerPed->GetControlFromPlayer(),false, pAutogyro);
|
|
|
|
if(pAutogyro->HasContactWheels())
|
|
{
|
|
pAutogyro->SetSteerAngle(-pAutogyro->GetYawControl()*pAutogyro->pHandling->m_fSteeringLock);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveAutogyro::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *UNUSED_PARAM(pVehicle))
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveHeli
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveHeli::CTaskVehiclePlayerDriveHeli() :
|
|
CTaskVehiclePlayerDriveRotaryWingAircraft()
|
|
{
|
|
m_bJustEnteredHoverMode = false;
|
|
m_bJustLeftHoverMode = false;
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_PLAYER_DRIVE_HELI);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveHeli::GetOverriddenGameplayCameraSettings(tGameplayCameraSettings& settings) const
|
|
{
|
|
const s32 state = GetState();
|
|
if(state == State_Hover)
|
|
{
|
|
settings.m_CameraHash = g_HeliHoverCameraHash;
|
|
//NOTE: We cannot clone the previous orientation as the hover camera needs to snap to align with the current heli orientation initially.
|
|
settings.m_Flags.ClearFlag(Flag_ShouldClonePreviousOrientation);
|
|
settings.m_Flags.ClearFlag(Flag_ShouldFallBackToIdealHeading);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveHeli::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
|
|
|
|
//player is always dirty
|
|
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
|
|
|
|
FSM_Begin
|
|
//
|
|
FSM_State(State_Drive)
|
|
FSM_OnEnter
|
|
Drive_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return Drive_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_NoDriver)
|
|
FSM_OnEnter
|
|
NoDriver_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return NoDriver_OnUpdate(pVehicle);
|
|
//
|
|
FSM_State(State_Hover)
|
|
FSM_OnEnter
|
|
Hover_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return Hover_OnUpdate(pVehicle);
|
|
FSM_OnExit
|
|
Hover_OnExit(pVehicle);
|
|
//
|
|
FSM_State(State_InactiveHover)
|
|
FSM_OnEnter
|
|
InactiveHover_OnEnter(pVehicle);
|
|
FSM_OnUpdate
|
|
return InactiveHover_OnUpdate(pVehicle);
|
|
FSM_End
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveHeli::ProcessDriverInputsForPlayerOnUpdate(CPed *pPlayerPed, CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CHeli*>(pVehicle));
|
|
CHeli *pHeli= static_cast<CHeli*>(pVehicle);
|
|
|
|
Assert(pPlayerPed && (pPlayerPed->IsControlledByLocalPlayer()));
|
|
|
|
// If this is a seaplane it could be anchored. Make sure anchor is cleared in this state.
|
|
if( CSeaPlaneExtension* pSeaPlaneExtension = pHeli->GetExtension<CSeaPlaneExtension>() )
|
|
{
|
|
if( pSeaPlaneExtension->GetAnchorHelper().IsAnchored() && !pSeaPlaneExtension->GetAnchorHelper().ShouldForcePlayerBoatToRemainAnchored() )
|
|
{
|
|
pSeaPlaneExtension->GetAnchorHelper().Anchor( false );
|
|
}
|
|
}
|
|
|
|
// auto throttle will keep us hovering, but won't stop us stopping from moving up, if that makes any sense?
|
|
float fDesiredThrottleControl = pControl->GetVehicleFlyThrottleUp().GetNorm01() - pControl->GetVehicleFlyThrottleDown().GetNorm01();
|
|
|
|
float fAutoThrottle = 0.0f;
|
|
{
|
|
if( !pVehicle->HasContactWheels() &&
|
|
( !pVehicle->pHandling->GetSeaPlaneHandlingData() || !pVehicle->m_nFlags.bPossiblyTouchesWater ) )
|
|
{
|
|
fAutoThrottle = 0.98f * rage::Clamp( 1.0f - pVehicle->GetVelocityIncludingReferenceFrame().z * ms_fHELI_AUTO_THROTTLE_FALLOFF, 0.0f, 1.0f );
|
|
}
|
|
|
|
// need to combine desired throttle and auto throttle, somehow
|
|
if(fDesiredThrottleControl > 0.01f)
|
|
{
|
|
fDesiredThrottleControl += 1.0f;
|
|
}
|
|
else if(fDesiredThrottleControl < -0.01f)
|
|
{
|
|
fDesiredThrottleControl = rage::Max(-0.1f, fDesiredThrottleControl + 0.5f);
|
|
}
|
|
else
|
|
{
|
|
fDesiredThrottleControl = fAutoThrottle;
|
|
}
|
|
}
|
|
|
|
float fThrottleChangeMult = rage::Powf(ms_fHELI_THROTTLE_CONTROL_DAMPING, fwTimer::GetTimeStep());
|
|
|
|
// Store this here since it will be overridden by superclass process control inputs
|
|
fDesiredThrottleControl = fThrottleChangeMult*pHeli->GetThrottleControl() + (1.0f - fThrottleChangeMult)*fDesiredThrottleControl;
|
|
|
|
CTaskVehiclePlayerDriveRotaryWingAircraft::ProcessDriverInputsForPlayerOnUpdate(pPlayerPed, pControl, pHeli);
|
|
|
|
pHeli->SetThrottleControl(fDesiredThrottleControl);
|
|
|
|
static dev_bool sbEnableHoverMode = false;
|
|
if(sbEnableHoverMode)
|
|
{
|
|
const ioValue &ioVal = pControl->GetVehicleHorn();
|
|
|
|
// if stick was clicked then enter "hover" mode
|
|
if(ioVal.IsReleased() && !m_bJustLeftHoverMode)
|
|
{
|
|
m_bJustEnteredHoverMode = true;
|
|
SetState(State_Hover);
|
|
}
|
|
else
|
|
{
|
|
m_bJustLeftHoverMode = false;
|
|
}
|
|
}
|
|
|
|
//Check if heli has light and if it needs turning on/off
|
|
if(pControl->GetVehicleHeadlight().IsPressed() && pPlayerPed->GetPlayerInfo()->GetCanUseSearchLight())
|
|
{
|
|
CVehicleWeaponMgr* pWeaponMgr = pVehicle->GetVehicleWeaponMgr();
|
|
if(pWeaponMgr)
|
|
{
|
|
for(int i = 0; i < pWeaponMgr->GetNumVehicleWeapons(); i++)
|
|
{
|
|
CVehicleWeapon* pWeapon = pWeaponMgr->GetVehicleWeapon(i);
|
|
if(pWeapon && pWeapon->GetType() == VGT_SEARCHLIGHT)
|
|
{
|
|
CSearchLight* pSearchlight = static_cast<CSearchLight*>(pWeapon);
|
|
pSearchlight->Fire(pPlayerPed,pVehicle,VEC3_ZERO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process the rear ramp door
|
|
//#if 0 // Disable open/close rear door of cargobob. B* 1369017
|
|
#if __BANK
|
|
if( CVehicleFactory::ms_rearDoorsCanOpen )
|
|
{
|
|
m_bDoorControlButtonUp |= pControl->GetVehicleRoof().IsUp();
|
|
|
|
if(m_bDoorControlButtonUp && pControl->GetVehicleRoof().HistoryHeldDown(ms_uTimeToHoldDoorControlButtonDown))
|
|
{
|
|
CCarDoor *pDoor = pVehicle->GetDoorFromId(VEH_DOOR_DSIDE_R);
|
|
if(pDoor && pDoor->GetIsIntact(pHeli))
|
|
{
|
|
if(pDoor->GetIsLatched(pHeli) || pDoor->GetTargetDoorRatio() < 0.01f)
|
|
{
|
|
pDoor->SetTargetDoorOpenRatio(1.0f, CCarDoor::DRIVEN_NORESET|CCarDoor::DRIVEN_SPECIAL);
|
|
}
|
|
else if(pDoor->GetTargetDoorRatio() > 0.99f)
|
|
{
|
|
pDoor->SetTargetDoorOpenRatio(0.0f, CCarDoor::DRIVEN_AUTORESET|CCarDoor::WILL_LOCK_DRIVEN);
|
|
}
|
|
}
|
|
m_bDoorControlButtonUp = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool HasRopeAndHook = false;
|
|
|
|
for(int i = 0; i < pVehicle->GetNumberOfVehicleGadgets(); i++)
|
|
{
|
|
CVehicleGadget *pVehicleGadget = pVehicle->GetVehicleGadget(i);
|
|
|
|
if(pVehicleGadget->GetType() == VGT_PICK_UP_ROPE || pVehicleGadget->GetType() == VGT_PICK_UP_ROPE_MAGNET)
|
|
{
|
|
HasRopeAndHook = true;
|
|
CVehicleGadgetPickUpRope *pPickUpRope = static_cast<CVehicleGadgetPickUpRope*>(pVehicleGadget);
|
|
|
|
//check whether the player wants to detach the vehicle
|
|
if(pControl->GetVehicleGrapplingHook().IsPressed())
|
|
{
|
|
pPickUpRope->Trigger();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!HasRopeAndHook)
|
|
{
|
|
TUNE_BOOL(OVERRIDE_CARGOBOB_PICKUP_ROPE_TYPE, false);
|
|
TUNE_BOOL(CARGOBOB_USE_MAGNET, true);
|
|
// ghost vehicles in non-contact races are not permitted to use the hook
|
|
if(pControl->GetVehicleGrapplingHook().IsPressed() && !NetworkInterface::IsAGhostVehicle(*pHeli))
|
|
{
|
|
if(OVERRIDE_CARGOBOB_PICKUP_ROPE_TYPE)
|
|
pHeli->SetPickupRopeType(CARGOBOB_USE_MAGNET ? PICKUP_MAGNET : PICKUP_HOOK);
|
|
|
|
pHeli->AddPickupRope();
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveHeli::ProcessDriverInputsForPlayerInactiveOnUpdate( CControl *UNUSED_PARAM(pControl), CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CHeli*>(pVehicle));
|
|
CHeli *pHeli= static_cast<CHeli*>(pVehicle);
|
|
|
|
if (pHeli->m_nVehicleFlags.bLimitSpeedWhenPlayerInactive)
|
|
{
|
|
if(pHeli->HasContactWheels())
|
|
{
|
|
pHeli->SetThrottleControl(0.0f);
|
|
pHeli->SetYawControl(0.0f);
|
|
pHeli->SetPitchControl(0.0f);
|
|
pHeli->SetRollControl(0.0f);
|
|
}
|
|
else
|
|
{
|
|
SetState(State_InactiveHover);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//State_InactiveHover
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CTaskVehiclePlayerDriveHeli::InactiveHover_OnEnter (CVehicle* UNUSED_PARAM(pVehicle))
|
|
{
|
|
SetNewTask(rage_new CTaskVehicleHover() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveHeli::InactiveHover_OnUpdate (CVehicle* pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CHeli*>(pVehicle));
|
|
CHeli *pHeli= static_cast<CHeli*>(pVehicle);
|
|
|
|
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_HOVER))
|
|
{
|
|
// If the subtask has terminated - restart
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
}
|
|
|
|
if(pHeli->HasContactWheels() && pHeli->GetVelocityIncludingReferenceFrame().Mag2() < 16.0f)
|
|
{
|
|
SetState(State_Drive);
|
|
return FSM_Continue;
|
|
}
|
|
|
|
//get the player controls
|
|
Assert( pVehicle->GetDriver() );
|
|
CPed* pPlayerPed = pVehicle->GetDriver();
|
|
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl==NULL)
|
|
return FSM_Continue;
|
|
|
|
//the control is active again so just go back to the drive state.
|
|
if(IsThePlayerControlInactive(pControl) == false)
|
|
{
|
|
SetState(State_Drive);
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//State_Hover
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CTaskVehiclePlayerDriveHeli::Hover_OnEnter (CVehicle* pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CHeli*>(pVehicle));
|
|
CHeli *pHeli= static_cast<CHeli*>(pVehicle);
|
|
|
|
pHeli->SetHoverMode(true);
|
|
pHeli->SetThrustVectoringMode(false);
|
|
|
|
Vector3 v = VEC3V_TO_VECTOR3(pHeli->GetTransform().GetPosition());
|
|
|
|
sVehicleMissionParams params;
|
|
params.m_fCruiseSpeed = 0.0f;
|
|
params.SetTargetPosition(v);
|
|
params.m_fTargetArriveDist = 0.0f;
|
|
SetNewTask(rage_new CTaskVehicleGoToHelicopter(params
|
|
, CTaskVehicleGoToHelicopter::HF_DontModifyOrientation|CTaskVehicleGoToHelicopter::HF_DontModifyPitch
|
|
|CTaskVehicleGoToHelicopter::HF_DontModifyThrottle|CTaskVehicleGoToHelicopter::HF_DontDoAvoidance
|
|
, -1.0f, 0) );
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
aiTask::FSM_Return CTaskVehiclePlayerDriveHeli::Hover_OnUpdate (CVehicle* pVehicle)
|
|
{
|
|
const bool bHasDriver = pVehicle->GetDriver() != NULL;
|
|
const bool bDriverInjured = pVehicle->GetDriver() && pVehicle->GetDriver()->IsInjured();
|
|
const bool bDriverArrested = pVehicle->GetDriver() && pVehicle->GetDriver()->GetIsArrested();
|
|
const bool bDriverJumpingOut = false;
|
|
CControl *pControl = NULL;
|
|
|
|
|
|
m_fTimeSincePlayingRecording = rage::Min(999.0f, m_fTimeSincePlayingRecording + fwTimer::GetTimeStep());
|
|
if( pVehicle && pVehicle->IsRunningCarRecording() )
|
|
{
|
|
m_fTimeSincePlayingRecording = 0.0f;
|
|
}
|
|
|
|
if (GetIsSubtaskFinished(CTaskTypes::TASK_VEHICLE_GOTO_HELICOPTER))
|
|
{
|
|
// If the subtask has terminated - restart
|
|
SetFlag(aiTaskFlags::RestartCurrentState);
|
|
}
|
|
|
|
if(bHasDriver && !bDriverArrested && !bDriverInjured && !bDriverJumpingOut && taskVerifyf(pVehicle->GetDriver()->GetPlayerInfo(), "Invalid player info!"))
|
|
{
|
|
Assert( pVehicle->GetDriver() );
|
|
Assert(pVehicle->GetDriver()->IsPlayer());
|
|
CPed* pPlayerPed = pVehicle->GetDriver();
|
|
|
|
CControl *pControl = pPlayerPed->GetControlFromPlayer();
|
|
if(pControl==NULL)
|
|
return FSM_Continue;
|
|
|
|
ProcessHoverDriverInputsForPlayer(pPlayerPed, pControl, pVehicle);
|
|
|
|
// Dont override the velocity unless the player hasn't been running a recording for at least a second
|
|
if(IsThePlayerControlInactive(pControl) && m_fTimeSincePlayingRecording > 0.1f)
|
|
{
|
|
ProcessDriverInputsForPlayerInactiveOnUpdate(pControl, pVehicle);
|
|
}
|
|
}
|
|
|
|
if(bHasDriver && !bDriverArrested && !bDriverInjured && !bDriverJumpingOut)
|
|
{
|
|
Assert( pVehicle->GetDriver() );
|
|
Assert(pVehicle->GetDriver()->IsPlayer());
|
|
CPed* pPlayerPed = pVehicle->GetDriver();
|
|
|
|
pControl = pPlayerPed->GetControlFromPlayer();
|
|
}
|
|
|
|
|
|
if(pControl)//if we have control check whether we should leave "Hover" mode
|
|
{
|
|
const ioValue &ioVal = pControl->GetVehicleHorn();
|
|
// if stick was clicked then quit "Hover" mode
|
|
if(ioVal.IsReleased() && !m_bJustEnteredHoverMode)
|
|
{
|
|
m_bJustLeftHoverMode = true;
|
|
SetState(State_Drive);
|
|
}
|
|
else
|
|
{
|
|
m_bJustEnteredHoverMode = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetState(State_Drive);//if anythings failed then just go back to the driving state.
|
|
}
|
|
|
|
return FSM_Continue;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveHeli::Hover_OnExit (CVehicle* pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CHeli*>(pVehicle));
|
|
CHeli *pHeli= static_cast<CHeli*>(pVehicle);
|
|
|
|
pHeli->SetHoverMode(false);
|
|
pHeli->SetThrustVectoringMode(true);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveHeli::ProcessHoverDriverInputsForPlayer (CPed *UNUSED_PARAM(pPlayerPed), CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CHeli*>(pVehicle));
|
|
CHeli *pHeli= static_cast<CHeli*>(pVehicle);
|
|
|
|
CTask* pTask = GetSubTask();
|
|
CTaskVehicleGoToHelicopter* pGotoHeliTask = NULL;
|
|
|
|
if(pTask && pTask->GetTaskType() == CTaskTypes::TASK_VEHICLE_GOTO_HELICOPTER)
|
|
{
|
|
//Vector3 v = VEC3V_TO_VECTOR3(pHeli->GetTransform().GetPosition());
|
|
pGotoHeliTask = (CTaskVehicleGoToHelicopter*)pTask;
|
|
}
|
|
|
|
if(pGotoHeliTask == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// auto throttle will keep us hovering, but won't stop us stopping from moving up, if that makes any sense?
|
|
float fDesiredThrottleControl = (pControl->GetVehicleFlyThrottleUp().GetNorm01()*2.0f) - pControl->GetVehicleFlyThrottleDown().GetNorm01();
|
|
|
|
pHeli->SetThrottleControl(fDesiredThrottleControl);
|
|
|
|
|
|
// If the throttle isn't pressed then hover around this point
|
|
if(fDesiredThrottleControl == 0.0f)
|
|
{
|
|
if(pGotoHeliTask->GetHeliFlags()&CTaskVehicleGoToHelicopter::HF_DontModifyThrottle)
|
|
{
|
|
Vector3 v = VEC3V_TO_VECTOR3(pHeli->GetTransform().GetPosition());
|
|
pGotoHeliTask->SetTargetPosition(&v);
|
|
pGotoHeliTask->UnSetHeliFlag(CTaskVehicleGoToHelicopter::HF_DontModifyThrottle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pGotoHeliTask->SetHeliFlag(CTaskVehicleGoToHelicopter::HF_DontModifyThrottle);
|
|
}
|
|
|
|
pHeli->SetPitchControl(0.0f);
|
|
pHeli->SetRollControl(0.0f);
|
|
|
|
#if USE_SIXAXIS_GESTURES
|
|
if(CControlMgr::GetPlayerPad() && CPadGestureMgr::GetMotionControlEnabled(CPadGestureMgr::MC_TYPE_AIRCRAFT))
|
|
{
|
|
// Static values here can be tweaked in widgets
|
|
|
|
CPadGesture* pGesture = CControlMgr::GetPlayerPad()->GetPadGesture();
|
|
if(pGesture)
|
|
{
|
|
// Rescale the raw pitch input to custom range
|
|
// Values will be clamped below so don't bother getting this function to do it
|
|
if(pControl->GetVehicleFlyPitchUpDown().IsEnabled())
|
|
{
|
|
pHeli->SetPitchControl(pGesture->GetPadPitchInRange(CHeli::MOTION_CONTROL_PITCH_MIN,CHeli::MOTION_CONTROL_PITCH_MAX,false,true));
|
|
}
|
|
|
|
// Rescale the raw roll too
|
|
// Values will be clamped below so don't bother getting this function to do it
|
|
if(pControl->GetVehicleFlyRollLeftRight().IsEnabled())
|
|
{
|
|
pHeli->SetRollControl(-pGesture->GetPadRollInRange(CHeli::MOTION_CONTROL_ROLL_MIN,CHeli::MOTION_CONTROL_ROLL_MAX,false));
|
|
}
|
|
|
|
}
|
|
}
|
|
//else
|
|
#endif // USE_SIXAXIS_GESTURES
|
|
{
|
|
pControl->SetVehicleFlySteeringExclusive();
|
|
pHeli->SetPitchControl( pHeli->GetPitchControl() + pControl->GetVehicleFlyPitchUpDown().GetNorm(ioValue::ALWAYS_DEAD_ZONE));
|
|
pHeli->SetRollControl( pHeli->GetRollControl() + -pControl->GetVehicleFlyRollLeftRight().GetNorm(ioValue::ALWAYS_DEAD_ZONE));
|
|
}
|
|
|
|
//Work out Yaw Control
|
|
const Vector3 target (pHeli->GetHoverModeDesiredTarget());
|
|
Vector3 targetDelta (target - VEC3V_TO_VECTOR3(pHeli->GetTransform().GetPosition()));
|
|
|
|
const float targetDeltaAngleFlat = fwAngle::GetATanOfXY(targetDelta.x, targetDelta.y);
|
|
const Vector3 targetDeltaFlat (targetDelta.x, targetDelta.y, 0.0f);
|
|
|
|
Vector3 forwardDirFlat = VEC3V_TO_VECTOR3(pHeli->GetTransform().GetB());
|
|
forwardDirFlat.z = 0.0f;
|
|
forwardDirFlat.Normalize();
|
|
|
|
|
|
// Predict our future orientation.
|
|
static float orientationPredictionTime = 0.3f;
|
|
Vector3 vecForward(VEC3V_TO_VECTOR3(pHeli->GetTransform().GetB()));
|
|
const float currOrientation = fwAngle::GetATanOfXY(vecForward.x, vecForward.y);
|
|
const float orientationDeltaPrev = fwAngle::LimitRadianAngleSafe(currOrientation - pHeli->GetHeliIntelligence()->GetOldOrientation());
|
|
const float predictedOrientation = currOrientation + orientationDeltaPrev * (orientationPredictionTime / fwTimer::GetTimeStep());
|
|
|
|
|
|
// Make the heli face the target coordinates.
|
|
const float toTargetPredictedOrienationDelta = fwAngle::LimitRadianAngleSafe(targetDeltaAngleFlat - predictedOrientation);
|
|
const float fYawControlUnclamped = toTargetPredictedOrienationDelta * -2.0f;
|
|
const float fYawControl = rage::Clamp(fYawControlUnclamped, -1.0f, 1.0f);
|
|
|
|
pHeli->SetYawControl(fYawControl);
|
|
|
|
|
|
bool bPlayerRollOrPitchInput = false;
|
|
if(pHeli->GetRollControl() == 0.0f)
|
|
{
|
|
pGotoHeliTask->UnSetHeliFlag(CTaskVehicleGoToHelicopter::HF_DontModifyRoll);
|
|
}
|
|
else
|
|
{
|
|
bPlayerRollOrPitchInput = true;
|
|
pGotoHeliTask->SetHeliFlag(CTaskVehicleGoToHelicopter::HF_DontModifyRoll);
|
|
pHeli->SetRollControl(Clamp(pHeli->GetRollControl(), -0.5f, 0.5f));
|
|
}
|
|
|
|
if(pHeli->GetPitchControl() == 0.0f)
|
|
{
|
|
vecForward = VEC3V_TO_VECTOR3(pHeli->GetTransform().GetB());
|
|
targetDelta.Normalize();
|
|
float desiredPitch = rage::Atan2f(targetDelta.z, targetDelta.XYMag());
|
|
desiredPitch = rage::Clamp(desiredPitch, -1.0f, 1.0f);
|
|
|
|
// In this new code we calculate the pitch that we want first.
|
|
//static float pitchToDesiredSpeedRatio = -0.1f;
|
|
static float pitchPredictionTime = 1.5f;
|
|
static float pitchControlMultiplier = 2.0f;
|
|
|
|
const float pitch = rage::Atan2f(vecForward.z, vecForward.XYMag());
|
|
float predictedPitch = pitch + (pitch - pHeli->GetHeliIntelligence()->GetOldTilt()) * (pitchPredictionTime / fwTimer::GetTimeStep());
|
|
float fPitchControl = (desiredPitch - predictedPitch) * pitchControlMultiplier;
|
|
fPitchControl = rage::Clamp(fPitchControl, -1.0f, 1.0f);
|
|
|
|
pHeli->SetPitchControl(fPitchControl);
|
|
|
|
pHeli->SetPitchControl(Clamp(pHeli->GetPitchControl(), -1.0f, 1.0f));
|
|
}
|
|
else
|
|
{
|
|
bPlayerRollOrPitchInput = true;
|
|
pHeli->SetPitchControl(Clamp(pHeli->GetPitchControl(), -0.5f, 0.5f));
|
|
}
|
|
|
|
|
|
if(bPlayerRollOrPitchInput)
|
|
{
|
|
pHeli->SetHoverMode(false);
|
|
}
|
|
else if( !pHeli->GetHoverMode())
|
|
{
|
|
pHeli->SetHoverMode(true);
|
|
const Vector3 *vOriginalTarget = pGotoHeliTask->GetTargetPosition();
|
|
Vector3 vNewTarget = VEC3V_TO_VECTOR3(pHeli->GetTransform().GetPosition());
|
|
vNewTarget.z = vOriginalTarget->z;//maintain height.
|
|
pGotoHeliTask->SetTargetPosition(&vNewTarget);
|
|
pGotoHeliTask->UnSetHeliFlag(CTaskVehicleGoToHelicopter::HF_DontModifyThrottle);
|
|
}
|
|
|
|
pHeli->SetSteerAngle(0.0f);
|
|
pHeli->SetBrake(1.0f);
|
|
pHeli->SetThrottle(0.0f);
|
|
pHeli->SetHandBrake(false);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
#if !__FINAL
|
|
const char * CTaskVehiclePlayerDriveHeli::GetStaticStateName( s32 iState )
|
|
{
|
|
Assert(iState>=State_Drive&&iState<=State_InactiveHover);
|
|
static const char* aStateNames[] =
|
|
{
|
|
"State_Drive",
|
|
"State_NoDriver",
|
|
"State_Hover",
|
|
"State_InactiveHover"
|
|
};
|
|
|
|
return aStateNames[iState];
|
|
}
|
|
#endif
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehiclePlayerDriveRotaryWingAircraft
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehiclePlayerDriveRotaryWingAircraft::CTaskVehiclePlayerDriveRotaryWingAircraft () :
|
|
CTaskVehiclePlayerDrive()
|
|
#if RSG_PC
|
|
,m_MouseSteeringInput(false)
|
|
#endif
|
|
{
|
|
m_TimeOfLastLandingGearToggle = 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehiclePlayerDriveRotaryWingAircraft::ProcessDriverInputsForPlayerOnUpdate(CPed *UNUSED_PARAM(pPlayerPed), CControl *pControl, CVehicle *pVehicle)
|
|
{
|
|
//cast the vehicle into the applicable type
|
|
Assert(dynamic_cast<CRotaryWingAircraft*>(pVehicle));
|
|
CRotaryWingAircraft *pRotaryWingAircraft= static_cast<CRotaryWingAircraft*>(pVehicle);
|
|
|
|
float fDesiredThrottleControl = pControl->GetVehicleFlyThrottleUp().GetNorm01() - pControl->GetVehicleFlyThrottleDown().GetNorm01();
|
|
|
|
float fThrottleChangeMult = rage::Powf(ms_fHELI_THROTTLE_CONTROL_DAMPING, fwTimer::GetTimeStep());
|
|
pRotaryWingAircraft->SetThrottleControl(fThrottleChangeMult*pRotaryWingAircraft->GetThrottleControl() + (1.0f - fThrottleChangeMult)*fDesiredThrottleControl);
|
|
|
|
#if USE_SIXAXIS_GESTURES
|
|
if(CControlMgr::GetPlayerPad() && CPadGestureMgr::GetMotionControlEnabled(CPadGestureMgr::MC_TYPE_AIRCRAFT))
|
|
{
|
|
// Static values here can be tweaked in widgets
|
|
|
|
CPadGesture* pGesture = CControlMgr::GetPlayerPad()->GetPadGesture();
|
|
if(pGesture)
|
|
{
|
|
// Rescale the raw pitch input to custom range
|
|
// Values will be clamped below so don't bother getting this function to do it
|
|
if(pControl->GetVehicleFlyPitchUpDown().IsEnabled())
|
|
{
|
|
pRotaryWingAircraft->SetPitchControl(pGesture->GetPadPitchInRange(CHeli::MOTION_CONTROL_PITCH_MIN,CHeli::MOTION_CONTROL_PITCH_MAX,false,true));
|
|
}
|
|
|
|
// Rescale the raw roll too
|
|
// Values will be clamped below so don't bother getting this function to do it
|
|
if(pControl->GetVehicleFlyRollLeftRight().IsEnabled())
|
|
{
|
|
pRotaryWingAircraft->SetRollControl(-pGesture->GetPadRollInRange(CHeli::MOTION_CONTROL_ROLL_MIN,CHeli::MOTION_CONTROL_ROLL_MAX,false));
|
|
}
|
|
|
|
// Dont clamp this in case we want ridic fast yaw
|
|
if(pControl->GetVehicleFlyYawRight().IsEnabled() || pControl->GetVehicleFlyYawLeft().IsEnabled())
|
|
{
|
|
pRotaryWingAircraft->SetYawControl( pGesture->GetPadYaw()*CHeli::MOTION_CONTROL_YAW_MULT );
|
|
}
|
|
}
|
|
}
|
|
//else
|
|
#endif // USE_SIXAXIS_GESTURES
|
|
{
|
|
pControl->SetVehicleFlySteeringExclusive();
|
|
|
|
#if RSG_PC
|
|
ioValue::ReadOptions readOptions = ioValue::ALWAYS_DEAD_ZONE;
|
|
|
|
bool bScaledAxisRollInput = pControl->GetVehicleFlyRollLeftRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS;
|
|
bool bScaledAxisPitchnput = pControl->GetVehicleFlyPitchUpDown().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS;
|
|
if(bScaledAxisRollInput || bScaledAxisPitchnput)
|
|
{
|
|
readOptions = ioValue::DEFAULT_OPTIONS;
|
|
}
|
|
|
|
float fPitchControl = pControl->GetVehicleFlyPitchUpDown().GetNorm(readOptions);
|
|
float fRollControl = -pControl->GetVehicleFlyRollLeftRight().GetNorm(readOptions);
|
|
const bool bScaledAxisYawInput = pControl->GetVehicleFlyYawLeft().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS
|
|
|| pControl->GetVehicleFlyYawRight().GetSource().m_Device == IOMS_MOUSE_SCALEDAXIS;
|
|
// Use Abs on the norm values as mouse mappings can be inverted, this is due to the way the mappings work
|
|
// (ideally we would alter this but its safer to fix the issue this way).
|
|
float fYawControl = Abs(pControl->GetVehicleFlyYawRight().GetNorm()) - Abs(pControl->GetVehicleFlyYawLeft().GetNorm());
|
|
|
|
static bool sb_pitchWasScaled = false;
|
|
|
|
if( bScaledAxisPitchnput )
|
|
{
|
|
sb_pitchWasScaled = true;
|
|
}
|
|
|
|
if( sb_pitchWasScaled )
|
|
{
|
|
bScaledAxisPitchnput = true;
|
|
}
|
|
|
|
|
|
float timeStep = fwTimer::GetTimeStep();
|
|
|
|
if(pControl->WasKeyboardMouseLastKnownSource())
|
|
{
|
|
if(bScaledAxisRollInput || bScaledAxisPitchnput || bScaledAxisYawInput)
|
|
{
|
|
// Undo fixup applied for scaled axis, without affecting scripted minigame inputs.
|
|
if(bScaledAxisRollInput)
|
|
{
|
|
fRollControl *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(bScaledAxisPitchnput)
|
|
{
|
|
fPitchControl *= c_fMouseAdjustmentScale;
|
|
}
|
|
if(bScaledAxisYawInput)
|
|
{
|
|
fYawControl *= c_fMouseAdjustmentScale;
|
|
}
|
|
m_MouseSteeringInput = true;
|
|
}
|
|
else if(pControl->GetVehicleFlyRollLeftRight().GetSource().m_Device == IOMS_MKB_AXIS || pControl->GetVehicleFlyPitchUpDown().GetSource().m_Device == IOMS_MKB_AXIS)// Keyboard steering input
|
|
{
|
|
m_MouseSteeringInput = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// HACK TO FIX GTAV B*2870991 - The input returns the control immediately back to joypad control when playing on mouse, even when there isn't one plugged in
|
|
if( !m_MouseSteeringInput ||
|
|
Abs( fRollControl ) > c_fMouseTollerance ||
|
|
Abs( fPitchControl ) > c_fMouseTollerance ||
|
|
Abs( fYawControl ) > c_fMouseTollerance )
|
|
{
|
|
m_MouseSteeringInput = false;
|
|
}
|
|
}
|
|
|
|
bool bMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Vehicle;
|
|
bool bCameraMouseFlySteering = CControl::GetMouseSteeringMode(PREF_MOUSE_FLY) == CControl::eMSM_Camera;
|
|
|
|
if(m_MouseSteeringInput && ((bMouseFlySteering && !CControlMgr::GetMainPlayerControl().GetDriveCameraToggleOn()) || (bCameraMouseFlySteering && CControlMgr::GetMainPlayerControl().GetDriveCameraToggleOn())))
|
|
{
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, HELI_STEERING_DEADZONE_CENTERING_SPEED_ROLL, 4.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, HELI_STEERING_DEADZONE_CENTERING_SPEED_PITCH, 0.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, HELI_STEERING_DEADZONE_CENTERING_SPEED_YAW, 0.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, HELI_STEERING_MULTIPLIER, 1.0f, 0.0f, 30.0f, 0.01f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, HELI_STEERING_CENTER_ALLOWANCE, 0.001f, 0.0f, 1.0f, 0.0001f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, HELI_STEERING_INPUT_DEADZONE, FLT_EPSILON, 0.0f, 1.0f, 0.0001f);
|
|
TUNE_GROUP_FLOAT(MOUSE_STEERING_TUNE, HELI_STEERING_DELTA_MULT, 5.0f, 0.0f, 30.0f, 0.1f);
|
|
|
|
float fAutoCenterMult = CPauseMenu::GetMenuPreference(PREF_MOUSE_AUTOCENTER_PLANE)/10.0f;
|
|
|
|
//Roll
|
|
if( bScaledAxisRollInput )
|
|
{
|
|
if (fabs(fRollControl) <= HELI_STEERING_INPUT_DEADZONE)
|
|
{
|
|
if(pRotaryWingAircraft->GetRollControl() > 0.0f)
|
|
{
|
|
fRollControl = Clamp(pRotaryWingAircraft->GetRollControl() - (HELI_STEERING_DEADZONE_CENTERING_SPEED_ROLL * fAutoCenterMult * timeStep), 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fRollControl = Clamp(pRotaryWingAircraft->GetRollControl() + (HELI_STEERING_DEADZONE_CENTERING_SPEED_ROLL * fAutoCenterMult * timeStep),-1.0f, 0.0f);
|
|
}
|
|
}
|
|
else if( Sign(fRollControl) == Sign(pRotaryWingAircraft->GetRollControl()) || rage::Abs(pRotaryWingAircraft->GetRollControl()) <= HELI_STEERING_CENTER_ALLOWANCE)
|
|
{
|
|
fRollControl = pRotaryWingAircraft->GetRollControl() + (fRollControl * HELI_STEERING_MULTIPLIER);
|
|
}
|
|
else
|
|
{
|
|
fRollControl = pRotaryWingAircraft->GetRollControl() + (fRollControl - pRotaryWingAircraft->GetRollControl()) * HELI_STEERING_DELTA_MULT * timeStep;
|
|
}
|
|
}
|
|
|
|
//Pitch
|
|
if( bScaledAxisPitchnput )
|
|
{
|
|
if (fabs(fPitchControl) <= HELI_STEERING_INPUT_DEADZONE)
|
|
{
|
|
if(pRotaryWingAircraft->GetPitchControl() > 0.0f)
|
|
{
|
|
fPitchControl = Clamp(pRotaryWingAircraft->GetPitchControl() - (HELI_STEERING_DEADZONE_CENTERING_SPEED_PITCH * fAutoCenterMult * timeStep), 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fPitchControl = Clamp(pRotaryWingAircraft->GetPitchControl() + (HELI_STEERING_DEADZONE_CENTERING_SPEED_PITCH * fAutoCenterMult * timeStep),-1.0f, 0.0f);
|
|
}
|
|
}
|
|
else if( Sign(fPitchControl) == Sign(pRotaryWingAircraft->GetPitchControl()) || rage::Abs(pRotaryWingAircraft->GetPitchControl()) <= HELI_STEERING_CENTER_ALLOWANCE)
|
|
{
|
|
fPitchControl = pRotaryWingAircraft->GetPitchControl() + (fPitchControl * HELI_STEERING_MULTIPLIER);
|
|
}
|
|
else
|
|
{
|
|
fPitchControl = pRotaryWingAircraft->GetPitchControl() + (fPitchControl - pRotaryWingAircraft->GetPitchControl()) * HELI_STEERING_DELTA_MULT * timeStep;
|
|
}
|
|
}
|
|
|
|
//Yaw
|
|
if( bScaledAxisYawInput )
|
|
{
|
|
if (fabs(fYawControl) <= HELI_STEERING_INPUT_DEADZONE)
|
|
{
|
|
if(pRotaryWingAircraft->GetYawControl() > 0.0f)
|
|
{
|
|
fYawControl = Clamp(pRotaryWingAircraft->GetYawControl() - (HELI_STEERING_DEADZONE_CENTERING_SPEED_YAW * fAutoCenterMult * timeStep), 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
fYawControl = Clamp(pRotaryWingAircraft->GetYawControl() + (HELI_STEERING_DEADZONE_CENTERING_SPEED_YAW * fAutoCenterMult * timeStep),-1.0f, 0.0f);
|
|
}
|
|
}
|
|
else if( Sign(fYawControl) == Sign(pRotaryWingAircraft->GetYawControl()) || rage::Abs(pRotaryWingAircraft->GetYawControl()) <= HELI_STEERING_CENTER_ALLOWANCE)
|
|
{
|
|
fYawControl = pRotaryWingAircraft->GetYawControl() + (fYawControl * HELI_STEERING_MULTIPLIER);
|
|
}
|
|
else
|
|
{
|
|
fYawControl = pRotaryWingAircraft->GetYawControl() + (fYawControl - pRotaryWingAircraft->GetYawControl()) * HELI_STEERING_DELTA_MULT * timeStep;
|
|
}
|
|
}
|
|
}
|
|
|
|
pRotaryWingAircraft->SetPitchControl( fPitchControl );
|
|
pRotaryWingAircraft->SetRollControl( fRollControl );
|
|
pRotaryWingAircraft->SetYawControl( fYawControl );
|
|
#else
|
|
|
|
#if USE_SIXAXIS_GESTURES
|
|
if (CControlMgr::GetPlayerPad() && !CPadGestureMgr::GetMotionControlEnabled(CPadGestureMgr::MC_TYPE_AIRCRAFT))
|
|
#endif // USE_SIXAXIS_GESTURES
|
|
{
|
|
pRotaryWingAircraft->SetYawControl(0.0f);
|
|
pRotaryWingAircraft->SetPitchControl(0.0f);
|
|
pRotaryWingAircraft->SetRollControl(0.0f);
|
|
}
|
|
|
|
pRotaryWingAircraft->SetPitchControl( pRotaryWingAircraft->GetPitchControl() + pControl->GetVehicleFlyPitchUpDown().GetNorm(ioValue::ALWAYS_DEAD_ZONE));
|
|
pRotaryWingAircraft->SetRollControl( pRotaryWingAircraft->GetRollControl() + -pControl->GetVehicleFlyRollLeftRight().GetNorm(ioValue::ALWAYS_DEAD_ZONE));
|
|
|
|
// Use Abs on the norm values as mouse mappings can be inverted, this is due to the way the mappings work (ideally we would alter this but its safer to fix the issue this way).
|
|
pRotaryWingAircraft->SetYawControl( pRotaryWingAircraft->GetYawControl() + Abs(pControl->GetVehicleFlyYawRight().GetNorm()) - Abs(pControl->GetVehicleFlyYawLeft().GetNorm()));
|
|
#endif
|
|
}
|
|
|
|
pRotaryWingAircraft->SetStrafeMode( Abs(pControl->GetVehicleFlyYawRight().GetNorm()) > 0.0f && Abs(pControl->GetVehicleFlyYawLeft().GetNorm()) > 0.0f );
|
|
|
|
pRotaryWingAircraft->SetPitchControl(Clamp(pRotaryWingAircraft->GetPitchControl(), -1.0f, 1.0f));
|
|
pRotaryWingAircraft->SetRollControl(Clamp(pRotaryWingAircraft->GetRollControl(), -1.0f, 1.0f));
|
|
pRotaryWingAircraft->SetYawControl(Clamp(pRotaryWingAircraft->GetYawControl(), -1.0f, 1.0f));
|
|
|
|
const float fSteeringAngle = pRotaryWingAircraft->InheritsFromBlimp() ? Clamp(pRotaryWingAircraft->GetRollControl(), -1.0f, 1.0f) * pVehicle->pHandling->m_fSteeringLock : 0.0f;
|
|
pRotaryWingAircraft->SetSteerAngle(fSteeringAngle);
|
|
pRotaryWingAircraft->SetBrake(1.0f);
|
|
pRotaryWingAircraft->SetThrottle(0.0f);
|
|
pRotaryWingAircraft->SetHandBrake(false);
|
|
|
|
// Make new undercarriage input
|
|
// Only allow toggle when in the air
|
|
if( pRotaryWingAircraft->InheritsFromHeli() &&
|
|
( !pRotaryWingAircraft->HasContactWheels() ||
|
|
pRotaryWingAircraft->GetIsJetPack() ) )
|
|
{
|
|
CHeli* pHeli = static_cast< CHeli* >( pRotaryWingAircraft );
|
|
|
|
if( pHeli->HasLandingGear() )
|
|
{
|
|
if(pControl->GetVehicleFlyUndercarriage().IsPressed())
|
|
{
|
|
CLandingGear& landingGear = pHeli->GetLandingGear();
|
|
|
|
// dirty hack to avoid conflict with player special abilities
|
|
// if we don't consume the input value, the input will be used twice
|
|
// because we disable input update inside CPlayerSpecialAbilityManager::UpdateSpecialAbilityTrigger
|
|
const_cast<ioValue&>(pControl->GetVehicleFlyUndercarriage()).SetCurrentValue(0);
|
|
|
|
u32 currentTime = fwTimer::GetTimeInMilliseconds();
|
|
static bank_u32 minTimeForGearToggle = 500;
|
|
switch( landingGear.GetPublicState() )
|
|
{
|
|
case CLandingGear::STATE_LOCKED_DOWN:
|
|
case CLandingGear::STATE_DEPLOYING:
|
|
if(currentTime > m_TimeOfLastLandingGearToggle + minTimeForGearToggle)
|
|
{
|
|
landingGear.ControlLandingGear(pVehicle,CLandingGear::COMMAND_RETRACT);
|
|
m_TimeOfLastLandingGearToggle = currentTime;
|
|
}
|
|
break;
|
|
case CLandingGear::STATE_LOCKED_UP:
|
|
case CLandingGear::STATE_RETRACTING:
|
|
if(currentTime > m_TimeOfLastLandingGearToggle + minTimeForGearToggle)
|
|
{
|
|
landingGear.ControlLandingGear(pVehicle,CLandingGear::COMMAND_DEPLOY);
|
|
m_TimeOfLastLandingGearToggle = currentTime;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
//class CTaskVehicleNoDriver
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTaskVehicleNoDriver::CTaskVehicleNoDriver(NoDriverType noDriverType /*= NO_DRIVER_TYPE_ABANDONED*/) :
|
|
m_eNoDriverType(noDriverType)
|
|
{
|
|
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_NO_DRIVER);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CTask::FSM_Return CTaskVehicleNoDriver::UpdateFSM( const s32 iState, const FSM_Event iEvent )
|
|
{
|
|
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
|
|
|
|
//player is always dirty
|
|
pVehicle->m_nVehicleFlags.bAvoidanceDirtyFlag = true;
|
|
|
|
FSM_Begin
|
|
// State_NoDriver
|
|
FSM_State(State_NoDriver)
|
|
FSM_OnUpdate
|
|
return NoDriver_OnUpdate(pVehicle);
|
|
FSM_End
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//State_NoDriver
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CTask::FSM_Return CTaskVehicleNoDriver::NoDriver_OnUpdate(CVehicle* pVehicle)
|
|
{
|
|
pVehicle->GetIntelligence()->ResetPretendOccupantEventData();
|
|
pVehicle->GetIntelligence()->FlushPretendOccupantEventGroup();
|
|
|
|
//set the controls for different vehicle types
|
|
switch(pVehicle->GetVehicleType())
|
|
{
|
|
case VEHICLE_TYPE_CAR:
|
|
case VEHICLE_TYPE_QUADBIKE:
|
|
ProcessAutomobile( static_cast<CAutomobile*>(pVehicle));
|
|
break;
|
|
case VEHICLE_TYPE_BOAT:
|
|
ProcessBoat(static_cast<CBoat*>(pVehicle));
|
|
break;
|
|
case VEHICLE_TYPE_SUBMARINE:
|
|
ProcessSub(static_cast<CSubmarine*>(pVehicle));
|
|
break;
|
|
case VEHICLE_TYPE_HELI:
|
|
case VEHICLE_TYPE_AUTOGYRO:
|
|
case VEHICLE_TYPE_BLIMP:
|
|
ProcessRotaryWingAirfcraft (static_cast<CAutogyro*>(pVehicle));
|
|
break;
|
|
case VEHICLE_TYPE_PLANE:
|
|
ProcessPlane(static_cast<CPlane*>(pVehicle));
|
|
break;
|
|
case VEHICLE_TYPE_BIKE:
|
|
case VEHICLE_TYPE_BICYCLE:
|
|
ProcessBike(static_cast<CBike*>(pVehicle));
|
|
break;
|
|
case VEHICLE_TYPE_TRAIN:
|
|
case VEHICLE_TYPE_TRAILER:
|
|
break;//nothing setup for these yet.
|
|
case VEHICLE_TYPE_SUBMARINECAR:
|
|
{
|
|
CSubmarineCar* submarineCar = (static_cast<CSubmarineCar*>(pVehicle));
|
|
if( submarineCar->IsInSubmarineMode() )
|
|
{
|
|
ProcessSub( static_cast<CSubmarine*>(pVehicle));
|
|
}
|
|
else
|
|
{
|
|
ProcessAutomobile( static_cast<CAutomobile*>(pVehicle));
|
|
}
|
|
}
|
|
break;
|
|
case VEHICLE_TYPE_AMPHIBIOUS_AUTOMOBILE:
|
|
case VEHICLE_TYPE_AMPHIBIOUS_QUADBIKE:
|
|
{
|
|
ProcessAutomobile( static_cast<CAutomobile*>(pVehicle) );
|
|
}
|
|
break;
|
|
default:
|
|
Assertf(0,"Vehicle type not supported");
|
|
}
|
|
|
|
return FSM_Quit;//just return as the controls should not get changed until another task is put on the tree.
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehicleNoDriver::ProcessAutomobile(CVehicle* pVehicle)
|
|
{
|
|
switch(m_eNoDriverType)
|
|
{
|
|
case NO_DRIVER_TYPE_ABANDONED:
|
|
case NO_DRIVER_TYPE_INITIALLY_CREATED:
|
|
pVehicle->m_nVehicleFlags.bSuppressBrakeLight = true;
|
|
if((pVehicle->GetVehicleType()==VEHICLE_TYPE_PLANE && pVehicle->GetVelocityIncludingReferenceFrame().Mag2() < 0.1f))
|
|
pVehicle->SetBrake(1.0f);
|
|
if(pVehicle->m_nVehicleFlags.bRestingOnPhysical)
|
|
pVehicle->SetBrake(0.5f);
|
|
else if(pVehicle->GetVelocityIncludingReferenceFrame().Mag2() < 0.5f)
|
|
pVehicle->SetBrake(0.2f);
|
|
else
|
|
pVehicle->SetBrake(0.0f);
|
|
|
|
pVehicle->SetHandBrake(false);
|
|
//pVehicle->SetSteerAngle(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
break;
|
|
|
|
case NO_DRIVER_TYPE_PLAYER_DISABLED:
|
|
if(pVehicle->GetVelocityIncludingReferenceFrame().Mag2() < 0.01f || (pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer()
|
|
&& (pVehicle->GetDriver()->GetIsArrested())))
|
|
{
|
|
pVehicle->SetHandBrake(true);
|
|
pVehicle->SetBrake(1.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
}
|
|
else
|
|
{
|
|
pVehicle->SetBrake(0.0f);
|
|
pVehicle->SetHandBrake(false);
|
|
}
|
|
pVehicle->SetSteerAngle(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
break;
|
|
|
|
case NO_DRIVER_TYPE_WRECKED:
|
|
pVehicle->SetBrake(0.05f);
|
|
pVehicle->SetHandBrake(true);
|
|
//pVehicle->SetSteerAngle(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
break;
|
|
|
|
case NO_DRIVER_TYPE_TOWED:
|
|
pVehicle->SetBrake(0.00f);
|
|
pVehicle->SetHandBrake(false);
|
|
pVehicle->SetSteerAngle(0.0f);
|
|
pVehicle->SetThrottle(0.0f);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(!pVehicle->IsAlarmActivated())
|
|
{
|
|
pVehicle->StopHorn();
|
|
}
|
|
}
|
|
|
|
float fPlaneApplyLargeAirBrakeSpeed2 = 25.0f;
|
|
float fPlaneStillVelocityThreshold = 0.1f;
|
|
|
|
void CTaskVehicleNoDriver::ProcessPlane(CPlane* pPlane)
|
|
{
|
|
if (pPlane->m_CarGenThatCreatedUs == -1 || pPlane->m_LastTimeWeHadADriver > 0)
|
|
{
|
|
pPlane->m_nVehicleFlags.bForceOtherVehiclesToStopForThisVehicle = true;
|
|
}
|
|
|
|
switch(m_eNoDriverType)
|
|
{
|
|
case NO_DRIVER_TYPE_ABANDONED:
|
|
{
|
|
// Don't crash the Microlight if at low altitude while abandoning (similar to helicopters)
|
|
bool bNearGround = false;
|
|
CVehicleModelInfo* pModelInfo = pPlane->GetVehicleModelInfo();
|
|
if (pModelInfo && pModelInfo->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_DONT_CRASH_ABANDONED_NEAR_GROUND))
|
|
{
|
|
const float fHeightThreshold = 3.0f;
|
|
Vector3 vStart = VEC3V_TO_VECTOR3(pPlane->GetVehiclePosition());
|
|
Vector3 vEnd = vStart;
|
|
vEnd.z += pPlane->GetBoundingBoxMin().z - fHeightThreshold;
|
|
WorldProbe::CShapeTestHitPoint probeHitPoint;
|
|
WorldProbe::CShapeTestResults probeResults(probeHitPoint);
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
probeDesc.SetStartAndEnd(vStart, vEnd);
|
|
probeDesc.SetResultsStructure(&probeResults);
|
|
probeDesc.SetExcludeEntity(pPlane);
|
|
probeDesc.SetIncludeFlags(ArchetypeFlags::GTA_MAP_TYPE_VEHICLE);
|
|
probeDesc.SetIsDirected(true);
|
|
|
|
bNearGround = WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc);
|
|
}
|
|
|
|
if(pPlane->IsInAir() && !bNearGround)
|
|
{
|
|
// Don't go into crash mode if another player is jacking you in VTOL mode
|
|
bool bPreventCrash = false;
|
|
CPed* lastDriver = 0;
|
|
if (pPlane->GetSeatManager())
|
|
lastDriver = pPlane->GetSeatManager()->GetLastPedInSeat(0);
|
|
|
|
if (pPlane->GetVerticalFlightModeAvaliable() && pPlane->GetVerticalFlightModeRatio() > 0 && lastDriver && (lastDriver->GetPedResetFlag(CPED_RESET_FLAG_BeingJacked) || lastDriver->GetPedConfigFlag(CPED_CONFIG_FLAG_IsInTheAir)))
|
|
{
|
|
CComponentReservation* pComponentReservation = pPlane->GetComponentReservationMgr()->GetSeatReservation(0, SA_directAccessSeat, 0);
|
|
if (pComponentReservation && pComponentReservation->GetPedUsingComponent())
|
|
{
|
|
bPreventCrash = true;
|
|
}
|
|
}
|
|
|
|
// Clear hover mode so we don't land back on plane after ejecting
|
|
if (!bPreventCrash && pPlane->GetVerticalFlightModeAvaliable())
|
|
{
|
|
pPlane->SetVerticalFlightModeRatio(0.0f);
|
|
}
|
|
|
|
if(!bPreventCrash && !pPlane->IsNetworkClone() && pPlane->GetSeatManager()->GetNumPlayers() == 0 && !pPlane->GetIntelligence()->GetTaskManager()->FindTaskByTypeActive(VEHICLE_TASK_TREE_PRIMARY, CTaskTypes::TASK_VEHICLE_CRASH))
|
|
{
|
|
lastDriver = 0;
|
|
if (pPlane->GetSeatManager())
|
|
lastDriver = pPlane->GetSeatManager()->GetLastPedInSeat(0);
|
|
|
|
// Create a crash task if one doesn't already exist
|
|
#if __BANK
|
|
aiDebugf1("CTaskVehicleNoDriver::ProcessPlane() - Creating CTaskVehicleCrash for %s(%p) that is above ground due to NO_DRIVER_TYPE_ABANDONED", AILogging::GetDynamicEntityNameSafe(pPlane), pPlane);
|
|
#endif
|
|
CTaskVehicleCrash *pCrashTask = rage_new CTaskVehicleCrash(lastDriver);
|
|
|
|
pCrashTask->SetCrashFlag(CTaskVehicleCrash::CF_BlowUpInstantly, false);
|
|
pCrashTask->SetCrashFlag(CTaskVehicleCrash::CF_InACutscene, false);
|
|
pCrashTask->SetCrashFlag(CTaskVehicleCrash::CF_AddExplosion, true);
|
|
|
|
pPlane->GetIntelligence()->AddTask(VEHICLE_TASK_TREE_PRIMARY, pCrashTask, VEHICLE_TASK_PRIORITY_CRASH);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float fPlaneVelocitySqr = pPlane->GetVelocityIncludingReferenceFrame().Mag2();
|
|
|
|
if (fPlaneVelocitySqr > fPlaneStillVelocityThreshold*fPlaneStillVelocityThreshold)
|
|
{
|
|
pPlane->SetBrake(1.0f);
|
|
}
|
|
|
|
pPlane->SetHandBrake(bNearGround); // Otherwise the Microlight will roll away forever
|
|
if(pPlane->GetVelocityIncludingReferenceFrame().Mag2() > fPlaneApplyLargeAirBrakeSpeed2)
|
|
{
|
|
pPlane->SetAirBrakeControl(5.0f);
|
|
}
|
|
pPlane->SetThrottle(0.0f);
|
|
}
|
|
}
|
|
break;
|
|
case NO_DRIVER_TYPE_INITIALLY_CREATED:
|
|
pPlane->m_nVehicleFlags.bSuppressBrakeLight = true;
|
|
if(pPlane->GetVelocityIncludingReferenceFrame().Mag2() < 0.1f)
|
|
pPlane->SetBrake(1.0f);
|
|
if(pPlane->m_nVehicleFlags.bRestingOnPhysical)
|
|
pPlane->SetBrake(0.5f);
|
|
else if(pPlane->GetVelocityIncludingReferenceFrame().Mag2() < 0.5f)
|
|
pPlane->SetBrake(0.2f);
|
|
else
|
|
pPlane->SetBrake(0.0f);
|
|
|
|
pPlane->SetHandBrake(false);
|
|
//pVehicle->SetSteerAngle(0.0f);
|
|
pPlane->SetThrottle(0.0f);
|
|
break;
|
|
case NO_DRIVER_TYPE_PLAYER_DISABLED:
|
|
if(pPlane->GetVelocityIncludingReferenceFrame().Mag2() < 0.01f || (pPlane->GetDriver() && pPlane->GetDriver()->IsPlayer()
|
|
&& (pPlane->GetDriver()->GetIsArrested())))
|
|
{
|
|
pPlane->SetHandBrake(true);
|
|
pPlane->SetBrake(1.0f);
|
|
pPlane->SetThrottle(0.0f);
|
|
}
|
|
else
|
|
{
|
|
pPlane->SetBrake(0.0f);
|
|
pPlane->SetHandBrake(false);
|
|
}
|
|
pPlane->SetSteerAngle(0.0f);
|
|
pPlane->SetThrottle(0.0f);
|
|
break;
|
|
|
|
case NO_DRIVER_TYPE_WRECKED:
|
|
pPlane->SetBrake(0.05f);
|
|
pPlane->SetHandBrake(true);
|
|
//pPlane->SetSteerAngle(0.0f);
|
|
pPlane->SetThrottle(0.0f);
|
|
break;
|
|
|
|
case NO_DRIVER_TYPE_TOWED:
|
|
pPlane->SetBrake(0.00f);
|
|
pPlane->SetHandBrake(false);
|
|
pPlane->SetSteerAngle(0.0f);
|
|
pPlane->SetThrottle(0.0f);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(!pPlane->IsAlarmActivated())
|
|
{
|
|
pPlane->StopHorn();
|
|
}
|
|
}
|
|
|
|
const float SECOND_LOD_DISTANCE = 150.0f;
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void CTaskVehicleNoDriver::ProcessSub (CSubmarine* pSub)
|
|
{
|
|
switch(m_eNoDriverType)
|
|
{
|
|
case(NO_DRIVER_TYPE_ABANDONED):
|
|
case(NO_DRIVER_TYPE_PLAYER_DISABLED):
|
|
case(NO_DRIVER_TYPE_WRECKED):
|
|
case(NO_DRIVER_TYPE_TOWED):
|
|
case(NO_DRIVER_TYPE_INITIALLY_CREATED):
|
|
{
|
|
CSubmarineHandling* subHandling = pSub->GetSubHandling();
|
|
pSub->SetSteerAngle(0.0f);
|
|
pSub->SetHandBrake(false);
|
|
pSub->SetBrake(1.0f);
|
|
pSub->SetThrottle(0.0f);
|
|
subHandling->SetDiveControl(0.1f);
|
|
subHandling->SetPitchControl(0.0f);
|
|
subHandling->SetYawControl(0.0f);
|
|
|
|
Vector3 vecTemp = VEC3V_TO_VECTOR3(pSub->GetTransform().GetPosition()) - CGameWorld::FindLocalPlayerCentreOfWorld();
|
|
if( vecTemp.Mag() > SECOND_LOD_DISTANCE )
|
|
{
|
|
pSub->SetVelocity(Vector3(0.0f,0.0f,0.0f)); // just incase
|
|
pSub->SetAngVelocity(Vector3(0.0f,0.0f,0.0f));
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void CTaskVehicleNoDriver::ProcessBoat(CVehicle* pBoat)
|
|
{
|
|
switch(m_eNoDriverType)
|
|
{
|
|
case(NO_DRIVER_TYPE_ABANDONED):
|
|
case(NO_DRIVER_TYPE_PLAYER_DISABLED):
|
|
case(NO_DRIVER_TYPE_WRECKED):
|
|
case(NO_DRIVER_TYPE_TOWED):
|
|
case(NO_DRIVER_TYPE_INITIALLY_CREATED):
|
|
{
|
|
// don't want to apply expensive physics and buoyancy calculations
|
|
// if range from player is large, since effects will not be noticed
|
|
pBoat->GetBoatHandling()->SetInWater( 1 );
|
|
pBoat->GetBoatHandling()->SetEngineInWater( 1 );
|
|
pBoat->SetIsInWater( TRUE );
|
|
|
|
pBoat->SetSteerAngle(0.0f);
|
|
pBoat->SetHandBrake(false);
|
|
pBoat->SetBrake(0.25f);
|
|
pBoat->SetThrottle(0.0f);
|
|
|
|
Vector3 vecTemp = VEC3V_TO_VECTOR3(pBoat->GetTransform().GetPosition()) - CGameWorld::FindLocalPlayerCentreOfWorld();
|
|
if( vecTemp.Mag() > SECOND_LOD_DISTANCE )
|
|
{
|
|
pBoat->SetVelocity(Vector3(0.0f,0.0f,0.0f)); // just incase
|
|
pBoat->SetAngVelocity(Vector3(0.0f,0.0f,0.0f));
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
extern float HELI_ABANDONED_THROTTLE; // from Heli.cpp
|
|
|
|
void CTaskVehicleNoDriver::ProcessRotaryWingAirfcraft(CRotaryWingAircraft* pAircraft)
|
|
{
|
|
if (pAircraft->m_CarGenThatCreatedUs == -1 || pAircraft->m_LastTimeWeHadADriver > 0)
|
|
{
|
|
pAircraft->m_nVehicleFlags.bForceOtherVehiclesToStopForThisVehicle = true;
|
|
}
|
|
|
|
switch(m_eNoDriverType)
|
|
{
|
|
case NO_DRIVER_TYPE_ABANDONED:
|
|
case NO_DRIVER_TYPE_INITIALLY_CREATED:
|
|
|
|
pAircraft->SetBrake(1.0f);
|
|
|
|
if(!pAircraft->IsInAir())
|
|
{
|
|
pAircraft->SetThrottleControl(0.0f);
|
|
pAircraft->SetPitchControl(0.0f);
|
|
pAircraft->SetYawControl(0.0f);
|
|
pAircraft->SetRollControl(0.0f);;
|
|
}
|
|
else
|
|
{
|
|
pAircraft->SetThrottleControl(HELI_ABANDONED_THROTTLE);
|
|
pAircraft->SetYawControl( pAircraft->GetYawControl() + (pAircraft->GetYawControl() > 0.0f ? 1.0f : -1.0f) * sfHeliAbandonedYawMult * (fwRandom::GetRandomTrueFalse() ? fwRandom::GetRandomNumberInRange(0.5f, 1.0f) : fwRandom::GetRandomNumberInRange(-0.5f, -0.25f)));
|
|
pAircraft->SetYawControl( Clamp(pAircraft->GetYawControl(), -10.0f, 10.0f));
|
|
|
|
pAircraft->SetPitchControl( pAircraft->GetPitchControl() + sfHeliAbandonedPitchMult * fwRandom::GetRandomNumberInRange(0.5f, 1.0f) * (fwRandom::GetRandomTrueFalse() ? 1.0f : -1.0f));
|
|
pAircraft->SetPitchControl( Clamp(pAircraft->GetPitchControl(), -10.0f, 10.0f) );
|
|
|
|
pAircraft->SetRollControl( pAircraft->GetRollControl() + sfHeliAbandonedPitchMult * fwRandom::GetRandomNumberInRange(0.5f, 1.0f) * (fwRandom::GetRandomTrueFalse() ? 1.0f : -1.0f));
|
|
pAircraft->SetRollControl( Clamp(pAircraft->GetRollControl(), -10.0f, 10.0f) );
|
|
}
|
|
break;
|
|
case NO_DRIVER_TYPE_WRECKED:
|
|
case NO_DRIVER_TYPE_PLAYER_DISABLED:
|
|
case NO_DRIVER_TYPE_TOWED:
|
|
{
|
|
const bool bHasDriver = pAircraft->GetDriver() != NULL;
|
|
const bool bDriverArrested = pAircraft->GetDriver() && pAircraft->GetDriver()->GetIsArrested();
|
|
const bool bDriverInjured = pAircraft->GetDriver() && pAircraft->GetDriver()->IsInjured();
|
|
if( !bHasDriver || bDriverArrested || bDriverInjured )
|
|
{
|
|
pAircraft->SetThrottle(0.0f);
|
|
if( bDriverInjured && pAircraft->HasContactWheels() == false )
|
|
{
|
|
pAircraft->SetThrottleControl(HELI_CRASH_THROTTLE);
|
|
pAircraft->SetPitchControl(HELI_CRASH_PITCH);
|
|
pAircraft->SetYawControl(HELI_CRASH_YAW);
|
|
pAircraft->SetRollControl(HELI_CRASH_ROLL);
|
|
}
|
|
else
|
|
{
|
|
pAircraft->SetThrottleControl(0.0f);
|
|
pAircraft->SetPitchControl(0.0f);
|
|
pAircraft->SetYawControl(0.0f);
|
|
pAircraft->SetRollControl(0.0f);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CTaskVehicleNoDriver::ProcessBike(CBike* pBike)
|
|
{
|
|
switch(m_eNoDriverType)
|
|
{
|
|
case NO_DRIVER_TYPE_ABANDONED:
|
|
case NO_DRIVER_TYPE_INITIALLY_CREATED:
|
|
{
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetHandBrake(false);
|
|
pBike->SetThrottle(0.0f);
|
|
|
|
if(pBike->GetVelocityIncludingReferenceFrame().Mag2() < 5.0f || pBike->m_nBikeFlags.bOnSideStand)
|
|
{
|
|
pBike->SetBrake(HIGHEST_BRAKING_WITHOUT_LIGHT);
|
|
if (pBike->GetLayoutInfo() && !pBike->GetLayoutInfo()->GetBikeLeansUnlessMoving())
|
|
{
|
|
if(pBike->m_nBikeFlags.bOnSideStand)
|
|
{
|
|
pBike->SetHandBrake(true);
|
|
float standLeanAngle = pBike->pHandling->GetBikeHandlingData()->m_fBikeOnStandLeanAngle;
|
|
if(standLeanAngle != 0.f && pBike->GetSteerAngle() == 0.0f)
|
|
{
|
|
const float fBikeOnStandLeanAngleInv = 1.0f / standLeanAngle;
|
|
float fSteerAngleFromLean = Clamp(pBike->m_fBikeLeanAngle * fBikeOnStandLeanAngleInv, 0.0f, 1.0f);
|
|
pBike->SetSteerAngle(fSteerAngleFromLean * BIKE_ON_STAND_STEER_ANGLE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NO_DRIVER_TYPE_PLAYER_DISABLED:
|
|
{
|
|
const bool bDriverArrested = pBike->GetDriver() && pBike->GetDriver()->GetIsArrested();
|
|
if(pBike->GetVelocityIncludingReferenceFrame().Mag2() < 0.01f || bDriverArrested )
|
|
{
|
|
pBike->SetBrake(1.0f);
|
|
pBike->SetHandBrake(true);
|
|
}
|
|
else
|
|
{
|
|
pBike->SetBrake(0.0f);
|
|
pBike->SetHandBrake(false);
|
|
}
|
|
pBike->SetSteerAngle(0.0f);
|
|
pBike->SetThrottle(0.0f);
|
|
break;
|
|
}
|
|
|
|
case NO_DRIVER_TYPE_WRECKED:
|
|
{
|
|
pBike->SetBrake(0.05f);
|
|
pBike->SetHandBrake(true);
|
|
pBike->SetSteerAngle(0.0f);
|
|
pBike->SetThrottle(0.0f);
|
|
break;
|
|
}
|
|
case NO_DRIVER_TYPE_TOWED:
|
|
{
|
|
pBike->SetBrake(0.00f);
|
|
pBike->SetHandBrake(false);
|
|
pBike->SetSteerAngle(0.0f);
|
|
pBike->SetThrottle(0.0f);
|
|
break;
|
|
}
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
pBike->StopHorn();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#if !__FINAL
|
|
const char * CTaskVehicleNoDriver::GetStaticStateName( s32 iState )
|
|
{
|
|
Assert(iState>=State_NoDriver&&iState<=State_NoDriver);
|
|
static const char* aStateNames[] =
|
|
{
|
|
"State_NoDriver",
|
|
};
|
|
|
|
return aStateNames[iState];
|
|
}
|
|
#endif
|