diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 71dddb31354..c9fc85b89ab 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -280,6 +280,7 @@ CClientGame::CClientGame(bool bLocalPlay) g_pMultiplayer->SetGameEntityRenderHandler(CClientGame::StaticGameEntityRenderHandler); g_pMultiplayer->SetFxSystemDestructionHandler(CClientGame::StaticFxSystemDestructionHandler); g_pMultiplayer->SetDrivebyAnimationHandler(CClientGame::StaticDrivebyAnimationHandler); + g_pMultiplayer->SetWaterCannonHitWorldHandler(CClientGame::StaticWaterCannonHitWorldHandler); g_pGame->SetPreWeaponFireHandler(CClientGame::PreWeaponFire); g_pGame->SetPostWeaponFireHandler(CClientGame::PostWeaponFire); g_pGame->SetTaskSimpleBeHitHandler(CClientGame::StaticTaskSimpleBeHitHandler); @@ -440,6 +441,7 @@ CClientGame::~CClientGame(void) g_pMultiplayer->SetGameModelRemoveHandler(NULL); g_pMultiplayer->SetGameEntityRenderHandler(NULL); g_pMultiplayer->SetDrivebyAnimationHandler(nullptr); + g_pMultiplayer->SetWaterCannonHitWorldHandler(nullptr); g_pGame->SetPreWeaponFireHandler(NULL); g_pGame->SetPostWeaponFireHandler(NULL); g_pGame->SetTaskSimpleBeHitHandler(NULL); @@ -2744,6 +2746,7 @@ void CClientGame::AddBuiltInEvents(void) m_Events.AddEvent("onClientElementStreamIn", "", NULL, false); m_Events.AddEvent("onClientElementStreamOut", "", NULL, false); m_Events.AddEvent("onClientElementDestroy", "", NULL, false); + m_Events.AddEvent("onClientElementHitByWaterCannon", "vehicle, hitX, hitY, hitZ, normalX, normalY, normalZ, model, materialID", nullptr, false); // Player events m_Events.AddEvent("onClientPlayerJoin", "", NULL, false); @@ -3798,6 +3801,11 @@ AnimationId CClientGame::StaticDrivebyAnimationHandler(AnimationId animGroup, As return g_pClientGame->DrivebyAnimationHandler(animGroup, animId); } +void CClientGame::StaticWaterCannonHitWorldHandler(SWaterCannonHitEvent& event) +{ + g_pClientGame->WaterCannonHitWorldHandler(event); +} + void CClientGame::DrawRadarAreasHandler(void) { m_pRadarAreaManager->DoPulse(); @@ -6885,3 +6893,28 @@ void CClientGame::RemoveAnimationAssociationFromMap(CAnimBlendAssociationSAInter { m_mapOfCustomAnimationAssociations.erase(pAnimAssociation); } + +void CClientGame::WaterCannonHitWorldHandler(SWaterCannonHitEvent& event) +{ + CClientEntity* const pVehicle = event.pGameVehicle ? g_pClientGame->GetGameEntityXRefManager()->FindClientVehicle(event.pGameVehicle) : nullptr; + + if (!pVehicle) + return; + + CClientEntity* pEntity = event.pHitGameEntity ? g_pClientGame->GetGameEntityXRefManager()->FindClientEntity(event.pHitGameEntity) : nullptr; + + if (!pEntity) + pEntity = m_pRootEntity; + + CLuaArguments arguments; + arguments.PushElement(pVehicle); + arguments.PushNumber(event.vecPosition.fX); + arguments.PushNumber(event.vecPosition.fY); + arguments.PushNumber(event.vecPosition.fZ); + arguments.PushNumber(event.vecNormal.fX); + arguments.PushNumber(event.vecNormal.fY); + arguments.PushNumber(event.vecNormal.fZ); + arguments.PushNumber(event.iModel); + arguments.PushNumber(event.ucColSurface); + pEntity->CallEvent("onClientElementHitByWaterCannon", arguments, false); +} diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 2abb2f94d31..e0dc906af09 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -527,6 +527,7 @@ class CClientGame static void StaticGameEntityRenderHandler(CEntitySAInterface* pEntity); static void StaticTaskSimpleBeHitHandler(CPedSAInterface* pPedAttacker, ePedPieceTypes hitBodyPart, int hitBodySide, int weaponId); static void StaticFxSystemDestructionHandler(void* pFxSAInterface); + static void StaticWaterCannonHitWorldHandler(SWaterCannonHitEvent& event); static AnimationId StaticDrivebyAnimationHandler(AnimationId animGroup, AssocGroupId animId); bool DamageHandler(CPed* pDamagePed, CEventDamage* pEvent); @@ -616,6 +617,8 @@ class CClientGame void InsertAnimationAssociationToMap(CAnimBlendAssociationSAInterface* pAnimAssociation, const std::shared_ptr& pIFPAnimations); void RemoveAnimationAssociationFromMap(CAnimBlendAssociationSAInterface* pAnimAssociation); + void WaterCannonHitWorldHandler(SWaterCannonHitEvent& event); + private: eStatus m_Status; eServerType m_ServerType; diff --git a/Client/multiplayer_sa/CMultiplayerSA.cpp b/Client/multiplayer_sa/CMultiplayerSA.cpp index a2bfaa75aec..8842f6485b7 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA.cpp @@ -300,6 +300,9 @@ DWORD dwFUNC_CAEVehicleAudioEntity__ProcessAIProp = FUNC_CAEVehicleAudioEntity__ #define HOOKPOS_CTaskSimpleSwim_ProcessSwimmingResistance 0x68A4EF DWORD RETURN_CTaskSimpleSwim_ProcessSwimmingResistance = 0x68A50E; +#define HOOKPOS_CWaterCannon__Render 0x72932A +static DWORD CONTINUE_CWaterCannon__Render = 0x72932F; + CPed* pContextSwitchedPed = 0; CVector vecCenterOfWorld; FLOAT fFalseHeading; @@ -367,6 +370,7 @@ ObjectDamageHandler* m_pObjectDamageHandler = NULL; ObjectBreakHandler* m_pObjectBreakHandler = NULL; FxSystemDestructionHandler* m_pFxSystemDestructionHandler = NULL; DrivebyAnimationHandler* m_pDrivebyAnimationHandler = NULL; +WaterCannonHitWorldHandler* m_pWaterCannonHitWorldHandler = nullptr; CEntitySAInterface* dwSavedPlayerPointer = 0; CEntitySAInterface* activeEntityForStreaming = 0; // the entity that the streaming system considers active @@ -524,6 +528,8 @@ void HOOK_CAEVehicleAudioEntity__ProcessDummyProp(); void HOOK_CTaskSimpleSwim_ProcessSwimmingResistance(); +static void HOOK_CWaterCannon__Render(); + CMultiplayerSA::CMultiplayerSA() { // Unprotect all of the GTASA code at once and leave it that way @@ -757,6 +763,8 @@ void CMultiplayerSA::InitHooks() // Fix GTA:SA swimming speed problem on higher fps HookInstall(HOOKPOS_CTaskSimpleSwim_ProcessSwimmingResistance, (DWORD)HOOK_CTaskSimpleSwim_ProcessSwimmingResistance, 6); + HookInstall(HOOKPOS_CWaterCannon__Render, (DWORD)HOOK_CWaterCannon__Render, 5); + // Disable GTA setting g_bGotFocus to false when we minimize MemSet((void*)ADDR_GotFocus, 0x90, pGameInterface->GetGameVersion() == VERSION_EU_10 ? 6 : 10); @@ -1490,6 +1498,10 @@ void CMultiplayerSA::InitHooks() // Allow to switch weapons while glued MemSetFast((void*)0x60D861, 0x90, 14); + // Allow water cannon to hit objects and players visually + MemSet((void*)0x72925D, 0x1, 1); // objects + MemSet((void*)0x729263, 0x1, 1); // players + InitHooks_CrashFixHacks(); // Init our 1.3 hooks. @@ -2250,6 +2262,11 @@ void CMultiplayerSA::SetDrivebyAnimationHandler(DrivebyAnimationHandler* pHandle m_pDrivebyAnimationHandler = pHandler; } +void CMultiplayerSA::SetWaterCannonHitWorldHandler(WaterCannonHitWorldHandler* pHandler) +{ + m_pWaterCannonHitWorldHandler = pHandler; +} + // What we do here is check if the idle handler has been set bool CMultiplayerSA::IsConnected(void) { @@ -6890,3 +6907,42 @@ void _declspec(naked) HOOK_CTaskSimpleSwim_ProcessSwimmingResistance() jmp RETURN_CTaskSimpleSwim_ProcessSwimmingResistance } } + +static void __cdecl WaterCannonHitWorld(CVehicleSAInterface* pGameVehicle, CColPointSAInterface* pColPoint, CEntitySAInterface** ppGameEntity) +{ + if (m_pWaterCannonHitWorldHandler) + { + CEntitySAInterface* const pGameEntity = ppGameEntity ? *ppGameEntity : nullptr; + const int iModel = pGameEntity ? pGameEntity->m_nModelIndex : -1; + + SWaterCannonHitEvent event = { + pGameVehicle, + pGameEntity, + pColPoint->Position, + pColPoint->Normal, + iModel, + pColPoint->ucSurfaceTypeB, + }; + + m_pWaterCannonHitWorldHandler(event); + } +} + +static void _declspec(naked) HOOK_CWaterCannon__Render() +{ + _asm + { + pushad + mov eax, [ebx] // CVehicleSAInterface* CWaterCannon::m_pVehicle + lea ebx, [esp + 100h - 54h] // CColPointSAInterface* + lea ecx, [esp + 100h - 58h] // CEntitySAInterface** + push ecx // ppGameEntity + push ebx // pColPoint + push eax // pGameVehicle + call WaterCannonHitWorld + add esp, 12 + popad + push 3E4CCCCDh + jmp CONTINUE_CWaterCannon__Render + } +} diff --git a/Client/multiplayer_sa/CMultiplayerSA.h b/Client/multiplayer_sa/CMultiplayerSA.h index 639a39d0f1f..90f014ec08e 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.h +++ b/Client/multiplayer_sa/CMultiplayerSA.h @@ -130,6 +130,7 @@ class CMultiplayerSA : public CMultiplayer void SetGameEntityRenderHandler(GameEntityRenderHandler* pHandler); void SetFxSystemDestructionHandler(FxSystemDestructionHandler* pHandler); void SetDrivebyAnimationHandler(DrivebyAnimationHandler* pHandler); + void SetWaterCannonHitWorldHandler(WaterCannonHitWorldHandler* pHandler); void AllowMouseMovement(bool bAllow); void DoSoundHacksOnLostFocus(bool bLostFocus); diff --git a/Client/sdk/multiplayer/CMultiplayer.h b/Client/sdk/multiplayer/CMultiplayer.h index 3c930d44e41..4751462a65d 100644 --- a/Client/sdk/multiplayer/CMultiplayer.h +++ b/Client/sdk/multiplayer/CMultiplayer.h @@ -37,6 +37,16 @@ struct SClothesCacheStats uint uiNumRemoved; }; +struct SWaterCannonHitEvent +{ + CEntitySAInterface* pGameVehicle; + CEntitySAInterface* pHitGameEntity; + CVector vecPosition; + CVector vecNormal; + int iModel; + unsigned char ucColSurface; +}; + class CAnimBlendAssociationSAInterface; class CAnimBlendStaticAssociationSAInterface; class CAnimBlendAssocGroupSAInterface; @@ -97,6 +107,7 @@ typedef void(GameModelRemoveHandler)(ushort usModelId); typedef void(GameEntityRenderHandler)(CEntitySAInterface* pEntity); typedef void(FxSystemDestructionHandler)(void* pFxSA); typedef AnimationId(DrivebyAnimationHandler)(AnimationId animGroup, AssocGroupId animId); +typedef void(WaterCannonHitWorldHandler)(SWaterCannonHitEvent& event); /** * This class contains information used for shot syncing, one exists per player. @@ -214,6 +225,7 @@ class CMultiplayer virtual void SetGameEntityRenderHandler(GameEntityRenderHandler* pHandler) = 0; virtual void SetFxSystemDestructionHandler(FxSystemDestructionHandler* pHandler) = 0; virtual void SetDrivebyAnimationHandler(DrivebyAnimationHandler* pHandler) = 0; + virtual void SetWaterCannonHitWorldHandler(WaterCannonHitWorldHandler* pHandler) = 0; virtual void AllowMouseMovement(bool bAllow) = 0; virtual void DoSoundHacksOnLostFocus(bool bLostFocus) = 0;