diff --git a/Client/core/Graphics/CRenderItemManager.TextureReplace.cpp b/Client/core/Graphics/CRenderItemManager.TextureReplace.cpp index 284de0fab1d..d89c0d29fe5 100644 --- a/Client/core/Graphics/CRenderItemManager.TextureReplace.cpp +++ b/Client/core/Graphics/CRenderItemManager.TextureReplace.cpp @@ -80,7 +80,7 @@ void CRenderItemManager::RemoveClientEntityRefs(CClientEntityBase* pClientEntity SShaderItemLayers* CRenderItemManager::GetAppliedShaderForD3DData(CD3DDUMMY* pD3DData) { // Save texture usage for later - MapInsert(m_FrameTextureUsage, pD3DData); + MapInsert(m_FrameTextureUsage, m_pRenderWare->ResolveD3DData(pD3DData)); return m_pRenderWare->GetAppliedShaderForD3DData(pD3DData); } diff --git a/Client/game_sa/CPointLightsSA.cpp b/Client/game_sa/CPointLightsSA.cpp index 8d0a22ecfa7..8aeda447f5b 100644 --- a/Client/game_sa/CPointLightsSA.cpp +++ b/Client/game_sa/CPointLightsSA.cpp @@ -12,6 +12,10 @@ #include "StdInc.h" #include "CPointLightsSA.h" #include "CEntitySA.h" +#include "CRenderWareSA.h" +#include "CGameSA.h" + +extern CGameSA* pGame; using CHeli_SearchLightCone_t = void(__cdecl*)(int handleId, CVector startPos, CVector endPos, float radius1, float unknownConstant, int unkown1, bool renderSpot, CVector* unkown3, CVector* unkown4, CVector* unknown5, int unknown6, float radius2); @@ -119,6 +123,11 @@ static void CSearchLight_RenderShadow(char type, void* texture, CVector* pos, fl // Get original color intensity multiplier float colorIntensity = static_cast(intensity) / 128.0f; + // The game draws the spot with the texture of the street lamp light pools, so a shader could not address + // one without the other. Give the searchlights a texture of their own. + if (RwTexture* pSpotTexture = pGame->GetRenderWareSA()->GetSearchLightSpotTexture()) + texture = pSpotTexture; + // CShadows::StoreShadowToBeRendered ((void(__cdecl*)(char, void*, CVector*, float, float, float, float, std::int16_t, unsigned char, unsigned char, unsigned char, float, bool, float, void*, bool))0x707390)(type, texture, pos, x1, y1, x2, y2, intensity, static_cast(searchLightColor.R * colorIntensity), diff --git a/Client/game_sa/CRadarSA.cpp b/Client/game_sa/CRadarSA.cpp index 1ae2a6a9cc9..ab89b290f9f 100644 --- a/Client/game_sa/CRadarSA.cpp +++ b/Client/game_sa/CRadarSA.cpp @@ -57,6 +57,13 @@ CMarker* CRadarSA::GetFreeMarker() return Markers[Index]; } +CMarker* CRadarSA::GetMarker(int iIndex) +{ + if (iIndex < 0 || iIndex >= MAX_MARKERS) + return nullptr; + return Markers[iIndex]; +} + void CRadarSA::DrawAreaOnRadar(float fX1, float fY1, float fX2, float fY2, const SharedUtil::SColor color) { // Convert color to required abgr at the last moment diff --git a/Client/game_sa/CRadarSA.h b/Client/game_sa/CRadarSA.h index d060b0b9152..d3ad40aa8ca 100644 --- a/Client/game_sa/CRadarSA.h +++ b/Client/game_sa/CRadarSA.h @@ -27,5 +27,6 @@ class CRadarSA : public CRadar ~CRadarSA(); CMarker* CreateMarker(CVector* vecPosition); CMarker* GetFreeMarker(); + CMarker* GetMarker(int iIndex); void DrawAreaOnRadar(float fX1, float fY1, float fX2, float fY2, const SharedUtil::SColor color); }; diff --git a/Client/game_sa/CRenderWareSA.ShaderSupport.cpp b/Client/game_sa/CRenderWareSA.ShaderSupport.cpp index aa2927788c9..d0f233e1684 100644 --- a/Client/game_sa/CRenderWareSA.ShaderSupport.cpp +++ b/Client/game_sa/CRenderWareSA.ShaderSupport.cpp @@ -15,6 +15,7 @@ #include "CGameSA.h" #include "CRenderWareSA.ShaderMatching.h" #include "CRenderWareSA.ShaderSupport.h" +#include "gamesa_renderware.h" extern CCoreInterface* g_pCore; extern CGameSA* pGame; @@ -25,6 +26,8 @@ extern CGameSA* pGame; #define ADDR_CClothesBuilder_ConstructTextures_End 0x05A6520 #define ADDR_CVehicle_DoHeadLightBeam_RenderPrimitive 0x06E13CD #define ADDR_CHeli_SearchLightCone_RenderPrimitive 0x06C62AD +#define VAR_CShadows_gpShadowExplosionTex 0x0C403F4 +#define FAKE_NAME_SEARCHLIGHT_SPOT "shad_searchlight" #define ADDR_CWaterCannon_Render_RenderPrimitive 0x072956B enum @@ -36,7 +39,9 @@ enum RT_3DNI, }; -int CRenderWareSA::ms_iRenderingType = 0; +int CRenderWareSA::ms_iRenderingType = 0; +CD3DDUMMY* CRenderWareSA::ms_pNoTextureD3DData = FAKE_D3DTEXTURE_NO_TEXTURE; +RwTexture* CRenderWareSA::ms_pSearchLightSpotTexture = nullptr; //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// @@ -428,6 +433,82 @@ void CRenderWareSA::SetRenderingClientEntity(CClientEntityBase* pClientEntity, u m_iRenderingEntityType = iTypeMask; } +//////////////////////////////////////////////////////////////// +// +// CRenderWareSA::GetSearchLightSpotTexture +// +// The ground spot of a searchlight is drawn by CShadows with the same texture as the +// light pools of street lamps. Give the searchlights a copy of that texture under a +// name of their own, so a shader can address the spots without touching the lamps +// while the spots look exactly as they do in the game. +// +//////////////////////////////////////////////////////////////// +RwTexture* CRenderWareSA::GetSearchLightSpotTexture() +{ + if (ms_pSearchLightSpotTexture) + return ms_pSearchLightSpotTexture; + + RwTexture* pSource = *reinterpret_cast(VAR_CShadows_gpShadowExplosionTex); + if (!pSource || !pSource->raster || !pSource->raster->renderResource) + return nullptr; + + // Read the game texture as plain BGRA rows (the pixels manager also decompresses DXT) + CPixelsManagerInterface* pPixelsManager = g_pCore->GetGraphics()->GetPixelsManager(); + CPixels sourcePixels; + uint uiWidth = 0, uiHeight = 0; + if (!pPixelsManager->GetTexturePixels(reinterpret_cast(pSource->raster->renderResource), sourcePixels) || + !pPixelsManager->GetPixelsSize(sourcePixels, uiWidth, uiHeight) || uiWidth == 0 || uiHeight == 0) + return nullptr; + + RwRaster* pRaster = RwRasterCreate(uiWidth, uiHeight, 32, RASTER_TYPE_TEXTURE | RASTER_FORMAT_8888); + if (!pRaster) + return nullptr; + + if (!RwRasterLock(pRaster, 0, RASTER_LOCK_WRITE) || !pRaster->pixels) + { + RwRasterDestroy(pRaster); + return nullptr; + } + + // The sdk struct calls it numLevels, but RenderWare keeps the row stride of the locked raster there + const uint uiSourcePitch = uiWidth * sizeof(DWORD); + int iStride = pRaster->numLevels; + if (iStride <= 0) + iStride = uiSourcePitch; + for (uint y = 0; y < uiHeight; y++) + memcpy(pRaster->pixels + y * iStride, sourcePixels.GetData() + y * uiSourcePitch, uiSourcePitch); + RwRasterUnlock(pRaster); + + RwTexture* pTexture = RwTextureCreate(pRaster); + if (!pTexture) + { + RwRasterDestroy(pRaster); + return nullptr; + } + strncpy(pTexture->name, FAKE_NAME_SEARCHLIGHT_SPOT, RW_TEXTURE_NAME_LENGTH - 1); + pTexture->flags = pSource->flags; + ms_pSearchLightSpotTexture = pTexture; + + // Make it known to the texture replacer under its own name + OnTextureStreamIn(CreateTexInfo(pTexture, FAKE_NAME_SEARCHLIGHT_SPOT, reinterpret_cast(pRaster->renderResource))); + return pTexture; +} + +//////////////////////////////////////////////////////////////// +// +// CRenderWareSA::ResolveD3DData +// +// Draws without a texture are keyed by a fake texture ('unnamed', or 'searchlight' for +// the helicopter searchlight cones) when doing a 3d model like render +// +//////////////////////////////////////////////////////////////// +CD3DDUMMY* CRenderWareSA::ResolveD3DData(CD3DDUMMY* pD3DData) +{ + if (pD3DData == nullptr && CRenderWareSA::ms_iRenderingType == RT_NONE) + return CRenderWareSA::ms_pNoTextureD3DData; + return pD3DData; +} + //////////////////////////////////////////////////////////////// // // CRenderWareSA::GetAppliedShaderForD3DData @@ -439,11 +520,7 @@ SShaderItemLayers* CRenderWareSA::GetAppliedShaderForD3DData(CD3DDUMMY* pD3DData { m_uiReplacementRequestCounter++; - // If rendering with no texture, and doing an 3d model like render, use the 'unnamed' texinfo - if (pD3DData == NULL && CRenderWareSA::ms_iRenderingType == RT_NONE) - pD3DData = FAKE_D3DTEXTURE_NO_TEXTURE; - - STexInfo* pTexInfo = MapFindRef(m_D3DDataTexInfoMap, pD3DData); + STexInfo* pTexInfo = MapFindRef(m_D3DDataTexInfoMap, ResolveD3DData(pD3DData)); if (!pTexInfo) return NULL; @@ -602,6 +679,12 @@ void CRenderWareSA::Initialize() STexInfo* pTexInfo = CreateTexInfo(FAKE_RWTEXTURE_NO_TEXTURE, FAKE_NAME_NO_TEXTURE, FAKE_D3DTEXTURE_NO_TEXTURE); OnTextureStreamIn(pTexInfo); } + if (!MapContains(m_D3DDataTexInfoMap, FAKE_D3DTEXTURE_SEARCHLIGHT)) + { + // Helicopter searchlight cones draw without a texture too, give them a name of their own + STexInfo* pTexInfo = CreateTexInfo(FAKE_RWTEXTURE_SEARCHLIGHT, FAKE_NAME_SEARCHLIGHT, FAKE_D3DTEXTURE_SEARCHLIGHT); + OnTextureStreamIn(pTexInfo); + } } //////////////////////////////////////////////////////////////// @@ -768,6 +851,9 @@ __declspec(noinline) void OnMY_RwIm3DRenderIndexedPrimitive_Pre(DWORD dwAddrCall dwAddrCalledFrom == ADDR_CWaterCannon_Render_RenderPrimitive) { CRenderWareSA::ms_iRenderingType = RT_NONE; // Treat these items like world models + // The searchlight cones get a texture name of their own, so a shader can target them without every other untextured draw + if (dwAddrCalledFrom == ADDR_CHeli_SearchLightCone_RenderPrimitive) + CRenderWareSA::ms_pNoTextureD3DData = FAKE_D3DTEXTURE_SEARCHLIGHT; } else { @@ -778,6 +864,7 @@ __declspec(noinline) void OnMY_RwIm3DRenderIndexedPrimitive_Pre(DWORD dwAddrCall __declspec(noinline) void OnMY_RwIm3DRenderIndexedPrimitive_Post(DWORD dwAddrCalledFrom) { CRenderWareSA::ms_iRenderingType = RT_NONE; + CRenderWareSA::ms_pNoTextureD3DData = FAKE_D3DTEXTURE_NO_TEXTURE; } // Hook info diff --git a/Client/game_sa/CRenderWareSA.ShaderSupport.h b/Client/game_sa/CRenderWareSA.ShaderSupport.h index a91f90044ac..19f596139c5 100644 --- a/Client/game_sa/CRenderWareSA.ShaderSupport.h +++ b/Client/game_sa/CRenderWareSA.ShaderSupport.h @@ -25,6 +25,9 @@ #define FAKE_D3DTEXTURE_NO_TEXTURE ((CD3DDUMMY*)-9) #define FAKE_RWTEXTURE_NO_TEXTURE ((RwTexture*)-10) #define FAKE_NAME_NO_TEXTURE "unnamed" +#define FAKE_D3DTEXTURE_SEARCHLIGHT ((CD3DDUMMY*)-11) +#define FAKE_RWTEXTURE_SEARCHLIGHT ((RwTexture*)-12) +#define FAKE_NAME_SEARCHLIGHT "searchlight" class CMatchChannel; class CMatchChannelManager; diff --git a/Client/game_sa/CRenderWareSA.h b/Client/game_sa/CRenderWareSA.h index 1fc217ad967..6112e19e0b5 100644 --- a/Client/game_sa/CRenderWareSA.h +++ b/Client/game_sa/CRenderWareSA.h @@ -101,6 +101,7 @@ class CRenderWareSA : public CRenderWare const char* GetTextureName(CD3DDUMMY* pD3DData); void SetRenderingClientEntity(CClientEntityBase* pClientEntity, ushort usModelId, int iTypeMask); SShaderItemLayers* GetAppliedShaderForD3DData(CD3DDUMMY* pD3DData); + CD3DDUMMY* ResolveD3DData(CD3DDUMMY* pD3DData); void AppendAdditiveMatch(CSHADERDUMMY* pShaderData, CClientEntityBase* pClientEntity, const char* strTextureNameMatch, float fShaderPriority, bool bShaderLayered, int iTypeMask, uint uiShaderCreateTime, bool bShaderUsesVertexShader, bool bAppendLayers); void AppendSubtractiveMatch(CSHADERDUMMY* pShaderData, CClientEntityBase* pClientEntity, const char* strTextureNameMatch); @@ -167,4 +168,7 @@ class CRenderWareSA : public CRenderWare bool m_bGTAVertexShadersEnabled; std::set m_SpecialTextures; static int ms_iRenderingType; + static CD3DDUMMY* ms_pNoTextureD3DData; // Fake texture used for draws without a texture + static RwTexture* ms_pSearchLightSpotTexture; + RwTexture* GetSearchLightSpotTexture(); }; diff --git a/Client/game_sa/gamesa_renderware.h b/Client/game_sa/gamesa_renderware.h index 224970c17cd..55b7ed42bee 100644 --- a/Client/game_sa/gamesa_renderware.h +++ b/Client/game_sa/gamesa_renderware.h @@ -98,6 +98,7 @@ typedef int(__cdecl* rwD3D9NativeTextureRead_t)(RwStream* stream, RwTexture** te typedef RwRaster*(__cdecl* RwRasterUnlock_t)(RwRaster* raster); typedef RwRaster*(__cdecl* RwRasterLock_t)(RwRaster* raster, unsigned char level, int lockmode); typedef RwRaster*(__cdecl* RwRasterCreate_t)(int width, int height, int depth, int flags); +typedef int(__cdecl* RwRasterDestroy_t)(RwRaster* raster); typedef RwTexture*(__cdecl* RwTextureCreate_t)(RwRaster* raster); typedef RpMaterial*(__cdecl* RpMaterialSetTexture_t)(RpMaterial* mat, RwTexture* tex); typedef RpHAnimHierarchy*(__cdecl* GetAnimHierarchyFromClump_t)(RpClump*); @@ -188,6 +189,7 @@ RWFUNC(RwTextureDestroy_t RwTextureDestroy, (RwTextureDestroy_t)0xDEAD) RWFUNC(RwRasterUnlock_t RwRasterUnlock, (RwRasterUnlock_t)0xDEAD) RWFUNC(RwRasterLock_t RwRasterLock, (RwRasterLock_t)0xDEAD) RWFUNC(RwRasterCreate_t RwRasterCreate, (RwRasterCreate_t)0xDEAD) +RWFUNC(RwRasterDestroy_t RwRasterDestroy, (RwRasterDestroy_t)0xDEAD) RWFUNC(RwTextureCreate_t RwTextureCreate, (RwTextureCreate_t)0xDEAD) RWFUNC(RpMaterialSetTexture_t RpMaterialSetTexture, (RpMaterialSetTexture_t)0xDEAD) RWFUNC(GetAnimHierarchyFromClump_t GetAnimHierarchyFromClump, (GetAnimHierarchyFromClump_t)0xDEAD) diff --git a/Client/game_sa/gamesa_renderware.hpp b/Client/game_sa/gamesa_renderware.hpp index 4f9bd21484c..14b04cf80cc 100644 --- a/Client/game_sa/gamesa_renderware.hpp +++ b/Client/game_sa/gamesa_renderware.hpp @@ -82,6 +82,7 @@ void InitRwFunctions() RwRasterUnlock = (RwRasterUnlock_t)0x007FAEC0; RwRasterLock = (RwRasterLock_t)0x007FB2D0; RwRasterCreate = (RwRasterCreate_t)0x007FB230; + RwRasterDestroy = (RwRasterDestroy_t)0x007FB020; RwTextureCreate = (RwTextureCreate_t)0x007F37C0; RpMaterialSetTexture = (RpMaterialSetTexture_t)0x0074DBC0; GetAnimHierarchyFromClump = (GetAnimHierarchyFromClump_t)0x734B10; diff --git a/Client/mods/deathmatch/logic/CClient3DMarker.cpp b/Client/mods/deathmatch/logic/CClient3DMarker.cpp index 0ceeb75d200..b1852da6be2 100644 --- a/Client/mods/deathmatch/logic/CClient3DMarker.cpp +++ b/Client/mods/deathmatch/logic/CClient3DMarker.cpp @@ -25,10 +25,12 @@ CClient3DMarker::CClient3DMarker(CClientMarker* pThis) m_dwType = static_cast(T3DMarkerType::MARKER3D_CYLINDER2); m_pMarker = NULL; m_ulIdentifier = (DWORD)this; + g_pClientGame->GetManager()->GetMarkerManager()->RegisterIdentifier(m_ulIdentifier, pThis); } CClient3DMarker::~CClient3DMarker() { + g_pClientGame->GetManager()->GetMarkerManager()->UnregisterIdentifier(m_ulIdentifier); } unsigned long CClient3DMarker::Get3DMarkerType() diff --git a/Client/mods/deathmatch/logic/CClientCheckpoint.cpp b/Client/mods/deathmatch/logic/CClientCheckpoint.cpp index 1a523ebd680..1bc327ad137 100644 --- a/Client/mods/deathmatch/logic/CClientCheckpoint.cpp +++ b/Client/mods/deathmatch/logic/CClientCheckpoint.cpp @@ -20,6 +20,7 @@ CClientCheckpoint::CClientCheckpoint(CClientMarker* pThis) // Init m_pThis = pThis; m_pCheckpoint = NULL; + m_dwIdentifier = 0; m_bStreamedIn = false; m_bVisible = true; m_uiIcon = CClientCheckpoint::ICON_NONE; @@ -37,6 +38,8 @@ CClientCheckpoint::~CClientCheckpoint() { // Eventually destroy the checkpoint Destroy(); + if (m_dwIdentifier) + g_pClientGame->GetManager()->GetMarkerManager()->UnregisterIdentifier(m_dwIdentifier); CClientEntityRefManager::RemoveEntityRefs(0, &m_pThis, NULL); } @@ -350,12 +353,15 @@ void CClientCheckpoint::Create(unsigned long ulIdentifier) if (ulIdentifier == 0) { s_ulIdentifier++; + if (m_dwIdentifier) + g_pClientGame->GetManager()->GetMarkerManager()->UnregisterIdentifier(m_dwIdentifier); m_dwIdentifier = s_ulIdentifier; } else { m_dwIdentifier = ulIdentifier; } + g_pClientGame->GetManager()->GetMarkerManager()->RegisterIdentifier(m_dwIdentifier, m_pThis); // Create it m_pCheckpoint = diff --git a/Client/mods/deathmatch/logic/CClientCorona.cpp b/Client/mods/deathmatch/logic/CClientCorona.cpp index c985fc20780..da90e65a34c 100644 --- a/Client/mods/deathmatch/logic/CClientCorona.cpp +++ b/Client/mods/deathmatch/logic/CClientCorona.cpp @@ -28,6 +28,7 @@ CClientCorona::CClientCorona(CClientMarker* pThis) // Pick an unique identifier static unsigned long ulIdentifier = 0xFFFFFFFF; m_ulIdentifier = --ulIdentifier; + g_pClientGame->GetManager()->GetMarkerManager()->RegisterIdentifier(m_ulIdentifier, pThis); } CClientCorona::~CClientCorona() @@ -38,6 +39,7 @@ CClientCorona::~CClientCorona() { pCorona->Disable(); } + g_pClientGame->GetManager()->GetMarkerManager()->UnregisterIdentifier(m_ulIdentifier); CClientEntityRefManager::RemoveEntityRefs(0, &m_pThis, NULL); } diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 6ed7e017179..f5cb50086e1 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -313,6 +314,8 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) g_pMultiplayer->SetGameModelRemoveHandler(CClientGame::StaticGameModelRemoveHandler); g_pMultiplayer->SetGameRunNamedAnimDestructorHandler(CClientGame::StaticGameRunNamedAnimDestructorHandler); g_pMultiplayer->SetGameEntityRenderHandler(CClientGame::StaticGameEntityRenderHandler); + g_pMultiplayer->SetRadarBlipRenderHandler(CClientGame::StaticRadarBlipRenderHandler); + g_pMultiplayer->SetMarkerRenderHandler(CClientGame::StaticMarkerRenderHandler); g_pMultiplayer->SetFxSystemDestructionHandler(CClientGame::StaticFxSystemDestructionHandler); g_pMultiplayer->SetDrivebyAnimationHandler(CClientGame::StaticDrivebyAnimationHandler); g_pMultiplayer->SetPedStepHandler(CClientGame::StaticPedStepHandler); @@ -528,6 +531,8 @@ CClientGame::~CClientGame() g_pMultiplayer->SetGameModelRemoveHandler(NULL); g_pMultiplayer->SetGameRunNamedAnimDestructorHandler(nullptr); g_pMultiplayer->SetGameEntityRenderHandler(NULL); + g_pMultiplayer->SetRadarBlipRenderHandler(nullptr); + g_pMultiplayer->SetMarkerRenderHandler(nullptr); g_pMultiplayer->SetDrivebyAnimationHandler(nullptr); g_pMultiplayer->SetPedStepHandler(nullptr); g_pMultiplayer->SetVehicleWeaponHitHandler(nullptr); @@ -3761,6 +3766,15 @@ void CClientGame::StaticGameEntityRenderHandler(CEntitySAInterface* pGameEntity) CPools* pPools = g_pGame->GetPools(); // Map to client entity and pass to the texture replacer CClientEntity* pClientEntity = pPools->GetClientEntity((DWORD*)pGameEntity); + if (!pClientEntity) + { + // Pickup and projectile objects are not registered in the object pool. The interface type is not visible + // here, so every miss pays for the two lookups: a hash find while pickups exist and a walk of the (short) projectile list. + CClientManager* pManager = g_pClientGame->GetManager(); + pClientEntity = pManager->GetPickupManager()->GetPickupByGameObject(pGameEntity); + if (!pClientEntity) + pClientEntity = pManager->GetProjectileManager()->Get(pGameEntity); + } if (pClientEntity) { int iTypeMask; @@ -3776,6 +3790,8 @@ void CClientGame::StaticGameEntityRenderHandler(CEntitySAInterface* pGameEntity) iTypeMask = TYPE_MASK_VEHICLE; break; case CCLIENTOBJECT: + case CCLIENTPICKUP: + case CCLIENTPROJECTILE: iTypeMask = TYPE_MASK_OBJECT; break; case CCLIENTBUILDING: @@ -3793,6 +3809,51 @@ void CClientGame::StaticGameEntityRenderHandler(CEntitySAInterface* pGameEntity) g_pGame->GetRenderWare()->SetRenderingClientEntity(NULL, 0xFFFF, TYPE_MASK_WORLD); } +void CClientGame::StaticRadarBlipRenderHandler(int iBlipIndex) +{ + if (iBlipIndex >= 0) + { + CMarker* pGameMarker = g_pGame->GetRadar()->GetMarker(iBlipIndex); + CClientRadarMarker* pBlip = g_pClientGame->GetManager()->GetRadarMarkerManager()->GetMarkerByGameMarker(pGameMarker); + if (pBlip) + { + g_pGame->GetRenderWare()->SetRenderingClientEntity(pBlip, 0xFFFF, TYPE_MASK_OTHER); + return; + } + } + + g_pGame->GetRenderWare()->SetRenderingClientEntity(nullptr, 0xFFFF, TYPE_MASK_WORLD); +} + +// CHeli::ProcessControl registers the searchlight cone and corona of a helicopter with the vehicle pointer plus this +static constexpr unsigned long HELI_SEARCHLIGHT_IDENTIFIER_OFFSET = 0xB; + +void CClientGame::StaticMarkerRenderHandler(unsigned long ulIdentifier) +{ + if (ulIdentifier) + { + CClientEntity* pEntity = g_pClientGame->GetManager()->GetMarkerManager()->GetEntityByIdentifier(ulIdentifier); + if (pEntity) + { + g_pGame->GetRenderWare()->SetRenderingClientEntity(pEntity, 0xFFFF, TYPE_MASK_OTHER); + return; + } + + // The pool lookup rounds to the containing slot, so make sure the identifier really points at a vehicle + const unsigned long ulVehicleInterface = ulIdentifier - HELI_SEARCHLIGHT_IDENTIFIER_OFFSET; + CClientEntity* pVehicle = g_pGame->GetPools()->GetClientEntity(reinterpret_cast(ulVehicleInterface)); + bool bExact = pVehicle && pVehicle->GetType() == CCLIENTVEHICLE && pVehicle->GetGameEntity() && + reinterpret_cast(pVehicle->GetGameEntity()->GetInterface()) == ulVehicleInterface; + if (bExact) + { + g_pGame->GetRenderWare()->SetRenderingClientEntity(pVehicle, static_cast(pVehicle)->GetModel(), TYPE_MASK_VEHICLE); + return; + } + } + + g_pGame->GetRenderWare()->SetRenderingClientEntity(nullptr, 0xFFFF, TYPE_MASK_WORLD); +} + void CClientGame::StaticTaskSimpleBeHitHandler(CPedSAInterface* pPedAttacker, ePedPieceTypes hitBodyPart, int hitBodySide, int weaponId) { g_pClientGame->TaskSimpleBeHitHandler(pPedAttacker, hitBodyPart, hitBodySide, weaponId); @@ -5033,6 +5094,9 @@ bool CClientGame::VehicleFellThroughMapHandler(CVehicleSAInterface* pVehicleInte // Clear stale pool entries to prevent dangling pointer crashes in GetClientEntity/GetEntity. void CClientGame::GameObjectDestructHandler(CEntitySAInterface* pObject) { + if (m_pManager) + m_pManager->GetPickupManager()->OnGameObjectDestroyed(pObject); + if (auto* pSlot = g_pGame->GetPools()->GetObject(reinterpret_cast(pObject))) { pSlot->pEntity = nullptr; diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index f010e6d0c57..1663ac69581 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -611,6 +611,8 @@ class CClientGame static void StaticGameRunNamedAnimDestructorHandler(class CTaskSimpleRunNamedAnimSAInterface* pTask); static bool StaticWorldSoundHandler(const SWorldSoundEvent& event); static void StaticGameEntityRenderHandler(CEntitySAInterface* pEntity); + static void StaticRadarBlipRenderHandler(int iBlipIndex); + static void StaticMarkerRenderHandler(unsigned long ulIdentifier); static void StaticTaskSimpleBeHitHandler(CPedSAInterface* pPedAttacker, ePedPieceTypes hitBodyPart, int hitBodySide, int weaponId); static void StaticFxSystemDestructionHandler(void* pFxSAInterface); static void StaticPedStepHandler(CPedSAInterface* pPed, bool bFoot); diff --git a/Client/mods/deathmatch/logic/CClientMarkerManager.cpp b/Client/mods/deathmatch/logic/CClientMarkerManager.cpp index 42b9059b276..b9baf3dadf4 100644 --- a/Client/mods/deathmatch/logic/CClientMarkerManager.cpp +++ b/Client/mods/deathmatch/logic/CClientMarkerManager.cpp @@ -49,6 +49,13 @@ void CClientMarkerManager::DeleteAll() // Clear the list m_Markers.clear(); + m_IdentifierMap.clear(); +} + +CClientEntity* CClientMarkerManager::GetEntityByIdentifier(unsigned long ulIdentifier) const +{ + auto iter = m_IdentifierMap.find(ulIdentifier); + return iter != m_IdentifierMap.end() ? iter->second : nullptr; } void CClientMarkerManager::DoPulse() diff --git a/Client/mods/deathmatch/logic/CClientMarkerManager.h b/Client/mods/deathmatch/logic/CClientMarkerManager.h index fd58c823ac3..6d072b8c074 100644 --- a/Client/mods/deathmatch/logic/CClientMarkerManager.h +++ b/Client/mods/deathmatch/logic/CClientMarkerManager.h @@ -12,6 +12,7 @@ #include "CClientMarker.h" #include +#include class CClientMarkerManager { @@ -25,6 +26,11 @@ class CClientMarkerManager void DeleteAll(); static bool IsMarkerModel(unsigned short usModel); + // Game marker and corona identifiers -> owning element (markers and searchlights) + CClientEntity* GetEntityByIdentifier(unsigned long ulIdentifier) const; + void RegisterIdentifier(unsigned long ulIdentifier, CClientEntity* pEntity) { m_IdentifierMap[ulIdentifier] = pEntity; } + void UnregisterIdentifier(unsigned long ulIdentifier) { m_IdentifierMap.erase(ulIdentifier); } + private: CClientMarkerManager(class CClientManager* pManager); ~CClientMarkerManager(); @@ -39,4 +45,6 @@ class CClientMarkerManager class CClientManager* m_pManager; CFastList m_Markers; bool m_bCanRemoveFromList; + + std::unordered_map m_IdentifierMap; }; diff --git a/Client/mods/deathmatch/logic/CClientPickup.cpp b/Client/mods/deathmatch/logic/CClientPickup.cpp index bf51ac1d9c8..8b6f76221e4 100644 --- a/Client/mods/deathmatch/logic/CClientPickup.cpp +++ b/Client/mods/deathmatch/logic/CClientPickup.cpp @@ -188,6 +188,8 @@ bool CClientPickup::Create() return false; } + m_pPickupManager->RegisterGameObject(m_pObject->GetInterface(), this); + // Create our collision sphere m_pCollision = new CClientColSphere(g_pClientGame->GetManager(), ElementID(INVALID_ELEMENT_ID), m_vecPosition, 1.0f); m_pCollision->m_pOwningPickup = this; @@ -220,6 +222,9 @@ void CClientPickup::Destroy() // Delete the pickup if (m_pPickup) { + if (m_pObject) + m_pPickupManager->UnregisterGameObject(m_pObject->GetInterface(), this); + // Clear object reference before Remove() to prevent dangling pointer m_pObject = nullptr; m_pPickup->Remove(); diff --git a/Client/mods/deathmatch/logic/CClientPickupManager.cpp b/Client/mods/deathmatch/logic/CClientPickupManager.cpp index 6cc7b07c3ca..5a5e15433d6 100644 --- a/Client/mods/deathmatch/logic/CClientPickupManager.cpp +++ b/Client/mods/deathmatch/logic/CClientPickupManager.cpp @@ -70,6 +70,7 @@ void CClientPickupManager::DeleteAll() // Clear the list m_List.clear(); + m_GameObjectMap.clear(); // Restore previous processing state if (!wasDisabled) @@ -124,6 +125,30 @@ unsigned short CClientPickupManager::GetWeaponModel(unsigned int uiWeaponID) return 0; } +CClientPickup* CClientPickupManager::GetPickupByGameObject(const CEntitySAInterface* pGameObject) const +{ + auto iter = m_GameObjectMap.find(pGameObject); + return iter != m_GameObjectMap.end() ? iter->second : nullptr; +} + +void CClientPickupManager::OnGameObjectDestroyed(const CEntitySAInterface* pGameObject) +{ + m_GameObjectMap.erase(pGameObject); +} + +void CClientPickupManager::RegisterGameObject(const CEntitySAInterface* pGameObject, CClientPickup* pPickup) +{ + m_GameObjectMap[pGameObject] = pPickup; +} + +void CClientPickupManager::UnregisterGameObject(const CEntitySAInterface* pGameObject, const CClientPickup* pPickup) +{ + // The object slot can already belong to a newer pickup + auto iter = m_GameObjectMap.find(pGameObject); + if (iter != m_GameObjectMap.end() && iter->second == pPickup) + m_GameObjectMap.erase(iter); +} + void CClientPickupManager::RemoveFromList(CClientPickup* pPickup) { if (!m_bDontRemoveFromList) diff --git a/Client/mods/deathmatch/logic/CClientPickupManager.h b/Client/mods/deathmatch/logic/CClientPickupManager.h index 384a8424750..71d8f2eecdd 100644 --- a/Client/mods/deathmatch/logic/CClientPickupManager.h +++ b/Client/mods/deathmatch/logic/CClientPickupManager.h @@ -15,6 +15,9 @@ class CClientPickupManager; #include "CClientManager.h" #include "CClientPickup.h" #include +#include + +class CEntitySAInterface; class CClientPickupManager { @@ -45,17 +48,24 @@ class CClientPickupManager void RestreamPickups(unsigned short usModel); void RestreamAllPickups(); + CClientPickup* GetPickupByGameObject(const CEntitySAInterface* pGameObject) const; + void OnGameObjectDestroyed(const CEntitySAInterface* pGameObject); + private: CClientPickupManager(CClientManager* pManager); ~CClientPickupManager(); void RemoveFromList(CClientPickup* pPickup); + void RegisterGameObject(const CEntitySAInterface* pGameObject, CClientPickup* pPickup); + void UnregisterGameObject(const CEntitySAInterface* pGameObject, const CClientPickup* pPickup); CClientManager* m_pManager; std::list m_List; bool m_bDontRemoveFromList; + std::unordered_map m_GameObjectMap; + bool m_bPickupProcessingDisabled; static unsigned int m_uiPickupCount; }; diff --git a/Client/mods/deathmatch/logic/CClientProjectileManager.cpp b/Client/mods/deathmatch/logic/CClientProjectileManager.cpp index 862ebc68c40..74ccf6fba4b 100644 --- a/Client/mods/deathmatch/logic/CClientProjectileManager.cpp +++ b/Client/mods/deathmatch/logic/CClientProjectileManager.cpp @@ -88,7 +88,8 @@ CClientProjectile* CClientProjectileManager::Get(CEntitySAInterface* pProjectile list::iterator iter = m_List.begin(); for (; iter != m_List.end(); iter++) { - if ((*iter)->GetGameEntity()->GetInterface() == pProjectile) + CEntity* pGameEntity = (*iter)->GetGameEntity(); + if (pGameEntity && pGameEntity->GetInterface() == pProjectile) { return (*iter); } diff --git a/Client/mods/deathmatch/logic/CClientRadarMarker.cpp b/Client/mods/deathmatch/logic/CClientRadarMarker.cpp index be13f67b3ff..5f48e73ff2c 100644 --- a/Client/mods/deathmatch/logic/CClientRadarMarker.cpp +++ b/Client/mods/deathmatch/logic/CClientRadarMarker.cpp @@ -227,6 +227,7 @@ void CClientRadarMarker::CreateMarker() m_pMarker->SetScale(m_usScale); m_pMarker->SetColor(color); m_pMarker->SetSprite(static_cast(m_ulSprite)); + m_pRadarMarkerManager->RegisterGameMarker(m_pMarker, this); } } } @@ -236,6 +237,7 @@ void CClientRadarMarker::DestroyMarker() { if (m_pMarker) { + m_pRadarMarkerManager->UnregisterGameMarker(m_pMarker); m_pMarker->Remove(); m_pMarker = NULL; } diff --git a/Client/mods/deathmatch/logic/CClientRadarMarkerManager.cpp b/Client/mods/deathmatch/logic/CClientRadarMarkerManager.cpp index e7f00c96cf0..6c965482bd1 100644 --- a/Client/mods/deathmatch/logic/CClientRadarMarkerManager.cpp +++ b/Client/mods/deathmatch/logic/CClientRadarMarkerManager.cpp @@ -54,9 +54,16 @@ void CClientRadarMarkerManager::DeleteAll() // Clear the list m_Markers.clear(); + m_GameMarkerMap.clear(); m_bCanRemoveFromList = true; } +CClientRadarMarker* CClientRadarMarkerManager::GetMarkerByGameMarker(const CMarker* pGameMarker) const +{ + auto iter = m_GameMarkerMap.find(pGameMarker); + return iter != m_GameMarkerMap.end() ? iter->second : nullptr; +} + CClientRadarMarker* CClientRadarMarkerManager::Get(ElementID ID) { // Grab the element with the given id. Check its type. diff --git a/Client/mods/deathmatch/logic/CClientRadarMarkerManager.h b/Client/mods/deathmatch/logic/CClientRadarMarkerManager.h index 8fd0450ca22..9e7eef6690d 100644 --- a/Client/mods/deathmatch/logic/CClientRadarMarkerManager.h +++ b/Client/mods/deathmatch/logic/CClientRadarMarkerManager.h @@ -14,6 +14,9 @@ class CClientRadarMarkerManager; #include "CClientRadarMarker.h" #include +#include + +class CMarker; class CClientRadarMarkerManager { @@ -38,9 +41,13 @@ class CClientRadarMarkerManager bool Exists(CClientRadarMarker* pMarker); static bool IsValidIcon(unsigned long ulIcon) noexcept { return ulIcon <= RADAR_MARKER_LIMIT; } + CClientRadarMarker* GetMarkerByGameMarker(const CMarker* pGameMarker) const; + private: void AddToList(CClientRadarMarker* pMarker) { m_Markers.push_back(pMarker); }; void RemoveFromList(CClientRadarMarker* pMarker); + void RegisterGameMarker(const CMarker* pGameMarker, CClientRadarMarker* pMarker) { m_GameMarkerMap[pGameMarker] = pMarker; } + void UnregisterGameMarker(const CMarker* pGameMarker) { m_GameMarkerMap.erase(pGameMarker); } void OrderMarkers(); static bool CompareOrderingIndex(CClientRadarMarker* p1, CClientRadarMarker* p2); @@ -49,6 +56,8 @@ class CClientRadarMarkerManager bool m_bCanRemoveFromList; std::list m_Markers; + std::unordered_map m_GameMarkerMap; + unsigned short m_usDimension; bool m_bOrderOnPulse; CVector m_vecCameraPosition; diff --git a/Client/mods/deathmatch/logic/CClientSearchLight.cpp b/Client/mods/deathmatch/logic/CClientSearchLight.cpp index deece435bb1..5b3c8f8c222 100644 --- a/Client/mods/deathmatch/logic/CClientSearchLight.cpp +++ b/Client/mods/deathmatch/logic/CClientSearchLight.cpp @@ -32,6 +32,12 @@ void CClientSearchLight::Render() { DoAttaching(); - if (IsStreamedIn()) - g_pGame->GetPointLights()->RenderHeliLight(m_StartPosition, m_EndPosition, m_StartRadius, m_EndRadius, m_bRenderSpot, m_color); + if (!IsStreamedIn()) + return; + + // Tell the texture replacer which element the cone belongs to + CRenderWare* pRenderWare = g_pGame->GetRenderWare(); + pRenderWare->SetRenderingClientEntity(this, 0xFFFF, TYPE_MASK_OTHER); + g_pGame->GetPointLights()->RenderHeliLight(m_StartPosition, m_EndPosition, m_StartRadius, m_EndRadius, m_bRenderSpot, m_color); + pRenderWare->SetRenderingClientEntity(nullptr, 0xFFFF, TYPE_MASK_WORLD); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index d8d393e085e..4d100ad1fdf 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -1303,6 +1303,36 @@ int CLuaEngineDefs::EngineReplaceVehiclePart(lua_State* luaVM) return 1; } +// Element types the texture replacer can tell apart while they render +static bool IsShaderTargetElement(CClientEntity* pElement) +{ + switch (pElement->GetType()) + { + case CCLIENTPED: + case CCLIENTPLAYER: + case CCLIENTVEHICLE: + case CCLIENTOBJECT: + case CCLIENTWEAPON: + case CCLIENTBUILDING: + case CCLIENTPICKUP: + case CCLIENTPROJECTILE: + case CCLIENTSEARCHLIGHT: + case CCLIENTRADARMARKER: + case CCLIENTMARKER: + return true; + default: + return false; + } +} + +static void ReadShaderTargetElement(CScriptArgReader& argStream, CClientEntity*& pElement) +{ + argStream.ReadUserData(pElement, nullptr); + if (!argStream.HasErrors() && pElement && !IsShaderTargetElement(pElement)) + argStream.SetCustomError("targetElement must be a ped, player, vehicle, object, weapon, building, pickup, projectile, searchlight, blip or marker", + "Bad argument"); +} + int CLuaEngineDefs::EngineApplyShaderToWorldTexture(lua_State* luaVM) { // bool engineApplyShaderToWorldTexture ( element shader, string textureName, [ element targetElement, bool appendLayers ] ) @@ -1314,7 +1344,7 @@ int CLuaEngineDefs::EngineApplyShaderToWorldTexture(lua_State* luaVM) CScriptArgReader argStream(luaVM); argStream.ReadUserData(pShader); argStream.ReadString(strTextureNameMatch); - argStream.ReadUserData(pElement, NULL); + ReadShaderTargetElement(argStream, pElement); argStream.ReadBool(bAppendLayers, true); if (!argStream.HasErrors()) @@ -1342,7 +1372,7 @@ int CLuaEngineDefs::EngineRemoveShaderFromWorldTexture(lua_State* luaVM) CScriptArgReader argStream(luaVM); argStream.ReadUserData(pShader); argStream.ReadString(strTextureNameMatch); - argStream.ReadUserData(pElement, NULL); + ReadShaderTargetElement(argStream, pElement); if (!argStream.HasErrors()) { diff --git a/Client/multiplayer_sa/CMultiplayerSA.h b/Client/multiplayer_sa/CMultiplayerSA.h index 9f85b307909..0f1e887c741 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.h +++ b/Client/multiplayer_sa/CMultiplayerSA.h @@ -148,6 +148,8 @@ class CMultiplayerSA : public CMultiplayer void SetGameModelRemoveHandler(GameModelRemoveHandler* pHandler); void SetGameRunNamedAnimDestructorHandler(GameRunNamedAnimDestructorHandler* pHandler); void SetGameEntityRenderHandler(GameEntityRenderHandler* pHandler); + void SetRadarBlipRenderHandler(RadarBlipRenderHandler* pHandler); + void SetMarkerRenderHandler(MarkerRenderHandler* pHandler); void SetFxSystemDestructionHandler(FxSystemDestructionHandler* pHandler); void SetDrivebyAnimationHandler(DrivebyAnimationHandler* pHandler); void SetPedStepHandler(PedStepHandler* pHandler); diff --git a/Client/multiplayer_sa/CMultiplayerSA_Rendering.cpp b/Client/multiplayer_sa/CMultiplayerSA_Rendering.cpp index 77c49d8de2b..bc6deb4c2c9 100644 --- a/Client/multiplayer_sa/CMultiplayerSA_Rendering.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA_Rendering.cpp @@ -12,6 +12,8 @@ #include extern CCoreInterface* g_pCore; GameEntityRenderHandler* pGameEntityRenderHandler = nullptr; +RadarBlipRenderHandler* pRadarBlipRenderHandler = nullptr; +MarkerRenderHandler* pMarkerRenderHandler = nullptr; PreRenderSkyHandler* pPreRenderSkyHandlerHandler = nullptr; RenderHeliLightHandler* pRenderHeliLightHandler = nullptr; RenderEverythingBarRoadsHandler* pRenderEverythingBarRoadsHandler = nullptr; @@ -733,6 +735,177 @@ static void __declspec(naked) HOOK_RenderEffects_HeliLight() // clang-format on } +////////////////////////////////////////////////////////////////////////////////////////// +// +// CRadar::DrawBlips +// +// Detect radar blip rendering +// +////////////////////////////////////////////////////////////////////////////////////////// +void OnMY_CRadar_DrawCoordBlip_Pre(int iBlipIndex) +{ + if (pRadarBlipRenderHandler) + pRadarBlipRenderHandler(iBlipIndex); +} + +void OnMY_CRadar_DrawCoordBlip_Post() +{ + if (pRadarBlipRenderHandler) + pRadarBlipRenderHandler(-1); +} + +// Hook info +#define HOOKPOS_CRadar_DrawBlips_CoordBlip 0x588347 +#define HOOKPOS_CRadar_DrawBlips_CoordBlipWaypoint 0x588411 // second pass for the waypoint icon +#define FUNC_CRadar_DrawCoordBlip 0x586D60 +static void __declspec(naked) HOOK_CRadar_DrawCoordBlip() +{ + MTA_VERIFY_HOOK_LOCAL_SIZE; + + // clang-format off + __asm + { + pushad + push dword ptr [esp+24h] + call OnMY_CRadar_DrawCoordBlip_Pre + add esp, 4*1 + popad + + // Original call with its arguments + push dword ptr [esp+8] + push dword ptr [esp+8] + mov eax, FUNC_CRadar_DrawCoordBlip + call eax + add esp, 4*2 + + pushad + call OnMY_CRadar_DrawCoordBlip_Post + popad + retn + } + // clang-format on +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// C3dMarkers::Render +// +// Detect 3D marker rendering (cylinder, arrow, ring and checkpoint geometry) +// +////////////////////////////////////////////////////////////////////////////////////////// +void OnMY_MarkerRender_Pre(DWORD dwIdentifier) +{ + if (pMarkerRenderHandler) + pMarkerRenderHandler(dwIdentifier); +} + +void OnMY_MarkerRender_Post() +{ + if (pMarkerRenderHandler) + pMarkerRenderHandler(0); +} + +// Hook info +#define HOOKPOS_C3dMarkers_Render_Marker 0x7250B1 +#define FUNC_C3dMarker_Render 0x7223D0 +#define OFFSET_C3dMarker_Identifier 0x54 +static void __declspec(naked) HOOK_C3dMarkers_Render_Marker() +{ + MTA_VERIFY_HOOK_LOCAL_SIZE; + + // clang-format off + __asm + { + pushad + push dword ptr [ecx+OFFSET_C3dMarker_Identifier] + call OnMY_MarkerRender_Pre + add esp, 4*1 + popad + + mov eax, FUNC_C3dMarker_Render + call eax + + pushad + call OnMY_MarkerRender_Post + popad + retn + } + // clang-format on +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CCoronas::Render +// +// Detect corona rendering +// +////////////////////////////////////////////////////////////////////////////////////////// +#define HOOKPOS_CCoronas_Render_Corona 0x6FB2E6 +#define HOOKSIZE_CCoronas_Render_Corona 5 +#define FUNC_CSprite_RenderOneXLUSprite_Rotate_Aspect 0x70D490 +#define OFFSET_CRegisteredCorona_EdiToIdentifier 0x2C // edi points at the corona + 0x38, the identifier is at + 0x0C +DWORD RETURN_CCoronas_Render_Corona = 0x6FB2EB; +static void __declspec(naked) HOOK_CCoronas_Render_Corona() +{ + MTA_VERIFY_HOOK_LOCAL_SIZE; + + // clang-format off + __asm + { + pushad + push dword ptr [edi-OFFSET_CRegisteredCorona_EdiToIdentifier] + call OnMY_MarkerRender_Pre + add esp, 4*1 + popad + + // Arguments are still on the stack + mov eax, FUNC_CSprite_RenderOneXLUSprite_Rotate_Aspect + call eax + + pushad + call OnMY_MarkerRender_Post + popad + jmp RETURN_CCoronas_Render_Corona + } + // clang-format on +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CHeli::RenderAllHeliSearchLights +// +// Detect the game's own helicopter searchlight cone rendering (id = heli + 0xB, also the corona identifier) +// +////////////////////////////////////////////////////////////////////////////////////////// +#define HOOKPOS_CHeli_RenderAllHeliSearchLights_Cone 0x6C7CE8 +#define HOOKSIZE_CHeli_RenderAllHeliSearchLights_Cone 5 +#define FUNC_CHeli_SearchLightCone 0x6C58E0 +DWORD RETURN_CHeli_RenderAllHeliSearchLights_Cone = 0x6C7CED; +static void __declspec(naked) HOOK_CHeli_RenderAllHeliSearchLights_Cone() +{ + MTA_VERIFY_HOOK_LOCAL_SIZE; + + // clang-format off + __asm + { + pushad + push dword ptr [esp+20h] + call OnMY_MarkerRender_Pre + add esp, 4*1 + popad + + // Arguments are still on the stack + mov eax, FUNC_CHeli_SearchLightCone + call eax + + pushad + call OnMY_MarkerRender_Post + popad + jmp RETURN_CHeli_RenderAllHeliSearchLights_Cone + } + // clang-format on +} + ////////////////////////////////////////////////////////////////////////////////////////// // // CMultiplayerSA::SetGameEntityRenderHandler @@ -744,6 +917,28 @@ void CMultiplayerSA::SetGameEntityRenderHandler(GameEntityRenderHandler* pHandle pGameEntityRenderHandler = pHandler; } +////////////////////////////////////////////////////////////////////////////////////////// +// +// CMultiplayerSA::SetMarkerRenderHandler +// +// +////////////////////////////////////////////////////////////////////////////////////////// +void CMultiplayerSA::SetMarkerRenderHandler(MarkerRenderHandler* pHandler) +{ + pMarkerRenderHandler = pHandler; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CMultiplayerSA::SetRadarBlipRenderHandler +// +// +////////////////////////////////////////////////////////////////////////////////////////// +void CMultiplayerSA::SetRadarBlipRenderHandler(RadarBlipRenderHandler* pHandler) +{ + pRadarBlipRenderHandler = pHandler; +} + ////////////////////////////////////////////////////////////////////////////////////////// // // CMultiplayerSA::SetPreRenderSkyHandler @@ -962,4 +1157,9 @@ void CMultiplayerSA::InitHooks_Rendering() EZHookInstallChecked(RwCameraSetNearClipPlane); EZHookInstall(RenderEffects_HeliLight); EZHookInstall(CRenderer_EverythingBarRoads); + HookInstallCall(HOOKPOS_CRadar_DrawBlips_CoordBlip, (DWORD)HOOK_CRadar_DrawCoordBlip); + HookInstallCall(HOOKPOS_CRadar_DrawBlips_CoordBlipWaypoint, (DWORD)HOOK_CRadar_DrawCoordBlip); + HookInstallCall(HOOKPOS_C3dMarkers_Render_Marker, (DWORD)HOOK_C3dMarkers_Render_Marker); + EZHookInstall(CCoronas_Render_Corona); + EZHookInstall(CHeli_RenderAllHeliSearchLights_Cone); } diff --git a/Client/sdk/game/CRadar.h b/Client/sdk/game/CRadar.h index 4a373c33442..fd5e860658c 100644 --- a/Client/sdk/game/CRadar.h +++ b/Client/sdk/game/CRadar.h @@ -21,5 +21,6 @@ class CRadar public: virtual CMarker* CreateMarker(CVector* vecPosition) = 0; virtual CMarker* GetFreeMarker() = 0; + virtual CMarker* GetMarker(int iIndex) = 0; virtual void DrawAreaOnRadar(float fX1, float fY1, float fX2, float fY2, const SharedUtil::SColor color) = 0; }; diff --git a/Client/sdk/game/CRenderWare.h b/Client/sdk/game/CRenderWare.h index 760fe299253..2db10c53849 100644 --- a/Client/sdk/game/CRenderWare.h +++ b/Client/sdk/game/CRenderWare.h @@ -107,6 +107,7 @@ class CRenderWare virtual void SetRenderingClientEntity(CClientEntityBase* pClientEntity, ushort usModelId, int iTypeMask) = 0; virtual SShaderItemLayers* GetAppliedShaderForD3DData(CD3DDUMMY* pD3DData) = 0; + virtual CD3DDUMMY* ResolveD3DData(CD3DDUMMY* pD3DData) = 0; virtual void AppendAdditiveMatch(CSHADERDUMMY* pShaderData, CClientEntityBase* pClientEntity, const char* strTextureNameMatch, float fShaderPriority, bool bShaderLayered, int iTypeMask, uint uiShaderCreateTime, bool bShaderUsesVertexShader, bool bAppendLayers) = 0; virtual void AppendSubtractiveMatch(CSHADERDUMMY* pShaderData, CClientEntityBase* pClientEntity, const char* strTextureNameMatch) = 0; diff --git a/Client/sdk/game/RenderWare.h b/Client/sdk/game/RenderWare.h index c0a4257b059..6ff56c98df0 100644 --- a/Client/sdk/game/RenderWare.h +++ b/Client/sdk/game/RenderWare.h @@ -173,6 +173,12 @@ enum RwRasterLockFlags RASTER_LOCK_READ = 2, RASTER_LOCK_LAST = RW_STRUCT_ALIGN }; +enum RwRasterFlags +{ + RASTER_TYPE_TEXTURE = 0x04, + RASTER_FORMAT_8888 = 0x0500, + RASTER_FLAGS_LAST = RW_STRUCT_ALIGN +}; enum RwTransformOrder { TRANSFORM_INITIAL = 0, diff --git a/Client/sdk/multiplayer/CMultiplayer.h b/Client/sdk/multiplayer/CMultiplayer.h index 28d2e074088..f0abe71fc87 100644 --- a/Client/sdk/multiplayer/CMultiplayer.h +++ b/Client/sdk/multiplayer/CMultiplayer.h @@ -132,6 +132,8 @@ typedef void(GameProjectileDestructHandler)(CEntitySAInterface* pProjectile); typedef void(GameModelRemoveHandler)(ushort usModelId); typedef void(GameRunNamedAnimDestructorHandler)(class CTaskSimpleRunNamedAnimSAInterface* pTask); typedef void(GameEntityRenderHandler)(CEntitySAInterface* pEntity); +typedef void(RadarBlipRenderHandler)(int iBlipIndex); +typedef void(MarkerRenderHandler)(unsigned long ulIdentifier); typedef void(FxSystemDestructionHandler)(void* pFxSA); typedef AnimationId(DrivebyAnimationHandler)(AnimationId animGroup, AssocGroupId animId); typedef void(PedStepHandler)(CPedSAInterface* pPed, bool bFoot); @@ -258,6 +260,8 @@ class CMultiplayer virtual void SetGameModelRemoveHandler(GameModelRemoveHandler* pHandler) = 0; virtual void SetGameRunNamedAnimDestructorHandler(GameRunNamedAnimDestructorHandler* pHandler) = 0; virtual void SetGameEntityRenderHandler(GameEntityRenderHandler* pHandler) = 0; + virtual void SetRadarBlipRenderHandler(RadarBlipRenderHandler* pHandler) = 0; + virtual void SetMarkerRenderHandler(MarkerRenderHandler* pHandler) = 0; virtual void SetFxSystemDestructionHandler(FxSystemDestructionHandler* pHandler) = 0; virtual void SetDrivebyAnimationHandler(DrivebyAnimationHandler* pHandler) = 0; virtual void SetPedStepHandler(PedStepHandler* pHandler) = 0;