Files
GTASource/game/vehicleAi/task/TaskVehicleAnimation.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1677 lines
50 KiB
C++

#include "TaskVehicleAnimation.h"
// Game headers
#include "animation/MoveVehicle.h"
#include "modelinfo/VehicleModelInfoExtensions.h"
#include "Vehicles/vehicle.h"
#include "VehicleAi/task/TaskVehicleMissionBase.h"
#include "VehicleAi/task/TaskVehicleGoto.h"
#include "vehicleAi/task/TaskVehicleGoToAutomobile.h"
#include "VehicleAi/task/TaskVehiclePark.h"
#include "VehicleAi/task/TaskVehicleTempAction.h"
#include "VehicleAi/VehicleIntelligence.h"
#include "peds/ped.h"
#include "peds/PlayerPed.h"
#include "physics/gtaInst.h"
#include "pharticulated/articulatedcollider.h"
#include "streaming/streaming.h" // For CStreaming::LoadAllRequestedObjects().
#include "system/control.h"
#include "phbound/boundcomposite.h"
#include "text/messages.h"
#include "audio/caraudioentity.h"
#include "vfx/vehicleglass/VehicleGlassManager.h"
#include "vehicles/Submarine.h"
AI_OPTIMISATIONS()
AI_VEHICLE_OPTIMISATIONS()
///////////////////////////////////////////////////////////////////////////////////////
CTaskVehicleAnimation::CTaskVehicleAnimation( const strStreamingObjectName pAnimDictName,
const char* pAnimName,
float playBackRate,
float playBackPhase,
bool holdAtEndOfAnimation,
bool useExtractedVelocity ) :
CTaskVehicleMissionBase(),
m_animDictIndex(-1),
m_animHashKey(0),
m_holdAtEndOfAnimation(holdAtEndOfAnimation),
m_playBackRate(playBackRate),
m_playBackPhase(playBackPhase),
m_useExtractedVelocity(useExtractedVelocity)
{
Assertf(pAnimDictName.IsNotNull(), "Null anim dictionary name\n");
Assertf(pAnimName, "Null anim name\n");
if (pAnimDictName.IsNotNull())
{
m_animDictIndex = fwAnimManager::FindSlot(pAnimDictName).Get();
}
if (pAnimName)
{
Assertf(strlen(pAnimName)<ANIM_NAMELEN, "Animation name is too long : %s ", pAnimName);
m_animHashKey = atStringHash(pAnimName);
}
#if __DEV
if (pAnimDictName.IsNotNull())
{
m_animDictName = pAnimDictName;
}
if (pAnimName)
{
strncpy(m_animName, pAnimName, ANIM_NAMELEN);
}
if (pAnimDictName.IsNotNull() && pAnimName)
{
Assertf(fwAnimManager::GetClipIfExistsByDictIndex(m_animDictIndex, m_animHashKey), "Missing anim (anim dict name, anim name): %s, %s\n", pAnimDictName.GetCStr(), pAnimName);
}
#endif //__DEV
Assert((m_animDictIndex != -1) && (m_animHashKey != 0));
Assertf(fwAnimManager::GetClipIfExistsByDictIndex(m_animDictIndex, m_animHashKey), "Missing anim (anim dict index, anim hash key): %d, %d\n", m_animDictIndex, m_animHashKey);
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_ANIMATION);
}
///////////////////////////////////////////////////////////////////////////////////////
CTaskVehicleAnimation::CTaskVehicleAnimation( u32 animDictIndex,
u32 animHashKey,
float playBackRate,
float playBackPhase,
bool holdAtEndOfAnimation,
bool useExtractedVelocity ) :
m_playBackPhase(playBackPhase),
m_playBackRate(playBackRate),
m_holdAtEndOfAnimation(holdAtEndOfAnimation),
m_useExtractedVelocity(useExtractedVelocity)
{
m_animHashKey = animHashKey;
m_animDictIndex = animDictIndex;
Assert((m_animDictIndex != -1) && (m_animHashKey != 0));
#if __DEV
const strStreamingObjectName pAnimDictName = fwAnimManager::GetName(strLocalIndex(m_animDictIndex));
if (pAnimDictName.IsNotNull())
{
m_animDictName = pAnimDictName;
}
const crClip *pClip = fwAnimManager::GetClipIfExistsByDictIndex(m_animDictIndex, m_animHashKey);
if (pClip && pClip->GetName())
{
strcpy(m_animName, pClip->GetName());
}
#endif // __DEV
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_ANIMATION);
}
CTaskVehicleAnimation::~CTaskVehicleAnimation()
{
if (GetVehicle() && GetVehicle()->GetAnimDirector())
{
CGenericClipHelper& helper = GetVehicle()->GetMoveVehicle().GetClipHelper();
helper.BlendOutClip(INSTANT_BLEND_OUT_DELTA);
}
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleAnimation::UpdateFSM( const s32 iState, const FSM_Event iEvent )
{
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
ENABLE_FRAG_OPTIMIZATION_ONLY(pVehicle->GiveFragCacheEntry(true);)
FSM_Begin
// Entry point
FSM_State(State_Start)
FSM_OnUpdate
return Start_OnUpdate(pVehicle);
// Play an animation
FSM_State(State_PlayingAnim)
FSM_OnEnter
PlayingAnim_OnEnter(pVehicle);
FSM_OnUpdate
return PlayingAnim_OnUpdate(pVehicle);
FSM_OnExit
PlayingAnim_OnExit(pVehicle);
// End quit - its finished.
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleAnimation::Start_OnUpdate(CVehicle* UNUSED_PARAM(pVehicle))
{
if(Verifyf(fwAnimManager::GetClipIfExistsByDictIndex(m_animDictIndex, m_animHashKey), "Animation does not exist"))
{
SetState(State_PlayingAnim);
return FSM_Continue;
}
else
{
return FSM_Quit;
}
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleAnimation::PlayingAnim_OnEnter( CVehicle* pVehicle )
{
pVehicle->m_nVehicleFlags.bAnimateJoints = true;
StartAnim(pVehicle);
}
///////////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleAnimation::IsAnimFinished() const
{
if( GetState() == State_PlayingAnim || GetState() == State_Finish)
{
const CGenericClipHelper& helper = GetVehicle()->GetMoveVehicle().GetClipHelper();
if (helper.GetLooped()==false)
{
float currentPhase = helper.GetPhase();
if(m_playBackPhase > 0.0f)
{
if ( currentPhase <= 0.01f )
{
return true;
}
}
else
{
if ( currentPhase >= 0.99f )
{
return true;
}
}
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleAnimation::PlayingAnim_OnUpdate( CVehicle* pVehicle )
{
CGenericClipHelper& helper = pVehicle->GetMoveVehicle().GetClipHelper();
// If there is no anim, the task will have finished
if(helper.GetClip()==NULL)
{
SetState(State_Finish);
}
else
{
if(!m_holdAtEndOfAnimation)
{
if(IsAnimFinished())
{
SetState(State_Finish);
}
}
if(m_useExtractedVelocity)
{
if (GetClipHelper()->GetClip())
{
const crClip* pClip = GetClipHelper()->GetClip();
static bool s_bEnableVehicleMoverExtraction = true;
if (s_bEnableVehicleMoverExtraction && fwAnimHelpers::HasMoverTranslation(*pClip))
{
fwDynamicEntityComponent *vehDynComp = pVehicle->CreateDynamicComponentIfMissing();
const Vector3 vecExtractelVel(vehDynComp->GetAnimatedVelocity());
float fExtractedAngularVel = vehDynComp->GetAnimatedAngularVelocity();
pVehicle->SetVelocity(vecExtractelVel);
float fDesiredHeading = fwAngle::LimitRadianAngleSafe(fExtractedAngularVel);
Vector3 vAngVel = VEC3_ZERO;
vAngVel += VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetC()) * (fDesiredHeading);
pVehicle->SetAngVelocity(vAngVel);
}
}
}
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleAnimation::PlayingAnim_OnExit(CVehicle* pVehicle)
{
pVehicle->m_nVehicleFlags.bAnimateJoints = false;
CGenericClipHelper& helper = pVehicle->GetMoveVehicle().GetClipHelper();
helper.BlendOutClip(INSTANT_BLEND_OUT_DELTA);
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleAnimation::StartAnim(CVehicle* pVehicle)
{
pVehicle->InitAnimLazy();
Assert(pVehicle->GetAnimDirector());
CGenericClipHelper& helper = pVehicle->GetMoveVehicle().GetClipHelper();
helper.BlendInClipByDictAndHash(pVehicle, m_animDictIndex, m_animHashKey, INSTANT_BLEND_IN_DELTA, INSTANT_BLEND_OUT_DELTA, false, m_holdAtEndOfAnimation );
//Stick to the last frame once the anim has finished.
if ( vehicleVerifyf(helper.GetClip()!=NULL, "Animation failed to start") )
{
helper.SetPhase(m_playBackPhase);
helper.SetRate(m_playBackRate);
}
}
////////////////////////////////////////////////////////////////////////////////////
#if !__FINAL
const char * CTaskVehicleAnimation::GetStaticStateName( s32 iState )
{
Assert(iState>=State_Start&&iState<=State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_PlayingAnim",
"State_Finish"
};
return aStateNames[iState];
}
#endif
///////////////////////////////////////////////////////////////////////////////////////
float CTaskVehicleConvertibleRoof::ms_fMaximumSpeedToOpenRoofSq = 10.0f * 10.0f;
u32 CTaskVehicleConvertibleRoof::ms_uTimeToHoldButtonDown = 250;
static const s32 MAX_ROOF_PARTS = 3;
eHierarchyId sRoofIds[MAX_ROOF_PARTS] =
{
VEH_MISC_A,
VEH_ROOF,
VEH_ROOF2,
};
CTaskVehicleConvertibleRoof::CTaskVehicleConvertibleRoof( bool bInstantlyMoveRoof, bool bRoofLowered ) :
#if DISPLAY_HELP_TEXT
m_HelpMessageDisplayed(false),
m_MessageTimeDisplayedFor(0),
#endif
m_bInstantlyMoveRoof(bInstantlyMoveRoof),
m_DesiredStateAfterClosingBoot(State_Invalid),
m_nStuckCounter(0),
m_bOkToDecrementCounter(true),
m_bWaitingOnDoorClosing(false),
m_bMechanismShouldJam(false),
m_bHasButtonBeenUp(false)
{
SetInternalTaskType(CTaskTypes::TASK_VEHICLE_CONVERTIBLE_ROOF);
if (bRoofLowered)
SetState(State_Roof_Lowered);
}
CTaskVehicleConvertibleRoof::~CTaskVehicleConvertibleRoof()
{
}
///////////////////////////////////////////////////////////////////////////////////////
CTaskVehicleConvertibleRoof::eConvertibleStates CTaskVehicleConvertibleRoof::GetRoofState()
{
CVehicle* pVehicle = GetVehicle();
CCarDoor *pDoor = pVehicle->GetDoorFromId( VEH_BOOT );
switch(GetState())
{
case State_Roof_Raised:
return STATE_RAISED;
case State_Lower_Roof:
return STATE_LOWERING;
case State_Roof_Lowered:
if( pDoor != NULL &&
( !pDoor->GetIsLatched( pVehicle ) || pDoor->GetEverKnockedOpen() || pVehicle->IsUpsideDown() ) )
{
return STATE_ROOF_STUCK_LOWERED;
}
return STATE_LOWERED;
case State_Raise_Roof:
return STATE_RAISING;
case State_Closing_Boot:
return STATE_CLOSING_BOOT;
case State_Roof_Stuck_Raised:
return STATE_ROOF_STUCK_RAISED;
case State_Roof_Stuck_Lowered:
return STATE_ROOF_STUCK_LOWERED;
default:
vehicleAssertf(0, "Invalid convertible roof state");
break;
}
//Default state
return STATE_RAISED;
}
///////////////////////////////////////////////////////////////////////////////////////
float CTaskVehicleConvertibleRoof::GetRoofProgress()
{
switch(GetState())
{
case State_Lower_Roof:
case State_Raise_Roof:
case State_Closing_Boot:
{
CVehicle *pVehicle = GetVehicle();
CMoveVehicle& move = pVehicle->GetMoveVehicle();
return move.GetMechanismPhase();
}
break;
case State_Roof_Raised:
case State_Roof_Stuck_Raised:
return 0.0f;
break;
case State_Roof_Lowered:
case State_Roof_Stuck_Lowered:
return 1.0f;
break;
default:
vehicleAssertf(0, "Invalid convertible roof state");
return 0.0f;
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleConvertibleRoof::UpdateFSM( const s32 iState, const FSM_Event iEvent )
{
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
/*
TUNE_BOOL(DEBUG_CONVERTIBLE_FSM, false);
if(DEBUG_CONVERTIBLE_FSM)
{
grcDebugDraw::Text(pVehicle->GetTransform().GetPosition(), Color_white, GetStaticStateName(iState), true);
}
*/
FSM_Begin
FSM_State(State_Roof_Raised)
FSM_OnEnter
RoofRaised_OnEnter(pVehicle);
FSM_OnUpdate
return RoofRaised_OnUpdate(pVehicle);
FSM_State(State_Lower_Roof)
FSM_OnEnter
LowerRoof_OnEnter(pVehicle);
FSM_OnUpdate
return LowerRoof_OnUpdate(pVehicle);
FSM_State(State_Roof_Lowered)
FSM_OnEnter
RoofLowered_OnEnter(pVehicle);
FSM_OnUpdate
return RoofLowered_OnUpdate(pVehicle);
FSM_State(State_Raise_Roof)
FSM_OnEnter
RaiseRoof_OnEnter(pVehicle);
FSM_OnUpdate
return RaiseRoof_OnUpdate(pVehicle);
FSM_State(State_Closing_Boot)
FSM_OnEnter
ClosingBoot_OnEnter(pVehicle);
FSM_OnUpdate
return ClosingBoot_OnUpdate(pVehicle);
FSM_OnExit
ClosingBoot_OnExit(pVehicle);
FSM_State(State_Roof_Stuck_Raised)
FSM_OnEnter
RoofStuckRaised_OnEnter(pVehicle);
FSM_OnUpdate
return RoofStuckRaised_OnUpdate(pVehicle);
FSM_State(State_Roof_Stuck_Lowered)
FSM_OnEnter
RoofStuckLowered_OnEnter(pVehicle);
FSM_OnUpdate
return RoofStuckLowered_OnUpdate(pVehicle);
// End quit - its finished.
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::CleanUp()
{
// Roof animation can continue after abort, so make sure vehicle flag is set to eventual position
CVehicle* pVehicle = GetVehicle();
if (GetState() == State_Lower_Roof)
{
pVehicle->m_nVehicleFlags.bRoofLowered = true;
}
else if (GetState() == State_Raise_Roof)
{
pVehicle->m_nVehicleFlags.bRoofLowered = false;
}
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::UnLatchRoof(CVehicle* pVehicle)
{
ENABLE_FRAG_OPTIMIZATION_ONLY(pVehicle->GiveFragCacheEntry(true);)
fragInstGta* pFragInst = pVehicle->GetVehicleFragInst();
Assert(pFragInst);
pFragInst->GetCacheEntry()->LazyArticulatedInit();
for(s32 i = 0; i < MAX_ROOF_PARTS; i++)
{
s32 iBoneIndex = pVehicle->GetBoneIndex(sRoofIds[i]);
s8 nFragGroup = (s8)pVehicle->GetVehicleFragInst()->GetGroupFromBoneIndex(iBoneIndex);
if(nFragGroup > -1 && GetIsLatched(pVehicle, nFragGroup))
{
s32 nGroup = pFragInst->GetType()->GetPhysics(0)->GetAllChildren()[nFragGroup]->GetOwnerGroupPointerIndex();
if(pFragInst->GetCacheEntry()->GetHierInst()->groupBroken->IsClear(nGroup))
{
pFragInst->OpenLatchAbove(nFragGroup);
phBoundComposite* pThisBoundComposite = static_cast<phBoundComposite*>(pVehicle->GetVehicleFragInst()->GetArchetype()->GetBound());
// turn on collision with world when open
if(pThisBoundComposite->GetTypeAndIncludeFlags())
pThisBoundComposite->SetIncludeFlags(nFragGroup, ArchetypeFlags::GTA_VEHICLE_INCLUDE_TYPES);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::LatchRoof(CVehicle* pVehicle)
{
ENABLE_FRAG_OPTIMIZATION_ONLY(pVehicle->GiveFragCacheEntry(true);)
for(s32 i = MAX_ROOF_PARTS-1; i >= 0; i--)
{
s32 iBoneIndex = pVehicle->GetBoneIndex(sRoofIds[i]);
s8 nFragGroup = (s8)pVehicle->GetVehicleFragInst()->GetGroupFromBoneIndex(iBoneIndex);
s32 nComponent = pVehicle->GetVehicleFragInst()->GetComponentFromBoneIndex(iBoneIndex);
if(nFragGroup > -1 && !GetIsLatched(pVehicle, nFragGroup) && pVehicle->IsColliderArticulated())
{
if(pVehicle->GetVehicleFragInst()->GetCached())
{
const phArticulatedCollider& articulatedCollider = *pVehicle->GetVehicleFragInst()->GetCacheEntry()->GetHierInst()->articulatedCollider;
// Find out to what link this fragTypeChild maps.
int linkIndex = articulatedCollider.GetLinkFromComponent(nComponent);
if(linkIndex > 0) // Only set latch if the component is part of the articulated body
{
SetLatch(pVehicle, nFragGroup);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::InitAnimation (CVehicle* pVehicle)
{
if (!pVehicle->GetAnimDirector())
{
pVehicle->InitAnimLazy();
}
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::RoofRaised_OnEnter (CVehicle* pVehicle)
{
#if DISPLAY_HELP_TEXT
CPed *pPed = pVehicle->GetDriver();
if( pPed && pPed->IsLocalPlayer() )
{
CHelpMessage::SetMessageText(TheText.Get("L_ROOF"));//if we have a roof and the hand brakes pressed, lower the roof.
m_MessageTimeDisplayedFor = 0;
m_HelpMessageDisplayed = true;
}
#endif
LatchRoof(pVehicle);
pVehicle->GetVehicleAudioEntity()->StopAllRoofLoops();
pVehicle->ToggleRoofCollision(true);
// Mark any windows which get rolled up as part of the animation as "up".
CConvertibleRoofWindowInfo* pWindowExtension = pVehicle->GetBaseModelInfo()->GetExtension<CConvertibleRoofWindowInfo>();
if(pWindowExtension)
{
atArray<eHierarchyId>& affectedWindowList = pWindowExtension->GetWindowList();
for(int i = 0; i < affectedWindowList.GetCount(); ++i)
{
// Flag this window as having been rolled up.
pVehicle->RollWindowUp(affectedWindowList[i]);
}
}
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
}
///////////////////////////////////////////////////////////////////////////////////////
#if DISPLAY_HELP_TEXT
static dev_s32 siHelpMessageDisplayTime = 3000;
#endif
CTask::FSM_Return CTaskVehicleConvertibleRoof::RoofRaised_OnUpdate (CVehicle* pVehicle)
{
CPed *pPed = pVehicle->GetDriver();
if( pPed && pPed->IsLocalPlayer() )
{
#if DISPLAY_HELP_TEXT
if(m_HelpMessageDisplayed)
{
u32 SystemTimeStep = fwTimer::GetTimeStepInMilliseconds();
m_MessageTimeDisplayedFor += SystemTimeStep;
if (m_MessageTimeDisplayedFor >= siHelpMessageDisplayTime)
{
CHelpMessage::Clear();
m_HelpMessageDisplayed = false;
}
}
#endif
CControl *pControl = pPed->GetControlFromPlayer();
m_bHasButtonBeenUp |= pControl->GetVehicleRoof().IsUp();
if(m_bHasButtonBeenUp && pControl->GetVehicleRoof().HistoryHeldDown(ms_uTimeToHoldButtonDown))
{
if(pVehicle->GetVelocity().Mag2() < ms_fMaximumSpeedToOpenRoofSq && pVehicle->m_nVehicleFlags.bEngineOn
&& (pVehicle->GetTransform().GetC().GetZf()>0.0f))
{
#if DISPLAY_HELP_TEXT
CHelpMessage::Clear();
m_HelpMessageDisplayed = false;
#endif
// If the boot is part of the animation and it isn't latched, it will screw up. Need to close
// the boot before switching to the raising state.
CCarDoor *pDoor = pVehicle->GetDoorFromId(VEH_BOOT);
if(pDoor == NULL || (pDoor->GetIsLatched(pVehicle) && !pDoor->GetEverKnockedOpen() && !pVehicle->IsUpsideDown()))
{
SetState(State_Lower_Roof);
}
else
{
m_DesiredStateAfterClosingBoot = State_Lower_Roof;
SetState(State_Closing_Boot);
}
}
}
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::LowerRoof_OnEnter (CVehicle* pVehicle)
{
m_animRequester.Release();
if (!AssertVerify(pVehicle))
return;
if (!AssertVerify(pVehicle->GetVehicleModelInfo()))
return;
InitAnimation(pVehicle);
// smash any partially broken windows if required
g_vehicleGlassMan.ConvertibleRoofAnimStart(pVehicle);
// Find the animation
// These anims need renaming and adding to animid's instead of loaded in from vehicles.ide
u32 iAnimHashKey = pVehicle->GetVehicleModelInfo()->GetConvertibleRoofAnimNameHash();
// Stream the anim dictionary
strLocalIndex iSlot = pVehicle->GetVehicleModelInfo()->GetClipDictionaryIndex();
if( vehicleVerifyf(iSlot.IsValid(), "No animation dictionary found for vehicle. Check vehicles.ide") )
{
if(!m_animRequester.Request(iSlot,fwAnimManager::GetStreamingModuleId(),STRFLAG_PRIORITY_LOAD))
{
// Force load
CStreaming::LoadAllRequestedObjects(true);
}
Assert(m_animRequester.HasLoaded());
}
UnLatchRoof(pVehicle);
float aRoofStrength[MAX_ROOF_PARTS] =
{
400.0f,
800.0f,
1400.0f,
};
if (!AssertVerify(pVehicle->GetFragInst()))
return;
if (!AssertVerify(pVehicle->GetFragInst()->GetCacheEntry()))
return;
if (!AssertVerify(pVehicle->GetFragInst()->GetType()))
return;
for(int i = 0; i < MAX_ROOF_PARTS; i++)
{
fragHierarchyInst* pHierInst = pVehicle->GetFragInst()->GetCacheEntry()->GetHierInst();
int iBoneIndex = pVehicle->GetVehicleModelInfo()->GetBoneIndex(sRoofIds[i]);
int iComponentIndex = pVehicle->GetFragInst()->GetType()->GetComponentFromBoneIndex(0,iBoneIndex);
if(iBoneIndex > -1 && iComponentIndex > -1)
{
if (!AssertVerify(pHierInst))
return;
phArticulatedCollider* pArticulatedCollider = pHierInst->articulatedCollider;
if (!AssertVerify(pArticulatedCollider))
return;
if(pVehicle->GetFragInst() && pVehicle->GetFragInst()->GetCacheEntry())
pVehicle->GetFragInst()->GetCacheEntry()->LazyArticulatedInit();
if(!Verifyf(iComponentIndex < pArticulatedCollider->GetNumComponents(),"iComponentIndex[%d] !< pArticulatedCollider->GetNumComponents()[%d] for vehicle[%s] : %s",iComponentIndex,pArticulatedCollider->GetNumComponents(),pVehicle->GetDebugName(), (pVehicle->GetCurrentPhysicsInst() && pVehicle->GetCurrentPhysicsInst()->GetArchetype() ? pVehicle->GetCurrentPhysicsInst()->GetArchetype()->GetFilename(): " noname ") ))
return;
int linkIndex = pArticulatedCollider->GetLinkFromComponent(iComponentIndex);
if(linkIndex > 0)
{
if (!AssertVerify(pHierInst->body))
return;
phJoint* pJoint = &pHierInst->body->GetJoint(linkIndex - 1);
if (pJoint)
{
pJoint->SetDriveState(phJoint::DRIVE_STATE_ANGLE);
switch (pJoint->GetJointType())
{
case phJoint::JNT_1DOF:
{
phJoint1Dof& joint1Dof = *static_cast<phJoint1Dof*>(pJoint);
joint1Dof.SetMuscleAngleStrength(aRoofStrength[i]);
joint1Dof.SetAngleLimits(-2.0f*PI, 2.0f*PI);//remove angle limits, wont be necessary once model is setup properly
}
}
}
}
}
}
if(m_bInstantlyMoveRoof)
{
pVehicle->m_nVehicleFlags.bForcePostCameraAnimUpdate = 1;
}
CMoveVehicle& move = pVehicle->GetMoveVehicle();
float playbackRate = 1.0f;
float playbackPhase = 0.0f;
if(!move.IsMechanismActive())
{
if(m_bInstantlyMoveRoof)
{
m_bInstantlyMoveRoof = false;
playbackPhase = 1.0f;
}
move.StartMechanism(fwAnimManager::GetClipIfExistsByDictIndex(iSlot.Get(), iAnimHashKey), 0.0f, playbackPhase, playbackRate);
}
else
{
move.OpenMechanism(playbackRate);
if(m_bInstantlyMoveRoof)
{
m_bInstantlyMoveRoof = false;
move.SetMechanismPhase(1.0f);
}
}
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleConvertibleRoof::LowerRoof_OnUpdate (CVehicle* pVehicle)
{
static float sfConversionRevertMinPhase = 0.40f;
CMoveVehicle& move = pVehicle->GetMoveVehicle();
if (move.IsMechanismFullyOpen())
{
pVehicle->m_nVehicleFlags.bRoofLowered = true;
SetState(State_Roof_Lowered);
}
if(pVehicle->GetVelocity().Mag2() < ms_fMaximumSpeedToOpenRoofSq)
{
CPed *pPed = pVehicle->GetDriver();
if( pPed && pPed->IsLocalPlayer() )
{
CControl *pControl = pPed->GetControlFromPlayer();
m_bHasButtonBeenUp |= pControl->GetVehicleRoof().IsUp();
if(m_bHasButtonBeenUp && pControl->GetVehicleRoof().HistoryHeldDown(ms_uTimeToHoldButtonDown))
{
SetState(State_Raise_Roof);
}
}
}
else
{
float fPhase = move.GetMechanismPhase();
// If vehicle is speed up, revert the convert process if it hasn't reached the half way
if(fPhase > 0.0f && fPhase < sfConversionRevertMinPhase)
{
SetState(State_Raise_Roof);
}
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::RoofLowered_OnEnter (CVehicle* pVehicle)
{
CPed *pPed = pVehicle->GetDriver();
if( pPed && pPed->IsLocalPlayer() )
{
#if DISPLAY_HELP_TEXT
CHelpMessage::SetMessageText(TheText.Get("R_ROOF"));//if we have a roof and the hand brakes pressed, lower the roof.
m_MessageTimeDisplayedFor = 0;
m_HelpMessageDisplayed = true;
#endif
}
//This latches the roof in the closed poisition, so i'm not doing it for now.
//LatchRoof(pVehicle);
pVehicle->GetVehicleAudioEntity()->StopAllRoofLoops();
pVehicle->ToggleRoofCollision(false);
// Mark any windows which get rolled down as part of the animation as "down".
CConvertibleRoofWindowInfo* pWindowExtension = pVehicle->GetBaseModelInfo()->GetExtension<CConvertibleRoofWindowInfo>();
if(pWindowExtension)
{
atArray<eHierarchyId>& affectedWindowList = pWindowExtension->GetWindowList();
for(int i = 0; i < affectedWindowList.GetCount(); ++i)
{
// Flag this window as having been rolled down.
pVehicle->RolldownWindow(affectedWindowList[i]);
}
}
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleConvertibleRoof::RoofLowered_OnUpdate (CVehicle* pVehicle)
{
CPed *pPed = pVehicle->GetDriver();
if( pPed && pPed->IsLocalPlayer() )
{
#if DISPLAY_HELP_TEXT
if(m_HelpMessageDisplayed)
{
u32 SystemTimeStep = fwTimer::GetTimeStepInMilliseconds();
m_MessageTimeDisplayedFor += SystemTimeStep;
if (m_MessageTimeDisplayedFor >= siHelpMessageDisplayTime)
{
CHelpMessage::Clear();
m_HelpMessageDisplayed = false;
}
}
#endif
CControl *pControl = pPed->GetControlFromPlayer();
m_bHasButtonBeenUp |= pControl->GetVehicleRoof().IsUp();
if(m_bHasButtonBeenUp && pControl->GetVehicleRoof().HistoryHeldDown(ms_uTimeToHoldButtonDown))
{
if(pVehicle->GetVelocity().Mag2() < ms_fMaximumSpeedToOpenRoofSq && pVehicle->m_nVehicleFlags.bEngineOn
&& (pVehicle->GetTransform().GetC().GetZf()>0.0f))
{
#if DISPLAY_HELP_TEXT
CHelpMessage::Clear();
m_HelpMessageDisplayed = false;
#endif
// If the boot is part of the animation and it isn't latched, it will screw up. Need to close
// the boot before switching to the raising state.
CCarDoor *pDoor = pVehicle->GetDoorFromId(VEH_BOOT);
if(pDoor == NULL || (pDoor->GetIsLatched(pVehicle) && !pDoor->GetEverKnockedOpen() && !pVehicle->IsUpsideDown()))
{
SetState(State_Raise_Roof);
}
else
{
m_DesiredStateAfterClosingBoot = State_Raise_Roof;
SetState(State_Closing_Boot);
}
}
}
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::RaiseRoof_OnEnter (CVehicle* pVehicle)
{
m_animRequester.Release();
InitAnimation(pVehicle);
Assert(pVehicle->GetVehicleModelInfo());
// Find the animation
// These anims need renaming and adding to animid's instead of loaded in from vehicles.ide
u32 iAnimHashKey = pVehicle->GetVehicleModelInfo()->GetConvertibleRoofAnimNameHash();
// Stream the anim dictionary
strLocalIndex iSlot = pVehicle->GetVehicleModelInfo()->GetClipDictionaryIndex();
if( vehicleVerifyf(iSlot.IsValid(), "No animation dictionary found for vehicle. Check vehicles.ide") )
{
if(!m_animRequester.Request(iSlot,fwAnimManager::GetStreamingModuleId(),STRFLAG_PRIORITY_LOAD))
{
// Force load
CStreaming::LoadAllRequestedObjects(true);
}
Assert(m_animRequester.HasLoaded());
}
//Not required as I don't latch the roof when the roof is open.
//UnLatchRoof(pVehicle);
if(m_bInstantlyMoveRoof)
{
pVehicle->m_nVehicleFlags.bForcePostCameraAnimUpdate = 1;
}
float playbackRate = -1.0f;
float playbackPhase = 1.0f;
CMoveVehicle& move = pVehicle->GetMoveVehicle();
if(!move.IsMechanismActive())
{
//if we want to instantly move the roof, set the playback phase to 0.0 i.e already completed
if(m_bInstantlyMoveRoof)
{
m_bInstantlyMoveRoof = false;
playbackPhase = 0.0f;
}
move.StartMechanism(fwAnimManager::GetClipIfExistsByDictIndex(iSlot.Get(), iAnimHashKey), 0.0f, playbackPhase, playbackRate);
}
else
{
move.CloseMechanism(playbackRate);
if(m_bInstantlyMoveRoof)
{
m_bInstantlyMoveRoof = false;
move.SetMechanismPhase(0.0f);
}
}
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
//This will allow rolled down windows (set when spawned) to roll up again
pVehicle->RollupWindows(false);
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleConvertibleRoof::RaiseRoof_OnUpdate (CVehicle* pVehicle)
{
static float sfConversionRevertMaxPhase = 0.50f;
CMoveVehicle& move = pVehicle->GetMoveVehicle();
if(move.IsMechanismFullyClosed())
{
pVehicle->m_nVehicleFlags.bRoofLowered = false;
SetState(State_Roof_Raised);
}
if(pVehicle->GetVelocity().Mag2() < ms_fMaximumSpeedToOpenRoofSq)
{
CPed *pPed = pVehicle->GetDriver();
if( pPed && pPed->IsLocalPlayer() )
{
CControl *pControl = pPed->GetControlFromPlayer();
m_bHasButtonBeenUp |= pControl->GetVehicleRoof().IsUp();
if(m_bHasButtonBeenUp && pControl->GetVehicleRoof().HistoryHeldDown(ms_uTimeToHoldButtonDown))
{
SetState(State_Lower_Roof);
}
}
}
else
{
// If vehicle is speed up, revert the convert process if it hasn't reached the half way
if(move.GetMechanismPhase() > sfConversionRevertMaxPhase)
{
SetState(State_Lower_Roof);
}
}
return FSM_Continue;
}
////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::ClosingBoot_OnEnter(CVehicle* UNUSED_PARAM(pVehicle))
{
}
////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleConvertibleRoof::ClosingBoot_OnUpdate(CVehicle* pVehicle)
{
CCarDoor* pBoot = pVehicle->GetDoorFromId(VEH_BOOT);
// Door can't be closed if it's broken off. In this case, stutter through the first few
// frames of the roof animation to give the user some visual clue that the roof is going
// nowhere.
if(!pBoot || (pBoot && (pBoot->GetFlag(CCarDoor::IS_BROKEN_OFF) || pBoot->GetEverKnockedOpen() || pVehicle->IsUpsideDown())))
{
m_bMechanismShouldJam = true;
m_bWaitingOnDoorClosing = false;
}
else
{
m_bMechanismShouldJam = false;
}
if(pBoot && !pBoot->GetIsClosed() && !m_bMechanismShouldJam)
{
if( !m_bWaitingOnDoorClosing )
{
pBoot->SetTargetDoorOpenRatio(0.0f, CCarDoor::DRIVEN_AUTORESET|CCarDoor::WILL_LOCK_DRIVEN);
m_bWaitingOnDoorClosing = true;
}
}
else
{
m_bMechanismShouldJam = true;
if( pBoot &&
!pBoot->GetFlag(CCarDoor::IS_BROKEN_OFF) )
{
pBoot->SetShutAndLatched(pVehicle);
}
Assert(m_DesiredStateAfterClosingBoot > State_Invalid);
switch(m_DesiredStateAfterClosingBoot)
{
case State_Lower_Roof:
case State_Roof_Stuck_Raised:
{
pVehicle->m_nVehicleFlags.bRoofLowered = false;
if(m_bMechanismShouldJam)
SetState(State_Roof_Stuck_Raised);
else
SetState(State_Roof_Raised);
}
break;
case State_Raise_Roof:
case State_Roof_Stuck_Lowered:
{
pVehicle->m_nVehicleFlags.bRoofLowered = true;
if(m_bWaitingOnDoorClosing)
{
SetState(State_Raise_Roof);
}
else if(m_bMechanismShouldJam)
{
SetState(State_Roof_Stuck_Lowered);
}
else
{
SetState(State_Roof_Lowered);
}
}
break;
default:
Assertf(false, "Unexpected desired state: %s.", GetStaticStateName(m_DesiredStateAfterClosingBoot));
}
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::ClosingBoot_OnExit(CVehicle* UNUSED_PARAM(pVehicle))
{
m_bWaitingOnDoorClosing = false;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::RoofStuckRaised_OnEnter(CVehicle* pVehicle)
{
// Set the number of times we want to try lowering the roof before giving up.
m_nStuckCounter = 3;
m_bOkToDecrementCounter = true;
// Make sure that the lowering animation is streamed in and started.
LowerRoof_OnEnter(pVehicle);
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleConvertibleRoof::RoofStuckRaised_OnUpdate(CVehicle* pVehicle)
{
CMoveVehicle& move = pVehicle->GetMoveVehicle();
static float sfRaisingAnimationMinPhase = 0.10f;
static float sfStuckLoweringPlaybackRate = 2.0f;
CCarDoor *pDoor = pVehicle->GetDoorFromId(VEH_BOOT);
// If car gets healed by a cheat or if we have tried enough times to raise the roof, we
// don't want to get stuck in this state.
if(pDoor == NULL || (pDoor && !(pDoor->GetFlag(CCarDoor::IS_BROKEN_OFF) || pDoor->GetEverKnockedOpen() || pVehicle->IsUpsideDown())) || !m_nStuckCounter)
{
pVehicle->m_nVehicleFlags.bRoofLowered = false;
SetState(State_Roof_Raised);
}
// If the boot is part of the animation and it isn't latched, it will screw up. Need to close
// the boot before switching to the raising state.
if( pDoor != NULL && !pDoor->GetFlag(CCarDoor::IS_BROKEN_OFF) &&
(!pDoor->GetIsLatched(pVehicle) || !pDoor->GetIsClosed()))
{
m_DesiredStateAfterClosingBoot = State_Roof_Stuck_Raised;
SetState(State_Closing_Boot);
return FSM_Continue;
}
if(move.GetMechanismPhase() > sfRaisingAnimationMinPhase)
{
move.CloseMechanism(-sfStuckLoweringPlaybackRate);
if(m_bOkToDecrementCounter)
{
--m_nStuckCounter;
m_bOkToDecrementCounter = false;
if(pVehicle->GetVehicleAudioEntity()->GetAudioVehicleType() == AUD_VEHICLE_CAR)
{
static_cast<audCarAudioEntity*>(pVehicle->GetVehicleAudioEntity())->TriggerRoofStuckSound();
}
}
}
else if(move.IsMechanismFullyClosed())
{
move.OpenMechanism(sfStuckLoweringPlaybackRate);
m_bOkToDecrementCounter = true;
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::RoofStuckLowered_OnEnter(CVehicle* pVehicle)
{
// Set the number of times we want to try raising the roof before giving up.
m_nStuckCounter = 3;
m_bOkToDecrementCounter = true;
// Make sure that the raising animation is streamed in and started.
RaiseRoof_OnEnter(pVehicle);
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleConvertibleRoof::RoofStuckLowered_OnUpdate(CVehicle* pVehicle)
{
CMoveVehicle& move = pVehicle->GetMoveVehicle();
static float sfLoweringAnimationMaxPhase = 0.90f;
static float sfStuckRaisingPlaybackRate = 2.0f;
CCarDoor *pDoor = pVehicle->GetDoorFromId(VEH_BOOT);
// If car gets healed by a cheat or if we have tried enough times to lower the roof, we
// don't want to get stuck in this state.
if(pDoor == NULL || (pDoor && !(pDoor->GetFlag(CCarDoor::IS_BROKEN_OFF) || pDoor->GetEverKnockedOpen() || pVehicle->IsUpsideDown())) || !m_nStuckCounter)
{
pVehicle->m_nVehicleFlags.bRoofLowered = true;
SetState(State_Roof_Lowered);
}
// If the boot is part of the animation and it isn't latched, it will screw up. Need to close
// the boot before switching to the raising state.
if(pDoor != NULL && !pDoor->GetFlag(CCarDoor::IS_BROKEN_OFF) &&
(!pDoor->GetIsLatched(pVehicle) || !pDoor->GetIsClosed()))
{
m_DesiredStateAfterClosingBoot = State_Roof_Stuck_Lowered;
SetState(State_Closing_Boot);
return FSM_Continue;
}
if(move.GetMechanismPhase() < sfLoweringAnimationMaxPhase)
{
move.OpenMechanism(sfStuckRaisingPlaybackRate);
if(m_bOkToDecrementCounter)
{
--m_nStuckCounter;
m_bOkToDecrementCounter = false;
if(pVehicle->GetVehicleAudioEntity()->GetAudioVehicleType() == AUD_VEHICLE_CAR)
{
static_cast<audCarAudioEntity*>(pVehicle->GetVehicleAudioEntity())->TriggerRoofStuckSound();
}
}
}
else if(move.IsMechanismFullyOpen())
{
move.CloseMechanism(-sfStuckRaisingPlaybackRate);
m_bOkToDecrementCounter = true;
}
return FSM_Continue;
}
////////////////////////////////////////////////////////////////////////////////////
bool CTaskVehicleConvertibleRoof::GetIsLatched(CVehicle* pParent, s32 roofFragGroup)
{
if(roofFragGroup < 0)
return false;
if(pParent->GetVehicleFragInst()->GetCached())
{
fragHierarchyInst* pHierInst = pParent->GetVehicleFragInst()->GetCacheEntry()->GetHierInst();
if(pHierInst->latchedJoints != NULL && pHierInst->latchedJoints->IsSet(roofFragGroup))
{
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleConvertibleRoof::SetLatch(CVehicle* pParent, s32 roofFragGroup)
{
if(!pParent || !pParent->GetVehicleFragInst())
return;
const fragType* pFragType = pParent->GetVehicleFragInst()->GetType();
Assert(!GetIsLatched(pParent, roofFragGroup));
if(roofFragGroup > 0
&& pParent->GetVehicleFragInst()->GetCacheEntry()
&& pParent->GetVehicleFragInst()->GetCacheEntry()->GetHierInst()
&& pParent->GetVehicleFragInst()->GetCacheEntry()->GetHierInst()->latchedJoints)
{
pParent->GetVehicleFragInst()->CloseLatchAbove(roofFragGroup);
phBoundComposite* pThisBoundComposite = static_cast<phBoundComposite*>(pParent->GetVehicleFragInst()->GetArchetype()->GetBound());
phBoundComposite* pTemplateBoundComposite = pFragType->GetPhysics(0)->GetCompositeBounds();
int nParentGroup = pFragType->GetPhysics(0)->GetAllChildren()[roofFragGroup]->GetOwnerGroupPointerIndex();
fragTypeGroup* pParentGroup = pFragType->GetPhysics(0)->GetAllGroups()[nParentGroup];
for (int childIndex = 0; childIndex < pParentGroup->GetNumChildren(); ++childIndex)
{
int nChild = pParentGroup->GetChildFragmentIndex() + childIndex;
pThisBoundComposite->SetCurrentMatrix(nChild, pTemplateBoundComposite->GetCurrentMatrix(nChild));
pThisBoundComposite->SetLastMatrix(nChild, pTemplateBoundComposite->GetCurrentMatrix(nChild));
}
for(int groupIndex = 0; groupIndex < pParentGroup->GetNumChildGroups(); ++groupIndex)
{
fragTypeGroup* pGroup = pFragType->GetPhysics(0)->GetAllGroups()[pParentGroup->GetChildGroupsPointersIndex() + groupIndex];
for (int childIndex = 0; childIndex < pGroup->GetNumChildren(); ++childIndex)
{
int nChild = pGroup->GetChildFragmentIndex() + childIndex;
pThisBoundComposite->SetCurrentMatrix(nChild, pTemplateBoundComposite->GetCurrentMatrix(nChild));
pThisBoundComposite->SetLastMatrix(nChild, pTemplateBoundComposite->GetCurrentMatrix(nChild));
}
}
// turn off collision with roof and the rest of the world when it's shut
if(pThisBoundComposite->GetTypeAndIncludeFlags())
pThisBoundComposite->SetIncludeFlags(roofFragGroup, ArchetypeFlags::GTA_CORE_SHAPETEST_TYPES);
}
}
////////////////////////////////////////////////////////////////////////////////////
#if !__FINAL
const char * CTaskVehicleConvertibleRoof::GetStaticStateName( s32 iState )
{
Assert(iState>=State_Roof_Raised&&iState<=State_Finish);
static const char* aStateNames[] =
{
"State_Roof_Raised",
"State_Lower_Roof",
"State_Roof_Lowered",
"State_Raise_Roof",
"State_Closing_Boot",
"State_Roof_Stuck_Raised",
"State_Roof_Stuck_Lowered",
"State_Finish",
};
return aStateNames[iState];
}
#endif
///////////////////////////////////////////////////////////////////////////////////////
CTaskVehicleTransformToSubmarine::CTaskVehicleTransformToSubmarine( bool bInstantlyTransform ) :
m_bInstantlyTransform( bInstantlyTransform ),
m_bHasButtonBeenUp( true ),
m_bUseAlternateInput( false ),
m_fTransformRateForNextAnimation( -1.0f )
{
SetInternalTaskType( CTaskTypes::TASK_VEHICLE_TRANSFORM_TO_SUBMARINE );
}
///////////////////////////////////////////////////////////////////////////////////////
CTaskVehicleTransformToSubmarine::eTransformStates CTaskVehicleTransformToSubmarine::GetTransformState()
{
switch( GetState() )
{
case State_Car:
return STATE_CAR;
case State_Transform_To_Submarine:
return STATE_TRANSFORMING_TO_SUBMARINE;
case State_Submarine:
return STATE_SUBMARINE;
case State_Transform_To_Car:
return STATE_TRANSFORMING_TO_CAR;
default:
vehicleAssertf(0, "Invalid vehicle transform state");
break;
}
//Default state
return STATE_CAR;
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleTransformToSubmarine::UpdateFSM( const s32 iState, const FSM_Event iEvent )
{
CVehicle *pVehicle = GetVehicle(); //Get the vehicle ptr.
FSM_Begin
FSM_State( State_Car )
FSM_OnEnter
Car_OnEnter( pVehicle );
FSM_OnUpdate
return Car_OnUpdate( pVehicle );
FSM_State( State_Transform_To_Submarine )
FSM_OnEnter
TransformToSubmarine_OnEnter( pVehicle );
FSM_OnUpdate
return TransformToSubmarine_OnUpdate( pVehicle );
FSM_State( State_Submarine )
FSM_OnEnter
Submarine_OnEnter( pVehicle );
FSM_OnUpdate
return Submarine_OnUpdate( pVehicle );
FSM_State( State_Transform_To_Car )
FSM_OnEnter
TransformToCar_OnEnter(pVehicle);
FSM_OnUpdate
return TransformToCar_OnUpdate(pVehicle);
// End quit - its finished.
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleTransformToSubmarine::Car_OnEnter( CVehicle* pVehicle )
{
// TODO: DO WE NEED TO STOP ANY AUDIO
//pVehicle->GetVehicleAudioEntity()->StopAllRoofLoops();
// TODO: PROBABLY NEED TO TOGGLE COLLISION ON THE BITS THAT STICK OUT WHEN IN SUBMARINE MODE
//pVehicle->ToggleRoofCollision(true);
CSubmarineCar* pSubmarineCar = static_cast< CSubmarineCar* >( pVehicle );
pSubmarineCar->SetTransformingComplete();
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleTransformToSubmarine::Car_OnUpdate( CVehicle* pVehicle )
{
CPed *pPed = pVehicle->GetDriver();
if( pPed && pPed->IsLocalPlayer() )
{
CControl *pControl = pPed->GetControlFromPlayer();
m_bHasButtonBeenUp |= pControl->GetVehicleRoof().IsUp();
bool beginTransform = m_bHasButtonBeenUp && pControl->GetVehicleRoof().HistoryHeldDown( CTaskVehicleConvertibleRoof::ms_uTimeToHoldButtonDown );
if( m_bUseAlternateInput )
{
beginTransform = pControl->GetVehicleTransform().IsReleased() && !pControl->GetVehicleTransform().IsReleasedAfterHistoryHeldDown( CTaskVehicleConvertibleRoof::ms_uTimeToHoldButtonDown );
}
if( beginTransform &&
!pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableSpycarTransformation) )
{
if( pVehicle->m_nVehicleFlags.bEngineOn )
{
SetState( State_Transform_To_Submarine );
}
}
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleTransformToSubmarine::TransformToSubmarine_OnEnter( CVehicle* pVehicle )
{
float playbackRate = 1.5f;
float playbackPhase = 0.0f;
CMoveVehicle& move = pVehicle->GetMoveVehicle();
if (m_fTransformRateForNextAnimation > 0.0f)
{
playbackRate = m_fTransformRateForNextAnimation;
m_fTransformRateForNextAnimation = -1.0f;
}
CSubmarineCar* pSubmarineCar = static_cast< CSubmarineCar* >( pVehicle );
pSubmarineCar->SetTransformingToSubmarine();
if(!move.IsMechanismActive())
{
strLocalIndex iSlot = pVehicle->GetVehicleModelInfo()->GetClipDictionaryIndex();
u32 iAnimHashKey = pVehicle->GetVehicleModelInfo()->GetConvertibleRoofAnimNameHash();
if( m_bInstantlyTransform )
{
m_bInstantlyTransform = false;
playbackPhase = 1.0f;
}
move.StartMechanism( fwAnimManager::GetClipIfExistsByDictIndex( iSlot.Get(), iAnimHashKey ), 0.0f, playbackPhase, playbackRate );
}
else
{
move.OpenMechanism(playbackRate);
if( m_bInstantlyTransform )
{
m_bInstantlyTransform = false;
move.SetMechanismPhase( 1.0f );
}
}
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
}
///////////////////////////////////////////////////////////////////////////////////////
static dev_float sfEnterSubmarineModeAnimationPhase = 0.35f;
CTask::FSM_Return CTaskVehicleTransformToSubmarine::TransformToSubmarine_OnUpdate( CVehicle* pVehicle )
{
CMoveVehicle& move = pVehicle->GetMoveVehicle();
if( move.IsMechanismFullyOpen() )
{
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( pVehicle );
submarineCar->SetSubmarineMode( true, true );
SetState( State_Submarine );
}
else
{
if( move.GetMechanismPhase() > sfEnterSubmarineModeAnimationPhase )
{
// Set the vehicle to be in submarine mode
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( pVehicle );
submarineCar->SetSubmarineMode( true, false );
}
}
if( !pVehicle->GetIsVisibleInSomeViewportThisFrame() )
{
pVehicle->m_nDEflags.bForcePrePhysicsAnimUpdate = true;
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleTransformToSubmarine::Submarine_OnEnter( CVehicle* pVehicle )
{
CSubmarineCar* pSubmarineCar = static_cast< CSubmarineCar* >( pVehicle );
pSubmarineCar->SetTransformingComplete();
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleTransformToSubmarine::Submarine_OnUpdate( CVehicle* pVehicle )
{
CPed *pPed = pVehicle->GetDriver();
if( pPed &&
pPed->IsLocalPlayer() )
{
CControl *pControl = pPed->GetControlFromPlayer();
m_bHasButtonBeenUp |= pControl->GetVehicleRoof().IsUp();
bool beginTransform = m_bHasButtonBeenUp && pControl->GetVehicleRoof().HistoryHeldDown( CTaskVehicleConvertibleRoof::ms_uTimeToHoldButtonDown );
if( m_bUseAlternateInput )
{
beginTransform = pControl->GetVehicleTransform().IsReleased() && !pControl->GetVehicleTransform().IsReleasedAfterHistoryHeldDown( CTaskVehicleConvertibleRoof::ms_uTimeToHoldButtonDown );
}
if( beginTransform &&
!pPed->GetPedResetFlag(CPED_RESET_FLAG_DisableSpycarTransformation) )
{
if( pVehicle->m_nVehicleFlags.bEngineOn )
{
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( pVehicle );
submarineCar->SetSubmarineMode( true, false );
SetState( State_Transform_To_Car );
}
}
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CTaskVehicleTransformToSubmarine::TransformToCar_OnEnter( CVehicle* pVehicle )
{
float playbackRate = -1.5f;
float playbackPhase = 1.0f;
CMoveVehicle& move = pVehicle->GetMoveVehicle();
if (m_fTransformRateForNextAnimation > 0.0f)
{
playbackRate = -m_fTransformRateForNextAnimation;
m_fTransformRateForNextAnimation = -1.0f;
}
CSubmarineCar* pSubmarineCar = static_cast< CSubmarineCar* >( pVehicle );
pSubmarineCar->SetTransformingToCar(m_bInstantlyTransform);
if(!move.IsMechanismActive())
{
strLocalIndex iSlot = pVehicle->GetVehicleModelInfo()->GetClipDictionaryIndex();
u32 iAnimHashKey = pVehicle->GetVehicleModelInfo()->GetConvertibleRoofAnimNameHash();
if( m_bInstantlyTransform )
{
m_bInstantlyTransform = false;
playbackPhase = 0.0f;
}
move.StartMechanism( fwAnimManager::GetClipIfExistsByDictIndex( iSlot.Get(), iAnimHashKey ), 0.0f, playbackPhase, playbackRate );
}
else
{
move.CloseMechanism(playbackRate);
if( m_bInstantlyTransform )
{
m_bInstantlyTransform = false;
move.SetMechanismPhase( 0.0f );
}
}
//Note that the button hasn't been up.
m_bHasButtonBeenUp = false;
}
///////////////////////////////////////////////////////////////////////////////////////
CTask::FSM_Return CTaskVehicleTransformToSubmarine::TransformToCar_OnUpdate( CVehicle* pVehicle )
{
CMoveVehicle& move = pVehicle->GetMoveVehicle();
if( move.IsMechanismFullyClosed() )
{
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( pVehicle );
submarineCar->SetSubmarineMode( false, true );
SetState( State_Car );
}
else
{
CMoveVehicle& move = pVehicle->GetMoveVehicle();
static float sfTransformToCarPhaseMin = 0.9f;
if( move.GetMechanismPhase() < sfTransformToCarPhaseMin )
{
// Set back to car mode as soon as we start transforming so that the suspension works correctly
CSubmarineCar* submarineCar = static_cast< CSubmarineCar* >( pVehicle );
submarineCar->SetSubmarineMode( false, false );
}
}
if( !pVehicle->GetIsVisibleInSomeViewportThisFrame() )
{
pVehicle->m_nDEflags.bForcePrePhysicsAnimUpdate = true;
}
return FSM_Continue;
}
///////////////////////////////////////////////////////////////////////////////////////
#if !__FINAL
const char * CTaskVehicleTransformToSubmarine::GetStaticStateName( s32 iState )
{
Assert( iState >= State_Car && iState <= State_Finish );
static const char* aStateNames[] =
{
"State_Car",
"State_Transform_To_Submarine",
"State_Submarine",
"State_Transform_To_Car",
"State_Finish",
};
return aStateNames[iState];
}
#endif