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

577 lines
16 KiB
C++

// File header
#include "Task/Movement/TaskParachuteObject.h"
#include "Task/Movement/TaskParachute.h"
// Game headers
#include "animation/MoveObject.h"
#include "Network/NetworkInterface.h"
#include "Objects/object.h"
AI_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
//////////////////////////////////////////////////////////////////////////
// CTaskParachuteObject
//////////////////////////////////////////////////////////////////////////
const fwMvFloatId CTaskParachuteObject::ms_BlendXAxisTurns("BlendXAxisTurns",0xB57F83B2);
const fwMvFloatId CTaskParachuteObject::ms_BlendYAxisTurns("BlendYAxisTurns",0xBD35F97F);
const fwMvFloatId CTaskParachuteObject::ms_BlendXAndYAxesTurns("BlendXAndYAxesTurns",0x7EC6CC8D);
const fwMvFloatId CTaskParachuteObject::ms_BlendIdleAndMotion("BlendIdleAndMotion",0xA2048626);
const fwMvFloatId CTaskParachuteObject::ms_Phase("Phase",0xA27F482B);
const fwMvRequestId CTaskParachuteObject::ms_Deploy("Deploy",0xC9200261);
const fwMvRequestId CTaskParachuteObject::ms_Idle("Idle",0x71C21326);
const fwMvRequestId CTaskParachuteObject::ms_Crumple("Crumple",0x8415F0E7);
const fwMvBooleanId CTaskParachuteObject::ms_Deploy_OnEnter("Deploy_OnEnter",0x1F9DC4D6);
const fwMvBooleanId CTaskParachuteObject::ms_DeployClipEnded("DeployClipEnded",0xB4DA4E24);
const fwMvBooleanId CTaskParachuteObject::ms_Idle_OnEnter("Idle_OnEnter",0xCA902721);
const fwMvBooleanId CTaskParachuteObject::ms_Crumple_OnEnter("Crumple_OnEnter",0x393E61E7);
const fwMvBooleanId CTaskParachuteObject::ms_CrumpleClipEnded("CrumpleClipEnded",0x19E59FCB);
CTaskParachuteObject::Tunables CTaskParachuteObject::ms_Tunables;
IMPLEMENT_MOVEMENT_TASK_TUNABLES(CTaskParachuteObject, 0x5ded8b11);
CTaskParachuteObject::CTaskParachuteObject()
: m_MoveNetworkHelper()
, m_ClipSetRequestHelper()
, m_uFlags(0)
, m_hasAlreadyDeployed(false)
{
SetInternalTaskType(CTaskTypes::TASK_PARACHUTE_OBJECT);
}
CTaskParachuteObject::~CTaskParachuteObject()
{
}
void CTaskParachuteObject::CleanUp()
{
//Grab the object.
CObject* pObject = GetObject();
//Check if the move network is active.
if(m_MoveNetworkHelper.IsNetworkActive())
{
//This is apparently needed to avoid crashes.
if(pObject->GetAnimDirector())
{
//Clear the network.
pObject->GetMoveObject().ClearNetwork(m_MoveNetworkHelper, 0.0f);
}
}
}
aiTask* CTaskParachuteObject::Copy() const
{
return rage_new CTaskParachuteObject();
}
bool CTaskParachuteObject::CanDeploy() const
{
return (m_uFlags.IsFlagSet(PORF_CanDeploy));
}
void CTaskParachuteObject::Crumple()
{
//Set the crumple flag.
m_uFlags.SetFlag(PORF_Crumple);
}
void CTaskParachuteObject::Deploy()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Set the deploy flag.
m_uFlags.SetFlag(PORF_Deploy);
}
bool CTaskParachuteObject::IsOut() const
{
return (m_uFlags.IsFlagSet(PORF_IsOut));
}
void CTaskParachuteObject::ProcessMoveParameters(float fBlendXAxisTurns, float fBlendYAxisTurns, float fBlendXAndYAxesTurns, float fBlendIdleAndMotion)
{
if(!m_MoveNetworkHelper.IsNetworkAttached())
{
return;
}
//Set the move parameters.
m_MoveNetworkHelper.SetFloat(ms_BlendXAxisTurns, fBlendXAxisTurns);
m_MoveNetworkHelper.SetFloat(ms_BlendYAxisTurns, fBlendYAxisTurns);
m_MoveNetworkHelper.SetFloat(ms_BlendXAndYAxesTurns, fBlendXAndYAxesTurns);
m_MoveNetworkHelper.SetFloat(ms_BlendIdleAndMotion, fBlendIdleAndMotion);
}
void CTaskParachuteObject::TaskSetState(const s32 iState)
{
aiTask::SetState(iState);
}
void CTaskParachuteObject::MakeVisible(CObject& rObject, bool bVisible)
{
if(taskVerifyf(!rObject.IsNetworkClone(), "The object is a clone."))
{
rObject.SetIsVisibleForModule(SETISVISIBLE_MODULE_SCRIPT, bVisible);
#if !__NO_OUTPUT
if(!bVisible)
{
if(rObject.GetIsVisible())
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : Parachute was set to not be visible, but is still visible.", fwTimer::GetFrameCount(), &rObject, BANK_ONLY(true ? rObject.GetDebugName() : ) "NO OBJECT");)
for(int i = 0; i < NUM_VISIBLE_MODULES; ++i)
{
eIsVisibleModule nModule = (eIsVisibleModule)i;
if(rObject.GetIsVisibleForModule(nModule))
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : Module %d is still visible.", fwTimer::GetFrameCount(), &rObject, BANK_ONLY(true ? rObject.GetDebugName() : ) "NO OBJECT", i);)
}
}
}
}
#endif
}
}
#if !__FINAL
const char* CTaskParachuteObject::GetStaticStateName(s32 iState)
{
Assert(iState >= State_Start && iState <= State_Finish);
static const char* aStateNames[] =
{
"State_Start",
"State_Stream",
"State_WaitForDeploy",
"State_Deploy",
"State_Idle",
"State_Crumple",
"State_Finish"
};
return aStateNames[iState];
}
#endif
CTask::FSM_Return CTaskParachuteObject::ProcessPreFSM()
{
//Process the streaming.
ProcessStreaming();
// Process the clone parachute visibility.
ProcessVisibility();
return FSM_Continue;
}
CTask::FSM_Return CTaskParachuteObject::UpdateFSM(const s32 iState, const FSM_Event iEvent)
{
FSM_Begin
FSM_State(State_Start)
FSM_OnEnter
Start_OnEnter();
FSM_OnUpdate
return Start_OnUpdate();
FSM_State(State_Stream)
FSM_OnUpdate
return Stream_OnUpdate();
FSM_State(State_WaitForDeploy)
FSM_OnEnter
WaitForDeploy_OnEnter();
FSM_OnUpdate
return WaitForDeploy_OnUpdate();
FSM_State(State_Deploy)
FSM_OnEnter
Deploy_OnEnter();
FSM_OnUpdate
return Deploy_OnUpdate();
FSM_State(State_Idle)
FSM_OnEnter
Idle_OnEnter();
FSM_OnUpdate
return Idle_OnUpdate();
FSM_State(State_Skydiving)
FSM_OnEnter
SkyDiving_OnEnter();
FSM_OnUpdate
return SkyDiving_OnUpdate();
FSM_State(State_Crumple)
FSM_OnEnter
Crumple_OnEnter();
FSM_OnUpdate
return Crumple_OnUpdate();
FSM_State(State_Finish)
FSM_OnUpdate
return FSM_Quit;
FSM_End
}
void CTaskParachuteObject::Start_OnEnter()
{
//Grab the object.
CObject* pObject = GetObject();
//Force animations to always play for the parachute, even if it is off-screen.
pObject->m_nDEflags.bForcePrePhysicsAnimUpdate = true;
}
CTask::FSM_Return CTaskParachuteObject::Start_OnUpdate()
{
//Move to the stream state.
TaskSetState(State_Stream);
return FSM_Continue;
}
CTask::FSM_Return CTaskParachuteObject::Stream_OnUpdate()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Check if the move network has streamed in.
bool bMoveNetworkStreamedIn = m_MoveNetworkHelper.IsNetworkDefStreamedIn(CClipNetworkMoveInfo::ms_NetworkTaskParachuteObject);
//Check if the clip set has streamed in.
bool bClipSetStreamedIn = m_ClipSetRequestHelper.IsLoaded();
//Ensure everything required to start the skydive has streamed in.
if(bMoveNetworkStreamedIn && bClipSetStreamedIn)
{
//Grab the object.
CObject* pObject = GetObject();
//Create the network player.
m_MoveNetworkHelper.CreateNetworkPlayer(pObject, CClipNetworkMoveInfo::ms_NetworkTaskParachuteObject);
//Set the clip set.
m_MoveNetworkHelper.SetClipSet(m_ClipSetRequestHelper.GetClipSetId());
//Set the network.
pObject->GetMoveObject().SetNetwork(m_MoveNetworkHelper, 0.0f);
//Move to the wait for deploy state.
TaskSetState(State_WaitForDeploy);
}
return FSM_Continue;
}
void CTaskParachuteObject::WaitForDeploy_OnEnter()
{
//Note that the parachute can be deployed.
m_uFlags.SetFlag(PORF_CanDeploy);
}
CTask::FSM_Return CTaskParachuteObject::WaitForDeploy_OnUpdate()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Wait for the deploy flag to be set.
if(m_uFlags.IsFlagSet(PORF_Deploy) || (UsesStateFromNetwork() && GetStateFromNetwork() == State_Deploy))
{
//Move to the deploy state.
TaskSetState(State_Deploy);
}
return FSM_Continue;
}
void CTaskParachuteObject::Deploy_OnEnter()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Note that the parachute can no longer be deployed.
m_uFlags.ClearFlag(PORF_CanDeploy);
//Note that the parachute should no longer be deployed.
m_uFlags.ClearFlag(PORF_Deploy);
//Send the request.
m_MoveNetworkHelper.SendRequest(ms_Deploy);
//Wait for the state to be entered.
m_MoveNetworkHelper.WaitForTargetState(ms_Deploy_OnEnter);
}
CTask::FSM_Return CTaskParachuteObject::Deploy_OnUpdate()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Ensure the state has been entered.
if(!m_MoveNetworkHelper.IsInTargetState())
{
return FSM_Continue;
}
//Check if the parachute is out.
if(!m_uFlags.IsFlagSet(PORF_IsOut))
{
//Check if the phase has exceeded the threshold.
float fPhase = m_MoveNetworkHelper.GetFloat(ms_Phase);
if(fPhase >= ms_Tunables.m_PhaseDuringDeployToConsiderOut)
{
//Set the flag.
m_uFlags.SetFlag(PORF_IsOut);
}
}
//Wait for the animation to end.
if(m_MoveNetworkHelper.GetBoolean(ms_DeployClipEnded))
{
//Move to the idle state.
TaskSetState(State_Skydiving);
}
return FSM_Continue;
}
void CTaskParachuteObject::Idle_OnEnter()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Send the request.
m_MoveNetworkHelper.SendRequest(ms_Idle);
//Wait for the state to be entered.
m_MoveNetworkHelper.WaitForTargetState(ms_Idle_OnEnter);
}
CTask::FSM_Return CTaskParachuteObject::Idle_OnUpdate()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Ensure the state has been entered.
if(!m_MoveNetworkHelper.IsInTargetState())
{
return FSM_Continue;
}
//Wait for the crumple.
if(m_uFlags.IsFlagSet(PORF_Crumple) || (UsesStateFromNetwork() && GetStateFromNetwork() == State_Crumple))
{
//Move to the crumple state.
TaskSetState(State_Crumple);
}
return FSM_Continue;
}
void CTaskParachuteObject::SkyDiving_OnEnter()
{
Idle_OnEnter();
}
CTask::FSM_Return CTaskParachuteObject::SkyDiving_OnUpdate()
{
return Idle_OnUpdate();
}
void CTaskParachuteObject::Crumple_OnEnter()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Clear the crumple flag.
m_uFlags.ClearFlag(PORF_Crumple);
//Send the request.
m_MoveNetworkHelper.SendRequest(ms_Crumple);
//Wait for the state to be entered.
m_MoveNetworkHelper.WaitForTargetState(ms_Crumple_OnEnter);
}
CTask::FSM_Return CTaskParachuteObject::Crumple_OnUpdate()
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : %s : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", GetStaticStateName(GetState()), __FUNCTION__);)
//Ensure the state has been entered.
if(!m_MoveNetworkHelper.IsInTargetState())
{
return FSM_Continue;
}
//Wait for the animation to end.
if(m_MoveNetworkHelper.GetBoolean(ms_CrumpleClipEnded))
{
//Finish the task.
TaskSetState(State_Finish);
}
return FSM_Continue;
}
void CTaskParachuteObject::ProcessVisibility()
{
//Grab the object.
CObject* pObject = GetObject();
if(pObject)
{
bool shouldBeVisible = false;
switch(GetState())
{
case State_Start:
case State_Stream:
case State_WaitForDeploy:
shouldBeVisible = false;
Assert(!m_uFlags.IsFlagSet(PORF_IsOut));
break;
case State_Deploy:
shouldBeVisible = true;
break;
case State_Idle:
case State_Crumple:
case State_Finish:
case State_Skydiving:
shouldBeVisible = true;
Assert(m_uFlags.IsFlagSet(PORF_IsOut));
break;
default:
Assertf(false, "%s : missing state?!", __FUNCTION__);
break;
}
if(!pObject->IsNetworkClone())
{
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : Make Visible : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", shouldBeVisible ? "yes" : "no");)
MakeVisible(*pObject, shouldBeVisible);
}
else
{
if(pObject->GetNetworkObject())
{
CNetObjPhysical* netobjPhysical = SafeCast(CNetObjPhysical, pObject->GetNetworkObject());
if (gnetVerify(netobjPhysical) && !netobjPhysical->IsLocalFlagSet(CNetObjGame::LOCALFLAG_SHOWINCUTSCENE) && GetState() != State_WaitForDeploy)
{
shouldBeVisible = !NetworkInterface::IsInMPCutscene();
}
}
NOTFINAL_ONLY(parachuteDebugf1("Frame %d : Object %p %s : Overriding Local Visiblity : %s", fwTimer::GetFrameCount(), GetObject(), BANK_ONLY(GetObject() ? GetObject()->GetDebugName() : ) "NO OBJECT", shouldBeVisible ? "yes" : "no");)
// we tell the network we're taking charge of the parachutes visibility from now on.....
((CNetObjEntity*)pObject->GetNetworkObject())->SetOverridingLocalVisibility(shouldBeVisible, __FUNCTION__, true);
}
}
}
void CTaskParachuteObject::MakeVisible(bool bVisible)
{
MakeVisible(*GetObject(), bVisible);
}
void CTaskParachuteObject::ProcessStreaming()
{
//Stream the move network.
m_MoveNetworkHelper.RequestNetworkDef(CClipNetworkMoveInfo::ms_NetworkTaskParachuteObject);
bool bAttachedToCar = false;
//Stream the clip set.
if (GetObject())
{
CObject* pObject = GetObject();
CPhysical* attachParent = (CPhysical *)pObject->GetAttachParent();
if (attachParent && attachParent->GetIsTypeVehicle())
{
m_ClipSetRequestHelper.Request(CLIP_SET_VEH_SKYDIVE_PARACHUTE_CHUTE);
bAttachedToCar = true;
}
}
if(!bAttachedToCar)
{
m_ClipSetRequestHelper.Request(CLIP_SET_SKYDIVE_PARACHUTE_CHUTE);
}
}
void CTaskParachuteObject::OnCloneTaskNoLongerRunningOnOwner()
{
SetStateFromNetwork(State_Finish);
}
CTaskInfo* CTaskParachuteObject::CreateQueriableState() const
{
return rage_new CClonedParachuteObjectInfo(GetState());
}
void CTaskParachuteObject::ReadQueriableState(CClonedFSMTaskInfo* pTaskInfo)
{
CTaskFSMClone::ReadQueriableState(pTaskInfo);
}
CTask::FSM_Return CTaskParachuteObject::UpdateClonedFSM(const s32 iState, const FSM_Event iEvent)
{
if(iEvent == aiTask::OnUpdate && GetStateFromNetwork() == State_Finish)
{
TaskSetState(State_Finish);
return FSM_Continue;
}
// the clone hasn't deployed the parachute yet
if(iEvent == aiTask::OnUpdate && !m_hasAlreadyDeployed && GetStateFromNetwork() == State_Skydiving)
{
m_hasAlreadyDeployed = true;
TaskSetState(State_Deploy);
return FSM_Continue;
}
return UpdateFSM(iState, iEvent);
}
bool CTaskParachuteObject::OverridesNetworkBlender(CPed*)
{
// we do not want to position sync until the ped is attached to the parachute after deploying has completed.
return (GetObject() && GetObject()->GetChildAttachment() == NULL);
}
bool CTaskParachuteObject::OverridesNetworkAttachment(CPed* )
{
// The TaskParachute running on the ped handles attachment and detachment at all times...
return true;
}
bool CTaskParachuteObject::IsInScope(const CPed* )
{
return true;
}
// Use state from network if the parachute object is attached to a vehicle
bool CTaskParachuteObject::UsesStateFromNetwork()
{
if (GetObject())
{
CObject* pObject = GetObject();
CPhysical* attachParent = (CPhysical *)pObject->GetAttachParent();
if (attachParent && attachParent->GetIsTypeVehicle())
{
return true;
}
}
return false;
}
CTaskFSMClone* CClonedParachuteObjectInfo::CreateCloneFSMTask(void)
{
return rage_new CTaskParachuteObject();
}