diff --git a/Client/game_sa/CFxSA.cpp b/Client/game_sa/CFxSA.cpp index e19d0db52e3..508347dfe0e 100644 --- a/Client/game_sa/CFxSA.cpp +++ b/Client/game_sa/CFxSA.cpp @@ -10,8 +10,16 @@ *****************************************************************************/ #include "StdInc.h" +#include "game/Common.h" +#include "game/RenderWare.h" +#include "CVector2D.h" #include "CFxSA.h" #include "CEntitySA.h" +; +using StoreShadowToBeRendered_t = int(__cdecl*)(eShadowType, RwTexture*, const CVector*, float, float, float, float, short, unsigned char, unsigned char, + unsigned char, float, bool, float, class CRealTimeShadow*, bool); +inline auto StoreShadowToBeRendered = reinterpret_cast(FUNC_FXSystem_StoreShadows); +inline unsigned short& CShadows_ShadowsStoredToBeRendered = *(unsigned short*)VAR_FXSystem_StoreShadows; void CFxSA::AddBlood(CVector& vecPosition, CVector& vecDirection, int iCount, float fBrightness) { @@ -329,3 +337,25 @@ void CFxSA::AddParticle(FxParticleSystems eFxParticle, const CVector& vecPositio ((int(__thiscall*)(FxSystem_c*, const CVector*, const CVector*, float, FxPrtMult_c*, float, float, float, int))FUNC_FXSystem_c_AddParticle)(fxParticleSystem, &vecPosition, &newDirection, 0, &fxPrt, -1.0f, fBrightness, 0, 0); } } + +bool CFxSA::IsShadowsLimitReached() +{ + // GTA:SA can handle max 48 shadows per frame + return CShadows_ShadowsStoredToBeRendered >= 48; +} + +bool CFxSA::AddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2, SColor color, + eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings) +{ + // Check if we can add more shadows this frame + if (IsShadowsLimitReached() || shadowTextureType >= eShadowTextureType::COUNT) + return false; + + // Get the RwTexture for the shadow + void* textureAddress = *(void**)(TEXTURE_FXSystem_Shadow + (int)shadowTextureType * 4); + RwTexture* pRwTexture = reinterpret_cast(textureAddress); + + // Store the shadow to be rendered + return StoreShadowToBeRendered(shadowType, pRwTexture, &vecPosition, vecOffset1.fX, vecOffset1.fY, vecOffset2.fX, vecOffset2.fY, color.A, color.R, color.G, + color.B, fZDistance, bDrawOnWater, 1, 0, bDrawOnBuildings); +} diff --git a/Client/game_sa/CFxSA.h b/Client/game_sa/CFxSA.h index efb7d0403f8..db5d107cd93 100644 --- a/Client/game_sa/CFxSA.h +++ b/Client/game_sa/CFxSA.h @@ -12,6 +12,7 @@ #pragma once #include +#include struct RwColor; class FxSystem_c; @@ -31,7 +32,9 @@ class FxSystem_c; #define FUNC_CFx_TriggerBulletSplash 0x4a10e0 #define FUNC_CFx_TriggerFootSplash 0x4a1150 #define FUNC_FXSystem_c_AddParticle 0x4AA440 - +#define FUNC_FXSystem_StoreShadows 0x707390 +#define VAR_FXSystem_StoreShadows 0xC403DC +#define TEXTURE_FXSystem_Shadow 0xC403E0 class CFxSAInterface { public: @@ -77,6 +80,9 @@ class CFxSA : public CFx void TriggerBulletSplash(CVector& vecPosition); void TriggerFootSplash(CVector& vecPosition); void AddParticle(FxParticleSystems eFxParticle, const CVector& vecPosition, const CVector& vecDirection, float fR, float fG, float fB, float fA, bool bRandomizeColors, std::uint32_t iCount, float fBrightness, float fSize, bool bRandomizeSizes, float fLife); + bool AddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2, SColor color, + eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings); + static bool IsShadowsLimitReached(); private: CFxSAInterface* m_pInterface; diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 0b3fae16e93..3abbb9bf4ab 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -8076,6 +8076,13 @@ bool CStaticFunctionDefinitions::FxCreateParticle(FxParticleSystems eFxParticle, return true; } +bool CStaticFunctionDefinitions::FxAddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, + const CVector2D& vecOffset2, SColor color, eShadowType shadowType, float fZDistance, bool bDrawOnWater, + bool bDrawOnBuildings) +{ + return g_pGame->GetFx()->AddShadow(shadowTextureType, vecPosition, vecOffset1, vecOffset2, color, shadowType, fZDistance, bDrawOnWater, bDrawOnBuildings); +} + CClientEffect* CStaticFunctionDefinitions::CreateEffect(CResource& Resource, const SString& strFxName, const CVector& vecPosition, bool bSoundEnable) { CClientEffect* pFx = m_pManager->GetEffectManager()->Create(strFxName, vecPosition, INVALID_ELEMENT_ID, bSoundEnable); diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h index e1f05f1d108..c4b4a1b622d 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -740,6 +740,8 @@ class CStaticFunctionDefinitions static bool FxAddBulletSplash(CVector& vecPosition); static bool FxAddFootSplash(CVector& vecPosition); static bool FxCreateParticle(FxParticleSystems eFxParticle, CVector& vecPosition, CVector& vecDirection, float fR, float fG, float fB, float fA, bool bRandomizeColors, std::uint32_t iCount, float fBrightness, float fSize, bool bRandomizeSizes, float fLife); + static bool FxAddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2, + SColor color, eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings); static CClientEffect* CreateEffect(CResource& Resource, const SString& strFxName, const CVector& vecPosition, bool bSoundEnable); // Sound funcs diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index c7eee93f848..2e98b535da9 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -860,6 +860,27 @@ ADD_ENUM(_D3DFORMAT::D3DFMT_G32R32F, "g32r32f") ADD_ENUM(_D3DFORMAT::D3DFMT_A32B32G32R32F, "a32b32g32r32f") IMPLEMENT_ENUM_CLASS_END("surface-format") +IMPLEMENT_ENUM_CLASS_BEGIN(eShadowTextureType) +ADD_ENUM(eShadowTextureType::CAR, "car") +ADD_ENUM(eShadowTextureType::PED, "ped") +ADD_ENUM(eShadowTextureType::HELI, "heli") +ADD_ENUM(eShadowTextureType::BIKE, "bike") +ADD_ENUM(eShadowTextureType::RCBARON, "rcbaron") +ADD_ENUM(eShadowTextureType::EXPLOSION, "explosion") +ADD_ENUM(eShadowTextureType::HEADLIGHT1, "headlight1") +ADD_ENUM(eShadowTextureType::HEADLIGHT2, "headlight2") +ADD_ENUM(eShadowTextureType::BLOOD, "blood") +ADD_ENUM(eShadowTextureType::HANDMAN, "handman") +ADD_ENUM(eShadowTextureType::WINCRACK, "wincrack") +ADD_ENUM(eShadowTextureType::LAMP, "lamp") +IMPLEMENT_ENUM_CLASS_END("shadow-texture-type") + +IMPLEMENT_ENUM_CLASS_BEGIN(eShadowType) +ADD_ENUM(eShadowType::DEFAULT, "default") +ADD_ENUM(eShadowType::ADDITIVE, "additive") +ADD_ENUM(eShadowType::INVCOLOR, "invcolor") +IMPLEMENT_ENUM_CLASS_END("shadow-type") + IMPLEMENT_ENUM_CLASS_BEGIN(eRenderStage) ADD_ENUM(eRenderStage::PRE_FX, "prefx") ADD_ENUM(eRenderStage::POST_FX, "postfx") diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index 399cdbf2101..a9e16c5f49d 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "enums/VehicleComponent.h" @@ -94,6 +95,8 @@ DECLARE_ENUM_CLASS(eModelIdeFlag); DECLARE_ENUM_CLASS(_D3DFORMAT); DECLARE_ENUM_CLASS(eRenderStage); DECLARE_ENUM_CLASS(FxParticleSystems); +DECLARE_ENUM_CLASS(eShadowTextureType); +DECLARE_ENUM_CLASS(eShadowType); DECLARE_ENUM(ePools); DECLARE_ENUM_CLASS(WorldProperty); DECLARE_ENUM_CLASS(eModelLoadState); @@ -522,6 +525,14 @@ inline SString GetClassTypeName(WorldProperty*) { return "world-property"; } +inline SString GetClassTypeName(eShadowTextureType*) +{ + return "shadow-texture-type"; +} +inline SString GetClassTypeName(eShadowType*) +{ + return "shadow-type"; +} inline SString GetClassTypeName(CClientVectorGraphic*) { diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.cpp index 2112aaf4394..96e3f524806 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.cpp @@ -11,6 +11,7 @@ #include "StdInc.h" #include "lua/CLuaFunctionParser.h" +#include void CLuaEffectDefs::LoadFunctions() { @@ -35,6 +36,7 @@ void CLuaEffectDefs::LoadFunctions() {"setEffectDensity", SetEffectDensity}, {"getEffectDensity", GetEffectDensity}, {"fxCreateParticle", ArgumentParser}, + {"fxAddShadow", ArgumentParser}, }; // Add functions @@ -62,6 +64,7 @@ void CLuaEffectDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "addWaterSplash", "fxAddWaterSplash"); lua_classfunction(luaVM, "addWood", "fxAddWood"); lua_classfunction(luaVM, "createParticle", "fxCreateParticle"); + lua_classfunction(luaVM, "createShadow", "fxAddShadow"); lua_classfunction(luaVM, "setDensity", "setEffectDensity"); lua_classfunction(luaVM, "setSpeed", "setEffectSpeed"); @@ -647,3 +650,23 @@ bool CLuaEffectDefs::FxCreateParticle(FxParticleSystems eParticleSystem, CVector { return CStaticFunctionDefinitions::FxCreateParticle(eParticleSystem, vecPosition, vecDirection, fR/255, fG/255, fB/255, fA/255, bRandomizeColors.value_or(false), iCount.value_or(1), fBrightness.value_or(1.0f), fSize.value_or(0.3f), bRandomizeSizes.value_or(false), fLife.value_or(1.0f)); } + +bool CLuaEffectDefs::FxAddShadow(eShadowTextureType shadowTextureType, CVector vecPosition, CVector2D vecOffset1, CVector2D vecOffset2, SColor color, + eShadowType shadowType, + float zDistance, bool bDrawOnWater, bool bDrawOnBuildings) +{ + if (vecOffset1.Length() > 32) + { + throw std::invalid_argument("First offset can not be longer than 32 units"); + } + else if (vecOffset2.Length() > 32) // bigger and close to limit shadows size can be partially invisible + { + throw std::invalid_argument("Second offset can not be longer than 32 units"); + } + else if (zDistance < 0 || zDistance > 3000) // negative distance not working + { + throw std::invalid_argument("Z Distance must be between 0.0 and 3000.0"); + } + return CStaticFunctionDefinitions::FxAddShadow(shadowTextureType, vecPosition, vecOffset1, vecOffset2, color, shadowType, zDistance, bDrawOnWater, + bDrawOnBuildings); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.h index d45107069cb..2228e0e643f 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.h @@ -38,5 +38,9 @@ class CLuaEffectDefs : public CLuaDefs LUA_DECLARE(SetEffectDensity); LUA_DECLARE(GetEffectDensity); - static bool FxCreateParticle(FxParticleSystems eParticleSystem, CVector vecPosition, CVector vecDirection, float fR, float fG, float fB, float fA, std::optional bRandomizeColors, std::optional iCount, std::optional fBrightness, std::optional fSize, std::optional bRandomizeSizes, std::optional fLife); + static bool FxCreateParticle(FxParticleSystems eParticleSystem, CVector vecPosition, CVector vecDirection, float fR, float fG, float fB, float fA, + std::optional bRandomizeColors, std::optional iCount, std::optional fBrightness, + std::optional fSize, std::optional bRandomizeSizes, std::optional fLife); + static bool FxAddShadow(eShadowTextureType shadowTextureType, CVector vecPosition, CVector2D vecOffset1, CVector2D vecOffset2, SColor color, + eShadowType shadowType, float zDistance, bool bDrawOnWater, bool bDrawOnBuildings); }; diff --git a/Client/sdk/game/CFx.h b/Client/sdk/game/CFx.h index d575f28b835..247fa4585cd 100644 --- a/Client/sdk/game/CFx.h +++ b/Client/sdk/game/CFx.h @@ -11,12 +11,40 @@ #pragma once #include "enums/FxParticleSystems.h" +#include class CEntity; class CVector; +class CVector2D; class CVehicle; struct RwColor; +enum class eShadowTextureType +{ + CAR, + PED, + HELI, + BIKE, + RCBARON, + EXPLOSION, + HEADLIGHT1, + HEADLIGHT2, + BLOOD, + HANDMAN, + WINCRACK, + LAMP, + COUNT +}; + +enum class eShadowType +{ + NONE, + DEFAULT, + ADDITIVE, + INVCOLOR, +}; + + class CFx { public: @@ -36,4 +64,6 @@ class CFx virtual void TriggerBulletSplash(CVector& vecPosition) = 0; virtual void TriggerFootSplash(CVector& vecPosition) = 0; virtual void AddParticle(FxParticleSystems eFxParticle, const CVector& vecPosition, const CVector& vecDirection, float fR, float fG, float fB, float fA, bool bRandomizeColors, std::uint32_t iCount, float fBrightness, float fSize, bool bRandomizeSizes, float fLife) = 0; + virtual bool AddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2, + SharedUtil::SColor color, eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings) = 0; };