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;