@@ -582,6 +582,7 @@ void CGameClient::OnConnected()
582582 m_Layers.Init (Kernel ()->RequestInterface <IMap>(), false );
583583 m_Collision.Init (Layers ());
584584 m_GameWorld.m_Core .InitSwitchers (m_Collision.m_HighestSwitchNumber );
585+ m_GameWorld.m_PredictedEvents .clear ();
585586 m_RaceHelper.Init (this );
586587
587588 // render loading before going through all components
@@ -1386,17 +1387,32 @@ void CGameClient::ProcessEvents()
13861387 if (Item.m_Type == NETEVENTTYPE_DAMAGEIND)
13871388 {
13881389 const CNetEvent_DamageInd *pEvent = (const CNetEvent_DamageInd *)Item.m_pData ;
1389- m_Effects.DamageIndicator (vec2 (pEvent->m_X , pEvent->m_Y ), direction (pEvent->m_Angle / 256 .0f ), Alpha);
1390+
1391+ vec2 DamageIndPos = vec2 (pEvent->m_X , pEvent->m_Y );
1392+ if (!m_PredictedWorld.CheckPredictedEventHandled (CGameWorld::CPredictedEvent (Item.m_Type , DamageIndPos, -1 , Client ()->GameTick (g_Config.m_ClDummy ), pEvent->m_Angle )))
1393+ {
1394+ m_Effects.DamageIndicator (vec2 (pEvent->m_X , pEvent->m_Y ), direction (pEvent->m_Angle / 256 .0f ), Alpha);
1395+ }
13901396 }
13911397 else if (Item.m_Type == NETEVENTTYPE_EXPLOSION)
13921398 {
13931399 const CNetEvent_Explosion *pEvent = (const CNetEvent_Explosion *)Item.m_pData ;
1394- m_Effects.Explosion (vec2 (pEvent->m_X , pEvent->m_Y ), Alpha);
1400+
1401+ vec2 ExplosionPos = vec2 (pEvent->m_X , pEvent->m_Y );
1402+ if (!m_PredictedWorld.CheckPredictedEventHandled (CGameWorld::CPredictedEvent (Item.m_Type , ExplosionPos, -1 , Client ()->GameTick (g_Config.m_ClDummy ))))
1403+ {
1404+ m_Effects.Explosion (ExplosionPos, Alpha);
1405+ }
13951406 }
13961407 else if (Item.m_Type == NETEVENTTYPE_HAMMERHIT)
13971408 {
13981409 const CNetEvent_HammerHit *pEvent = (const CNetEvent_HammerHit *)Item.m_pData ;
1399- m_Effects.HammerHit (vec2 (pEvent->m_X , pEvent->m_Y ), Alpha, Volume);
1410+
1411+ vec2 HammerHitPos = vec2 (pEvent->m_X , pEvent->m_Y );
1412+ if (!m_PredictedWorld.CheckPredictedEventHandled (CGameWorld::CPredictedEvent (Item.m_Type , HammerHitPos, -1 , Client ()->GameTick (g_Config.m_ClDummy ))))
1413+ {
1414+ m_Effects.HammerHit (HammerHitPos, Alpha, Volume);
1415+ }
14001416 }
14011417 else if (Item.m_Type == NETEVENTTYPE_BIRTHDAY)
14021418 {
@@ -1427,7 +1443,11 @@ void CGameClient::ProcessEvents()
14271443 if (m_GameInfo.m_RaceSounds && ((pEvent->m_SoundId == SOUND_GUN_FIRE && !g_Config.m_SndGun ) || (pEvent->m_SoundId == SOUND_PLAYER_PAIN_LONG && !g_Config.m_SndLongPain )))
14281444 continue ;
14291445
1430- m_Sounds.PlayAt (CSounds::CHN_WORLD, pEvent->m_SoundId , 1 .0f , vec2 (pEvent->m_X , pEvent->m_Y ));
1446+ vec2 SoundPos = vec2 (pEvent->m_X , pEvent->m_Y );
1447+ if (!m_PredictedWorld.CheckPredictedEventHandled (CGameWorld::CPredictedEvent (Item.m_Type , SoundPos, -1 , Client ()->GameTick (g_Config.m_ClDummy ), pEvent->m_SoundId )))
1448+ {
1449+ m_Sounds.PlayAt (CSounds::CHN_WORLD, pEvent->m_SoundId , 1 .0f , SoundPos);
1450+ }
14311451 }
14321452 else if (Item.m_Type == NETEVENTTYPE_MAPSOUNDWORLD)
14331453 {
@@ -1545,6 +1565,7 @@ static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize,
15451565 Info.m_NoWeakHookAndBounce = false ;
15461566 Info.m_NoSkinChangeForFrozen = false ;
15471567 Info.m_DDRaceTeam = false ;
1568+ Info.m_PredictEvents = DDRace || Vanilla;
15481569
15491570 if (Version >= 0 )
15501571 {
@@ -1608,6 +1629,10 @@ static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize,
16081629 {
16091630 Info.m_DDRaceTeam = Flags2 & GAMEINFOFLAG2_DDRACE_TEAM;
16101631 }
1632+ if (Version >= 11 )
1633+ {
1634+ Info.m_PredictEvents = Flags2 & GAMEINFOFLAG2_PREDICT_EVENTS;
1635+ }
16111636
16121637 return Info;
16131638}
@@ -2518,6 +2543,9 @@ void CGameClient::OnPredict()
25182543
25192544 // init
25202545 bool Dummy = g_Config.m_ClDummy ^ m_IsDummySwapping;
2546+
2547+ // PredictedEvents are only handled in predicted world, so update them here
2548+ m_GameWorld.m_PredictedEvents = m_PredictedWorld.m_PredictedEvents ;
25212549 m_PredictedWorld.CopyWorld (&m_GameWorld);
25222550
25232551 // don't predict inactive players, or entities from other teams
@@ -2592,6 +2620,8 @@ void CGameClient::OnPredict()
25922620
25932621 m_PredictedWorld.Tick ();
25942622
2623+ HandlePredictedEvents (Tick);
2624+
25952625 // fetch the current characters
25962626 if (Tick == PredictionTick)
25972627 {
@@ -2636,6 +2666,10 @@ void CGameClient::OnPredict()
26362666 m_Sounds.PlayAndRecord (CSounds::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1 .0f , Pos);
26372667 if (Events & COREEVENT_HOOK_HIT_NOHOOK)
26382668 m_Sounds.PlayAndRecord (CSounds::CHN_WORLD, SOUND_HOOK_NOATTACH, 1 .0f , Pos);
2669+ if (Events & COREEVENT_HOOK_ATTACH_PLAYER)
2670+ {
2671+ m_PredictedWorld.CreatePredictedSound (Pos, SOUND_HOOK_ATTACH_PLAYER, pLocalChar->GetCid ());
2672+ }
26392673 }
26402674 }
26412675
@@ -3388,6 +3422,7 @@ void CGameClient::UpdatePrediction()
33883422 m_GameWorld.m_WorldConfig .m_PredictWeapons = AntiPingWeapons ();
33893423 m_GameWorld.m_WorldConfig .m_BugDDRaceInput = m_GameInfo.m_BugDDRaceInput ;
33903424 m_GameWorld.m_WorldConfig .m_NoWeakHookAndBounce = m_GameInfo.m_NoWeakHookAndBounce ;
3425+ m_GameWorld.m_WorldConfig .m_PredictEvents = m_GameInfo.m_PredictEvents ;
33913426
33923427 if (!m_Snap.m_pLocalCharacter )
33933428 {
@@ -3706,6 +3741,54 @@ void CGameClient::UpdateRenderedCharacters()
37063741 }
37073742}
37083743
3744+ void CGameClient::HandlePredictedEvents (const int Tick)
3745+ {
3746+ const float Alpha = 1 .0f ;
3747+ const float Volume = 1 .0f ;
3748+
3749+ auto EventsIterator = m_PredictedWorld.m_PredictedEvents .begin ();
3750+ while (EventsIterator != m_PredictedWorld.m_PredictedEvents .end ())
3751+ {
3752+ if (!EventsIterator->m_Handled && EventsIterator->m_Tick <= Tick)
3753+ {
3754+ if (EventsIterator->m_EventId == NETEVENTTYPE_SOUNDWORLD)
3755+ {
3756+ if (m_GameInfo.m_RaceSounds && ((EventsIterator->m_ExtraInfo == SOUND_GUN_FIRE && !g_Config.m_SndGun ) || (EventsIterator->m_ExtraInfo == SOUND_PLAYER_PAIN_LONG && !g_Config.m_SndLongPain )))
3757+ {
3758+ EventsIterator = m_PredictedWorld.m_PredictedEvents .erase (EventsIterator);
3759+ continue ;
3760+ }
3761+ m_Sounds.PlayAt (CSounds::CHN_WORLD, EventsIterator->m_ExtraInfo , 1 .0f , EventsIterator->m_Pos );
3762+ }
3763+ else if (EventsIterator->m_EventId == NETEVENTTYPE_EXPLOSION)
3764+ {
3765+ m_Effects.Explosion (EventsIterator->m_Pos , Alpha);
3766+ }
3767+ else if (EventsIterator->m_EventId == NETEVENTTYPE_HAMMERHIT)
3768+ {
3769+ m_Effects.HammerHit (EventsIterator->m_Pos , Alpha, Volume);
3770+ }
3771+ else if (EventsIterator->m_EventId == NETEVENTTYPE_DAMAGEIND)
3772+ {
3773+ m_Effects.DamageIndicator (EventsIterator->m_Pos , direction (EventsIterator->m_ExtraInfo / 256 .0f ), Alpha);
3774+ }
3775+
3776+ EventsIterator->m_Handled = true ;
3777+ ++EventsIterator;
3778+ continue ;
3779+ }
3780+ else if (Tick - EventsIterator->m_Tick > 3 * Client ()->GameTickSpeed ()) // 3 seconds
3781+ {
3782+ // remove too old events
3783+ EventsIterator = m_PredictedWorld.m_PredictedEvents .erase (EventsIterator);
3784+ }
3785+ else
3786+ {
3787+ ++EventsIterator;
3788+ }
3789+ }
3790+ }
3791+
37093792void CGameClient::DetectStrongHook ()
37103793{
37113794 // attempt to detect strong/weak between players
0 commit comments