@@ -38,10 +38,10 @@ using std::vector;
38
38
extern CClientGame* g_pClientGame;
39
39
40
40
#ifndef M_PI
41
- #define M_PI 3.14159265358979323846
41
+ #define M_PI 3.14159265358979323846
42
42
#endif
43
43
44
- #define INVALID_VALUE 0xFFFFFFFF
44
+ #define INVALID_VALUE 0xFFFFFFFF
45
45
46
46
#define PED_INTERPOLATION_WARP_THRESHOLD 5 // Minimal threshold
47
47
#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
1166
1166
for (; iter != listEnd; iter++)
1167
1167
{
1168
1168
pTempVehicle = *iter;
1169
-
1169
+
1170
1170
if (pTempVehicle->IsLocalEntity () != localVehicles)
1171
1171
continue ;
1172
1172
@@ -2885,11 +2885,7 @@ void CClientPed::StreamedInPulse(bool bDoStandardPulses)
2885
2885
2886
2886
m_bRequestedAnimation = false ;
2887
2887
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 ();
2893
2889
}
2894
2890
}
2895
2891
@@ -3400,8 +3396,8 @@ void CClientPed::Interpolate()
3400
3396
{
3401
3397
// We're not at the end?
3402
3398
if (ulCurrentTime < m_ulEndRotationTime)
3403
- {
3404
- const float fDelta = GetOffsetRadians (m_fBeginRotation, m_fTargetRotationA);
3399
+ {
3400
+ const float fDelta = GetOffsetRadians (m_fBeginRotation, m_fTargetRotationA);
3405
3401
3406
3402
// Hack for the wrap-around (the edge seems to be varying...)
3407
3403
if (fDelta < -M_PI || fDelta > M_PI)
@@ -3411,7 +3407,7 @@ void CClientPed::Interpolate()
3411
3407
}
3412
3408
else
3413
3409
{
3414
- // Interpolate the player rotation
3410
+ // Interpolate the player rotation
3415
3411
const float fDeltaTime = float (m_ulEndRotationTime - m_ulBeginRotationTime);
3416
3412
const float fCameraDelta = GetOffsetRadians (m_fBeginCameraRotation, m_fTargetCameraRotation);
3417
3413
const float fProgress = float (ulCurrentTime - m_ulBeginRotationTime);
@@ -3676,18 +3672,15 @@ void CClientPed::_CreateModel()
3676
3672
Kill (WEAPONTYPE_UNARMED, 0 , false , true );
3677
3673
}
3678
3674
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)
3681
3677
{
3682
3678
if (m_bisCurrentAnimationCustom)
3683
3679
{
3684
3680
m_bisNextAnimationCustom = true ;
3685
3681
}
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 ();
3691
3684
}
3692
3685
3693
3686
// Set the voice that corresponds to our model
@@ -3939,7 +3932,7 @@ void CClientPed::_ChangeModel()
3939
3932
// So make sure clothes geometry is built now...
3940
3933
m_pClothes->AddAllToModel ();
3941
3934
m_pPlayerPed->RebuildPlayer ();
3942
- }
3935
+ }
3943
3936
3944
3937
// Remove reference to the old model we used (Flag extra GTA reference to be removed as well)
3945
3938
pLoadedModel->RemoveRef (true );
@@ -3958,18 +3951,14 @@ void CClientPed::_ChangeModel()
3958
3951
m_bDontChangeRadio = false ;
3959
3952
3960
3953
// 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)
3962
3955
{
3963
3956
if (m_bisCurrentAnimationCustom)
3964
3957
{
3965
3958
m_bisNextAnimationCustom = true ;
3966
3959
}
3967
3960
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 ();
3973
3962
}
3974
3963
3975
3964
// Set the voice that corresponds to the new model
@@ -5260,7 +5249,7 @@ void CClientPed::Respawn(CVector* pvecPosition, bool bRestoreState, bool bCamera
5260
5249
// Restore the camera's interior whether we're restoring player states or not
5261
5250
g_pGame->GetWorld ()->SetCurrentArea (ucCameraInterior);
5262
5251
5263
- // Reset goggle effect
5252
+ // Reset goggle effect
5264
5253
g_pMultiplayer->SetNightVisionEnabled (bOldNightVision, false );
5265
5254
g_pMultiplayer->SetThermalVisionEnabled (bOldThermalVision, false );
5266
5255
@@ -5794,6 +5783,10 @@ void CClientPed::RunNamedAnimation(std::unique_ptr<CAnimBlock>& pBlock, const ch
5794
5783
m_AnimationCache.bUpdatePosition = bUpdatePosition;
5795
5784
m_AnimationCache.bInterruptable = bInterruptable;
5796
5785
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 ;
5797
5790
}
5798
5791
5799
5792
void CClientPed::KillAnimation ()
@@ -5827,6 +5820,46 @@ std::unique_ptr<CAnimBlock> CClientPed::GetAnimationBlock()
5827
5820
return nullptr ;
5828
5821
}
5829
5822
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
+
5830
5863
void CClientPed::PostWeaponFire ()
5831
5864
{
5832
5865
m_ulLastTimeFired = CClientTime::GetTime ();
@@ -5919,7 +5952,7 @@ bool CClientPed::SetOnFire(bool bIsOnFire)
5919
5952
{
5920
5953
if (m_pPlayerPed)
5921
5954
return m_pPlayerPed->SetOnFire (bIsOnFire);
5922
-
5955
+
5923
5956
m_bIsOnFire = bIsOnFire;
5924
5957
return true ;
5925
5958
}
@@ -6296,9 +6329,9 @@ void CClientPed::HandleWaitingForGroundToLoad()
6296
6329
{
6297
6330
// If not near any MTA objects, then don't bother waiting
6298
6331
SetFrozenWaitingForGroundToLoad (false );
6299
- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6332
+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6300
6333
OutputDebugLine (" [AsyncLoading] FreezeUntilCollisionLoaded - Early stop" );
6301
- #endif
6334
+ #endif
6302
6335
return ;
6303
6336
}
6304
6337
@@ -6319,29 +6352,29 @@ void CClientPed::HandleWaitingForGroundToLoad()
6319
6352
bool bASync = g_pGame->IsASyncLoadingEnabled ();
6320
6353
bool bMTAObjLimit = pObjectManager->IsObjectLimitReached ();
6321
6354
bool bHasModel = GetModelInfo () != NULL ;
6322
- #ifndef ASYNC_LOADING_DEBUG_OUTPUTA
6355
+ #ifndef ASYNC_LOADING_DEBUG_OUTPUTA
6323
6356
bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded (vecPosition, fUseRadius , m_usDimension);
6324
- #else
6357
+ #else
6325
6358
SString strAround;
6326
6359
bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded (vecPosition, fUseRadius , m_usDimension, &strAround);
6327
- #endif
6360
+ #endif
6328
6361
6329
- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6362
+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6330
6363
SString status = SString (
6331
6364
" %2.2f,%2.2f,%2.2f bASync:%d bHasModel:%d bMTALoaded:%d bMTAObjLimit:%d m_fGroundCheckTolerance:%2.2f m_fObjectsAroundTolerance:%2.2f "
6332
6365
" fUseRadius:%2.1f" ,
6333
6366
vecPosition.fX , vecPosition.fY , vecPosition.fZ , bASync, bHasModel, bMTALoaded, bMTAObjLimit, m_fGroundCheckTolerance, m_fObjectsAroundTolerance,
6334
6367
fUseRadius );
6335
- #endif
6368
+ #endif
6336
6369
6337
6370
// See if ground is ready
6338
6371
if ((!bHasModel || !bMTALoaded) && m_fObjectsAroundTolerance < 1 .f )
6339
6372
{
6340
6373
m_fGroundCheckTolerance = 0 .f ;
6341
6374
m_fObjectsAroundTolerance = std::min (1 .f , m_fObjectsAroundTolerance + 0 .01f );
6342
- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6375
+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6343
6376
status += (" FreezeUntilCollisionLoaded - wait" );
6344
- #endif
6377
+ #endif
6345
6378
}
6346
6379
else
6347
6380
{
@@ -6354,24 +6387,24 @@ void CClientPed::HandleWaitingForGroundToLoad()
6354
6387
if (fUseDist > -0 .2f && fUseDist < 1 .5f )
6355
6388
SetFrozenWaitingForGroundToLoad (false );
6356
6389
6357
- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6390
+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6358
6391
status += (SString (" GetDistanceFromGround: fDist:%2.2f fUseDist:%2.2f" , fDist , fUseDist ));
6359
- #endif
6392
+ #endif
6360
6393
6361
6394
// Stop waiting after 3 frames, if the object limit has not been reached. (bASync should always be false here)
6362
6395
if (m_fGroundCheckTolerance > 0 .03f && !bMTAObjLimit && !bASync)
6363
6396
SetFrozenWaitingForGroundToLoad (false );
6364
6397
}
6365
6398
6366
- #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6399
+ #ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6367
6400
OutputDebugLine (SStringX (" [AsyncLoading] " )++ status);
6368
6401
g_pCore->GetGraphics ()->DrawString (10 , 220 , -1 , 1 , status);
6369
6402
6370
6403
std::vector<SString> lineList;
6371
6404
strAround.Split (" \n " , lineList);
6372
6405
for (unsigned int i = 0 ; i < lineList.size (); i++)
6373
6406
g_pCore->GetGraphics ()->DrawString (10 , 230 + i * 10 , -1 , 1 , lineList[i]);
6374
- #endif
6407
+ #endif
6375
6408
}
6376
6409
6377
6410
//
@@ -6658,7 +6691,6 @@ bool CClientPed::ExitVehicle()
6658
6691
return false ;
6659
6692
}
6660
6693
6661
-
6662
6694
// Check the server is compatible if we are a ped
6663
6695
if (!IsLocalPlayer () && !g_pNet->CanServerBitStream (eBitStreamVersion::PedEnterExit))
6664
6696
{
@@ -6789,7 +6821,7 @@ void CClientPed::UpdateVehicleInOut()
6789
6821
CClientVehicle* vehicle = GetRealOccupiedVehicle ();
6790
6822
if (!vehicle)
6791
6823
return ;
6792
-
6824
+
6793
6825
// Call the onClientVehicleEnter event for the ped
6794
6826
// Check if it is cancelled before allowing the ped to enter the vehicle
6795
6827
CLuaArguments arguments;
@@ -6816,7 +6848,7 @@ void CClientPed::UpdateVehicleInOut()
6816
6848
6817
6849
if (realVehicle)
6818
6850
return ;
6819
-
6851
+
6820
6852
// Call the onClientVehicleExit event for the ped
6821
6853
CLuaArguments arguments;
6822
6854
arguments.PushElement (this ); // player / ped
@@ -6843,7 +6875,7 @@ void CClientPed::UpdateVehicleInOut()
6843
6875
// If we aren't working on leaving the car (he's eiter finished or cancelled/failed leaving)
6844
6876
if (IsLeavingVehicle ())
6845
6877
return ;
6846
-
6878
+
6847
6879
// Are we outside the car?
6848
6880
CClientVehicle* pVehicle = GetRealOccupiedVehicle ();
6849
6881
if (pVehicle)
@@ -7047,7 +7079,7 @@ void CClientPed::UpdateVehicleInOut()
7047
7079
// If we aren't getting jacked
7048
7080
if (m_bIsGettingJacked)
7049
7081
return ;
7050
-
7082
+
7051
7083
CClientVehicle* pVehicle = GetRealOccupiedVehicle ();
7052
7084
CClientVehicle* pOccupiedVehicle = GetOccupiedVehicle ();
7053
7085
@@ -7062,15 +7094,15 @@ void CClientPed::UpdateVehicleInOut()
7062
7094
// Are we supposed to be in a vehicle? But aren't?
7063
7095
if (!pOccupiedVehicle || pVehicle || IsWarpInToVehicleRequired ())
7064
7096
return ;
7065
-
7097
+
7066
7098
// Jax: this happens when we try to warp into a streamed out vehicle, including when we use CClientVehicle::StreamInNow
7067
7099
// ..maybe we need a different way to detect bike falls?
7068
7100
7069
7101
// Tell the server
7070
7102
NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream ();
7071
7103
if (!pBitStream)
7072
7104
return ;
7073
-
7105
+
7074
7106
// Write the ped or player ID to it
7075
7107
if (g_pNet->CanServerBitStream (eBitStreamVersion::PedEnterExit))
7076
7108
{
0 commit comments