From fc87562498ffef20ab8811ceccac199a8aa1a7ed Mon Sep 17 00:00:00 2001 From: FileEX Date: Mon, 16 Sep 2024 01:50:06 +0200 Subject: [PATCH 01/17] Initial commit --- Client/game_sa/C2DEffectSAInterface.h | 219 +++++++++++++++ Client/game_sa/CModelInfoSA.cpp | 31 ++- Client/game_sa/CModelInfoSA.h | 8 +- Client/mods/deathmatch/logic/CClient2DFX.cpp | 203 ++++++++++++++ Client/mods/deathmatch/logic/CClient2DFX.h | 33 +++ .../deathmatch/logic/CClient2DFXManager.cpp | 260 ++++++++++++++++++ .../deathmatch/logic/CClient2DFXManager.h | 36 +++ Client/mods/deathmatch/logic/CClientEntity.h | 2 + .../mods/deathmatch/logic/CClientManager.cpp | 4 + Client/mods/deathmatch/logic/CClientManager.h | 3 + Client/mods/deathmatch/logic/CResource.cpp | 10 + Client/mods/deathmatch/logic/CResource.h | 2 + .../logic/lua/CLuaFunctionParseHelpers.cpp | 65 +++++ .../logic/lua/CLuaFunctionParseHelpers.h | 47 ++++ .../deathmatch/logic/luadefs/CLuaDefs.cpp | 2 + .../mods/deathmatch/logic/luadefs/CLuaDefs.h | 1 + .../logic/luadefs/CLuaEngineDefs.cpp | 23 +- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 2 + Client/sdk/game/CModelInfo.h | 51 ++++ 19 files changed, 996 insertions(+), 6 deletions(-) create mode 100644 Client/game_sa/C2DEffectSAInterface.h create mode 100644 Client/mods/deathmatch/logic/CClient2DFX.cpp create mode 100644 Client/mods/deathmatch/logic/CClient2DFX.h create mode 100644 Client/mods/deathmatch/logic/CClient2DFXManager.cpp create mode 100644 Client/mods/deathmatch/logic/CClient2DFXManager.h diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h new file mode 100644 index 0000000000..db6a68a87a --- /dev/null +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -0,0 +1,219 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/C2DEffectSAInterface.h + * PURPOSE: Header file for 2dfx game interface layer + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ +#pragma once + +#include "game/RenderWare.h" +#include "game/CModelInfo.h" + +#define ARRAY_2DFXInfoStore 0xB4C2D8 // C2dfxInfoStore d2fxModels + +struct t2dEffectLight +{ + RwColor color; + float coronaFarClip; + float pointLightRange; + float coronaSize; + float shadowSize; + + // Flags + union + { + struct + { + // Flags 1 + std::uint16_t checkObstacles : 1; + std::uint16_t fogType : 1; + std::uint16_t withoutCorona : 1; + std::uint16_t onlyLongDistance : 1; + std::uint16_t atDay : 1; + std::uint16_t atNight : 1; + std::uint16_t blinking1 : 1; + + // Flags 2 + std::uint16_t onlyFromBelow : 1; + std::uint16_t blinking2 : 1; + std::uint16_t updateHeightAboveGround : 1; + std::uint16_t checkDirection : 1; + std::uint16_t blinking3 : 1; + }; + + std::uint16_t flags; + }; + + e2dCoronaFlashType coronaFlashType; // show mode + bool coronaEnableReflection; + std::uint8_t coronaFlareType; // lens flare effect 0 - off, 1 - on + std::uint8_t shadowColorMultiplier; + std::int8_t shadowZDistance; + std::int8_t offsetX; + std::int8_t offsetY; + std::int8_t offsetZ; + std::uint8_t field_1E[2]; + RwTexture* coronaTex; + RwTexture* shadowTex; + std::int32_t field_28; + std::int32_t field_2C; +}; + +// The particle effect name is an entry in effects.fxp +struct t2dEffectParticle +{ + char szName[24]; +}; + +// It`s used for spawning peds (Like on ticketbooth, Windows of shops, Blackjack-tables) +// It includes information about the External Script ped is going to use when spawned, it`s facing angle and it`s behaviour +struct t2dEffectAttractor +{ + RwV3d queueDirection; + RwV3d useDirection; + RwV3d forwardDirection; + e2dAttractorType attractorType; + std::uint8_t pedExistingProbability; + std::uint8_t field_26; + std::uint8_t flags; + char szScriptName[8]; +}; + +// entry-exit markers similar to ipl version +struct t2dEffectEnex +{ + float enterAngle; // Rotation angle enter-marker (relative to the object) + RwV3d size; // The radius of the approximation to the marker + RwV3d exitPosn; // The position of exit-marker (offset relative to enter position) + float exitAngle; // angle of rotation exit-marker (relative to the object) + std::int16_t interiorId; + std::uint8_t flags1; // Unknown flags + std::uint8_t skyColor; + char szInteriorName[8]; + std::uint8_t timeOn; + std::uint8_t timeOff; + + // Flags 2 + union + { + struct + { + std::uint8_t unknown1 : 1; + std::uint8_t unknown2 : 1; + std::uint8_t timedEffect : 1; + }; + std::uint8_t flags2; + }; + + std::uint8_t field_2F; +}; + +struct t2dEffectRoadsign +{ + RwV2d size; + RwV3d rotation; + + // Flags + union + { + struct + { + std::uint8_t numOfLines : 2; + std::uint8_t symbolsPerLine : 2; + std::uint8_t textColor : 2; + }; + + std::uint8_t flags; + }; + + std::uint8_t field_16[2]; + char* text; // size 64 + RpAtomic* atomic; +}; + +// Section defines a place where peds can cover during firefights +struct t2dEffectCoverPoint +{ + RwV2d direction; + std::uint8_t type; + std::uint8_t field_9[3]; +}; + +// Example in vgseesc01.dff +struct t2dEffectEscalator +{ + RwV3d bottom; + RwV3d top; + RwV3d end; // Z pos, matches top Z if escalator goes up, bottom Z if it goes down + std::uint8_t direction; // 0 - down, 1 - up + std::uint8_t field_25[3]; +}; + +// Example in kb_bandit_u.dff +struct t2dEffectTriggerPoint +{ + std::int32_t id; +}; + +// Some interiors stuff, probably unused? +struct t2dEffectFurniture +{ + std::uint8_t type; + std::int8_t groupId; + // size + std::uint8_t width; + std::uint8_t depth; + std::uint8_t height; + // doors + std::int8_t door; + std::int8_t l_door[2]; // start, end + std::int8_t r_door[2]; // start, end + std::int8_t t_door[2]; // start, end + // windows + std::int8_t l_window[2]; // start, end + std::int8_t r_window[2]; // start, end + std::int8_t t_window[2]; // start, end + // something like offsets? + std::int8_t goLeft[3]; // x,y,z? + std::int8_t goBottom[3]; // x,y,z? + std::int8_t goWidth[3]; // x,y,z? + std::int8_t goDepth[3]; // x,y,z? + + std::uint8_t seed; + std::uint8_t status; + float rotation; +}; + +union t2dEffectUnion +{ + t2dEffectLight light; + t2dEffectParticle particle; + t2dEffectAttractor attractor; + t2dEffectEnex enex; + t2dEffectRoadsign roadsign; + t2dEffectCoverPoint coverPoint; + t2dEffectEscalator escalator; + t2dEffectTriggerPoint triggerPoint; // slot machine, k.a.a.c gates, basketball hoop + t2dEffectFurniture furniture; +}; + +class C2DEffectSAInterface +{ +public: + RwV3d position; + e2dEffectType type; + std::uint8_t field_D[3]; + + t2dEffectUnion effect; +}; + +class C2DEffectInfoStoreSAInterface +{ +public: + std::uint32_t objCount; + C2DEffectSAInterface objects[180]; // or 100 +}; diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 2db97a5245..15922085ee 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -1735,7 +1735,7 @@ void CModelInfoSA::MakeObjectModel(ushort usBaseID) MemCpyFast(m_pInterface, pBaseObjectInfo, sizeof(CBaseModelInfoSAInterface)); m_pInterface->usNumberOfRefs = 0; m_pInterface->pRwObject = nullptr; - m_pInterface->usUnknown = 65535; + m_pInterface->s2DEffectIndex = -1; m_pInterface->usDynamicIndex = 65535; ppModelInfo[m_dwModelID] = m_pInterface; @@ -1752,7 +1752,7 @@ void CModelInfoSA::MakeTimedObjectModel(ushort usBaseID) MemCpyFast(m_pInterface, pBaseObjectInfo, sizeof(CTimeModelInfoSAInterface)); m_pInterface->usNumberOfRefs = 0; m_pInterface->pRwObject = nullptr; - m_pInterface->usUnknown = 65535; + m_pInterface->s2DEffectIndex = -1; m_pInterface->usDynamicIndex = 65535; m_pInterface->timeInfo.m_wOtherTimeModel = 0; @@ -1769,7 +1769,7 @@ void CModelInfoSA::MakeClumpModel(ushort usBaseID) MemCpyFast(pNewInterface, pBaseObjectInfo, sizeof(CClumpModelInfoSAInterface)); pNewInterface->usNumberOfRefs = 0; pNewInterface->pRwObject = nullptr; - pNewInterface->usUnknown = 65535; + pNewInterface->s2DEffectIndex = -1; pNewInterface->usDynamicIndex = 65535; ppModelInfo[m_dwModelID] = pNewInterface; @@ -1787,7 +1787,7 @@ void CModelInfoSA::MakeVehicleAutomobile(ushort usBaseID) m_pInterface->usNumberOfRefs = 0; m_pInterface->pRwObject = nullptr; m_pInterface->pVisualInfo = nullptr; - m_pInterface->usUnknown = 65535; + m_pInterface->s2DEffectIndex = -1; m_pInterface->usDynamicIndex = 65535; ppModelInfo[m_dwModelID] = m_pInterface; @@ -2064,6 +2064,29 @@ bool CModelInfoSA::IsTowableBy(CModelInfo* towingModel) return isTowable; } +C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const e2dEffectType& type) +{ + // Get 2DFX store + auto* effectStore = reinterpret_cast(ARRAY_2DFXInfoStore); + + // Add item + C2DEffectSAInterface& obj = effectStore->objects[effectStore->objCount]; + ++effectStore->objCount; + + // Call CBaseModelInfo::Add2dEffect + ((void(__thiscall*)(CBaseModelInfoSAInterface*, C2DEffectSAInterface*))FUNC_CBaseModelInfo_Add2dEffect)(m_pInterface, &obj); + + // Init + obj.position = RwV3d{position.fX, position.fY, position.fZ}; + obj.type = type; + + // modelid = 1337 x = 0.1 y = 0.1 z = 0.75 type = 0 r = 52 g = 177 b = 235 a = 255 corona_name = corona star shadow_name = shad_exp farClip = 100 pointLRange = 18 coronaSize = 2 shadowSize = 10 shadowMultiplier = 40 showMode = 0 enableReflection = 0 flareType = 0 flags = 96, zDist = 0 offX = 0 offY = 0 offZ = 0 + //char* line = "1337 0.1 0.1 0.75 0 52 177 235 255 \"coronastar\" \"shad_exp\" 100 18 2 10 40 0 0 0 96 0 0 0 0"; + //((void(__cdecl*)(char* line))0x5B7670)(line); + + return &obj; +} + ////////////////////////////////////////////////////////////////////////////////////////// // // CModelInfoSA::ForceUnload diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index b261bcc04a..c67d45f4f3 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -14,6 +14,7 @@ #include #include #include "CRenderWareSA.h" +#include "C2DEffectSAInterface.h" class CPedModelInfoSA; class CPedModelInfoSAInterface; @@ -63,6 +64,8 @@ static void* ARRAY_ModelInfo = *(void**)(0x403DA4 + 3); #define VAR_CTempColModels_ModelPed1 0x968DF0 +#define FUNC_CBaseModelInfo_Add2dEffect 0x4C4D20 + class CBaseModelInfoSAInterface; class CModelInfoSAInterface { @@ -158,7 +161,7 @@ class CBaseModelInfoSAInterface unsigned char ucAlpha : 8; // +12 unsigned char ucNumOf2DEffects : 8; // +13 - unsigned short usUnknown : 16; // +14 Something with 2d effects + short s2DEffectIndex : 16; // +14 unsigned short usDynamicIndex : 16; // +16 @@ -466,6 +469,9 @@ class CModelInfoSA : public CModelInfo // Vehicle towing functions bool IsTowableBy(CModelInfo* towingModel) override; + // 2DFX functions + C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type); + bool IsDynamic() { return m_pInterface ? m_pInterface->usDynamicIndex != 0xffff : false; }; private: diff --git a/Client/mods/deathmatch/logic/CClient2DFX.cpp b/Client/mods/deathmatch/logic/CClient2DFX.cpp new file mode 100644 index 0000000000..59acf51622 --- /dev/null +++ b/Client/mods/deathmatch/logic/CClient2DFX.cpp @@ -0,0 +1,203 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: Client/mods/deathmatch/logic/CClient2DFX.cpp + * PURPOSE: Handling 2DFX effects + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CClient2DFX.h" +#include "CClient2DFXManager.h" +#include "../game_sa/C2DEffectSAInterface.h" + +#define mask(n) ((1 << (n)) - 1) + +CClient2DFX::CClient2DFX(class CClientManager* manager, ElementID ID) : ClassInit(this), CClientEntity(ID), m_2DFXManager(manager->Get2DFXManager()) +{ + m_pManager = manager; + SetTypeName("2dfx"); + + m_2DFXManager->AddToList(this); +} + +CClient2DFX::~CClient2DFX() +{ + m_2DFXManager->RemoveFromList(this); +} + +bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData) +{ + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(model)); + if (!modelInfo) + return false; + + C2DEffectSAInterface* effect = modelInfo->Add2DFXEffect(position, type); + if (!effect) + return false; + + // Set effect + m_effectInterface = effect; + + // Push current TXD + ((void(__cdecl*)())0x7316A0)(); + + // Find txd slot + int slot = ((int(__fastcall*)(int,int,char*))0x731850)(0,0,"particle"); + + // Set txd slot + ((void(__cdecl*)(int))0x7319C0)(slot); + + // Set properties + switch (type) + { + case e2dEffectType::LIGHT: + { + t2dEffectLight& light = effect->effect.light; + + auto* drawDistance = MapFind(effectData, "draw_distance"); + light.coronaFarClip = std::get(*drawDistance); + + auto* lightRange = MapFind(effectData, "light_range"); + light.pointLightRange = std::get(*lightRange); + + auto* coronaSize = MapFind(effectData, "corona_size"); + light.coronaSize = std::get(*coronaSize); + + auto* shadowSize = MapFind(effectData, "shadow_size"); + light.shadowSize = std::get(*shadowSize); + + auto* shadowMultiplier = MapFind(effectData, "shadow_multiplier"); + light.shadowColorMultiplier = static_cast(std::get(*shadowMultiplier)); + + auto* showMode = MapFind(effectData, "show_mode"); + e2dCoronaFlashType flashType; + StringToEnum(std::get(*showMode), flashType); + light.coronaFlashType = flashType; + + auto* coronaReflection = MapFind(effectData, "corona_reflection"); + light.coronaEnableReflection = std::get(*coronaReflection); + + auto* coronaFlareType = MapFind(effectData, "flare_type"); + light.coronaFlareType = static_cast(std::get(*coronaFlareType)); + + auto* flags = MapFind(effectData, "flags"); + light.flags = static_cast(std::get(*flags)); + + auto* shadowZDistance = MapFind(effectData, "shadow_distance"); + light.shadowZDistance = static_cast(std::get(*shadowZDistance)); + + auto* offsetX = MapFind(effectData, "offsetX"); + light.offsetX = static_cast(std::get(*offsetX)); + + auto* offsetY = MapFind(effectData, "offsetY"); + light.offsetY = static_cast(std::get(*offsetY)); + + auto* offsetZ = MapFind(effectData, "offsetZ"); + light.offsetZ = static_cast(std::get(*offsetZ)); + + auto* color = MapFind(effectData, "color"); + int colorValue = static_cast(std::get(*color)); + light.color = RwColor{static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; + + auto* coronaTexture = MapFind(effectData, "corona_name"); + light.coronaTex = ((RwTexture * (__cdecl*)(const char*, const char*))0x7F3AC0)(std::get(*coronaTexture).c_str(), nullptr); + + auto* shadowTexture = MapFind(effectData, "shadow_name"); + light.shadowTex = ((RwTexture * (__cdecl*)(const char*, const char*))0x7F3AC0)(std::get(*shadowTexture).c_str(), nullptr); + + // ??? + if (light.flags & 4) // withoutCorona + effect->effect.particle.szName[20] &= ~2u; + + break; + } + case e2dEffectType::PARTICLE: + { + auto* particleName = MapFind(effectData, "name"); + auto& stringName = std::get(*particleName); + std::strncpy(effect->effect.particle.szName, stringName.c_str(), sizeof(effect->effect.particle.szName) - 1); + + break; + } + case e2dEffectType::ROADSIGN: + { + t2dEffectRoadsign& roadsign = effect->effect.roadsign; + + auto* sizeX = MapFind(effectData, "sizeX"); + auto* sizeY = MapFind(effectData, "sizeY"); + roadsign.size = RwV2d{std::get(*sizeX), std::get(*sizeY)}; + + auto* rotX = MapFind(effectData, "rotX"); + auto* rotY = MapFind(effectData, "rotY"); + auto* rotZ = MapFind(effectData, "rotZ"); + roadsign.rotation = RwV3d{std::get(*rotX), std::get(*rotY), std::get(*rotZ)}; + + auto* flags = MapFind(effectData, "flags"); + roadsign.flags = static_cast(std::get(*flags)); + + auto* text = MapFind(effectData, "text"); + auto* text2 = MapFind(effectData, "text2"); + auto* text3 = MapFind(effectData, "text3"); + auto* text4 = MapFind(effectData, "text4"); + + roadsign.text = static_cast(malloc(64)); + if (roadsign.text) + { + std::memcpy(roadsign.text, std::get(*text).c_str(), 4); + std::memcpy(roadsign.text + 4, std::get(*text2).c_str(), 4); + std::memcpy(roadsign.text + 8, std::get(*text3).c_str(), 4); + std::memcpy(roadsign.text + 12, std::get(*text4).c_str(), 4); + } + + roadsign.atomic = nullptr; + + break; + } + case e2dEffectType::TRIGGER_POINT: + { + auto* triggerID = MapFind(effectData, "id"); + effect->effect.triggerPoint.id = static_cast(std::get(*triggerID)); + + break; + } + case e2dEffectType::ESCALATOR: + { + t2dEffectEscalator& escalator = effect->effect.escalator; + + auto* bottomX = MapFind(effectData, "bottomX"); + auto* bottomY = MapFind(effectData, "bottomY"); + auto* bottomZ = MapFind(effectData, "bottomZ"); + escalator.bottom = RwV3d{std::get(*bottomX), std::get(*bottomY), std::get(*bottomZ)}; + + auto* topX = MapFind(effectData, "topX"); + auto* topY = MapFind(effectData, "topY"); + auto* topZ = MapFind(effectData, "topZ"); + escalator.top = RwV3d{std::get(*topX), std::get(*topY), std::get(*topZ)}; + + auto* endX = MapFind(effectData, "endX"); + auto* endY = MapFind(effectData, "endY"); + auto* endZ = MapFind(effectData, "endZ"); + escalator.end = RwV3d{std::get(*endX), std::get(*endY), std::get(*endZ)}; + + auto* dir = MapFind(effectData, "direction"); + escalator.direction = static_cast(std::get(*dir)); + + break; + } + case e2dEffectType::ATTRACTOR: + case e2dEffectType::ENEX: + case e2dEffectType::FURNITURE: + case e2dEffectType::COVER_POINT: + case e2dEffectType::SUN_GLARE: + break; + } + + // Pop current txd + ((void(__cdecl*)())0x7316B0)(); + + auto* createdEffect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, int))0x4C4C70)(modelInfo->GetInterface(), 0); + + return true; +} diff --git a/Client/mods/deathmatch/logic/CClient2DFX.h b/Client/mods/deathmatch/logic/CClient2DFX.h new file mode 100644 index 0000000000..c94e2ea46f --- /dev/null +++ b/Client/mods/deathmatch/logic/CClient2DFX.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: Client/mods/deathmatch/logic/CClient2DFX.h + * PURPOSE: Handling 2DFX effects + * + *****************************************************************************/ +#pragma once + +#include "CClientEntity.h" + +class CClient2DFX final : public CClientEntity +{ + DECLARE_CLASS(CClient2DFX, CClientEntity) + friend class CClient2DFXManager; + +public: + CClient2DFX(class CClientManager* manager, ElementID ID); + ~CClient2DFX(); + + eClientEntityType GetType() const { return CCLIENT2DFX; } + + void Unlink(){}; + void GetPosition(CVector& vecPosition) const {}; + void SetPosition(const CVector& vecPosition){}; + + bool Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData); + +private: + class CClient2DFXManager* m_2DFXManager; + C2DEffectSAInterface* m_effectInterface; +}; diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp new file mode 100644 index 0000000000..4b6ff19fce --- /dev/null +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -0,0 +1,260 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: Client/mods/deathmatch/logic/CClient2DFXManager.cpp + * PURPOSE: Handling 2DFX effects manager + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CClient2DFXManager.h" + +CClient2DFXManager::CClient2DFXManager(CClientManager* mainManager) : m_mainManager(mainManager) +{ + // Init + m_canRemoveFromList = true; +} + +CClient2DFXManager::~CClient2DFXManager() +{ + // Delete all 2DFX's + RemoveAll(); +} + +void CClient2DFXManager::RemoveAll() +{ + // Make sure they don't remove themselves from our list + m_canRemoveFromList = false; + + // Run through our list deleting the 2DFX's + for (auto* effect : m_effectsList) + delete effect; + + // Allow list removal again + m_canRemoveFromList = true; +} + +void CClient2DFXManager::RemoveFromList(CClient2DFX* effect) +{ + if (m_canRemoveFromList) + m_effectsList.remove(effect); +} + +bool CClient2DFXManager::Exists(CClient2DFX* effect) +{ + return std::find(m_effectsList.begin(), m_effectsList.end(), effect) != m_effectsList.end(); +} + +CClient2DFX* CClient2DFXManager::Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData) +{ + auto* effect = new CClient2DFX(m_mainManager, INVALID_ELEMENT_ID); + if (!effect) + return nullptr; + + if (!effect->Create(model, position, type, effectData)) + return nullptr; + + return effect; +} + +const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectType, std::unordered_map>& effectData) +{ + // Check if keys & values are ok! + switch (effectType) + { + case e2dEffectType::LIGHT: + { + // corona far clip + auto* drawDistance = MapFind(effectData, "draw_distance"); + if (!drawDistance || !std::holds_alternative(*drawDistance)) + return "Invalid \"draw_distance\" value"; + + auto* lightRange = MapFind(effectData, "light_range"); + if (!lightRange || !std::holds_alternative(*lightRange)) + return "Invalid \"light_range\" value"; + + auto* coronaSize = MapFind(effectData, "corona_size"); + if (!coronaSize || !std::holds_alternative(*coronaSize)) + return "Invalid \"corona_size\" value"; + + auto* shadowSize = MapFind(effectData, "shadow_size"); + if (!shadowSize || !std::holds_alternative(*shadowSize)) + return "Invalid \"shadow_size\" value"; + + auto* shadowMultiplier = MapFind(effectData, "shadow_multiplier"); + if (!shadowMultiplier || !std::holds_alternative(*shadowMultiplier) || std::get(*shadowMultiplier) < 0) + return "Invalid \"shadow_multiplier\" value"; + + auto* showMode = MapFind(effectData, "show_mode"); + e2dCoronaFlashType tempType; + if (!showMode || !std::holds_alternative(*showMode) || !StringToEnum(std::get(*showMode), tempType)) + return "Invalid \"show_mode\" value"; + + auto* coronaReflection = MapFind(effectData, "corona_reflection"); + if (!coronaReflection || !std::holds_alternative(*coronaReflection)) + return "Invalid \"corona_reflection\" value"; + + auto* coronaFlareType = MapFind(effectData, "flare_type"); + if (!coronaFlareType || !std::holds_alternative(*coronaFlareType) || (std::get(*coronaFlareType) < 0 || std::get(*coronaFlareType) > 1.0f)) + return "Invalid \"flare_type\" value"; + + auto* flags = MapFind(effectData, "flags"); + if (!flags || !std::holds_alternative(*flags) || std::get(*flags) < 0) + return "Invalid \"flags\" value"; + + auto* shadowZDistance = MapFind(effectData, "shadow_distance"); + if (!shadowZDistance || (!std::holds_alternative(*shadowZDistance))) + return "Invalid \"shadow_distance\" value"; + + auto* offsetX = MapFind(effectData, "offsetX"); + if (!offsetX || !std::holds_alternative(*offsetX)) + return "Invalid \"offsetX\" value"; + + auto* offsetY = MapFind(effectData, "offsetY"); + if (!offsetY || !std::holds_alternative(*offsetY)) + return "Invalid \"offsetY\" value"; + + auto* offsetZ = MapFind(effectData, "offsetZ"); + if (!offsetZ || !std::holds_alternative(*offsetZ)) + return "Invalid \"offsetZ\" value"; + + auto* color = MapFind(effectData, "color"); + if (!color || !std::holds_alternative(*color)) + return "Invalid \"color\" value"; + + auto* coronaTexture = MapFind(effectData, "corona_name"); + e2dEffectTextureName tempName; + if (!coronaTexture || !std::holds_alternative(*coronaTexture) || !StringToEnum(std::get(*coronaTexture), tempName)) + return "Invalid \"corona_name\" value"; + + auto* shadowTexture = MapFind(effectData, "shadow_name"); + e2dEffectTextureName shadowName; + if (!shadowTexture || !std::holds_alternative(*shadowTexture) || !StringToEnum(std::get(*shadowTexture), shadowName)) + return "Invalid \"shadow_name\" value"; + + break; + } + case e2dEffectType::PARTICLE: + { + auto* particleName = MapFind(effectData, "name"); + if (!particleName || !std::holds_alternative(*particleName) || std::get(*particleName).length() > 24) + return "Invalid \"particle name\" value"; + + break; + } + case e2dEffectType::ATTRACTOR: + // Unnecessary in MTA + break; + case e2dEffectType::SUN_GLARE: + // It has no properties, it only uses position and type fields + break; + case e2dEffectType::FURNITURE: + // Unnecessary in MTA (Probably unused even in SA) + break; + case e2dEffectType::ENEX: + // Unnecessary in MTA + break; + case e2dEffectType::ROADSIGN: + { + auto* sizeX = MapFind(effectData, "sizeX"); + if (!sizeX || !std::holds_alternative(*sizeX)) + return "Invalid \"sizeX\" value"; + + auto* sizeY = MapFind(effectData, "sizeY"); + if (!sizeY || !std::holds_alternative(*sizeY)) + return "Invalid \"sizeY\" value"; + + auto* rotX = MapFind(effectData, "rotX"); + if (!rotX || !std::holds_alternative(*rotX)) + return "Invalid \"rotX\" value"; + + auto* rotY = MapFind(effectData, "rotY"); + if (!rotY || !std::holds_alternative(*rotY)) + return "Invalid \"rotY\" value"; + + auto* rotZ = MapFind(effectData, "rotZ"); + if (!rotZ || !std::holds_alternative(*rotZ)) + return "Invalid \"rotZ\" value"; + + auto* flags = MapFind(effectData, "flags"); + if (!flags || !std::holds_alternative(*flags) || std::get(*flags) < 0) + return "Invalid \"flags\" value"; + + auto* text = MapFind(effectData, "text1"); + if (!text || !std::holds_alternative(*text)) + return "Invalid \"text1\" value"; + + auto* text2 = MapFind(effectData, "text2"); + if (!text2 || !std::holds_alternative(*text2)) + return "Invalid \"text2\" value"; + + auto* text3 = MapFind(effectData, "text3"); + if (!text3 || !std::holds_alternative(*text3)) + return "Invalid \"text3\" value"; + + auto* text4 = MapFind(effectData, "text4"); + if (!text4 || !std::holds_alternative(*text4)) + return "Invalid \"text4\" value"; + + break; + } + case e2dEffectType::TRIGGER_POINT: + { + auto* id = MapFind(effectData, "id"); + if (!id || !std::holds_alternative(*id)) + return "Invalid \"id\" value"; + + break; + } + case e2dEffectType::COVER_POINT: + // Unnecessary in MTA + break; + case e2dEffectType::ESCALATOR: + { + auto* bottomX = MapFind(effectData, "bottomX"); + if (!bottomX || !std::holds_alternative(*bottomX)) + return "Invalid \"bottomX\" value"; + + auto* bottomY = MapFind(effectData, "bottomY"); + if (!bottomY || !std::holds_alternative(*bottomY)) + return "Invalid \"bottomY\" value"; + + auto* bottomZ = MapFind(effectData, "bottomZ"); + if (!bottomZ || !std::holds_alternative(*bottomZ)) + return "Invalid \"bottomZ\" value"; + + auto* topX = MapFind(effectData, "topX"); + if (!topX || !std::holds_alternative(*topX)) + return "Invalid \"topX\" value"; + + auto* topY = MapFind(effectData, "topY"); + if (!topY || !std::holds_alternative(*topY)) + return "Invalid \"topY\" value"; + + auto* topZ = MapFind(effectData, "topZ"); + if (!topX || !std::holds_alternative(*topZ)) + return "Invalid \"topZ\" value"; + + auto* endX = MapFind(effectData, "endX"); + if (!endX || !std::holds_alternative(*endX)) + return "Invalid \"endX\" value"; + + auto* endY = MapFind(effectData, "endY"); + if (!endY || !std::holds_alternative(*endY)) + return "Invalid \"endY\" value"; + + auto* endZ = MapFind(effectData, "endZ"); + if (!endZ || !std::holds_alternative(*endZ)) + return "Invalid \"endZ\" value"; + + auto* direction = MapFind(effectData, "direction"); + if (!direction || !std::holds_alternative(*direction) || (std::get(*direction) < 0 || std::get(*direction) > 1.0f)) + return "Invalid \"direction\" value"; + + break; + } + } + + return nullptr; +} diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.h b/Client/mods/deathmatch/logic/CClient2DFXManager.h new file mode 100644 index 0000000000..554246ce48 --- /dev/null +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.h @@ -0,0 +1,36 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: Client/mods/deathmatch/logic/CClient2DFXManager.h + * PURPOSE: Handling 2DFX effects manager + * + *****************************************************************************/ +#pragma once + +#include "CClient2DFX.h" + +class CClient2DFXManager +{ + friend class CClient2DFX; + +public: + CClient2DFXManager(class CClientManager* mainManager); + ~CClient2DFXManager(); + + void RemoveAll(); + bool Exists(CClient2DFX* effect); + + CClient2DFX* Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData); + + static const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectType, std::unordered_map>& effectData); + +private: + void AddToList(CClient2DFX* effect) { m_effectsList.push_back(effect); } + void RemoveFromList(CClient2DFX* effect); + + class CClientManager* m_mainManager; + + std::list m_effectsList; + bool m_canRemoveFromList; +}; diff --git a/Client/mods/deathmatch/logic/CClientEntity.h b/Client/mods/deathmatch/logic/CClientEntity.h index 0d84526d8b..9ef4e6fb3d 100644 --- a/Client/mods/deathmatch/logic/CClientEntity.h +++ b/Client/mods/deathmatch/logic/CClientEntity.h @@ -80,6 +80,7 @@ enum eClientEntityType CCLIENTUNKNOWN, CCLIENTIMG, CCLIENTBUILDING, + CCLIENT2DFX, }; class CEntity; @@ -145,6 +146,7 @@ enum eCClientEntityClassTypes CLASS_CClientSearchLight, CLASS_CClientIMG, CLASS_CClientBuilding, + CLASS_CClient2DFX, }; class CClientEntity : public CClientEntityBase diff --git a/Client/mods/deathmatch/logic/CClientManager.cpp b/Client/mods/deathmatch/logic/CClientManager.cpp index e3a808555b..b27fb069fe 100644 --- a/Client/mods/deathmatch/logic/CClientManager.cpp +++ b/Client/mods/deathmatch/logic/CClientManager.cpp @@ -55,6 +55,7 @@ CClientManager::CClientManager() m_pPacketRecorder = new CClientPacketRecorder(this); m_pImgManager = new CClientIMGManager(this); m_pBuildingManager = new CClientBuildingManager(this); + m_p2DFXManager = new CClient2DFXManager(this); m_bBeingDeleted = false; m_bGameUnloadedFlag = false; @@ -181,6 +182,9 @@ CClientManager::~CClientManager() delete m_pBuildingManager; m_pBuildingManager = nullptr; + + delete m_p2DFXManager; + m_p2DFXManager = nullptr; } // diff --git a/Client/mods/deathmatch/logic/CClientManager.h b/Client/mods/deathmatch/logic/CClientManager.h index 0e9f0c7702..19dc6da001 100644 --- a/Client/mods/deathmatch/logic/CClientManager.h +++ b/Client/mods/deathmatch/logic/CClientManager.h @@ -44,6 +44,7 @@ class CClientManager; #include "CClientModelManager.h" #include "CClientIMGManager.h" #include "CClientBuildingManager.h" +#include "CClient2DFXManager.h" class CClientProjectileManager; class CClientExplosionManager; @@ -98,6 +99,7 @@ class CClientManager CClientPointLightsManager* GetPointLightsManager() { return m_pPointLightsManager; } CClientIMGManager* GetIMGManager() { return m_pImgManager; } CClientBuildingManager* GetBuildingManager() const noexcept { return m_pBuildingManager; } + CClient2DFXManager* Get2DFXManager() const noexcept { return m_p2DFXManager; } bool IsGameLoaded() { return g_pGame->GetSystemState() == 9 && !m_bGameUnloadedFlag && g_pCore->GetNetwork()->GetServerBitStreamVersion(); } bool IsBeingDeleted() { return m_bBeingDeleted; } @@ -151,6 +153,7 @@ class CClientManager CClientIMGManager* m_pImgManager; CClientPacketRecorder* m_pPacketRecorder; CClientBuildingManager* m_pBuildingManager; + CClient2DFXManager* m_p2DFXManager; bool m_bBeingDeleted; bool m_bGameUnloadedFlag; int m_iNumLowLODElements; diff --git a/Client/mods/deathmatch/logic/CResource.cpp b/Client/mods/deathmatch/logic/CResource.cpp index dcf119ce46..352ff4fd56 100644 --- a/Client/mods/deathmatch/logic/CResource.cpp +++ b/Client/mods/deathmatch/logic/CResource.cpp @@ -73,6 +73,11 @@ CResource::CResource(unsigned short usNetID, const char* szResourceName, CClient m_pResourceIMGRoot = new CClientDummy(g_pClientGame->GetManager(), INVALID_ELEMENT_ID, "imgroot"); m_pResourceIMGRoot->MakeSystemEntity(); + // Create our 2DFX root element. We set its parent when we're loaded. + // Make it a system entity so nothing but us can delete it. + m_pResource2DFXRoot = new CClientDummy(g_pClientGame->GetManager(), INVALID_ELEMENT_ID, "2dfxroot"); + m_pResource2DFXRoot->MakeSystemEntity(); + m_strResourceDirectoryPath = SString("%s/resources/%s", g_pClientGame->GetFileCacheRoot(), *m_strResourceName); m_strResourcePrivateDirectoryPath = PathJoin(CServerIdManager::GetSingleton()->GetConnectionPrivateDirectory(), m_strResourceName); @@ -140,6 +145,10 @@ CResource::~CResource() g_pClientGame->GetElementDeleter()->DeleteRecursive(m_pResourceGUIEntity); m_pResourceGUIEntity = NULL; + // Destroy the 2dfx root so all 2dfx elements are deleted except those moved out + g_pClientGame->GetElementDeleter()->DeleteRecursive(m_pResource2DFXRoot); + m_pResource2DFXRoot = nullptr; + // Undo all changes to water g_pGame->GetWaterManager()->UndoChanges(this); @@ -256,6 +265,7 @@ void CResource::Load() m_pResourceDFFEntity->SetParent(m_pResourceEntity); m_pResourceGUIEntity->SetParent(m_pResourceEntity); m_pResourceTXDRoot->SetParent(m_pResourceEntity); + m_pResource2DFXRoot->SetParent(m_pResourceEntity); } CLogger::LogPrintf("> Starting resource '%s'", *m_strResourceName); diff --git a/Client/mods/deathmatch/logic/CResource.h b/Client/mods/deathmatch/logic/CResource.h index 8100d3fc79..17fa4b31ab 100644 --- a/Client/mods/deathmatch/logic/CResource.h +++ b/Client/mods/deathmatch/logic/CResource.h @@ -79,6 +79,7 @@ class CResource CClientEntity* GetResourceTXDRoot() { return m_pResourceTXDRoot; }; CClientEntity* GetResourceIFPRoot() { return m_pResourceIFPRoot; }; CClientEntity* GetResourceIMGRoot() { return m_pResourceIMGRoot; }; + CClientEntity* GetResource2DFXRoot() { return m_pResource2DFXRoot; }; CResourceModelStreamer* GetResourceModelStreamer() { return &m_modelStreamer; }; @@ -128,6 +129,7 @@ class CResource class CClientEntity* m_pResourceTXDRoot; class CClientEntity* m_pResourceIFPRoot; class CClientEntity* m_pResourceIMGRoot; + class CClientEntity* m_pResource2DFXRoot; unsigned short m_usRemainingNoClientCacheScripts; bool m_bLoadAfterReceivingNoClientCacheScripts; CMtaVersion m_strMinServerReq; diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 64708dea0c..8570fd84e0 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -910,6 +910,71 @@ ADD_ENUM(PreloadAreaOption::COLLISIONS, "collisions") ADD_ENUM(PreloadAreaOption::ALL, "all") IMPLEMENT_ENUM_CLASS_END("preload-area-option") +IMPLEMENT_ENUM_CLASS_BEGIN(e2dEffectType) +ADD_ENUM(e2dEffectType::LIGHT, "light") +ADD_ENUM(e2dEffectType::PARTICLE, "particle") +ADD_ENUM(e2dEffectType::ATTRACTOR, "ped_attractor") +ADD_ENUM(e2dEffectType::SUN_GLARE, "sun_glare") +ADD_ENUM(e2dEffectType::FURNITURE, "furniture") +ADD_ENUM(e2dEffectType::ENEX, "enter_exit") +ADD_ENUM(e2dEffectType::ROADSIGN, "roadsign") +ADD_ENUM(e2dEffectType::TRIGGER_POINT, "trigger_point") +ADD_ENUM(e2dEffectType::COVER_POINT, "cover_point") +ADD_ENUM(e2dEffectType::ESCALATOR, "escalator") +IMPLEMENT_ENUM_CLASS_END("2dfx-type") + +IMPLEMENT_ENUM_CLASS_BEGIN(e2dCoronaFlashType) +ADD_ENUM(e2dCoronaFlashType::DEFAULT, "default") +ADD_ENUM(e2dCoronaFlashType::RANDOM, "random_flashing") +ADD_ENUM(e2dCoronaFlashType::RANDOM_WHEN_WET, "random_at_wet_weather") +ADD_ENUM(e2dCoronaFlashType::ANIM_SPEED_4X, "anim_speed_4x") +ADD_ENUM(e2dCoronaFlashType::ANIM_SPEED_2X, "anim_speed_2x") +ADD_ENUM(e2dCoronaFlashType::ANIM_SPEED_1X, "anim_speed_1x") +ADD_ENUM(e2dCoronaFlashType::WARNLIGHT, "warnlight") +ADD_ENUM(e2dCoronaFlashType::TRAFFICLIGHT, "trafficlight") +ADD_ENUM(e2dCoronaFlashType::TRAINCROSSING, "traincrosslight") +ADD_ENUM(e2dCoronaFlashType::ONLY_RAIN, "at_rain_only") +ADD_ENUM(e2dCoronaFlashType::ON5_OFF5, "on_off_at_5") +ADD_ENUM(e2dCoronaFlashType::ON6_OFF4, "on_at_6_off_at_4") +ADD_ENUM(e2dCoronaFlashType::ON4_OFF6, "on_at_4_off_at_6") +IMPLEMENT_ENUM_CLASS_END("2dfx-light-mode") + +IMPLEMENT_ENUM_CLASS_BEGIN(e2dEffectTextureName) +ADD_ENUM(e2dEffectTextureName::CORONA_STAR, "coronastar") +ADD_ENUM(e2dEffectTextureName::SHAD_HELI, "shad_heli") +ADD_ENUM(e2dEffectTextureName::SHAD_EXP, "shad_exp") +ADD_ENUM(e2dEffectTextureName::SHAD_CAR, "shad_car") +ADD_ENUM(e2dEffectTextureName::SHAD_BIKE, "shad_bike") +ADD_ENUM(e2dEffectTextureName::SEABD32, "seabd32") +ADD_ENUM(e2dEffectTextureName::ROADSIGNFONT, "roadsignfont") +ADD_ENUM(e2dEffectTextureName::PARTICLESKID, "particleskid") +ADD_ENUM(e2dEffectTextureName::LUNAR, "lunar") +ADD_ENUM(e2dEffectTextureName::LOCKONFIRE, "lockonFire") +ADD_ENUM(e2dEffectTextureName::LOCKON, "lockon") +ADD_ENUM(e2dEffectTextureName::LAMP_SHAD_64, "lamp_shad_64") +ADD_ENUM(e2dEffectTextureName::HEADLIGHT1, "headlight1") +ADD_ENUM(e2dEffectTextureName::HEADLIGHT, "headlight") +ADD_ENUM(e2dEffectTextureName::HANDMAN, "handman") +ADD_ENUM(e2dEffectTextureName::FINISHFLAG, "finishflag") +ADD_ENUM(e2dEffectTextureName::CORONARINGB, "coronaringb") +ADD_ENUM(e2dEffectTextureName::CORONAREFLECT, "coronareflect") +ADD_ENUM(e2dEffectTextureName::CORONAMOON, "coronamoon") +ADD_ENUM(e2dEffectTextureName::CORONAHEADLIGHTLINE, "coronaheadlightline") +ADD_ENUM(e2dEffectTextureName::CLOUDMASKED, "cloudmasked") +ADD_ENUM(e2dEffectTextureName::CLOUDHIGH, "cloudhigh") +ADD_ENUM(e2dEffectTextureName::CLOUD1, "cloud1") +ADD_ENUM(e2dEffectTextureName::CARFX1, "carfx1") +ADD_ENUM(e2dEffectTextureName::BLOODPOOL_64, "bloodpool_64") +ADD_ENUM(e2dEffectTextureName::WINCRACK_32, "wincrack_32") +ADD_ENUM(e2dEffectTextureName::WHITE, "white") +ADD_ENUM(e2dEffectTextureName::WATERWAKE, "waterwake") +ADD_ENUM(e2dEffectTextureName::WATERCLEAR256, "waterclear256") +ADD_ENUM(e2dEffectTextureName::TXGRASSBIG1, "txgrassbig1") +ADD_ENUM(e2dEffectTextureName::TXGRASSBIG0, "txgrassbig0") +ADD_ENUM(e2dEffectTextureName::SHAD_RCBARON, "shad_rcbaron") +ADD_ENUM(e2dEffectTextureName::SHAD_PED, "shad_ped") +IMPLEMENT_ENUM_CLASS_END("2dfx-texture-name") + // // CResource from userdata // diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index e134a73bea..0d9b463269 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -88,6 +88,48 @@ DECLARE_ENUM(ePools); DECLARE_ENUM(eWorldProperty); DECLARE_ENUM_CLASS(eModelLoadState); DECLARE_ENUM_CLASS(PreloadAreaOption); +DECLARE_ENUM_CLASS(e2dEffectType); +DECLARE_ENUM_CLASS(e2dCoronaFlashType); + +// For corona name & shadow name +enum class e2dEffectTextureName +{ + CORONA_STAR, + SHAD_HELI, + SHAD_EXP, + SHAD_CAR, + SHAD_BIKE, + SEABD32, + ROADSIGNFONT, + PARTICLESKID, + LUNAR, + LOCKONFIRE, + LOCKON, + LAMP_SHAD_64, + HEADLIGHT1, + HEADLIGHT, + HANDMAN, + FINISHFLAG, + CORONARINGB, + CORONAREFLECT, + CORONAMOON, + CORONAHEADLIGHTLINE, + CLOUDMASKED, + CLOUDHIGH, + CLOUD1, + CARFX1, + BLOODPOOL_64, + WINCRACK_32, + WHITE, + WATERWAKE, + WATERCLEAR256, + TXGRASSBIG1, + TXGRASSBIG0, + TARGET256, + SHAD_RCBARON, + SHAD_PED, +}; +DECLARE_ENUM_CLASS(e2dEffectTextureName) class CRemoteCall; @@ -511,6 +553,11 @@ inline SString GetClassTypeName(CClientVectorGraphic*) return "svg"; } +inline SString GetClassTypeName(CClient2DFX*) +{ + return "2dfx"; +} + // // CResource from userdata // diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaDefs.cpp index 457f72998f..107af6dcbd 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDefs.cpp @@ -32,6 +32,7 @@ CClientColModelManager* CLuaDefs::m_pColModelManager = NULL; CRegisteredCommands* CLuaDefs::m_pRegisteredCommands = NULL; CClientIMGManager* CLuaDefs::m_pImgManager = NULL; CClientBuildingManager* CLuaDefs::m_pBuildingManager = nullptr; +CClient2DFXManager* CLuaDefs::m_p2DFXManager = nullptr; bool ms_bRegisterdPostCallHook = false; void CLuaDefs::Initialize(CClientGame* pClientGame, CLuaManager* pLuaManager, CScriptDebugging* pScriptDebugging) @@ -58,6 +59,7 @@ void CLuaDefs::Initialize(CClientGame* pClientGame, CLuaManager* pLuaManager, CS m_pRegisteredCommands = pClientGame->GetRegisteredCommands(); m_pImgManager = m_pManager->GetIMGManager(); m_pBuildingManager = m_pManager->GetBuildingManager(); + m_p2DFXManager = m_pManager->Get2DFXManager(); } int CLuaDefs::CanUseFunction(lua_CFunction f, lua_State* luaVM) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaDefs.h index 3f3c18386f..e65cfc8131 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDefs.h @@ -66,6 +66,7 @@ class CLuaDefs static CRegisteredCommands* m_pRegisteredCommands; static CClientIMGManager* m_pImgManager; static CClientBuildingManager* m_pBuildingManager; + static CClient2DFXManager* m_p2DFXManager; protected: // Old style: Only warn on failure. This should diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 7ca02230d5..052b69c2e5 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -146,7 +146,8 @@ void CLuaEngineDefs::LoadFunctions() {"engineGetPoolUsedCapacity", ArgumentParser}, {"engineSetPoolCapacity", ArgumentParser}, {"enginePreloadWorldArea", ArgumentParser}, - + {"engineAddModel2DFX", ArgumentParser}, + // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); // CLuaCFunctions::AddFunction ( "enginePositionAtomic", EnginePositionAtomic ); @@ -2560,3 +2561,23 @@ void CLuaEngineDefs::EnginePreloadWorldArea(CVector position, std::optionalGetStreaming()->LoadSceneCollision(&position); } + +std::variant CLuaEngineDefs::EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, std::unordered_map> effectData) +{ + if (!CClientObjectManager::IsValidModel(modelID) && !CClientVehicleManager::IsValidModel(modelID) && !CClientBuildingManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + const char* error = CClient2DFXManager::IsValidEffectData(effectType, effectData); + if (error) + throw std::invalid_argument(error); + + CClient2DFX* effect = m_p2DFXManager->Add2DFX(modelID, position, effectType, effectData); + if (!effect) + return false; + + CResource* resource = &lua_getownerresource(luaVM); + if (resource) + effect->SetParent(resource->GetResource2DFXRoot()); + + return effect; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index a67ecfc68d..f8a287e573 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -94,6 +94,8 @@ class CLuaEngineDefs : public CLuaDefs static void EnginePreloadWorldArea(CVector position, std::optional option); + static std::variant EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, std::unordered_map> effectData); + private: static void AddEngineColClass(lua_State* luaVM); static void AddEngineTxdClass(lua_State* luaVM); diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 08cb032e4c..6db5f13bd8 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -20,6 +20,7 @@ class CColModel; class CPedModelInfo; struct RpClump; struct RwObject; +class C2DEffectSAInterface; class CBoundingBox { @@ -84,6 +85,53 @@ enum eVehicleUpgradePosn VEHICLE_UPGRADE_POSN_MISC, }; +enum class e2dEffectType : std::uint8_t +{ + LIGHT = 0, + PARTICLE, + ATTRACTOR, + SUN_GLARE, + FURNITURE, + ENEX, + ROADSIGN, + TRIGGER_POINT, + COVER_POINT, + ESCALATOR, +}; + +enum class e2dCoronaFlashType : std::uint8_t +{ + DEFAULT = 0, + RANDOM, + RANDOM_WHEN_WET, + ANIM_SPEED_4X, + ANIM_SPEED_2X, + ANIM_SPEED_1X, + WARNLIGHT, // Used on model nt_roadblockci + TRAFFICLIGHT, + TRAINCROSSING, + UNUSED, + ONLY_RAIN, + ON5_OFF5, + ON6_OFF4, + ON4_OFF6, +}; + +enum class e2dAttractorType : std::int8_t +{ + UNDEFINED = -1, + ATM = 0, + SEAT, + STOP, + PIZZA, + SHELTER, + TRIGGER_SCRIPT, + LOOK_AT, + SCRIPTED, + PARK, + STEP, +}; + struct SVehicleSupportedUpgrades { SVehicleSupportedUpgrades() { Reset(); } @@ -247,6 +295,9 @@ class CModelInfo // Vehicle towing functions virtual bool IsTowableBy(CModelInfo* towingModel) = 0; + // 2dfx functions + virtual C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type) = 0; + virtual unsigned int GetParentID() = 0; virtual bool IsDynamic() = 0; }; From 1d2c3f5bfe0ff26177692dd8c4e70e2e368429e3 Mon Sep 17 00:00:00 2001 From: FileEX Date: Tue, 17 Sep 2024 22:56:16 +0200 Subject: [PATCH 02/17] Update and cleanup --- Client/game_sa/CModelInfoSA.cpp | 79 +++++++++++++++++++- Client/game_sa/CModelInfoSA.h | 3 + Client/mods/deathmatch/logic/CClient2DFX.cpp | 4 +- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 15922085ee..a8dd17d36b 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -35,6 +35,7 @@ std::map CModelInfo std::unordered_map CModelInfoSA::ms_OriginalObjectPropertiesGroups; std::unordered_map> CModelInfoSA::ms_VehicleModelDefaultWheelSizes; std::map CModelInfoSA::ms_DefaultTxdIDMap; +std::map CModelInfoSA::d2fxEffects; union tIdeFlags { @@ -1867,6 +1868,49 @@ void _declspec(naked) HOOK_NodeNameStreamRead() } } +#define HOOKPOS_Get2dEffect 0x4C4CD8 +#define HOOKSIZE_Get2dEffect 6 +static constexpr DWORD RETURN_CBaseModelInfo_Get2dEffect = 0x4C4CDE; +int index = -1; +int count = 0; +int offset = *(int*)0xC3A1E0; +void _declspec(naked) CModelInfoSA::HOOK_Get2dEffect() +{ + + _asm + { + mov esi, [esp+8+0xC] + pushad + mov index, esi + mov count, ebp + } + + if (index > count * 64 + offset) + { + // return from vector + auto* effect = MapGet(d2fxEffects, index); + _asm + { + popad + mov eax, effect + pop edi + pop esi + pop ebp + pop ebx + retn 4 + } + } + else + { + _asm + { + popad + cmp esi, ebp + jmp RETURN_CBaseModelInfo_Get2dEffect + } + } +} + ////////////////////////////////////////////////////////////////////////////////////////// // // Setup hooks @@ -1875,6 +1919,7 @@ void _declspec(naked) HOOK_NodeNameStreamRead() void CModelInfoSA::StaticSetHooks() { EZHookInstall(NodeNameStreamRead); + EZHookInstall(Get2dEffect); } // Recursive RwFrame children searching function @@ -2066,12 +2111,34 @@ bool CModelInfoSA::IsTowableBy(CModelInfo* towingModel) C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const e2dEffectType& type) { + // Init new effect + C2DEffectSAInterface* effectInterface = new C2DEffectSAInterface(); + effectInterface->position = RwV3d{position.fX, position.fY, position.fZ}; + effectInterface->type = type; + + // Call CBaseModelInfo::Add2dEffect + //((void(__thiscall*)(CBaseModelInfoSAInterface*, C2DEffectSAInterface*))FUNC_CBaseModelInfo_Add2dEffect)(m_pInterface, effectInterface); + m_pInterface->s2DEffectIndex = -1; + + if (m_pInterface->ucNumOf2DEffects) + m_pInterface->ucNumOf2DEffects++; + else + m_pInterface->ucNumOf2DEffects = 1; + + // Save our effect + MapSet(d2fxEffects, m_dwModelID, effectInterface); + + return effectInterface; + + //*(g2dEffectPluginOffset + a3) = _dataAllocatedForAll2dfx; + + /* // Get 2DFX store auto* effectStore = reinterpret_cast(ARRAY_2DFXInfoStore); // Add item - C2DEffectSAInterface& obj = effectStore->objects[effectStore->objCount]; - ++effectStore->objCount; + //C2DEffectSAInterface& obj = effectStore->objects[effectStore->objCount]; + //effectStore->objCount++; // Call CBaseModelInfo::Add2dEffect ((void(__thiscall*)(CBaseModelInfoSAInterface*, C2DEffectSAInterface*))FUNC_CBaseModelInfo_Add2dEffect)(m_pInterface, &obj); @@ -2079,12 +2146,16 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const // Init obj.position = RwV3d{position.fX, position.fY, position.fZ}; obj.type = type; - + + // Set special flags + //m_pInterface->usFlags |= 0x2000; + + // modelid = 1337 x = 0.1 y = 0.1 z = 0.75 type = 0 r = 52 g = 177 b = 235 a = 255 corona_name = corona star shadow_name = shad_exp farClip = 100 pointLRange = 18 coronaSize = 2 shadowSize = 10 shadowMultiplier = 40 showMode = 0 enableReflection = 0 flareType = 0 flags = 96, zDist = 0 offX = 0 offY = 0 offZ = 0 //char* line = "1337 0.1 0.1 0.75 0 52 177 235 255 \"coronastar\" \"shad_exp\" 100 18 2 10 40 0 0 0 96 0 0 0 0"; //((void(__cdecl*)(char* line))0x5B7670)(line); - return &obj; + return &obj;*/ } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index c67d45f4f3..1df15573ae 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -332,6 +332,7 @@ class CModelInfoSA : public CModelInfo static std::unordered_map ms_OriginalObjectPropertiesGroups; static std::unordered_map> ms_VehicleModelDefaultWheelSizes; static std::map ms_DefaultTxdIDMap; + static std::map d2fxEffects; SVehicleSupportedUpgrades m_ModelSupportedUpgrades; public: @@ -456,6 +457,8 @@ class CModelInfoSA : public CModelInfo void DeallocateModel(void); unsigned int GetParentID() { return m_dwParentID; }; + static void HOOK_Get2dEffect(); + SVehicleSupportedUpgrades GetVehicleSupportedUpgrades() { return m_ModelSupportedUpgrades; } void InitialiseSupportedUpgrades(RpClump* pClump); diff --git a/Client/mods/deathmatch/logic/CClient2DFX.cpp b/Client/mods/deathmatch/logic/CClient2DFX.cpp index 59acf51622..74988af2db 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFX.cpp @@ -44,7 +44,7 @@ bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2d ((void(__cdecl*)())0x7316A0)(); // Find txd slot - int slot = ((int(__fastcall*)(int,int,char*))0x731850)(0,0,"particle"); + int slot = ((int(__cdecl*)(const char*))0x731850)("particle"); // Set txd slot ((void(__cdecl*)(int))0x7319C0)(slot); @@ -197,7 +197,5 @@ bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2d // Pop current txd ((void(__cdecl*)())0x7316B0)(); - auto* createdEffect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, int))0x4C4C70)(modelInfo->GetInterface(), 0); - return true; } From dba4364f942e8f72b786160063704815992038c6 Mon Sep 17 00:00:00 2001 From: FileEX Date: Fri, 20 Sep 2024 21:47:43 +0200 Subject: [PATCH 03/17] Clean & Rework --- Client/game_sa/C2DEffectSAInterface.h | 90 ++- Client/game_sa/CFxSA.cpp | 4 +- Client/game_sa/CFxSA.h | 150 +++-- Client/game_sa/CGameSA.cpp | 5 + Client/game_sa/CGameSA.h | 1 + Client/game_sa/CModelInfoSA.cpp | 530 +++++++++++++----- Client/game_sa/CModelInfoSA.h | 21 +- Client/mods/deathmatch/logic/CClient2DFX.cpp | 165 +----- Client/mods/deathmatch/logic/CClient2DFX.h | 8 +- .../deathmatch/logic/CClient2DFXManager.cpp | 296 +++++++++- .../deathmatch/logic/CClient2DFXManager.h | 10 +- Client/mods/deathmatch/logic/CClientGame.cpp | 1 + .../logic/luadefs/CLuaEngineDefs.cpp | 197 ++++++- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 18 +- Client/sdk/game/CGame.h | 1 + Client/sdk/game/CModelInfo.h | 11 + 16 files changed, 1126 insertions(+), 382 deletions(-) diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h index db6a68a87a..4c873fbcfb 100644 --- a/Client/game_sa/C2DEffectSAInterface.h +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -11,10 +11,30 @@ #pragma once #include "game/RenderWare.h" -#include "game/CModelInfo.h" +#include "CObjectSA.h" #define ARRAY_2DFXInfoStore 0xB4C2D8 // C2dfxInfoStore d2fxModels +#define FUNC_C2DEffect_Shutdown 0x4C57D0 +#define FUNC_PushCurrentTxd 0x7316A0 +#define FUNC_FindTxdSlot 0x731850 +#define FUNC_SetCurrentTxd 0x7319C0 +#define FUNC_PopCurrentTxd 0x7316B0 +#define FUNC_RwReadTexture 0x7F3AC0 + +// Escalators stuff +#define ARRAY_CEscalators 0xC6E9A8 +#define NUM_MAX_ESCALATORS 32 +#define FUNC_CEscalator_SwitchOff 0x717860 + +// fx stuff +#define FUNC_Fx_c_DestroyEntityFx 0x4A1280 +#define VAR_G_Fx 0xA9AE00 +#define OFFSET_FxSystem_Entities 0xC +#define OFFSET_FxSystem_Link_Prev 0x4 + +#define FUNC_RwTextureDestroy 0x7F3820 + struct t2dEffectLight { RwColor color; @@ -131,7 +151,7 @@ struct t2dEffectRoadsign }; std::uint8_t field_16[2]; - char* text; // size 64 + char* text; // size 64 RpAtomic* atomic; }; @@ -154,6 +174,7 @@ struct t2dEffectEscalator }; // Example in kb_bandit_u.dff +// Used to determine additional coordinates that can be used in scripts struct t2dEffectTriggerPoint { std::int32_t id; @@ -215,5 +236,68 @@ class C2DEffectInfoStoreSAInterface { public: std::uint32_t objCount; - C2DEffectSAInterface objects[180]; // or 100 + C2DEffectSAInterface objects[100]; +}; + +class C2DEffectPluginDataSAInterface +{ +public: + std::uint32_t count; + C2DEffectSAInterface objects[]; +}; + +class CEscalatorSAInterface +{ +public: + RwV3d startPos; + RwV3d bottomPos; + RwV3d topPos; + RwV3d endPos; + std::uint8_t rotation[72]; // CMatrixSAInterface + bool exist; + bool objectCreated; + bool moveDown; + std::uint8_t field_7B; // pad + std::int32_t numIntermediatePlanes; + std::uint32_t numBottomPlanes; + std::uint32_t numTopPlanes; + std::uint8_t field_88[8]; // unused field + RwSphere bounding; + float currentPosition; + CEntitySAInterface* entity; + CObjectSAInterface* objects[42]; }; + +class C2DEffectSA +{ +public: + static int effect2dPluginOffset; +}; + +static void PrepareTexturesForLightEffect(RwTexture* coronaTex, RwTexture* shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist) +{ + // Call CTxdStore::PushCurrentTxd + ((void(__cdecl*)())FUNC_PushCurrentTxd)(); + // Call CTxdStore::FindTxdSlot + int slot = ((int(__cdecl*)(const char*))FUNC_FindTxdSlot)("particle"); + // Call CTxdStore::SetCurrentTxd + ((void(__cdecl*)(int))FUNC_SetCurrentTxd)(slot); + + if (removeIfExist) + { + using RwTextureDestroy = void(__cdecl*)(RwTexture*); + + if (coronaTex) + ((RwTextureDestroy)FUNC_RwTextureDestroy)(coronaTex); + if (shadowTex) + ((RwTextureDestroy)FUNC_RwTextureDestroy)(shadowTex); + } + + // Call RwReadTexture + using RwReadTexture = RwTexture*(__cdecl*)(const char*, const char*); + coronaTex = ((RwReadTexture)FUNC_RwReadTexture)(coronaName, nullptr); + shadowTex = ((RwReadTexture)FUNC_RwReadTexture)(shadowName, nullptr); + + // Call CTxdStore::PopCurrentTxd + ((void(__cdecl*)())FUNC_PopCurrentTxd)(); +} diff --git a/Client/game_sa/CFxSA.cpp b/Client/game_sa/CFxSA.cpp index b063cb927d..bd8c92f8cf 100644 --- a/Client/game_sa/CFxSA.cpp +++ b/Client/game_sa/CFxSA.cpp @@ -246,7 +246,7 @@ void CFxSA::AddParticle(eFxParticleSystems eFxParticle, const CVector& vecPositi FxPrtMult_c fxPrt{{fR,fG,fB,fA}, fSize, 0, fLife}; CVector newDirection; - FxSystem_c* fxParticleSystem; + FxSystem_cSAInterface* fxParticleSystem; switch (eFxParticle) { @@ -326,6 +326,6 @@ void CFxSA::AddParticle(eFxParticleSystems eFxParticle, const CVector& vecPositi newDirection.fZ = (rand() % 10000) * 0.0001f * 4 - 2 + newDirection.fZ; // Call FxSystem_c::AddParticle - ((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); + ((int(__thiscall*)(FxSystem_cSAInterface*, 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); } } diff --git a/Client/game_sa/CFxSA.h b/Client/game_sa/CFxSA.h index 7d61c1759a..8a7f4e78a2 100644 --- a/Client/game_sa/CFxSA.h +++ b/Client/game_sa/CFxSA.h @@ -12,48 +12,125 @@ #pragma once #include +#include "CFxSystemBPSA.h" +#include "CVector.h" struct RwColor; -class FxSystem_c; - -#define FUNC_CFx_AddBlood 0x49eb00 -#define FUNC_CFx_AddWood 0x49ee10 -#define FUNC_CFx_AddSparks 0x49f040 -#define FUNC_CFx_AddTyreBurst 0x49f300 -#define FUNC_CFx_AddBulletImpact 0x49f3d0 -#define FUNC_CFx_AddPunchImpact 0x49f670 -#define FUNC_CFx_AddDebris 0x49f750 -#define FUNC_CFx_AddGlass 0x49f970 -#define FUNC_CFx_TriggerWaterHydrant 0x4a0d70 -#define FUNC_CFx_TriggerGunshot 0x4a0de0 -#define FUNC_CFx_TriggerTankFire 0x4a0fa0 -#define FUNC_CFx_TriggerWaterSplash 0x4a1070 -#define FUNC_CFx_TriggerBulletSplash 0x4a10e0 -#define FUNC_CFx_TriggerFootSplash 0x4a1150 -#define FUNC_FXSystem_c_AddParticle 0x4AA440 +struct RwRaster; +struct RwMatrix; + +#define FUNC_CFx_AddBlood 0x49eb00 +#define FUNC_CFx_AddWood 0x49ee10 +#define FUNC_CFx_AddSparks 0x49f040 +#define FUNC_CFx_AddTyreBurst 0x49f300 +#define FUNC_CFx_AddBulletImpact 0x49f3d0 +#define FUNC_CFx_AddPunchImpact 0x49f670 +#define FUNC_CFx_AddDebris 0x49f750 +#define FUNC_CFx_AddGlass 0x49f970 +#define FUNC_CFx_TriggerWaterHydrant 0x4a0d70 +#define FUNC_CFx_TriggerGunshot 0x4a0de0 +#define FUNC_CFx_TriggerTankFire 0x4a0fa0 +#define FUNC_CFx_TriggerWaterSplash 0x4a1070 +#define FUNC_CFx_TriggerBulletSplash 0x4a10e0 +#define FUNC_CFx_TriggerFootSplash 0x4a1150 +#define FUNC_FxSystem_c_AddParticle 0x4AA440 + +enum class eFXQuality : std::uint32_t +{ + QUALITY_LOW = 0, + QUALITY_MEDIUM, + QUALITY_HIGH, + QUALITY_VERY_HIGH, +}; + +enum class eFxSystemPlayState : std::uint8_t +{ + PLAYING = 0, + STOPPED, + UNKNOWN, +}; + +enum class eFxSystemKillState : std::uint8_t +{ + NOT_KILLED = 0, + PLAY_AND_KILL, + KILLED, + UNKNOWN, +}; + +class FxSystem_cSAInterface +{ +public: + std::uint32_t m_link[2]; // ListItem_c + CFxSystemBPSAInterface* m_bluePrint; + void* m_transformMatrix; // RwMatrixTag* + std::uint8_t m_baseMatrix[64]; // RwMatrixTag + eFxSystemPlayState m_playState; + eFxSystemKillState m_killState; + bool m_useConstTime; + std::uint8_t field_53[2]; + float m_cameraDistance; + std::uint16_t m_constTime; + std::uint16_t m_rateMult; + std::uint16_t m_timeMult; + + union + { + struct + { + std::uint8_t m_hasOwnMatrix : 1; + std::uint8_t m_local : 1; + std::uint8_t m_useZTest : 1; + std::uint8_t m_stopParticleCreation : 1; + std::uint8_t m_prevCulled : 1; + std::uint8_t m_mustCreateParticles : 1; + }; + + std::uint8_t flags; + }; + + std::uint8_t field_63; + float m_loopInterval; + CVector m_velAdd; + void* m_boundingSphere; // CParticleBounding* or FxSphere_c* + void** m_primsList; // FxPrim_c** + std::uint8_t m_fireAE[136]; // CAEFireAudioEntity +}; class CFxSAInterface { public: - FxSystem_c* m_fxSysBlood; - FxSystem_c* m_fxSysBoatSplash; - FxSystem_c* m_fxSysBubble; - FxSystem_c* m_fxSysDebris; - FxSystem_c* m_fxSysSmoke; - FxSystem_c* m_fxSysGunshell; - FxSystem_c* m_fxSysSand; - FxSystem_c* m_fxSysSand2; - FxSystem_c* m_fxSysSmokeHuge; - FxSystem_c* m_fxSysSmoke2; - FxSystem_c* m_fxSysSpark; - FxSystem_c* m_fxSysSpark2; - FxSystem_c* m_fxSysSplash; - FxSystem_c* m_fxSysWake; - FxSystem_c* m_fxSysWaterSplash; - FxSystem_c* m_fxSysWheelDirt; - FxSystem_c* m_fxSysGlass; + FxSystem_cSAInterface* m_fxSysBlood; + FxSystem_cSAInterface* m_fxSysBoatSplash; + FxSystem_cSAInterface* m_fxSysBubble; + FxSystem_cSAInterface* m_fxSysDebris; + FxSystem_cSAInterface* m_fxSysSmoke; + FxSystem_cSAInterface* m_fxSysGunshell; + FxSystem_cSAInterface* m_fxSysSand; + FxSystem_cSAInterface* m_fxSysSand2; + FxSystem_cSAInterface* m_fxSysSmokeHuge; + FxSystem_cSAInterface* m_fxSysSmoke2; + FxSystem_cSAInterface* m_fxSysSpark; + FxSystem_cSAInterface* m_fxSysSpark2; + FxSystem_cSAInterface* m_fxSysSplash; + FxSystem_cSAInterface* m_fxSysWake; + FxSystem_cSAInterface* m_fxSysWaterSplash; + FxSystem_cSAInterface* m_fxSysWheelDirt; + FxSystem_cSAInterface* m_fxSysGlass; -private: + // List_c + void* m_lastParticleEntity; + void* m_firstParticleEntity; + std::uint32_t m_particleEntitiesCount; + + std::uint32_t m_numCreatedBloodPools; + eFXQuality m_fxQuality; + std::uint32_t m_verticesCount2; + std::uint32_t m_verticesCount; + std::uint32_t m_transformRenderFlags; + RwRaster* m_rasterToRender; + RwMatrix* m_transformLTM; + void* m_verts; // RxObjSpace3DVertex* }; class CFxSA : public CFx @@ -76,7 +153,8 @@ class CFxSA : public CFx void TriggerWaterSplash(CVector& vecPosition); void TriggerBulletSplash(CVector& vecPosition); void TriggerFootSplash(CVector& vecPosition); - void AddParticle(eFxParticleSystems 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); + void AddParticle(eFxParticleSystems 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); private: CFxSAInterface* m_pInterface; diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 07ac611c09..6786f32cea 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -1072,6 +1072,11 @@ void CGameSA::ResetAlphaTransparencies() CModelInfoSA::StaticResetAlphaTransparencies(); } +void CGameSA::ResetModel2DFXEffects() +{ + CModelInfoSA::StaticReset2DFXEffects(); +} + // Disable VSync by forcing what normally happends at the end of the loading screens // Note #1: This causes the D3D device to be reset after the next frame // Note #2: Some players do not need this to disable VSync. (Possibly because their video card driver settings override it somewhere) diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index cf4788d953..edfdce6508 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -288,6 +288,7 @@ class CGameSA : public CGame void ResetModelLodDistances(); void ResetModelFlags(); void ResetAlphaTransparencies(); + void ResetModel2DFXEffects(); void DisableVSync(); void ResetModelTimes(); diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index a8dd17d36b..e04ea28b2e 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -19,6 +19,7 @@ #include "CPedSA.h" #include "CWorldSA.h" #include "gamesa_renderware.h" +#include "CFxSA.h" extern CCoreInterface* g_pCore; extern CGameSA* pGame; @@ -35,7 +36,13 @@ std::map CModelInfo std::unordered_map CModelInfoSA::ms_OriginalObjectPropertiesGroups; std::unordered_map> CModelInfoSA::ms_VehicleModelDefaultWheelSizes; std::map CModelInfoSA::ms_DefaultTxdIDMap; -std::map CModelInfoSA::d2fxEffects; + +std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; +static std::unordered_map m_numCustom2dfxEffects; +static std::vector d2fxEffects; +static std::vector removedDefaultEffects; + +int C2DEffectSA::effect2dPluginOffset = *(int*)0xC3A1E0; // g2dEffectPluginOffset union tIdeFlags { @@ -106,6 +113,12 @@ CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() return m_pInterface = ppModelInfo[m_dwModelID]; } +void CModelInfoSA::SetModelID(DWORD dwModelID) +{ + m_dwModelID = dwModelID; + MapSet(m_numCustom2dfxEffects, ppModelInfo[dwModelID], 0); +} + bool CModelInfoSA::IsBoat() { DWORD dwFunction = FUNC_IsBoatModel; @@ -1170,6 +1183,11 @@ void CModelInfoSA::ResetAlphaTransparency() } } +void CModelInfoSA::StaticReset2DFXEffects() +{ + +} + short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) { short sreturn = -1; @@ -1825,102 +1843,6 @@ void CModelInfoSA::DeallocateModel(void) ppModelInfo[m_dwModelID] = nullptr; *pGame->GetStreaming()->GetStreamingInfo(m_dwModelID) = CStreamingInfo{}; } -////////////////////////////////////////////////////////////////////////////////////////// -// -// Hook for NodeNameStreamRead -// -// Ignore extra characters in dff frame name -// -////////////////////////////////////////////////////////////////////////////////////////// -__declspec(noinline) void OnMY_NodeNameStreamRead(RwStream* stream, char* pDest, uint uiSize) -{ - // Calc sizes - const uint uiMaxBufferSize = 24; - uint uiAmountToRead = std::min(uiMaxBufferSize - 1, uiSize); - uint uiAmountToSkip = uiSize - uiAmountToRead; - - // Read good bit - RwStreamRead(stream, pDest, uiAmountToRead); - pDest[uiAmountToRead] = 0; - - // Skip bad bit (this might not be required) - if (uiAmountToSkip > 0) - RwStreamSkip(stream, uiAmountToSkip); -} - -// Hook info -#define HOOKPOS_NodeNameStreamRead 0x072FA68 -#define HOOKSIZE_NodeNameStreamRead 15 -DWORD RETURN_NodeNameStreamRead = 0x072FA77; -void _declspec(naked) HOOK_NodeNameStreamRead() -{ - _asm - { - pushad - push edi - push esi - push ebx - call OnMY_NodeNameStreamRead - add esp, 4*3 - popad - - jmp RETURN_NodeNameStreamRead - } -} - -#define HOOKPOS_Get2dEffect 0x4C4CD8 -#define HOOKSIZE_Get2dEffect 6 -static constexpr DWORD RETURN_CBaseModelInfo_Get2dEffect = 0x4C4CDE; -int index = -1; -int count = 0; -int offset = *(int*)0xC3A1E0; -void _declspec(naked) CModelInfoSA::HOOK_Get2dEffect() -{ - - _asm - { - mov esi, [esp+8+0xC] - pushad - mov index, esi - mov count, ebp - } - - if (index > count * 64 + offset) - { - // return from vector - auto* effect = MapGet(d2fxEffects, index); - _asm - { - popad - mov eax, effect - pop edi - pop esi - pop ebp - pop ebx - retn 4 - } - } - else - { - _asm - { - popad - cmp esi, ebp - jmp RETURN_CBaseModelInfo_Get2dEffect - } - } -} - -////////////////////////////////////////////////////////////////////////////////////////// -// -// Setup hooks -// -////////////////////////////////////////////////////////////////////////////////////////// -void CModelInfoSA::StaticSetHooks() -{ - EZHookInstall(NodeNameStreamRead); - EZHookInstall(Get2dEffect); -} // Recursive RwFrame children searching function void CModelInfoSA::RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel) @@ -2109,55 +2031,6 @@ bool CModelInfoSA::IsTowableBy(CModelInfo* towingModel) return isTowable; } -C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const e2dEffectType& type) -{ - // Init new effect - C2DEffectSAInterface* effectInterface = new C2DEffectSAInterface(); - effectInterface->position = RwV3d{position.fX, position.fY, position.fZ}; - effectInterface->type = type; - - // Call CBaseModelInfo::Add2dEffect - //((void(__thiscall*)(CBaseModelInfoSAInterface*, C2DEffectSAInterface*))FUNC_CBaseModelInfo_Add2dEffect)(m_pInterface, effectInterface); - m_pInterface->s2DEffectIndex = -1; - - if (m_pInterface->ucNumOf2DEffects) - m_pInterface->ucNumOf2DEffects++; - else - m_pInterface->ucNumOf2DEffects = 1; - - // Save our effect - MapSet(d2fxEffects, m_dwModelID, effectInterface); - - return effectInterface; - - //*(g2dEffectPluginOffset + a3) = _dataAllocatedForAll2dfx; - - /* - // Get 2DFX store - auto* effectStore = reinterpret_cast(ARRAY_2DFXInfoStore); - - // Add item - //C2DEffectSAInterface& obj = effectStore->objects[effectStore->objCount]; - //effectStore->objCount++; - - // Call CBaseModelInfo::Add2dEffect - ((void(__thiscall*)(CBaseModelInfoSAInterface*, C2DEffectSAInterface*))FUNC_CBaseModelInfo_Add2dEffect)(m_pInterface, &obj); - - // Init - obj.position = RwV3d{position.fX, position.fY, position.fZ}; - obj.type = type; - - // Set special flags - //m_pInterface->usFlags |= 0x2000; - - - // modelid = 1337 x = 0.1 y = 0.1 z = 0.75 type = 0 r = 52 g = 177 b = 235 a = 255 corona_name = corona star shadow_name = shad_exp farClip = 100 pointLRange = 18 coronaSize = 2 shadowSize = 10 shadowMultiplier = 40 showMode = 0 enableReflection = 0 flareType = 0 flags = 96, zDist = 0 offX = 0 offY = 0 offZ = 0 - //char* line = "1337 0.1 0.1 0.75 0 52 177 235 255 \"coronastar\" \"shad_exp\" 100 18 2 10 40 0 0 0 96 0 0 0 0"; - //((void(__cdecl*)(char* line))0x5B7670)(line); - - return &obj;*/ -} - ////////////////////////////////////////////////////////////////////////////////////////// // // CModelInfoSA::ForceUnload @@ -2203,3 +2076,368 @@ bool CModelInfoSA::ForceUnload() return true; } + +void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) +{ + if (MapContains(ms_DefaultEffectsMap, m_dwModelID) && MapContains(MapGet(ms_DefaultEffectsMap, m_dwModelID), effect)) + return; + + // Copy an existing default effect + C2DEffectSAInterface* copy = new C2DEffectSAInterface(); + memcpy(copy, effect, sizeof(C2DEffectSAInterface)); + + // Create a copy of textures for the lights + // We must to do this, because C2DEffect::Shutdown removes them + if (copy->type == e2dEffectType::LIGHT) + PrepareTexturesForLightEffect(copy->effect.light.coronaTex, copy->effect.light.shadowTex, effect->effect.light.coronaTex->name, effect->effect.light.shadowTex->name, false); + else if (copy->type == e2dEffectType::ROADSIGN) + { + // Create a copy of text and atomic for the roadsign + // We must to do this, because C2DEffect::Shutdown removes them + std::strncpy(copy->effect.roadsign.text, effect->effect.roadsign.text, 64); + copy->effect.roadsign.atomic = RpAtomicClone(effect->effect.roadsign.atomic); + } + + // Store copy in the map + MapSet(ms_DefaultEffectsMap, m_dwModelID, std::unordered_map{{effect, copy}}); +} + +bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return false; + + if (!MapContains(ms_DefaultEffectsMap, m_dwModelID)) + return false; + + // Restore default effects + auto& map = MapGet(ms_DefaultEffectsMap, m_dwModelID); + for (auto& it = map.begin(); it != map.end();) + { + // Copy data from copied effect to the default + memcpy(it->first, it->second, sizeof(C2DEffectSAInterface)); + + // Increase the counter if this effect was removed + auto& removedEffect = std::find(removedDefaultEffects.begin(), removedDefaultEffects.end(), it->first); + if (removedEffect != removedDefaultEffects.end()) + { + removedDefaultEffects.erase(removedEffect); + m_pInterface->ucNumOf2DEffects++; + } + + // We no longer need a copy + // So delete it + delete it->second; + it = map.erase(it); + } + + // Clear both maps + map.clear(); + ms_DefaultEffectsMap.clear(); + + // Remove all custom effects + if (removeCustomEffects) + { + std::uint32_t numEffects = m_pInterface->ucNumOf2DEffects; + for (std::uint32_t i = 0; i < numEffects; i++) + { + C2DEffectSAInterface* effect = Get2DFXFromIndex(i); + if (!effect) + continue; + + // If this is not custom effect, continue + auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); + if (it == d2fxEffects.end()) + continue; + + m_pInterface->ucNumOf2DEffects--; + MapGet(m_numCustom2dfxEffects, m_pInterface)--; + + Remove2DFX(effect, true); + } + + d2fxEffects.clear(); + } + + return true; +} + +C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const e2dEffectType& type) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return nullptr; + + // Init new effect + C2DEffectSAInterface* effectInterface = new C2DEffectSAInterface(); + effectInterface->position = RwV3d{position.fX, position.fY, position.fZ}; + effectInterface->type = type; + + // Update counters + m_pInterface->ucNumOf2DEffects = m_pInterface->ucNumOf2DEffects ? m_pInterface->ucNumOf2DEffects + 1 : 1; + MapGet(m_numCustom2dfxEffects, m_pInterface)++; + + // Save our effect + d2fxEffects.push_back(effectInterface); + + // Keep original effects + StoreDefault2DFXEffect(effectInterface); + + return effectInterface; +} + +void CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool isCustom) +{ + if (!effect) + return; + + switch (effect->type) + { + case e2dEffectType::ROADSIGN: + case e2dEffectType::LIGHT: + { + // Call C2DEffect::Shutdown + ((void(__thiscall*)(C2DEffectSAInterface*))FUNC_C2DEffect_Shutdown)(effect); + + // Prevent creation when stream in but keep in memory so we can restore it later + effect->type = e2dEffectType::NONE; + break; + } + case e2dEffectType::PARTICLE: + { + auto* fx = reinterpret_cast(VAR_G_Fx); + void* lastParticle = fx->m_lastParticleEntity; + + while (lastParticle) + { + auto** entity = reinterpret_cast(reinterpret_cast(lastParticle) + OFFSET_FxSystem_Entities); + auto* prevParticle = *reinterpret_cast(reinterpret_cast(lastParticle) + OFFSET_FxSystem_Link_Prev); + + if (entity && *entity && static_cast((*entity)->m_nModelIndex) == m_dwModelID) + { + // Call Fx_c::DestroyEntityFx + ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*))FUNC_Fx_c_DestroyEntityFx)(fx, *entity); + + // Prevent creation when stream in but keep in memory so we can restore it later + effect->type = e2dEffectType::NONE; + } + + lastParticle = prevParticle; + } + + break; + } + case e2dEffectType::ESCALATOR: + { + auto* escalators = reinterpret_cast(ARRAY_CEscalators); + for (std::uint8_t i = 0; i < NUM_MAX_ESCALATORS; i++) + { + if (!escalators[i].exist || !escalators[i].entity) + continue; + + if (static_cast(escalators[i].entity->m_nModelIndex) != m_dwModelID) + continue; + + // Call CEscalator::SwitchOff + ((void(__thiscall*)(CEscalatorSAInterface*))FUNC_CEscalator_SwitchOff)(&escalators[i]); + escalators[i].exist = false; + } + + // Prevent creation when stream in but keep in memory so we can restore it later + effect->type = e2dEffectType::NONE; + } + case e2dEffectType::SUN_GLARE: + { + // Prevent from rendering but keep in memory so we can restore it later + effect->type = e2dEffectType::NONE; + break; + } + } + + // If it's custom effect then delete it. If it's default effect then store it as removed + if (isCustom) + { + delete effect; + effect = nullptr; + } + else + removedDefaultEffects.push_back(effect); +} + +bool CModelInfoSA::Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return false; + + C2DEffectSAInterface* effect = Get2DFXFromIndex(index); + if (!effect) + return false; + + auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); + bool isCustomEffect = it != d2fxEffects.end(); + + if (!includeDefault && !isCustomEffect) + return false; + + StoreDefault2DFXEffect(effect); + + m_pInterface->ucNumOf2DEffects--; + if (isCustomEffect) + { + MapGet(m_numCustom2dfxEffects, m_pInterface)--; + d2fxEffects.erase(it); + } + + Remove2DFX(effect, isCustomEffect); + return true; +} + +bool CModelInfoSA::RemoveAll2DFXEffects(bool includeDefault) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return false; + + std::uint32_t numEffects = m_pInterface->ucNumOf2DEffects; + + for (std::uint32_t i = 0; i < numEffects; i++) + { + C2DEffectSAInterface* effect = Get2DFXFromIndex(i); + if (!effect) + continue; + + auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); + bool isCustomEffect = it != d2fxEffects.end(); + if (!includeDefault && !isCustomEffect) + continue; + + StoreDefault2DFXEffect(effect); + + m_pInterface->ucNumOf2DEffects--; + if (isCustomEffect) + MapGet(m_numCustom2dfxEffects, m_pInterface)--; + + Remove2DFX(effect, isCustomEffect); + } + + d2fxEffects.clear(); + + return true; +} + +C2DEffectSAInterface* CModelInfoSA::Get2DFXFromIndex(std::uint32_t index) +{ + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return nullptr; + + // Call CBaseModelInfo::Get2dEffect + return ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(m_pInterface, index); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Hook for NodeNameStreamRead +// +// Ignore extra characters in dff frame name +// +////////////////////////////////////////////////////////////////////////////////////////// +static __declspec(noinline) void OnMY_NodeNameStreamRead(RwStream* stream, char* pDest, uint uiSize) +{ + // Calc sizes + const uint uiMaxBufferSize = 24; + uint uiAmountToRead = std::min(uiMaxBufferSize - 1, uiSize); + uint uiAmountToSkip = uiSize - uiAmountToRead; + + // Read good bit + RwStreamRead(stream, pDest, uiAmountToRead); + pDest[uiAmountToRead] = 0; + + // Skip bad bit (this might not be required) + if (uiAmountToSkip > 0) + RwStreamSkip(stream, uiAmountToSkip); +} + +// Hook info +#define HOOKPOS_NodeNameStreamRead 0x072FA68 +#define HOOKSIZE_NodeNameStreamRead 15 +DWORD RETURN_NodeNameStreamRead = 0x072FA77; +static void _declspec(naked) HOOK_NodeNameStreamRead() +{ + _asm + { + pushad + push edi + push esi + push ebx + call OnMY_NodeNameStreamRead + add esp, 4*3 + popad + + jmp RETURN_NodeNameStreamRead + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Hook for CBaseModelInfo:Get2dEffect +// +// Handle custom 2dfx's +// +////////////////////////////////////////////////////////////////////////////////////////// +static C2DEffectSAInterface* Get2dEffect(CBaseModelInfoSAInterface* modelInfo, RpGeometry* geometry, std::uint32_t numPluginEffects, std::uint32_t index) +{ + if (!geometry) + numPluginEffects = 0; + + static auto* storedEffects = reinterpret_cast(ARRAY_2DFXInfoStore); + + std::uint32_t numCustomEffects = m_numCustom2dfxEffects[modelInfo]; + std::uint32_t numStoredEffects = modelInfo->ucNumOf2DEffects - numPluginEffects - numCustomEffects; + + if (index < numStoredEffects) + return &storedEffects->objects[index + modelInfo->s2DEffectIndex]; + else if (index < numStoredEffects + numPluginEffects) + { + auto* pluginEffectData = *RWPLUGINOFFSET(C2DEffectPluginDataSAInterface*, geometry, C2DEffectSA::effect2dPluginOffset); + return &pluginEffectData->objects[index - numStoredEffects]; + } + else + return d2fxEffects[index - numPluginEffects - numStoredEffects]; + + return nullptr; +} + +#define HOOKPOS_Get2dEffect 0x4C4CDC +#define HOOKSIZE_Get2dEffect 10 +static void _declspec(naked) HOOK_Get2dEffect() +{ + _asm + { + push esi + push eax + push edi + push ebx + call Get2dEffect + add esp, 16 + + pop edi + pop esi + pop ebp + pop ebx + retn 4 + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Setup hooks +// +////////////////////////////////////////////////////////////////////////////////////////// +void CModelInfoSA::StaticSetHooks() +{ + EZHookInstall(NodeNameStreamRead); + EZHookInstall(Get2dEffect); +} diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 1df15573ae..4026b66e18 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -64,7 +64,7 @@ static void* ARRAY_ModelInfo = *(void**)(0x403DA4 + 3); #define VAR_CTempColModels_ModelPed1 0x968DF0 -#define FUNC_CBaseModelInfo_Add2dEffect 0x4C4D20 +#define FUNC_CBaseModelInfo_Get2dEffect 0x4C4C70 class CBaseModelInfoSAInterface; class CModelInfoSAInterface @@ -332,8 +332,8 @@ class CModelInfoSA : public CModelInfo static std::unordered_map ms_OriginalObjectPropertiesGroups; static std::unordered_map> ms_VehicleModelDefaultWheelSizes; static std::map ms_DefaultTxdIDMap; - static std::map d2fxEffects; SVehicleSupportedUpgrades m_ModelSupportedUpgrades; + static std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; public: CModelInfoSA(); @@ -390,7 +390,7 @@ class CModelInfoSA : public CModelInfo static void StaticResetLodDistances(); void RestreamIPL(); static void StaticFlushPendingRestreamIPL(); - static void StaticSetHooks(); + static void StaticSetHooks(); bool GetTime(char& cHourOn, char& cHourOff); bool SetTime(char cHourOn, char cHourOff); static void StaticResetModelTimes(); @@ -444,7 +444,7 @@ class CModelInfoSA : public CModelInfo // Decreases the collision slot reference counter for the original collision model void RemoveColRef() override; - void SetModelID(DWORD dwModelID) { m_dwModelID = dwModelID; } + void SetModelID(DWORD dwModelID); RwObject* GetRwObject() { return m_pInterface ? m_pInterface->pRwObject : NULL; } @@ -457,8 +457,6 @@ class CModelInfoSA : public CModelInfo void DeallocateModel(void); unsigned int GetParentID() { return m_dwParentID; }; - static void HOOK_Get2dEffect(); - SVehicleSupportedUpgrades GetVehicleSupportedUpgrades() { return m_ModelSupportedUpgrades; } void InitialiseSupportedUpgrades(RpClump* pClump); @@ -474,6 +472,15 @@ class CModelInfoSA : public CModelInfo // 2DFX functions C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type); + void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false); + bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false); + bool RemoveAll2DFXEffects(bool includeDefault); + C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index); + std::uint32_t Get2DFXCount() const { return m_pInterface ? m_pInterface->ucNumOf2DEffects : 0; } + + void StoreDefault2DFXEffect(C2DEffectSAInterface* effect); + bool Reset2DFXEffects(bool removeCustomEffects = false); + static void StaticReset2DFXEffects(); bool IsDynamic() { return m_pInterface ? m_pInterface->usDynamicIndex != 0xffff : false; }; @@ -482,3 +489,5 @@ class CModelInfoSA : public CModelInfo void RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel); void SetModelSpecialType(eModelSpecialType eType, bool bState); }; + +void HOOK_NodeNameStreamRead(); diff --git a/Client/mods/deathmatch/logic/CClient2DFX.cpp b/Client/mods/deathmatch/logic/CClient2DFX.cpp index 74988af2db..8aac2a8fe2 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFX.cpp @@ -10,11 +10,9 @@ #include "StdInc.h" #include "CClient2DFX.h" #include "CClient2DFXManager.h" -#include "../game_sa/C2DEffectSAInterface.h" -#define mask(n) ((1 << (n)) - 1) - -CClient2DFX::CClient2DFX(class CClientManager* manager, ElementID ID) : ClassInit(this), CClientEntity(ID), m_2DFXManager(manager->Get2DFXManager()) +CClient2DFX::CClient2DFX(class CClientManager* manager, ElementID ID) + : ClassInit(this), CClientEntity(ID), m_2DFXManager(manager->Get2DFXManager()), m_effectInterface(nullptr), m_effectType(e2dEffectType::NONE) { m_pManager = manager; SetTypeName("2dfx"); @@ -39,163 +37,10 @@ bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2d // Set effect m_effectInterface = effect; + m_effectType = effect->type; - // Push current TXD - ((void(__cdecl*)())0x7316A0)(); - - // Find txd slot - int slot = ((int(__cdecl*)(const char*))0x731850)("particle"); - - // Set txd slot - ((void(__cdecl*)(int))0x7319C0)(slot); - - // Set properties - switch (type) - { - case e2dEffectType::LIGHT: - { - t2dEffectLight& light = effect->effect.light; - - auto* drawDistance = MapFind(effectData, "draw_distance"); - light.coronaFarClip = std::get(*drawDistance); - - auto* lightRange = MapFind(effectData, "light_range"); - light.pointLightRange = std::get(*lightRange); - - auto* coronaSize = MapFind(effectData, "corona_size"); - light.coronaSize = std::get(*coronaSize); - - auto* shadowSize = MapFind(effectData, "shadow_size"); - light.shadowSize = std::get(*shadowSize); - - auto* shadowMultiplier = MapFind(effectData, "shadow_multiplier"); - light.shadowColorMultiplier = static_cast(std::get(*shadowMultiplier)); - - auto* showMode = MapFind(effectData, "show_mode"); - e2dCoronaFlashType flashType; - StringToEnum(std::get(*showMode), flashType); - light.coronaFlashType = flashType; - - auto* coronaReflection = MapFind(effectData, "corona_reflection"); - light.coronaEnableReflection = std::get(*coronaReflection); - - auto* coronaFlareType = MapFind(effectData, "flare_type"); - light.coronaFlareType = static_cast(std::get(*coronaFlareType)); - - auto* flags = MapFind(effectData, "flags"); - light.flags = static_cast(std::get(*flags)); - - auto* shadowZDistance = MapFind(effectData, "shadow_distance"); - light.shadowZDistance = static_cast(std::get(*shadowZDistance)); - - auto* offsetX = MapFind(effectData, "offsetX"); - light.offsetX = static_cast(std::get(*offsetX)); - - auto* offsetY = MapFind(effectData, "offsetY"); - light.offsetY = static_cast(std::get(*offsetY)); - - auto* offsetZ = MapFind(effectData, "offsetZ"); - light.offsetZ = static_cast(std::get(*offsetZ)); - - auto* color = MapFind(effectData, "color"); - int colorValue = static_cast(std::get(*color)); - light.color = RwColor{static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; - - auto* coronaTexture = MapFind(effectData, "corona_name"); - light.coronaTex = ((RwTexture * (__cdecl*)(const char*, const char*))0x7F3AC0)(std::get(*coronaTexture).c_str(), nullptr); - - auto* shadowTexture = MapFind(effectData, "shadow_name"); - light.shadowTex = ((RwTexture * (__cdecl*)(const char*, const char*))0x7F3AC0)(std::get(*shadowTexture).c_str(), nullptr); - - // ??? - if (light.flags & 4) // withoutCorona - effect->effect.particle.szName[20] &= ~2u; - - break; - } - case e2dEffectType::PARTICLE: - { - auto* particleName = MapFind(effectData, "name"); - auto& stringName = std::get(*particleName); - std::strncpy(effect->effect.particle.szName, stringName.c_str(), sizeof(effect->effect.particle.szName) - 1); - - break; - } - case e2dEffectType::ROADSIGN: - { - t2dEffectRoadsign& roadsign = effect->effect.roadsign; - - auto* sizeX = MapFind(effectData, "sizeX"); - auto* sizeY = MapFind(effectData, "sizeY"); - roadsign.size = RwV2d{std::get(*sizeX), std::get(*sizeY)}; - - auto* rotX = MapFind(effectData, "rotX"); - auto* rotY = MapFind(effectData, "rotY"); - auto* rotZ = MapFind(effectData, "rotZ"); - roadsign.rotation = RwV3d{std::get(*rotX), std::get(*rotY), std::get(*rotZ)}; - - auto* flags = MapFind(effectData, "flags"); - roadsign.flags = static_cast(std::get(*flags)); - - auto* text = MapFind(effectData, "text"); - auto* text2 = MapFind(effectData, "text2"); - auto* text3 = MapFind(effectData, "text3"); - auto* text4 = MapFind(effectData, "text4"); - - roadsign.text = static_cast(malloc(64)); - if (roadsign.text) - { - std::memcpy(roadsign.text, std::get(*text).c_str(), 4); - std::memcpy(roadsign.text + 4, std::get(*text2).c_str(), 4); - std::memcpy(roadsign.text + 8, std::get(*text3).c_str(), 4); - std::memcpy(roadsign.text + 12, std::get(*text4).c_str(), 4); - } - - roadsign.atomic = nullptr; - - break; - } - case e2dEffectType::TRIGGER_POINT: - { - auto* triggerID = MapFind(effectData, "id"); - effect->effect.triggerPoint.id = static_cast(std::get(*triggerID)); - - break; - } - case e2dEffectType::ESCALATOR: - { - t2dEffectEscalator& escalator = effect->effect.escalator; - - auto* bottomX = MapFind(effectData, "bottomX"); - auto* bottomY = MapFind(effectData, "bottomY"); - auto* bottomZ = MapFind(effectData, "bottomZ"); - escalator.bottom = RwV3d{std::get(*bottomX), std::get(*bottomY), std::get(*bottomZ)}; - - auto* topX = MapFind(effectData, "topX"); - auto* topY = MapFind(effectData, "topY"); - auto* topZ = MapFind(effectData, "topZ"); - escalator.top = RwV3d{std::get(*topX), std::get(*topY), std::get(*topZ)}; - - auto* endX = MapFind(effectData, "endX"); - auto* endY = MapFind(effectData, "endY"); - auto* endZ = MapFind(effectData, "endZ"); - escalator.end = RwV3d{std::get(*endX), std::get(*endY), std::get(*endZ)}; - - auto* dir = MapFind(effectData, "direction"); - escalator.direction = static_cast(std::get(*dir)); - - break; - } - case e2dEffectType::ATTRACTOR: - case e2dEffectType::ENEX: - case e2dEffectType::FURNITURE: - case e2dEffectType::COVER_POINT: - case e2dEffectType::SUN_GLARE: - break; - } - - // Pop current txd - ((void(__cdecl*)())0x7316B0)(); + if (!m_2DFXManager->Set2DFXProperties(effect, effectData)) + return false; return true; } diff --git a/Client/mods/deathmatch/logic/CClient2DFX.h b/Client/mods/deathmatch/logic/CClient2DFX.h index c94e2ea46f..a77b529d68 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.h +++ b/Client/mods/deathmatch/logic/CClient2DFX.h @@ -22,12 +22,16 @@ class CClient2DFX final : public CClientEntity eClientEntityType GetType() const { return CCLIENT2DFX; } void Unlink(){}; - void GetPosition(CVector& vecPosition) const {}; - void SetPosition(const CVector& vecPosition){}; + void GetPosition(CVector& vecPosition) const {} + void SetPosition(const CVector& vecPosition){} bool Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData); + e2dEffectType Get2DFXType() const noexcept { return m_effectType; } + C2DEffectSAInterface* Get2DFX() const noexcept { return m_effectInterface; } + private: class CClient2DFXManager* m_2DFXManager; C2DEffectSAInterface* m_effectInterface; + e2dEffectType m_effectType; }; diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index 4b6ff19fce..e4c7bc81f8 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -10,6 +10,8 @@ #include "StdInc.h" #include "CClient2DFXManager.h" +#define mask(n) ((1 << (n)) - 1) + CClient2DFXManager::CClient2DFXManager(CClientManager* mainManager) : m_mainManager(mainManager) { // Init @@ -53,11 +55,255 @@ CClient2DFX* CClient2DFXManager::Add2DFX(std::uint32_t model, const CVector& pos return nullptr; if (!effect->Create(model, position, type, effectData)) - return nullptr; + { + delete effect; + effect = nullptr; + } return effect; } +bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, std::unordered_map>& effectData) +{ + if (!effect) + return false; + + // Set properties + switch (effect->type) + { + case e2dEffectType::LIGHT: + { + + t2dEffectLight& light = effect->effect.light; + + auto* drawDistance = MapFind(effectData, "draw_distance"); + light.coronaFarClip = std::get(*drawDistance); + + auto* lightRange = MapFind(effectData, "light_range"); + light.pointLightRange = std::get(*lightRange); + + auto* coronaSize = MapFind(effectData, "corona_size"); + light.coronaSize = std::get(*coronaSize); + + auto* shadowSize = MapFind(effectData, "shadow_size"); + light.shadowSize = std::get(*shadowSize); + + auto* shadowMultiplier = MapFind(effectData, "shadow_multiplier"); + light.shadowColorMultiplier = static_cast(std::get(*shadowMultiplier)); + + auto* showMode = MapFind(effectData, "show_mode"); + e2dCoronaFlashType flashType; + StringToEnum(std::get(*showMode), flashType); + light.coronaFlashType = flashType; + + auto* coronaReflection = MapFind(effectData, "corona_reflection"); + light.coronaEnableReflection = std::get(*coronaReflection); + + auto* coronaFlareType = MapFind(effectData, "flare_type"); + light.coronaFlareType = static_cast(std::get(*coronaFlareType)); + + auto* flags = MapFind(effectData, "flags"); + light.flags = static_cast(std::get(*flags)); + + auto* shadowZDistance = MapFind(effectData, "shadow_distance"); + light.shadowZDistance = static_cast(std::get(*shadowZDistance)); + + auto* offsetX = MapFind(effectData, "offsetX"); + light.offsetX = static_cast(std::get(*offsetX)); + + auto* offsetY = MapFind(effectData, "offsetY"); + light.offsetY = static_cast(std::get(*offsetY)); + + auto* offsetZ = MapFind(effectData, "offsetZ"); + light.offsetZ = static_cast(std::get(*offsetZ)); + + auto* color = MapFind(effectData, "color"); + int colorValue = static_cast(std::get(*color)); + light.color = RwColor{static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; + + auto* coronaTexture = MapFind(effectData, "corona_name"); + auto* shadowTexture = MapFind(effectData, "shadow_name"); + PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(*coronaTexture).c_str(), std::get(*shadowTexture).c_str(), true); + break; + } + case e2dEffectType::PARTICLE: + { + auto* particleName = MapFind(effectData, "name"); + auto& stringName = std::get(*particleName); + std::strncpy(effect->effect.particle.szName, stringName.c_str(), sizeof(effect->effect.particle.szName) - 1); + + break; + } + case e2dEffectType::ROADSIGN: + { + t2dEffectRoadsign& roadsign = effect->effect.roadsign; + + auto* sizeX = MapFind(effectData, "sizeX"); + auto* sizeY = MapFind(effectData, "sizeY"); + roadsign.size = RwV2d{std::get(*sizeX), std::get(*sizeY)}; + + auto* rotX = MapFind(effectData, "rotX"); + auto* rotY = MapFind(effectData, "rotY"); + auto* rotZ = MapFind(effectData, "rotZ"); + roadsign.rotation = RwV3d{std::get(*rotX), std::get(*rotY), std::get(*rotZ)}; + + auto* flags = MapFind(effectData, "flags"); + roadsign.flags = static_cast(std::get(*flags)); + + auto* text = MapFind(effectData, "text"); + auto* text2 = MapFind(effectData, "text2"); + auto* text3 = MapFind(effectData, "text3"); + auto* text4 = MapFind(effectData, "text4"); + + roadsign.text = static_cast(malloc(64)); + if (roadsign.text) + { + std::memcpy(roadsign.text, std::get(*text).c_str(), 4); + std::memcpy(roadsign.text + 4, std::get(*text2).c_str(), 4); + std::memcpy(roadsign.text + 8, std::get(*text3).c_str(), 4); + std::memcpy(roadsign.text + 12, std::get(*text4).c_str(), 4); + } + + roadsign.atomic = nullptr; + + break; + } + case e2dEffectType::ESCALATOR: + { + t2dEffectEscalator& escalator = effect->effect.escalator; + + auto* bottomX = MapFind(effectData, "bottomX"); + auto* bottomY = MapFind(effectData, "bottomY"); + auto* bottomZ = MapFind(effectData, "bottomZ"); + escalator.bottom = RwV3d{std::get(*bottomX), std::get(*bottomY), std::get(*bottomZ)}; + + auto* topX = MapFind(effectData, "topX"); + auto* topY = MapFind(effectData, "topY"); + auto* topZ = MapFind(effectData, "topZ"); + escalator.top = RwV3d{std::get(*topX), std::get(*topY), std::get(*topZ)}; + + auto* endX = MapFind(effectData, "endX"); + auto* endY = MapFind(effectData, "endY"); + auto* endZ = MapFind(effectData, "endZ"); + escalator.end = RwV3d{std::get(*endX), std::get(*endY), std::get(*endZ)}; + + auto* dir = MapFind(effectData, "direction"); + escalator.direction = static_cast(std::get(*dir)); + + break; + } + case e2dEffectType::ATTRACTOR: + case e2dEffectType::ENEX: + case e2dEffectType::FURNITURE: + case e2dEffectType::COVER_POINT: + case e2dEffectType::TRIGGER_POINT: + case e2dEffectType::SUN_GLARE: // This effect has no properties but works in MTA + break; + } + + return true; +} + +std::unordered_map> CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect) +{ + auto properties = std::unordered_map>(); + if (!effect) + return properties; + + // Get properties + switch (effect->type) + { + case e2dEffectType::LIGHT: + { + const t2dEffectLight& light = effect->effect.light; + + MapSet(properties, "draw_distance", light.coronaFarClip); + MapSet(properties, "light_range", light.pointLightRange); + MapSet(properties, "corona_size", light.coronaSize); + MapSet(properties, "shadow_size", light.shadowSize); + MapSet(properties, "shadow_multiplier", light.shadowColorMultiplier); + MapSet(properties, "show_mode", EnumToString(light.coronaFlashType)); + MapSet(properties, "corona_reflection", light.coronaEnableReflection); + MapSet(properties, "flare_type", light.coronaFlareType); + MapSet(properties, "flags", light.flags); + MapSet(properties, "shadow_distance", light.shadowZDistance); + MapSet(properties, "offsetX", light.offsetX); + MapSet(properties, "offsetY", light.offsetY); + MapSet(properties, "offsetZ", light.offsetZ); + std::uint32_t colorValue = (static_cast(light.color.r) << 0) | + (static_cast(light.color.g) << 8) | + (static_cast(light.color.b) << 16) | + (static_cast(light.color.a) << 24); + MapSet(properties, "color", colorValue); + MapSet(properties, "corona_name", !light.coronaTex ? "" : static_cast(light.coronaTex->name)); + MapSet(properties, "shadow_name", !light.shadowTex ? "" : static_cast(light.shadowTex->name)); + + break; + } + case e2dEffectType::PARTICLE: + { + MapSet(properties, "name", static_cast(effect->effect.particle.szName)); + break; + } + case e2dEffectType::ROADSIGN: + { + const t2dEffectRoadsign& roadsign = effect->effect.roadsign; + + MapSet(properties, "sizeX", roadsign.size.x); + MapSet(properties, "sizeY", roadsign.size.y); + MapSet(properties, "rotX", roadsign.rotation.x); + MapSet(properties, "rotY", roadsign.rotation.y); + MapSet(properties, "rotZ", roadsign.rotation.z); + MapSet(properties, "flags", roadsign.flags); + MapSet(properties, "text", !roadsign.text ? "" : roadsign.text); + + break; + } + case e2dEffectType::ESCALATOR: + { + const t2dEffectEscalator& escalator = effect->effect.escalator; + + MapSet(properties, "bottomX", escalator.bottom.x); + MapSet(properties, "bottomY", escalator.bottom.y); + MapSet(properties, "bottomZ", escalator.bottom.z); + MapSet(properties, "topX", escalator.top.x); + MapSet(properties, "topY", escalator.top.y); + MapSet(properties, "topZ", escalator.top.z); + MapSet(properties, "endX", escalator.end.x); + MapSet(properties, "endY", escalator.end.y); + MapSet(properties, "endZ", escalator.end.z); + MapSet(properties, "direction", escalator.direction); + + break; + } + case e2dEffectType::ATTRACTOR: + case e2dEffectType::ENEX: + case e2dEffectType::FURNITURE: + case e2dEffectType::COVER_POINT: + case e2dEffectType::TRIGGER_POINT: + case e2dEffectType::SUN_GLARE: + break; + } + + return properties; +} + +void CClient2DFXManager::Set2DFXPosition(C2DEffectSAInterface* effect, const CVector& position) +{ + if (!effect) + return; + + effect->position = RwV3d{position.fX, position.fY, position.fZ}; +} + +CVector* CClient2DFXManager::Get2DFXPosition(C2DEffectSAInterface* effect) +{ + if (!effect) + return nullptr; + + return reinterpret_cast(&effect->position); +} + const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectType, std::unordered_map>& effectData) { // Check if keys & values are ok! @@ -143,18 +389,6 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp break; } - case e2dEffectType::ATTRACTOR: - // Unnecessary in MTA - break; - case e2dEffectType::SUN_GLARE: - // It has no properties, it only uses position and type fields - break; - case e2dEffectType::FURNITURE: - // Unnecessary in MTA (Probably unused even in SA) - break; - case e2dEffectType::ENEX: - // Unnecessary in MTA - break; case e2dEffectType::ROADSIGN: { auto* sizeX = MapFind(effectData, "sizeX"); @@ -199,17 +433,6 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp break; } - case e2dEffectType::TRIGGER_POINT: - { - auto* id = MapFind(effectData, "id"); - if (!id || !std::holds_alternative(*id)) - return "Invalid \"id\" value"; - - break; - } - case e2dEffectType::COVER_POINT: - // Unnecessary in MTA - break; case e2dEffectType::ESCALATOR: { auto* bottomX = MapFind(effectData, "bottomX"); @@ -254,7 +477,32 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp break; } + case e2dEffectType::SUN_GLARE: + // It has no properties, it only uses position and type fields + break; + case e2dEffectType::ATTRACTOR: + // Unnecessary in MTA + break; + case e2dEffectType::FURNITURE: + // Unnecessary in MTA (Probably unused even in SA) + break; + case e2dEffectType::ENEX: + // Unnecessary in MTA + break; + case e2dEffectType::TRIGGER_POINT: + { + // Unnecessary in MTA + break; + } + case e2dEffectType::COVER_POINT: + // Unnecessary in MTA + break; } return nullptr; } + +bool CClient2DFXManager::IsValidModel(std::uint32_t model) noexcept +{ + return CClientObjectManager::IsValidModel(model) || CClientBuildingManager::IsValidModel(model) || CClientVehicleManager::IsValidModel(model); +} diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.h b/Client/mods/deathmatch/logic/CClient2DFXManager.h index 554246ce48..3c452f0e9c 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.h +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.h @@ -9,6 +9,7 @@ #pragma once #include "CClient2DFX.h" +#include "../game_sa/C2DEffectSAInterface.h" class CClient2DFXManager { @@ -23,7 +24,14 @@ class CClient2DFXManager CClient2DFX* Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData); - static const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectType, std::unordered_map>& effectData); + bool Set2DFXProperties(C2DEffectSAInterface* effect, std::unordered_map>& effectData); + std::unordered_map> Get2DFXProperties(C2DEffectSAInterface* effect); + + void Set2DFXPosition(C2DEffectSAInterface* effect, const CVector& position); + CVector* Get2DFXPosition(C2DEffectSAInterface* effect); + + static bool IsValidModel(std::uint32_t model) noexcept; + static const char* IsValidEffectData(const e2dEffectType& effectType, std::unordered_map>& effectData); private: void AddToList(CClient2DFX* effect) { m_effectsList.push_back(effect); } diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index bc20947eac..03787ca567 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -3430,6 +3430,7 @@ void CClientGame::Event_OnIngame() g_pGame->ResetModelFlags(); g_pGame->ResetAlphaTransparencies(); g_pGame->ResetModelTimes(); + g_pGame->ResetModel2DFXEffects(); // Make sure we can access all areas g_pGame->GetStats()->ModifyStat(CITIES_PASSED, 2.0); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 052b69c2e5..52eef08634 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -147,6 +147,17 @@ void CLuaEngineDefs::LoadFunctions() {"engineSetPoolCapacity", ArgumentParser}, {"enginePreloadWorldArea", ArgumentParser}, {"engineAddModel2DFX", ArgumentParser}, + {"engineRemoveModel2DFX", ArgumentParser}, + {"engineSetModel2DFXProperties", ArgumentParser}, + {"engineSet2DFXProperties", ArgumentParser}, + {"engineGetModel2DFXProperties", ArgumentParser}, + {"engineGet2DFXProperties", ArgumentParser}, + {"engineSet2DFXPosition", ArgumentParser}, + {"engineSetModel2DFXPosition", ArgumentParser}, + {"engineGet2DFXPosition", ArgumentParser}, + {"engineGetModel2DFXPosition", ArgumentParser}, + {"engineGetModel2DFXCount", ArgumentParser}, + {"engineResetModel2DFXEffects", ArgumentParser}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); @@ -221,6 +232,7 @@ void CLuaEngineDefs::AddClass(lua_State* luaVM) AddEngineTxdClass(luaVM); AddEngineDffClass(luaVM); AddEngineImgClass(luaVM); + AddEngine2DFXClass(luaVM); } void CLuaEngineDefs::AddEngineColClass(lua_State* luaVM) @@ -273,6 +285,24 @@ void CLuaEngineDefs::AddEngineDffClass(lua_State* luaVM) lua_registerclass(luaVM, "EngineDFF", "Element"); } +void CLuaEngineDefs::AddEngine2DFXClass(lua_State* luaVM) +{ + lua_newclass(luaVM); + + // set functions + lua_classfunction(luaVM, "setProperties", "engineSet2DFXProperties"); + lua_classfunction(luaVM, "setPosition", "engineSet2DFXPosition"); + + // get functions + lua_classfunction(luaVM, "getProperties", "engineGet2DFXProperties"); + lua_classfunction(luaVM, "getPosition", "engineGet2DFXPosition"); + + lua_classvariable(luaVM, "properties", "engineSet2DFXProperties", "engineGet2DFXProperties"); + lua_classvariable(luaVM, "position", "engineSet2DFXPosition", "engineGet2DFXPosition"); + + lua_registerclass(luaVM, "Effect2DFX", "Element"); +} + int CLuaEngineDefs::EngineLoadCOL(lua_State* luaVM) { SString input; @@ -2564,7 +2594,12 @@ void CLuaEngineDefs::EnginePreloadWorldArea(CVector position, std::optional CLuaEngineDefs::EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, std::unordered_map> effectData) { - if (!CClientObjectManager::IsValidModel(modelID) && !CClientVehicleManager::IsValidModel(modelID) && !CClientBuildingManager::IsValidModel(modelID)) + // Only these effects make sense in MTA + if (effectType != e2dEffectType::LIGHT && effectType != e2dEffectType::PARTICLE && effectType != e2dEffectType::ROADSIGN && + effectType != e2dEffectType::ESCALATOR && effectType != e2dEffectType::SUN_GLARE) + return false; + + if (!CClient2DFXManager::IsValidModel(modelID)) throw std::invalid_argument("Invalid model ID"); const char* error = CClient2DFXManager::IsValidEffectData(effectType, effectData); @@ -2581,3 +2616,163 @@ std::variant CLuaEngineDefs::EngineAddModel2DFX(lua_State* l return effect; } + +bool CLuaEngineDefs::EngineRemoveModel2DFX(std::uint32_t modelID, std::optional index, std::optional includeDefault) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + if (index.has_value()) + { + auto count = EngineGetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + } + + return index.has_value() ? modelInfo->Remove2DFXEffectAtIndex(index.value(), includeDefault.value_or(false)) : modelInfo->RemoveAll2DFXEffects(includeDefault.value_or(false)); +} + +bool CLuaEngineDefs::EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, std::unordered_map> effectData) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = EngineGetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + const char* error = CClient2DFXManager::IsValidEffectData(effect->type, effectData); + if (error) + throw std::invalid_argument(error); + + modelInfo->StoreDefault2DFXEffect(effect); + return m_p2DFXManager->Set2DFXProperties(effect, effectData); +} + +bool CLuaEngineDefs::EngineSet2DFXProperties(CClient2DFX* effect, std::unordered_map> effectData) +{ + const char* error = CClient2DFXManager::IsValidEffectData(effect->Get2DFXType(), effectData); + if (error) + throw std::invalid_argument(error); + + return m_p2DFXManager->Set2DFXProperties(effect->Get2DFX(), effectData); +} + +bool CLuaEngineDefs::EngineSetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = EngineGetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + modelInfo->StoreDefault2DFXEffect(effect); + m_p2DFXManager->Set2DFXPosition(effect, position); + return true; +} + +bool CLuaEngineDefs::EngineSet2DFXPosition(CClient2DFX* effect, CVector position) +{ + m_p2DFXManager->Set2DFXPosition(effect->Get2DFX(), position); + return true; +} + +std::variant> CLuaEngineDefs::EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = EngineGetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + CVector* position = m_p2DFXManager->Get2DFXPosition(effect); + return std::make_tuple(position->fX, position->fY, position->fZ); +} + +std::variant> CLuaEngineDefs::EngineGet2DFXPosition(CClient2DFX* effect) +{ + CVector* position = m_p2DFXManager->Get2DFXPosition(effect->Get2DFX()); + if (!position) + return false; + + return std::make_tuple(position->fX, position->fY, position->fZ); +} + +std::variant>> CLuaEngineDefs::EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = EngineGetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + return m_p2DFXManager->Get2DFXProperties(effect); +} + +std::variant>> CLuaEngineDefs::EngineGet2DFXProperties(CClient2DFX* effect) +{ + return m_p2DFXManager->Get2DFXProperties(effect->Get2DFX()); +} + +std::variant CLuaEngineDefs::EngineGetModel2DFXCount(std::uint32_t modelID) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + return modelInfo->Get2DFXCount(); +} + +bool CLuaEngineDefs::EngineResetModel2DFXEffects(std::uint32_t modelID, std::optional removeCustomEffects) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + return modelInfo->Reset2DFXEffects(removeCustomEffects.value_or(true)); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index f8a287e573..de74cae529 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -94,11 +94,27 @@ class CLuaEngineDefs : public CLuaDefs static void EnginePreloadWorldArea(CVector position, std::optional option); - static std::variant EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, std::unordered_map> effectData); + static std::variant EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, + std::unordered_map> effectData); + static bool EngineRemoveModel2DFX(std::uint32_t modelID, std::optional index, std::optional includeDefault); + static bool EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, + std::unordered_map> effectData); + static bool EngineSet2DFXProperties(CClient2DFX* effect, std::unordered_map> effectData); + static bool EngineSetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position); + static bool EngineSet2DFXPosition(CClient2DFX* effect, CVector position); + static std::variant> EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index); + static std::variant> EngineGet2DFXPosition(CClient2DFX* effect); + static std::variant>> + EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index); + static std::variant>> + EngineGet2DFXProperties(CClient2DFX* effect); + static std::variant EngineGetModel2DFXCount(std::uint32_t modelID); + static bool EngineResetModel2DFXEffects(std::uint32_t modelID, std::optional removeCustomEffects); private: static void AddEngineColClass(lua_State* luaVM); static void AddEngineTxdClass(lua_State* luaVM); static void AddEngineDffClass(lua_State* luaVM); static void AddEngineImgClass(lua_State* luaVM); + static void AddEngine2DFXClass(lua_State* luaVM); }; diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index 2314fdad0e..412f68841d 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -246,6 +246,7 @@ class __declspec(novtable) CGame virtual void ResetModelLodDistances() = 0; virtual void ResetModelFlags() = 0; virtual void ResetAlphaTransparencies() = 0; + virtual void ResetModel2DFXEffects() = 0; virtual void DisableVSync() = 0; virtual void ResetModelTimes() = 0; diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 6db5f13bd8..4c56b0807d 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -97,6 +97,8 @@ enum class e2dEffectType : std::uint8_t TRIGGER_POINT, COVER_POINT, ESCALATOR, + + NONE, }; enum class e2dCoronaFlashType : std::uint8_t @@ -297,6 +299,15 @@ class CModelInfo // 2dfx functions virtual C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type) = 0; + virtual void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false) = 0; + virtual bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false) = 0; + virtual bool RemoveAll2DFXEffects(bool includeDefault = false) = 0; + + virtual C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index) = 0; + virtual std::uint32_t Get2DFXCount() const = 0; + + virtual void StoreDefault2DFXEffect(C2DEffectSAInterface* effect) = 0; + virtual bool Reset2DFXEffects(bool removeCustomEffects = false) = 0; virtual unsigned int GetParentID() = 0; virtual bool IsDynamic() = 0; From 738c13d0d08a4c03edbaf2b89abd9ac265a87dd9 Mon Sep 17 00:00:00 2001 From: FileEX Date: Fri, 20 Sep 2024 22:09:41 +0200 Subject: [PATCH 04/17] Spaces --- Client/game_sa/CModelInfoSA.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 4026b66e18..f0f4b50ca6 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -390,7 +390,7 @@ class CModelInfoSA : public CModelInfo static void StaticResetLodDistances(); void RestreamIPL(); static void StaticFlushPendingRestreamIPL(); - static void StaticSetHooks(); + static void StaticSetHooks(); bool GetTime(char& cHourOn, char& cHourOff); bool SetTime(char cHourOn, char cHourOff); static void StaticResetModelTimes(); @@ -478,7 +478,7 @@ class CModelInfoSA : public CModelInfo C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index); std::uint32_t Get2DFXCount() const { return m_pInterface ? m_pInterface->ucNumOf2DEffects : 0; } - void StoreDefault2DFXEffect(C2DEffectSAInterface* effect); + void StoreDefault2DFXEffect(C2DEffectSAInterface* effect); bool Reset2DFXEffects(bool removeCustomEffects = false); static void StaticReset2DFXEffects(); From a6d3c2a826f60dfea41a27ec78f32e86be307119 Mon Sep 17 00:00:00 2001 From: FileEX Date: Fri, 20 Sep 2024 22:36:18 +0200 Subject: [PATCH 05/17] Fix possible crash --- Client/game_sa/C2DEffectSAInterface.h | 2 +- Client/game_sa/CModelInfoSA.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h index 4c873fbcfb..88c3efe1e4 100644 --- a/Client/game_sa/C2DEffectSAInterface.h +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -274,7 +274,7 @@ class C2DEffectSA static int effect2dPluginOffset; }; -static void PrepareTexturesForLightEffect(RwTexture* coronaTex, RwTexture* shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist) +static void PrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist) { // Call CTxdStore::PushCurrentTxd ((void(__cdecl*)())FUNC_PushCurrentTxd)(); diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index e04ea28b2e..550c8d6c9d 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -2089,7 +2089,10 @@ void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) // Create a copy of textures for the lights // We must to do this, because C2DEffect::Shutdown removes them if (copy->type == e2dEffectType::LIGHT) - PrepareTexturesForLightEffect(copy->effect.light.coronaTex, copy->effect.light.shadowTex, effect->effect.light.coronaTex->name, effect->effect.light.shadowTex->name, false); + { + if (effect->effect.light.coronaTex && effect->effect.light.shadowTex) + PrepareTexturesForLightEffect(copy->effect.light.coronaTex, copy->effect.light.shadowTex, effect->effect.light.coronaTex->name, effect->effect.light.shadowTex->name, false); + } else if (copy->type == e2dEffectType::ROADSIGN) { // Create a copy of text and atomic for the roadsign @@ -2181,9 +2184,6 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const // Save our effect d2fxEffects.push_back(effectInterface); - // Keep original effects - StoreDefault2DFXEffect(effectInterface); - return effectInterface; } From 837354d1fb8828b6bdd1bcd9b556f02f51add487 Mon Sep 17 00:00:00 2001 From: FileEX Date: Thu, 26 Sep 2024 02:43:51 +0200 Subject: [PATCH 06/17] Update --- Client/game_sa/C2DEffectSAInterface.h | 1 + Client/game_sa/CModelInfoSA.cpp | 60 +- Client/game_sa/CModelInfoSA.h | 2 +- Client/mods/deathmatch/logic/CClient2DFX.cpp | 10 +- Client/mods/deathmatch/logic/CClient2DFX.h | 5 +- .../deathmatch/logic/CClient2DFXManager.cpp | 700 ++++++++++++++++-- .../deathmatch/logic/CClient2DFXManager.h | 15 +- .../logic/lua/CLuaFunctionParseHelpers.cpp | 43 ++ .../logic/lua/CLuaFunctionParseHelpers.h | 3 +- .../logic/luadefs/CLuaEngineDefs.cpp | 76 +- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 29 +- Client/sdk/game/CModelInfo.h | 50 +- 12 files changed, 879 insertions(+), 115 deletions(-) diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h index 88c3efe1e4..90c1f8f1f5 100644 --- a/Client/game_sa/C2DEffectSAInterface.h +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -51,6 +51,7 @@ struct t2dEffectLight // Flags 1 std::uint16_t checkObstacles : 1; std::uint16_t fogType : 1; + std::uint16_t fogType2 : 1; std::uint16_t withoutCorona : 1; std::uint16_t onlyLongDistance : 1; std::uint16_t atDay : 1; diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 550c8d6c9d..5910e15d6e 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -37,7 +37,7 @@ std::unordered_map CModelInfo std::unordered_map> CModelInfoSA::ms_VehicleModelDefaultWheelSizes; std::map CModelInfoSA::ms_DefaultTxdIDMap; -std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; +std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; static std::unordered_map m_numCustom2dfxEffects; static std::vector d2fxEffects; static std::vector removedDefaultEffects; @@ -1185,7 +1185,47 @@ void CModelInfoSA::ResetAlphaTransparency() void CModelInfoSA::StaticReset2DFXEffects() { + for (auto& iter = ms_DefaultEffectsMap.begin(); iter != ms_DefaultEffectsMap.end(); iter++) + { + CBaseModelInfoSAInterface* modelInfoInterface = ppModelInfo[iter->first]; + if (!modelInfoInterface) + continue; + + for (auto innerIter = iter->second.begin(); innerIter != iter->second.end();) + { + // Copy default effect + memcpy(innerIter->first, innerIter->second, sizeof(C2DEffectSAInterface)); + + // Increase the counter if this effect was removed + auto& removedEffect = std::find(removedDefaultEffects.begin(), removedDefaultEffects.end(), innerIter->first); + if (removedEffect != removedDefaultEffects.end()) + { + removedDefaultEffects.erase(removedEffect); + modelInfoInterface->ucNumOf2DEffects++; + } + // Delete copy of the default effect + delete innerIter->second; + innerIter = iter->second.erase(innerIter); + } + + // Decrement the counter by the number of custom effects + auto customEffectsCount = MapGet(m_numCustom2dfxEffects, modelInfoInterface); + if (customEffectsCount && customEffectsCount > 0) + modelInfoInterface->ucNumOf2DEffects -= customEffectsCount; + + MapSet(m_numCustom2dfxEffects, modelInfoInterface, 0); + } + + // Remove all custom effects + for (auto& customEffect : d2fxEffects) + if (customEffect) + delete customEffect; + + // Clear maps + removedDefaultEffects.clear(); + ms_DefaultEffectsMap.clear(); + d2fxEffects.clear(); } short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) @@ -2187,7 +2227,7 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const return effectInterface; } -void CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool isCustom) +void CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool isCustom, bool decrementCounters) { if (!effect) return; @@ -2255,6 +2295,16 @@ void CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool isCustom) } } + if (decrementCounters) + { + m_pInterface->ucNumOf2DEffects--; + MapGet(m_numCustom2dfxEffects, m_pInterface)--; + + auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); + if (it != d2fxEffects.end()) + d2fxEffects.erase(it); + } + // If it's custom effect then delete it. If it's default effect then store it as removed if (isCustom) { @@ -2281,7 +2331,8 @@ bool CModelInfoSA::Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefa if (!includeDefault && !isCustomEffect) return false; - StoreDefault2DFXEffect(effect); + if (!isCustomEffect) + StoreDefault2DFXEffect(effect); m_pInterface->ucNumOf2DEffects--; if (isCustomEffect) @@ -2313,7 +2364,8 @@ bool CModelInfoSA::RemoveAll2DFXEffects(bool includeDefault) if (!includeDefault && !isCustomEffect) continue; - StoreDefault2DFXEffect(effect); + if (!isCustomEffect) + StoreDefault2DFXEffect(effect); m_pInterface->ucNumOf2DEffects--; if (isCustomEffect) diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index f0f4b50ca6..da195f4cf7 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -472,7 +472,7 @@ class CModelInfoSA : public CModelInfo // 2DFX functions C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type); - void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false); + void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false, bool decrementCounters = false); bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false); bool RemoveAll2DFXEffects(bool includeDefault); C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index); diff --git a/Client/mods/deathmatch/logic/CClient2DFX.cpp b/Client/mods/deathmatch/logic/CClient2DFX.cpp index 8aac2a8fe2..a3b0ce7810 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFX.cpp @@ -23,9 +23,16 @@ CClient2DFX::CClient2DFX(class CClientManager* manager, ElementID ID) CClient2DFX::~CClient2DFX() { m_2DFXManager->RemoveFromList(this); + + // Destroy effect + CModelInfo* modelInfo = g_pGame->GetModelInfo(m_model); + if (!modelInfo) + return; + + modelInfo->Remove2DFX(m_effectInterface, true, true); } -bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData) +bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData) { CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(model)); if (!modelInfo) @@ -38,6 +45,7 @@ bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2d // Set effect m_effectInterface = effect; m_effectType = effect->type; + m_model = static_cast(model); if (!m_2DFXManager->Set2DFXProperties(effect, effectData)) return false; diff --git a/Client/mods/deathmatch/logic/CClient2DFX.h b/Client/mods/deathmatch/logic/CClient2DFX.h index a77b529d68..a9629eb09c 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.h +++ b/Client/mods/deathmatch/logic/CClient2DFX.h @@ -10,6 +10,8 @@ #include "CClientEntity.h" +using effectDataMap = std::unordered_map>; + class CClient2DFX final : public CClientEntity { DECLARE_CLASS(CClient2DFX, CClientEntity) @@ -25,7 +27,7 @@ class CClient2DFX final : public CClientEntity void GetPosition(CVector& vecPosition) const {} void SetPosition(const CVector& vecPosition){} - bool Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData); + bool Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData); e2dEffectType Get2DFXType() const noexcept { return m_effectType; } C2DEffectSAInterface* Get2DFX() const noexcept { return m_effectInterface; } @@ -34,4 +36,5 @@ class CClient2DFX final : public CClientEntity class CClient2DFXManager* m_2DFXManager; C2DEffectSAInterface* m_effectInterface; e2dEffectType m_effectType; + DWORD m_model; }; diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index e4c7bc81f8..0adb4b008a 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -43,12 +43,12 @@ void CClient2DFXManager::RemoveFromList(CClient2DFX* effect) m_effectsList.remove(effect); } -bool CClient2DFXManager::Exists(CClient2DFX* effect) +bool CClient2DFXManager::Exists(CClient2DFX* effect) const { return std::find(m_effectsList.begin(), m_effectsList.end(), effect) != m_effectsList.end(); } -CClient2DFX* CClient2DFXManager::Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData) +CClient2DFX* CClient2DFXManager::Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData) { auto* effect = new CClient2DFX(m_mainManager, INVALID_ELEMENT_ID); if (!effect) @@ -63,7 +63,7 @@ CClient2DFX* CClient2DFXManager::Add2DFX(std::uint32_t model, const CVector& pos return effect; } -bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, std::unordered_map>& effectData) +bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, const effectDataMap& effectData) { if (!effect) return false; @@ -73,39 +73,38 @@ bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, std::un { case e2dEffectType::LIGHT: { - t2dEffectLight& light = effect->effect.light; - auto* drawDistance = MapFind(effectData, "draw_distance"); + auto* drawDistance = MapFind(effectData, "drawDistance"); light.coronaFarClip = std::get(*drawDistance); - auto* lightRange = MapFind(effectData, "light_range"); + auto* lightRange = MapFind(effectData, "lightRange"); light.pointLightRange = std::get(*lightRange); - auto* coronaSize = MapFind(effectData, "corona_size"); + auto* coronaSize = MapFind(effectData, "coronaSize"); light.coronaSize = std::get(*coronaSize); - auto* shadowSize = MapFind(effectData, "shadow_size"); + auto* shadowSize = MapFind(effectData, "shadowSize"); light.shadowSize = std::get(*shadowSize); - auto* shadowMultiplier = MapFind(effectData, "shadow_multiplier"); + auto* shadowMultiplier = MapFind(effectData, "shadowMultiplier"); light.shadowColorMultiplier = static_cast(std::get(*shadowMultiplier)); - auto* showMode = MapFind(effectData, "show_mode"); + auto* showMode = MapFind(effectData, "showMode"); e2dCoronaFlashType flashType; StringToEnum(std::get(*showMode), flashType); light.coronaFlashType = flashType; - auto* coronaReflection = MapFind(effectData, "corona_reflection"); + auto* coronaReflection = MapFind(effectData, "coronaReflection"); light.coronaEnableReflection = std::get(*coronaReflection); - auto* coronaFlareType = MapFind(effectData, "flare_type"); + auto* coronaFlareType = MapFind(effectData, "flareType"); light.coronaFlareType = static_cast(std::get(*coronaFlareType)); auto* flags = MapFind(effectData, "flags"); light.flags = static_cast(std::get(*flags)); - auto* shadowZDistance = MapFind(effectData, "shadow_distance"); + auto* shadowZDistance = MapFind(effectData, "shadowDistance"); light.shadowZDistance = static_cast(std::get(*shadowZDistance)); auto* offsetX = MapFind(effectData, "offsetX"); @@ -118,19 +117,18 @@ bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, std::un light.offsetZ = static_cast(std::get(*offsetZ)); auto* color = MapFind(effectData, "color"); - int colorValue = static_cast(std::get(*color)); + int colorValue = static_cast(std::get(*color)); light.color = RwColor{static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; - auto* coronaTexture = MapFind(effectData, "corona_name"); - auto* shadowTexture = MapFind(effectData, "shadow_name"); + auto* coronaTexture = MapFind(effectData, "coronaName"); + auto* shadowTexture = MapFind(effectData, "shadowName"); PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(*coronaTexture).c_str(), std::get(*shadowTexture).c_str(), true); break; } case e2dEffectType::PARTICLE: { auto* particleName = MapFind(effectData, "name"); - auto& stringName = std::get(*particleName); - std::strncpy(effect->effect.particle.szName, stringName.c_str(), sizeof(effect->effect.particle.szName) - 1); + std::strncpy(effect->effect.particle.szName, std::get(*particleName).c_str(), sizeof(effect->effect.particle.szName) - 1); break; } @@ -192,21 +190,17 @@ bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, std::un break; } - case e2dEffectType::ATTRACTOR: - case e2dEffectType::ENEX: - case e2dEffectType::FURNITURE: - case e2dEffectType::COVER_POINT: - case e2dEffectType::TRIGGER_POINT: case e2dEffectType::SUN_GLARE: // This effect has no properties but works in MTA + default: break; } return true; } -std::unordered_map> CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect) +effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect) const { - auto properties = std::unordered_map>(); + auto properties = effectDataMap(); if (!effect) return properties; @@ -217,26 +211,25 @@ std::unordered_mapeffect.light; - MapSet(properties, "draw_distance", light.coronaFarClip); - MapSet(properties, "light_range", light.pointLightRange); - MapSet(properties, "corona_size", light.coronaSize); - MapSet(properties, "shadow_size", light.shadowSize); - MapSet(properties, "shadow_multiplier", light.shadowColorMultiplier); - MapSet(properties, "show_mode", EnumToString(light.coronaFlashType)); - MapSet(properties, "corona_reflection", light.coronaEnableReflection); - MapSet(properties, "flare_type", light.coronaFlareType); - MapSet(properties, "flags", light.flags); - MapSet(properties, "shadow_distance", light.shadowZDistance); - MapSet(properties, "offsetX", light.offsetX); - MapSet(properties, "offsetY", light.offsetY); - MapSet(properties, "offsetZ", light.offsetZ); - std::uint32_t colorValue = (static_cast(light.color.r) << 0) | - (static_cast(light.color.g) << 8) | - (static_cast(light.color.b) << 16) | - (static_cast(light.color.a) << 24); - MapSet(properties, "color", colorValue); - MapSet(properties, "corona_name", !light.coronaTex ? "" : static_cast(light.coronaTex->name)); - MapSet(properties, "shadow_name", !light.shadowTex ? "" : static_cast(light.shadowTex->name)); + MapSet(properties, "drawDistance", light.coronaFarClip); + MapSet(properties, "lightRange", light.pointLightRange); + MapSet(properties, "coronaSize", light.coronaSize); + MapSet(properties, "shadowSize", light.shadowSize); + MapSet(properties, "shadowMultiplier", static_cast(light.shadowColorMultiplier)); + MapSet(properties, "showMode", EnumToString(light.coronaFlashType)); + MapSet(properties, "coronaReflection", light.coronaEnableReflection); + MapSet(properties, "flareType", static_cast(light.coronaFlareType)); + MapSet(properties, "flags", static_cast(light.flags)); + MapSet(properties, "shadowDistance", static_cast(light.shadowZDistance)); + MapSet(properties, "offsetX", static_cast(light.offsetX)); + MapSet(properties, "offsetY", static_cast(light.offsetY)); + MapSet(properties, "offsetZ", static_cast(light.offsetZ)); + + int colorValue = (static_cast(light.color.a) << 24) | (static_cast(light.color.r) << 16) | (static_cast(light.color.g) << 8) | static_cast(light.color.b); + MapSet(properties, "color", static_cast(colorValue)); + + MapSet(properties, "coronaName", !light.coronaTex ? "" : static_cast(light.coronaTex->name)); + MapSet(properties, "shadowName", !light.shadowTex ? "" : static_cast(light.shadowTex->name)); break; } @@ -254,7 +247,7 @@ std::unordered_map(roadsign.flags)); MapSet(properties, "text", !roadsign.text ? "" : roadsign.text); break; @@ -272,22 +265,575 @@ std::unordered_map(escalator.direction)); break; } - case e2dEffectType::ATTRACTOR: - case e2dEffectType::ENEX: - case e2dEffectType::FURNITURE: - case e2dEffectType::COVER_POINT: - case e2dEffectType::TRIGGER_POINT: - case e2dEffectType::SUN_GLARE: + default: break; } return properties; } +bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue) +{ + if (!effect) + return false; + + switch (effect->type) + { + case e2dEffectType::LIGHT: + { + t2dEffectLight& light = effect->effect.light; + + switch (property) + { + case e2dEffectProperty::FAR_CLIP_DISTANCE: + { + if (std::holds_alternative(propertyValue)) + { + light.coronaFarClip = std::get(propertyValue); + return true; + } + break; + } + case e2dEffectProperty::LIGHT_RANGE: + { + if (std::holds_alternative(propertyValue)) + { + light.pointLightRange = std::get(propertyValue); + return true; + } + break; + } + case e2dEffectProperty::CORONA_SIZE: + { + if (std::holds_alternative(propertyValue)) + { + light.coronaSize = std::get(propertyValue); + return true; + } + break; + } + case e2dEffectProperty::SHADOW_SIZE: + { + if (std::holds_alternative(propertyValue)) + { + light.shadowSize = std::get(propertyValue); + return true; + } + break; + } + case e2dEffectProperty::SHADOW_MULT: + { + if (std::holds_alternative(propertyValue)) + { + light.shadowColorMultiplier = std::get(propertyValue); + return true; + } + break; + } + case e2dEffectProperty::FLASH_TYPE: + { + if (std::holds_alternative(propertyValue)) + { + e2dCoronaFlashType showMode; + if (StringToEnum(std::get(propertyValue), showMode)) + { + light.coronaFlashType = showMode; + return true; + } + } + + break; + } + case e2dEffectProperty::CORONA_REFLECTION: + { + if (std::holds_alternative(propertyValue)) + { + light.coronaEnableReflection = std::get(propertyValue); + return true; + } + break; + } + case e2dEffectProperty::FLARE_TYPE: + { + if (std::holds_alternative(propertyValue)) + { + light.coronaFlareType = static_cast(std::get(propertyValue)); + return true; + } + break; + } + case e2dEffectProperty::CORONA_FLAGS: + { + if (std::holds_alternative(propertyValue)) + { + light.flags = static_cast(std::get(propertyValue)); + return true; + } + break; + } + case e2dEffectProperty::SHADOW_DISTANCE: + { + if (std::holds_alternative(propertyValue)) + { + light.shadowZDistance = static_cast(std::get(propertyValue)); + return true; + } + break; + } + case e2dEffectProperty::OFFSET_X: + { + if (std::holds_alternative(propertyValue)) + { + light.offsetX = static_cast(std::get(propertyValue)); + return true; + } + break; + } + case e2dEffectProperty::OFFSET_Y: + { + if (std::holds_alternative(propertyValue)) + { + light.offsetY = static_cast(std::get(propertyValue)); + return true; + } + break; + } + case e2dEffectProperty::OFFSET_Z: + { + if (std::holds_alternative(propertyValue)) + { + light.offsetZ = static_cast(std::get(propertyValue)); + return true; + } + + break; + } + case e2dEffectProperty::COLOR: + { + if (std::holds_alternative(propertyValue)) + { + int colorValue = static_cast(std::get(propertyValue)); + light.color = RwColor{static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; + + return true; + } + + break; + } + case e2dEffectProperty::CORONA_NAME: + { + if (std::holds_alternative(propertyValue)) + { + e2dEffectTextureName coronaName; + if (StringToEnum(std::get(propertyValue), coronaName)) + { + PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(propertyValue).c_str(), light.shadowTex->name, true); + return true; + } + } + + break; + } + case e2dEffectProperty::SHADOW_NAME: + { + if (std::holds_alternative(propertyValue)) + { + e2dEffectTextureName shadowName; + if (StringToEnum(std::get(propertyValue), shadowName)) + { + PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, light.coronaTex->name, std::get(propertyValue).c_str(), true); + return true; + } + } + + break; + } + default: + break; + } + + break; + } + case e2dEffectType::PARTICLE: + { + if (property == e2dEffectProperty::PRT_NAME) + { + if (std::holds_alternative(propertyValue)) + { + std::strncpy(effect->effect.particle.szName, std::get(propertyValue).c_str(), sizeof(effect->effect.particle.szName) - 1); + return true; + } + } + + break; + } + case e2dEffectType::ROADSIGN: + { + t2dEffectRoadsign roadsign = effect->effect.roadsign; + + switch (property) + { + case e2dEffectProperty::SIZE_X: + { + if (std::holds_alternative(propertyValue)) + { + roadsign.size.x = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::SIZE_Y: + { + if (std::holds_alternative(propertyValue)) + { + roadsign.size.y = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::ROT_X: + { + if (std::holds_alternative(propertyValue)) + { + roadsign.rotation.x = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::ROT_Y: + { + if (std::holds_alternative(propertyValue)) + { + roadsign.rotation.y = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::ROT_Z: + { + if (std::holds_alternative(propertyValue)) + { + roadsign.rotation.z = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::ROADSIGN_FLAGS: + { + if (std::holds_alternative(propertyValue)) + { + roadsign.flags = static_cast(std::get(propertyValue)); + return true; + } + + break; + } + case e2dEffectProperty::TEXT: + { + if (std::holds_alternative(propertyValue)) + { + if (!roadsign.text) + roadsign.text = static_cast(malloc(64)); + + std::memcpy(roadsign.text, std::get(propertyValue).c_str(), 4); + return true; + } + + break; + } + case e2dEffectProperty::TEXT_2: + { + if (std::holds_alternative(propertyValue)) + { + if (!roadsign.text) + roadsign.text = static_cast(malloc(64)); + + std::memcpy(roadsign.text + 4, std::get(propertyValue).c_str(), 4); + return true; + } + + break; + } + case e2dEffectProperty::TEXT_3: + { + if (std::holds_alternative(propertyValue)) + { + if (!roadsign.text) + roadsign.text = static_cast(malloc(64)); + + std::memcpy(roadsign.text + 8, std::get(propertyValue).c_str(), 4); + return true; + } + + break; + } + case e2dEffectProperty::TEXT_4: + { + if (std::holds_alternative(propertyValue)) + { + if (!roadsign.text) + roadsign.text = static_cast(malloc(64)); + + std::memcpy(roadsign.text + 12, std::get(propertyValue).c_str(), 4); + return true; + } + + break; + } + default: + break; + } + break; + } + case e2dEffectType::ESCALATOR: + { + t2dEffectEscalator escalator = effect->effect.escalator; + + switch (property) + { + case e2dEffectProperty::BOTTOM_X: + { + if (std::holds_alternative(propertyValue)) + { + escalator.bottom.x = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::BOTTOM_Y: + { + if (std::holds_alternative(propertyValue)) + { + escalator.bottom.y = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::BOTTOM_Z: + { + if (std::holds_alternative(propertyValue)) + { + escalator.bottom.z = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::TOP_X: + { + if (std::holds_alternative(propertyValue)) + { + escalator.top.x = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::TOP_Y: + { + if (std::holds_alternative(propertyValue)) + { + escalator.top.y = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::TOP_Z: + { + if (std::holds_alternative(propertyValue)) + { + escalator.top.z = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::END_X: + { + if (std::holds_alternative(propertyValue)) + { + escalator.end.x = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::END_Y: + { + if (std::holds_alternative(propertyValue)) + { + escalator.end.y = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::END_Z: + { + if (std::holds_alternative(propertyValue)) + { + escalator.end.z = std::get(propertyValue); + return true; + } + + break; + } + case e2dEffectProperty::DIRECTION: + { + if (std::holds_alternative(propertyValue)) + { + escalator.direction = static_cast(std::get(propertyValue)); + return true; + } + + break; + } + } + + break; + } + default: + break; + } + + return false; +} + +std::variant CClient2DFXManager::Get2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property) +{ + if (!effect) + return false; + + switch (effect->type) + { + case e2dEffectType::LIGHT: + { + const t2dEffectLight& light = effect->effect.light; + + switch (property) + { + case e2dEffectProperty::FAR_CLIP_DISTANCE: + return light.coronaFarClip; + case e2dEffectProperty::LIGHT_RANGE: + return light.pointLightRange; + case e2dEffectProperty::CORONA_SIZE: + return light.coronaSize; + case e2dEffectProperty::SHADOW_SIZE: + return light.shadowSize; + case e2dEffectProperty::SHADOW_MULT: + return static_cast(light.shadowColorMultiplier); + case e2dEffectProperty::FLASH_TYPE: + return EnumToString(light.coronaFlashType); + case e2dEffectProperty::CORONA_REFLECTION: + return light.coronaEnableReflection; + case e2dEffectProperty::FLARE_TYPE: + return static_cast(light.coronaFlareType); + case e2dEffectProperty::CORONA_FLAGS: + return static_cast(light.flags); + case e2dEffectProperty::SHADOW_DISTANCE: + return static_cast(light.shadowZDistance); + case e2dEffectProperty::OFFSET_X: + return static_cast(light.offsetX); + case e2dEffectProperty::OFFSET_Y: + return static_cast(light.offsetY); + case e2dEffectProperty::OFFSET_Z: + return static_cast(light.offsetZ); + case e2dEffectProperty::COLOR: + { + int colorValue = (static_cast(light.color.a) << 24) | (static_cast(light.color.r) << 16) | (static_cast(light.color.g) << 8) | static_cast(light.color.b); + return static_cast(colorValue); + } + case e2dEffectProperty::CORONA_NAME: + return light.coronaTex ? light.coronaTex->name : ""; + case e2dEffectProperty::SHADOW_NAME: + return light.shadowTex ? light.shadowTex->name : ""; + } + + break; + } + case e2dEffectType::PARTICLE: + { + if (property == e2dEffectProperty::PRT_NAME) + return effect->effect.particle.szName ? effect->effect.particle.szName : ""; + + break; + } + case e2dEffectType::ROADSIGN: + { + const t2dEffectRoadsign& roadsign = effect->effect.roadsign; + + switch (property) + { + case e2dEffectProperty::SIZE_X: + return roadsign.size.x; + case e2dEffectProperty::SIZE_Y: + return roadsign.size.y; + case e2dEffectProperty::ROT_X: + return roadsign.rotation.x; + case e2dEffectProperty::ROT_Y: + return roadsign.rotation.y; + case e2dEffectProperty::ROT_Z: + return roadsign.rotation.z; + case e2dEffectProperty::ROADSIGN_FLAGS: + return static_cast(roadsign.flags); + case e2dEffectProperty::TEXT: + case e2dEffectProperty::TEXT_2: + case e2dEffectProperty::TEXT_3: + case e2dEffectProperty::TEXT_4: + return roadsign.text ? roadsign.text : ""; + } + + break; + } + case e2dEffectType::ESCALATOR: + { + const t2dEffectEscalator& escalator = effect->effect.escalator; + + switch (property) + { + case e2dEffectProperty::BOTTOM_X: + return escalator.bottom.x; + case e2dEffectProperty::BOTTOM_Y: + return escalator.bottom.y; + case e2dEffectProperty::BOTTOM_Z: + return escalator.bottom.z; + case e2dEffectProperty::TOP_X: + return escalator.top.x; + case e2dEffectProperty::TOP_Y: + return escalator.top.y; + case e2dEffectProperty::TOP_Z: + return escalator.top.z; + case e2dEffectProperty::END_X: + return escalator.end.x; + case e2dEffectProperty::END_Y: + return escalator.end.y; + case e2dEffectProperty::END_Z: + return escalator.end.z; + case e2dEffectProperty::DIRECTION: + return static_cast(escalator.direction); + } + + break; + } + default: + break; + } + + return false; +} + void CClient2DFXManager::Set2DFXPosition(C2DEffectSAInterface* effect, const CVector& position) { if (!effect) @@ -296,7 +842,7 @@ void CClient2DFXManager::Set2DFXPosition(C2DEffectSAInterface* effect, const CVe effect->position = RwV3d{position.fX, position.fY, position.fZ}; } -CVector* CClient2DFXManager::Get2DFXPosition(C2DEffectSAInterface* effect) +CVector* CClient2DFXManager::Get2DFXPosition(C2DEffectSAInterface* effect) const { if (!effect) return nullptr; @@ -304,7 +850,7 @@ CVector* CClient2DFXManager::Get2DFXPosition(C2DEffectSAInterface* effect) return reinterpret_cast(&effect->position); } -const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectType, std::unordered_map>& effectData) +const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectType, const effectDataMap& effectData) { // Check if keys & values are ok! switch (effectType) @@ -312,46 +858,46 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp case e2dEffectType::LIGHT: { // corona far clip - auto* drawDistance = MapFind(effectData, "draw_distance"); + auto* drawDistance = MapFind(effectData, "drawDistance"); if (!drawDistance || !std::holds_alternative(*drawDistance)) - return "Invalid \"draw_distance\" value"; + return "Invalid \"drawDistance\" value"; - auto* lightRange = MapFind(effectData, "light_range"); + auto* lightRange = MapFind(effectData, "lightRange"); if (!lightRange || !std::holds_alternative(*lightRange)) - return "Invalid \"light_range\" value"; + return "Invalid \"lightRange\" value"; - auto* coronaSize = MapFind(effectData, "corona_size"); + auto* coronaSize = MapFind(effectData, "coronaSize"); if (!coronaSize || !std::holds_alternative(*coronaSize)) - return "Invalid \"corona_size\" value"; + return "Invalid \"coronaSize\" value"; - auto* shadowSize = MapFind(effectData, "shadow_size"); + auto* shadowSize = MapFind(effectData, "shadowSize"); if (!shadowSize || !std::holds_alternative(*shadowSize)) - return "Invalid \"shadow_size\" value"; + return "Invalid \"shadowSize\" value"; - auto* shadowMultiplier = MapFind(effectData, "shadow_multiplier"); + auto* shadowMultiplier = MapFind(effectData, "shadowMultiplier"); if (!shadowMultiplier || !std::holds_alternative(*shadowMultiplier) || std::get(*shadowMultiplier) < 0) - return "Invalid \"shadow_multiplier\" value"; + return "Invalid \"shadowMultiplier\" value"; - auto* showMode = MapFind(effectData, "show_mode"); + auto* showMode = MapFind(effectData, "showMode"); e2dCoronaFlashType tempType; if (!showMode || !std::holds_alternative(*showMode) || !StringToEnum(std::get(*showMode), tempType)) - return "Invalid \"show_mode\" value"; + return "Invalid \"showMode\" value"; - auto* coronaReflection = MapFind(effectData, "corona_reflection"); + auto* coronaReflection = MapFind(effectData, "coronaReflection"); if (!coronaReflection || !std::holds_alternative(*coronaReflection)) - return "Invalid \"corona_reflection\" value"; + return "Invalid \"coronaReflection\" value"; - auto* coronaFlareType = MapFind(effectData, "flare_type"); + auto* coronaFlareType = MapFind(effectData, "flareType"); if (!coronaFlareType || !std::holds_alternative(*coronaFlareType) || (std::get(*coronaFlareType) < 0 || std::get(*coronaFlareType) > 1.0f)) - return "Invalid \"flare_type\" value"; + return "Invalid \"flareType\" value"; auto* flags = MapFind(effectData, "flags"); if (!flags || !std::holds_alternative(*flags) || std::get(*flags) < 0) return "Invalid \"flags\" value"; - auto* shadowZDistance = MapFind(effectData, "shadow_distance"); + auto* shadowZDistance = MapFind(effectData, "shadowDistance"); if (!shadowZDistance || (!std::holds_alternative(*shadowZDistance))) - return "Invalid \"shadow_distance\" value"; + return "Invalid \"shadowDistance\" value"; auto* offsetX = MapFind(effectData, "offsetX"); if (!offsetX || !std::holds_alternative(*offsetX)) @@ -369,15 +915,15 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp if (!color || !std::holds_alternative(*color)) return "Invalid \"color\" value"; - auto* coronaTexture = MapFind(effectData, "corona_name"); + auto* coronaTexture = MapFind(effectData, "coronaName"); e2dEffectTextureName tempName; if (!coronaTexture || !std::holds_alternative(*coronaTexture) || !StringToEnum(std::get(*coronaTexture), tempName)) - return "Invalid \"corona_name\" value"; + return "Invalid \"coronaName\" value"; - auto* shadowTexture = MapFind(effectData, "shadow_name"); + auto* shadowTexture = MapFind(effectData, "shadowName"); e2dEffectTextureName shadowName; if (!shadowTexture || !std::holds_alternative(*shadowTexture) || !StringToEnum(std::get(*shadowTexture), shadowName)) - return "Invalid \"shadow_name\" value"; + return "Invalid \"shadowName\" value"; break; } diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.h b/Client/mods/deathmatch/logic/CClient2DFXManager.h index 3c452f0e9c..82e24a6566 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.h +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.h @@ -20,18 +20,21 @@ class CClient2DFXManager ~CClient2DFXManager(); void RemoveAll(); - bool Exists(CClient2DFX* effect); + bool Exists(CClient2DFX* effect) const; - CClient2DFX* Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, std::unordered_map>& effectData); + CClient2DFX* Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData); - bool Set2DFXProperties(C2DEffectSAInterface* effect, std::unordered_map>& effectData); - std::unordered_map> Get2DFXProperties(C2DEffectSAInterface* effect); + bool Set2DFXProperties(C2DEffectSAInterface* effect, const effectDataMap& effectData); + effectDataMap Get2DFXProperties(C2DEffectSAInterface* effect) const; + + bool Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue); + std::variant Get2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property); void Set2DFXPosition(C2DEffectSAInterface* effect, const CVector& position); - CVector* Get2DFXPosition(C2DEffectSAInterface* effect); + CVector* Get2DFXPosition(C2DEffectSAInterface* effect) const; static bool IsValidModel(std::uint32_t model) noexcept; - static const char* IsValidEffectData(const e2dEffectType& effectType, std::unordered_map>& effectData); + static const char* IsValidEffectData(const e2dEffectType& effectType, const effectDataMap& effectData); private: void AddToList(CClient2DFX* effect) { m_effectsList.push_back(effect); } diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 8570fd84e0..83e014e3b8 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -975,6 +975,49 @@ ADD_ENUM(e2dEffectTextureName::SHAD_RCBARON, "shad_rcbaron") ADD_ENUM(e2dEffectTextureName::SHAD_PED, "shad_ped") IMPLEMENT_ENUM_CLASS_END("2dfx-texture-name") +IMPLEMENT_ENUM_CLASS_BEGIN(e2dEffectProperty) +ADD_ENUM(e2dEffectProperty::FAR_CLIP_DISTANCE, "drawDistance") +ADD_ENUM(e2dEffectProperty::LIGHT_RANGE, "lightRange") +ADD_ENUM(e2dEffectProperty::CORONA_SIZE, "coronaSize") +ADD_ENUM(e2dEffectProperty::SHADOW_SIZE, "shadowSize") +ADD_ENUM(e2dEffectProperty::SHADOW_MULT, "shadowMultiplier") +ADD_ENUM(e2dEffectProperty::FLASH_TYPE, "showMode") +ADD_ENUM(e2dEffectProperty::CORONA_REFLECTION, "coronaReflection") +ADD_ENUM(e2dEffectProperty::FLARE_TYPE, "flareType") +ADD_ENUM(e2dEffectProperty::CORONA_FLAGS, "flags") +ADD_ENUM(e2dEffectProperty::SHADOW_DISTANCE, "shadowDistance") +ADD_ENUM(e2dEffectProperty::OFFSET_X, "offsetX") +ADD_ENUM(e2dEffectProperty::OFFSET_Y, "offsetY") +ADD_ENUM(e2dEffectProperty::OFFSET_Z, "offsetZ") +ADD_ENUM(e2dEffectProperty::COLOR, "color") +ADD_ENUM(e2dEffectProperty::CORONA_NAME, "coronaName") +ADD_ENUM(e2dEffectProperty::SHADOW_NAME, "shadowName") + +ADD_ENUM(e2dEffectProperty::PRT_NAME, "name") + +ADD_ENUM(e2dEffectProperty::SIZE_X, "sizeX") +ADD_ENUM(e2dEffectProperty::SIZE_Y, "sizeY") +ADD_ENUM(e2dEffectProperty::ROT_X, "rotX") +ADD_ENUM(e2dEffectProperty::ROT_Y, "rotY") +ADD_ENUM(e2dEffectProperty::ROT_Z, "rotZ") +ADD_ENUM(e2dEffectProperty::ROADSIGN_FLAGS, "flags") +ADD_ENUM(e2dEffectProperty::TEXT, "text1") +ADD_ENUM(e2dEffectProperty::TEXT_2, "text2") +ADD_ENUM(e2dEffectProperty::TEXT_3, "text3") +ADD_ENUM(e2dEffectProperty::TEXT_4, "text4") + +ADD_ENUM(e2dEffectProperty::BOTTOM_X, "bottomX") +ADD_ENUM(e2dEffectProperty::BOTTOM_Y, "bottomY") +ADD_ENUM(e2dEffectProperty::BOTTOM_Z, "bottomZ") +ADD_ENUM(e2dEffectProperty::TOP_X, "topX") +ADD_ENUM(e2dEffectProperty::TOP_Y, "topY") +ADD_ENUM(e2dEffectProperty::TOP_Z, "topZ") +ADD_ENUM(e2dEffectProperty::END_X, "endX") +ADD_ENUM(e2dEffectProperty::END_Y, "endY") +ADD_ENUM(e2dEffectProperty::END_Z, "endZ") +ADD_ENUM(e2dEffectProperty::DIRECTION, "direction") +IMPLEMENT_ENUM_CLASS_END("2dfx-property-name") + // // CResource from userdata // diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index 0d9b463269..820dcfda76 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -90,6 +90,7 @@ DECLARE_ENUM_CLASS(eModelLoadState); DECLARE_ENUM_CLASS(PreloadAreaOption); DECLARE_ENUM_CLASS(e2dEffectType); DECLARE_ENUM_CLASS(e2dCoronaFlashType); +DECLARE_ENUM_CLASS(e2dEffectProperty); // For corona name & shadow name enum class e2dEffectTextureName @@ -553,7 +554,7 @@ inline SString GetClassTypeName(CClientVectorGraphic*) return "svg"; } -inline SString GetClassTypeName(CClient2DFX*) +inline SString GetClassTypeName(CClient2DFX*) noexcept { return "2dfx"; } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 52eef08634..b81178f433 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -152,6 +152,10 @@ void CLuaEngineDefs::LoadFunctions() {"engineSet2DFXProperties", ArgumentParser}, {"engineGetModel2DFXProperties", ArgumentParser}, {"engineGet2DFXProperties", ArgumentParser}, + {"engineSetModel2DFXProperty", ArgumentParser}, + {"engineSet2DFXProperty", ArgumentParser}, + {"engineGetModel2DFXProperty", ArgumentParser}, + {"engineGet2DFXProperty", ArgumentParser}, {"engineSet2DFXPosition", ArgumentParser}, {"engineSetModel2DFXPosition", ArgumentParser}, {"engineGet2DFXPosition", ArgumentParser}, @@ -2592,7 +2596,7 @@ void CLuaEngineDefs::EnginePreloadWorldArea(CVector position, std::optionalGetStreaming()->LoadSceneCollision(&position); } -std::variant CLuaEngineDefs::EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, std::unordered_map> effectData) +std::variant CLuaEngineDefs::EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, effectDataMap effectData) { // Only these effects make sense in MTA if (effectType != e2dEffectType::LIGHT && effectType != e2dEffectType::PARTICLE && effectType != e2dEffectType::ROADSIGN && @@ -2604,7 +2608,7 @@ std::variant CLuaEngineDefs::EngineAddModel2DFX(lua_State* l const char* error = CClient2DFXManager::IsValidEffectData(effectType, effectData); if (error) - throw std::invalid_argument(error); + throw LuaFunctionError(error); CClient2DFX* effect = m_p2DFXManager->Add2DFX(modelID, position, effectType, effectData); if (!effect) @@ -2636,7 +2640,7 @@ bool CLuaEngineDefs::EngineRemoveModel2DFX(std::uint32_t modelID, std::optional< return index.has_value() ? modelInfo->Remove2DFXEffectAtIndex(index.value(), includeDefault.value_or(false)) : modelInfo->RemoveAll2DFXEffects(includeDefault.value_or(false)); } -bool CLuaEngineDefs::EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, std::unordered_map> effectData) +bool CLuaEngineDefs::EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData) { if (!CClient2DFXManager::IsValidModel(modelID)) throw std::invalid_argument("Invalid model ID"); @@ -2655,21 +2659,46 @@ bool CLuaEngineDefs::EngineSetModel2DFXProperties(std::uint32_t modelID, std::ui const char* error = CClient2DFXManager::IsValidEffectData(effect->type, effectData); if (error) - throw std::invalid_argument(error); + throw LuaFunctionError(error); modelInfo->StoreDefault2DFXEffect(effect); return m_p2DFXManager->Set2DFXProperties(effect, effectData); } -bool CLuaEngineDefs::EngineSet2DFXProperties(CClient2DFX* effect, std::unordered_map> effectData) +bool CLuaEngineDefs::EngineSet2DFXProperties(CClient2DFX* effect, effectDataMap effectData) { const char* error = CClient2DFXManager::IsValidEffectData(effect->Get2DFXType(), effectData); if (error) - throw std::invalid_argument(error); + throw LuaFunctionError(error); return m_p2DFXManager->Set2DFXProperties(effect->Get2DFX(), effectData); } +bool CLuaEngineDefs::EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = EngineGetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + return m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue); +} + +bool CLuaEngineDefs::EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue) +{ + return m_p2DFXManager->Set2DFXProperty(effect->Get2DFX(), property, propertyValue); +} + bool CLuaEngineDefs::EngineSetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position) { if (!CClient2DFXManager::IsValidModel(modelID)) @@ -2698,7 +2727,7 @@ bool CLuaEngineDefs::EngineSet2DFXPosition(CClient2DFX* effect, CVector position return true; } -std::variant> CLuaEngineDefs::EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index) +std::variant> CLuaEngineDefs::EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index) { if (!CClient2DFXManager::IsValidModel(modelID)) throw std::invalid_argument("Invalid model ID"); @@ -2719,16 +2748,41 @@ std::variant> CLuaEngineDefs::EngineGetMod return std::make_tuple(position->fX, position->fY, position->fZ); } -std::variant> CLuaEngineDefs::EngineGet2DFXPosition(CClient2DFX* effect) +std::variant> CLuaEngineDefs::EngineGet2DFXPosition(CClient2DFX* effect) { CVector* position = m_p2DFXManager->Get2DFXPosition(effect->Get2DFX()); if (!position) return false; - + return std::make_tuple(position->fX, position->fY, position->fZ); } -std::variant>> CLuaEngineDefs::EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index) +std::variant CLuaEngineDefs::EngineGetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = EngineGetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + return m_p2DFXManager->Get2DFXProperty(effect, property); +} + +std::variant CLuaEngineDefs::EngineGet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property) +{ + return m_p2DFXManager->Get2DFXProperty(effect->Get2DFX(), property); +} + +std::variant CLuaEngineDefs::EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index) { if (!CClient2DFXManager::IsValidModel(modelID)) throw std::invalid_argument("Invalid model ID"); @@ -2748,7 +2802,7 @@ std::variantGet2DFXProperties(effect); } -std::variant>> CLuaEngineDefs::EngineGet2DFXProperties(CClient2DFX* effect) +std::variant CLuaEngineDefs::EngineGet2DFXProperties(CClient2DFX* effect) { return m_p2DFXManager->Get2DFXProperties(effect->Get2DFX()); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index de74cae529..e64d571d6c 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -94,22 +94,27 @@ class CLuaEngineDefs : public CLuaDefs static void EnginePreloadWorldArea(CVector position, std::optional option); - static std::variant EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, - std::unordered_map> effectData); + // 2dfx functions + static std::variant EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, effectDataMap effectData); static bool EngineRemoveModel2DFX(std::uint32_t modelID, std::optional index, std::optional includeDefault); - static bool EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, - std::unordered_map> effectData); - static bool EngineSet2DFXProperties(CClient2DFX* effect, std::unordered_map> effectData); + static bool EngineResetModel2DFXEffects(std::uint32_t modelID, std::optional removeCustomEffects); + + // Set functions + static bool EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData); + static bool EngineSet2DFXProperties(CClient2DFX* effect, effectDataMap effectData); + static bool EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue); + static bool EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue); static bool EngineSetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position); static bool EngineSet2DFXPosition(CClient2DFX* effect, CVector position); - static std::variant> EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index); - static std::variant> EngineGet2DFXPosition(CClient2DFX* effect); - static std::variant>> - EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index); - static std::variant>> - EngineGet2DFXProperties(CClient2DFX* effect); + + // Get functions + static std::variant> EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index); + static std::variant> EngineGet2DFXPosition(CClient2DFX* effect); + static std::variant EngineGetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property); + static std::variant EngineGet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property); + static std::variant EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index); + static std::variant EngineGet2DFXProperties(CClient2DFX* effect); static std::variant EngineGetModel2DFXCount(std::uint32_t modelID); - static bool EngineResetModel2DFXEffects(std::uint32_t modelID, std::optional removeCustomEffects); private: static void AddEngineColClass(lua_State* luaVM); diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 4c56b0807d..0e51570673 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -119,6 +119,54 @@ enum class e2dCoronaFlashType : std::uint8_t ON4_OFF6, }; +enum class e2dEffectProperty +{ + // light properties + FAR_CLIP_DISTANCE, + LIGHT_RANGE, + CORONA_SIZE, + SHADOW_SIZE, + SHADOW_MULT, + FLASH_TYPE, + CORONA_REFLECTION, + FLARE_TYPE, + CORONA_FLAGS, + SHADOW_DISTANCE, + OFFSET_X, + OFFSET_Y, + OFFSET_Z, + COLOR, + CORONA_NAME, + SHADOW_NAME, + + // particle properties + PRT_NAME, + + // roadsign properties + SIZE_X, + SIZE_Y, + ROT_X, + ROT_Y, + ROT_Z, + ROADSIGN_FLAGS, + TEXT, + TEXT_2, + TEXT_3, + TEXT_4, + + // escalator properties + BOTTOM_X, + BOTTOM_Y, + BOTTOM_Z, + TOP_X, + TOP_Y, + TOP_Z, + END_X, + END_Y, + END_Z, + DIRECTION, +}; + enum class e2dAttractorType : std::int8_t { UNDEFINED = -1, @@ -299,7 +347,7 @@ class CModelInfo // 2dfx functions virtual C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type) = 0; - virtual void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false) = 0; + virtual void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false, bool decrementCounters = false) = 0; virtual bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false) = 0; virtual bool RemoveAll2DFXEffects(bool includeDefault = false) = 0; From a26b3e65a15195435d8f2fdf8e2bc4203f7a7369 Mon Sep 17 00:00:00 2001 From: FileEX Date: Thu, 26 Sep 2024 15:46:45 +0200 Subject: [PATCH 07/17] Fix color value --- Client/mods/deathmatch/logic/CClient2DFXManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index 0adb4b008a..6c2079153e 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -117,8 +117,8 @@ bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, const e light.offsetZ = static_cast(std::get(*offsetZ)); auto* color = MapFind(effectData, "color"); - int colorValue = static_cast(std::get(*color)); - light.color = RwColor{static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; + unsigned long colorValue = static_cast(std::get(*color)); + light.color = RwColor{static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; auto* coronaTexture = MapFind(effectData, "coronaName"); auto* shadowTexture = MapFind(effectData, "shadowName"); @@ -416,8 +416,8 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - int colorValue = static_cast(std::get(propertyValue)); - light.color = RwColor{static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; + unsigned long colorValue = static_cast(std::get(propertyValue)); + light.color = RwColor{static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; return true; } From 0f8cbc18de0c87aa28f7bdf18dc01f42179bdcd7 Mon Sep 17 00:00:00 2001 From: FileEX Date: Thu, 26 Sep 2024 18:36:03 +0200 Subject: [PATCH 08/17] Cleanup 1/2 --- Client/game_sa/C2DEffectSAInterface.h | 11 +- Client/game_sa/CModelInfoSA.cpp | 4 +- .../deathmatch/logic/CClient2DFXManager.cpp | 175 ++++-------------- .../deathmatch/logic/CClient2DFXManager.h | 2 +- .../logic/lua/CLuaFunctionParseHelpers.cpp | 3 +- .../logic/luadefs/CLuaEngineDefs.cpp | 4 +- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 4 +- Client/sdk/game/CModelInfo.h | 5 +- 8 files changed, 58 insertions(+), 150 deletions(-) diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h index 90c1f8f1f5..624cb58757 100644 --- a/Client/game_sa/C2DEffectSAInterface.h +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -288,16 +288,19 @@ static void PrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& sha { using RwTextureDestroy = void(__cdecl*)(RwTexture*); - if (coronaTex) + if (coronaTex && coronaName) ((RwTextureDestroy)FUNC_RwTextureDestroy)(coronaTex); - if (shadowTex) + if (shadowTex && shadowName) ((RwTextureDestroy)FUNC_RwTextureDestroy)(shadowTex); } // Call RwReadTexture using RwReadTexture = RwTexture*(__cdecl*)(const char*, const char*); - coronaTex = ((RwReadTexture)FUNC_RwReadTexture)(coronaName, nullptr); - shadowTex = ((RwReadTexture)FUNC_RwReadTexture)(shadowName, nullptr); + if (coronaName) + coronaTex = ((RwReadTexture)FUNC_RwReadTexture)(coronaName, nullptr); + + if (shadowName) + shadowTex = ((RwReadTexture)FUNC_RwReadTexture)(shadowName, nullptr); // Call CTxdStore::PopCurrentTxd ((void(__cdecl*)())FUNC_PopCurrentTxd)(); diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 5910e15d6e..6f5cd7ec78 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -2175,9 +2175,10 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) it = map.erase(it); } - // Clear both maps + // Clear maps map.clear(); ms_DefaultEffectsMap.clear(); + removedDefaultEffects.clear(); // Remove all custom effects if (removeCustomEffects) @@ -2352,7 +2353,6 @@ bool CModelInfoSA::RemoveAll2DFXEffects(bool includeDefault) return false; std::uint32_t numEffects = m_pInterface->ucNumOf2DEffects; - for (std::uint32_t i = 0; i < numEffects; i++) { C2DEffectSAInterface* effect = Get2DFXFromIndex(i); diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index 6c2079153e..5a8372d476 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -11,6 +11,7 @@ #include "CClient2DFXManager.h" #define mask(n) ((1 << (n)) - 1) +static constexpr std::size_t roadsignTextSize = 64; CClient2DFXManager::CClient2DFXManager(CClientManager* mainManager) : m_mainManager(mainManager) { @@ -68,134 +69,22 @@ bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, const e if (!effect) return false; - // Set properties - switch (effect->type) - { - case e2dEffectType::LIGHT: - { - t2dEffectLight& light = effect->effect.light; - - auto* drawDistance = MapFind(effectData, "drawDistance"); - light.coronaFarClip = std::get(*drawDistance); - - auto* lightRange = MapFind(effectData, "lightRange"); - light.pointLightRange = std::get(*lightRange); - - auto* coronaSize = MapFind(effectData, "coronaSize"); - light.coronaSize = std::get(*coronaSize); - - auto* shadowSize = MapFind(effectData, "shadowSize"); - light.shadowSize = std::get(*shadowSize); - - auto* shadowMultiplier = MapFind(effectData, "shadowMultiplier"); - light.shadowColorMultiplier = static_cast(std::get(*shadowMultiplier)); - - auto* showMode = MapFind(effectData, "showMode"); - e2dCoronaFlashType flashType; - StringToEnum(std::get(*showMode), flashType); - light.coronaFlashType = flashType; - - auto* coronaReflection = MapFind(effectData, "coronaReflection"); - light.coronaEnableReflection = std::get(*coronaReflection); - - auto* coronaFlareType = MapFind(effectData, "flareType"); - light.coronaFlareType = static_cast(std::get(*coronaFlareType)); - - auto* flags = MapFind(effectData, "flags"); - light.flags = static_cast(std::get(*flags)); - - auto* shadowZDistance = MapFind(effectData, "shadowDistance"); - light.shadowZDistance = static_cast(std::get(*shadowZDistance)); - - auto* offsetX = MapFind(effectData, "offsetX"); - light.offsetX = static_cast(std::get(*offsetX)); - - auto* offsetY = MapFind(effectData, "offsetY"); - light.offsetY = static_cast(std::get(*offsetY)); - - auto* offsetZ = MapFind(effectData, "offsetZ"); - light.offsetZ = static_cast(std::get(*offsetZ)); - - auto* color = MapFind(effectData, "color"); - unsigned long colorValue = static_cast(std::get(*color)); - light.color = RwColor{static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; + bool success = true; - auto* coronaTexture = MapFind(effectData, "coronaName"); - auto* shadowTexture = MapFind(effectData, "shadowName"); - PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(*coronaTexture).c_str(), std::get(*shadowTexture).c_str(), true); - break; - } - case e2dEffectType::PARTICLE: + for (const auto& [k, v] : effectData) + { + e2dEffectProperty property; + if (!StringToEnum(k, property)) { - auto* particleName = MapFind(effectData, "name"); - std::strncpy(effect->effect.particle.szName, std::get(*particleName).c_str(), sizeof(effect->effect.particle.szName) - 1); - - break; + success = false; + continue; } - case e2dEffectType::ROADSIGN: - { - t2dEffectRoadsign& roadsign = effect->effect.roadsign; - - auto* sizeX = MapFind(effectData, "sizeX"); - auto* sizeY = MapFind(effectData, "sizeY"); - roadsign.size = RwV2d{std::get(*sizeX), std::get(*sizeY)}; - auto* rotX = MapFind(effectData, "rotX"); - auto* rotY = MapFind(effectData, "rotY"); - auto* rotZ = MapFind(effectData, "rotZ"); - roadsign.rotation = RwV3d{std::get(*rotX), std::get(*rotY), std::get(*rotZ)}; - - auto* flags = MapFind(effectData, "flags"); - roadsign.flags = static_cast(std::get(*flags)); - - auto* text = MapFind(effectData, "text"); - auto* text2 = MapFind(effectData, "text2"); - auto* text3 = MapFind(effectData, "text3"); - auto* text4 = MapFind(effectData, "text4"); - - roadsign.text = static_cast(malloc(64)); - if (roadsign.text) - { - std::memcpy(roadsign.text, std::get(*text).c_str(), 4); - std::memcpy(roadsign.text + 4, std::get(*text2).c_str(), 4); - std::memcpy(roadsign.text + 8, std::get(*text3).c_str(), 4); - std::memcpy(roadsign.text + 12, std::get(*text4).c_str(), 4); - } - - roadsign.atomic = nullptr; - - break; - } - case e2dEffectType::ESCALATOR: - { - t2dEffectEscalator& escalator = effect->effect.escalator; - - auto* bottomX = MapFind(effectData, "bottomX"); - auto* bottomY = MapFind(effectData, "bottomY"); - auto* bottomZ = MapFind(effectData, "bottomZ"); - escalator.bottom = RwV3d{std::get(*bottomX), std::get(*bottomY), std::get(*bottomZ)}; - - auto* topX = MapFind(effectData, "topX"); - auto* topY = MapFind(effectData, "topY"); - auto* topZ = MapFind(effectData, "topZ"); - escalator.top = RwV3d{std::get(*topX), std::get(*topY), std::get(*topZ)}; - - auto* endX = MapFind(effectData, "endX"); - auto* endY = MapFind(effectData, "endY"); - auto* endZ = MapFind(effectData, "endZ"); - escalator.end = RwV3d{std::get(*endX), std::get(*endY), std::get(*endZ)}; - - auto* dir = MapFind(effectData, "direction"); - escalator.direction = static_cast(std::get(*dir)); - - break; - } - case e2dEffectType::SUN_GLARE: // This effect has no properties but works in MTA - default: - break; + if (!Set2DFXProperty(effect, property, v)) + success = false; } - return true; + return success; } effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect) const @@ -276,7 +165,7 @@ effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect return properties; } -bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue) +bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue) { if (!effect) return false; @@ -366,7 +255,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d } break; } - case e2dEffectProperty::CORONA_FLAGS: + case e2dEffectProperty::FLAGS: { if (std::holds_alternative(propertyValue)) { @@ -431,7 +320,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d e2dEffectTextureName coronaName; if (StringToEnum(std::get(propertyValue), coronaName)) { - PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(propertyValue).c_str(), light.shadowTex->name, true); + PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(propertyValue).c_str(), light.shadowTex ? light.shadowTex->name : nullptr, true); return true; } } @@ -445,7 +334,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d e2dEffectTextureName shadowName; if (StringToEnum(std::get(propertyValue), shadowName)) { - PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, light.coronaTex->name, std::get(propertyValue).c_str(), true); + PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, light.coronaTex ? light.coronaTex->name : nullptr, std::get(propertyValue).c_str(), true); return true; } } @@ -465,6 +354,8 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d if (std::holds_alternative(propertyValue)) { std::strncpy(effect->effect.particle.szName, std::get(propertyValue).c_str(), sizeof(effect->effect.particle.szName) - 1); + effect->effect.particle.szName[sizeof(effect->effect.particle.szName) - 1] = '\0'; + return true; } } @@ -527,7 +418,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d break; } - case e2dEffectProperty::ROADSIGN_FLAGS: + case e2dEffectProperty::FLAGS: { if (std::holds_alternative(propertyValue)) { @@ -542,9 +433,13 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d if (std::holds_alternative(propertyValue)) { if (!roadsign.text) - roadsign.text = static_cast(malloc(64)); + roadsign.text = static_cast(malloc(roadsignTextSize)); + + if (!roadsign.text) + break; std::memcpy(roadsign.text, std::get(propertyValue).c_str(), 4); + roadsign.text[roadsignTextSize - 1] = '\0'; return true; } @@ -555,9 +450,13 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d if (std::holds_alternative(propertyValue)) { if (!roadsign.text) - roadsign.text = static_cast(malloc(64)); + roadsign.text = static_cast(malloc(roadsignTextSize)); + + if (!roadsign.text) + break; std::memcpy(roadsign.text + 4, std::get(propertyValue).c_str(), 4); + roadsign.text[roadsignTextSize - 1] = '\0'; return true; } @@ -568,9 +467,13 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d if (std::holds_alternative(propertyValue)) { if (!roadsign.text) - roadsign.text = static_cast(malloc(64)); + roadsign.text = static_cast(malloc(roadsignTextSize)); + + if (!roadsign.text) + break; std::memcpy(roadsign.text + 8, std::get(propertyValue).c_str(), 4); + roadsign.text[roadsignTextSize - 1] = '\0'; return true; } @@ -581,9 +484,13 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d if (std::holds_alternative(propertyValue)) { if (!roadsign.text) - roadsign.text = static_cast(malloc(64)); + roadsign.text = static_cast(malloc(roadsignTextSize)); + + if (!roadsign.text) + break; std::memcpy(roadsign.text + 12, std::get(propertyValue).c_str(), 4); + roadsign.text[roadsignTextSize - 1] = '\0'; return true; } @@ -740,7 +647,7 @@ std::variant CClient2DFXManager::Get2DFXProperty(C2DEf return light.coronaEnableReflection; case e2dEffectProperty::FLARE_TYPE: return static_cast(light.coronaFlareType); - case e2dEffectProperty::CORONA_FLAGS: + case e2dEffectProperty::FLAGS: return static_cast(light.flags); case e2dEffectProperty::SHADOW_DISTANCE: return static_cast(light.shadowZDistance); @@ -786,7 +693,7 @@ std::variant CClient2DFXManager::Get2DFXProperty(C2DEf return roadsign.rotation.y; case e2dEffectProperty::ROT_Z: return roadsign.rotation.z; - case e2dEffectProperty::ROADSIGN_FLAGS: + case e2dEffectProperty::FLAGS: return static_cast(roadsign.flags); case e2dEffectProperty::TEXT: case e2dEffectProperty::TEXT_2: @@ -1018,7 +925,7 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp return "Invalid \"endZ\" value"; auto* direction = MapFind(effectData, "direction"); - if (!direction || !std::holds_alternative(*direction) || (std::get(*direction) < 0 || std::get(*direction) > 1.0f)) + if (!direction || !std::holds_alternative(*direction) || (std::get(*direction) < 0.0f || std::get(*direction) > 1.0f)) return "Invalid \"direction\" value"; break; diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.h b/Client/mods/deathmatch/logic/CClient2DFXManager.h index 82e24a6566..5dd9220bcc 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.h +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.h @@ -27,7 +27,7 @@ class CClient2DFXManager bool Set2DFXProperties(C2DEffectSAInterface* effect, const effectDataMap& effectData); effectDataMap Get2DFXProperties(C2DEffectSAInterface* effect) const; - bool Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue); + bool Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue); std::variant Get2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property); void Set2DFXPosition(C2DEffectSAInterface* effect, const CVector& position); diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 83e014e3b8..0a3cf88e95 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -984,7 +984,6 @@ ADD_ENUM(e2dEffectProperty::SHADOW_MULT, "shadowMultiplier") ADD_ENUM(e2dEffectProperty::FLASH_TYPE, "showMode") ADD_ENUM(e2dEffectProperty::CORONA_REFLECTION, "coronaReflection") ADD_ENUM(e2dEffectProperty::FLARE_TYPE, "flareType") -ADD_ENUM(e2dEffectProperty::CORONA_FLAGS, "flags") ADD_ENUM(e2dEffectProperty::SHADOW_DISTANCE, "shadowDistance") ADD_ENUM(e2dEffectProperty::OFFSET_X, "offsetX") ADD_ENUM(e2dEffectProperty::OFFSET_Y, "offsetY") @@ -992,6 +991,7 @@ ADD_ENUM(e2dEffectProperty::OFFSET_Z, "offsetZ") ADD_ENUM(e2dEffectProperty::COLOR, "color") ADD_ENUM(e2dEffectProperty::CORONA_NAME, "coronaName") ADD_ENUM(e2dEffectProperty::SHADOW_NAME, "shadowName") +ADD_ENUM(e2dEffectProperty::FLAGS, "flags") ADD_ENUM(e2dEffectProperty::PRT_NAME, "name") @@ -1000,7 +1000,6 @@ ADD_ENUM(e2dEffectProperty::SIZE_Y, "sizeY") ADD_ENUM(e2dEffectProperty::ROT_X, "rotX") ADD_ENUM(e2dEffectProperty::ROT_Y, "rotY") ADD_ENUM(e2dEffectProperty::ROT_Z, "rotZ") -ADD_ENUM(e2dEffectProperty::ROADSIGN_FLAGS, "flags") ADD_ENUM(e2dEffectProperty::TEXT, "text1") ADD_ENUM(e2dEffectProperty::TEXT_2, "text2") ADD_ENUM(e2dEffectProperty::TEXT_3, "text3") diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index b81178f433..69ebd9068f 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -2674,7 +2674,7 @@ bool CLuaEngineDefs::EngineSet2DFXProperties(CClient2DFX* effect, effectDataMap return m_p2DFXManager->Set2DFXProperties(effect->Get2DFX(), effectData); } -bool CLuaEngineDefs::EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue) +bool CLuaEngineDefs::EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue) { if (!CClient2DFXManager::IsValidModel(modelID)) throw std::invalid_argument("Invalid model ID"); @@ -2694,7 +2694,7 @@ bool CLuaEngineDefs::EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint return m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue); } -bool CLuaEngineDefs::EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue) +bool CLuaEngineDefs::EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue) { return m_p2DFXManager->Set2DFXProperty(effect->Get2DFX(), property, propertyValue); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index e64d571d6c..b844f30355 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -102,8 +102,8 @@ class CLuaEngineDefs : public CLuaDefs // Set functions static bool EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData); static bool EngineSet2DFXProperties(CClient2DFX* effect, effectDataMap effectData); - static bool EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue); - static bool EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue); + static bool EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue); + static bool EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue); static bool EngineSetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position); static bool EngineSet2DFXPosition(CClient2DFX* effect, CVector position); diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 0e51570673..bb1036d498 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -122,7 +122,7 @@ enum class e2dCoronaFlashType : std::uint8_t enum class e2dEffectProperty { // light properties - FAR_CLIP_DISTANCE, + FAR_CLIP_DISTANCE = 0, LIGHT_RANGE, CORONA_SIZE, SHADOW_SIZE, @@ -130,7 +130,6 @@ enum class e2dEffectProperty FLASH_TYPE, CORONA_REFLECTION, FLARE_TYPE, - CORONA_FLAGS, SHADOW_DISTANCE, OFFSET_X, OFFSET_Y, @@ -138,6 +137,7 @@ enum class e2dEffectProperty COLOR, CORONA_NAME, SHADOW_NAME, + FLAGS, // for light & roadsign // particle properties PRT_NAME, @@ -148,7 +148,6 @@ enum class e2dEffectProperty ROT_X, ROT_Y, ROT_Z, - ROADSIGN_FLAGS, TEXT, TEXT_2, TEXT_3, From 3f687cc326ddce7587316dcfb87676e1dbfc134c Mon Sep 17 00:00:00 2001 From: FileEX Date: Thu, 26 Sep 2024 21:48:14 +0200 Subject: [PATCH 09/17] Cleanup 2/2 --- Client/game_sa/CModelInfoSA.cpp | 86 ++++++------------- Client/game_sa/CModelInfoSA.h | 2 +- Client/mods/deathmatch/logic/CClient2DFX.cpp | 2 +- .../deathmatch/logic/CClient2DFXManager.cpp | 2 +- .../logic/luadefs/CLuaEngineDefs.cpp | 1 + Client/sdk/game/CModelInfo.h | 2 +- 6 files changed, 33 insertions(+), 62 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 6f5cd7ec78..5a6f950280 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -1206,6 +1206,7 @@ void CModelInfoSA::StaticReset2DFXEffects() // Delete copy of the default effect delete innerIter->second; + innerIter->second = nullptr; innerIter = iter->second.erase(innerIter); } @@ -2177,8 +2178,7 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) // Clear maps map.clear(); - ms_DefaultEffectsMap.clear(); - removedDefaultEffects.clear(); + ms_DefaultEffectsMap.erase(m_dwModelID); // Remove all custom effects if (removeCustomEffects) @@ -2190,18 +2190,8 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) if (!effect) continue; - // If this is not custom effect, continue - auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); - if (it == d2fxEffects.end()) - continue; - - m_pInterface->ucNumOf2DEffects--; - MapGet(m_numCustom2dfxEffects, m_pInterface)--; - - Remove2DFX(effect, true); + Remove2DFX(effect, false); } - - d2fxEffects.clear(); } return true; @@ -2228,10 +2218,30 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const return effectInterface; } -void CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool isCustom, bool decrementCounters) +bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) { + m_pInterface = ppModelInfo[m_dwModelID]; + if (!m_pInterface) + return false; + if (!effect) - return; + return false; + + auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); + bool isCustomEffect = it != d2fxEffects.end(); + + if (!includeDefault && !isCustomEffect) + return false; + + if (!isCustomEffect) + StoreDefault2DFXEffect(effect); + + m_pInterface->ucNumOf2DEffects--; + if (isCustomEffect) + { + MapGet(m_numCustom2dfxEffects, m_pInterface)--; + d2fxEffects.erase(it); + } switch (effect->type) { @@ -2296,18 +2306,8 @@ void CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool isCustom, bool } } - if (decrementCounters) - { - m_pInterface->ucNumOf2DEffects--; - MapGet(m_numCustom2dfxEffects, m_pInterface)--; - - auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); - if (it != d2fxEffects.end()) - d2fxEffects.erase(it); - } - // If it's custom effect then delete it. If it's default effect then store it as removed - if (isCustom) + if (isCustomEffect) { delete effect; effect = nullptr; @@ -2326,24 +2326,7 @@ bool CModelInfoSA::Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefa if (!effect) return false; - auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); - bool isCustomEffect = it != d2fxEffects.end(); - - if (!includeDefault && !isCustomEffect) - return false; - - if (!isCustomEffect) - StoreDefault2DFXEffect(effect); - - m_pInterface->ucNumOf2DEffects--; - if (isCustomEffect) - { - MapGet(m_numCustom2dfxEffects, m_pInterface)--; - d2fxEffects.erase(it); - } - - Remove2DFX(effect, isCustomEffect); - return true; + return Remove2DFX(effect, includeDefault); } bool CModelInfoSA::RemoveAll2DFXEffects(bool includeDefault) @@ -2359,23 +2342,10 @@ bool CModelInfoSA::RemoveAll2DFXEffects(bool includeDefault) if (!effect) continue; - auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); - bool isCustomEffect = it != d2fxEffects.end(); - if (!includeDefault && !isCustomEffect) - continue; - - if (!isCustomEffect) - StoreDefault2DFXEffect(effect); - - m_pInterface->ucNumOf2DEffects--; - if (isCustomEffect) - MapGet(m_numCustom2dfxEffects, m_pInterface)--; - - Remove2DFX(effect, isCustomEffect); + Remove2DFX(effect, includeDefault); } d2fxEffects.clear(); - return true; } diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index da195f4cf7..6c5691a893 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -472,7 +472,7 @@ class CModelInfoSA : public CModelInfo // 2DFX functions C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type); - void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false, bool decrementCounters = false); + bool Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault); bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false); bool RemoveAll2DFXEffects(bool includeDefault); C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index); diff --git a/Client/mods/deathmatch/logic/CClient2DFX.cpp b/Client/mods/deathmatch/logic/CClient2DFX.cpp index a3b0ce7810..3bbe11e5ad 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFX.cpp @@ -29,7 +29,7 @@ CClient2DFX::~CClient2DFX() if (!modelInfo) return; - modelInfo->Remove2DFX(m_effectInterface, true, true); + modelInfo->Remove2DFX(m_effectInterface, false); } bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData) diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index 5a8372d476..d3f1ed3810 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -305,7 +305,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - unsigned long colorValue = static_cast(std::get(propertyValue)); + std::uint32_t colorValue = static_cast(std::get(propertyValue)); light.color = RwColor{static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; return true; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 69ebd9068f..f701664ef8 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -2691,6 +2691,7 @@ bool CLuaEngineDefs::EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint if (!effect) return false; + modelInfo->StoreDefault2DFXEffect(effect); return m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue); } diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index bb1036d498..6de796075b 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -346,7 +346,7 @@ class CModelInfo // 2dfx functions virtual C2DEffectSAInterface* Add2DFXEffect(const CVector& position, const e2dEffectType& type) = 0; - virtual void Remove2DFX(C2DEffectSAInterface* effect, bool isCustom = false, bool decrementCounters = false) = 0; + virtual bool Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) = 0; virtual bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false) = 0; virtual bool RemoveAll2DFXEffects(bool includeDefault = false) = 0; From 6919ff41118b4a9c2b13d90e55f9e143d6502984 Mon Sep 17 00:00:00 2001 From: FileEX Date: Fri, 27 Sep 2024 20:25:40 +0200 Subject: [PATCH 10/17] Update CModelInfoSA.cpp --- Client/game_sa/CModelInfoSA.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 5a6f950280..6fe90b9fde 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -1218,11 +1218,6 @@ void CModelInfoSA::StaticReset2DFXEffects() MapSet(m_numCustom2dfxEffects, modelInfoInterface, 0); } - // Remove all custom effects - for (auto& customEffect : d2fxEffects) - if (customEffect) - delete customEffect; - // Clear maps removedDefaultEffects.clear(); ms_DefaultEffectsMap.clear(); @@ -2214,7 +2209,6 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const // Save our effect d2fxEffects.push_back(effectInterface); - return effectInterface; } From fdddb297778e4014609518daa05ed8a73dc35622 Mon Sep 17 00:00:00 2001 From: FileEX Date: Fri, 27 Sep 2024 22:06:21 +0200 Subject: [PATCH 11/17] Move defs to separate file --- Client/mods/deathmatch/StdInc.h | 1 + Client/mods/deathmatch/logic/lua/CLuaMain.cpp | 1 + .../mods/deathmatch/logic/lua/CLuaManager.cpp | 1 + .../deathmatch/logic/luadefs/CLua2DFXDefs.cpp | 301 ++++++++++++++++++ .../deathmatch/logic/luadefs/CLua2DFXDefs.h | 43 +++ .../logic/luadefs/CLuaEngineDefs.cpp | 271 ---------------- .../deathmatch/logic/luadefs/CLuaEngineDefs.h | 23 -- 7 files changed, 347 insertions(+), 294 deletions(-) create mode 100644 Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp create mode 100644 Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.h diff --git a/Client/mods/deathmatch/StdInc.h b/Client/mods/deathmatch/StdInc.h index 465478454f..8ca32aaf5b 100644 --- a/Client/mods/deathmatch/StdInc.h +++ b/Client/mods/deathmatch/StdInc.h @@ -146,6 +146,7 @@ #include #include #include +#include #include // Shared includes diff --git a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp index 6b794be016..33c25e78de 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaMain.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaMain.cpp @@ -128,6 +128,7 @@ void CLuaMain::InitClasses(lua_State* luaVM) CLuaWaterDefs::AddClass(luaVM); CLuaWeaponDefs::AddClass(luaVM); CLuaBuildingDefs::AddClass(luaVM); + CLua2DFXDefs::AddClass(luaVM); CLuaShared::AddClasses(luaVM); } diff --git a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp index c2a28809af..ae1d1585d8 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp @@ -281,4 +281,5 @@ void CLuaManager::LoadCFunctions() CLuaClientDefs::LoadFunctions(); CLuaDiscordDefs::LoadFunctions(); CLuaBuildingDefs::LoadFunctions(); + CLua2DFXDefs::LoadFunctions(); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp new file mode 100644 index 0000000000..9d87075f93 --- /dev/null +++ b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp @@ -0,0 +1,301 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: mods/shared_logic/luadefs/CLua2DFXDefs.cpp + * PURPOSE: Lua definitions class + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CLua2DFXDefs.h" +#include + +void CLua2DFXDefs::LoadFunctions() +{ + constexpr static const std::pair functions[]{ + // Create / destroy functions + {"addModel2DFX", ArgumentParser}, + {"removeModel2DFX", ArgumentParser}, + {"resetModel2DFX", ArgumentParser}, + + // Set functions + {"setModel2DFXProperties", ArgumentParser}, + {"set2DFXProperties", ArgumentParser}, + {"setModel2DFXProperty", ArgumentParser}, + {"set2DFXProperty", ArgumentParser}, + {"setModel2DFXPosition", ArgumentParser}, + {"set2DFXPosition", ArgumentParser}, + + // Get functions + {"getModel2DFXProperties", ArgumentParser}, + {"get2DFXProperties", ArgumentParser}, + {"getModel2DFXProperty", ArgumentParser}, + {"get2DFXProperty", ArgumentParser}, + {"getModel2DFXPosition", ArgumentParser}, + {"get2DFXPosition", ArgumentParser}, + {"getModel2DFXCount", ArgumentParser}, + }; + + // Add functions + for (const auto& [name, func] : functions) + CLuaCFunctions::AddFunction(name, func); +} + +void CLua2DFXDefs::AddClass(lua_State* luaVM) +{ + lua_newclass(luaVM); + + lua_classfunction(luaVM, "add", "addModel2DFX"); + + lua_classfunction(luaVM, "setProperties", "set2DFXProperties"); + lua_classfunction(luaVM, "setProperty", "set2DFXProperty"); + lua_classfunction(luaVM, "setPosition", "set2DFXPosition"); + + lua_classfunction(luaVM, "getProperties", "get2DFXProperties"); + lua_classfunction(luaVM, "getProperty", "get2DFXProperty"); + lua_classfunction(luaVM, "getPosition", "get2DFXPosition"); + + lua_classvariable(luaVM, "properties", "get2DFXProperties", "set2DFXProperties"); + lua_classvariable(luaVM, "position", "get2DFXPosition", "set2DFXPosition"); + + lua_registerclass(luaVM, "2DFX", "Element"); +} + +std::variant CLua2DFXDefs::AddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, effectDataMap effectData) +{ + // Only these effects make sense in MTA + if (effectType != e2dEffectType::LIGHT && effectType != e2dEffectType::PARTICLE && effectType != e2dEffectType::ROADSIGN && + effectType != e2dEffectType::ESCALATOR && effectType != e2dEffectType::SUN_GLARE) + return false; + + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + const char* error = CClient2DFXManager::IsValidEffectData(effectType, effectData); + if (error) + throw LuaFunctionError(error); + + CClient2DFX* effect = m_p2DFXManager->Add2DFX(modelID, position, effectType, effectData); + if (!effect) + return false; + + CResource* resource = &lua_getownerresource(luaVM); + if (resource) + effect->SetParent(resource->GetResource2DFXRoot()); + + return effect; +} + +bool CLua2DFXDefs::RemoveModel2DFX(std::uint32_t modelID, std::optional index, std::optional includeDefault) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + if (index.has_value()) + { + auto count = GetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + } + + return index.has_value() ? modelInfo->Remove2DFXEffectAtIndex(index.value(), includeDefault.value_or(false)) : modelInfo->RemoveAll2DFXEffects(includeDefault.value_or(false)); +} + +bool CLua2DFXDefs::ResetModel2DFX(std::uint32_t modelID, std::optional removeCustomEffects) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + return modelInfo->Reset2DFXEffects(removeCustomEffects.value_or(true)); +} + +bool CLua2DFXDefs::SetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = GetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + const char* error = CClient2DFXManager::IsValidEffectData(effect->type, effectData); + if (error) + throw LuaFunctionError(error); + + modelInfo->StoreDefault2DFXEffect(effect); + return m_p2DFXManager->Set2DFXProperties(effect, effectData); +} + +bool CLua2DFXDefs::Set2DFXProperties(CClient2DFX* effect, effectDataMap effectData) +{ + const char* error = CClient2DFXManager::IsValidEffectData(effect->Get2DFXType(), effectData); + if (error) + throw LuaFunctionError(error); + + return m_p2DFXManager->Set2DFXProperties(effect->Get2DFX(), effectData); +} + +bool CLua2DFXDefs::SetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = GetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + modelInfo->StoreDefault2DFXEffect(effect); + return m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue); +} + +bool CLua2DFXDefs::Set2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue) +{ + return m_p2DFXManager->Set2DFXProperty(effect->Get2DFX(), property, propertyValue); +} + +bool CLua2DFXDefs::SetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = GetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + modelInfo->StoreDefault2DFXEffect(effect); + m_p2DFXManager->Set2DFXPosition(effect, position); + return true; +} + +bool CLua2DFXDefs::Set2DFXPosition(CClient2DFX* effect, CVector position) +{ + m_p2DFXManager->Set2DFXPosition(effect->Get2DFX(), position); + return true; +} + +std::variant> CLua2DFXDefs::GetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = GetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + CVector* position = m_p2DFXManager->Get2DFXPosition(effect); + return std::make_tuple(position->fX, position->fY, position->fZ); +} + +std::variant> CLua2DFXDefs::Get2DFXPosition(CClient2DFX* effect) +{ + CVector* position = m_p2DFXManager->Get2DFXPosition(effect->Get2DFX()); + if (!position) + return false; + + return std::make_tuple(position->fX, position->fY, position->fZ); +} + +std::variant CLua2DFXDefs::GetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = GetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + return m_p2DFXManager->Get2DFXProperties(effect); +} + +std::variant CLua2DFXDefs::Get2DFXProperties(CClient2DFX* effect) +{ + return m_p2DFXManager->Get2DFXProperties(effect->Get2DFX()); +} + +std::variant CLua2DFXDefs::GetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + auto count = GetModel2DFXCount(modelID); + if (std::holds_alternative(count) && index >= std::get(count)) + throw std::invalid_argument("Invalid effect index"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + auto* effect = modelInfo->Get2DFXFromIndex(index); + if (!effect) + return false; + + return m_p2DFXManager->Get2DFXProperty(effect, property); +} + +std::variant CLua2DFXDefs::Get2DFXProperty(CClient2DFX* effect, e2dEffectProperty property) +{ + return m_p2DFXManager->Get2DFXProperty(effect->Get2DFX(), property); +} + +std::variant CLua2DFXDefs::GetModel2DFXCount(std::uint32_t modelID) +{ + if (!CClient2DFXManager::IsValidModel(modelID)) + throw std::invalid_argument("Invalid model ID"); + + CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + if (!modelInfo) + return false; + + return modelInfo->Get2DFXCount(); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.h b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.h new file mode 100644 index 0000000000..5b1b087ca6 --- /dev/null +++ b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.h @@ -0,0 +1,43 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: mods/shared_logic/luadefs/CLua2DFXDefs.cpp + * PURPOSE: Lua definitions class + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once +#include "CLuaDefs.h" +#include + +class CLua2DFXDefs : public CLuaDefs +{ +public: + static void LoadFunctions(); + static void AddClass(lua_State* luaVM); + + // Create/destroy functions + static std::variant AddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, effectDataMap effectData); + static bool RemoveModel2DFX(std::uint32_t modelID, std::optional index, std::optional includeDefault); + static bool ResetModel2DFX(std::uint32_t modelID, std::optional removeCustomEffects); + + // Set functions + static bool SetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData); + static bool Set2DFXProperties(CClient2DFX* effect, effectDataMap effectData); + static bool SetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue); + static bool Set2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue); + static bool SetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position); + static bool Set2DFXPosition(CClient2DFX* effect, CVector position); + + // Get functions + static std::variant> GetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index); + static std::variant> Get2DFXPosition(CClient2DFX* effect); + static std::variant GetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property); + static std::variant Get2DFXProperty(CClient2DFX* effect, e2dEffectProperty property); + static std::variant GetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index); + static std::variant Get2DFXProperties(CClient2DFX* effect); + static std::variant GetModel2DFXCount(std::uint32_t modelID); +}; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index f701664ef8..be20b18451 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -146,22 +146,6 @@ void CLuaEngineDefs::LoadFunctions() {"engineGetPoolUsedCapacity", ArgumentParser}, {"engineSetPoolCapacity", ArgumentParser}, {"enginePreloadWorldArea", ArgumentParser}, - {"engineAddModel2DFX", ArgumentParser}, - {"engineRemoveModel2DFX", ArgumentParser}, - {"engineSetModel2DFXProperties", ArgumentParser}, - {"engineSet2DFXProperties", ArgumentParser}, - {"engineGetModel2DFXProperties", ArgumentParser}, - {"engineGet2DFXProperties", ArgumentParser}, - {"engineSetModel2DFXProperty", ArgumentParser}, - {"engineSet2DFXProperty", ArgumentParser}, - {"engineGetModel2DFXProperty", ArgumentParser}, - {"engineGet2DFXProperty", ArgumentParser}, - {"engineSet2DFXPosition", ArgumentParser}, - {"engineSetModel2DFXPosition", ArgumentParser}, - {"engineGet2DFXPosition", ArgumentParser}, - {"engineGetModel2DFXPosition", ArgumentParser}, - {"engineGetModel2DFXCount", ArgumentParser}, - {"engineResetModel2DFXEffects", ArgumentParser}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); @@ -236,7 +220,6 @@ void CLuaEngineDefs::AddClass(lua_State* luaVM) AddEngineTxdClass(luaVM); AddEngineDffClass(luaVM); AddEngineImgClass(luaVM); - AddEngine2DFXClass(luaVM); } void CLuaEngineDefs::AddEngineColClass(lua_State* luaVM) @@ -289,24 +272,6 @@ void CLuaEngineDefs::AddEngineDffClass(lua_State* luaVM) lua_registerclass(luaVM, "EngineDFF", "Element"); } -void CLuaEngineDefs::AddEngine2DFXClass(lua_State* luaVM) -{ - lua_newclass(luaVM); - - // set functions - lua_classfunction(luaVM, "setProperties", "engineSet2DFXProperties"); - lua_classfunction(luaVM, "setPosition", "engineSet2DFXPosition"); - - // get functions - lua_classfunction(luaVM, "getProperties", "engineGet2DFXProperties"); - lua_classfunction(luaVM, "getPosition", "engineGet2DFXPosition"); - - lua_classvariable(luaVM, "properties", "engineSet2DFXProperties", "engineGet2DFXProperties"); - lua_classvariable(luaVM, "position", "engineSet2DFXPosition", "engineGet2DFXPosition"); - - lua_registerclass(luaVM, "Effect2DFX", "Element"); -} - int CLuaEngineDefs::EngineLoadCOL(lua_State* luaVM) { SString input; @@ -2595,239 +2560,3 @@ void CLuaEngineDefs::EnginePreloadWorldArea(CVector position, std::optionalGetStreaming()->LoadSceneCollision(&position); } - -std::variant CLuaEngineDefs::EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, effectDataMap effectData) -{ - // Only these effects make sense in MTA - if (effectType != e2dEffectType::LIGHT && effectType != e2dEffectType::PARTICLE && effectType != e2dEffectType::ROADSIGN && - effectType != e2dEffectType::ESCALATOR && effectType != e2dEffectType::SUN_GLARE) - return false; - - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - const char* error = CClient2DFXManager::IsValidEffectData(effectType, effectData); - if (error) - throw LuaFunctionError(error); - - CClient2DFX* effect = m_p2DFXManager->Add2DFX(modelID, position, effectType, effectData); - if (!effect) - return false; - - CResource* resource = &lua_getownerresource(luaVM); - if (resource) - effect->SetParent(resource->GetResource2DFXRoot()); - - return effect; -} - -bool CLuaEngineDefs::EngineRemoveModel2DFX(std::uint32_t modelID, std::optional index, std::optional includeDefault) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - if (index.has_value()) - { - auto count = EngineGetModel2DFXCount(modelID); - if (std::holds_alternative(count) && index >= std::get(count)) - throw std::invalid_argument("Invalid effect index"); - } - - return index.has_value() ? modelInfo->Remove2DFXEffectAtIndex(index.value(), includeDefault.value_or(false)) : modelInfo->RemoveAll2DFXEffects(includeDefault.value_or(false)); -} - -bool CLuaEngineDefs::EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - auto count = EngineGetModel2DFXCount(modelID); - if (std::holds_alternative(count) && index >= std::get(count)) - throw std::invalid_argument("Invalid effect index"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - auto* effect = modelInfo->Get2DFXFromIndex(index); - if (!effect) - return false; - - const char* error = CClient2DFXManager::IsValidEffectData(effect->type, effectData); - if (error) - throw LuaFunctionError(error); - - modelInfo->StoreDefault2DFXEffect(effect); - return m_p2DFXManager->Set2DFXProperties(effect, effectData); -} - -bool CLuaEngineDefs::EngineSet2DFXProperties(CClient2DFX* effect, effectDataMap effectData) -{ - const char* error = CClient2DFXManager::IsValidEffectData(effect->Get2DFXType(), effectData); - if (error) - throw LuaFunctionError(error); - - return m_p2DFXManager->Set2DFXProperties(effect->Get2DFX(), effectData); -} - -bool CLuaEngineDefs::EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - auto count = EngineGetModel2DFXCount(modelID); - if (std::holds_alternative(count) && index >= std::get(count)) - throw std::invalid_argument("Invalid effect index"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - auto* effect = modelInfo->Get2DFXFromIndex(index); - if (!effect) - return false; - - modelInfo->StoreDefault2DFXEffect(effect); - return m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue); -} - -bool CLuaEngineDefs::EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue) -{ - return m_p2DFXManager->Set2DFXProperty(effect->Get2DFX(), property, propertyValue); -} - -bool CLuaEngineDefs::EngineSetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - auto count = EngineGetModel2DFXCount(modelID); - if (std::holds_alternative(count) && index >= std::get(count)) - throw std::invalid_argument("Invalid effect index"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - auto* effect = modelInfo->Get2DFXFromIndex(index); - if (!effect) - return false; - - modelInfo->StoreDefault2DFXEffect(effect); - m_p2DFXManager->Set2DFXPosition(effect, position); - return true; -} - -bool CLuaEngineDefs::EngineSet2DFXPosition(CClient2DFX* effect, CVector position) -{ - m_p2DFXManager->Set2DFXPosition(effect->Get2DFX(), position); - return true; -} - -std::variant> CLuaEngineDefs::EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - auto count = EngineGetModel2DFXCount(modelID); - if (std::holds_alternative(count) && index >= std::get(count)) - throw std::invalid_argument("Invalid effect index"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - auto* effect = modelInfo->Get2DFXFromIndex(index); - if (!effect) - return false; - - CVector* position = m_p2DFXManager->Get2DFXPosition(effect); - return std::make_tuple(position->fX, position->fY, position->fZ); -} - -std::variant> CLuaEngineDefs::EngineGet2DFXPosition(CClient2DFX* effect) -{ - CVector* position = m_p2DFXManager->Get2DFXPosition(effect->Get2DFX()); - if (!position) - return false; - - return std::make_tuple(position->fX, position->fY, position->fZ); -} - -std::variant CLuaEngineDefs::EngineGetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - auto count = EngineGetModel2DFXCount(modelID); - if (std::holds_alternative(count) && index >= std::get(count)) - throw std::invalid_argument("Invalid effect index"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - auto* effect = modelInfo->Get2DFXFromIndex(index); - if (!effect) - return false; - - return m_p2DFXManager->Get2DFXProperty(effect, property); -} - -std::variant CLuaEngineDefs::EngineGet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property) -{ - return m_p2DFXManager->Get2DFXProperty(effect->Get2DFX(), property); -} - -std::variant CLuaEngineDefs::EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - auto count = EngineGetModel2DFXCount(modelID); - if (std::holds_alternative(count) && index >= std::get(count)) - throw std::invalid_argument("Invalid effect index"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - auto* effect = modelInfo->Get2DFXFromIndex(index); - if (!effect) - return false; - - return m_p2DFXManager->Get2DFXProperties(effect); -} - -std::variant CLuaEngineDefs::EngineGet2DFXProperties(CClient2DFX* effect) -{ - return m_p2DFXManager->Get2DFXProperties(effect->Get2DFX()); -} - -std::variant CLuaEngineDefs::EngineGetModel2DFXCount(std::uint32_t modelID) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - return modelInfo->Get2DFXCount(); -} - -bool CLuaEngineDefs::EngineResetModel2DFXEffects(std::uint32_t modelID, std::optional removeCustomEffects) -{ - if (!CClient2DFXManager::IsValidModel(modelID)) - throw std::invalid_argument("Invalid model ID"); - - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); - if (!modelInfo) - return false; - - return modelInfo->Reset2DFXEffects(removeCustomEffects.value_or(true)); -} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index b844f30355..a67ecfc68d 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -94,32 +94,9 @@ class CLuaEngineDefs : public CLuaDefs static void EnginePreloadWorldArea(CVector position, std::optional option); - // 2dfx functions - static std::variant EngineAddModel2DFX(lua_State* luaVM, std::uint32_t modelID, CVector position, e2dEffectType effectType, effectDataMap effectData); - static bool EngineRemoveModel2DFX(std::uint32_t modelID, std::optional index, std::optional includeDefault); - static bool EngineResetModel2DFXEffects(std::uint32_t modelID, std::optional removeCustomEffects); - - // Set functions - static bool EngineSetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData); - static bool EngineSet2DFXProperties(CClient2DFX* effect, effectDataMap effectData); - static bool EngineSetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue); - static bool EngineSet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue); - static bool EngineSetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position); - static bool EngineSet2DFXPosition(CClient2DFX* effect, CVector position); - - // Get functions - static std::variant> EngineGetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index); - static std::variant> EngineGet2DFXPosition(CClient2DFX* effect); - static std::variant EngineGetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property); - static std::variant EngineGet2DFXProperty(CClient2DFX* effect, e2dEffectProperty property); - static std::variant EngineGetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index); - static std::variant EngineGet2DFXProperties(CClient2DFX* effect); - static std::variant EngineGetModel2DFXCount(std::uint32_t modelID); - private: static void AddEngineColClass(lua_State* luaVM); static void AddEngineTxdClass(lua_State* luaVM); static void AddEngineDffClass(lua_State* luaVM); static void AddEngineImgClass(lua_State* luaVM); - static void AddEngine2DFXClass(lua_State* luaVM); }; From ebb2b2ad7ee250293f20c66fdcf0b1c73233e1c4 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 28 Sep 2024 15:10:40 +0200 Subject: [PATCH 12/17] Fix bugs after stream in --- Client/game_sa/CEntitySA.cpp | 79 ++++++++++++++++++++++++-- Client/game_sa/CModelInfoSA.cpp | 99 +++++++++++++++++++++++++++------ Client/game_sa/CModelInfoSA.h | 4 +- Client/sdk/game/CModelInfo.h | 3 + 4 files changed, 161 insertions(+), 24 deletions(-) diff --git a/Client/game_sa/CEntitySA.cpp b/Client/game_sa/CEntitySA.cpp index 02a9f00380..4c6f4228df 100644 --- a/Client/game_sa/CEntitySA.cpp +++ b/Client/game_sa/CEntitySA.cpp @@ -66,11 +66,6 @@ CRect* CEntitySAInterface::GetBoundRect_(CRect* pRect) return pRect; } -void CEntitySAInterface::StaticSetHooks() -{ - HookInstall(0x534120, &CEntitySAInterface::GetBoundRect_); -} - CEntitySA::CEntitySA() { // Set these variables to a constant state @@ -715,3 +710,77 @@ bool CEntitySA::GetUnderwater() { return m_pInterface->bUnderwater; } + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Hook for CEntity::CreateEffects & CEntity::DestroyEffects +// +// Handle modified 2dfx effects during streaming +// This is necessary because once the object is streamed in, the 2dfx effects are loaded from RwStream +// and the default effects are restored even though they have been modified. +// +////////////////////////////////////////////////////////////////////////////////////////// +static void Keep2DFXEffectsBeforeRemove(std::uint32_t modelID) +{ + CModelInfo* modelInfo = pGame->GetModelInfo(modelID); + if (!modelInfo) + return; + + modelInfo->CopyModified2DFXEffects(); +} + +#define HOOKPOS_CEntity_DestroyEffects 0x533C01 +#define HOOKSIZE_CEntity_DestroyEffects 5 +static constexpr DWORD CONTINUE_CEntity_DestroyEffects = 0x533C06; +static void _declspec(naked) HOOK_CEntity_DestroyEffects() +{ + _asm + { + pushad + push eax + call Keep2DFXEffectsBeforeRemove + add esp, 4 + popad + + movzx eax, byte ptr [ecx+0Dh] + push ebp + jmp CONTINUE_CEntity_DestroyEffects + } +} + +static void Restore2DFXEffects(std::uint32_t modelID) +{ + CModelInfo* modelInfo = pGame->GetModelInfo(modelID); + if (!modelInfo) + return; + + modelInfo->RestoreModified2DFXEffects(); +} + +#define HOOKPOS_CEntity_CreateEffects 0x533BAE +#define HOOKSIZE_CEntity_CreateEffects 9 +static constexpr DWORD RETURN_CEntity_CreateEffects = 0x533BB7; +static void _declspec(naked) HOOK_CEntity_CreateEffects() +{ + _asm + { + pushad + push [ebp+22h] + call Restore2DFXEffects + add esp, 4 + popad + + pop edi + pop ebp + pop ebx + add esp, 0C0h + jmp RETURN_CEntity_CreateEffects + } +} + +void CEntitySAInterface::StaticSetHooks() +{ + HookInstall(0x534120, &CEntitySAInterface::GetBoundRect_); + EZHookInstall(CEntity_DestroyEffects); + EZHookInstall(CEntity_CreateEffects); +} diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 6fe90b9fde..aedbf8d6e1 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -37,10 +37,12 @@ std::unordered_map CModelInfo std::unordered_map> CModelInfoSA::ms_VehicleModelDefaultWheelSizes; std::map CModelInfoSA::ms_DefaultTxdIDMap; -std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; -static std::unordered_map m_numCustom2dfxEffects; -static std::vector d2fxEffects; -static std::vector removedDefaultEffects; +static std::unordered_map> ms_DefaultEffectsMap; +static std::unordered_map numCustom2dfxEffects; +static std::vector d2fxEffects; +static std::vector removedDefault2dfxEffects; +static std::unordered_map defaultNumOf2DFXEffects; +static std::unordered_map> tempCopy2dfxEffects; int C2DEffectSA::effect2dPluginOffset = *(int*)0xC3A1E0; // g2dEffectPluginOffset @@ -116,7 +118,7 @@ CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() void CModelInfoSA::SetModelID(DWORD dwModelID) { m_dwModelID = dwModelID; - MapSet(m_numCustom2dfxEffects, ppModelInfo[dwModelID], 0); + MapSet(numCustom2dfxEffects, ppModelInfo[dwModelID], 0); } bool CModelInfoSA::IsBoat() @@ -1197,10 +1199,10 @@ void CModelInfoSA::StaticReset2DFXEffects() memcpy(innerIter->first, innerIter->second, sizeof(C2DEffectSAInterface)); // Increase the counter if this effect was removed - auto& removedEffect = std::find(removedDefaultEffects.begin(), removedDefaultEffects.end(), innerIter->first); - if (removedEffect != removedDefaultEffects.end()) + auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), innerIter->first); + if (removedEffect != removedDefault2dfxEffects.end()) { - removedDefaultEffects.erase(removedEffect); + removedDefault2dfxEffects.erase(removedEffect); modelInfoInterface->ucNumOf2DEffects++; } @@ -1211,15 +1213,15 @@ void CModelInfoSA::StaticReset2DFXEffects() } // Decrement the counter by the number of custom effects - auto customEffectsCount = MapGet(m_numCustom2dfxEffects, modelInfoInterface); + auto customEffectsCount = MapGet(numCustom2dfxEffects, modelInfoInterface); if (customEffectsCount && customEffectsCount > 0) modelInfoInterface->ucNumOf2DEffects -= customEffectsCount; - MapSet(m_numCustom2dfxEffects, modelInfoInterface, 0); + MapSet(numCustom2dfxEffects, modelInfoInterface, 0); } // Clear maps - removedDefaultEffects.clear(); + removedDefault2dfxEffects.clear(); ms_DefaultEffectsMap.clear(); d2fxEffects.clear(); } @@ -2118,6 +2120,9 @@ void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) if (MapContains(ms_DefaultEffectsMap, m_dwModelID) && MapContains(MapGet(ms_DefaultEffectsMap, m_dwModelID), effect)) return; + if (!MapContains(defaultNumOf2DFXEffects, m_dwModelID)) + MapSet(defaultNumOf2DFXEffects, m_dwModelID, ppModelInfo[m_dwModelID]->ucNumOf2DEffects); + // Copy an existing default effect C2DEffectSAInterface* copy = new C2DEffectSAInterface(); memcpy(copy, effect, sizeof(C2DEffectSAInterface)); @@ -2158,10 +2163,10 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) memcpy(it->first, it->second, sizeof(C2DEffectSAInterface)); // Increase the counter if this effect was removed - auto& removedEffect = std::find(removedDefaultEffects.begin(), removedDefaultEffects.end(), it->first); - if (removedEffect != removedDefaultEffects.end()) + auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), it->first); + if (removedEffect != removedDefault2dfxEffects.end()) { - removedDefaultEffects.erase(removedEffect); + removedDefault2dfxEffects.erase(removedEffect); m_pInterface->ucNumOf2DEffects++; } @@ -2205,7 +2210,7 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const // Update counters m_pInterface->ucNumOf2DEffects = m_pInterface->ucNumOf2DEffects ? m_pInterface->ucNumOf2DEffects + 1 : 1; - MapGet(m_numCustom2dfxEffects, m_pInterface)++; + MapGet(numCustom2dfxEffects, m_pInterface)++; // Save our effect d2fxEffects.push_back(effectInterface); @@ -2233,7 +2238,7 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) m_pInterface->ucNumOf2DEffects--; if (isCustomEffect) { - MapGet(m_numCustom2dfxEffects, m_pInterface)--; + MapGet(numCustom2dfxEffects, m_pInterface)--; d2fxEffects.erase(it); } @@ -2307,7 +2312,7 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) effect = nullptr; } else - removedDefaultEffects.push_back(effect); + removedDefault2dfxEffects.push_back(effect); } bool CModelInfoSA::Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault) @@ -2410,7 +2415,7 @@ static C2DEffectSAInterface* Get2dEffect(CBaseModelInfoSAInterface* modelInfo, R static auto* storedEffects = reinterpret_cast(ARRAY_2DFXInfoStore); - std::uint32_t numCustomEffects = m_numCustom2dfxEffects[modelInfo]; + std::uint32_t numCustomEffects = numCustom2dfxEffects[modelInfo]; std::uint32_t numStoredEffects = modelInfo->ucNumOf2DEffects - numPluginEffects - numCustomEffects; if (index < numStoredEffects) @@ -2447,6 +2452,64 @@ static void _declspec(naked) HOOK_Get2dEffect() } } +void CModelInfoSA::CopyModified2DFXEffects() +{ + CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; + if (!modelInfo || modelInfo->ucNumOf2DEffects == 0) + return; + + // Has modified effects? + if (!MapContains(ms_DefaultEffectsMap, m_dwModelID)) + return; + + auto tempVec = std::vector(); + std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); + for (std::uint32_t i = 0; i < numEffects; i++) + { + auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effect) + continue; + + // Copy effect + auto copy = new C2DEffectSAInterface(); + memcpy(copy, effect, sizeof(C2DEffectSAInterface)); + tempVec.push_back(copy); + } + + MapSet(tempCopy2dfxEffects, m_dwModelID, tempVec); +} + +void CModelInfoSA::RestoreModified2DFXEffects() +{ + if (!MapContains(tempCopy2dfxEffects, m_dwModelID)) + return; + + CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; + if (!modelInfo) + return; + + std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); + auto& tempVec = MapGet(tempCopy2dfxEffects, m_dwModelID); + if (tempVec.size() > 0) + { + for (std::uint32_t i = 0; i < numEffects; i++) + { + auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effect) + continue; + + if (tempVec[i]) + { + memcpy(effect, tempVec[i], sizeof(C2DEffectSAInterface)); + delete tempVec[i]; + } + } + } + + tempVec.clear(); + tempCopy2dfxEffects.erase(m_dwModelID); +} + ////////////////////////////////////////////////////////////////////////////////////////// // // Setup hooks diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 6c5691a893..73aeb6642a 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -333,7 +333,6 @@ class CModelInfoSA : public CModelInfo static std::unordered_map> ms_VehicleModelDefaultWheelSizes; static std::map ms_DefaultTxdIDMap; SVehicleSupportedUpgrades m_ModelSupportedUpgrades; - static std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; public: CModelInfoSA(); @@ -482,6 +481,9 @@ class CModelInfoSA : public CModelInfo bool Reset2DFXEffects(bool removeCustomEffects = false); static void StaticReset2DFXEffects(); + void CopyModified2DFXEffects(); + void RestoreModified2DFXEffects(); + bool IsDynamic() { return m_pInterface ? m_pInterface->usDynamicIndex != 0xffff : false; }; private: diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 6de796075b..477b6641d4 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -356,6 +356,9 @@ class CModelInfo virtual void StoreDefault2DFXEffect(C2DEffectSAInterface* effect) = 0; virtual bool Reset2DFXEffects(bool removeCustomEffects = false) = 0; + virtual void CopyModified2DFXEffects() = 0; + virtual void RestoreModified2DFXEffects() = 0; + virtual unsigned int GetParentID() = 0; virtual bool IsDynamic() = 0; }; From 7e38dc0a0b806806bc156e2471913d67055ce1ec Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 28 Sep 2024 16:39:50 +0200 Subject: [PATCH 13/17] Update & cleanup --- Client/game_sa/C2DEffectSAInterface.h | 1 + Client/game_sa/CModelInfoSA.cpp | 213 +++++++++++------- Client/game_sa/CModelInfoSA.h | 3 + Client/mods/deathmatch/logic/CClient2DFX.h | 3 +- .../deathmatch/logic/luadefs/CLua2DFXDefs.cpp | 34 ++- Client/sdk/game/CModelInfo.h | 3 + 6 files changed, 174 insertions(+), 83 deletions(-) diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h index 624cb58757..0c05fb34b5 100644 --- a/Client/game_sa/C2DEffectSAInterface.h +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -29,6 +29,7 @@ // fx stuff #define FUNC_Fx_c_DestroyEntityFx 0x4A1280 +#define FUNC_Fx_c_CreateEntityFx 0x4A11E0 #define VAR_G_Fx 0xA9AE00 #define OFFSET_FxSystem_Entities 0xC #define OFFSET_FxSystem_Link_Prev 0x4 diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index aedbf8d6e1..fd5ea27144 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -45,6 +45,7 @@ static std::unordered_map static std::unordered_map> tempCopy2dfxEffects; int C2DEffectSA::effect2dPluginOffset = *(int*)0xC3A1E0; // g2dEffectPluginOffset +static auto* fx = reinterpret_cast(VAR_G_Fx); union tIdeFlags { @@ -1196,7 +1197,7 @@ void CModelInfoSA::StaticReset2DFXEffects() for (auto innerIter = iter->second.begin(); innerIter != iter->second.end();) { // Copy default effect - memcpy(innerIter->first, innerIter->second, sizeof(C2DEffectSAInterface)); + MemCpy(innerIter->first, innerIter->second, sizeof(C2DEffectSAInterface)); // Increase the counter if this effect was removed auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), innerIter->first); @@ -1218,12 +1219,18 @@ void CModelInfoSA::StaticReset2DFXEffects() modelInfoInterface->ucNumOf2DEffects -= customEffectsCount; MapSet(numCustom2dfxEffects, modelInfoInterface, 0); + + // Destroy copies + auto& copies = MapGet(tempCopy2dfxEffects, iter->first); + for (auto copy : copies) + delete copy; } - // Clear maps + // Clear maps & vectors removedDefault2dfxEffects.clear(); ms_DefaultEffectsMap.clear(); d2fxEffects.clear(); + tempCopy2dfxEffects.clear(); } short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) @@ -2115,6 +2122,55 @@ bool CModelInfoSA::ForceUnload() return true; } +auto CModelInfoSA::GetEntitiesFromFx(std::uint32_t modelID) +{ + auto vec = std::vector(); + void* lastParticle = fx->m_lastParticleEntity; + + while (lastParticle) + { + auto** entity = reinterpret_cast(reinterpret_cast(lastParticle) + OFFSET_FxSystem_Entities); + auto* prevParticle = *reinterpret_cast(reinterpret_cast(lastParticle) + OFFSET_FxSystem_Link_Prev); + + if (entity && *entity && static_cast((*entity)->m_nModelIndex) == modelID) + vec.push_back(*entity); + + lastParticle = prevParticle; + } + + return vec; +} + +void CModelInfoSA::Update2DFXEffect(C2DEffectSAInterface* effect) +{ + if (!effect) + return; + + // This function aims to keep 2dfx effects updated to avoid restreaming + switch (effect->type) + { + case e2dEffectType::PARTICLE: + { + auto entities = GetEntitiesFromFx(m_dwModelID); + for (auto entity : entities) + { + // Call Fx_c::DestroyEntityFx + ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*))FUNC_Fx_c_DestroyEntityFx)(fx, entity); + + RwMatrix* matrixTransform = nullptr; + if (auto* object = reinterpret_cast(entity->m_pRwObject)) + { + if (auto* frame = static_cast(object->parent)) + matrixTransform = &frame->modelling; + } + + // Call Fx_c::CreateEntityFx + ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*, const char*, RwV3d*, RwMatrix*))FUNC_Fx_c_CreateEntityFx)(fx, entity, effect->effect.particle.szName, &effect->position, matrixTransform); + } + } + } +} + void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) { if (MapContains(ms_DefaultEffectsMap, m_dwModelID) && MapContains(MapGet(ms_DefaultEffectsMap, m_dwModelID), effect)) @@ -2125,7 +2181,7 @@ void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) // Copy an existing default effect C2DEffectSAInterface* copy = new C2DEffectSAInterface(); - memcpy(copy, effect, sizeof(C2DEffectSAInterface)); + MemCpyFast(copy, effect, sizeof(C2DEffectSAInterface)); // Create a copy of textures for the lights // We must to do this, because C2DEffect::Shutdown removes them @@ -2160,7 +2216,7 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) for (auto& it = map.begin(); it != map.end();) { // Copy data from copied effect to the default - memcpy(it->first, it->second, sizeof(C2DEffectSAInterface)); + MemCpyFast(it->first, it->second, sizeof(C2DEffectSAInterface)); // Increase the counter if this effect was removed auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), it->first); @@ -2170,15 +2226,23 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) m_pInterface->ucNumOf2DEffects++; } + Update2DFXEffect(it->first); + // We no longer need a copy // So delete it delete it->second; it = map.erase(it); } + // Delete temp copies + auto& copies = MapGet(tempCopy2dfxEffects, m_dwModelID); + for (auto* copy : copies) + delete copy; + // Clear maps map.clear(); ms_DefaultEffectsMap.erase(m_dwModelID); + tempCopy2dfxEffects.erase(m_dwModelID); // Remove all custom effects if (removeCustomEffects) @@ -2256,26 +2320,17 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) } case e2dEffectType::PARTICLE: { - auto* fx = reinterpret_cast(VAR_G_Fx); - void* lastParticle = fx->m_lastParticleEntity; - - while (lastParticle) + auto entities = GetEntitiesFromFx(m_dwModelID); + for (auto* entity : entities) { - auto** entity = reinterpret_cast(reinterpret_cast(lastParticle) + OFFSET_FxSystem_Entities); - auto* prevParticle = *reinterpret_cast(reinterpret_cast(lastParticle) + OFFSET_FxSystem_Link_Prev); - - if (entity && *entity && static_cast((*entity)->m_nModelIndex) == m_dwModelID) - { - // Call Fx_c::DestroyEntityFx - ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*))FUNC_Fx_c_DestroyEntityFx)(fx, *entity); + // Call Fx_c::DestroyEntityFx + ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*))FUNC_Fx_c_DestroyEntityFx)(fx, entity); - // Prevent creation when stream in but keep in memory so we can restore it later - effect->type = e2dEffectType::NONE; - } - - lastParticle = prevParticle; + // Prevent creation when stream in but keep in memory so we can restore it later + effect->type = e2dEffectType::NONE; } + entities.clear(); break; } case e2dEffectType::ESCALATOR: @@ -2358,6 +2413,66 @@ C2DEffectSAInterface* CModelInfoSA::Get2DFXFromIndex(std::uint32_t index) return ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(m_pInterface, index); } +void CModelInfoSA::CopyModified2DFXEffects() +{ + CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; + if (!modelInfo || modelInfo->ucNumOf2DEffects == 0) + return; + + // Has modified effects? + if (!MapContains(ms_DefaultEffectsMap, m_dwModelID)) + return; + + auto tempVec = std::vector(); + std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); + for (std::uint32_t i = 0; i < numEffects; i++) + { + auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index)) FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effect) + continue; + + // Copy effect + auto copy = new C2DEffectSAInterface(); + MemCpyFast(copy, effect, sizeof(C2DEffectSAInterface)); + tempVec.push_back(copy); + } + + MapSet(tempCopy2dfxEffects, m_dwModelID, tempVec); +} + +void CModelInfoSA::RestoreModified2DFXEffects() +{ + if (!MapContains(tempCopy2dfxEffects, m_dwModelID)) + return; + + CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; + if (!modelInfo) + return; + + std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); + auto& tempVec = MapGet(tempCopy2dfxEffects, m_dwModelID); + if (tempVec.size() > 0) + { + for (std::uint32_t i = 0; i < numEffects; i++) + { + auto effect = ((C2DEffectSAInterface*(__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effect) + continue; + + if (tempVec[i]) + { + MemCpyFast(effect, tempVec[i], sizeof(C2DEffectSAInterface)); + delete tempVec[i]; + } + + Update2DFXEffect(effect); + } + } + + tempVec.clear(); + tempCopy2dfxEffects.erase(m_dwModelID); +} + ////////////////////////////////////////////////////////////////////////////////////////// // // Hook for NodeNameStreamRead @@ -2452,64 +2567,6 @@ static void _declspec(naked) HOOK_Get2dEffect() } } -void CModelInfoSA::CopyModified2DFXEffects() -{ - CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; - if (!modelInfo || modelInfo->ucNumOf2DEffects == 0) - return; - - // Has modified effects? - if (!MapContains(ms_DefaultEffectsMap, m_dwModelID)) - return; - - auto tempVec = std::vector(); - std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); - for (std::uint32_t i = 0; i < numEffects; i++) - { - auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); - if (!effect) - continue; - - // Copy effect - auto copy = new C2DEffectSAInterface(); - memcpy(copy, effect, sizeof(C2DEffectSAInterface)); - tempVec.push_back(copy); - } - - MapSet(tempCopy2dfxEffects, m_dwModelID, tempVec); -} - -void CModelInfoSA::RestoreModified2DFXEffects() -{ - if (!MapContains(tempCopy2dfxEffects, m_dwModelID)) - return; - - CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; - if (!modelInfo) - return; - - std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); - auto& tempVec = MapGet(tempCopy2dfxEffects, m_dwModelID); - if (tempVec.size() > 0) - { - for (std::uint32_t i = 0; i < numEffects; i++) - { - auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); - if (!effect) - continue; - - if (tempVec[i]) - { - memcpy(effect, tempVec[i], sizeof(C2DEffectSAInterface)); - delete tempVec[i]; - } - } - } - - tempVec.clear(); - tempCopy2dfxEffects.erase(m_dwModelID); -} - ////////////////////////////////////////////////////////////////////////////////////////// // // Setup hooks diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index 73aeb6642a..d5b181dbf5 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -476,6 +476,9 @@ class CModelInfoSA : public CModelInfo bool RemoveAll2DFXEffects(bool includeDefault); C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index); std::uint32_t Get2DFXCount() const { return m_pInterface ? m_pInterface->ucNumOf2DEffects : 0; } + void Update2DFXEffect(C2DEffectSAInterface* effect); + + static auto GetEntitiesFromFx(std::uint32_t modelID); void StoreDefault2DFXEffect(C2DEffectSAInterface* effect); bool Reset2DFXEffects(bool removeCustomEffects = false); diff --git a/Client/mods/deathmatch/logic/CClient2DFX.h b/Client/mods/deathmatch/logic/CClient2DFX.h index a9629eb09c..d6dffe5e33 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.h +++ b/Client/mods/deathmatch/logic/CClient2DFX.h @@ -31,10 +31,11 @@ class CClient2DFX final : public CClientEntity e2dEffectType Get2DFXType() const noexcept { return m_effectType; } C2DEffectSAInterface* Get2DFX() const noexcept { return m_effectInterface; } + std::uint32_t GetModelID() const noexcept { return m_model; } private: class CClient2DFXManager* m_2DFXManager; C2DEffectSAInterface* m_effectInterface; e2dEffectType m_effectType; - DWORD m_model; + std::uint32_t m_model; }; diff --git a/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp index 9d87075f93..2288c51ebf 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp @@ -142,16 +142,29 @@ bool CLua2DFXDefs::SetModel2DFXProperties(std::uint32_t modelID, std::uint32_t i throw LuaFunctionError(error); modelInfo->StoreDefault2DFXEffect(effect); - return m_p2DFXManager->Set2DFXProperties(effect, effectData); + + if (!m_p2DFXManager->Set2DFXProperties(effect, effectData)) + return false; + + modelInfo->Update2DFXEffect(effect); + return true; } bool CLua2DFXDefs::Set2DFXProperties(CClient2DFX* effect, effectDataMap effectData) { + CModelInfo* modelInfo = g_pGame->GetModelInfo(effect->GetModelID()); + if (!modelInfo) + return false; + const char* error = CClient2DFXManager::IsValidEffectData(effect->Get2DFXType(), effectData); if (error) throw LuaFunctionError(error); - return m_p2DFXManager->Set2DFXProperties(effect->Get2DFX(), effectData); + if (!m_p2DFXManager->Set2DFXProperties(effect->Get2DFX(), effectData)) + return false; + + modelInfo->Update2DFXEffect(effect->Get2DFX()); + return true; } bool CLua2DFXDefs::SetModel2DFXProperty(std::uint32_t modelID, std::uint32_t index, e2dEffectProperty property, std::variant propertyValue) @@ -172,12 +185,25 @@ bool CLua2DFXDefs::SetModel2DFXProperty(std::uint32_t modelID, std::uint32_t ind return false; modelInfo->StoreDefault2DFXEffect(effect); - return m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue); + + if (!m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue)) + return false; + + modelInfo->Update2DFXEffect(effect); + return true; } bool CLua2DFXDefs::Set2DFXProperty(CClient2DFX* effect, e2dEffectProperty property, std::variant propertyValue) { - return m_p2DFXManager->Set2DFXProperty(effect->Get2DFX(), property, propertyValue); + CModelInfo* modelInfo = g_pGame->GetModelInfo(effect->GetModelID()); + if (!modelInfo) + return false; + + if (!m_p2DFXManager->Set2DFXProperty(effect->Get2DFX(), property, propertyValue)) + return false; + + modelInfo->Update2DFXEffect(effect->Get2DFX()); + return true; } bool CLua2DFXDefs::SetModel2DFXPosition(std::uint32_t modelID, std::uint32_t index, CVector position) diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index 477b6641d4..a64f95d5ad 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -89,6 +89,7 @@ enum class e2dEffectType : std::uint8_t { LIGHT = 0, PARTICLE, + UNKNOWN, ATTRACTOR, SUN_GLARE, FURNITURE, @@ -353,6 +354,8 @@ class CModelInfo virtual C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index) = 0; virtual std::uint32_t Get2DFXCount() const = 0; + virtual void Update2DFXEffect(C2DEffectSAInterface* effect) = 0; + virtual void StoreDefault2DFXEffect(C2DEffectSAInterface* effect) = 0; virtual bool Reset2DFXEffects(bool removeCustomEffects = false) = 0; From b137a39fa49465da16362d42403a2cb5b231e8ba Mon Sep 17 00:00:00 2001 From: FileEX Date: Sun, 29 Sep 2024 20:59:38 +0200 Subject: [PATCH 14/17] Cleanup, fix bugs, crashes and memory leaks --- Client/game_sa/C2DEffectSA.cpp | 101 ++++++++++++++++++ Client/game_sa/C2DEffectSA.h | 44 ++++++++ Client/game_sa/C2DEffectSAInterface.h | 61 +---------- Client/game_sa/CModelInfoSA.cpp | 49 +++++---- Client/game_sa/gamesa_renderware.h | 2 + Client/game_sa/gamesa_renderware.hpp | 3 +- Client/mods/deathmatch/logic/CClient2DFX.cpp | 12 +++ .../deathmatch/logic/CClient2DFXManager.cpp | 59 ++++------ 8 files changed, 212 insertions(+), 119 deletions(-) create mode 100644 Client/game_sa/C2DEffectSA.cpp create mode 100644 Client/game_sa/C2DEffectSA.h diff --git a/Client/game_sa/C2DEffectSA.cpp b/Client/game_sa/C2DEffectSA.cpp new file mode 100644 index 0000000000..c118a4b952 --- /dev/null +++ b/Client/game_sa/C2DEffectSA.cpp @@ -0,0 +1,101 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/C2DEffectSA.cpp + * PURPOSE: 2DFX static class + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "gamesa_renderware.h" +#include "C2DEffectSA.h" +#include "C2DEffectSAInterface.h" + +// C2DEffect::Shutdown causes random unknown crash in ntdll.dll so we need own function +void C2DEffectSA::Shutdown(C2DEffectSAInterface* effect) +{ + if (!effect) + return; + + if (effect->type == e2dEffectType::ROADSIGN) + { + t2dEffectRoadsign& roadsign = effect->effect.roadsign; + + if (roadsign.text) + { + std::free(roadsign.text); + roadsign.text = nullptr; + } + + if (roadsign.atomic) + { + RwFrame* frame = RpAtomicGetFrame(roadsign.atomic); + if (frame) + { + RpAtomicSetFrame(roadsign.atomic, nullptr); + RwFrameDestroy(frame); + } + + RpAtomicDestroy(roadsign.atomic); + roadsign.atomic = nullptr; + } + } + else if (effect->type == e2dEffectType::LIGHT) + { + t2dEffectLight& light = effect->effect.light; + + if (light.coronaTex) + { + RwTextureDestroy(light.coronaTex); + light.coronaTex = nullptr; + } + + if (light.shadowTex) + { + RwTextureDestroy(light.shadowTex); + light.shadowTex = nullptr; + } + } +} + +void C2DEffectSA::SafeDelete2DFXEffect(C2DEffectSAInterface* effect) +{ + if (!effect) + return; + + if (effect->type == e2dEffectType::ROADSIGN || effect->type == e2dEffectType::LIGHT) + Shutdown(effect); + + delete effect; + effect = nullptr; +} + +void C2DEffectSA::PrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist) +{ + // Call CTxdStore::PushCurrentTxd + ((void(__cdecl*)())FUNC_PushCurrentTxd)(); + // Call CTxdStore::FindTxdSlot + int slot = ((int(__cdecl*)(const char*))FUNC_FindTxdSlot)("particle"); + // Call CTxdStore::SetCurrentTxd + ((void(__cdecl*)(int))FUNC_SetCurrentTxd)(slot); + + if (removeIfExist) + { + if (coronaTex && coronaName) + RwTextureDestroy(coronaTex); + if (shadowTex && shadowName) + RwTextureDestroy(shadowTex); + } + + if (coronaName) + coronaTex = RwReadTexture(coronaName, nullptr); + + if (shadowName) + shadowTex = RwReadTexture(shadowName, nullptr); + + // Call CTxdStore::PopCurrentTxd + ((void(__cdecl*)())FUNC_PopCurrentTxd)(); +} diff --git a/Client/game_sa/C2DEffectSA.h b/Client/game_sa/C2DEffectSA.h new file mode 100644 index 0000000000..13ab4412e8 --- /dev/null +++ b/Client/game_sa/C2DEffectSA.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/C2DEffectSA.h + * PURPOSE: Header file for 2dfx static class + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#define ARRAY_2DFXInfoStore 0xB4C2D8 // C2dfxInfoStore d2fxModels + +#define FUNC_C2DEffect_Shutdown 0x4C57D0 +#define FUNC_PushCurrentTxd 0x7316A0 +#define FUNC_FindTxdSlot 0x731850 +#define FUNC_SetCurrentTxd 0x7319C0 +#define FUNC_PopCurrentTxd 0x7316B0 + +// Escalators stuff +#define ARRAY_CEscalators 0xC6E9A8 +#define NUM_MAX_ESCALATORS 32 +#define FUNC_CEscalator_SwitchOff 0x717860 + +// fx stuff +#define FUNC_Fx_c_DestroyEntityFx 0x4A1280 +#define FUNC_Fx_c_CreateEntityFx 0x4A11E0 +#define VAR_G_Fx 0xA9AE00 +#define OFFSET_FxSystem_Entities 0xC +#define OFFSET_FxSystem_Link_Prev 0x4 + +class C2DEffectSAInterface; + +class C2DEffectSA +{ +public: + static int effect2dPluginOffset; + + static void Shutdown(C2DEffectSAInterface* effect); + static void SafeDelete2DFXEffect(C2DEffectSAInterface* effect); + static void PrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist); +}; diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h index 0c05fb34b5..15ff87fa86 100644 --- a/Client/game_sa/C2DEffectSAInterface.h +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -11,30 +11,9 @@ #pragma once #include "game/RenderWare.h" +#include "game/CModelInfo.h" #include "CObjectSA.h" - -#define ARRAY_2DFXInfoStore 0xB4C2D8 // C2dfxInfoStore d2fxModels - -#define FUNC_C2DEffect_Shutdown 0x4C57D0 -#define FUNC_PushCurrentTxd 0x7316A0 -#define FUNC_FindTxdSlot 0x731850 -#define FUNC_SetCurrentTxd 0x7319C0 -#define FUNC_PopCurrentTxd 0x7316B0 -#define FUNC_RwReadTexture 0x7F3AC0 - -// Escalators stuff -#define ARRAY_CEscalators 0xC6E9A8 -#define NUM_MAX_ESCALATORS 32 -#define FUNC_CEscalator_SwitchOff 0x717860 - -// fx stuff -#define FUNC_Fx_c_DestroyEntityFx 0x4A1280 -#define FUNC_Fx_c_CreateEntityFx 0x4A11E0 -#define VAR_G_Fx 0xA9AE00 -#define OFFSET_FxSystem_Entities 0xC -#define OFFSET_FxSystem_Link_Prev 0x4 - -#define FUNC_RwTextureDestroy 0x7F3820 +#include "C2DEffectSA.h" struct t2dEffectLight { @@ -270,39 +249,7 @@ class CEscalatorSAInterface CObjectSAInterface* objects[42]; }; -class C2DEffectSA +static void StaticPrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist) { -public: - static int effect2dPluginOffset; -}; - -static void PrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist) -{ - // Call CTxdStore::PushCurrentTxd - ((void(__cdecl*)())FUNC_PushCurrentTxd)(); - // Call CTxdStore::FindTxdSlot - int slot = ((int(__cdecl*)(const char*))FUNC_FindTxdSlot)("particle"); - // Call CTxdStore::SetCurrentTxd - ((void(__cdecl*)(int))FUNC_SetCurrentTxd)(slot); - - if (removeIfExist) - { - using RwTextureDestroy = void(__cdecl*)(RwTexture*); - - if (coronaTex && coronaName) - ((RwTextureDestroy)FUNC_RwTextureDestroy)(coronaTex); - if (shadowTex && shadowName) - ((RwTextureDestroy)FUNC_RwTextureDestroy)(shadowTex); - } - - // Call RwReadTexture - using RwReadTexture = RwTexture*(__cdecl*)(const char*, const char*); - if (coronaName) - coronaTex = ((RwReadTexture)FUNC_RwReadTexture)(coronaName, nullptr); - - if (shadowName) - shadowTex = ((RwReadTexture)FUNC_RwReadTexture)(shadowName, nullptr); - - // Call CTxdStore::PopCurrentTxd - ((void(__cdecl*)())FUNC_PopCurrentTxd)(); + C2DEffectSA::PrepareTexturesForLightEffect(coronaTex, shadowTex, coronaName, shadowName, removeIfExist); } diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index fd5ea27144..567a37808f 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -20,6 +20,7 @@ #include "CWorldSA.h" #include "gamesa_renderware.h" #include "CFxSA.h" +#include "C2DEffectSA.h" extern CCoreInterface* g_pCore; extern CGameSA* pGame; @@ -1208,8 +1209,7 @@ void CModelInfoSA::StaticReset2DFXEffects() } // Delete copy of the default effect - delete innerIter->second; - innerIter->second = nullptr; + C2DEffectSA::SafeDelete2DFXEffect(innerIter->second); innerIter = iter->second.erase(innerIter); } @@ -1223,7 +1223,7 @@ void CModelInfoSA::StaticReset2DFXEffects() // Destroy copies auto& copies = MapGet(tempCopy2dfxEffects, iter->first); for (auto copy : copies) - delete copy; + C2DEffectSA::SafeDelete2DFXEffect(copy); } // Clear maps & vectors @@ -2173,6 +2173,10 @@ void CModelInfoSA::Update2DFXEffect(C2DEffectSAInterface* effect) void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) { + // Is custom effect? + if (std::find(d2fxEffects.begin(), d2fxEffects.end(), effect) != d2fxEffects.end()) + return; + if (MapContains(ms_DefaultEffectsMap, m_dwModelID) && MapContains(MapGet(ms_DefaultEffectsMap, m_dwModelID), effect)) return; @@ -2188,13 +2192,19 @@ void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) if (copy->type == e2dEffectType::LIGHT) { if (effect->effect.light.coronaTex && effect->effect.light.shadowTex) - PrepareTexturesForLightEffect(copy->effect.light.coronaTex, copy->effect.light.shadowTex, effect->effect.light.coronaTex->name, effect->effect.light.shadowTex->name, false); + C2DEffectSA::PrepareTexturesForLightEffect(copy->effect.light.coronaTex, copy->effect.light.shadowTex, effect->effect.light.coronaTex->name, effect->effect.light.shadowTex->name, false); } else if (copy->type == e2dEffectType::ROADSIGN) { // Create a copy of text and atomic for the roadsign // We must to do this, because C2DEffect::Shutdown removes them - std::strncpy(copy->effect.roadsign.text, effect->effect.roadsign.text, 64); + copy->effect.roadsign.text = static_cast(std::malloc(64)); + if (copy->effect.roadsign.text) + { + std::memset(copy->effect.roadsign.text, 0, 64); + std::strncpy(copy->effect.roadsign.text, effect->effect.roadsign.text, 64); + } + copy->effect.roadsign.atomic = RpAtomicClone(effect->effect.roadsign.atomic); } @@ -2230,14 +2240,14 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) // We no longer need a copy // So delete it - delete it->second; + C2DEffectSA::SafeDelete2DFXEffect(it->second); it = map.erase(it); } // Delete temp copies auto& copies = MapGet(tempCopy2dfxEffects, m_dwModelID); for (auto* copy : copies) - delete copy; + C2DEffectSA::SafeDelete2DFXEffect(copy); // Clear maps map.clear(); @@ -2300,20 +2310,14 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) StoreDefault2DFXEffect(effect); m_pInterface->ucNumOf2DEffects--; - if (isCustomEffect) - { - MapGet(numCustom2dfxEffects, m_pInterface)--; - d2fxEffects.erase(it); - } switch (effect->type) { case e2dEffectType::ROADSIGN: case e2dEffectType::LIGHT: { - // Call C2DEffect::Shutdown - ((void(__thiscall*)(C2DEffectSAInterface*))FUNC_C2DEffect_Shutdown)(effect); - + C2DEffectSA::Shutdown(effect); + // Prevent creation when stream in but keep in memory so we can restore it later effect->type = e2dEffectType::NONE; break; @@ -2322,15 +2326,13 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) { auto entities = GetEntitiesFromFx(m_dwModelID); for (auto* entity : entities) - { // Call Fx_c::DestroyEntityFx ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*))FUNC_Fx_c_DestroyEntityFx)(fx, entity); - // Prevent creation when stream in but keep in memory so we can restore it later - effect->type = e2dEffectType::NONE; - } - entities.clear(); + + // Prevent creation when stream in but keep in memory so we can restore it later + effect->type = e2dEffectType::NONE; break; } case e2dEffectType::ESCALATOR: @@ -2358,11 +2360,16 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) effect->type = e2dEffectType::NONE; break; } + default: + return false; } // If it's custom effect then delete it. If it's default effect then store it as removed if (isCustomEffect) { + MapGet(numCustom2dfxEffects, m_pInterface)--; + d2fxEffects.erase(it); + delete effect; effect = nullptr; } @@ -2462,7 +2469,7 @@ void CModelInfoSA::RestoreModified2DFXEffects() if (tempVec[i]) { MemCpyFast(effect, tempVec[i], sizeof(C2DEffectSAInterface)); - delete tempVec[i]; + C2DEffectSA::SafeDelete2DFXEffect(tempVec[i]); } Update2DFXEffect(effect); diff --git a/Client/game_sa/gamesa_renderware.h b/Client/game_sa/gamesa_renderware.h index 594c203b59..03bffd749b 100644 --- a/Client/game_sa/gamesa_renderware.h +++ b/Client/game_sa/gamesa_renderware.h @@ -105,6 +105,7 @@ typedef RpHAnimHierarchy*(__cdecl* GetAnimHierarchyFromSkinClump_t)(RpClump*); typedef int(__cdecl* RpHAnimIDGetIndex_t)(RpHAnimHierarchy*, int); typedef RwMatrix*(__cdecl* RpHAnimHierarchyGetMatrixArray_t)(RpHAnimHierarchy*); typedef RtQuat*(__cdecl* RtQuatRotate_t)(RtQuat* quat, const RwV3d* axis, float angle, RwOpCombineType combineOp); +typedef RwTexture*(__cdecl* RwReadTexture_t)(const char* name, const char* mask); /*****************************************************************************/ /** Renderware function mappings **/ @@ -195,6 +196,7 @@ RWFUNC(GetAnimHierarchyFromSkinClump_t GetAnimHierarchyFromSkinClump, (GetAnimHi RWFUNC(RpHAnimIDGetIndex_t RpHAnimIDGetIndex, (RpHAnimIDGetIndex_t)0xDEAD) RWFUNC(RpHAnimHierarchyGetMatrixArray_t RpHAnimHierarchyGetMatrixArray, (RpHAnimHierarchyGetMatrixArray_t)0xDEAD) RWFUNC(RtQuatRotate_t RtQuatRotate, (RtQuatRotate_t)0xDEAD) +RWFUNC(RwReadTexture_t RwReadTexture, reinterpret_cast(0xDEAD)) /*****************************************************************************/ /** GTA function definitions and mappings **/ diff --git a/Client/game_sa/gamesa_renderware.hpp b/Client/game_sa/gamesa_renderware.hpp index a638cfede0..edabf9a712 100644 --- a/Client/game_sa/gamesa_renderware.hpp +++ b/Client/game_sa/gamesa_renderware.hpp @@ -89,7 +89,8 @@ void InitRwFunctions() RpHAnimIDGetIndex = (RpHAnimIDGetIndex_t)0x7C51A0; RpHAnimHierarchyGetMatrixArray = (RpHAnimHierarchyGetMatrixArray_t)0x7C5120; RtQuatRotate = (RtQuatRotate_t)0x7EB7C0; - + RwReadTexture = reinterpret_cast(0x7F3AC0); + SetTextureDict = (SetTextureDict_t)0x007319C0; LoadClumpFile = (LoadClumpFile_t)0x005371F0; LoadModel = (LoadModel_t)0x0040C6B0; diff --git a/Client/mods/deathmatch/logic/CClient2DFX.cpp b/Client/mods/deathmatch/logic/CClient2DFX.cpp index 3bbe11e5ad..d128260f25 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFX.cpp @@ -47,6 +47,18 @@ bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2d m_effectType = effect->type; m_model = static_cast(model); + switch (effect->type) + { + case e2dEffectType::ROADSIGN: + { + t2dEffectRoadsign& roadsign = effect->effect.roadsign; + roadsign.size = RwV2d{0, 0}; + roadsign.rotation = RwV3d{0, 0, 0}; + roadsign.atomic = nullptr; + roadsign.text = nullptr; + } + } + if (!m_2DFXManager->Set2DFXProperties(effect, effectData)) return false; diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index d3f1ed3810..6bb1b615ec 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -137,7 +137,7 @@ effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect MapSet(properties, "rotY", roadsign.rotation.y); MapSet(properties, "rotZ", roadsign.rotation.z); MapSet(properties, "flags", static_cast(roadsign.flags)); - MapSet(properties, "text", !roadsign.text ? "" : roadsign.text); + MapSet(properties, "text", !roadsign.text ? "" : std::string(roadsign.text, roadsignTextSize)); break; } @@ -320,7 +320,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d e2dEffectTextureName coronaName; if (StringToEnum(std::get(propertyValue), coronaName)) { - PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(propertyValue).c_str(), light.shadowTex ? light.shadowTex->name : nullptr, true); + //StaticPrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(propertyValue).c_str(), light.shadowTex ? light.shadowTex->name : nullptr, true); return true; } } @@ -334,7 +334,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d e2dEffectTextureName shadowName; if (StringToEnum(std::get(propertyValue), shadowName)) { - PrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, light.coronaTex ? light.coronaTex->name : nullptr, std::get(propertyValue).c_str(), true); + //StaticPrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, light.coronaTex ? light.coronaTex->name : nullptr, std::get(propertyValue).c_str(), true); return true; } } @@ -353,9 +353,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - std::strncpy(effect->effect.particle.szName, std::get(propertyValue).c_str(), sizeof(effect->effect.particle.szName) - 1); - effect->effect.particle.szName[sizeof(effect->effect.particle.szName) - 1] = '\0'; - + std::strncpy(effect->effect.particle.szName, std::get(propertyValue).c_str(), sizeof(effect->effect.particle.szName)); return true; } } @@ -364,7 +362,16 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d } case e2dEffectType::ROADSIGN: { - t2dEffectRoadsign roadsign = effect->effect.roadsign; + t2dEffectRoadsign& roadsign = effect->effect.roadsign; + + if (!roadsign.text) + { + roadsign.text = static_cast(std::malloc(roadsignTextSize)); + if (!roadsign.text) + break; + + std::memset(roadsign.text, 0, roadsignTextSize); + } switch (property) { @@ -432,14 +439,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - if (!roadsign.text) - roadsign.text = static_cast(malloc(roadsignTextSize)); - - if (!roadsign.text) - break; - - std::memcpy(roadsign.text, std::get(propertyValue).c_str(), 4); - roadsign.text[roadsignTextSize - 1] = '\0'; + std::strncpy(roadsign.text, std::get(propertyValue).c_str(), 16); return true; } @@ -449,14 +449,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - if (!roadsign.text) - roadsign.text = static_cast(malloc(roadsignTextSize)); - - if (!roadsign.text) - break; - - std::memcpy(roadsign.text + 4, std::get(propertyValue).c_str(), 4); - roadsign.text[roadsignTextSize - 1] = '\0'; + std::strncpy(roadsign.text+16, std::get(propertyValue).c_str(), 16); return true; } @@ -466,14 +459,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - if (!roadsign.text) - roadsign.text = static_cast(malloc(roadsignTextSize)); - - if (!roadsign.text) - break; - - std::memcpy(roadsign.text + 8, std::get(propertyValue).c_str(), 4); - roadsign.text[roadsignTextSize - 1] = '\0'; + std::strncpy(roadsign.text + 32, std::get(propertyValue).c_str(), 16); return true; } @@ -483,14 +469,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - if (!roadsign.text) - roadsign.text = static_cast(malloc(roadsignTextSize)); - - if (!roadsign.text) - break; - - std::memcpy(roadsign.text + 12, std::get(propertyValue).c_str(), 4); - roadsign.text[roadsignTextSize - 1] = '\0'; + std::strncpy(roadsign.text + 48, std::get(propertyValue).c_str(), 16); return true; } @@ -503,7 +482,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d } case e2dEffectType::ESCALATOR: { - t2dEffectEscalator escalator = effect->effect.escalator; + t2dEffectEscalator& escalator = effect->effect.escalator; switch (property) { From b27c97704153c8f15a3def45db2f87c457293cfd Mon Sep 17 00:00:00 2001 From: FileEX Date: Tue, 1 Oct 2024 18:05:41 +0200 Subject: [PATCH 15/17] Huge rework & update --- Client/game_sa/C2DEffectSA.cpp | 246 ++++++++++++ Client/game_sa/C2DEffectSA.h | 102 ++++- Client/game_sa/C2DEffectSAInterface.h | 7 +- Client/game_sa/C2DEffectsSA.cpp | 66 ++++ Client/game_sa/C2DEffectsSA.h | 26 ++ Client/game_sa/CGameSA.cpp | 2 + Client/game_sa/CGameSA.h | 5 +- Client/game_sa/CModelInfoSA.cpp | 231 +++++------- Client/game_sa/CModelInfoSA.h | 16 +- Client/mods/deathmatch/logic/CClient2DFX.cpp | 32 +- Client/mods/deathmatch/logic/CClient2DFX.h | 10 +- .../deathmatch/logic/CClient2DFXManager.cpp | 354 ++++++++++-------- .../deathmatch/logic/CClient2DFXManager.h | 15 +- .../deathmatch/logic/luadefs/CLua2DFXDefs.cpp | 34 +- Client/sdk/game/C2DEffect.h | 95 +++++ Client/sdk/game/C2DEffects.h | 125 +++++++ Client/sdk/game/CGame.h | 2 + Client/sdk/game/CModelInfo.h | 107 +----- 18 files changed, 1031 insertions(+), 444 deletions(-) create mode 100644 Client/game_sa/C2DEffectsSA.cpp create mode 100644 Client/game_sa/C2DEffectsSA.h create mode 100644 Client/sdk/game/C2DEffect.h create mode 100644 Client/sdk/game/C2DEffects.h diff --git a/Client/game_sa/C2DEffectSA.cpp b/Client/game_sa/C2DEffectSA.cpp index c118a4b952..982dbfb679 100644 --- a/Client/game_sa/C2DEffectSA.cpp +++ b/Client/game_sa/C2DEffectSA.cpp @@ -13,6 +13,252 @@ #include "gamesa_renderware.h" #include "C2DEffectSA.h" #include "C2DEffectSAInterface.h" +#include "CGameSA.h" + +extern CGameSA* pGame; + +C2DEffectSA::C2DEffectSA(C2DEffectSAInterface* effectInterface, std::uint32_t modelID) : m_effectInterface(effectInterface), m_model(modelID) +{ + pGame->Get2DEffects()->AddToList(this); +} + +void C2DEffectSA::Destroy() const +{ + CModelInfo* modelInfo = pGame->GetModelInfo(m_model); + if (modelInfo) + modelInfo->Remove2DFX(m_effectInterface, false); +} + +void C2DEffectSA::SetPosition(const CVector& position) +{ + if (m_effectInterface) + m_effectInterface->position = RwV3d{position.fX, position.fY, position.fZ}; +} + +CVector& C2DEffectSA::GetPosition() +{ + if (m_effectInterface) + return CVector(m_effectInterface->position.x, m_effectInterface->position.y, m_effectInterface->position.z); + + return CVector(); +} + +void C2DEffectSA::SetCoronaFarClip(float clip) +{ + if (IsValidLight()) + m_effectInterface->effect.light.coronaFarClip = clip; +} + +void C2DEffectSA::SetCoronaPointLightRange(float range) +{ + if (IsValidLight()) + m_effectInterface->effect.light.pointLightRange = range; +} + +void C2DEffectSA::SetCoronaSize(float size) +{ + if (IsValidLight()) + m_effectInterface->effect.light.coronaSize = size; +} + +void C2DEffectSA::SetShadowSize(float size) +{ + if (IsValidLight()) + m_effectInterface->effect.light.shadowSize = size; +} + +void C2DEffectSA::SetShadowMultiplier(std::uint8_t multiplier) +{ + if (IsValidLight()) + m_effectInterface->effect.light.shadowColorMultiplier = multiplier; +} + +void C2DEffectSA::SetCoronaShowMode(e2dCoronaFlashType showMode) +{ + if (IsValidLight()) + m_effectInterface->effect.light.coronaFlashType = showMode; +} + +void C2DEffectSA::SetCoronaReflectionsEnabled(bool enable) +{ + if (IsValidLight()) + m_effectInterface->effect.light.coronaEnableReflection = enable; +} + +void C2DEffectSA::SetCoronaFlareType(std::uint8_t flareType) +{ + if (IsValidLight()) + m_effectInterface->effect.light.coronaFlareType = flareType; +} + +void C2DEffectSA::SetLightFlags(std::uint16_t flags) +{ + if (IsValidLight()) + m_effectInterface->effect.light.flags = flags; +} + +void C2DEffectSA::SetShadowDistance(std::int8_t distance) +{ + if (IsValidLight()) + m_effectInterface->effect.light.shadowZDistance = distance; +} + +void C2DEffectSA::SetCoronaOffsets(const CVector& offsets) +{ + if (IsValidLight()) + { + m_effectInterface->effect.light.offsetX = offsets.fX; + m_effectInterface->effect.light.offsetY = offsets.fY; + m_effectInterface->effect.light.offsetZ = offsets.fZ; + } +} + +void C2DEffectSA::SetCoronaColor(const RwColor& color) +{ + if (IsValidLight()) + m_effectInterface->effect.light.color = color; +} + +void C2DEffectSA::SetCoronaTexture(const std::string& name) +{ + if (IsValidLight()) + { + if (m_effectInterface->effect.light.coronaTex) + RwTextureDestroy(m_effectInterface->effect.light.coronaTex); + + // Call CTxdStore::PushCurrentTxd + ((void(__cdecl*)())FUNC_PushCurrentTxd)(); + // Call CTxdStore::FindTxdSlot + int slot = ((int(__cdecl*)(const char*))FUNC_FindTxdSlot)("particle"); + // Call CTxdStore::SetCurrentTxd + ((void(__cdecl*)(int))FUNC_SetCurrentTxd)(slot); + + m_effectInterface->effect.light.coronaTex = RwReadTexture(name.c_str(), nullptr); + + // Call CTxdStore::PopCurrentTxd + ((void(__cdecl*)())FUNC_PopCurrentTxd)(); + } +} + +void C2DEffectSA::SetShadowTexture(const std::string& name) +{ + if (IsValidLight()) + { + if (m_effectInterface->effect.light.shadowTex) + RwTextureDestroy(m_effectInterface->effect.light.shadowTex); + + // Call CTxdStore::PushCurrentTxd + ((void(__cdecl*)())FUNC_PushCurrentTxd)(); + // Call CTxdStore::FindTxdSlot + int slot = ((int(__cdecl*)(const char*))FUNC_FindTxdSlot)("particle"); + // Call CTxdStore::SetCurrentTxd + ((void(__cdecl*)(int))FUNC_SetCurrentTxd)(slot); + + m_effectInterface->effect.light.shadowTex = RwReadTexture(name.c_str(), nullptr); + + // Call CTxdStore::PopCurrentTxd + ((void(__cdecl*)())FUNC_PopCurrentTxd)(); + } +} + +CVector C2DEffectSA::GetCoronaOffsets() const +{ + if (IsValidLight()) + return CVector(m_effectInterface->effect.light.offsetX, m_effectInterface->effect.light.offsetY, m_effectInterface->effect.light.offsetZ); + + return CVector(); +} + +void C2DEffectSA::SetParticleName(const std::string& name) +{ + if (m_effectInterface && m_effectInterface->type == e2dEffectType::PARTICLE) + std::strncpy(m_effectInterface->effect.particle.szName, name.c_str(), 24); +} + +void C2DEffectSA::SetRoadsignSize(const RwV2d& size) +{ + if (IsValidRoadsign()) + m_effectInterface->effect.roadsign.size = size; +} + +void C2DEffectSA::SetRoadsignRotation(const RwV3d& rotation) +{ + if (IsValidRoadsign()) + m_effectInterface->effect.roadsign.rotation = rotation; +} + +void C2DEffectSA::SetRoadsignFlags(std::uint8_t flags) +{ + if (IsValidRoadsign()) + m_effectInterface->effect.roadsign.flags = flags; +} + +void C2DEffectSA::SetRoadsignText(const std::string& text, std::uint8_t line) +{ + if (IsValidRoadsign()) + { + if (!m_effectInterface->effect.roadsign.text) + m_effectInterface->effect.roadsign.text = static_cast(std::malloc(64)); + + if (!m_effectInterface->effect.roadsign.text) + return; + + std::strncpy(m_effectInterface->effect.roadsign.text + 16 * (line - 1), text.c_str(), 16); + } +} + +void C2DEffectSA::SetEscalatorBottom(const RwV3d& bottom) +{ + if (IsValidEscalator()) + m_effectInterface->effect.escalator.bottom = bottom; +} + +void C2DEffectSA::SetEscalatorTop(const RwV3d& top) +{ + if (IsValidEscalator()) + m_effectInterface->effect.escalator.top = top; +} + +void C2DEffectSA::SetEscalatorEnd(const RwV3d& end) +{ + if (IsValidEscalator()) + m_effectInterface->effect.escalator.end = end; +} + +void C2DEffectSA::SetEscalatorDirection(std::uint8_t direction) +{ + if (IsValidEscalator()) + m_effectInterface->effect.escalator.direction = direction; +} + +C2DEffectSAInterface* C2DEffectSA::CreateCopy(C2DEffectSAInterface* effect) +{ + C2DEffectSAInterface* copy = new C2DEffectSAInterface(); + MemCpyFast(copy, effect, sizeof(C2DEffectSAInterface)); + + // Create a copy of textures for the lights + // We must to do this, because C2DEffect::Shutdown removes them + if (copy->type == e2dEffectType::LIGHT) + { + if (effect->effect.light.coronaTex && effect->effect.light.shadowTex) + C2DEffectSA::PrepareTexturesForLightEffect(copy->effect.light.coronaTex, copy->effect.light.shadowTex, effect->effect.light.coronaTex->name, effect->effect.light.shadowTex->name, false); + } + else if (copy->type == e2dEffectType::ROADSIGN) + { + // Create a copy of text and atomic for the roadsign + // We must to do this, because C2DEffect::Shutdown removes them + copy->effect.roadsign.text = static_cast(std::malloc(64)); + if (copy->effect.roadsign.text) + { + std::memset(copy->effect.roadsign.text, 0, 64); + std::strncpy(copy->effect.roadsign.text, effect->effect.roadsign.text, 64); + } + + copy->effect.roadsign.atomic = RpAtomicClone(effect->effect.roadsign.atomic); + } + + return copy; +} // C2DEffect::Shutdown causes random unknown crash in ntdll.dll so we need own function void C2DEffectSA::Shutdown(C2DEffectSAInterface* effect) diff --git a/Client/game_sa/C2DEffectSA.h b/Client/game_sa/C2DEffectSA.h index 13ab4412e8..f41f15e834 100644 --- a/Client/game_sa/C2DEffectSA.h +++ b/Client/game_sa/C2DEffectSA.h @@ -3,13 +3,16 @@ * PROJECT: Multi Theft Auto * LICENSE: See LICENSE in the top level directory * FILE: game_sa/C2DEffectSA.h - * PURPOSE: Header file for 2dfx static class + * PURPOSE: Header file for 2dfx class * * Multi Theft Auto is available from https://www.multitheftauto.com/ * *****************************************************************************/ #pragma once +#include "game/C2DEffect.h" +#include "C2DEffectSAInterface.h" +#include #define ARRAY_2DFXInfoStore 0xB4C2D8 // C2dfxInfoStore d2fxModels @@ -31,14 +34,103 @@ #define OFFSET_FxSystem_Entities 0xC #define OFFSET_FxSystem_Link_Prev 0x4 -class C2DEffectSAInterface; - -class C2DEffectSA +class C2DEffectSA : public C2DEffect { public: - static int effect2dPluginOffset; + C2DEffectSA(C2DEffectSAInterface* effectInterface, std::uint32_t modelID); + ~C2DEffectSA() = default; + + C2DEffectSAInterface* GetInterface() noexcept { return m_effectInterface; } + + bool IsValidLight() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::LIGHT; }; + bool IsValidRoadsign() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::ROADSIGN; } + bool IsValidEscalator() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::ESCALATOR; } + bool IsValidParticle() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::PARTICLE; } + + void Destroy() const; + + void SetPosition(const CVector& position) override; + CVector& GetPosition() override; + + // Light properties + // Set + void SetCoronaFarClip(float clip) override; + void SetCoronaPointLightRange(float range) override; + void SetCoronaSize(float size) override; + void SetShadowSize(float size) override; + void SetShadowMultiplier(std::uint8_t multiplier) override; + void SetCoronaShowMode(e2dCoronaFlashType showMode) override; + void SetCoronaReflectionsEnabled(bool enable) override; + void SetCoronaFlareType(std::uint8_t flareType) override; + void SetLightFlags(std::uint16_t flags) override; + void SetShadowDistance(std::int8_t distance) override; + void SetCoronaOffsets(const CVector& offsets) override; + void SetCoronaColor(const RwColor& color) override; + void SetCoronaTexture(const std::string& name) override; + void SetShadowTexture(const std::string& name) override; + + // Get + float GetCoronaFarClip() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFarClip : 0.0f; } + float GetCoronaPointLightRange() const override { return IsValidLight() ? m_effectInterface->effect.light.pointLightRange : 0.0f; } + float GetCoronaSize() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaSize : 0.0f; } + float GetShadowSize() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowSize : 0.0f; } + std::uint8_t GetShadowMultiplier() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowColorMultiplier : 0; } + e2dCoronaFlashType GetCoronaShowMode() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFlashType : e2dCoronaFlashType::UNUSED; } + bool GetCoronaReflectionsEnabled() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaEnableReflection : false; } + std::uint8_t GetCoronaFlareType() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFlareType : 0; } + std::uint16_t GetLightFlags() const override { return IsValidLight() ? m_effectInterface->effect.light.flags : 0; } + std::int8_t GetShadowDistance() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowZDistance : 0; } + CVector GetCoronaOffsets() const override; + RwColor GetCoronaColor() const override { return IsValidLight() ? m_effectInterface->effect.light.color : RwColor{0,0,0,0}; } + std::string GetCoronaTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.coronaTex ? m_effectInterface->effect.light.coronaTex->name : "") : ""; } + std::string GetShadowTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.shadowTex ? m_effectInterface->effect.light.shadowTex->name : "") : ""; } + + // Particle properties + // Set + void SetParticleName(const std::string& name) override; + + // Get + std::string GetParticleName() const override { return IsValidParticle() ? (m_effectInterface->effect.particle.szName ? m_effectInterface->effect.particle.szName : "") : ""; } + + // Roadsign properties + // Set + void SetRoadsignSize(const RwV2d& size) override; + void SetRoadsignRotation(const RwV3d& rotation) override; + void SetRoadsignFlags(std::uint8_t flags) override; + void SetRoadsignText(const std::string& text, std::uint8_t line) override; + + // Get + RwV2d GetRoadsignSize() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.size : RwV2d{0,0}; } + RwV3d GetRoadsignRotation() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.rotation : RwV3d{0,0,0}; } + std::uint16_t GetRoadsignFlags() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.flags : 0; } + std::string GetRoadsignText() const override { return IsValidRoadsign() ? (m_effectInterface->effect.roadsign.text ? std::string(m_effectInterface->effect.roadsign.text, strnlen(m_effectInterface->effect.roadsign.text, 64)) : "") : ""; } + + // Escalator properties + // Set + void SetEscalatorBottom(const RwV3d& bottom) override; + void SetEscalatorTop(const RwV3d& top) override; + void SetEscalatorEnd(const RwV3d& end) override; + void SetEscalatorDirection(std::uint8_t direction) override; + + // Get + RwV3d GetEscalatorBottom() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.bottom : RwV3d{0, 0, 0}; } + RwV3d GetEscalatorTop() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.top : RwV3d{0, 0, 0}; } + RwV3d GetEscalatorEnd() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.end : RwV3d{0, 0, 0}; } + std::uint8_t GetEscalatorDirection() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.direction : 0; } + + e2dEffectType GetEffectType() override { return m_effectInterface ? m_effectInterface->type : e2dEffectType::NONE; } + + static C2DEffectSAInterface* CreateCopy(C2DEffectSAInterface* effect); static void Shutdown(C2DEffectSAInterface* effect); static void SafeDelete2DFXEffect(C2DEffectSAInterface* effect); static void PrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist); + +public: + static int effect2dPluginOffset; + +private: + C2DEffectSAInterface* m_effectInterface; + std::uint32_t m_model; + }; diff --git a/Client/game_sa/C2DEffectSAInterface.h b/Client/game_sa/C2DEffectSAInterface.h index 15ff87fa86..711275993c 100644 --- a/Client/game_sa/C2DEffectSAInterface.h +++ b/Client/game_sa/C2DEffectSAInterface.h @@ -12,8 +12,8 @@ #include "game/RenderWare.h" #include "game/CModelInfo.h" +#include "game/C2DEffects.h" #include "CObjectSA.h" -#include "C2DEffectSA.h" struct t2dEffectLight { @@ -248,8 +248,3 @@ class CEscalatorSAInterface CEntitySAInterface* entity; CObjectSAInterface* objects[42]; }; - -static void StaticPrepareTexturesForLightEffect(RwTexture*& coronaTex, RwTexture*& shadowTex, const char* coronaName, const char* shadowName, bool removeIfExist) -{ - C2DEffectSA::PrepareTexturesForLightEffect(coronaTex, shadowTex, coronaName, shadowName, removeIfExist); -} diff --git a/Client/game_sa/C2DEffectsSA.cpp b/Client/game_sa/C2DEffectsSA.cpp new file mode 100644 index 0000000000..f258c045ab --- /dev/null +++ b/Client/game_sa/C2DEffectsSA.cpp @@ -0,0 +1,66 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/C2DEffectsSA.cpp + * PURPOSE: 2dfx class + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "C2DEffectsSA.h" +#include "C2DEffectSA.h" +#include "CGameSA.h" + +extern CGameSA* pGame; + +C2DEffect* C2DEffectsSA::Create(std::uint32_t model, const CVector& position, const e2dEffectType& type) +{ + CModelInfo* modelInfo = pGame->GetModelInfo(model); + if (!modelInfo) + return nullptr; + + C2DEffectSAInterface* effectInterface = modelInfo->Add2DFXEffect(position, type); + if (!effectInterface) + return nullptr; + + C2DEffectSA* effect = new C2DEffectSA(effectInterface, model); + return effect; +} + +void C2DEffectsSA::Destroy(C2DEffect* effect) +{ + if (!effect) + return; + + auto* effectInterface = dynamic_cast(effect)->GetInterface(); + for (auto it = m_effects.begin(); it != m_effects.end(); ++it) + { + if ((*it)->GetInterface() == effectInterface) + { + (*it)->Destroy(); + + delete *it; + m_effects.erase(it); + break; + } + } +} + +C2DEffectSA* C2DEffectsSA::Get(C2DEffectSAInterface* effectInterface) const +{ + for (const auto& effect : m_effects) + if (effect->GetInterface() == effectInterface) + return effect; + + return nullptr; +} + +void C2DEffectsSA::RemoveFromList(C2DEffectSA* effect) +{ + auto& it = std::find(m_effects.begin(), m_effects.end(), effect); + if (it != m_effects.end()) + m_effects.erase(it); +} diff --git a/Client/game_sa/C2DEffectsSA.h b/Client/game_sa/C2DEffectsSA.h new file mode 100644 index 0000000000..94195a4fec --- /dev/null +++ b/Client/game_sa/C2DEffectsSA.h @@ -0,0 +1,26 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/C2DEffectsSA.h + * PURPOSE: Header file for 2dfx class + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once +#include "C2DEffectSA.h" + +class C2DEffectsSA : public C2DEffects +{ +public: + C2DEffect* Create(std::uint32_t model, const CVector& position, const e2dEffectType& type) override; + void Destroy(C2DEffect* effect) override; + C2DEffectSA* Get(C2DEffectSAInterface* effectInterface) const override; + void AddToList(C2DEffectSA* effect) { m_effects.push_back(effect); } + void RemoveFromList(C2DEffectSA* effect); + +private: + std::list m_effects; +}; diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 6786f32cea..f5e4767fc3 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -59,6 +59,7 @@ #include "CIplStoreSA.h" #include "CBuildingRemovalSA.h" #include "CCheckpointSA.h" +#include "C2DEffectsSA.h" extern CGameSA* pGame; @@ -146,6 +147,7 @@ CGameSA::CGameSA() m_pBuildingRemoval = new CBuildingRemovalSA(); m_pRenderer = std::make_unique(); + m_p2DEffects = std::make_unique(); // Normal weapon types (WEAPONSKILL_STD) for (int i = 0; i < NUM_WeaponInfosStdSkill; i++) diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index edfdce6508..a5f06c9db7 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -18,6 +18,7 @@ #include "CCoverManagerSA.h" #include "CPlantManagerSA.h" #include "CRendererSA.h" +#include "C2DEffectSA.h" class CAnimBlendClumpDataSAInterface; class CObjectGroupPhysicalPropertiesSA; @@ -174,7 +175,8 @@ class CGameSA : public CGame CPlantManagerSA* GetPlantManager() const noexcept { return m_pPlantManager; }; CBuildingRemoval* GetBuildingRemoval() { return m_pBuildingRemoval; } CRenderer* GetRenderer() const noexcept override { return m_pRenderer.get(); } - + C2DEffects* Get2DEffects() const noexcept override { return m_p2DEffects.get(); } + CWeaponInfo* GetWeaponInfo(eWeaponType weapon, eWeaponSkill skill = WEAPONSKILL_STD); CModelInfo* GetModelInfo(DWORD dwModelID, bool bCanBeInvalid = false); CObjectGroupPhysicalProperties* GetObjectGroupPhysicalProperties(unsigned char ucObjectGroup); @@ -350,6 +352,7 @@ class CGameSA : public CGame CBuildingRemoval* m_pBuildingRemoval; std::unique_ptr m_pRenderer; + std::unique_ptr m_p2DEffects; CPad* m_pPad; CAERadioTrackManager* m_pCAERadioTrackManager; diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index 567a37808f..b35cbe4425 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -38,12 +38,11 @@ std::unordered_map CModelInfo std::unordered_map> CModelInfoSA::ms_VehicleModelDefaultWheelSizes; std::map CModelInfoSA::ms_DefaultTxdIDMap; -static std::unordered_map> ms_DefaultEffectsMap; -static std::unordered_map numCustom2dfxEffects; -static std::vector d2fxEffects; -static std::vector removedDefault2dfxEffects; -static std::unordered_map defaultNumOf2DFXEffects; -static std::unordered_map> tempCopy2dfxEffects; +std::unordered_map CModelInfoSA::ms_DefaultNumOf2DFXEffects; +std::unordered_map> CModelInfoSA::ms_DefaultEffectsMap; +static std::unordered_map ms_NumOfCustom2DFXEffects; +static std::vector ms_Custom2DFXEffects; +std::unordered_map> CModelInfoSA::ms_TempCopiesOfDefault2DFXEffects; int C2DEffectSA::effect2dPluginOffset = *(int*)0xC3A1E0; // g2dEffectPluginOffset static auto* fx = reinterpret_cast(VAR_G_Fx); @@ -120,7 +119,7 @@ CBaseModelInfoSAInterface* CModelInfoSA::GetInterface() void CModelInfoSA::SetModelID(DWORD dwModelID) { m_dwModelID = dwModelID; - MapSet(numCustom2dfxEffects, ppModelInfo[dwModelID], 0); + MapSet(ms_NumOfCustom2DFXEffects, ppModelInfo[dwModelID], 0); } bool CModelInfoSA::IsBoat() @@ -1200,37 +1199,24 @@ void CModelInfoSA::StaticReset2DFXEffects() // Copy default effect MemCpy(innerIter->first, innerIter->second, sizeof(C2DEffectSAInterface)); - // Increase the counter if this effect was removed - auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), innerIter->first); - if (removedEffect != removedDefault2dfxEffects.end()) - { - removedDefault2dfxEffects.erase(removedEffect); - modelInfoInterface->ucNumOf2DEffects++; - } - // Delete copy of the default effect C2DEffectSA::SafeDelete2DFXEffect(innerIter->second); innerIter = iter->second.erase(innerIter); } - // Decrement the counter by the number of custom effects - auto customEffectsCount = MapGet(numCustom2dfxEffects, modelInfoInterface); - if (customEffectsCount && customEffectsCount > 0) - modelInfoInterface->ucNumOf2DEffects -= customEffectsCount; - - MapSet(numCustom2dfxEffects, modelInfoInterface, 0); + // Restore counter + modelInfoInterface->ucNumOf2DEffects = MapGet(ms_DefaultNumOf2DFXEffects, iter->first); // Destroy copies - auto& copies = MapGet(tempCopy2dfxEffects, iter->first); - for (auto copy : copies) + auto& copies = MapGet(ms_TempCopiesOfDefault2DFXEffects, iter->first); + for (auto& copy : copies) C2DEffectSA::SafeDelete2DFXEffect(copy); } // Clear maps & vectors - removedDefault2dfxEffects.clear(); ms_DefaultEffectsMap.clear(); - d2fxEffects.clear(); - tempCopy2dfxEffects.clear(); + ms_Custom2DFXEffects.clear(); + ms_TempCopiesOfDefault2DFXEffects.clear(); } short CModelInfoSA::GetAvailableVehicleMod(unsigned short usUpgrade) @@ -2141,13 +2127,17 @@ auto CModelInfoSA::GetEntitiesFromFx(std::uint32_t modelID) return vec; } -void CModelInfoSA::Update2DFXEffect(C2DEffectSAInterface* effect) +void CModelInfoSA::Update2DFXEffect(C2DEffectSA* effect) { if (!effect) return; + C2DEffectSAInterface* effectInterface = effect->GetInterface(); + if (!effectInterface) + return; + // This function aims to keep 2dfx effects updated to avoid restreaming - switch (effect->type) + switch (effectInterface->type) { case e2dEffectType::PARTICLE: { @@ -2165,7 +2155,7 @@ void CModelInfoSA::Update2DFXEffect(C2DEffectSAInterface* effect) } // Call Fx_c::CreateEntityFx - ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*, const char*, RwV3d*, RwMatrix*))FUNC_Fx_c_CreateEntityFx)(fx, entity, effect->effect.particle.szName, &effect->position, matrixTransform); + ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*, const char*, RwV3d*, RwMatrix*))FUNC_Fx_c_CreateEntityFx)(fx, entity, effectInterface->effect.particle.szName, &effectInterface->position, matrixTransform); } } } @@ -2174,45 +2164,21 @@ void CModelInfoSA::Update2DFXEffect(C2DEffectSAInterface* effect) void CModelInfoSA::StoreDefault2DFXEffect(C2DEffectSAInterface* effect) { // Is custom effect? - if (std::find(d2fxEffects.begin(), d2fxEffects.end(), effect) != d2fxEffects.end()) + if (std::find(ms_Custom2DFXEffects.begin(), ms_Custom2DFXEffects.end(), effect) != ms_Custom2DFXEffects.end()) return; + // Is effect stored already? if (MapContains(ms_DefaultEffectsMap, m_dwModelID) && MapContains(MapGet(ms_DefaultEffectsMap, m_dwModelID), effect)) return; - if (!MapContains(defaultNumOf2DFXEffects, m_dwModelID)) - MapSet(defaultNumOf2DFXEffects, m_dwModelID, ppModelInfo[m_dwModelID]->ucNumOf2DEffects); - // Copy an existing default effect - C2DEffectSAInterface* copy = new C2DEffectSAInterface(); - MemCpyFast(copy, effect, sizeof(C2DEffectSAInterface)); - - // Create a copy of textures for the lights - // We must to do this, because C2DEffect::Shutdown removes them - if (copy->type == e2dEffectType::LIGHT) - { - if (effect->effect.light.coronaTex && effect->effect.light.shadowTex) - C2DEffectSA::PrepareTexturesForLightEffect(copy->effect.light.coronaTex, copy->effect.light.shadowTex, effect->effect.light.coronaTex->name, effect->effect.light.shadowTex->name, false); - } - else if (copy->type == e2dEffectType::ROADSIGN) - { - // Create a copy of text and atomic for the roadsign - // We must to do this, because C2DEffect::Shutdown removes them - copy->effect.roadsign.text = static_cast(std::malloc(64)); - if (copy->effect.roadsign.text) - { - std::memset(copy->effect.roadsign.text, 0, 64); - std::strncpy(copy->effect.roadsign.text, effect->effect.roadsign.text, 64); - } - - copy->effect.roadsign.atomic = RpAtomicClone(effect->effect.roadsign.atomic); - } + C2DEffectSAInterface* copy = C2DEffectSA::CreateCopy(effect); // Store copy in the map MapSet(ms_DefaultEffectsMap, m_dwModelID, std::unordered_map{{effect, copy}}); } -bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) +bool CModelInfoSA::Reset2DFXEffects() { m_pInterface = ppModelInfo[m_dwModelID]; if (!m_pInterface) @@ -2223,51 +2189,33 @@ bool CModelInfoSA::Reset2DFXEffects(bool removeCustomEffects) // Restore default effects auto& map = MapGet(ms_DefaultEffectsMap, m_dwModelID); + auto* effects = pGame->Get2DEffects(); for (auto& it = map.begin(); it != map.end();) { // Copy data from copied effect to the default MemCpyFast(it->first, it->second, sizeof(C2DEffectSAInterface)); - // Increase the counter if this effect was removed - auto& removedEffect = std::find(removedDefault2dfxEffects.begin(), removedDefault2dfxEffects.end(), it->first); - if (removedEffect != removedDefault2dfxEffects.end()) - { - removedDefault2dfxEffects.erase(removedEffect); - m_pInterface->ucNumOf2DEffects++; - } - - Update2DFXEffect(it->first); - // We no longer need a copy // So delete it C2DEffectSA::SafeDelete2DFXEffect(it->second); it = map.erase(it); + + // Update effect + Update2DFXEffect(effects->Get(it->first)); } + // Restore counter + m_pInterface->ucNumOf2DEffects = MapGet(ms_DefaultNumOf2DFXEffects, m_dwModelID); + // Delete temp copies - auto& copies = MapGet(tempCopy2dfxEffects, m_dwModelID); + auto& copies = MapGet(ms_TempCopiesOfDefault2DFXEffects, m_dwModelID); for (auto* copy : copies) C2DEffectSA::SafeDelete2DFXEffect(copy); // Clear maps map.clear(); ms_DefaultEffectsMap.erase(m_dwModelID); - tempCopy2dfxEffects.erase(m_dwModelID); - - // Remove all custom effects - if (removeCustomEffects) - { - std::uint32_t numEffects = m_pInterface->ucNumOf2DEffects; - for (std::uint32_t i = 0; i < numEffects; i++) - { - C2DEffectSAInterface* effect = Get2DFXFromIndex(i); - if (!effect) - continue; - - Remove2DFX(effect, false); - } - } - + ms_TempCopiesOfDefault2DFXEffects.erase(m_dwModelID); return true; } @@ -2284,10 +2232,10 @@ C2DEffectSAInterface* CModelInfoSA::Add2DFXEffect(const CVector& position, const // Update counters m_pInterface->ucNumOf2DEffects = m_pInterface->ucNumOf2DEffects ? m_pInterface->ucNumOf2DEffects + 1 : 1; - MapGet(numCustom2dfxEffects, m_pInterface)++; + MapGet(ms_NumOfCustom2DFXEffects, m_pInterface)++; // Save our effect - d2fxEffects.push_back(effectInterface); + ms_Custom2DFXEffects.push_back(effectInterface); return effectInterface; } @@ -2300,8 +2248,8 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) if (!effect) return false; - auto& it = std::find(d2fxEffects.begin(), d2fxEffects.end(), effect); - bool isCustomEffect = it != d2fxEffects.end(); + auto& it = std::find(ms_Custom2DFXEffects.begin(), ms_Custom2DFXEffects.end(), effect); + bool isCustomEffect = it != ms_Custom2DFXEffects.end(); if (!includeDefault && !isCustomEffect) return false; @@ -2324,7 +2272,7 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) } case e2dEffectType::PARTICLE: { - auto entities = GetEntitiesFromFx(m_dwModelID); + auto& entities = GetEntitiesFromFx(m_dwModelID); for (auto* entity : entities) // Call Fx_c::DestroyEntityFx ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*))FUNC_Fx_c_DestroyEntityFx)(fx, entity); @@ -2364,17 +2312,15 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) return false; } - // If it's custom effect then delete it. If it's default effect then store it as removed + // If it's custom effect then delete it if (isCustomEffect) { - MapGet(numCustom2dfxEffects, m_pInterface)--; - d2fxEffects.erase(it); + MapGet(ms_NumOfCustom2DFXEffects, m_pInterface)--; + ms_Custom2DFXEffects.erase(it); delete effect; effect = nullptr; } - else - removedDefault2dfxEffects.push_back(effect); } bool CModelInfoSA::Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault) @@ -2383,11 +2329,11 @@ bool CModelInfoSA::Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefa if (!m_pInterface) return false; - C2DEffectSAInterface* effect = Get2DFXFromIndex(index); + C2DEffectSA* effect = Get2DFXFromIndex(index); if (!effect) return false; - return Remove2DFX(effect, includeDefault); + return Remove2DFX(effect->GetInterface(), includeDefault); } bool CModelInfoSA::RemoveAll2DFXEffects(bool includeDefault) @@ -2399,25 +2345,28 @@ bool CModelInfoSA::RemoveAll2DFXEffects(bool includeDefault) std::uint32_t numEffects = m_pInterface->ucNumOf2DEffects; for (std::uint32_t i = 0; i < numEffects; i++) { - C2DEffectSAInterface* effect = Get2DFXFromIndex(i); + C2DEffectSA* effect = Get2DFXFromIndex(i); if (!effect) continue; - Remove2DFX(effect, includeDefault); + Remove2DFX(effect->GetInterface(), includeDefault); } - d2fxEffects.clear(); return true; } -C2DEffectSAInterface* CModelInfoSA::Get2DFXFromIndex(std::uint32_t index) +C2DEffectSA* CModelInfoSA::Get2DFXFromIndex(std::uint32_t index) { m_pInterface = ppModelInfo[m_dwModelID]; if (!m_pInterface) return nullptr; // Call CBaseModelInfo::Get2dEffect - return ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(m_pInterface, index); + auto* effectInterface = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(m_pInterface, index); + if (!effectInterface) + return nullptr; + + return pGame->Get2DEffects()->Get(effectInterface); } void CModelInfoSA::CopyModified2DFXEffects() @@ -2426,58 +2375,74 @@ void CModelInfoSA::CopyModified2DFXEffects() if (!modelInfo || modelInfo->ucNumOf2DEffects == 0) return; - // Has modified effects? - if (!MapContains(ms_DefaultEffectsMap, m_dwModelID)) - return; - + bool hasModifiedEffects = MapContains(ms_DefaultEffectsMap, m_dwModelID); auto tempVec = std::vector(); - std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); + std::uint32_t numEffects = MapGet(ms_DefaultNumOf2DFXEffects, m_dwModelID); + auto* effects = pGame->Get2DEffects(); for (std::uint32_t i = 0; i < numEffects; i++) { - auto effect = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index)) FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); - if (!effect) + auto effectInterface = ((C2DEffectSAInterface * (__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index)) FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effectInterface) continue; - // Copy effect - auto copy = new C2DEffectSAInterface(); - MemCpyFast(copy, effect, sizeof(C2DEffectSAInterface)); - tempVec.push_back(copy); + // Delete our 2dfx object + C2DEffectSA* effect = effects->Get(effectInterface); + if (effect) + { + effects->RemoveFromList(effect); + delete effect; + } + + if (hasModifiedEffects) + { + // Copy effect + auto* copy = C2DEffectSA::CreateCopy(effectInterface); + tempVec.push_back(copy); + } } - MapSet(tempCopy2dfxEffects, m_dwModelID, tempVec); + if (hasModifiedEffects) + MapSet(ms_TempCopiesOfDefault2DFXEffects, m_dwModelID, tempVec); } void CModelInfoSA::RestoreModified2DFXEffects() { - if (!MapContains(tempCopy2dfxEffects, m_dwModelID)) - return; - CBaseModelInfoSAInterface* modelInfo = ppModelInfo[m_dwModelID]; if (!modelInfo) return; - std::uint32_t numEffects = MapGet(defaultNumOf2DFXEffects, m_dwModelID); - auto& tempVec = MapGet(tempCopy2dfxEffects, m_dwModelID); - if (tempVec.size() > 0) + // Set default num of 2dfx effects + if (!MapContains(ms_DefaultNumOf2DFXEffects, m_dwModelID)) + MapSet(ms_DefaultNumOf2DFXEffects, m_dwModelID, modelInfo->ucNumOf2DEffects); + + // Create C2DEffectSA instance for each default effect interface + std::uint32_t numEffects = MapGet(ms_DefaultNumOf2DFXEffects, m_dwModelID); + auto* tempVec = MapFind(ms_TempCopiesOfDefault2DFXEffects, m_dwModelID); + for (std::uint32_t i = 0; i < numEffects; i++) { - for (std::uint32_t i = 0; i < numEffects; i++) - { - auto effect = ((C2DEffectSAInterface*(__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t index))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); - if (!effect) - continue; + auto* effectInterface = ((C2DEffectSAInterface*(__thiscall*)(CBaseModelInfoSAInterface*, std::uint32_t))FUNC_CBaseModelInfo_Get2dEffect)(modelInfo, i); + if (!effectInterface) + continue; - if (tempVec[i]) - { - MemCpyFast(effect, tempVec[i], sizeof(C2DEffectSAInterface)); - C2DEffectSA::SafeDelete2DFXEffect(tempVec[i]); - } + // Create our 2dfx object instance + C2DEffectSA* effect = new C2DEffectSA(effectInterface, m_dwModelID); + if (!effect) + continue; + + if (tempVec && (*tempVec)[i]) + { + MemCpyFast(effectInterface, (*tempVec)[i], sizeof(C2DEffectSAInterface)); + C2DEffectSA::SafeDelete2DFXEffect((*tempVec)[i]); Update2DFXEffect(effect); } } - tempVec.clear(); - tempCopy2dfxEffects.erase(m_dwModelID); + if (tempVec) + { + tempVec->clear(); + ms_TempCopiesOfDefault2DFXEffects.erase(m_dwModelID); + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -2537,7 +2502,7 @@ static C2DEffectSAInterface* Get2dEffect(CBaseModelInfoSAInterface* modelInfo, R static auto* storedEffects = reinterpret_cast(ARRAY_2DFXInfoStore); - std::uint32_t numCustomEffects = numCustom2dfxEffects[modelInfo]; + std::uint32_t numCustomEffects = ms_NumOfCustom2DFXEffects[modelInfo]; std::uint32_t numStoredEffects = modelInfo->ucNumOf2DEffects - numPluginEffects - numCustomEffects; if (index < numStoredEffects) @@ -2548,7 +2513,7 @@ static C2DEffectSAInterface* Get2dEffect(CBaseModelInfoSAInterface* modelInfo, R return &pluginEffectData->objects[index - numStoredEffects]; } else - return d2fxEffects[index - numPluginEffects - numStoredEffects]; + return ms_Custom2DFXEffects[index - numPluginEffects - numStoredEffects]; return nullptr; } diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index d5b181dbf5..964c22751e 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -12,9 +12,10 @@ #pragma once #include +#include "C2DEffectSAInterface.h" #include #include "CRenderWareSA.h" -#include "C2DEffectSAInterface.h" +#include "C2DEffectSA.h" class CPedModelInfoSA; class CPedModelInfoSAInterface; @@ -332,6 +333,11 @@ class CModelInfoSA : public CModelInfo static std::unordered_map ms_OriginalObjectPropertiesGroups; static std::unordered_map> ms_VehicleModelDefaultWheelSizes; static std::map ms_DefaultTxdIDMap; + + static std::unordered_map ms_DefaultNumOf2DFXEffects; + static std::unordered_map> ms_DefaultEffectsMap; + static std::unordered_map> ms_TempCopiesOfDefault2DFXEffects; + SVehicleSupportedUpgrades m_ModelSupportedUpgrades; public: @@ -474,14 +480,16 @@ class CModelInfoSA : public CModelInfo bool Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault); bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false); bool RemoveAll2DFXEffects(bool includeDefault); - C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index); + C2DEffectSA* Get2DFXFromIndex(std::uint32_t index); std::uint32_t Get2DFXCount() const { return m_pInterface ? m_pInterface->ucNumOf2DEffects : 0; } - void Update2DFXEffect(C2DEffectSAInterface* effect); + void Update2DFXEffect(C2DEffectSA* effect); + void Update2DFXEffect(C2DEffect* effect) { Update2DFXEffect(dynamic_cast(effect)); } static auto GetEntitiesFromFx(std::uint32_t modelID); + void StoreDefault2DFXEffect(C2DEffectSA* effect) { StoreDefault2DFXEffect(effect->GetInterface()); } void StoreDefault2DFXEffect(C2DEffectSAInterface* effect); - bool Reset2DFXEffects(bool removeCustomEffects = false); + bool Reset2DFXEffects(); static void StaticReset2DFXEffects(); void CopyModified2DFXEffects(); diff --git a/Client/mods/deathmatch/logic/CClient2DFX.cpp b/Client/mods/deathmatch/logic/CClient2DFX.cpp index d128260f25..da4a1506a3 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFX.cpp @@ -12,7 +12,7 @@ #include "CClient2DFXManager.h" CClient2DFX::CClient2DFX(class CClientManager* manager, ElementID ID) - : ClassInit(this), CClientEntity(ID), m_2DFXManager(manager->Get2DFXManager()), m_effectInterface(nullptr), m_effectType(e2dEffectType::NONE) + : ClassInit(this), CClientEntity(ID), m_2DFXManager(manager->Get2DFXManager()), m_effect(nullptr), m_effectType(e2dEffectType::NONE), m_model(0) { m_pManager = manager; SetTypeName("2dfx"); @@ -25,11 +25,7 @@ CClient2DFX::~CClient2DFX() m_2DFXManager->RemoveFromList(this); // Destroy effect - CModelInfo* modelInfo = g_pGame->GetModelInfo(m_model); - if (!modelInfo) - return; - - modelInfo->Remove2DFX(m_effectInterface, false); + g_pGame->Get2DEffects()->Destroy(m_effect); } bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData) @@ -38,29 +34,15 @@ bool CClient2DFX::Create(std::uint32_t model, const CVector& position, const e2d if (!modelInfo) return false; - C2DEffectSAInterface* effect = modelInfo->Add2DFXEffect(position, type); + C2DEffect* effect = g_pGame->Get2DEffects()->Create(model, position, type); if (!effect) return false; - // Set effect - m_effectInterface = effect; - m_effectType = effect->type; - m_model = static_cast(model); + m_2DFXManager->Set2DFXProperties(effect, effectData); - switch (effect->type) - { - case e2dEffectType::ROADSIGN: - { - t2dEffectRoadsign& roadsign = effect->effect.roadsign; - roadsign.size = RwV2d{0, 0}; - roadsign.rotation = RwV3d{0, 0, 0}; - roadsign.atomic = nullptr; - roadsign.text = nullptr; - } - } - - if (!m_2DFXManager->Set2DFXProperties(effect, effectData)) - return false; + m_effect = effect; + m_effectType = type; + m_model = model; return true; } diff --git a/Client/mods/deathmatch/logic/CClient2DFX.h b/Client/mods/deathmatch/logic/CClient2DFX.h index d6dffe5e33..787b60e237 100644 --- a/Client/mods/deathmatch/logic/CClient2DFX.h +++ b/Client/mods/deathmatch/logic/CClient2DFX.h @@ -9,6 +9,8 @@ #pragma once #include "CClientEntity.h" +#include "game/C2DEffects.h" +#include "game/C2DEffect.h" using effectDataMap = std::unordered_map>; @@ -29,13 +31,13 @@ class CClient2DFX final : public CClientEntity bool Create(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData); - e2dEffectType Get2DFXType() const noexcept { return m_effectType; } - C2DEffectSAInterface* Get2DFX() const noexcept { return m_effectInterface; } - std::uint32_t GetModelID() const noexcept { return m_model; } + e2dEffectType Get2DFXType() const noexcept { return m_effectType; } + C2DEffect* Get2DFX() const noexcept { return m_effect; } + std::uint32_t GetModelID() const noexcept { return m_model; } private: class CClient2DFXManager* m_2DFXManager; - C2DEffectSAInterface* m_effectInterface; + C2DEffect* m_effect; e2dEffectType m_effectType; std::uint32_t m_model; }; diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index 6bb1b615ec..6fae3e8032 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -64,7 +64,7 @@ CClient2DFX* CClient2DFXManager::Add2DFX(std::uint32_t model, const CVector& pos return effect; } -bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, const effectDataMap& effectData) +bool CClient2DFXManager::Set2DFXProperties(C2DEffect* effect, const effectDataMap& effectData) { if (!effect) return false; @@ -87,74 +87,69 @@ bool CClient2DFXManager::Set2DFXProperties(C2DEffectSAInterface* effect, const e return success; } -effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect) const +effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffect* effect) const { auto properties = effectDataMap(); if (!effect) return properties; // Get properties - switch (effect->type) + switch (effect->GetEffectType()) { case e2dEffectType::LIGHT: { - const t2dEffectLight& light = effect->effect.light; - - MapSet(properties, "drawDistance", light.coronaFarClip); - MapSet(properties, "lightRange", light.pointLightRange); - MapSet(properties, "coronaSize", light.coronaSize); - MapSet(properties, "shadowSize", light.shadowSize); - MapSet(properties, "shadowMultiplier", static_cast(light.shadowColorMultiplier)); - MapSet(properties, "showMode", EnumToString(light.coronaFlashType)); - MapSet(properties, "coronaReflection", light.coronaEnableReflection); - MapSet(properties, "flareType", static_cast(light.coronaFlareType)); - MapSet(properties, "flags", static_cast(light.flags)); - MapSet(properties, "shadowDistance", static_cast(light.shadowZDistance)); - MapSet(properties, "offsetX", static_cast(light.offsetX)); - MapSet(properties, "offsetY", static_cast(light.offsetY)); - MapSet(properties, "offsetZ", static_cast(light.offsetZ)); - - int colorValue = (static_cast(light.color.a) << 24) | (static_cast(light.color.r) << 16) | (static_cast(light.color.g) << 8) | static_cast(light.color.b); + MapSet(properties, "drawDistance", effect->GetCoronaFarClip()); + MapSet(properties, "lightRange", effect->GetCoronaPointLightRange()); + MapSet(properties, "coronaSize", effect->GetCoronaSize()); + MapSet(properties, "shadowSize", effect->GetShadowSize()); + MapSet(properties, "shadowMultiplier", static_cast(effect->GetShadowMultiplier())); + MapSet(properties, "showMode", EnumToString(effect->GetCoronaShowMode())); + MapSet(properties, "coronaReflection", effect->GetCoronaReflectionsEnabled()); + MapSet(properties, "flareType", static_cast(effect->GetCoronaFlareType())); + MapSet(properties, "flags", static_cast(effect->GetLightFlags())); + MapSet(properties, "shadowDistance", static_cast(effect->GetShadowDistance())); + MapSet(properties, "offsetX", effect->GetCoronaOffsets().fX); + MapSet(properties, "offsetY", effect->GetCoronaOffsets().fY); + MapSet(properties, "offsetZ", effect->GetCoronaOffsets().fZ); + + RwColor& color = effect->GetCoronaColor(); + int colorValue = (static_cast(color.a) << 24) | (static_cast(color.r) << 16) | (static_cast(color.g) << 8) | static_cast(color.b); MapSet(properties, "color", static_cast(colorValue)); - MapSet(properties, "coronaName", !light.coronaTex ? "" : static_cast(light.coronaTex->name)); - MapSet(properties, "shadowName", !light.shadowTex ? "" : static_cast(light.shadowTex->name)); + MapSet(properties, "coronaName", effect->GetCoronaTexture()); + MapSet(properties, "shadowName", effect->GetShadowTexture()); break; } case e2dEffectType::PARTICLE: { - MapSet(properties, "name", static_cast(effect->effect.particle.szName)); + MapSet(properties, "name", effect->GetParticleName()); break; } case e2dEffectType::ROADSIGN: { - const t2dEffectRoadsign& roadsign = effect->effect.roadsign; - - MapSet(properties, "sizeX", roadsign.size.x); - MapSet(properties, "sizeY", roadsign.size.y); - MapSet(properties, "rotX", roadsign.rotation.x); - MapSet(properties, "rotY", roadsign.rotation.y); - MapSet(properties, "rotZ", roadsign.rotation.z); - MapSet(properties, "flags", static_cast(roadsign.flags)); - MapSet(properties, "text", !roadsign.text ? "" : std::string(roadsign.text, roadsignTextSize)); + MapSet(properties, "sizeX", effect->GetRoadsignSize().x); + MapSet(properties, "sizeY", effect->GetRoadsignSize().y); + MapSet(properties, "rotX", effect->GetRoadsignRotation().x); + MapSet(properties, "rotY", effect->GetRoadsignRotation().y); + MapSet(properties, "rotZ", effect->GetRoadsignRotation().z); + MapSet(properties, "flags", static_cast(effect->GetRoadsignFlags())); + MapSet(properties, "text", effect->GetRoadsignText()); break; } case e2dEffectType::ESCALATOR: { - const t2dEffectEscalator& escalator = effect->effect.escalator; - - MapSet(properties, "bottomX", escalator.bottom.x); - MapSet(properties, "bottomY", escalator.bottom.y); - MapSet(properties, "bottomZ", escalator.bottom.z); - MapSet(properties, "topX", escalator.top.x); - MapSet(properties, "topY", escalator.top.y); - MapSet(properties, "topZ", escalator.top.z); - MapSet(properties, "endX", escalator.end.x); - MapSet(properties, "endY", escalator.end.y); - MapSet(properties, "endZ", escalator.end.z); - MapSet(properties, "direction", static_cast(escalator.direction)); + MapSet(properties, "bottomX", effect->GetEscalatorBottom().x); + MapSet(properties, "bottomY", effect->GetEscalatorBottom().y); + MapSet(properties, "bottomZ", effect->GetEscalatorBottom().z); + MapSet(properties, "topX", effect->GetEscalatorTop().x); + MapSet(properties, "topY", effect->GetEscalatorTop().y); + MapSet(properties, "topZ", effect->GetEscalatorTop().z); + MapSet(properties, "endX", effect->GetEscalatorEnd().x); + MapSet(properties, "endY", effect->GetEscalatorEnd().y); + MapSet(properties, "endZ", effect->GetEscalatorEnd().z); + MapSet(properties, "direction", static_cast(effect->GetEscalatorDirection())); break; } @@ -165,24 +160,22 @@ effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffectSAInterface* effect return properties; } -bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue) +bool CClient2DFXManager::Set2DFXProperty(C2DEffect* effect, const e2dEffectProperty& property, const std::variant& propertyValue) { if (!effect) return false; - switch (effect->type) + switch (effect->GetEffectType()) { case e2dEffectType::LIGHT: { - t2dEffectLight& light = effect->effect.light; - switch (property) { case e2dEffectProperty::FAR_CLIP_DISTANCE: { if (std::holds_alternative(propertyValue)) { - light.coronaFarClip = std::get(propertyValue); + effect->SetCoronaFarClip(std::get(propertyValue)); return true; } break; @@ -191,7 +184,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.pointLightRange = std::get(propertyValue); + effect->SetCoronaPointLightRange(std::get(propertyValue)); return true; } break; @@ -200,7 +193,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.coronaSize = std::get(propertyValue); + effect->SetCoronaSize(std::get(propertyValue)); return true; } break; @@ -209,7 +202,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.shadowSize = std::get(propertyValue); + effect->SetShadowSize(std::get(propertyValue)); return true; } break; @@ -218,7 +211,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.shadowColorMultiplier = std::get(propertyValue); + effect->SetShadowMultiplier(std::get(propertyValue)); return true; } break; @@ -230,7 +223,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d e2dCoronaFlashType showMode; if (StringToEnum(std::get(propertyValue), showMode)) { - light.coronaFlashType = showMode; + effect->SetCoronaShowMode(showMode); return true; } } @@ -241,7 +234,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.coronaEnableReflection = std::get(propertyValue); + effect->SetCoronaReflectionsEnabled(std::get(propertyValue)); return true; } break; @@ -250,7 +243,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.coronaFlareType = static_cast(std::get(propertyValue)); + effect->SetCoronaFlareType(static_cast(std::get(propertyValue))); return true; } break; @@ -259,7 +252,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.flags = static_cast(std::get(propertyValue)); + effect->SetLightFlags(static_cast(std::get(propertyValue))); return true; } break; @@ -268,7 +261,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.shadowZDistance = static_cast(std::get(propertyValue)); + effect->SetShadowDistance(static_cast(std::get(propertyValue))); return true; } break; @@ -277,7 +270,10 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.offsetX = static_cast(std::get(propertyValue)); + CVector& offsets = effect->GetCoronaOffsets(); + offsets.fX = static_cast(std::get(propertyValue)); + + effect->SetCoronaOffsets(offsets); return true; } break; @@ -286,7 +282,10 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.offsetY = static_cast(std::get(propertyValue)); + CVector& offsets = effect->GetCoronaOffsets(); + offsets.fY = static_cast(std::get(propertyValue)); + + effect->SetCoronaOffsets(offsets); return true; } break; @@ -295,7 +294,10 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - light.offsetZ = static_cast(std::get(propertyValue)); + CVector& offsets = effect->GetCoronaOffsets(); + offsets.fZ = static_cast(std::get(propertyValue)); + + effect->SetCoronaOffsets(offsets); return true; } @@ -306,7 +308,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d if (std::holds_alternative(propertyValue)) { std::uint32_t colorValue = static_cast(std::get(propertyValue)); - light.color = RwColor{static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 24) & mask(8))}; + effect->SetCoronaColor(RwColor{static_cast((colorValue >> 16) & mask(8)), static_cast((colorValue >> 8) & mask(8)), static_cast((colorValue >> 0) & mask(8)), static_cast((colorValue >> 24) & mask(8))}); return true; } @@ -320,7 +322,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d e2dEffectTextureName coronaName; if (StringToEnum(std::get(propertyValue), coronaName)) { - //StaticPrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, std::get(propertyValue).c_str(), light.shadowTex ? light.shadowTex->name : nullptr, true); + effect->SetCoronaTexture(std::get(propertyValue)); return true; } } @@ -334,7 +336,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d e2dEffectTextureName shadowName; if (StringToEnum(std::get(propertyValue), shadowName)) { - //StaticPrepareTexturesForLightEffect(light.coronaTex, light.shadowTex, light.coronaTex ? light.coronaTex->name : nullptr, std::get(propertyValue).c_str(), true); + effect->SetShadowTexture(std::get(propertyValue)); return true; } } @@ -353,7 +355,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - std::strncpy(effect->effect.particle.szName, std::get(propertyValue).c_str(), sizeof(effect->effect.particle.szName)); + effect->SetParticleName(std::get(propertyValue)); return true; } } @@ -362,24 +364,15 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d } case e2dEffectType::ROADSIGN: { - t2dEffectRoadsign& roadsign = effect->effect.roadsign; - - if (!roadsign.text) - { - roadsign.text = static_cast(std::malloc(roadsignTextSize)); - if (!roadsign.text) - break; - - std::memset(roadsign.text, 0, roadsignTextSize); - } - switch (property) { case e2dEffectProperty::SIZE_X: { if (std::holds_alternative(propertyValue)) { - roadsign.size.x = std::get(propertyValue); + RwV2d& size = effect->GetRoadsignSize(); + size.x = std::get(propertyValue); + return true; } @@ -389,7 +382,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - roadsign.size.y = std::get(propertyValue); + RwV2d& size = effect->GetRoadsignSize(); + size.y = std::get(propertyValue); + return true; } @@ -399,7 +394,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - roadsign.rotation.x = std::get(propertyValue); + RwV3d& rotation = effect->GetRoadsignRotation(); + rotation.x = std::get(propertyValue); + return true; } @@ -409,7 +406,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - roadsign.rotation.y = std::get(propertyValue); + RwV3d& rotation = effect->GetRoadsignRotation(); + rotation.y = std::get(propertyValue); + return true; } @@ -419,7 +418,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - roadsign.rotation.z = std::get(propertyValue); + RwV3d& rotation = effect->GetRoadsignRotation(); + rotation.z = std::get(propertyValue); + return true; } @@ -429,7 +430,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - roadsign.flags = static_cast(std::get(propertyValue)); + effect->SetRoadsignFlags(static_cast(std::get(propertyValue))); return true; } @@ -439,7 +440,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - std::strncpy(roadsign.text, std::get(propertyValue).c_str(), 16); + effect->SetRoadsignText(std::get(propertyValue), 1); return true; } @@ -449,7 +450,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - std::strncpy(roadsign.text+16, std::get(propertyValue).c_str(), 16); + effect->SetRoadsignText(std::get(propertyValue), 2); return true; } @@ -459,7 +460,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - std::strncpy(roadsign.text + 32, std::get(propertyValue).c_str(), 16); + effect->SetRoadsignText(std::get(propertyValue), 3); return true; } @@ -469,7 +470,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - std::strncpy(roadsign.text + 48, std::get(propertyValue).c_str(), 16); + effect->SetRoadsignText(std::get(propertyValue), 4); return true; } @@ -482,15 +483,15 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d } case e2dEffectType::ESCALATOR: { - t2dEffectEscalator& escalator = effect->effect.escalator; - switch (property) { case e2dEffectProperty::BOTTOM_X: { if (std::holds_alternative(propertyValue)) { - escalator.bottom.x = std::get(propertyValue); + RwV3d& bottom = effect->GetEscalatorBottom(); + bottom.x = std::get(propertyValue); + return true; } @@ -500,7 +501,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.bottom.y = std::get(propertyValue); + RwV3d& bottom = effect->GetEscalatorBottom(); + bottom.y = std::get(propertyValue); + return true; } @@ -510,7 +513,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.bottom.z = std::get(propertyValue); + RwV3d& bottom = effect->GetEscalatorBottom(); + bottom.z = std::get(propertyValue); + return true; } @@ -520,7 +525,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.top.x = std::get(propertyValue); + RwV3d& top = effect->GetEscalatorTop(); + top.x = std::get(propertyValue); + return true; } @@ -530,7 +537,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.top.y = std::get(propertyValue); + RwV3d& top = effect->GetEscalatorTop(); + top.y = std::get(propertyValue); + return true; } @@ -540,7 +549,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.top.z = std::get(propertyValue); + RwV3d& top = effect->GetEscalatorTop(); + top.z = std::get(propertyValue); + return true; } @@ -550,7 +561,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.end.x = std::get(propertyValue); + RwV3d& end = effect->GetEscalatorEnd(); + end.x = std::get(propertyValue); + return true; } @@ -560,7 +573,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.end.y = std::get(propertyValue); + RwV3d& end = effect->GetEscalatorEnd(); + end.y = std::get(propertyValue); + return true; } @@ -570,7 +585,9 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.end.z = std::get(propertyValue); + RwV3d& end = effect->GetEscalatorEnd(); + end.z = std::get(propertyValue); + return true; } @@ -580,7 +597,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d { if (std::holds_alternative(propertyValue)) { - escalator.direction = static_cast(std::get(propertyValue)); + effect->SetEscalatorDirection(static_cast(std::get(propertyValue))); return true; } @@ -597,54 +614,54 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffectSAInterface* effect, const e2d return false; } -std::variant CClient2DFXManager::Get2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property) +std::variant CClient2DFXManager::Get2DFXProperty(C2DEffect* effect, const e2dEffectProperty& property) { if (!effect) return false; - switch (effect->type) + switch (effect->GetEffectType()) { case e2dEffectType::LIGHT: { - const t2dEffectLight& light = effect->effect.light; - switch (property) { case e2dEffectProperty::FAR_CLIP_DISTANCE: - return light.coronaFarClip; + return effect->GetCoronaFarClip(); case e2dEffectProperty::LIGHT_RANGE: - return light.pointLightRange; + return effect->GetCoronaPointLightRange(); case e2dEffectProperty::CORONA_SIZE: - return light.coronaSize; + return effect->GetCoronaSize(); case e2dEffectProperty::SHADOW_SIZE: - return light.shadowSize; + return effect->GetShadowSize(); case e2dEffectProperty::SHADOW_MULT: - return static_cast(light.shadowColorMultiplier); + return static_cast(effect->GetShadowMultiplier()); case e2dEffectProperty::FLASH_TYPE: - return EnumToString(light.coronaFlashType); + return EnumToString(effect->GetCoronaShowMode()); case e2dEffectProperty::CORONA_REFLECTION: - return light.coronaEnableReflection; + return effect->GetCoronaReflectionsEnabled(); case e2dEffectProperty::FLARE_TYPE: - return static_cast(light.coronaFlareType); + return static_cast(effect->GetCoronaFlareType()); case e2dEffectProperty::FLAGS: - return static_cast(light.flags); + return static_cast(effect->GetLightFlags()); case e2dEffectProperty::SHADOW_DISTANCE: - return static_cast(light.shadowZDistance); + return static_cast(effect->GetShadowDistance()); case e2dEffectProperty::OFFSET_X: - return static_cast(light.offsetX); + return static_cast(effect->GetCoronaOffsets().fX); case e2dEffectProperty::OFFSET_Y: - return static_cast(light.offsetY); + return static_cast(effect->GetCoronaOffsets().fY); case e2dEffectProperty::OFFSET_Z: - return static_cast(light.offsetZ); + return static_cast(effect->GetCoronaOffsets().fZ); case e2dEffectProperty::COLOR: { - int colorValue = (static_cast(light.color.a) << 24) | (static_cast(light.color.r) << 16) | (static_cast(light.color.g) << 8) | static_cast(light.color.b); + RwColor& color = effect->GetCoronaColor(); + int colorValue = (static_cast(color.a) << 24) | (static_cast(color.r) << 16) | (static_cast(color.g) << 8) | static_cast(color.b); + return static_cast(colorValue); } case e2dEffectProperty::CORONA_NAME: - return light.coronaTex ? light.coronaTex->name : ""; + return effect->GetCoronaTexture(); case e2dEffectProperty::SHADOW_NAME: - return light.shadowTex ? light.shadowTex->name : ""; + return effect->GetShadowTexture(); } break; @@ -652,63 +669,59 @@ std::variant CClient2DFXManager::Get2DFXProperty(C2DEf case e2dEffectType::PARTICLE: { if (property == e2dEffectProperty::PRT_NAME) - return effect->effect.particle.szName ? effect->effect.particle.szName : ""; + return effect->GetParticleName(); break; } case e2dEffectType::ROADSIGN: { - const t2dEffectRoadsign& roadsign = effect->effect.roadsign; - switch (property) { case e2dEffectProperty::SIZE_X: - return roadsign.size.x; + return effect->GetRoadsignSize().x; case e2dEffectProperty::SIZE_Y: - return roadsign.size.y; + return effect->GetRoadsignSize().y; case e2dEffectProperty::ROT_X: - return roadsign.rotation.x; + return effect->GetRoadsignRotation().x; case e2dEffectProperty::ROT_Y: - return roadsign.rotation.y; + return effect->GetRoadsignRotation().y; case e2dEffectProperty::ROT_Z: - return roadsign.rotation.z; + return effect->GetRoadsignRotation().z; case e2dEffectProperty::FLAGS: - return static_cast(roadsign.flags); + return static_cast(effect->GetRoadsignFlags()); case e2dEffectProperty::TEXT: case e2dEffectProperty::TEXT_2: case e2dEffectProperty::TEXT_3: case e2dEffectProperty::TEXT_4: - return roadsign.text ? roadsign.text : ""; + return effect->GetRoadsignText(); } break; } case e2dEffectType::ESCALATOR: { - const t2dEffectEscalator& escalator = effect->effect.escalator; - switch (property) { case e2dEffectProperty::BOTTOM_X: - return escalator.bottom.x; + return effect->GetEscalatorBottom().x; case e2dEffectProperty::BOTTOM_Y: - return escalator.bottom.y; + return effect->GetEscalatorBottom().y; case e2dEffectProperty::BOTTOM_Z: - return escalator.bottom.z; + return effect->GetEscalatorBottom().z; case e2dEffectProperty::TOP_X: - return escalator.top.x; + return effect->GetEscalatorTop().x; case e2dEffectProperty::TOP_Y: - return escalator.top.y; + return effect->GetEscalatorTop().y; case e2dEffectProperty::TOP_Z: - return escalator.top.z; + return effect->GetEscalatorTop().z; case e2dEffectProperty::END_X: - return escalator.end.x; + return effect->GetEscalatorEnd().x; case e2dEffectProperty::END_Y: - return escalator.end.y; + return effect->GetEscalatorEnd().y; case e2dEffectProperty::END_Z: - return escalator.end.z; + return effect->GetEscalatorEnd().z; case e2dEffectProperty::DIRECTION: - return static_cast(escalator.direction); + return static_cast(effect->GetEscalatorDirection()); } break; @@ -720,20 +733,20 @@ std::variant CClient2DFXManager::Get2DFXProperty(C2DEf return false; } -void CClient2DFXManager::Set2DFXPosition(C2DEffectSAInterface* effect, const CVector& position) +void CClient2DFXManager::Set2DFXPosition(C2DEffect* effect, const CVector& position) { if (!effect) return; - effect->position = RwV3d{position.fX, position.fY, position.fZ}; + effect->SetPosition(position); } -CVector* CClient2DFXManager::Get2DFXPosition(C2DEffectSAInterface* effect) const +CVector* CClient2DFXManager::Get2DFXPosition(C2DEffect* effect) const { if (!effect) return nullptr; - return reinterpret_cast(&effect->position); + return &effect->GetPosition(); } const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectType, const effectDataMap& effectData) @@ -761,7 +774,7 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp return "Invalid \"shadowSize\" value"; auto* shadowMultiplier = MapFind(effectData, "shadowMultiplier"); - if (!shadowMultiplier || !std::holds_alternative(*shadowMultiplier) || std::get(*shadowMultiplier) < 0) + if (!shadowMultiplier || !std::holds_alternative(*shadowMultiplier) || std::get(*shadowMultiplier) < 0.0f) return "Invalid \"shadowMultiplier\" value"; auto* showMode = MapFind(effectData, "showMode"); @@ -774,11 +787,11 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp return "Invalid \"coronaReflection\" value"; auto* coronaFlareType = MapFind(effectData, "flareType"); - if (!coronaFlareType || !std::holds_alternative(*coronaFlareType) || (std::get(*coronaFlareType) < 0 || std::get(*coronaFlareType) > 1.0f)) + if (!coronaFlareType || !std::holds_alternative(*coronaFlareType) || (std::get(*coronaFlareType) < 0.0f || std::get(*coronaFlareType) > 1.0f)) return "Invalid \"flareType\" value"; auto* flags = MapFind(effectData, "flags"); - if (!flags || !std::holds_alternative(*flags) || std::get(*flags) < 0) + if (!flags || !std::holds_alternative(*flags) || std::get(*flags) < 0.0f) return "Invalid \"flags\" value"; auto* shadowZDistance = MapFind(effectData, "shadowDistance"); @@ -844,7 +857,7 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp return "Invalid \"rotZ\" value"; auto* flags = MapFind(effectData, "flags"); - if (!flags || !std::holds_alternative(*flags) || std::get(*flags) < 0) + if (!flags || !std::holds_alternative(*flags) || std::get(*flags) < 0.0f) return "Invalid \"flags\" value"; auto* text = MapFind(effectData, "text1"); @@ -934,6 +947,51 @@ const char* CClient2DFXManager::IsValidEffectData(const e2dEffectType& effectTyp return nullptr; } +bool CClient2DFXManager::TryDestroyCustom2DFXEffect(std::uint32_t modelID, std::int8_t index) +{ + bool removed = false; + + if (index == -1) + { + for (auto it = m_effectsList.begin(); it != m_effectsList.end();) + { + if ((*it)->GetModelID() == modelID) + { + delete *it; + it = m_effectsList.erase(it); + + removed = true; + } + else + ++it; + } + } + else + { + CModelInfo* modelInfo = g_pGame->GetModelInfo(modelID); + if (!modelInfo) + return false; + + C2DEffect* effect = reinterpret_cast(modelInfo->Get2DFXFromIndex(static_cast(index))); + if (!effect) + return false; + + for (auto* clientEffect : m_effectsList) + { + if (clientEffect->Get2DFX() == effect) + { + RemoveFromList(clientEffect); + delete clientEffect; + + removed = true; + break; + } + } + } + + return removed; +} + bool CClient2DFXManager::IsValidModel(std::uint32_t model) noexcept { return CClientObjectManager::IsValidModel(model) || CClientBuildingManager::IsValidModel(model) || CClientVehicleManager::IsValidModel(model); diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.h b/Client/mods/deathmatch/logic/CClient2DFXManager.h index 5dd9220bcc..12bcbf6012 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.h +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.h @@ -9,7 +9,6 @@ #pragma once #include "CClient2DFX.h" -#include "../game_sa/C2DEffectSAInterface.h" class CClient2DFXManager { @@ -24,14 +23,16 @@ class CClient2DFXManager CClient2DFX* Add2DFX(std::uint32_t model, const CVector& position, const e2dEffectType& type, const effectDataMap& effectData); - bool Set2DFXProperties(C2DEffectSAInterface* effect, const effectDataMap& effectData); - effectDataMap Get2DFXProperties(C2DEffectSAInterface* effect) const; + bool Set2DFXProperties(C2DEffect* effect, const effectDataMap& effectData); + effectDataMap Get2DFXProperties(C2DEffect* effect) const; - bool Set2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property, const std::variant& propertyValue); - std::variant Get2DFXProperty(C2DEffectSAInterface* effect, const e2dEffectProperty& property); + bool Set2DFXProperty(C2DEffect* effect, const e2dEffectProperty& property, const std::variant& propertyValue); + std::variant Get2DFXProperty(C2DEffect* effect, const e2dEffectProperty& property); - void Set2DFXPosition(C2DEffectSAInterface* effect, const CVector& position); - CVector* Get2DFXPosition(C2DEffectSAInterface* effect) const; + void Set2DFXPosition(C2DEffect* effect, const CVector& position); + CVector* Get2DFXPosition(C2DEffect* effect) const; + + bool TryDestroyCustom2DFXEffect(std::uint32_t modelID, std::int8_t index); static bool IsValidModel(std::uint32_t model) noexcept; static const char* IsValidEffectData(const e2dEffectType& effectType, const effectDataMap& effectData); diff --git a/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp index 2288c51ebf..308a7ee795 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLua2DFXDefs.cpp @@ -94,7 +94,7 @@ bool CLua2DFXDefs::RemoveModel2DFX(std::uint32_t modelID, std::optionalGetModelInfo(static_cast(modelID)); + CModelInfo* modelInfo = g_pGame->GetModelInfo(modelID); if (!modelInfo) return false; @@ -105,6 +105,10 @@ bool CLua2DFXDefs::RemoveModel2DFX(std::uint32_t modelID, std::optionalTryDestroyCustom2DFXEffect(modelID, index.value_or(-1))) + return true; + return index.has_value() ? modelInfo->Remove2DFXEffectAtIndex(index.value(), includeDefault.value_or(false)) : modelInfo->RemoveAll2DFXEffects(includeDefault.value_or(false)); } @@ -117,7 +121,10 @@ bool CLua2DFXDefs::ResetModel2DFX(std::uint32_t modelID, std::optional rem if (!modelInfo) return false; - return modelInfo->Reset2DFXEffects(removeCustomEffects.value_or(true)); + if (removeCustomEffects.has_value() && removeCustomEffects.value()) + m_p2DFXManager->TryDestroyCustom2DFXEffect(modelID, -1); + + return modelInfo->Reset2DFXEffects(); } bool CLua2DFXDefs::SetModel2DFXProperties(std::uint32_t modelID, std::uint32_t index, effectDataMap effectData) @@ -137,13 +144,13 @@ bool CLua2DFXDefs::SetModel2DFXProperties(std::uint32_t modelID, std::uint32_t i if (!effect) return false; - const char* error = CClient2DFXManager::IsValidEffectData(effect->type, effectData); + const char* error = CClient2DFXManager::IsValidEffectData(reinterpret_cast(effect)->GetEffectType(), effectData); if (error) throw LuaFunctionError(error); modelInfo->StoreDefault2DFXEffect(effect); - if (!m_p2DFXManager->Set2DFXProperties(effect, effectData)) + if (!m_p2DFXManager->Set2DFXProperties(reinterpret_cast(effect), effectData)) return false; modelInfo->Update2DFXEffect(effect); @@ -186,7 +193,7 @@ bool CLua2DFXDefs::SetModel2DFXProperty(std::uint32_t modelID, std::uint32_t ind modelInfo->StoreDefault2DFXEffect(effect); - if (!m_p2DFXManager->Set2DFXProperty(effect, property, propertyValue)) + if (!m_p2DFXManager->Set2DFXProperty(reinterpret_cast(effect), property, propertyValue)) return false; modelInfo->Update2DFXEffect(effect); @@ -224,13 +231,20 @@ bool CLua2DFXDefs::SetModel2DFXPosition(std::uint32_t modelID, std::uint32_t ind return false; modelInfo->StoreDefault2DFXEffect(effect); - m_p2DFXManager->Set2DFXPosition(effect, position); + m_p2DFXManager->Set2DFXPosition(reinterpret_cast(effect), position); + + modelInfo->Update2DFXEffect(effect); return true; } bool CLua2DFXDefs::Set2DFXPosition(CClient2DFX* effect, CVector position) { + CModelInfo* modelInfo = g_pGame->GetModelInfo(effect->GetModelID()); + if (!modelInfo) + return false; + m_p2DFXManager->Set2DFXPosition(effect->Get2DFX(), position); + modelInfo->Update2DFXEffect(effect->Get2DFX()); return true; } @@ -251,7 +265,7 @@ std::variant> CLua2DFXDefs::GetModel2DF if (!effect) return false; - CVector* position = m_p2DFXManager->Get2DFXPosition(effect); + CVector* position = m_p2DFXManager->Get2DFXPosition(reinterpret_cast(effect)); return std::make_tuple(position->fX, position->fY, position->fZ); } @@ -281,7 +295,7 @@ std::variant CLua2DFXDefs::GetModel2DFXProperties(std::uint if (!effect) return false; - return m_p2DFXManager->Get2DFXProperties(effect); + return m_p2DFXManager->Get2DFXProperties(reinterpret_cast(effect)); } std::variant CLua2DFXDefs::Get2DFXProperties(CClient2DFX* effect) @@ -306,7 +320,7 @@ std::variant CLua2DFXDefs::GetModel2DFXProperty(std::u if (!effect) return false; - return m_p2DFXManager->Get2DFXProperty(effect, property); + return m_p2DFXManager->Get2DFXProperty(reinterpret_cast(effect), property); } std::variant CLua2DFXDefs::Get2DFXProperty(CClient2DFX* effect, e2dEffectProperty property) @@ -319,7 +333,7 @@ std::variant CLua2DFXDefs::GetModel2DFXCount(std::uint32_t if (!CClient2DFXManager::IsValidModel(modelID)) throw std::invalid_argument("Invalid model ID"); - CModelInfo* modelInfo = g_pGame->GetModelInfo(static_cast(modelID)); + CModelInfo* modelInfo = g_pGame->GetModelInfo(modelID); if (!modelInfo) return false; diff --git a/Client/sdk/game/C2DEffect.h b/Client/sdk/game/C2DEffect.h new file mode 100644 index 0000000000..f78614ebaf --- /dev/null +++ b/Client/sdk/game/C2DEffect.h @@ -0,0 +1,95 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/game/C2DEffect.h + * PURPOSE: Game 2dfx interface + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + * + *****************************************************************************/ + +#pragma once +#include "C2DEffects.h" + +struct RwColor; +struct RwV2d; +struct RwV3d; +class CVector; + +class C2DEffect +{ +public: + virtual void SetPosition(const CVector& position) = 0; + virtual CVector& GetPosition() = 0; + + virtual e2dEffectType GetEffectType() = 0; + + // Light properties + // Set + virtual void SetCoronaFarClip(float clip) = 0; + virtual void SetCoronaPointLightRange(float range) = 0; + virtual void SetCoronaSize(float size) = 0; + virtual void SetShadowSize(float size) = 0; + virtual void SetShadowMultiplier(std::uint8_t multiplier) = 0; + virtual void SetCoronaShowMode(e2dCoronaFlashType showMode) = 0; + virtual void SetCoronaReflectionsEnabled(bool enable) = 0; + virtual void SetCoronaFlareType(std::uint8_t flareType) = 0; + virtual void SetLightFlags(std::uint16_t flags) = 0; + virtual void SetShadowDistance(std::int8_t distance) = 0; + virtual void SetCoronaOffsets(const CVector& offsets) = 0; + virtual void SetCoronaColor(const RwColor& color) = 0; + virtual void SetCoronaTexture(const std::string& name) = 0; + virtual void SetShadowTexture(const std::string& name) = 0; + + // Get + virtual float GetCoronaFarClip() const = 0; + virtual float GetCoronaPointLightRange() const = 0; + virtual float GetCoronaSize() const = 0; + virtual float GetShadowSize() const = 0; + virtual std::uint8_t GetShadowMultiplier() const = 0; + virtual e2dCoronaFlashType GetCoronaShowMode() const = 0; + virtual bool GetCoronaReflectionsEnabled() const = 0; + virtual std::uint8_t GetCoronaFlareType() const = 0; + virtual std::uint16_t GetLightFlags() const = 0; + virtual std::int8_t GetShadowDistance() const = 0; + virtual CVector GetCoronaOffsets() const = 0; + virtual RwColor GetCoronaColor() const = 0; + virtual std::string GetCoronaTexture() const = 0; + virtual std::string GetShadowTexture() const = 0; + + // Particle properties + // Set + virtual void SetParticleName(const std::string& name) = 0; + + // Get + virtual std::string GetParticleName() const = 0; + + // Roadsign properties + // Set + virtual void SetRoadsignSize(const RwV2d& size) = 0; + virtual void SetRoadsignRotation(const RwV3d& rotation) = 0; + virtual void SetRoadsignFlags(std::uint8_t flags) = 0; + virtual void SetRoadsignText(const std::string& text, std::uint8_t line) = 0; + + // Get + virtual RwV2d GetRoadsignSize() const = 0; + virtual RwV3d GetRoadsignRotation() const = 0; + virtual std::uint16_t GetRoadsignFlags() const = 0; + virtual std::string GetRoadsignText() const = 0; + + // Escalator properties + // Set + virtual void SetEscalatorBottom(const RwV3d& bottom) = 0; + virtual void SetEscalatorTop(const RwV3d& top) = 0; + virtual void SetEscalatorEnd(const RwV3d& end) = 0; + virtual void SetEscalatorDirection(std::uint8_t direction) = 0; + + // Get + virtual RwV3d GetEscalatorBottom() const = 0; + virtual RwV3d GetEscalatorTop() const = 0; + virtual RwV3d GetEscalatorEnd() const = 0; + virtual std::uint8_t GetEscalatorDirection() const = 0; + +}; diff --git a/Client/sdk/game/C2DEffects.h b/Client/sdk/game/C2DEffects.h new file mode 100644 index 0000000000..f897afc6d1 --- /dev/null +++ b/Client/sdk/game/C2DEffects.h @@ -0,0 +1,125 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/game/C2DEffect.h + * PURPOSE: Game 2dfx interface + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + * + *****************************************************************************/ + +#pragma once + +class C2DEffectSA; +class C2DEffect; +class CVector; +class C2DEffectSAInterface; + +enum class e2dEffectType : std::uint8_t +{ + LIGHT = 0, + PARTICLE, + UNKNOWN, + ATTRACTOR, + SUN_GLARE, + FURNITURE, + ENEX, + ROADSIGN, + TRIGGER_POINT, + COVER_POINT, + ESCALATOR, + + NONE, +}; + +enum class e2dCoronaFlashType : std::uint8_t +{ + DEFAULT = 0, + RANDOM, + RANDOM_WHEN_WET, + ANIM_SPEED_4X, + ANIM_SPEED_2X, + ANIM_SPEED_1X, + WARNLIGHT, // Used on model nt_roadblockci + TRAFFICLIGHT, + TRAINCROSSING, + UNUSED, + ONLY_RAIN, + ON5_OFF5, + ON6_OFF4, + ON4_OFF6, +}; + +enum class e2dEffectProperty +{ + // light properties + FAR_CLIP_DISTANCE = 0, + LIGHT_RANGE, + CORONA_SIZE, + SHADOW_SIZE, + SHADOW_MULT, + FLASH_TYPE, + CORONA_REFLECTION, + FLARE_TYPE, + SHADOW_DISTANCE, + OFFSET_X, + OFFSET_Y, + OFFSET_Z, + COLOR, + CORONA_NAME, + SHADOW_NAME, + FLAGS, // for light & roadsign + + // particle properties + PRT_NAME, + + // roadsign properties + SIZE_X, + SIZE_Y, + ROT_X, + ROT_Y, + ROT_Z, + TEXT, + TEXT_2, + TEXT_3, + TEXT_4, + + // escalator properties + BOTTOM_X, + BOTTOM_Y, + BOTTOM_Z, + TOP_X, + TOP_Y, + TOP_Z, + END_X, + END_Y, + END_Z, + DIRECTION, +}; + +enum class e2dAttractorType : std::int8_t +{ + UNDEFINED = -1, + ATM = 0, + SEAT, + STOP, + PIZZA, + SHELTER, + TRIGGER_SCRIPT, + LOOK_AT, + SCRIPTED, + PARK, + STEP, +}; + +class C2DEffects +{ +public: + virtual C2DEffect* Create(std::uint32_t model, const CVector& position, const e2dEffectType& type) = 0; + virtual void Destroy(C2DEffect* effect) = 0; + virtual C2DEffectSA* Get(C2DEffectSAInterface* effectInterface) const = 0; + virtual void AddToList(C2DEffectSA* effect) = 0; + virtual void RemoveFromList(C2DEffectSA* effect) = 0; +}; diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index 412f68841d..dce44502ff 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -69,6 +69,7 @@ class CWorld; class CIplStore; class CBuildingRemoval; class CRenderer; +class C2DEffects; enum eEntityType; enum ePedPieceTypes; @@ -151,6 +152,7 @@ class __declspec(novtable) CGame virtual CColStore* GetCollisionStore() = 0; virtual CBuildingRemoval* GetBuildingRemoval() = 0; virtual CRenderer* GetRenderer() const noexcept = 0; + virtual C2DEffects* Get2DEffects() const noexcept = 0; virtual CWeaponInfo* GetWeaponInfo(eWeaponType weapon, eWeaponSkill skill = WEAPONSKILL_STD) = 0; virtual CModelInfo* GetModelInfo(DWORD dwModelID, bool bCanBeInvalid = false) = 0; diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index a64f95d5ad..e998251291 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -14,13 +14,13 @@ #include #include "CAnimBlock.h" #include "Common.h" +#include "C2DEffect.h" class CBaseModelInfoSAInterface; class CColModel; class CPedModelInfo; struct RpClump; struct RwObject; -class C2DEffectSAInterface; class CBoundingBox { @@ -85,103 +85,6 @@ enum eVehicleUpgradePosn VEHICLE_UPGRADE_POSN_MISC, }; -enum class e2dEffectType : std::uint8_t -{ - LIGHT = 0, - PARTICLE, - UNKNOWN, - ATTRACTOR, - SUN_GLARE, - FURNITURE, - ENEX, - ROADSIGN, - TRIGGER_POINT, - COVER_POINT, - ESCALATOR, - - NONE, -}; - -enum class e2dCoronaFlashType : std::uint8_t -{ - DEFAULT = 0, - RANDOM, - RANDOM_WHEN_WET, - ANIM_SPEED_4X, - ANIM_SPEED_2X, - ANIM_SPEED_1X, - WARNLIGHT, // Used on model nt_roadblockci - TRAFFICLIGHT, - TRAINCROSSING, - UNUSED, - ONLY_RAIN, - ON5_OFF5, - ON6_OFF4, - ON4_OFF6, -}; - -enum class e2dEffectProperty -{ - // light properties - FAR_CLIP_DISTANCE = 0, - LIGHT_RANGE, - CORONA_SIZE, - SHADOW_SIZE, - SHADOW_MULT, - FLASH_TYPE, - CORONA_REFLECTION, - FLARE_TYPE, - SHADOW_DISTANCE, - OFFSET_X, - OFFSET_Y, - OFFSET_Z, - COLOR, - CORONA_NAME, - SHADOW_NAME, - FLAGS, // for light & roadsign - - // particle properties - PRT_NAME, - - // roadsign properties - SIZE_X, - SIZE_Y, - ROT_X, - ROT_Y, - ROT_Z, - TEXT, - TEXT_2, - TEXT_3, - TEXT_4, - - // escalator properties - BOTTOM_X, - BOTTOM_Y, - BOTTOM_Z, - TOP_X, - TOP_Y, - TOP_Z, - END_X, - END_Y, - END_Z, - DIRECTION, -}; - -enum class e2dAttractorType : std::int8_t -{ - UNDEFINED = -1, - ATM = 0, - SEAT, - STOP, - PIZZA, - SHELTER, - TRIGGER_SCRIPT, - LOOK_AT, - SCRIPTED, - PARK, - STEP, -}; - struct SVehicleSupportedUpgrades { SVehicleSupportedUpgrades() { Reset(); } @@ -351,13 +254,15 @@ class CModelInfo virtual bool Remove2DFXEffectAtIndex(std::uint32_t index, bool includeDefault = false) = 0; virtual bool RemoveAll2DFXEffects(bool includeDefault = false) = 0; - virtual C2DEffectSAInterface* Get2DFXFromIndex(std::uint32_t index) = 0; + virtual C2DEffectSA* Get2DFXFromIndex(std::uint32_t index) = 0; virtual std::uint32_t Get2DFXCount() const = 0; - virtual void Update2DFXEffect(C2DEffectSAInterface* effect) = 0; + virtual void Update2DFXEffect(C2DEffectSA* effect) = 0; + virtual void Update2DFXEffect(C2DEffect* effect) = 0; + virtual void StoreDefault2DFXEffect(C2DEffectSA* effect) = 0; virtual void StoreDefault2DFXEffect(C2DEffectSAInterface* effect) = 0; - virtual bool Reset2DFXEffects(bool removeCustomEffects = false) = 0; + virtual bool Reset2DFXEffects() = 0; virtual void CopyModified2DFXEffects() = 0; virtual void RestoreModified2DFXEffects() = 0; From 942c4f9c21d16431832d4451e8243b2307b96dc8 Mon Sep 17 00:00:00 2001 From: FileEX Date: Wed, 2 Oct 2024 22:38:50 +0200 Subject: [PATCH 16/17] Roadsign support --- Client/game_sa/C2DEffectSA.cpp | 145 +++++++++++++++--- Client/game_sa/C2DEffectSA.h | 21 ++- Client/game_sa/CModelInfoSA.cpp | 30 +++- Client/game_sa/gamesa_renderware.h | 3 +- Client/game_sa/gamesa_renderware.hpp | 1 + .../deathmatch/logic/CClient2DFXManager.cpp | 49 +++--- Client/multiplayer_sa/CMultiplayerSA.cpp | 4 +- Client/sdk/game/C2DEffect.h | 12 +- 8 files changed, 208 insertions(+), 57 deletions(-) diff --git a/Client/game_sa/C2DEffectSA.cpp b/Client/game_sa/C2DEffectSA.cpp index 982dbfb679..7d838e7eaf 100644 --- a/Client/game_sa/C2DEffectSA.cpp +++ b/Client/game_sa/C2DEffectSA.cpp @@ -198,7 +198,10 @@ void C2DEffectSA::SetRoadsignText(const std::string& text, std::uint8_t line) if (IsValidRoadsign()) { if (!m_effectInterface->effect.roadsign.text) + { m_effectInterface->effect.roadsign.text = static_cast(std::malloc(64)); + MemSetFast(m_effectInterface->effect.roadsign.text, 0, 64); + } if (!m_effectInterface->effect.roadsign.text) return; @@ -207,6 +210,32 @@ void C2DEffectSA::SetRoadsignText(const std::string& text, std::uint8_t line) } } +RwV2d& C2DEffectSA::GetRoadsignSize() +{ + if (IsValidRoadsign()) + return m_effectInterface->effect.roadsign.size; + + static RwV2d dummySize{0, 0}; + return dummySize; +} + +RwV3d& C2DEffectSA::GetRoadsignRotation() +{ + if (IsValidRoadsign()) + return m_effectInterface->effect.roadsign.rotation; + + static RwV3d dummyRotation{0, 0, 0}; + return dummyRotation; +} + +std::string C2DEffectSA::GetRoadsignText() const +{ + if (IsValidRoadsign() && m_effectInterface->effect.roadsign.text) + return std::string(m_effectInterface->effect.roadsign.text, 64); + + return ""; +} + void C2DEffectSA::SetEscalatorBottom(const RwV3d& bottom) { if (IsValidEscalator()) @@ -231,6 +260,99 @@ void C2DEffectSA::SetEscalatorDirection(std::uint8_t direction) m_effectInterface->effect.escalator.direction = direction; } +RwV3d& C2DEffectSA::GetEscalatorBottom() +{ + if (IsValidEscalator()) + return m_effectInterface->effect.escalator.bottom; + + static RwV3d dummyBottom{0, 0, 0}; + return dummyBottom; +} + +RwV3d& C2DEffectSA::GetEscalatorTop() +{ + if (IsValidEscalator()) + return m_effectInterface->effect.escalator.top; + + static RwV3d dummyTop{0, 0, 0}; + return dummyTop; +} + +RwV3d& C2DEffectSA::GetEscalatorEnd() +{ + if (IsValidEscalator()) + return m_effectInterface->effect.escalator.end; + + static RwV3d dummyEnd{0, 0, 0}; + return dummyEnd; +} + +RpAtomic* C2DEffectSA::Roadsign_CreateAtomic(const RwV3d& position, const RwV3d& rotation, float sizeX, float sizeY, std::uint32_t numLines, char* line1, char* line2, char* line3, char* line4, std::uint32_t numLetters, std::uint8_t palleteID) +{ + // Call CCustomRoadsignMgr::CreateRoadsignAtomic + RpAtomic* atomic = ((RpAtomic*(__cdecl*)(float, float, std::int32_t, char*, char*, char*, char*, std::int32_t, std::uint8_t))0x6FF2D0)(sizeX, sizeY, numLines, line1, line2, line3, line4, numLetters, palleteID); + RwFrame* frame = RpAtomicGetFrame(atomic); + RwFrameSetIdentity(frame); + + const RwV3d axis0{1.0f, 0.0f, 0.0f}, axis1{0.0f, 1.0f, 0.0f}, axis2{0.0f, 0.0f, 1.0f}; + RwFrameRotate(frame, &axis2, rotation.z, rwCOMBINEREPLACE); + RwFrameRotate(frame, &axis0, rotation.x, rwCOMBINEPOSTCONCAT); + RwFrameRotate(frame, &axis1, rotation.y, rwCOMBINEPOSTCONCAT); + + RwFrameTranslate(frame, &position, TRANSFORM_AFTER); + RwFrameUpdateObjects(frame); + + return atomic; +} + +std::uint32_t C2DEffectSA::Roadsign_GetPalleteIDFromFlags(std::uint8_t flags) +{ + std::uint32_t id = (flags >> 4) & 3; + return id <= 3 ? id : 0; +} + +std::uint32_t C2DEffectSA::Roadsign_GetNumLettersFromFlags(std::uint8_t flags) +{ + std::uint32_t letters = (flags >> 2) & 3; + switch (letters) + { + case 1u: + return 2; + case 2u: + return 4; + case 3u: + return 8; + default: + return 16; + } +} + +std::uint32_t C2DEffectSA::Roadsign_GetNumLinesFromFlags(std::uint8_t flags) +{ + std::uint32_t lines = flags & 3; + return (lines <= 3 && lines > 0) ? lines : 4; +} + +void C2DEffectSA::Roadsign_DestroyAtomic(C2DEffectSAInterface* effect) +{ + if (!effect) + return; + + t2dEffectRoadsign& roadsign = effect->effect.roadsign; + if (roadsign.atomic) + { + RwFrame* frame = RpAtomicGetFrame(roadsign.atomic); + if (frame) + { + RpAtomicSetFrame(roadsign.atomic, nullptr); + RwFrameDestroy(frame); + } + + RpAtomicDestroy(roadsign.atomic); + roadsign.atomic = nullptr; + } +} + C2DEffectSAInterface* C2DEffectSA::CreateCopy(C2DEffectSAInterface* effect) { C2DEffectSAInterface* copy = new C2DEffectSAInterface(); @@ -250,17 +372,16 @@ C2DEffectSAInterface* C2DEffectSA::CreateCopy(C2DEffectSAInterface* effect) copy->effect.roadsign.text = static_cast(std::malloc(64)); if (copy->effect.roadsign.text) { - std::memset(copy->effect.roadsign.text, 0, 64); - std::strncpy(copy->effect.roadsign.text, effect->effect.roadsign.text, 64); - } + MemSetFast(copy->effect.roadsign.text, 0, 64); + MemCpyFast(copy->effect.roadsign.text, effect->effect.roadsign.text, 64); - copy->effect.roadsign.atomic = RpAtomicClone(effect->effect.roadsign.atomic); + copy->effect.roadsign.atomic = Roadsign_CreateAtomic(copy->position, copy->effect.roadsign.rotation, copy->effect.roadsign.size.x, copy->effect.roadsign.size.y, Roadsign_GetNumLinesFromFlags(copy->effect.roadsign.flags), ©->effect.roadsign.text[0], ©->effect.roadsign.text[16], ©->effect.roadsign.text[32], ©->effect.roadsign.text[48], Roadsign_GetNumLettersFromFlags(copy->effect.roadsign.flags), Roadsign_GetPalleteIDFromFlags(copy->effect.roadsign.flags)); + } } return copy; } -// C2DEffect::Shutdown causes random unknown crash in ntdll.dll so we need own function void C2DEffectSA::Shutdown(C2DEffectSAInterface* effect) { if (!effect) @@ -269,25 +390,13 @@ void C2DEffectSA::Shutdown(C2DEffectSAInterface* effect) if (effect->type == e2dEffectType::ROADSIGN) { t2dEffectRoadsign& roadsign = effect->effect.roadsign; + Roadsign_DestroyAtomic(effect); if (roadsign.text) { std::free(roadsign.text); roadsign.text = nullptr; } - - if (roadsign.atomic) - { - RwFrame* frame = RpAtomicGetFrame(roadsign.atomic); - if (frame) - { - RpAtomicSetFrame(roadsign.atomic, nullptr); - RwFrameDestroy(frame); - } - - RpAtomicDestroy(roadsign.atomic); - roadsign.atomic = nullptr; - } } else if (effect->type == e2dEffectType::LIGHT) { diff --git a/Client/game_sa/C2DEffectSA.h b/Client/game_sa/C2DEffectSA.h index f41f15e834..79a0bd2d37 100644 --- a/Client/game_sa/C2DEffectSA.h +++ b/Client/game_sa/C2DEffectSA.h @@ -41,6 +41,7 @@ class C2DEffectSA : public C2DEffect ~C2DEffectSA() = default; C2DEffectSAInterface* GetInterface() noexcept { return m_effectInterface; } + e2dEffectType GetEffectType() override { return m_effectInterface ? m_effectInterface->type : e2dEffectType::NONE; } bool IsValidLight() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::LIGHT; }; bool IsValidRoadsign() const noexcept { return m_effectInterface && m_effectInterface->type == e2dEffectType::ROADSIGN; } @@ -80,7 +81,7 @@ class C2DEffectSA : public C2DEffect std::uint8_t GetCoronaFlareType() const override { return IsValidLight() ? m_effectInterface->effect.light.coronaFlareType : 0; } std::uint16_t GetLightFlags() const override { return IsValidLight() ? m_effectInterface->effect.light.flags : 0; } std::int8_t GetShadowDistance() const override { return IsValidLight() ? m_effectInterface->effect.light.shadowZDistance : 0; } - CVector GetCoronaOffsets() const override; + CVector GetCoronaOffsets() const override; RwColor GetCoronaColor() const override { return IsValidLight() ? m_effectInterface->effect.light.color : RwColor{0,0,0,0}; } std::string GetCoronaTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.coronaTex ? m_effectInterface->effect.light.coronaTex->name : "") : ""; } std::string GetShadowTexture() const override { return IsValidLight() ? (m_effectInterface->effect.light.shadowTex ? m_effectInterface->effect.light.shadowTex->name : "") : ""; } @@ -100,10 +101,10 @@ class C2DEffectSA : public C2DEffect void SetRoadsignText(const std::string& text, std::uint8_t line) override; // Get - RwV2d GetRoadsignSize() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.size : RwV2d{0,0}; } - RwV3d GetRoadsignRotation() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.rotation : RwV3d{0,0,0}; } + RwV2d& GetRoadsignSize() override; + RwV3d& GetRoadsignRotation() override; std::uint16_t GetRoadsignFlags() const override { return IsValidRoadsign() ? m_effectInterface->effect.roadsign.flags : 0; } - std::string GetRoadsignText() const override { return IsValidRoadsign() ? (m_effectInterface->effect.roadsign.text ? std::string(m_effectInterface->effect.roadsign.text, strnlen(m_effectInterface->effect.roadsign.text, 64)) : "") : ""; } + std::string GetRoadsignText() const override; // Escalator properties // Set @@ -113,12 +114,16 @@ class C2DEffectSA : public C2DEffect void SetEscalatorDirection(std::uint8_t direction) override; // Get - RwV3d GetEscalatorBottom() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.bottom : RwV3d{0, 0, 0}; } - RwV3d GetEscalatorTop() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.top : RwV3d{0, 0, 0}; } - RwV3d GetEscalatorEnd() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.end : RwV3d{0, 0, 0}; } + RwV3d& GetEscalatorBottom() override; + RwV3d& GetEscalatorTop() override; + RwV3d& GetEscalatorEnd() override; std::uint8_t GetEscalatorDirection() const override { return IsValidEscalator() ? m_effectInterface->effect.escalator.direction : 0; } - e2dEffectType GetEffectType() override { return m_effectInterface ? m_effectInterface->type : e2dEffectType::NONE; } + static RpAtomic* Roadsign_CreateAtomic(const RwV3d& position, const RwV3d& rotation, float sizeX, float sizeY, std::uint32_t numLines, char* line1, char* line2, char* line3, char* line4, std::uint32_t numLetters, std::uint8_t palleteID); + static std::uint32_t Roadsign_GetPalleteIDFromFlags(std::uint8_t flags); + static std::uint32_t Roadsign_GetNumLettersFromFlags(std::uint8_t flags); + static std::uint32_t Roadsign_GetNumLinesFromFlags(std::uint8_t flags); + static void Roadsign_DestroyAtomic(C2DEffectSAInterface* effect); static C2DEffectSAInterface* CreateCopy(C2DEffectSAInterface* effect); diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index b35cbe4425..e7a23908e4 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -2157,6 +2157,20 @@ void CModelInfoSA::Update2DFXEffect(C2DEffectSA* effect) // Call Fx_c::CreateEntityFx ((void(__thiscall*)(CFxSAInterface*, CEntitySAInterface*, const char*, RwV3d*, RwMatrix*))FUNC_Fx_c_CreateEntityFx)(fx, entity, effectInterface->effect.particle.szName, &effectInterface->position, matrixTransform); } + + break; + } + case e2dEffectType::ROADSIGN: + { + t2dEffectRoadsign& roadsign = effectInterface->effect.roadsign; + C2DEffectSA::Roadsign_DestroyAtomic(effectInterface); + + std::uint32_t numLines = C2DEffectSA::Roadsign_GetNumLinesFromFlags(roadsign.flags); + std::uint32_t numLetters = C2DEffectSA::Roadsign_GetNumLettersFromFlags(roadsign.flags); + std::uint8_t palleteID = C2DEffectSA::Roadsign_GetPalleteIDFromFlags(roadsign.flags); + roadsign.atomic = C2DEffectSA::Roadsign_CreateAtomic(effectInterface->position, roadsign.rotation, roadsign.size.x, roadsign.size.y, numLines, &roadsign.text[0], &roadsign.text[16], &roadsign.text[32], &roadsign.text[48], numLetters, palleteID); + + break; } } } @@ -2301,6 +2315,7 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) // Prevent creation when stream in but keep in memory so we can restore it later effect->type = e2dEffectType::NONE; + break; } case e2dEffectType::SUN_GLARE: { @@ -2318,8 +2333,11 @@ bool CModelInfoSA::Remove2DFX(C2DEffectSAInterface* effect, bool includeDefault) MapGet(ms_NumOfCustom2DFXEffects, m_pInterface)--; ms_Custom2DFXEffects.erase(it); - delete effect; - effect = nullptr; + if (effect) + { + delete effect; + effect = nullptr; + } } } @@ -2432,8 +2450,14 @@ void CModelInfoSA::RestoreModified2DFXEffects() if (tempVec && (*tempVec)[i]) { MemCpyFast(effectInterface, (*tempVec)[i], sizeof(C2DEffectSAInterface)); - C2DEffectSA::SafeDelete2DFXEffect((*tempVec)[i]); + effectInterface->effect.roadsign.text = static_cast(std::malloc(64)); + if (effectInterface->effect.roadsign.text) + { + MemSetFast(effectInterface->effect.roadsign.text, 0, 64); + MemCpyFast(effectInterface->effect.roadsign.text, (*tempVec)[i]->effect.roadsign.text, 64); + } + C2DEffectSA::SafeDelete2DFXEffect((*tempVec)[i]); Update2DFXEffect(effect); } } diff --git a/Client/game_sa/gamesa_renderware.h b/Client/game_sa/gamesa_renderware.h index 03bffd749b..48a1b2417c 100644 --- a/Client/game_sa/gamesa_renderware.h +++ b/Client/game_sa/gamesa_renderware.h @@ -106,6 +106,7 @@ typedef int(__cdecl* RpHAnimIDGetIndex_t)(RpHAnimHierarchy*, int); typedef RwMatrix*(__cdecl* RpHAnimHierarchyGetMatrixArray_t)(RpHAnimHierarchy*); typedef RtQuat*(__cdecl* RtQuatRotate_t)(RtQuat* quat, const RwV3d* axis, float angle, RwOpCombineType combineOp); typedef RwTexture*(__cdecl* RwReadTexture_t)(const char* name, const char* mask); +typedef RwFrame*(__cdecl* RwFrameRotate_t)(RwFrame* frame, const RwV3d* axis, float angle, RwOpCombineType combine); /*****************************************************************************/ /** Renderware function mappings **/ @@ -197,7 +198,7 @@ RWFUNC(RpHAnimIDGetIndex_t RpHAnimIDGetIndex, (RpHAnimIDGetIndex_t)0xDEAD) RWFUNC(RpHAnimHierarchyGetMatrixArray_t RpHAnimHierarchyGetMatrixArray, (RpHAnimHierarchyGetMatrixArray_t)0xDEAD) RWFUNC(RtQuatRotate_t RtQuatRotate, (RtQuatRotate_t)0xDEAD) RWFUNC(RwReadTexture_t RwReadTexture, reinterpret_cast(0xDEAD)) - +RWFUNC(RwFrameRotate_t RwFrameRotate, reinterpret_cast(0xDEAD)) /*****************************************************************************/ /** GTA function definitions and mappings **/ /*****************************************************************************/ diff --git a/Client/game_sa/gamesa_renderware.hpp b/Client/game_sa/gamesa_renderware.hpp index edabf9a712..a8633cc2ab 100644 --- a/Client/game_sa/gamesa_renderware.hpp +++ b/Client/game_sa/gamesa_renderware.hpp @@ -90,6 +90,7 @@ void InitRwFunctions() RpHAnimHierarchyGetMatrixArray = (RpHAnimHierarchyGetMatrixArray_t)0x7C5120; RtQuatRotate = (RtQuatRotate_t)0x7EB7C0; RwReadTexture = reinterpret_cast(0x7F3AC0); + RwFrameRotate = reinterpret_cast(0x7F1010); SetTextureDict = (SetTextureDict_t)0x007319C0; LoadClumpFile = (LoadClumpFile_t)0x005371F0; diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index 6fae3e8032..4a17c7265e 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -108,9 +108,11 @@ effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffect* effect) const MapSet(properties, "flareType", static_cast(effect->GetCoronaFlareType())); MapSet(properties, "flags", static_cast(effect->GetLightFlags())); MapSet(properties, "shadowDistance", static_cast(effect->GetShadowDistance())); - MapSet(properties, "offsetX", effect->GetCoronaOffsets().fX); - MapSet(properties, "offsetY", effect->GetCoronaOffsets().fY); - MapSet(properties, "offsetZ", effect->GetCoronaOffsets().fZ); + + const CVector& offsets = effect->GetCoronaOffsets(); + MapSet(properties, "offsetX", offsets.fX); + MapSet(properties, "offsetY", offsets.fY); + MapSet(properties, "offsetZ", offsets.fZ); RwColor& color = effect->GetCoronaColor(); int colorValue = (static_cast(color.a) << 24) | (static_cast(color.r) << 16) | (static_cast(color.g) << 8) | static_cast(color.b); @@ -128,11 +130,14 @@ effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffect* effect) const } case e2dEffectType::ROADSIGN: { - MapSet(properties, "sizeX", effect->GetRoadsignSize().x); - MapSet(properties, "sizeY", effect->GetRoadsignSize().y); - MapSet(properties, "rotX", effect->GetRoadsignRotation().x); - MapSet(properties, "rotY", effect->GetRoadsignRotation().y); - MapSet(properties, "rotZ", effect->GetRoadsignRotation().z); + const RwV2d& size = effect->GetRoadsignSize(); + const RwV3d& rot = effect->GetRoadsignRotation(); + + MapSet(properties, "sizeX", size.x); + MapSet(properties, "sizeY", size.y); + MapSet(properties, "rotX", rot.x); + MapSet(properties, "rotY", rot.y); + MapSet(properties, "rotZ", rot.z); MapSet(properties, "flags", static_cast(effect->GetRoadsignFlags())); MapSet(properties, "text", effect->GetRoadsignText()); @@ -140,15 +145,19 @@ effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffect* effect) const } case e2dEffectType::ESCALATOR: { - MapSet(properties, "bottomX", effect->GetEscalatorBottom().x); - MapSet(properties, "bottomY", effect->GetEscalatorBottom().y); - MapSet(properties, "bottomZ", effect->GetEscalatorBottom().z); - MapSet(properties, "topX", effect->GetEscalatorTop().x); - MapSet(properties, "topY", effect->GetEscalatorTop().y); - MapSet(properties, "topZ", effect->GetEscalatorTop().z); - MapSet(properties, "endX", effect->GetEscalatorEnd().x); - MapSet(properties, "endY", effect->GetEscalatorEnd().y); - MapSet(properties, "endZ", effect->GetEscalatorEnd().z); + const RwV3d& bottom = effect->GetEscalatorBottom(); + const RwV3d& top = effect->GetEscalatorTop(); + const RwV3d& end = effect->GetEscalatorEnd(); + + MapSet(properties, "bottomX", bottom.x); + MapSet(properties, "bottomY", bottom.y); + MapSet(properties, "bottomZ", bottom.z); + MapSet(properties, "topX", top.x); + MapSet(properties, "topY", top.y); + MapSet(properties, "topZ", top.z); + MapSet(properties, "endX", end.x); + MapSet(properties, "endY", end.y); + MapSet(properties, "endZ", end.z); MapSet(properties, "direction", static_cast(effect->GetEscalatorDirection())); break; @@ -270,7 +279,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffect* effect, const e2dEffectPrope { if (std::holds_alternative(propertyValue)) { - CVector& offsets = effect->GetCoronaOffsets(); + CVector offsets = effect->GetCoronaOffsets(); offsets.fX = static_cast(std::get(propertyValue)); effect->SetCoronaOffsets(offsets); @@ -282,7 +291,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffect* effect, const e2dEffectPrope { if (std::holds_alternative(propertyValue)) { - CVector& offsets = effect->GetCoronaOffsets(); + CVector offsets = effect->GetCoronaOffsets(); offsets.fY = static_cast(std::get(propertyValue)); effect->SetCoronaOffsets(offsets); @@ -294,7 +303,7 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffect* effect, const e2dEffectPrope { if (std::holds_alternative(propertyValue)) { - CVector& offsets = effect->GetCoronaOffsets(); + CVector offsets = effect->GetCoronaOffsets(); offsets.fZ = static_cast(std::get(propertyValue)); effect->SetCoronaOffsets(offsets); diff --git a/Client/multiplayer_sa/CMultiplayerSA.cpp b/Client/multiplayer_sa/CMultiplayerSA.cpp index 28d5b1f551..63e4429a44 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA.cpp @@ -3018,8 +3018,10 @@ void _declspec(naked) HOOK_CCustomRoadsignMgr__RenderRoadsignAtomic() cmp esi, 0 jz no_render - // original code + // original code with our null check mov eax, dword ptr[esi+4] + test eax, eax + jz no_render fsub [eax+64] mov edx, HOOKPOS_CCustomRoadsignMgr__RenderRoadsignAtomic add edx, 6 diff --git a/Client/sdk/game/C2DEffect.h b/Client/sdk/game/C2DEffect.h index f78614ebaf..c384c198e2 100644 --- a/Client/sdk/game/C2DEffect.h +++ b/Client/sdk/game/C2DEffect.h @@ -54,7 +54,7 @@ class C2DEffect virtual std::uint8_t GetCoronaFlareType() const = 0; virtual std::uint16_t GetLightFlags() const = 0; virtual std::int8_t GetShadowDistance() const = 0; - virtual CVector GetCoronaOffsets() const = 0; + virtual CVector GetCoronaOffsets() const = 0; virtual RwColor GetCoronaColor() const = 0; virtual std::string GetCoronaTexture() const = 0; virtual std::string GetShadowTexture() const = 0; @@ -74,8 +74,8 @@ class C2DEffect virtual void SetRoadsignText(const std::string& text, std::uint8_t line) = 0; // Get - virtual RwV2d GetRoadsignSize() const = 0; - virtual RwV3d GetRoadsignRotation() const = 0; + virtual RwV2d& GetRoadsignSize() = 0; + virtual RwV3d& GetRoadsignRotation() = 0; virtual std::uint16_t GetRoadsignFlags() const = 0; virtual std::string GetRoadsignText() const = 0; @@ -87,9 +87,9 @@ class C2DEffect virtual void SetEscalatorDirection(std::uint8_t direction) = 0; // Get - virtual RwV3d GetEscalatorBottom() const = 0; - virtual RwV3d GetEscalatorTop() const = 0; - virtual RwV3d GetEscalatorEnd() const = 0; + virtual RwV3d& GetEscalatorBottom() = 0; + virtual RwV3d& GetEscalatorTop() = 0; + virtual RwV3d& GetEscalatorEnd() = 0; virtual std::uint8_t GetEscalatorDirection() const = 0; }; From ee4593b1a89d36f2a907776323d695f00bdf3d09 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 5 Oct 2024 19:53:03 +0200 Subject: [PATCH 17/17] Review --- Client/game_sa/C2DEffectSA.cpp | 2 +- Client/game_sa/C2DEffectSA.h | 2 +- Client/mods/deathmatch/logic/CClient2DFXManager.cpp | 10 ---------- Client/sdk/game/C2DEffect.h | 2 +- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Client/game_sa/C2DEffectSA.cpp b/Client/game_sa/C2DEffectSA.cpp index 7d838e7eaf..184764902c 100644 --- a/Client/game_sa/C2DEffectSA.cpp +++ b/Client/game_sa/C2DEffectSA.cpp @@ -35,7 +35,7 @@ void C2DEffectSA::SetPosition(const CVector& position) m_effectInterface->position = RwV3d{position.fX, position.fY, position.fZ}; } -CVector& C2DEffectSA::GetPosition() +CVector& C2DEffectSA::GetPosition() const { if (m_effectInterface) return CVector(m_effectInterface->position.x, m_effectInterface->position.y, m_effectInterface->position.z); diff --git a/Client/game_sa/C2DEffectSA.h b/Client/game_sa/C2DEffectSA.h index 79a0bd2d37..bb7b43980e 100644 --- a/Client/game_sa/C2DEffectSA.h +++ b/Client/game_sa/C2DEffectSA.h @@ -51,7 +51,7 @@ class C2DEffectSA : public C2DEffect void Destroy() const; void SetPosition(const CVector& position) override; - CVector& GetPosition() override; + CVector& GetPosition() const override; // Light properties // Set diff --git a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp index 4a17c7265e..eb662ef4f6 100644 --- a/Client/mods/deathmatch/logic/CClient2DFXManager.cpp +++ b/Client/mods/deathmatch/logic/CClient2DFXManager.cpp @@ -162,8 +162,6 @@ effectDataMap CClient2DFXManager::Get2DFXProperties(C2DEffect* effect) const break; } - default: - break; } return properties; @@ -352,8 +350,6 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffect* effect, const e2dEffectPrope break; } - default: - break; } break; @@ -485,8 +481,6 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffect* effect, const e2dEffectPrope break; } - default: - break; } break; } @@ -616,8 +610,6 @@ bool CClient2DFXManager::Set2DFXProperty(C2DEffect* effect, const e2dEffectPrope break; } - default: - break; } return false; @@ -735,8 +727,6 @@ std::variant CClient2DFXManager::Get2DFXProperty(C2DEf break; } - default: - break; } return false; diff --git a/Client/sdk/game/C2DEffect.h b/Client/sdk/game/C2DEffect.h index c384c198e2..788580893f 100644 --- a/Client/sdk/game/C2DEffect.h +++ b/Client/sdk/game/C2DEffect.h @@ -22,7 +22,7 @@ class C2DEffect { public: virtual void SetPosition(const CVector& position) = 0; - virtual CVector& GetPosition() = 0; + virtual CVector& GetPosition() const = 0; virtual e2dEffectType GetEffectType() = 0;