diff --git a/src/ModelExtrasAPI.cpp b/src/ModelExtrasAPI.cpp index c56242ec..cd5bb46e 100755 --- a/src/ModelExtrasAPI.cpp +++ b/src/ModelExtrasAPI.cpp @@ -5,9 +5,11 @@ #include "utils/datamgr.h" #include "utils/modelinfomgr.h" #include "utils/car.h" +#include "utils/render.h" #include "features/sirens.h" #include "features/lights/data.h" #include "features/lights/manager.h" +#include "features/lights/components/headlight.h" #include "features/plate.h" #include "features/carcols.h" #include "features/dirtfx.h" @@ -21,9 +23,26 @@ #include "features/exhausts.h" #include "features/backfire.h" #include "features/pedcols.h" +#include "features/soundeffects.h" +#include "features/slidedoor.h" +#include "features/rotatedoor.h" +#include "features/wheel.h" +#include "features/wheelhub.h" +#include "features/chain.h" +#include "features/clock.h" +#include "enums/lightingmode.h" +#include "utils/audiomgr.h" #include #include +namespace { +static void SafeStrCopy(char *dst, int maxLen, const std::string &src) { + if (dst && maxLen > 0) { + strncpy_s(dst, maxLen, src.c_str(), _TRUNCATE); + } +} +} + extern "C" { // Core & Lifecycle @@ -39,6 +58,14 @@ bool ME_IsFeatureAvail(ME_FeatureID featureId) { return ModelExtras::m_bEnabledFeatures.test(idx); } +bool ME_IsFeatureEnabled(ME_FeatureID featureId) { + auto idx = static_cast(featureId); + if (idx < 0 || idx >= static_cast(eFeatureMatrix::FeatureCount)) { + return false; + } + return CBaseFeature::IsEnabled(static_cast(idx)); +} + void ME_Reload() { ModelExtras::Reload(); } @@ -108,6 +135,126 @@ void ME_SetLightOverride(CVehicle *pVeh, int overrideMode) { } } +bool ME_GetFogLights(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = LightManager::m_VehData.Get(pVeh); + return data.bFogLightsOn; +} + +void ME_SetFogLights(CVehicle *pVeh, bool on) { + if (!pVeh) return; + auto &data = LightManager::m_VehData.Get(pVeh); + data.bFogLightsOn = on; + if (on) { + ModelInfoMgr::EnableMaterial(pVeh, eMaterialType::FogLightLeft); + ModelInfoMgr::EnableMaterial(pVeh, eMaterialType::FogLightRight); + } +} + +bool ME_GetHighBeam(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = LightManager::m_VehData.Get(pVeh); + return data.bLongLightsOn; +} + +void ME_SetHighBeam(CVehicle *pVeh, bool on) { + if (!pVeh) return; + auto &data = LightManager::m_VehData.Get(pVeh); + data.bLongLightsOn = on; + data.fHighBeamFactor = on ? 1.0f : 0.0f; +} + +float ME_GetLightInertia(CVehicle *pVeh, ME_LightID lightId) { + if (!pVeh) return 0.0f; + auto matType = static_cast(lightId); + if (matType < 0 || matType >= eMaterialType::TotalMaterial) return 0.0f; + auto &data = LightManager::m_VehData.Get(pVeh); + return LightManager::GetLightInertia(pVeh, data, matType); +} + +bool ME_GetLightColor(CVehicle *pVeh, ME_LightID lightId, ME_Color *onColor, ME_Color *offColor) { + if (!pVeh) return false; + auto matType = static_cast(lightId); + if (matType < 0 || matType >= eMaterialType::TotalMaterial) return false; + MatStateColor col = LightManager::GetMaterialColor(pVeh, matType); + if (onColor) *onColor = {col.on.r, col.on.g, col.on.b, col.on.a}; + if (offColor) *offColor = {col.off.r, col.off.g, col.off.b, col.off.a}; + return true; +} + +float ME_GetLightFactor(CVehicle *pVeh, ME_LightID lightId) { + if (!pVeh) return 0.0f; + auto matType = static_cast(lightId); + if (matType < 0 || matType >= eMaterialType::TotalMaterial) return 0.0f; + auto &data = LightManager::m_VehData.Get(pVeh); + return data.fLightFactor[matType]; +} + +int ME_GetLightDummyCount(CVehicle *pVeh, ME_LightID lightId) { + if (!pVeh) return 0; + auto matType = static_cast(lightId); + if (matType < 0 || matType >= eMaterialType::TotalMaterial) return 0; + auto &data = LightManager::m_VehData.Get(pVeh); + return static_cast(data.dummies[matType].size()); +} + +bool ME_GetLightDummyData(CVehicle *pVeh, ME_LightID lightId, int dummyIndex, ME_LightDummyInfo *outInfo) { + if (!pVeh || !outInfo || dummyIndex < 0) return false; + auto matType = static_cast(lightId); + if (matType < 0 || matType >= eMaterialType::TotalMaterial) return false; + auto &data = LightManager::m_VehData.Get(pVeh); + if (dummyIndex >= static_cast(data.dummies[matType].size())) return false; + const auto &c = data.dummies[matType][dummyIndex]->GetRef(); + outInfo->pFrame = c.frame; + outInfo->pos = c.position; + outInfo->color = {c.corona.color.r, c.corona.color.g, c.corona.color.b, c.corona.color.a}; + outInfo->fSize = c.corona.size; + outInfo->fAngle = c.rotation.angle; + outInfo->lightingType = static_cast(c.corona.lightingType); + return true; +} + +bool ME_IsLightDamaged(CVehicle *pVeh, ME_LightID lightId, int dummyIndex) { + if (!pVeh) return false; + auto matType = static_cast(lightId); + if (matType < 0 || matType >= eMaterialType::TotalMaterial) return false; + auto &data = LightManager::m_VehData.Get(pVeh); + if (dummyIndex >= 0 && dummyIndex < static_cast(data.dummies[matType].size())) { + return CarUtil::IsDummyDamaged(pVeh, data.dummies[matType][dummyIndex]->GetRef()); + } + if (matType == eMaterialType::HeadLightLeft) return CarUtil::IsLightDamaged(pVeh, eLights::LIGHT_FRONT_LEFT); + if (matType == eMaterialType::HeadLightRight) return CarUtil::IsLightDamaged(pVeh, eLights::LIGHT_FRONT_RIGHT); + if (matType == eMaterialType::TailLightLeft || matType == eMaterialType::BrakeLightLeft) return CarUtil::IsLightDamaged(pVeh, eLights::LIGHT_REAR_LEFT); + if (matType == eMaterialType::TailLightRight || matType == eMaterialType::BrakeLightRight) return CarUtil::IsLightDamaged(pVeh, eLights::LIGHT_REAR_RIGHT); + return false; +} + +bool ME_ArePopUpHeadlightsOpen(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = LightManager::m_VehData.Get(pVeh); + return HeadlightComponent::AreHeadlightsOpen(pVeh, data); +} + +bool ME_HasPopUpHeadlights(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = LightManager::m_VehData.Get(pVeh); + if (pVeh->m_nModelIndex == MODEL_ZR350) return true; + return data.bHasVehFuncsPopUp; +} + +bool ME_IsLightRendered(CVehicle *pVeh, ME_LightID lightId) { + if (!pVeh) return false; + auto matType = static_cast(lightId); + if (matType < 0 || matType >= eMaterialType::TotalMaterial) return false; + auto &data = LightManager::m_VehData.Get(pVeh); + return data.bLightRenderedThisFrame[matType]; +} + +float ME_GetRealisticSpeed(CVehicle *pVeh) { + if (!pVeh) return 0.0f; + return CarUtil::GetVehicleSpeedRealistic(pVeh); +} + // Sirens API int ME_GetSirenStateCount(CVehicle *pVeh) { if (!pVeh) return 0; @@ -196,6 +343,18 @@ bool ME_IsCustomSirenPlaying(CVehicle *pVeh) { return Sirens::IsPlayingCustomSiren(pVeh); } +bool ME_HasSirens(CVehicle *pVeh) { + if (!pVeh) return false; + return Sirens::IsSirenModel(pVeh->m_nModelIndex); +} + +int ME_GetSirenSoundState(CVehicle *pVeh) { + if (!pVeh) return -1; + auto *pData = Sirens::GetVehicleData(pVeh); + if (!pData) return -1; + return pData->m_nActiveSirenSoundState; +} + // License Plate API bool ME_GetPlateText(CVehicle *pVeh, char *outBuffer, int maxLen) { if (!pVeh || !outBuffer || maxLen <= 0) return false; @@ -220,6 +379,65 @@ bool ME_HasCustomPlate(CVehicle *pVeh) { return data.m_pCustomPlateTex != nullptr || !data.m_szLastPlateText.empty(); } +bool ME_GetPlateColors(CVehicle *pVeh, ME_Color *onColor, ME_Color *offColor) { + if (!pVeh) return false; + if (!DataMgr::Has(pVeh->m_nModelIndex)) return false; + const auto &json = DataMgr::Get(pVeh->m_nModelIndex); + const nlohmann::json *pSec = nullptr; + if (json.contains("plate")) pSec = &json["plate"]; + else if (json.contains("license_plate")) pSec = &json["license_plate"]; + else if (json.contains("lights")) { + if (json["lights"].contains("plate")) pSec = &json["lights"]["plate"]; + else if (json["lights"].contains("license_plate")) pSec = &json["lights"]["license_plate"]; + } + if (!pSec) return false; + + auto parseCol = [](const nlohmann::json &sec, const char *key, ME_Color *outCol) -> bool { + if (!outCol) return false; + const nlohmann::json *val = nullptr; + if (sec.contains(key)) val = &sec[key]; + else if (sec.contains("material") && sec["material"].contains(key)) val = &sec["material"][key]; + if (!val) return false; + if (val->is_array() && val->size() >= 3) { + outCol->r = (*val)[0].get(); + outCol->g = (*val)[1].get(); + outCol->b = (*val)[2].get(); + outCol->a = val->size() >= 4 ? (*val)[3].get() : 255; + return true; + } + if (val->is_object()) { + outCol->r = val->value("red", val->value("r", 255)); + outCol->g = val->value("green", val->value("g", 255)); + outCol->b = val->value("blue", val->value("b", 255)); + outCol->a = val->value("alpha", val->value("a", 255)); + return true; + } + return false; + }; + + bool foundOn = parseCol(*pSec, "color", onColor); + bool foundOff = parseCol(*pSec, "color_off", offColor); + return foundOn || foundOff; +} + +int ME_GetPlateCity(CVehicle *pVeh) { + if (!pVeh) return -1; + auto &data = LicensePlate::m_VehData.Get(pVeh); + return data.cityId; +} + +void ME_SetPlateCity(CVehicle *pVeh, int cityId) { + if (!pVeh) return; + auto &data = LicensePlate::m_VehData.Get(pVeh); + data.cityId = cityId; +} + +bool ME_IsPlateNightIlluminated(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = LicensePlate::m_VehData.Get(pVeh); + return data.m_bNightTexture; +} + // Carcols & Colors API bool ME_GetVehicleColors(CVehicle *pVeh, ME_Color *col1, ME_Color *col2, ME_Color *col3, ME_Color *col4) { if (!pVeh) return false; @@ -287,17 +505,17 @@ ME_ExhaustInfo ME_GetExhaustData(CVehicle *pVeh, int index) { return info; } -void ME_SetExhaustData(CVehicle *pVeh, int index, ME_ExhaustInfo &info) { +void ME_SetExhaustData(CVehicle *pVeh, int index, ME_ExhaustInfo &data) { if (!pVeh) return; - auto &data = ExhaustFx::m_VehData.Get(pVeh); - if (index >= 0 && index < static_cast(data.m_pDummies.size())) { - auto &ex = data.m_pDummies[index].second; - ex.pFrame = info.pFrame; - ex.Color = {info.Color.r, info.Color.g, info.Color.b, info.Color.a}; - ex.fSpeedMul = info.fSpeedMul; - ex.fLifeTime = info.fLifeTime; - ex.fSizeMul = info.fSizeMul; - ex.bNitroEffect = info.bNitroEffect; + auto &vData = ExhaustFx::m_VehData.Get(pVeh); + if (index >= 0 && index < static_cast(vData.m_pDummies.size())) { + auto &ex = vData.m_pDummies[index].second; + ex.pFrame = data.pFrame; + ex.Color = {data.Color.r, data.Color.g, data.Color.b, data.Color.a}; + ex.fSpeedMul = data.fSpeedMul; + ex.fLifeTime = data.fLifeTime; + ex.fSizeMul = data.fSizeMul; + ex.bNitroEffect = data.bNitroEffect; } } @@ -309,19 +527,30 @@ void ME_TriggerNitro(CVehicle *pVeh, bool enable) { } } +bool ME_IsNitroFiring(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = ExhaustFx::m_VehData.Get(pVeh); + return (CTimer::m_FrameCounter - data.lastNitroFrame) <= 1; +} + // Backfire API void ME_TriggerBackfire(CVehicle *pVeh, bool bPlaySound) { if (!pVeh) return; BackFireEffect::BackFireSingle(pVeh, bPlaySound); } +void ME_TriggerBackfireBurst(CVehicle *pVeh, bool bPlaySound) { + if (!pVeh) return; + BackFireEffect::BackFireMulti(pVeh, bPlaySound); +} + bool ME_IsBackfireActive(CVehicle *pVeh) { if (!pVeh) return false; auto &data = BackFireEffect::m_VehData.Get(pVeh); return data.m_nleftFires > 0; } -// Dashboard & Gauges API +// Dashboard, Gauges & Clock API float ME_GetVehicleMileage(CVehicle *pVeh) { if (!pVeh) return 0.0f; auto &data = MileageIndicator::m_VehData.Get(pVeh); @@ -339,6 +568,80 @@ void ME_SetVehicleMileage(CVehicle *pVeh, float mileageKm) { } } +bool ME_HasOdometer(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = MileageIndicator::m_VehData.Get(pVeh); + return !data.vecIndicatorData.empty(); +} + +float ME_GetSpeedometerValue(CVehicle *pVeh) { + if (!pVeh) return 0.0f; + auto &data = SpeedGauge::m_VehData.Get(pVeh); + if (!data.vecGaugeData.empty()) { + return data.vecGaugeData.begin()->second.fCurRotation; + } + return 0.0f; +} + +bool ME_HasSpeedometer(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = SpeedGauge::m_VehData.Get(pVeh); + return !data.vecGaugeData.empty(); +} + +float ME_GetRPMValue(CVehicle *pVeh) { + if (!pVeh) return 0.0f; + auto &data = RPMGauge::m_VehData.Get(pVeh); + if (!data.vecGaugeData.empty()) { + return data.vecGaugeData.begin()->second.fCurRotation; + } + return 0.0f; +} + +bool ME_HasRPMGauge(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = RPMGauge::m_VehData.Get(pVeh); + return !data.vecGaugeData.empty(); +} + +float ME_GetTurboValue(CVehicle *pVeh) { + if (!pVeh) return 0.0f; + auto &data = TurboGauge::m_VehData.Get(pVeh); + if (!data.vecGaugeData.empty()) { + return data.vecGaugeData.begin()->second.fCurRotation; + } + return 0.0f; +} + +bool ME_HasTurboGauge(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = TurboGauge::m_VehData.Get(pVeh); + return !data.vecGaugeData.empty(); +} + +bool ME_HasGearIndicator(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = GearIndicator::m_VehData.Get(pVeh); + return !data.vecIndicatorData.empty(); +} + +int ME_GetGearIndicatorValue(CVehicle *pVeh) { + if (!pVeh) return 0; + auto &data = GearIndicator::m_VehData.Get(pVeh); + if (!data.vecIndicatorData.empty()) { + return static_cast(data.vecIndicatorData[0].iCurrent); + } + return 0; +} + +void ME_SetGearIndicatorValue(CVehicle *pVeh, int gear) { + if (!pVeh || gear < 0) return; + auto &data = GearIndicator::m_VehData.Get(pVeh); + for (auto &ind : data.vecIndicatorData) { + ind.iCurrent = static_cast(gear); + } +} + bool ME_GetDashboardLEDState(CVehicle *pVeh, ME_LightID ledId) { if (!pVeh) return false; auto matType = static_cast(ledId); @@ -353,6 +656,24 @@ void ME_SetDashboardLEDState(CVehicle *pVeh, ME_LightID ledId, bool state) { } } +bool ME_IsClockAvailable(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = DigitalClockFeature::m_VehData.Get(pVeh); + return data.m_pRootFrame != nullptr || data.m_pDigitsRoot != nullptr; +} + +bool ME_IsClock12HourFormat(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = DigitalClockFeature::m_VehData.Get(pVeh); + return data.m_b12HourFormat; +} + +void ME_SetClock12HourFormat(CVehicle *pVeh, bool is12Hour) { + if (!pVeh) return; + auto &data = DigitalClockFeature::m_VehData.Get(pVeh); + data.m_b12HourFormat = is12Hour; +} + // Dirt FX API float ME_GetDirtLevel(CVehicle *pVeh) { return pVeh ? pVeh->m_fDirtLevel : 0.0f; @@ -371,6 +692,21 @@ void ME_SetRemapIndex(CVehicle *pVeh, int remapIndex) { if (pVeh) pVeh->SetRemap(remapIndex); } +bool ME_HasRemaps(int modelIndex) { + if (modelIndex < 0 || modelIndex >= 20000) return false; + return Remap::HasRemaps(modelIndex); +} + +int ME_GetRemapCount(CVehicle *pVeh) { + if (!pVeh) return 0; + return Remap::GetRemapCount(pVeh->m_nModelIndex); +} + +int ME_GetRemapCountByModel(int modelIndex) { + if (modelIndex < 0 || modelIndex >= 20000) return 0; + return Remap::GetRemapCount(modelIndex); +} + // Spoilers & Convertible Roof API bool ME_IsRoofOpen(CVehicle *pVeh) { if (!pVeh) return false; @@ -383,6 +719,24 @@ void ME_SetRoofOpen(CVehicle *pVeh, bool open) { data.m_bRoofTargetExpanded = open; } +bool ME_HasConvertibleRoof(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = ConvertibleRoof::m_VehData.Get(pVeh); + return !data.m_Roofs.empty() || !data.m_Boots.empty(); +} + +int ME_GetRoofAnimPhase(CVehicle *pVeh) { + if (!pVeh) return 0; + auto &data = ConvertibleRoof::m_VehData.Get(pVeh); + return static_cast(data.m_phase); +} + +int ME_GetSpoilerCount(CVehicle *pVeh) { + if (!pVeh) return 0; + auto &data = Spoiler::m_VehData.Get(pVeh); + return static_cast(data.m_Spoilers.size()); +} + float ME_GetSpoilerAngle(CVehicle *pVeh, int spoilerIndex) { if (!pVeh) return 0.0f; auto &data = Spoiler::m_VehData.Get(pVeh); @@ -392,6 +746,68 @@ float ME_GetSpoilerAngle(CVehicle *pVeh, int spoilerIndex) { return 0.0f; } +void ME_SetSpoilerAngle(CVehicle *pVeh, int spoilerIndex, float angle) { + if (!pVeh) return; + auto &data = Spoiler::m_VehData.Get(pVeh); + if (spoilerIndex >= 0 && spoilerIndex < static_cast(data.m_Spoilers.size())) { + data.m_Spoilers[spoilerIndex].m_fCurrentRotation = angle; + } +} + +// Doors & Animation API +bool ME_HasSlideDoors(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = SlideDoor::m_VehData.Get(pVeh); + return !data.leftFront.empty() || !data.rightFront.empty() || !data.leftRear.empty() || !data.rightRear.empty(); +} + +bool ME_HasRotateDoors(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = RotateDoor::m_VehData.Get(pVeh); + return !data.leftFront.empty() || !data.rightFront.empty() || !data.leftRear.empty() || !data.rightRear.empty() || !data.boot.empty() || !data.bonnet.empty(); +} + +// Wheels, Hubs & Chain API +bool ME_HasExtraWheels(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = ExtraWheel::m_VehData.Get(pVeh); + for (int i = 0; i < static_cast(eWheelPos::COUNT); ++i) { + if (!data.pExtras[i].empty()) return true; + } + return false; +} + +int ME_GetExtraWheelCount(CVehicle *pVeh, int wheelPos) { + if (!pVeh || wheelPos < 0 || wheelPos >= static_cast(eWheelPos::COUNT)) return 0; + auto &data = ExtraWheel::m_VehData.Get(pVeh); + return static_cast(data.pExtras[wheelPos].size()); +} + +bool ME_HasWheelHubs(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = WheelHub::m_VehData.Get(pVeh); + return data.m_pWRF || data.m_pHRF || data.m_pWRM || data.m_pHRM || data.m_pWRR || data.m_pHRR || + data.m_pWLF || data.m_pHLF || data.m_pWLM || data.m_pHLM || data.m_pWLR || data.m_pHLR; +} + +bool ME_HasAnimatedChain(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = ChainFeature::m_VehData.Get(pVeh); + return data.m_pRootFrame != nullptr || !data.m_FrameList.empty(); +} + +int ME_GetChainFrameIndex(CVehicle *pVeh) { + if (!pVeh) return 0; + auto &data = ChainFeature::m_VehData.Get(pVeh); + return static_cast(data.m_nCurChain); +} + +int ME_GetChainFrameCount(CVehicle *pVeh) { + if (!pVeh) return 0; + auto &data = ChainFeature::m_VehData.Get(pVeh); + return static_cast(data.m_FrameList.size()); +} + // Rollback Bed API bool ME_IsRollbackBedExpanded(CVehicle *pVeh) { if (!pVeh) return false; @@ -405,6 +821,33 @@ void ME_SetRollbackBedExpanded(CVehicle *pVeh, bool expanded) { data.bExpanded = expanded; } +bool ME_HasRollbackBed(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = RollbackBed::m_VehData.Get(pVeh); + return data.pBedFrame != nullptr; +} + +float ME_GetRollbackBedTilt(CVehicle *pVeh) { + if (!pVeh) return 0.0f; + auto &data = RollbackBed::m_VehData.Get(pVeh); + return data.fBedCurRot; +} + +int ME_GetRollbackBedPistonCount(CVehicle *pVeh) { + if (!pVeh) return 0; + auto &data = RollbackBed::m_VehData.Get(pVeh); + return static_cast(data.m_Pistons.size()); +} + +float ME_GetRollbackBedPistonMove(CVehicle *pVeh, int pistonIndex) { + if (!pVeh) return 0.0f; + auto &data = RollbackBed::m_VehData.Get(pVeh); + if (pistonIndex >= 0 && pistonIndex < static_cast(data.m_Pistons.size())) { + return data.m_Pistons[pistonIndex].fCurMove; + } + return 0.0f; +} + // Spotlights API bool ME_IsSpotlightActive(CVehicle *pVeh) { if (!pVeh) return false; @@ -417,6 +860,12 @@ void ME_SetSpotlightActive(CVehicle *pVeh, bool active) { data.bEnabled = active; } +bool ME_HasSpotlight(CVehicle *pVeh) { + if (!pVeh) return false; + auto &data = SpotLights::m_VehData.Get(pVeh); + return data.pFrame != nullptr; +} + // Ped Colors API int ME_GetPedVariationCount(int modelIndex) { if (modelIndex < 0 || modelIndex >= 20000) return 0; @@ -427,5 +876,627 @@ int ME_GetPedVariationCount(int modelIndex) { return 0; } +bool ME_GetPedColors(CPed *pPed, ME_Color *col1, ME_Color *col2, ME_Color *col3, ME_Color *col4) { + if (!pPed) return false; + auto &data = PedColors::GetPedData().Get(pPed); + if (!data.m_bUsingPedCols || data.m_Colors.size() < 4) return false; + if (col1) *col1 = {data.m_Colors[0].r, data.m_Colors[0].g, data.m_Colors[0].b, data.m_Colors[0].a}; + if (col2) *col2 = {data.m_Colors[1].r, data.m_Colors[1].g, data.m_Colors[1].b, data.m_Colors[1].a}; + if (col3) *col3 = {data.m_Colors[2].r, data.m_Colors[2].g, data.m_Colors[2].b, data.m_Colors[2].a}; + if (col4) *col4 = {data.m_Colors[3].r, data.m_Colors[3].g, data.m_Colors[3].b, data.m_Colors[3].a}; + return true; +} + +bool ME_SetPedColors(CPed *pPed, ME_Color col1, ME_Color col2, ME_Color col3, ME_Color col4) { + if (!pPed) return false; + auto &data = PedColors::GetPedData().Get(pPed); + if (data.m_Colors.size() < 4) { + data.m_Colors.resize(4); + } + data.m_Colors[0] = CRGBA(col1.r, col1.g, col1.b, col1.a); + data.m_Colors[1] = CRGBA(col2.r, col2.g, col2.b, col2.a); + data.m_Colors[2] = CRGBA(col3.r, col3.g, col3.b, col3.a); + data.m_Colors[3] = CRGBA(col4.r, col4.g, col4.b, col4.a); + data.m_bUsingPedCols = true; + return true; +} + +// Audio & Sounds API +bool ME_PlaySound(CVehicle *pVeh, const char *soundPath, float volume) { + if (!pVeh || !soundPath) return false; + AudioMgr::Play3DSound(soundPath, pVeh->GetPosition(), pVeh, volume); + return true; +} + +float ME_GetBrakePressure(CVehicle *pVeh) { + if (!pVeh) return 0.0f; + auto &sData = SoundEffects::m_VehData.Get(pVeh); + return sData.m_fBrakePressure; +} + +bool ME_PlayFileSound(const char *soundPath, float volume) { + if (!soundPath) return false; + AudioMgr::PlayFileSound(soundPath, volume); + return true; +} + +void ME_PlaySwitchSound(CVehicle *pVeh) { + AudioMgr::PlaySwitchSound(pVeh); +} + +bool ME_GetSirenAudioPath(int modeIndex, char *outBuffer, int maxLen) { + if (!outBuffer || maxLen <= 0) return false; + std::string path = AudioMgr::GetSirenAudioPath(modeIndex); + if (path.empty()) return false; + SafeStrCopy(outBuffer, maxLen, path); + return true; +} + +// Visual & Render Configuration +float ME_GetMaterialAmbientMul() { + return ModelInfoMgr::GetMaterialAmbientMul(); +} + +void ME_SetMaterialAmbientMul(float mul) { + ModelInfoMgr::gfMaterialAmbientMul = std::max(0.0f, mul); +} + +float ME_GetCoronaDistanceMul() { + return RenderUtil::GetCoronaDistanceMul(); +} + +float ME_GetCoronaNearClip() { + return RenderUtil::GetCoronaNearClip(); +} + +float ME_GetLightShadowDistance() { + return RenderUtil::GetLightShadowDistance(); +} + +float ME_GetHighBeamPointLightMul() { + return LightsConfig::Get().fHighBeamPointLightMul; +} + +bool ME_IsAutoIndicatorsOnSteerEnabled() { + return LightsConfig::Get().bAutoIndicatorsOnSteer; +} + +bool ME_IsFoglightTiedToHeadlight() { + return LightsConfig::Get().bFoglightTiedToHeadlight; +} + +bool ME_IsPlayerIdleBrakeLightsEnabled() { + return LightsConfig::Get().bPlayerIdleBrakeLights; +} + +// Vehicle Physics & Auxiliary +float ME_GetVehiclePitch(CVehicle *pVeh) { + if (!pVeh) return 0.0f; + return CarUtil::GetVehiclePitch(pVeh); +} + +bool ME_IsVehicleDoingWheelie(CVehicle *pVeh) { + if (!pVeh) return false; + return CarUtil::IsVehicleDoingWheelie(pVeh); +} + +bool ME_IsVehicleEngineOff(CVehicle *pVeh) { + if (!pVeh) return true; + return CarUtil::IsEngineOff(pVeh); +} + +// Helpers for JSON config extraction +static bool ParseJsonColor(const nlohmann::json &val, ME_Color &outCol, const nlohmann::json *pRoot = nullptr) { + if (val.is_array() && val.size() >= 3) { + outCol.r = val[0].get(); + outCol.g = val[1].get(); + outCol.b = val[2].get(); + outCol.a = val.size() >= 4 ? val[3].get() : 255; + return true; + } + if (val.is_object()) { + outCol.r = val.value("red", val.value("r", 255)); + outCol.g = val.value("green", val.value("g", 255)); + outCol.b = val.value("blue", val.value("b", 255)); + outCol.a = val.value("alpha", val.value("a", 255)); + return true; + } + if (val.is_string() && pRoot && pRoot->contains("colors") && (*pRoot)["colors"].contains(val.get())) { + return ParseJsonColor((*pRoot)["colors"][val.get()], outCol, pRoot); + } + return false; +} + +// ========================================== +// JSONC Configuration Export API Implementation +// ========================================== + +// Model Data & Metadata +bool ME_HasModelData(int modelIndex) { + if (modelIndex < 0 || modelIndex >= 20000) return false; + return DataMgr::Has(modelIndex); +} + +bool ME_GetModelDataPath(int modelIndex, char *outPath, int maxLen) { + if (modelIndex < 0 || modelIndex >= 20000 || !outPath || maxLen <= 0) return false; + if (!DataMgr::Has(modelIndex)) return false; + const auto &path = DataMgr::GetPath(modelIndex); + SafeStrCopy(outPath, maxLen, path); + return true; +} + +bool ME_GetModelMetadata(int modelIndex, char *outAuthor, int maxAuthorLen, char *outDesc, int maxDescLen, char *outCreationTime, int maxCreationTimeLen, int *outMinVer) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson) return false; + const nlohmann::json *pMeta = nullptr; + if (pJson->contains("metadata")) pMeta = &(*pJson)["metadata"]; + else if (pJson->contains("Metadata")) pMeta = &(*pJson)["Metadata"]; + if (!pMeta) return false; + + if (outAuthor && maxAuthorLen > 0) { + SafeStrCopy(outAuthor, maxAuthorLen, pMeta->value("author", "")); + } + if (outDesc && maxDescLen > 0) { + SafeStrCopy(outDesc, maxDescLen, pMeta->value("desc", "")); + } + if (outCreationTime && maxCreationTimeLen > 0) { + SafeStrCopy(outCreationTime, maxCreationTimeLen, pMeta->value("creationtime", "")); + } + if (outMinVer) { + *outMinVer = pMeta->value("minver", MOD_VERSION_NUMBER); + } + return true; +} + +// Vehicle Lights JSON Config +bool ME_GetGlobalLightInertia(int modelIndex, float *outInertia) { + if (!outInertia) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("lights")) return false; + const auto &lights = (*pJson)["lights"]; + if (lights.contains("inertia")) { + *outInertia = lights.value("inertia", 0.0f); + return true; + } + return false; +} + +bool ME_GetLightDummyConfig(int modelIndex, const char *dummyName, ME_Color *outCoronaColor, float *outCoronaSize, int *outLightingType, ME_Color *outShadowColor, float *outShadowSize, char *outShadowTexture, int maxTextureLen, bool *outRotationChecks, float *outInertia, int *outStrobeDelay) { + if (!dummyName) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("lights")) return false; + const auto &lights = (*pJson)["lights"]; + if (!lights.contains(dummyName)) return false; + const auto &dSec = lights[dummyName]; + + if (dSec.contains("corona")) { + const auto &cSec = dSec["corona"]; + if (outCoronaColor && cSec.contains("color")) { + ParseJsonColor(cSec["color"], *outCoronaColor, pJson); + } + if (outCoronaSize) { + *outCoronaSize = cSec.value("size", 0.35f); + } + if (outLightingType) { + *outLightingType = static_cast(GetLightingMode(cSec.value("type", "directional"))); + } + } + if (dSec.contains("shadow")) { + const auto &sSec = dSec["shadow"]; + if (outShadowColor && sSec.contains("color")) { + ParseJsonColor(sSec["color"], *outShadowColor, pJson); + } + if (outShadowSize) { + *outShadowSize = sSec.value("size", 1.0f); + } + if (outShadowTexture && maxTextureLen > 0) { + SafeStrCopy(outShadowTexture, maxTextureLen, sSec.value("texture", "pointlight")); + } + if (outRotationChecks) { + *outRotationChecks = sSec.value("rotationchecks", true); + } + } + if (outInertia && dSec.contains("inertia")) { + *outInertia = dSec.value("inertia", 0.0f); + } + if (outStrobeDelay && dSec.contains("strobedelay")) { + *outStrobeDelay = dSec.value("strobedelay", 1000); + } + return true; +} + +bool ME_GetLightGroupConfig(int modelIndex, ME_LightID lightId, ME_Color *outCoronaColor, float *outCoronaSize, int *outLightingType, ME_Color *outShadowColor, float *outShadowSize, char *outShadowTexture, int maxTextureLen, float *outInertia) { + auto matType = static_cast(lightId); + const char *grpKey = LightManager::GetLightGroupKey(matType); + const char *specKey = LightManager::GetLightSpecificKey(matType); + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("lights")) return false; + const auto &lights = (*pJson)["lights"]; + + const nlohmann::json *pSec = nullptr; + if (specKey && lights.contains(specKey)) pSec = &lights[specKey]; + else if (grpKey && lights.contains(grpKey)) pSec = &lights[grpKey]; + if (!pSec) return false; + + if (pSec->contains("corona")) { + const auto &cSec = (*pSec)["corona"]; + if (outCoronaColor && cSec.contains("color")) { + ParseJsonColor(cSec["color"], *outCoronaColor, pJson); + } + if (outCoronaSize) { + *outCoronaSize = cSec.value("size", 0.35f); + } + if (outLightingType) { + *outLightingType = static_cast(GetLightingMode(cSec.value("type", "directional"))); + } + } + if (pSec->contains("shadow")) { + const auto &sSec = (*pSec)["shadow"]; + if (outShadowColor && sSec.contains("color")) { + ParseJsonColor(sSec["color"], *outShadowColor, pJson); + } + if (outShadowSize) { + *outShadowSize = sSec.value("size", 1.0f); + } + if (outShadowTexture && maxTextureLen > 0) { + SafeStrCopy(outShadowTexture, maxTextureLen, sSec.value("texture", "pointlight")); + } + } + if (outInertia && pSec->contains("inertia")) { + *outInertia = pSec->value("inertia", 0.0f); + } + return true; +} + +// Sirens JSON Config +bool ME_IsSirenImVehFt(int modelIndex) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("sirens")) return false; + return (*pJson)["sirens"].value("imvehft", false); +} + +bool ME_GetSirenStateName(int modelIndex, int stateIndex, char *outName, int maxNameLen) { + if (!outName || maxNameLen <= 0 || stateIndex < 0) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("sirens") || !(*pJson)["sirens"].contains("states")) return false; + const auto &states = (*pJson)["sirens"]["states"]; + if (!states.is_object()) return false; + int idx = 0; + for (auto it = states.begin(); it != states.end(); ++it, ++idx) { + if (idx == stateIndex) { + SafeStrCopy(outName, maxNameLen, it.key()); + return true; + } + } + return false; +} + +int ME_GetSirenItemCount(int modelIndex, int stateIndex) { + if (stateIndex < 0) return 0; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("sirens") || !(*pJson)["sirens"].contains("states")) return 0; + const auto &states = (*pJson)["sirens"]["states"]; + if (!states.is_object()) return 0; + int idx = 0; + for (auto it = states.begin(); it != states.end(); ++it, ++idx) { + if (idx == stateIndex && it.value().is_object()) { + return static_cast(it.value().size()); + } + } + return 0; +} + +bool ME_GetSirenItemConfig(int modelIndex, int stateIndex, const char *sirenKey, ME_Color *outColor, float *outSize, float *outInertia, int *outStartState, char *outType, int maxTypeLen, ME_Color *outShadowColor, float *outShadowSize, char *outShadowType, int maxShadowTypeLen, float *outAngleOffset) { + if (!sirenKey || stateIndex < 0) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("sirens") || !(*pJson)["sirens"].contains("states")) return false; + const auto &states = (*pJson)["sirens"]["states"]; + if (!states.is_object()) return false; + int idx = 0; + const nlohmann::json *pStateSec = nullptr; + for (auto it = states.begin(); it != states.end(); ++it, ++idx) { + if (idx == stateIndex) { + pStateSec = &it.value(); + break; + } + } + if (!pStateSec || !pStateSec->contains(sirenKey)) return false; + const auto &item = (*pStateSec)[sirenKey]; + + if (outColor && item.contains("color")) { + ParseJsonColor(item["color"], *outColor, pJson); + } + if (outSize) { + *outSize = item.value("size", 0.35f); + } + if (outInertia) { + *outInertia = item.value("inertia", 0.0f); + } + if (outStartState) { + *outStartState = item.value("state", 1); + } + if (outType && maxTypeLen > 0) { + SafeStrCopy(outType, maxTypeLen, item.value("type", "directional")); + } + if (item.contains("shadow")) { + const auto &sh = item["shadow"]; + if (outShadowColor && sh.contains("color")) { + ParseJsonColor(sh["color"], *outShadowColor, pJson); + } + if (outShadowSize) { + *outShadowSize = sh.value("size", 1.0f); + } + if (outShadowType && maxShadowTypeLen > 0) { + SafeStrCopy(outShadowType, maxShadowTypeLen, sh.value("type", "pointlight")); + } + if (outAngleOffset) { + *outAngleOffset = sh.value("angleoffset", 0.0f); + } + } + return true; +} + +bool ME_GetSirenItemPattern(int modelIndex, int stateIndex, const char *sirenKey, int *outPatternDelays, int maxDelays, int *outDelayCount) { + if (!sirenKey || stateIndex < 0 || !outPatternDelays || maxDelays <= 0) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("sirens") || !(*pJson)["sirens"].contains("states")) return false; + const auto &states = (*pJson)["sirens"]["states"]; + if (!states.is_object()) return false; + int idx = 0; + const nlohmann::json *pStateSec = nullptr; + for (auto it = states.begin(); it != states.end(); ++it, ++idx) { + if (idx == stateIndex) { + pStateSec = &it.value(); + break; + } + } + if (!pStateSec || !pStateSec->contains(sirenKey)) return false; + const auto &item = (*pStateSec)[sirenKey]; + if (!item.contains("pattern") || !item["pattern"].is_array()) return false; + const auto &pat = item["pattern"]; + int count = std::min(static_cast(pat.size()), maxDelays); + for (int i = 0; i < count; ++i) { + outPatternDelays[i] = pat[i].is_number() ? pat[i].get() : 0; + } + if (outDelayCount) { + *outDelayCount = static_cast(pat.size()); + } + return true; +} + +// Gauges JSON Config +bool ME_GetSpeedometerConfig(int modelIndex, const char *nodeName, float *outMaxSpeed, float *outMaxRotation, bool *outIsKph) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("gauges")) return false; + std::string name = nodeName ? nodeName : "x_sm"; + if (!(*pJson)["gauges"].contains(name)) return false; + const auto &g = (*pJson)["gauges"][name]; + if (outMaxSpeed) *outMaxSpeed = g.value("maxspeed", 240.0f); + if (outMaxRotation) *outMaxRotation = g.value("maxrotation", 240.0f); + if (outIsKph) *outIsKph = g.value("kph", true); + return true; +} + +bool ME_GetRPMConfig(int modelIndex, const char *nodeName, float *outMaxRPM, float *outMaxRotation) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("gauges")) return false; + std::string name = nodeName ? nodeName : "x_rpm"; + if (!(*pJson)["gauges"].contains(name)) return false; + const auto &g = (*pJson)["gauges"][name]; + if (outMaxRPM) *outMaxRPM = g.value("maxrpm", 8000.0f); + if (outMaxRotation) *outMaxRotation = g.value("maxrotation", 240.0f); + return true; +} + +bool ME_GetTurboConfig(int modelIndex, const char *nodeName, float *outMaxTurbo, float *outMaxRotation) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("gauges")) return false; + std::string name = nodeName ? nodeName : "x_tm"; + if (!(*pJson)["gauges"].contains(name)) return false; + const auto &g = (*pJson)["gauges"][name]; + if (outMaxTurbo) *outMaxTurbo = g.value("maxturbo", 2.0f); + if (outMaxRotation) *outMaxRotation = g.value("maxrotation", 240.0f); + return true; +} + +bool ME_GetFixedGaugeConfig(int modelIndex, const char *nodeName, float *outMinAngle, float *outMaxAngle) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("gauges")) return false; + std::string name = nodeName ? nodeName : "x_gauge_fixed"; + if (!(*pJson)["gauges"].contains(name)) return false; + const auto &g = (*pJson)["gauges"][name]; + if (outMinAngle) *outMinAngle = g.value("minangle", 20.0f); + if (outMaxAngle) *outMaxAngle = g.value("maxangle", 70.0f); + return true; +} + +bool ME_GetOdometerConfig(int modelIndex, const char *nodeName, bool *outIsKph) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("gauges")) return false; + std::string name = nodeName ? nodeName : "x_odometer"; + if (!(*pJson)["gauges"].contains(name)) return false; + const auto &g = (*pJson)["gauges"][name]; + if (outIsKph) *outIsKph = g.value("kph", true); + return true; +} + +// Clocks JSON Config +bool ME_GetClockConfig(int modelIndex, const char *nodeName, bool *out12HourFormat) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("clocks")) return false; + std::string name = nodeName ? nodeName : "x_dclock"; + if (!(*pJson)["clocks"].contains(name)) return false; + const auto &c = (*pJson)["clocks"][name]; + if (out12HourFormat) *out12HourFormat = c.value("12hformat", false); + return true; +} + +// Animated Doors JSON Config +bool ME_GetSlideDoorConfig(int modelIndex, const char *nodeName, float *outMovMul, float *outPopOut) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("doors") || !nodeName) return false; + if (!(*pJson)["doors"].contains(nodeName)) return false; + const auto &d = (*pJson)["doors"][nodeName]; + if (outMovMul) *outMovMul = d.value("movmul", 1.0f); + if (outPopOut) *outPopOut = d.value("popout", 0.15f); + return true; +} + +bool ME_GetRotateDoorConfig(int modelIndex, const char *nodeName, float *outMul, float *outPopOut) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("doors") || !nodeName) return false; + if (!(*pJson)["doors"].contains(nodeName)) return false; + const auto &d = (*pJson)["doors"][nodeName]; + if (outMul) *outMul = d.value("mul", 1.0f); + if (outPopOut) *outPopOut = d.value("popout", 0.15f); + return true; +} + +// Spoilers JSON Config +bool ME_GetSpoilerConfig(int modelIndex, const char *nodeName, float *outRotation, float *outTimeMs, float *outTriggerSpeed) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("spoilers")) return false; + std::string name = nodeName ? nodeName : "movspoiler"; + if (!(*pJson)["spoilers"].contains(name)) return false; + const auto &s = (*pJson)["spoilers"][name]; + if (outRotation) *outRotation = s.value("rotation", 30.0f); + if (outTimeMs) *outTimeMs = s.value("time", 3000.0f); + if (outTriggerSpeed) *outTriggerSpeed = s.value("triggerspeed", 20.0f); + return true; +} + +// Convertible Roofs JSON Config +bool ME_GetRoofConfig(int modelIndex, const char *nodeName, float *outRotation, float *outSpeed) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("roofs") || !nodeName) return false; + if (!(*pJson)["roofs"].contains(nodeName)) return false; + const auto &r = (*pJson)["roofs"][nodeName]; + if (outRotation) *outRotation = r.value("rotation", 60.0f); + if (outSpeed) *outSpeed = r.value("speed", 2.0f); + return true; +} + +// Rollback Bed JSON Config +bool ME_GetRollbackBedConfig(int modelIndex, float *outBedTargetRot, float *outBedRotSpeed, float *outHyTargetRot, float *outHyRotSpeed, float *outHyTargetMove, float *outHyMoveSpeed) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("rollback_bed")) return false; + const auto &rb = (*pJson)["rollback_bed"]; + if (rb.contains("bed")) { + const auto &bed = rb["bed"]; + if (outBedTargetRot) *outBedTargetRot = bed.value("target_rot", 12.0f); + if (outBedRotSpeed) *outBedRotSpeed = bed.value("rot_speed", 1.0f); + } + if (rb.contains("hydraulics")) { + const auto &hy = rb["hydraulics"]; + if (outHyTargetRot) *outHyTargetRot = hy.value("target_rot", 18.0f); + if (outHyRotSpeed) *outHyRotSpeed = hy.value("rot_speed", 1.0f); + if (outHyTargetMove) *outHyTargetMove = hy.value("target_move", 2.0f); + if (outHyMoveSpeed) *outHyMoveSpeed = hy.value("move_speed", 1.0f); + } + return true; +} + +// Exhausts JSON Config +bool ME_GetExhaustConfig(int modelIndex, const char *nodeName, float *outLifetime, float *outSpeed, float *outSize, bool *outNitroEffect, ME_Color *outColor) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("exhausts") || !nodeName) return false; + if (!(*pJson)["exhausts"].contains(nodeName)) return false; + const auto &ex = (*pJson)["exhausts"][nodeName]; + if (outLifetime) *outLifetime = ex.value("lifetime", 0.4f); + if (outSpeed) *outSpeed = ex.value("speed", 1.0f); + if (outSize) *outSize = ex.value("size", 1.0f); + if (outNitroEffect) *outNitroEffect = ex.value("nitro_effect", true); + if (outColor && ex.contains("color")) { + ParseJsonColor(ex["color"], *outColor, pJson); + } + return true; +} + +// License Plate JSON Config +bool ME_GetPlateConfig(int modelIndex, ME_Color *outOnColor, ME_Color *outOffColor) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson) return false; + const nlohmann::json *pSec = nullptr; + if (pJson->contains("plate")) pSec = &(*pJson)["plate"]; + else if (pJson->contains("license_plate")) pSec = &(*pJson)["license_plate"]; + else if (pJson->contains("lights")) { + if ((*pJson)["lights"].contains("plate")) pSec = &(*pJson)["lights"]["plate"]; + else if ((*pJson)["lights"].contains("license_plate")) pSec = &(*pJson)["lights"]["license_plate"]; + } + if (!pSec) return false; + + bool foundOn = false; + bool foundOff = false; + if (outOnColor) { + if (pSec->contains("color")) foundOn = ParseJsonColor((*pSec)["color"], *outOnColor, pJson); + else if (pSec->contains("material") && (*pSec)["material"].contains("color")) foundOn = ParseJsonColor((*pSec)["material"]["color"], *outOnColor, pJson); + } + if (outOffColor) { + if (pSec->contains("color_off")) foundOff = ParseJsonColor((*pSec)["color_off"], *outOffColor, pJson); + else if (pSec->contains("material") && (*pSec)["material"].contains("color_off")) foundOff = ParseJsonColor((*pSec)["material"]["color_off"], *outOffColor, pJson); + } + return foundOn || foundOff; +} + +// Carcols JSON Config +int ME_GetCarcolColorCount(int modelIndex) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("carcols") || !(*pJson)["carcols"].contains("colors") || !(*pJson)["carcols"]["colors"].is_array()) return 0; + return static_cast((*pJson)["carcols"]["colors"].size()); +} + +bool ME_GetCarcolColor(int modelIndex, int colorIndex, ME_Color *outColor) { + if (!outColor || colorIndex < 0) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("carcols") || !(*pJson)["carcols"].contains("colors") || !(*pJson)["carcols"]["colors"].is_array()) return false; + const auto &cols = (*pJson)["carcols"]["colors"]; + if (colorIndex >= static_cast(cols.size())) return false; + return ParseJsonColor(cols[colorIndex], *outColor, pJson); +} + +bool ME_GetCarcolVariationData(int modelIndex, int varIndex, int *outPrimary, int *outSecondary, int *outTertiary, int *outQuaternary) { + if (varIndex < 0) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("carcols") || !(*pJson)["carcols"].contains("variations") || !(*pJson)["carcols"]["variations"].is_array()) return false; + const auto &vars = (*pJson)["carcols"]["variations"]; + if (varIndex >= static_cast(vars.size())) return false; + const auto &v = vars[varIndex]; + if (outPrimary) *outPrimary = v.value("primary", 0); + if (outSecondary) *outSecondary = v.value("secondary", 0); + if (outTertiary) *outTertiary = v.value("tertiary", 0); + if (outQuaternary) *outQuaternary = v.value("quaternary", 0); + return true; +} + +// PedCols JSON Config +int ME_GetPedColColorCount(int modelIndex) { + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("pedcols") || !(*pJson)["pedcols"].contains("colors") || !(*pJson)["pedcols"]["colors"].is_array()) return 0; + return static_cast((*pJson)["pedcols"]["colors"].size()); +} + +bool ME_GetPedColColor(int modelIndex, int colorIndex, ME_Color *outColor) { + if (!outColor || colorIndex < 0) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("pedcols") || !(*pJson)["pedcols"].contains("colors") || !(*pJson)["pedcols"]["colors"].is_array()) return false; + const auto &cols = (*pJson)["pedcols"]["colors"]; + if (colorIndex >= static_cast(cols.size())) return false; + return ParseJsonColor(cols[colorIndex], *outColor, pJson); +} + +bool ME_GetPedColVariationData(int modelIndex, int varIndex, int *outPrimary, int *outSecondary, int *outTertiary, int *outQuaternary) { + if (varIndex < 0) return false; + const auto *pJson = DataMgr::Find(modelIndex); + if (!pJson || !pJson->contains("pedcols") || !(*pJson)["pedcols"].contains("variations") || !(*pJson)["pedcols"]["variations"].is_array()) return false; + const auto &vars = (*pJson)["pedcols"]["variations"]; + if (varIndex >= static_cast(vars.size())) return false; + const auto &v = vars[varIndex]; + if (outPrimary) *outPrimary = v.value("primary", 0); + if (outSecondary) *outSecondary = v.value("secondary", 0); + if (outTertiary) *outTertiary = v.value("tertiary", 0); + if (outQuaternary) *outQuaternary = v.value("quaternary", 0); + return true; +} + } // extern "C" diff --git a/src/ModelExtrasAPI.h b/src/ModelExtrasAPI.h index 530d20ca..faac03be 100755 --- a/src/ModelExtrasAPI.h +++ b/src/ModelExtrasAPI.h @@ -26,6 +26,16 @@ struct ME_ExhaustInfo bool bNitroEffect; }; +struct ME_LightDummyInfo +{ + RwFrame *pFrame; + CVector pos; + ME_Color color; + float fSize; + float fAngle; + int lightingType; +}; + enum ME_FeatureID { ME_TextureRemapper, @@ -137,17 +147,32 @@ extern "C" ME_WRAPPER int ME_GetAPIVersion(); ME_WRAPPER int ME_GetVersion(); ME_WRAPPER bool ME_IsFeatureAvail(ME_FeatureID featureId); + ME_WRAPPER bool ME_IsFeatureEnabled(ME_FeatureID featureId); ME_WRAPPER void ME_Reload(); ME_WRAPPER void ME_ReloadVehicle(CVehicle *pVeh); // Vehicle Lights - ME_WRAPPER bool ME_GetVehicleLightState(CVehicle *pVeh, ME_LightID lightId); - ME_WRAPPER void ME_SetVehicleLightState(CVehicle *pVeh, ME_LightID lightId, bool state); - ME_WRAPPER bool ME_IsLightAvailable(CVehicle *pVeh, ME_LightID lightId); - ME_WRAPPER int ME_GetIndicatorState(CVehicle *pVeh); - ME_WRAPPER void ME_SetIndicatorState(CVehicle *pVeh, int state); - ME_WRAPPER int ME_GetLightOverride(CVehicle *pVeh); - ME_WRAPPER void ME_SetLightOverride(CVehicle *pVeh, int overrideMode); + ME_WRAPPER bool ME_GetVehicleLightState(CVehicle *pVeh, ME_LightID lightId); + ME_WRAPPER void ME_SetVehicleLightState(CVehicle *pVeh, ME_LightID lightId, bool state); + ME_WRAPPER bool ME_IsLightAvailable(CVehicle *pVeh, ME_LightID lightId); + ME_WRAPPER int ME_GetIndicatorState(CVehicle *pVeh); + ME_WRAPPER void ME_SetIndicatorState(CVehicle *pVeh, int state); + ME_WRAPPER int ME_GetLightOverride(CVehicle *pVeh); + ME_WRAPPER void ME_SetLightOverride(CVehicle *pVeh, int overrideMode); + ME_WRAPPER bool ME_GetFogLights(CVehicle *pVeh); + ME_WRAPPER void ME_SetFogLights(CVehicle *pVeh, bool on); + ME_WRAPPER bool ME_GetHighBeam(CVehicle *pVeh); + ME_WRAPPER void ME_SetHighBeam(CVehicle *pVeh, bool on); + ME_WRAPPER float ME_GetLightInertia(CVehicle *pVeh, ME_LightID lightId); + ME_WRAPPER bool ME_GetLightColor(CVehicle *pVeh, ME_LightID lightId, ME_Color *onColor, ME_Color *offColor); + ME_WRAPPER float ME_GetLightFactor(CVehicle *pVeh, ME_LightID lightId); + ME_WRAPPER int ME_GetLightDummyCount(CVehicle *pVeh, ME_LightID lightId); + ME_WRAPPER bool ME_GetLightDummyData(CVehicle *pVeh, ME_LightID lightId, int dummyIndex, ME_LightDummyInfo *outInfo); + ME_WRAPPER bool ME_IsLightDamaged(CVehicle *pVeh, ME_LightID lightId, int dummyIndex); + ME_WRAPPER bool ME_ArePopUpHeadlightsOpen(CVehicle *pVeh); + ME_WRAPPER bool ME_HasPopUpHeadlights(CVehicle *pVeh); + ME_WRAPPER bool ME_IsLightRendered(CVehicle *pVeh, ME_LightID lightId); + ME_WRAPPER float ME_GetRealisticSpeed(CVehicle *pVeh); // Sirens ME_WRAPPER int ME_GetSirenStateCount(CVehicle *pVeh); @@ -161,11 +186,17 @@ extern "C" ME_WRAPPER bool ME_IsSirenActive(CVehicle *pVeh); ME_WRAPPER bool ME_IsSirenVehicle(CVehicle *pVeh); ME_WRAPPER bool ME_IsCustomSirenPlaying(CVehicle *pVeh); + ME_WRAPPER bool ME_HasSirens(CVehicle *pVeh); + ME_WRAPPER int ME_GetSirenSoundState(CVehicle *pVeh); // License Plate ME_WRAPPER bool ME_GetPlateText(CVehicle *pVeh, char *outBuffer, int maxLen); ME_WRAPPER bool ME_SetPlateText(CVehicle *pVeh, const char *text); ME_WRAPPER bool ME_HasCustomPlate(CVehicle *pVeh); + ME_WRAPPER bool ME_GetPlateColors(CVehicle *pVeh, ME_Color *onColor, ME_Color *offColor); + ME_WRAPPER int ME_GetPlateCity(CVehicle *pVeh); + ME_WRAPPER void ME_SetPlateCity(CVehicle *pVeh, int cityId); + ME_WRAPPER bool ME_IsPlateNightIlluminated(CVehicle *pVeh); // Carcols & Colors ME_WRAPPER bool ME_GetVehicleColors(CVehicle *pVeh, ME_Color *col1, ME_Color *col2, ME_Color *col3, ME_Color *col4); @@ -179,16 +210,31 @@ extern "C" ME_WRAPPER ME_ExhaustInfo ME_GetExhaustData(CVehicle *pVeh, int index); ME_WRAPPER void ME_SetExhaustData(CVehicle *pVeh, int index, ME_ExhaustInfo &data); ME_WRAPPER void ME_TriggerNitro(CVehicle *pVeh, bool enable); + ME_WRAPPER bool ME_IsNitroFiring(CVehicle *pVeh); // Backfire ME_WRAPPER void ME_TriggerBackfire(CVehicle *pVeh, bool bPlaySound); + ME_WRAPPER void ME_TriggerBackfireBurst(CVehicle *pVeh, bool bPlaySound); ME_WRAPPER bool ME_IsBackfireActive(CVehicle *pVeh); - // Dashboard & Gauges + // Dashboard, Gauges & Clock ME_WRAPPER float ME_GetVehicleMileage(CVehicle *pVeh); ME_WRAPPER void ME_SetVehicleMileage(CVehicle *pVeh, float mileageKm); + ME_WRAPPER bool ME_HasOdometer(CVehicle *pVeh); + ME_WRAPPER float ME_GetSpeedometerValue(CVehicle *pVeh); + ME_WRAPPER bool ME_HasSpeedometer(CVehicle *pVeh); + ME_WRAPPER float ME_GetRPMValue(CVehicle *pVeh); + ME_WRAPPER bool ME_HasRPMGauge(CVehicle *pVeh); + ME_WRAPPER float ME_GetTurboValue(CVehicle *pVeh); + ME_WRAPPER bool ME_HasTurboGauge(CVehicle *pVeh); + ME_WRAPPER bool ME_HasGearIndicator(CVehicle *pVeh); + ME_WRAPPER int ME_GetGearIndicatorValue(CVehicle *pVeh); + ME_WRAPPER void ME_SetGearIndicatorValue(CVehicle *pVeh, int gear); ME_WRAPPER bool ME_GetDashboardLEDState(CVehicle *pVeh, ME_LightID ledId); ME_WRAPPER void ME_SetDashboardLEDState(CVehicle *pVeh, ME_LightID ledId, bool state); + ME_WRAPPER bool ME_IsClockAvailable(CVehicle *pVeh); + ME_WRAPPER bool ME_IsClock12HourFormat(CVehicle *pVeh); + ME_WRAPPER void ME_SetClock12HourFormat(CVehicle *pVeh, bool is12Hour); // Dirt FX ME_WRAPPER float ME_GetDirtLevel(CVehicle *pVeh); @@ -197,22 +243,131 @@ extern "C" // Remap ME_WRAPPER int ME_GetRemapIndex(CVehicle *pVeh); ME_WRAPPER void ME_SetRemapIndex(CVehicle *pVeh, int remapIndex); + ME_WRAPPER bool ME_HasRemaps(int modelIndex); + ME_WRAPPER int ME_GetRemapCount(CVehicle *pVeh); + ME_WRAPPER int ME_GetRemapCountByModel(int modelIndex); // Spoilers & Convertible Roof ME_WRAPPER bool ME_IsRoofOpen(CVehicle *pVeh); ME_WRAPPER void ME_SetRoofOpen(CVehicle *pVeh, bool open); + ME_WRAPPER bool ME_HasConvertibleRoof(CVehicle *pVeh); + ME_WRAPPER int ME_GetRoofAnimPhase(CVehicle *pVeh); + ME_WRAPPER int ME_GetSpoilerCount(CVehicle *pVeh); ME_WRAPPER float ME_GetSpoilerAngle(CVehicle *pVeh, int spoilerIndex); + ME_WRAPPER void ME_SetSpoilerAngle(CVehicle *pVeh, int spoilerIndex, float angle); + + // Doors & Animation + ME_WRAPPER bool ME_HasSlideDoors(CVehicle *pVeh); + ME_WRAPPER bool ME_HasRotateDoors(CVehicle *pVeh); + + // Wheels, Hubs & Chain + ME_WRAPPER bool ME_HasExtraWheels(CVehicle *pVeh); + ME_WRAPPER int ME_GetExtraWheelCount(CVehicle *pVeh, int wheelPos); + ME_WRAPPER bool ME_HasWheelHubs(CVehicle *pVeh); + ME_WRAPPER bool ME_HasAnimatedChain(CVehicle *pVeh); + ME_WRAPPER int ME_GetChainFrameIndex(CVehicle *pVeh); + ME_WRAPPER int ME_GetChainFrameCount(CVehicle *pVeh); // Rollback Bed - ME_WRAPPER bool ME_IsRollbackBedExpanded(CVehicle *pVeh); - ME_WRAPPER void ME_SetRollbackBedExpanded(CVehicle *pVeh, bool expanded); + ME_WRAPPER bool ME_IsRollbackBedExpanded(CVehicle *pVeh); + ME_WRAPPER void ME_SetRollbackBedExpanded(CVehicle *pVeh, bool expanded); + ME_WRAPPER bool ME_HasRollbackBed(CVehicle *pVeh); + ME_WRAPPER float ME_GetRollbackBedTilt(CVehicle *pVeh); + ME_WRAPPER int ME_GetRollbackBedPistonCount(CVehicle *pVeh); + ME_WRAPPER float ME_GetRollbackBedPistonMove(CVehicle *pVeh, int pistonIndex); // Spotlights ME_WRAPPER bool ME_IsSpotlightActive(CVehicle *pVeh); ME_WRAPPER void ME_SetSpotlightActive(CVehicle *pVeh, bool active); + ME_WRAPPER bool ME_HasSpotlight(CVehicle *pVeh); // Ped Colors - ME_WRAPPER int ME_GetPedVariationCount(int modelIndex); + ME_WRAPPER int ME_GetPedVariationCount(int modelIndex); + ME_WRAPPER bool ME_GetPedColors(CPed *pPed, ME_Color *col1, ME_Color *col2, ME_Color *col3, ME_Color *col4); + ME_WRAPPER bool ME_SetPedColors(CPed *pPed, ME_Color col1, ME_Color col2, ME_Color col3, ME_Color col4); + + // Audio & Sounds + ME_WRAPPER bool ME_PlaySound(CVehicle *pVeh, const char *soundPath, float volume); + ME_WRAPPER float ME_GetBrakePressure(CVehicle *pVeh); + ME_WRAPPER bool ME_PlayFileSound(const char *soundPath, float volume); + ME_WRAPPER void ME_PlaySwitchSound(CVehicle *pVeh); + ME_WRAPPER bool ME_GetSirenAudioPath(int modeIndex, char *outBuffer, int maxLen); + + // Visual & Render Configuration + ME_WRAPPER float ME_GetMaterialAmbientMul(); + ME_WRAPPER void ME_SetMaterialAmbientMul(float mul); + ME_WRAPPER float ME_GetCoronaDistanceMul(); + ME_WRAPPER float ME_GetCoronaNearClip(); + ME_WRAPPER float ME_GetLightShadowDistance(); + ME_WRAPPER float ME_GetHighBeamPointLightMul(); + ME_WRAPPER bool ME_IsAutoIndicatorsOnSteerEnabled(); + ME_WRAPPER bool ME_IsFoglightTiedToHeadlight(); + ME_WRAPPER bool ME_IsPlayerIdleBrakeLightsEnabled(); + + // Vehicle Physics & Auxiliary + ME_WRAPPER float ME_GetVehiclePitch(CVehicle *pVeh); + ME_WRAPPER bool ME_IsVehicleDoingWheelie(CVehicle *pVeh); + ME_WRAPPER bool ME_IsVehicleEngineOff(CVehicle *pVeh); + + // ========================================== + // JSONC Configuration Export API + // ========================================== + + // Model Data & Metadata + ME_WRAPPER bool ME_HasModelData(int modelIndex); + ME_WRAPPER bool ME_GetModelDataPath(int modelIndex, char *outPath, int maxLen); + ME_WRAPPER bool ME_GetModelMetadata(int modelIndex, char *outAuthor, int maxAuthorLen, char *outDesc, int maxDescLen, char *outCreationTime, int maxCreationTimeLen, int *outMinVer); + + // Vehicle Lights JSON Config + ME_WRAPPER bool ME_GetGlobalLightInertia(int modelIndex, float *outInertia); + ME_WRAPPER bool ME_GetLightDummyConfig(int modelIndex, const char *dummyName, ME_Color *outCoronaColor, float *outCoronaSize, int *outLightingType, ME_Color *outShadowColor, float *outShadowSize, char *outShadowTexture, int maxTextureLen, bool *outRotationChecks, float *outInertia, int *outStrobeDelay); + ME_WRAPPER bool ME_GetLightGroupConfig(int modelIndex, ME_LightID lightId, ME_Color *outCoronaColor, float *outCoronaSize, int *outLightingType, ME_Color *outShadowColor, float *outShadowSize, char *outShadowTexture, int maxTextureLen, float *outInertia); + + // Sirens JSON Config + ME_WRAPPER bool ME_IsSirenImVehFt(int modelIndex); + ME_WRAPPER bool ME_GetSirenStateName(int modelIndex, int stateIndex, char *outName, int maxNameLen); + ME_WRAPPER int ME_GetSirenItemCount(int modelIndex, int stateIndex); + ME_WRAPPER bool ME_GetSirenItemConfig(int modelIndex, int stateIndex, const char *sirenKey, ME_Color *outColor, float *outSize, float *outInertia, int *outStartState, char *outType, int maxTypeLen, ME_Color *outShadowColor, float *outShadowSize, char *outShadowType, int maxShadowTypeLen, float *outAngleOffset); + ME_WRAPPER bool ME_GetSirenItemPattern(int modelIndex, int stateIndex, const char *sirenKey, int *outPatternDelays, int maxDelays, int *outDelayCount); + + // Gauges & Meters JSON Config + ME_WRAPPER bool ME_GetSpeedometerConfig(int modelIndex, const char *nodeName, float *outMaxSpeed, float *outMaxRotation, bool *outIsKph); + ME_WRAPPER bool ME_GetRPMConfig(int modelIndex, const char *nodeName, float *outMaxRPM, float *outMaxRotation); + ME_WRAPPER bool ME_GetTurboConfig(int modelIndex, const char *nodeName, float *outMaxTurbo, float *outMaxRotation); + ME_WRAPPER bool ME_GetFixedGaugeConfig(int modelIndex, const char *nodeName, float *outMinAngle, float *outMaxAngle); + ME_WRAPPER bool ME_GetOdometerConfig(int modelIndex, const char *nodeName, bool *outIsKph); + + // Clocks JSON Config + ME_WRAPPER bool ME_GetClockConfig(int modelIndex, const char *nodeName, bool *out12HourFormat); + + // Animated Doors JSON Config + ME_WRAPPER bool ME_GetSlideDoorConfig(int modelIndex, const char *nodeName, float *outMovMul, float *outPopOut); + ME_WRAPPER bool ME_GetRotateDoorConfig(int modelIndex, const char *nodeName, float *outMul, float *outPopOut); + + // Spoilers JSON Config + ME_WRAPPER bool ME_GetSpoilerConfig(int modelIndex, const char *nodeName, float *outRotation, float *outTimeMs, float *outTriggerSpeed); + + // Convertible Roofs JSON Config + ME_WRAPPER bool ME_GetRoofConfig(int modelIndex, const char *nodeName, float *outRotation, float *outSpeed); + + // Rollback Bed JSON Config + ME_WRAPPER bool ME_GetRollbackBedConfig(int modelIndex, float *outBedTargetRot, float *outBedRotSpeed, float *outHyTargetRot, float *outHyRotSpeed, float *outHyTargetMove, float *outHyMoveSpeed); + + // Exhausts JSON Config + ME_WRAPPER bool ME_GetExhaustConfig(int modelIndex, const char *nodeName, float *outLifetime, float *outSpeed, float *outSize, bool *outNitroEffect, ME_Color *outColor); + + // License Plate JSON Config + ME_WRAPPER bool ME_GetPlateConfig(int modelIndex, ME_Color *outOnColor, ME_Color *outOffColor); + + // Carcols JSON Config + ME_WRAPPER int ME_GetCarcolColorCount(int modelIndex); + ME_WRAPPER bool ME_GetCarcolColor(int modelIndex, int colorIndex, ME_Color *outColor); + ME_WRAPPER bool ME_GetCarcolVariationData(int modelIndex, int varIndex, int *outPrimary, int *outSecondary, int *outTertiary, int *outQuaternary); + + // PedCols JSON Config + ME_WRAPPER int ME_GetPedColColorCount(int modelIndex); + ME_WRAPPER bool ME_GetPedColColor(int modelIndex, int colorIndex, ME_Color *outColor); + ME_WRAPPER bool ME_GetPedColVariationData(int modelIndex, int varIndex, int *outPrimary, int *outSecondary, int *outTertiary, int *outQuaternary); #ifdef __cplusplus } diff --git a/src/features/pedcols.cpp b/src/features/pedcols.cpp index 8376e002..e890c1c6 100755 --- a/src/features/pedcols.cpp +++ b/src/features/pedcols.cpp @@ -65,6 +65,7 @@ PedData::PedData(CPed *pPed) { const auto& selectedVar = variations[varIdx]; const std::vector keys = { "primary", "secondary", "tertiary", "quaternary" }; + m_Colors.assign(4, CRGBA(255, 255, 255, 255)); for (size_t i = 0; i < keys.size(); ++i) { if (selectedVar.contains(keys[i])) { diff --git a/src/features/pedcols.h b/src/features/pedcols.h index c968b7d2..4500387a 100755 --- a/src/features/pedcols.h +++ b/src/features/pedcols.h @@ -33,4 +33,5 @@ class PedColors : public CBaseFeature { public: PedColors() : CBaseFeature("PedCols", "FEATURES", eFeatureMatrix::PedCols) {} + static PedExtendedData& GetPedData() { return m_PedData; } }; diff --git a/src/features/remap.h b/src/features/remap.h index 3c7709de..5ec2434c 100755 --- a/src/features/remap.h +++ b/src/features/remap.h @@ -32,4 +32,13 @@ class Remap : public CVehFeature public: Remap() : CVehFeature("TextureRemapper", "FEATURES", eFeatureMatrix::TextureRemapper) {} static void ProcessTextures(CVehicle *pVeh, RpMaterial *pMat); + static bool HasRemaps(int modelIndex) { + auto it = xRemaps.find(modelIndex); + return it != xRemaps.end() && !it->second.pTextures.empty(); + } + static int GetRemapCount(int modelIndex) { + auto it = xRemaps.find(modelIndex); + if (it == xRemaps.end() || it->second.pTextures.empty()) return 0; + return static_cast(it->second.pTextures.begin()->second.size()); + } }; \ No newline at end of file diff --git a/src/utils/render.cpp b/src/utils/render.cpp index 118703b2..d390bfb1 100755 --- a/src/utils/render.cpp +++ b/src/utils/render.cpp @@ -100,6 +100,24 @@ static void EnsureConfigLoaded() } } +float RenderUtil::GetCoronaDistanceMul() +{ + EnsureConfigLoaded(); + return gfCoronaDistanceMul; +} + +float RenderUtil::GetCoronaNearClip() +{ + EnsureConfigLoaded(); + return gfCoronaNearClip; +} + +float RenderUtil::GetLightShadowDistance() +{ + EnsureConfigLoaded(); + return gfLightShadowDistance; +} + void RenderUtil::RegisterCorona(CEntity *pEntity, int coronaID, CVector pos, CRGBA col, float size) { EnsureConfigLoaded(); diff --git a/src/utils/render.h b/src/utils/render.h index 3ae5f7ff..8629578d 100755 --- a/src/utils/render.h +++ b/src/utils/render.h @@ -21,4 +21,7 @@ class RenderUtil static void RegisterShadow(CEntity *pEntity, CVector position, CRGBA col, float angle, eDummyPos dummyPos, const std::string &shadwTexName, CVector2D shdwSz = {1.0f, 1.0f}, CVector2D shdwOffset = {0.0f, 0.0f}, RwTexture *pTexture = nullptr); static void RegisterShadowDirectional(const DummyConfig *pConfig, const std::string &shadwTexName, float shdwSz, float alphaMul = 1.0f); static void ReloadConfig(); + static float GetCoronaDistanceMul(); + static float GetCoronaNearClip(); + static float GetLightShadowDistance(); }; \ No newline at end of file