@@ -38,10 +38,10 @@ using std::vector;
3838extern CClientGame* g_pClientGame;
3939
4040#ifndef M_PI
41- #define M_PI 3.14159265358979323846
41+ #define M_PI 3.14159265358979323846
4242#endif
4343
44- #define INVALID_VALUE 0xFFFFFFFF
44+ #define INVALID_VALUE 0xFFFFFFFF
4545
4646#define PED_INTERPOLATION_WARP_THRESHOLD 5 // Minimal threshold
4747#define PED_INTERPOLATION_WARP_THRESHOLD_FOR_SPEED 5 // Units to increment the threshold per speed unit
@@ -1166,7 +1166,7 @@ CClientVehicle* CClientPed::GetClosestEnterableVehicle(bool bGetPositionFromClos
11661166 for (; iter != listEnd; iter++)
11671167 {
11681168 pTempVehicle = *iter;
1169-
1169+
11701170 if (pTempVehicle->IsLocalEntity () != localVehicles)
11711171 continue ;
11721172
@@ -2885,11 +2885,7 @@ void CClientPed::StreamedInPulse(bool bDoStandardPulses)
28852885
28862886 m_bRequestedAnimation = false ;
28872887
2888- // Copy our name incase it gets deleted
2889- SString strAnimName = m_AnimationCache.strName ;
2890- // Run our animation
2891- RunNamedAnimation (m_pAnimationBlock, strAnimName, m_AnimationCache.iTime , m_AnimationCache.iBlend , m_AnimationCache.bLoop ,
2892- m_AnimationCache.bUpdatePosition , m_AnimationCache.bInterruptable , m_AnimationCache.bFreezeLastFrame );
2888+ RunAnimationFromCache ();
28932889 }
28942890 }
28952891
@@ -3400,8 +3396,8 @@ void CClientPed::Interpolate()
34003396 {
34013397 // We're not at the end?
34023398 if (ulCurrentTime < m_ulEndRotationTime)
3403- {
3404- const float fDelta = GetOffsetRadians (m_fBeginRotation, m_fTargetRotationA);
3399+ {
3400+ const float fDelta = GetOffsetRadians (m_fBeginRotation, m_fTargetRotationA);
34053401
34063402 // Hack for the wrap-around (the edge seems to be varying...)
34073403 if (fDelta < -M_PI || fDelta > M_PI)
@@ -3411,7 +3407,7 @@ void CClientPed::Interpolate()
34113407 }
34123408 else
34133409 {
3414- // Interpolate the player rotation
3410+ // Interpolate the player rotation
34153411 const float fDeltaTime = float (m_ulEndRotationTime - m_ulBeginRotationTime);
34163412 const float fCameraDelta = GetOffsetRadians (m_fBeginCameraRotation, m_fTargetCameraRotation);
34173413 const float fProgress = float (ulCurrentTime - m_ulBeginRotationTime);
@@ -3676,18 +3672,15 @@ void CClientPed::_CreateModel()
36763672 Kill (WEAPONTYPE_UNARMED, 0 , false , true );
36773673 }
36783674
3679- // Are we still playing a looped animation?
3680- if (m_AnimationCache.bLoop && m_pAnimationBlock)
3675+ // Are we still playing animation?
3676+ if (( m_AnimationCache.bLoop || m_AnimationCache. bFreezeLastFrame || m_AnimationCache. progressWaitForStreamIn ) && m_pAnimationBlock)
36813677 {
36823678 if (m_bisCurrentAnimationCustom)
36833679 {
36843680 m_bisNextAnimationCustom = true ;
36853681 }
3686- // Copy our anim name incase it gets deleted
3687- SString strAnimName = m_AnimationCache.strName ;
3688- // Run our animation
3689- RunNamedAnimation (m_pAnimationBlock, strAnimName, m_AnimationCache.iTime , m_AnimationCache.iBlend , m_AnimationCache.bLoop ,
3690- m_AnimationCache.bUpdatePosition , m_AnimationCache.bInterruptable , m_AnimationCache.bFreezeLastFrame );
3682+
3683+ RunAnimationFromCache ();
36913684 }
36923685
36933686 // Set the voice that corresponds to our model
@@ -3939,7 +3932,7 @@ void CClientPed::_ChangeModel()
39393932 // So make sure clothes geometry is built now...
39403933 m_pClothes->AddAllToModel ();
39413934 m_pPlayerPed->RebuildPlayer ();
3942- }
3935+ }
39433936
39443937 // Remove reference to the old model we used (Flag extra GTA reference to be removed as well)
39453938 pLoadedModel->RemoveRef (true );
@@ -3958,18 +3951,14 @@ void CClientPed::_ChangeModel()
39583951 m_bDontChangeRadio = false ;
39593952
39603953 // Are we still playing a looped animation?
3961- if (m_AnimationCache.bLoop && m_pAnimationBlock)
3954+ if (( m_AnimationCache.bLoop || m_AnimationCache. bFreezeLastFrame || m_AnimationCache. progressWaitForStreamIn ) && m_pAnimationBlock)
39623955 {
39633956 if (m_bisCurrentAnimationCustom)
39643957 {
39653958 m_bisNextAnimationCustom = true ;
39663959 }
39673960
3968- // Copy our anim name incase it gets deleted
3969- SString strAnimName = m_AnimationCache.strName ;
3970- // Run our animation
3971- RunNamedAnimation (m_pAnimationBlock, strAnimName, m_AnimationCache.iTime , m_AnimationCache.iBlend , m_AnimationCache.bLoop ,
3972- m_AnimationCache.bUpdatePosition , m_AnimationCache.bInterruptable , m_AnimationCache.bFreezeLastFrame );
3961+ RunAnimationFromCache ();
39733962 }
39743963
39753964 // Set the voice that corresponds to the new model
@@ -5260,7 +5249,7 @@ void CClientPed::Respawn(CVector* pvecPosition, bool bRestoreState, bool bCamera
52605249 // Restore the camera's interior whether we're restoring player states or not
52615250 g_pGame->GetWorld ()->SetCurrentArea (ucCameraInterior);
52625251
5263- // Reset goggle effect
5252+ // Reset goggle effect
52645253 g_pMultiplayer->SetNightVisionEnabled (bOldNightVision, false );
52655254 g_pMultiplayer->SetThermalVisionEnabled (bOldThermalVision, false );
52665255
@@ -5794,6 +5783,10 @@ void CClientPed::RunNamedAnimation(std::unique_ptr<CAnimBlock>& pBlock, const ch
57945783 m_AnimationCache.bUpdatePosition = bUpdatePosition;
57955784 m_AnimationCache.bInterruptable = bInterruptable;
57965785 m_AnimationCache.bFreezeLastFrame = bFreezeLastFrame;
5786+ m_AnimationCache.progress = 0 .0f ;
5787+ m_AnimationCache.speed = 1 .0f ;
5788+ m_AnimationCache.progressWaitForStreamIn = false ;
5789+ m_AnimationCache.elapsedTime = 0 .0f ;
57975790}
57985791
57995792void CClientPed::KillAnimation ()
@@ -5827,6 +5820,46 @@ std::unique_ptr<CAnimBlock> CClientPed::GetAnimationBlock()
58275820 return nullptr ;
58285821}
58295822
5823+ void CClientPed::RunAnimationFromCache ()
5824+ {
5825+ if (!m_pAnimationBlock)
5826+ return ;
5827+
5828+ bool needCalcProgress = m_AnimationCache.progressWaitForStreamIn ;
5829+ float elapsedTime = m_AnimationCache.elapsedTime ;
5830+
5831+ // Copy our name incase it gets deleted
5832+ std::string animName = m_AnimationCache.strName ;
5833+
5834+ // Run our animation
5835+ RunNamedAnimation (m_pAnimationBlock, animName.c_str (), m_AnimationCache.iTime , m_AnimationCache.iBlend , m_AnimationCache.bLoop , m_AnimationCache.bUpdatePosition , m_AnimationCache.bInterruptable , m_AnimationCache.bFreezeLastFrame );
5836+
5837+ auto animAssoc = g_pGame->GetAnimManager ()->RpAnimBlendClumpGetAssociation (GetClump (), animName.c_str ());
5838+ if (!animAssoc)
5839+ return ;
5840+
5841+ // If the anim is synced from the server side, we need to calculate the progress
5842+ float progress = m_AnimationCache.progress ;
5843+ if (needCalcProgress)
5844+ {
5845+ float animLength = animAssoc->GetLength ();
5846+
5847+ if (m_AnimationCache.bFreezeLastFrame ) // time and loop is ignored if freezeLastFrame is true
5848+ progress = (elapsedTime / animLength) * m_AnimationCache.speed ;
5849+ else
5850+ {
5851+ if (m_AnimationCache.bLoop )
5852+ progress = std::fmod (elapsedTime * m_AnimationCache.speed , animLength) / animLength;
5853+ else
5854+ // For non-looped animations, limit duration to animLength if time exceeds it
5855+ progress = (elapsedTime / (m_AnimationCache.iTime <= animLength ? m_AnimationCache.iTime : animLength)) * m_AnimationCache.speed ;
5856+ }
5857+ }
5858+
5859+ animAssoc->SetCurrentProgress (std::clamp (progress, 0 .0f , 1 .0f ));
5860+ animAssoc->SetCurrentSpeed (m_AnimationCache.speed );
5861+ }
5862+
58305863void CClientPed::PostWeaponFire ()
58315864{
58325865 m_ulLastTimeFired = CClientTime::GetTime ();
@@ -5919,7 +5952,7 @@ bool CClientPed::SetOnFire(bool bIsOnFire)
59195952{
59205953 if (m_pPlayerPed)
59215954 return m_pPlayerPed->SetOnFire (bIsOnFire);
5922-
5955+
59235956 m_bIsOnFire = bIsOnFire;
59245957 return true ;
59255958}
@@ -6296,9 +6329,9 @@ void CClientPed::HandleWaitingForGroundToLoad()
62966329 {
62976330 // If not near any MTA objects, then don't bother waiting
62986331 SetFrozenWaitingForGroundToLoad (false );
6299- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6332+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63006333 OutputDebugLine (" [AsyncLoading] FreezeUntilCollisionLoaded - Early stop" );
6301- #endif
6334+ #endif
63026335 return ;
63036336 }
63046337
@@ -6319,29 +6352,29 @@ void CClientPed::HandleWaitingForGroundToLoad()
63196352 bool bASync = g_pGame->IsASyncLoadingEnabled ();
63206353 bool bMTAObjLimit = pObjectManager->IsObjectLimitReached ();
63216354 bool bHasModel = GetModelInfo () != NULL ;
6322- #ifndef ASYNC_LOADING_DEBUG_OUTPUTA
6355+ #ifndef ASYNC_LOADING_DEBUG_OUTPUTA
63236356 bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded (vecPosition, fUseRadius , m_usDimension);
6324- #else
6357+ #else
63256358 SString strAround;
63266359 bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded (vecPosition, fUseRadius , m_usDimension, &strAround);
6327- #endif
6360+ #endif
63286361
6329- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6362+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63306363 SString status = SString (
63316364 " %2.2f,%2.2f,%2.2f bASync:%d bHasModel:%d bMTALoaded:%d bMTAObjLimit:%d m_fGroundCheckTolerance:%2.2f m_fObjectsAroundTolerance:%2.2f "
63326365 " fUseRadius:%2.1f" ,
63336366 vecPosition.fX , vecPosition.fY , vecPosition.fZ , bASync, bHasModel, bMTALoaded, bMTAObjLimit, m_fGroundCheckTolerance, m_fObjectsAroundTolerance,
63346367 fUseRadius );
6335- #endif
6368+ #endif
63366369
63376370 // See if ground is ready
63386371 if ((!bHasModel || !bMTALoaded) && m_fObjectsAroundTolerance < 1 .f )
63396372 {
63406373 m_fGroundCheckTolerance = 0 .f ;
63416374 m_fObjectsAroundTolerance = std::min (1 .f , m_fObjectsAroundTolerance + 0 .01f );
6342- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6375+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63436376 status += (" FreezeUntilCollisionLoaded - wait" );
6344- #endif
6377+ #endif
63456378 }
63466379 else
63476380 {
@@ -6354,24 +6387,24 @@ void CClientPed::HandleWaitingForGroundToLoad()
63546387 if (fUseDist > -0 .2f && fUseDist < 1 .5f )
63556388 SetFrozenWaitingForGroundToLoad (false );
63566389
6357- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6390+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63586391 status += (SString (" GetDistanceFromGround: fDist:%2.2f fUseDist:%2.2f" , fDist , fUseDist ));
6359- #endif
6392+ #endif
63606393
63616394 // Stop waiting after 3 frames, if the object limit has not been reached. (bASync should always be false here)
63626395 if (m_fGroundCheckTolerance > 0 .03f && !bMTAObjLimit && !bASync)
63636396 SetFrozenWaitingForGroundToLoad (false );
63646397 }
63656398
6366- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6399+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63676400 OutputDebugLine (SStringX (" [AsyncLoading] " )++ status);
63686401 g_pCore->GetGraphics ()->DrawString (10 , 220 , -1 , 1 , status);
63696402
63706403 std::vector<SString> lineList;
63716404 strAround.Split (" \n " , lineList);
63726405 for (unsigned int i = 0 ; i < lineList.size (); i++)
63736406 g_pCore->GetGraphics ()->DrawString (10 , 230 + i * 10 , -1 , 1 , lineList[i]);
6374- #endif
6407+ #endif
63756408}
63766409
63776410//
@@ -6658,7 +6691,6 @@ bool CClientPed::ExitVehicle()
66586691 return false ;
66596692 }
66606693
6661-
66626694 // Check the server is compatible if we are a ped
66636695 if (!IsLocalPlayer () && !g_pNet->CanServerBitStream (eBitStreamVersion::PedEnterExit))
66646696 {
@@ -6789,7 +6821,7 @@ void CClientPed::UpdateVehicleInOut()
67896821 CClientVehicle* vehicle = GetRealOccupiedVehicle ();
67906822 if (!vehicle)
67916823 return ;
6792-
6824+
67936825 // Call the onClientVehicleEnter event for the ped
67946826 // Check if it is cancelled before allowing the ped to enter the vehicle
67956827 CLuaArguments arguments;
@@ -6816,7 +6848,7 @@ void CClientPed::UpdateVehicleInOut()
68166848
68176849 if (realVehicle)
68186850 return ;
6819-
6851+
68206852 // Call the onClientVehicleExit event for the ped
68216853 CLuaArguments arguments;
68226854 arguments.PushElement (this ); // player / ped
@@ -6843,7 +6875,7 @@ void CClientPed::UpdateVehicleInOut()
68436875 // If we aren't working on leaving the car (he's eiter finished or cancelled/failed leaving)
68446876 if (IsLeavingVehicle ())
68456877 return ;
6846-
6878+
68476879 // Are we outside the car?
68486880 CClientVehicle* pVehicle = GetRealOccupiedVehicle ();
68496881 if (pVehicle)
@@ -7047,7 +7079,7 @@ void CClientPed::UpdateVehicleInOut()
70477079 // If we aren't getting jacked
70487080 if (m_bIsGettingJacked)
70497081 return ;
7050-
7082+
70517083 CClientVehicle* pVehicle = GetRealOccupiedVehicle ();
70527084 CClientVehicle* pOccupiedVehicle = GetOccupiedVehicle ();
70537085
@@ -7062,15 +7094,15 @@ void CClientPed::UpdateVehicleInOut()
70627094 // Are we supposed to be in a vehicle? But aren't?
70637095 if (!pOccupiedVehicle || pVehicle || IsWarpInToVehicleRequired ())
70647096 return ;
7065-
7097+
70667098 // Jax: this happens when we try to warp into a streamed out vehicle, including when we use CClientVehicle::StreamInNow
70677099 // ..maybe we need a different way to detect bike falls?
70687100
70697101 // Tell the server
70707102 NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream ();
70717103 if (!pBitStream)
70727104 return ;
7073-
7105+
70747106 // Write the ped or player ID to it
70757107 if (g_pNet->CanServerBitStream (eBitStreamVersion::PedEnterExit))
70767108 {
0 commit comments