From bbf5ee038658f7598cadd77a0de8a4fa849d491c Mon Sep 17 00:00:00 2001 From: ThirteenAG Date: Mon, 2 Dec 2024 20:26:37 +0800 Subject: [PATCH] gtade: add 3 and vc versions --- .github/workflows/main.yml | 14 + premake5.lua | 8 + readme.md | 14 +- release.bat | 2 + source/GTA3DE.XboxRainDroplets.cpp | 290 +++++++++++++++++ source/GTAVCDE.XboxRainDroplets.cpp | 298 ++++++++++++++++++ .../inis/GTA3DE.XboxRainDroplets.ini | 10 + .../inis/GTAVCDE.XboxRainDroplets.ini | 10 + 8 files changed, 645 insertions(+), 1 deletion(-) create mode 100644 source/GTA3DE.XboxRainDroplets.cpp create mode 100644 source/GTAVCDE.XboxRainDroplets.cpp create mode 100644 source/resources/inis/GTA3DE.XboxRainDroplets.ini create mode 100644 source/resources/inis/GTAVCDE.XboxRainDroplets.ini diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3ae7911..c85baab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -181,3 +181,17 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} tag: gtasade artifacts: bin/GTASADE.XboxRainDroplets.zip + - name: GTAVCDE.XboxRainDroplets + uses: ./.github/workflows/release_tag + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: gtavcde + artifacts: bin/GTAVCDE.XboxRainDroplets.zip + - name: GTA3DE.XboxRainDroplets + uses: ./.github/workflows/release_tag + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: gta3de + artifacts: bin/GTA3DE.XboxRainDroplets.zip diff --git a/premake5.lua b/premake5.lua index 957de3a..96364eb 100644 --- a/premake5.lua +++ b/premake5.lua @@ -125,6 +125,14 @@ project "GTASADE.XboxRainDroplets" architecture "x64" add_kananlib() setpaths("Z:/WFP/Games/Grand Theft Auto The Definitive Edition/GTA San Andreas - Definitive Edition/", "Gameface/Binaries/Win64/SanAndreas.exe", "Gameface/Binaries/Win64/scripts/") +project "GTAVCDE.XboxRainDroplets" + architecture "x64" + add_kananlib() + setpaths("Z:/WFP/Games/Grand Theft Auto The Definitive Edition/GTA Vice City - Definitive Edition/", "Gameface/Binaries/Win64/ViceCity.exe", "Gameface/Binaries/Win64/scripts/") +project "GTA3DE.XboxRainDroplets" + architecture "x64" + add_kananlib() + setpaths("Z:/WFP/Games/Grand Theft Auto The Definitive Edition/GTA III - Definitive Edition/", "Gameface/Binaries/Win64/LibertyCity.exe", "Gameface/Binaries/Win64/scripts/") --tests --project "GTA3.XboxRainDroplets" -- setpaths("Z:/WFP/Games/Grand Theft Auto/GTAIII/", "gta3.exe", "scripts/") diff --git a/readme.md b/readme.md index 205a91a..4bdba1a 100644 --- a/readme.md +++ b/readme.md @@ -96,7 +96,19 @@ Various ini settings allow to customize the appearance and can be edited while i #### Screen: -![scb](https://github.com/user-attachments/assets/af077a90-a75f-4e28-bca0-5ecd0b194ce3) +![gtasade](https://github.com/user-attachments/assets/af077a90-a75f-4e28-bca0-5ecd0b194ce3) + +## GTA Vice City The Definitive Edition - [Download Link](https://github.com/ThirteenAG/XboxRainDroplets/releases/tag/gtavcde) + +#### Screen: + +![gtavcde](https://github.com/user-attachments/assets/eabbf55d-0bcd-4b28-b7fd-225e2a094126) + +## GTA III The Definitive Edition - [Download Link](https://github.com/ThirteenAG/XboxRainDroplets/releases/tag/gta3de) + +#### Screen: + +![gta3de](https://github.com/user-attachments/assets/79c92148-a348-4cbd-964a-e40e13d3aede) ## Other diff --git a/release.bat b/release.bat index 5ab1aa3..91e21dd 100644 --- a/release.bat +++ b/release.bat @@ -15,6 +15,8 @@ Driv3r.XboxRainDroplets DriverParallelLines.XboxRainDroplets SplinterCellBlacklist.XboxRainDroplets GTASADE.XboxRainDroplets +GTAVCDE.XboxRainDroplets +GTA3DE.XboxRainDroplets ) do ( 7za a -tzip ".\bin\%%x.zip" ".\bin\%%x.asi" ".\bin\%%x.ini" ) diff --git a/source/GTA3DE.XboxRainDroplets.cpp b/source/GTA3DE.XboxRainDroplets.cpp new file mode 100644 index 0000000..9159981 --- /dev/null +++ b/source/GTA3DE.XboxRainDroplets.cpp @@ -0,0 +1,290 @@ +#include "xrd11.h" +#include +#include +#include + +#define FUSIONDXHOOK_INCLUDE_D3D12 1 +#define FUSIONDXHOOK_USE_SAFETYHOOK 0 +#include "FusionDxHook.h" + +namespace CWeather +{ + GameRef Rain; +} + +namespace CCutsceneMgr +{ + GameRef ms_running; +} + +namespace CTimer +{ + GameRef m_CodePause; + GameRef m_UserPause; +} + +namespace CCullZones +{ + GameRef CurrentFlags_Camera; + bool CamNoRain() + { + return (CurrentFlags_Camera & 8) != 0; + } + + GameRef CurrentFlags_Player; + bool PlayerNoRain() + { + return (CurrentFlags_Player & 8) != 0; + } +} + +struct CCamera +{ + char unk[0x8]; + RwMatrix m_mCameraMatrix; +}; + +GameRef TheCamera; +GameRef gGameState; + +bool NoDrops() +{ + return false; +} + +bool NoRain() +{ + return CCullZones::CamNoRain() || CCullZones::PlayerNoRain() || NoDrops(); +} + +void SprayDroplets(RwV3d* position, float pd = 20.0f, bool isBlood = false) +{ + RwV3d dist; + RwV3dSub(&dist, position, &WaterDrops::ms_lastPos); + float len = RwV3dLength(&dist); + if (len <= pd) + WaterDrops::FillScreenMoving((1.0f / (len / 2.0f)) * 100.0f, isBlood); +} + +thread_local std::string cachedPrt; +thread_local std::map prtMap; + +namespace FxManager_c +{ + SafetyHookInline shCreateFxSystem{}; + void CreateFxSystem(void* a1, void* name, int a3, int id) + { + shCreateFxSystem.fastcall(a1, name, a3, id); + cachedPrt = *(const char**)name; // FString + } +} + +bool bGotoAddParticle = false; +namespace UParticleSystemComponent +{ + SafetyHookInline shResetParticles{}; + void* ResetParticles(void* UParticleSystemComponent, void* a2, void* a3, void* a4) + { + auto res = shResetParticles.fastcall(UParticleSystemComponent, a2, a3, a4); + prtMap[res] = std::move(cachedPrt); + + bGotoAddParticle = false; + RwV3d* position = (RwV3d*)a3; + if (position->x == 0.0f && position->y == 0.0f && position->z == 0.0f) + { + bGotoAddParticle = true; + } + else + { + std::string_view name = prtMap[res]; + if (name == "water_swim") + { + SprayDroplets((RwV3d*)a3, 10.0f, false); + } + if (name == "water_splsh_sml") + { + SprayDroplets((RwV3d*)a3, 10.0f, false); + } + else if (name == "water_splash") + { + SprayDroplets((RwV3d*)a3, 20.0f, false); + } + else if (name == "water_splash_big") + { + SprayDroplets((RwV3d*)a3, 30.0f, false); + } + else if (name == "water_hydrant") + { + WaterDrops::RegisterSplash((RwV3d*)a3, 10.0f, 600, 100.0f); + } + } + + return res; + } +} + +namespace FxSystem_c +{ + SafetyHookInline shAddParticle{}; + void AddParticle(void* a1, RwV3d* position, void* a3, float a4, int* a5) + { + //if (bGotoAddParticle) + { + std::string_view name = prtMap[a1]; + + if (name == "prt_blood" || name == "prt_blood_Fountain") + { + SprayDroplets(position, 3.0f, true); + } + else if (name == "boat_prop") + { + SprayDroplets(position, 20.0f, false); + } + else if (name == "prt_watersplash") + { + SprayDroplets(position, 20.0f, false); + } + else if (name == "prt_watercannon_splash") + { + SprayDroplets(position, 7.0f, false); + } + } + + return shAddParticle.fastcall(a1, position, a3, a4, a5); + } +} + +uintptr_t ResolveDisplacement(hook::pattern& pattern, ptrdiff_t offset = 0) +{ + return utility::resolve_displacement((uintptr_t)pattern.get_first(offset)).value_or(0); +} + +void Init() +{ + FusionDxHook::Init(); // For DX12 compat, no hooks applied + + WaterDrops::ReadIniSettings(); + static DXGI_FORMAT gFormat = DXGI_FORMAT_R10G10B10A2_UNORM; + + auto pattern = hook::pattern("40 38 2D ? ? ? ? 0F 85 ? ? ? ? 40 38 2D ? ? ? ? 0F 85"); + CTimer::m_CodePause.SetAddress(ResolveDisplacement(pattern)); + CTimer::m_UserPause.SetAddress(ResolveDisplacement(pattern, 13)); + + pattern = hook::pattern("F3 0F 5C 3D ? ? ? ? 0F 28 C7"); + CWeather::Rain.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("0F B6 15 ? ? ? ? F3 44 0F 10 0D"); + CCutsceneMgr::ms_running.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("F6 05 ? ? ? ? ? 74 ? F6 05 ? ? ? ? ? 0F 85 ? ? ? ? 48 8B 05"); + CCullZones::CurrentFlags_Camera.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("F6 05 ? ? ? ? ? 0F 85 ? ? ? ? 48 8B 05"); + CCullZones::CurrentFlags_Player.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("8B 05 ? ? ? ? 83 F8 09"); + gGameState.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("48 8D 0D ? ? ? ? E8 ? ? ? ? 0F B6 05 ? ? ? ? 48 69 C8 78 01 00 00 4A 8B 8C 21"); + TheCamera.SetAddress(ResolveDisplacement(pattern)); + + static auto PresentHook = [](IDXGISwapChain* pSwapChain) + { + if (gGameState < 9) + return; + + Sire::Init(Sire::SIRE_RENDERER_DX11, pSwapChain); + Sire::SetTextureFormat(Sire::GetCurrentRenderer(), gFormat); + + WaterDrops::right = TheCamera.get().m_mCameraMatrix.right; + WaterDrops::up = TheCamera.get().m_mCameraMatrix.up; + WaterDrops::at = TheCamera.get().m_mCameraMatrix.at; + WaterDrops::pos = TheCamera.get().m_mCameraMatrix.pos; + + //when you put camera underwater, droplets disappear instantly instead of fading out + if (NoDrops()) { + WaterDrops::Clear(); + return; + } + + if (NoRain()) + WaterDrops::ms_rainIntensity = 0.0f; + else + WaterDrops::ms_rainIntensity = CWeather::Rain; + + if (!CTimer::m_CodePause && !CTimer::m_UserPause) + { + WaterDrops::Process(); + + if (WaterDrops::ms_numDrops > 0 && !CCutsceneMgr::ms_running) + { + WaterDrops::Render(); + } + } + }; + + pattern = hook::pattern("48 8B 4B ? 48 8B 01 FF 50 ? 8B F0"); + static auto FD3D11ViewportPresentCheckedHook = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + auto pSwapChain = *(IDXGISwapChain**)(regs.rbx + 0x78); + PresentHook(pSwapChain); + }); + + pattern = hook::pattern("48 8B 01 44 8B C3 8B D5"); + static auto FD3D12ViewportPresentCheckedHook = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + auto pSwapChain = (IDXGISwapChain*)(regs.rcx); + Sire::SetCommandQueue(FusionDxHook::D3D12::GetCommandQueueFromSwapChain(pSwapChain)); + PresentHook(pSwapChain); + }); + + pattern = hook::pattern("89 44 24 ? 41 FF 52 ? BA 00 00 00 80"); + static auto onBeforeResizeHookD3D11 = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + //IDXGISwapChain* pSwapChain = (IDXGISwapChain*)regs.rcx; + //UINT Width = regs.r8; + //UINT Height = regs.r9; + gFormat = (DXGI_FORMAT)regs.rax; + + WaterDrops::Reset(); + Sire::Shutdown(); + }); + + pattern = hook::pattern("44 89 74 24 ? 89 44 24 ? 41 FF 52"); + static auto onBeforeResizeHookD3D12 = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + //IDXGISwapChain* pSwapChain = (IDXGISwapChain*)regs.rcx; + //UINT Width = regs.r8; + //UINT Height = regs.r9; + gFormat = (DXGI_FORMAT)regs.rax; + + WaterDrops::Reset(); + Sire::Shutdown(); + }); + + pattern = hook::pattern("E8 ? ? ? ? 49 8B 07 49 8B CF 48 8B 95"); + FxManager_c::shCreateFxSystem = safetyhook::create_inline(ResolveDisplacement(pattern), FxManager_c::CreateFxSystem); + + pattern = hook::pattern("E8 ? ? ? ? 48 8B D8 48 85 C0 74 ? 48 8B C8 E8 ? ? ? ? 48 8B 53"); + UParticleSystemComponent::shResetParticles = safetyhook::create_inline(ResolveDisplacement(pattern), UParticleSystemComponent::ResetParticles); + + pattern = hook::pattern("E8 ? ? ? ? FF C3 3B DF 0F 8C ? ? ? ? 44 0F 28 BC 24"); + FxSystem_c::shAddParticle = safetyhook::create_inline(ResolveDisplacement(pattern), FxSystem_c::AddParticle); +} + +extern "C" __declspec(dllexport) void InitializeASI() +{ + std::call_once(CallbackHandler::flag, []() + { + CallbackHandler::RegisterCallback(Init); + }); +} + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/) +{ + if (reason == DLL_PROCESS_ATTACH) + { + if (!IsUALPresent()) { InitializeASI(); } + } + return TRUE; +} \ No newline at end of file diff --git a/source/GTAVCDE.XboxRainDroplets.cpp b/source/GTAVCDE.XboxRainDroplets.cpp new file mode 100644 index 0000000..fafc05a --- /dev/null +++ b/source/GTAVCDE.XboxRainDroplets.cpp @@ -0,0 +1,298 @@ +#include "xrd11.h" +#include +#include +#include + +#define FUSIONDXHOOK_INCLUDE_D3D12 1 +#define FUSIONDXHOOK_USE_SAFETYHOOK 0 +#include "FusionDxHook.h" + +namespace CWeather +{ + GameRef Rain; +} + +namespace CCutsceneMgr +{ + GameRef ms_running; +} + +namespace CGame +{ + GameRef currArea; +} + +namespace CTimer +{ + GameRef m_CodePause; + GameRef m_UserPause; +} + +namespace CCullZones +{ + GameRef CurrentFlags_Camera; + bool CamNoRain() + { + return (CurrentFlags_Camera & 8) != 0; + } + + GameRef CurrentFlags_Player; + bool PlayerNoRain() + { + return (CurrentFlags_Player & 8) != 0; + } +} + +struct CCamera +{ + char unk[0x860]; + RwMatrix m_mCameraMatrix; +}; + +GameRef TheCamera; +GameRef gGameState; + +bool NoDrops() +{ + return false; +} + +bool NoRain() +{ + return CCullZones::CamNoRain() || CCullZones::PlayerNoRain() || CGame::currArea != 0 || NoDrops(); +} + +void SprayDroplets(RwV3d* position, float pd = 20.0f, bool isBlood = false) +{ + RwV3d dist; + RwV3dSub(&dist, position, &WaterDrops::ms_lastPos); + float len = RwV3dLength(&dist); + if (len <= pd) + WaterDrops::FillScreenMoving((1.0f / (len / 2.0f)) * 100.0f, isBlood); +} + +thread_local std::string cachedPrt; +thread_local std::map prtMap; + +namespace FxManager_c +{ + SafetyHookInline shCreateFxSystem{}; + void CreateFxSystem(void* a1, void* name, int a3, int id) + { + shCreateFxSystem.fastcall(a1, name, a3, id); + cachedPrt = *(const char**)name; // FString + } +} + +bool bGotoAddParticle = false; +namespace UParticleSystemComponent +{ + SafetyHookInline shResetParticles{}; + void* ResetParticles(void* UParticleSystemComponent, void* a2, void* a3, void* a4) + { + auto res = shResetParticles.fastcall(UParticleSystemComponent, a2, a3, a4); + prtMap[res] = std::move(cachedPrt); + + bGotoAddParticle = false; + RwV3d* position = (RwV3d*)a3; + if (position->x == 0.0f && position->y == 0.0f && position->z == 0.0f) + { + bGotoAddParticle = true; + } + else + { + std::string_view name = prtMap[res]; + if (name == "water_swim") + { + SprayDroplets((RwV3d*)a3, 10.0f, false); + } + if (name == "water_splsh_sml") + { + SprayDroplets((RwV3d*)a3, 10.0f, false); + } + else if (name == "water_splash") + { + SprayDroplets((RwV3d*)a3, 20.0f, false); + } + else if (name == "water_splash_big") + { + SprayDroplets((RwV3d*)a3, 30.0f, false); + } + else if (name == "water_hydrant") + { + WaterDrops::RegisterSplash((RwV3d*)a3, 10.0f, 600, 100.0f); + } + } + + return res; + } +} + +namespace FxSystem_c +{ + SafetyHookInline shAddParticle{}; + void AddParticle(void* a1, RwV3d* position, void* a3, float a4, int* a5) + { + //if (bGotoAddParticle) + { + std::string_view name = prtMap[a1]; + + if (name == "prt_blood" || name == "prt_blood_Fountain") + { + SprayDroplets(position, 3.0f, true); + } + else if (name == "boat_prop") + { + SprayDroplets(position, 20.0f, false); + } + else if (name == "prt_watersplash") + { + SprayDroplets(position, 20.0f, false); + } + else if (name == "prt_watercannon_splash") + { + SprayDroplets(position, 7.0f, false); + } + } + + return shAddParticle.fastcall(a1, position, a3, a4, a5); + } +} + +uintptr_t ResolveDisplacement(hook::pattern& pattern, ptrdiff_t offset = 0) +{ + return utility::resolve_displacement((uintptr_t)pattern.get_first(offset)).value_or(0); +} + +void Init() +{ + FusionDxHook::Init(); // For DX12 compat, no hooks applied + + WaterDrops::ReadIniSettings(); + static DXGI_FORMAT gFormat = DXGI_FORMAT_R10G10B10A2_UNORM; + + auto pattern = hook::pattern("44 38 3D ? ? ? ? 0F 85 ? ? ? ? 44 38 3D ? ? ? ? 0F 85 ? ? ? ? 44 38 3D"); + CTimer::m_CodePause.SetAddress(ResolveDisplacement(pattern)); + CTimer::m_UserPause.SetAddress(ResolveDisplacement(pattern, 13)); + + pattern = hook::pattern("F3 0F 10 0D ? ? ? ? 0F 2F 0D ? ? ? ? 0F 86"); + CWeather::Rain.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("44 88 2D ? ? ? ? 48 89 88"); + CCutsceneMgr::ms_running.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("8B 05 ? ? ? ? 85 C0 74 ? 83 F8 0D"); + CGame::currArea.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("F6 05 ? ? ? ? ? 75 ? F6 05 ? ? ? ? ? 0F 84"); + CCullZones::CurrentFlags_Camera.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("F6 05 ? ? ? ? ? 0F 84 ? ? ? ? 41 B9 04 00 00 00"); + CCullZones::CurrentFlags_Player.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("83 3D ? ? ? ? ? 0F 84 ? ? ? ? 48 8B 0D ? ? ? ? 83 B9"); + gGameState.SetAddress(ResolveDisplacement(pattern)); + + pattern = hook::pattern("48 8D 35 ? ? ? ? F3 0F 58 C8"); + TheCamera.SetAddress(ResolveDisplacement(pattern)); + + static auto PresentHook = [](IDXGISwapChain* pSwapChain) + { + if (gGameState < 9) + return; + + Sire::Init(Sire::SIRE_RENDERER_DX11, pSwapChain); + Sire::SetTextureFormat(Sire::GetCurrentRenderer(), gFormat); + + WaterDrops::right = TheCamera.get().m_mCameraMatrix.right; + WaterDrops::up = TheCamera.get().m_mCameraMatrix.up; + WaterDrops::at = TheCamera.get().m_mCameraMatrix.at; + WaterDrops::pos = TheCamera.get().m_mCameraMatrix.pos; + + //when you put camera underwater, droplets disappear instantly instead of fading out + if (NoDrops()) { + WaterDrops::Clear(); + return; + } + + if (NoRain()) + WaterDrops::ms_rainIntensity = 0.0f; + else + WaterDrops::ms_rainIntensity = CWeather::Rain; + + if (!CTimer::m_CodePause && !CTimer::m_UserPause) + { + WaterDrops::Process(); + + if (WaterDrops::ms_numDrops > 0 && !CCutsceneMgr::ms_running) + { + WaterDrops::Render(); + } + } + }; + + pattern = hook::pattern("48 8B 4B ? 48 8B 01 FF 50 ? 8B F0"); + static auto FD3D11ViewportPresentCheckedHook = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + auto pSwapChain = *(IDXGISwapChain**)(regs.rbx + 0x78); + PresentHook(pSwapChain); + }); + + pattern = hook::pattern("48 8B 01 44 8B C3 8B D5"); + static auto FD3D12ViewportPresentCheckedHook = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + auto pSwapChain = (IDXGISwapChain*)(regs.rcx); + Sire::SetCommandQueue(FusionDxHook::D3D12::GetCommandQueueFromSwapChain(pSwapChain)); + PresentHook(pSwapChain); + }); + + pattern = hook::pattern("89 44 24 ? 41 FF 52 ? BA 00 00 00 80"); + static auto onBeforeResizeHookD3D11 = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + //IDXGISwapChain* pSwapChain = (IDXGISwapChain*)regs.rcx; + //UINT Width = regs.r8; + //UINT Height = regs.r9; + gFormat = (DXGI_FORMAT)regs.rax; + + WaterDrops::Reset(); + Sire::Shutdown(); + }); + + pattern = hook::pattern("44 89 74 24 ? 89 44 24 ? 41 FF 52"); + static auto onBeforeResizeHookD3D12 = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) + { + //IDXGISwapChain* pSwapChain = (IDXGISwapChain*)regs.rcx; + //UINT Width = regs.r8; + //UINT Height = regs.r9; + gFormat = (DXGI_FORMAT)regs.rax; + + WaterDrops::Reset(); + Sire::Shutdown(); + }); + + pattern = hook::pattern("E8 ? ? ? ? 49 8B 07 49 8B CF 48 8B 95"); + FxManager_c::shCreateFxSystem = safetyhook::create_inline(ResolveDisplacement(pattern), FxManager_c::CreateFxSystem); + + pattern = hook::pattern("E8 ? ? ? ? 48 8B D8 48 85 C0 74 ? 48 8B C8 E8 ? ? ? ? 48 8B 53"); + UParticleSystemComponent::shResetParticles = safetyhook::create_inline(ResolveDisplacement(pattern), UParticleSystemComponent::ResetParticles); + + pattern = hook::pattern("E8 ? ? ? ? FF C3 3B DF 0F 8C ? ? ? ? 44 0F 28 BC 24"); + FxSystem_c::shAddParticle = safetyhook::create_inline(ResolveDisplacement(pattern), FxSystem_c::AddParticle); +} + +extern "C" __declspec(dllexport) void InitializeASI() +{ + std::call_once(CallbackHandler::flag, []() + { + CallbackHandler::RegisterCallback(Init); + }); +} + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/) +{ + if (reason == DLL_PROCESS_ATTACH) + { + if (!IsUALPresent()) { InitializeASI(); } + } + return TRUE; +} \ No newline at end of file diff --git a/source/resources/inis/GTA3DE.XboxRainDroplets.ini b/source/resources/inis/GTA3DE.XboxRainDroplets.ini new file mode 100644 index 0000000..a45607c --- /dev/null +++ b/source/resources/inis/GTA3DE.XboxRainDroplets.ini @@ -0,0 +1,10 @@ +[MAIN] +MinSize = 4 +MaxSize = 20 +MaxDrops = 600 +MaxMovingDrops = 2000 +RadialMovement = 1 +EnableGravity = 1 +SpeedAdjuster = 1.0 +MoveStep = 0.1 +BloodDrops = 1 diff --git a/source/resources/inis/GTAVCDE.XboxRainDroplets.ini b/source/resources/inis/GTAVCDE.XboxRainDroplets.ini new file mode 100644 index 0000000..a45607c --- /dev/null +++ b/source/resources/inis/GTAVCDE.XboxRainDroplets.ini @@ -0,0 +1,10 @@ +[MAIN] +MinSize = 4 +MaxSize = 20 +MaxDrops = 600 +MaxMovingDrops = 2000 +RadialMovement = 1 +EnableGravity = 1 +SpeedAdjuster = 1.0 +MoveStep = 0.1 +BloodDrops = 1