diff --git a/resource/common/locale/English.toml b/resource/common/locale/English.toml index e9c6b57..4d75d6a 100644 --- a/resource/common/locale/English.toml +++ b/resource/common/locale/English.toml @@ -131,7 +131,7 @@ TotalMinutesDay = "Total minutes per day" VerySunny = "Very sunny" Weather = "Weather" WeatherID = "Weather ID" -WeatherIDText = "Sets weather by IDs (0-255). Don't touch unless you know what you're doing!" +WeatherIDText = "Sets weather by IDs (0-255)." [Main] TranslationLanguage = "English" @@ -472,9 +472,16 @@ CemterMassZ = "Centre of mass Z" Cheap = "Cheap" Color = "Color" ColorPicker = "Color picker" +ColorType = "Color type" ColProof = "Collision proof" Component = "Component" Country = "Country" +Customize = "Customize" +CustomizeLabel = "Customization label" +CutomizeClear = "Clear customizations" +CutomizeClearMSG = "Cleared customizations" +CutomizeSave = "Save customizations" +CutomizeSaveMSG = "Saved customizations" Damage = "Damage" DampingLvl = "Damping level" DensityMul = "Density multiplier" @@ -583,6 +590,7 @@ ResetHandling = "Reset handling" ResetHandlingMSG = "Handling reset successfully" ResetTexture = "Reset texture" ResetTextureMSG = "Texture reset successfully" +Save = "Save" SaveFile = "Save to file" SaveFileMSG = "Handling saved successfully" SeatOffset = "Seat offset" diff --git a/src/custom/neon_sa.cpp b/src/custom/neon_sa.cpp index a0e9005..50d8ff8 100644 --- a/src/custom/neon_sa.cpp +++ b/src/custom/neon_sa.cpp @@ -184,45 +184,42 @@ NeonMgr::NeonMgr() { Events::vehicleRenderEvent += [this](CVehicle* pVeh) { - if (m_bEnabled) + NeonData* data = &m_VehNeon.Get(pVeh); + if (data->m_bNeonInstalled && !pVeh->IsUpsideDown()) { - NeonData* data = &m_VehNeon.Get(pVeh); - if (data->m_bNeonInstalled && !pVeh->IsUpsideDown()) + if (!m_pNeonTexture) { - if (!m_pNeonTexture) + m_pNeonTexture = LoadTextureFromMemory((char*)neon_mask, sizeof(neon_mask)); + } + + CVector Pos = CModelInfo::GetModelInfo(pVeh->m_nModelIndex)->m_pColModel->m_boundBox.m_vecMin; + CVector center = pVeh->TransformFromObjectSpace(CVector(0.0f, 0.0f, 0.0f)); + CVector up = pVeh->TransformFromObjectSpace(CVector(0.0f, -Pos.y - data->m_fVal, 0.0f)) - center; + CVector right = pVeh->TransformFromObjectSpace(CVector(Pos.x + data->m_fVal, 0.0f, 0.0f)) - center; + CShadows::StoreShadowToBeRendered(5, m_pNeonTexture, ¢er, up.x, up.y, right.x, right.y, 180, data->m_Color.r, + data->m_Color.g, data->m_Color.b, 2.0f, false, 1.0f, 0, true); + + if (data->m_bPulsing) + { + size_t delta = CTimer::m_snTimeInMilliseconds - CTimer::m_snPreviousTimeInMilliseconds; + + if (data->m_fVal < 0.0f) { - m_pNeonTexture = LoadTextureFromMemory((char*)neon_mask, sizeof(neon_mask)); + data->m_bIncrement = true; } - CVector Pos = CModelInfo::GetModelInfo(pVeh->m_nModelIndex)->m_pColModel->m_boundBox.m_vecMin; - CVector center = pVeh->TransformFromObjectSpace(CVector(0.0f, 0.0f, 0.0f)); - CVector up = pVeh->TransformFromObjectSpace(CVector(0.0f, -Pos.y - data->m_fVal, 0.0f)) - center; - CVector right = pVeh->TransformFromObjectSpace(CVector(Pos.x + data->m_fVal, 0.0f, 0.0f)) - center; - CShadows::StoreShadowToBeRendered(5, m_pNeonTexture, ¢er, up.x, up.y, right.x, right.y, 180, data->m_Color.r, - data->m_Color.g, data->m_Color.b, 2.0f, false, 1.0f, 0, true); - - if (data->m_bPulsing) + if (data->m_fVal > 0.3f) { - size_t delta = CTimer::m_snTimeInMilliseconds - CTimer::m_snPreviousTimeInMilliseconds; + data->m_bIncrement = false; + } - if (data->m_fVal < 0.0f) - { - data->m_bIncrement = true; - } - - if (data->m_fVal > 0.3f) - { - data->m_bIncrement = false; - } - - if (data->m_bIncrement) - { - data->m_fVal += 0.0003f * delta; - } - else - { - data->m_fVal -= 0.0003f * delta; - } + if (data->m_bIncrement) + { + data->m_fVal += 0.0003f * delta; + } + else + { + data->m_fVal -= 0.0003f * delta; } } } @@ -268,4 +265,9 @@ void NeonMgr::Install(CVehicle* pVeh, int r, int g, int b) void NeonMgr::Remove(CVehicle* pVeh) { m_VehNeon.Get(pVeh).m_bNeonInstalled = false; +} + +NeonMgr::NeonData NeonMgr::GetData(CVehicle *pVeh) +{ + return m_VehNeon.Get(pVeh); } \ No newline at end of file diff --git a/src/custom/neon_sa.h b/src/custom/neon_sa.h index a110d87..c122492 100644 --- a/src/custom/neon_sa.h +++ b/src/custom/neon_sa.h @@ -38,6 +38,9 @@ private: public: + // Returns internal neon data + NeonData GetData(CVehicle *pVeh); + // Installs neons with color void Install(CVehicle* veh, int r, int g, int b); diff --git a/src/custom/paint_sa.cpp b/src/custom/paint_sa.cpp index a4ac8ef..fbf190e 100644 --- a/src/custom/paint_sa.cpp +++ b/src/custom/paint_sa.cpp @@ -1,321 +1,293 @@ -// Portion of this source is taken from MoonAdditions https://github.com/THE-FYP/MoonAdditions - -// Copyright (c) 2012 DK22Pac -// Copyright (c) 2017 FYP -// Copyright (c) 2021 Grinch_ - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - #include "pch.h" #include "paint_sa.h" #include "utils/util.h" -#include +#include "../../include/kiero/minhook/MinHook.h" -void Paint::InjectHooks() +PaintMgr& Paint = PaintMgr::Get(); + +// This works with silentpatch +static auto oEntityRender = (void(__fastcall*)(CVehicle*))0x534310; +void __fastcall hkEntityRender(CVehicle* pVeh) { - static bool init; - - if (init) + if (!pVeh || pVeh->m_nType != ENTITY_TYPE_VEHICLE) { + oEntityRender(pVeh); return; } + auto& data = Paint.GetData(pVeh); - Events::vehicleRenderEvent.before += [](CVehicle* pVeh) + /* + Game colors won't ve visible over material colors + So reset material colors & apply game colors here + */ + if (pVeh->m_nPrimaryColor != data.m_nCarColors[0] + || pVeh->m_nSecondaryColor != data.m_nCarColors[1]) { - VehData& data = m_VehData.Get(pVeh); - - // reset custom color if color id changed - if (pVeh->m_nPrimaryColor != data.primary_color - || pVeh->m_nSecondaryColor != data.secondary_color) + for (auto& it : data.m_nMapInfoList) { - for (auto& it : data.materialProperties) - data.resetMaterialColor(it.first); - - data.primary_color = pVeh->m_nPrimaryColor; - data.secondary_color = pVeh->m_nSecondaryColor; + data.ResetMatColor(it.first); } - for (auto& it : data.materialProperties) + data.m_nCarColors[0] = pVeh->m_nPrimaryColor; + data.m_nCarColors[1] = pVeh->m_nSecondaryColor; + data.m_nCarColors[2] = pVeh->m_nTertiaryColor; + data.m_nCarColors[3] = pVeh->m_nQuaternaryColor; + } + + + /* + Applying our custom material colors here + */ + for (auto& it : data.m_nMapInfoList) + { + if (it.second.m_bRecolor) { - if (it.second._recolor) + it.second.m_nOriginalColor = it.first->color; + it.first->color = it.second.m_nColor; + it.second.m_nOriginalGeometryFlags = it.second.m_pGeometry->flags; + it.second.m_pGeometry->flags |= rpGEOMETRYMODULATEMATERIALCOLOR; + } + + if (it.second.m_bRetexture) + { + auto tex = it.second.m_pTexture; + if (tex) { - it.second._originalColor = it.first->color; - it.first->color = it.second._color; - it.second._originalGeometryFlags = it.second._geometry->flags; - it.second._geometry->flags |= rpGEOMETRYMODULATEMATERIALCOLOR; + it.second.m_pOriginalTexture = it.first->texture; + it.first->texture = tex; } - if (it.second._retexture) + else { - auto tex = it.second._texture; - if (tex) - { - it.second._originalTexture = it.first->texture; - it.first->texture = tex; - } - else - { - it.second._retexture = false; - } + it.second.m_bRetexture = false; + } + } + } + oEntityRender(pVeh); +}; + +PaintMgr::PaintMgr() +{ + MH_Initialize(); + MH_CreateHook((void*)0x534310, hkEntityRender, (void**)&oEntityRender); + MH_EnableHook((void*)0x534310); + + // This doesn't work for helicopters? SilentPatch? + static ThiscallEvent, PRIORITY_BEFORE, ArgPickN, void(CVehicle*)> vehicleResetAfterRender; + vehicleResetAfterRender += [this](CVehicle* pVeh) + { + PaintData& data = m_VehPaint.Get(pVeh); + for (auto& it : data.m_nMapInfoList) + { + if (it.second.m_bRecolor) + { + it.first->color = it.second.m_nOriginalColor; + it.second.m_pGeometry->flags = it.second.m_nOriginalGeometryFlags; + } + if (it.second.m_bRetexture) + { + it.first->texture = it.second.m_pOriginalTexture; } } }; - - ThiscallEvent, PRIORITY_BEFORE, ArgPickN, void(CVehicle*)> vehicleResetAfterRender; - vehicleResetAfterRender += [](CVehicle* pVeh) - { - for (auto& it : m_VehData.Get(pVeh).materialProperties) - { - if (it.second._recolor) - { - it.first->color = it.second._originalColor; - it.second._geometry->flags = it.second._originalGeometryFlags; - } - if (it.second._retexture) - { - it.first->texture = it.second._originalTexture; - } - } - }; - init = true; } -void Paint::VehData::setMaterialColor(RpMaterial* material, RpGeometry* geometry, RwRGBA color, bool filter_mat) +void PaintMgr::SetCarcols(CVehicle *pVeh, uint primary, uint secondary, uint tertiary, uint quaternary, bool reset) { - auto& matProps = materialProperties[material]; + *(uint8_replacement*)(int(pVeh) + 0x433 + 1) = primary; + *(uint8_replacement*)(int(pVeh) + 0x433 + 2) = secondary; + *(uint8_replacement*)(int(pVeh) + 0x433 + 3) = tertiary; + *(uint8_replacement*)(int(pVeh) + 0x433 + 4) = quaternary; - if (!filter_mat - || (material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00) - || (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF)) + // stop trigger reset + if (!reset) { - matProps._recolor = true; - matProps._color = color; - matProps._geometry = geometry; + auto& data = Paint.GetData(pVeh); + data.m_nCarColors[0] = *(uint8_replacement*)(int(pVeh) + 0x433 + 1); + data.m_nCarColors[1] = *(uint8_replacement*)(int(pVeh) + 0x433 + 2); + data.m_nCarColors[2] = *(uint8_replacement*)(int(pVeh) + 0x433 + 3); + data.m_nCarColors[3] = *(uint8_replacement*)(int(pVeh) + 0x433 + 4); + } + +} + +void PaintMgr::PaintData::SetMatColor(RpMaterial* material, RpGeometry* geometry, RwRGBA color) +{ + auto& matInfo = m_nMapInfoList[material]; + if ((material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00) + || (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF)) + { + matInfo.m_bRecolor = true; + matInfo.m_nColor = color; + matInfo.m_pGeometry = geometry; } } -void Paint::VehData::setMaterialTexture(RpMaterial* material, RwTexture* texture, bool filter_mat) +void PaintMgr::PaintData::SetMatTexture(RpMaterial* material, RwTexture* texture) { - auto& matProps = materialProperties[material]; - - if (!filter_mat - || (material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00) - || (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF)) + auto& matInfo = m_nMapInfoList[material]; + if ((material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00) + || (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF)) { - matProps._retexture = true; - matProps._texture = texture; + matInfo.m_bRetexture = true; + matInfo.m_pTexture = texture; } } -void Paint::VehData::resetMaterialColor(RpMaterial* material) +void PaintMgr::PaintData::ResetMatColor(RpMaterial* material) { - auto& matProps = materialProperties[material]; - matProps._recolor = false; - matProps._color = {0, 0, 0, 0}; + auto& matInfo = m_nMapInfoList[material]; + matInfo.m_bRecolor = false; + matInfo.m_nColor = {0, 0, 0, 0}; } -void Paint::VehData::resetMaterialTexture(RpMaterial* material) +void PaintMgr::PaintData::ResetMatTexture(RpMaterial* material) { - auto& matProps = materialProperties[material]; - matProps._retexture = false; - matProps._texture = nullptr; + auto& matInfo = m_nMapInfoList[material]; + matInfo.m_bRetexture = false; + matInfo.m_pTexture = nullptr; } -void Paint::NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function func) +PaintMgr::PaintData& PaintMgr::GetData(CVehicle *pVeh) +{ + return m_VehPaint.Get(pVeh); +} + +static void NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function func) { if (frame) { func(frame); - if (RwFrame* newFrame = frame->child) + { NodeWrapperRecursive(newFrame, pVeh, func); + } if (RwFrame* newFrame = frame->next) + { NodeWrapperRecursive(newFrame, pVeh, func); + } } return; } -void Paint::GenerateNodeList(CVehicle* pVeh, std::vector& names_vec, std::string& selected) +struct BindData { - static int vehModel = 0; - if (vehModel == pVeh->m_nModelIndex) - { - return; - } + void *pData; + CRGBA color; + RwTexture* pTexture; +}; - // reset to default - names_vec.clear(); - names_vec.push_back("Default"); - selected = "Default"; - - RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent; - - NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame) - { - const std::string name = GetFrameNodeName(frame); - if (!(std::find(names_vec.begin(), names_vec.end(), name) != names_vec.end())) - { - names_vec.push_back(name); - } - }); - vehModel = pVeh->m_nModelIndex; -} - -void Paint::SetNodeColor(CVehicle* pVeh, std::string node_name, CRGBA color, bool filter_mat) +void PaintMgr::SetColor(CVehicle* pVeh, CRGBA color) { - RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent; - - NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame) + RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent; + NodeWrapperRecursive(pFrame, pVeh, [&](RwFrame* frame) { - const std::string name = GetFrameNodeName(frame); - - struct ST + BindData bindData { &m_VehPaint.Get(pVeh), color }; + RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* { - CRGBA _color; - bool _filter; - } st; - - st._color = color; - st._filter = filter_mat; - - if (node_name == "Default" || node_name == name) - { - RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* + if (object->type == rpATOMIC) { - if (object->type == rpATOMIC) + RpAtomic* atomic = reinterpret_cast(object); + + BindData* bind = reinterpret_cast(data); + CRGBA color = bind->color; + PaintData* pData = reinterpret_cast(bind->pData); + + for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i) { - RpAtomic* atomic = reinterpret_cast(object); - - ST* st = reinterpret_cast(data); - CRGBA* color = &st->_color; - bool filter_mat = st->_filter; - - VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle); - - for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i) - data.setMaterialColor(atomic->geometry->matList.materials[i], atomic->geometry, - {color->r, color->g, color->b, 255}, filter_mat); + pData->SetMatColor(atomic->geometry->matList.materials[i], atomic->geometry, + {color.r, color.g, color.b, color.a}); } - return object; - }, &st); - } + } + return object; + }, &bindData); }); } -void Paint::SetNodeTexture(CVehicle* pVeh, std::string node_name, std::string texturename, bool filter_mat) +void PaintMgr::SetTexture(CVehicle* pVeh, std::string name) { - RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent; - RwTexture* texture = nullptr; + RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent; + RwTexture* pTexture = nullptr; + // find the texture for (auto const& tex : m_TextureData.m_ImagesList) { - if (tex.get()->m_FileName == texturename) + if (tex.get()->m_FileName == name) { - texture = tex.get()->m_pRwTexture; + pTexture = tex.get()->m_pRwTexture; break; } } - NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame) + if (!pTexture) { - const std::string name = GetFrameNodeName(frame); + return; + } - struct ST + m_VehPaint.Get(pVeh).m_nTextureName = name; + NodeWrapperRecursive(pFrame, pVeh, [&](RwFrame* frame) + { + BindData bindData { &m_VehPaint.Get(pVeh), {}, pTexture }; + RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* { - RwTexture* _tex; - bool _filter; - } st; - - st._tex = texture; - st._filter = filter_mat; - - if (node_name == "Default" || node_name == name) - { - RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* + if (object->type == rpATOMIC) { - if (object->type == rpATOMIC) + RpAtomic* atomic = reinterpret_cast(object); + + BindData* bind = reinterpret_cast(data); + RwTexture *pTex = bind->pTexture; + PaintData* pData = reinterpret_cast(bind->pData); + + for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i) { - RpAtomic* atomic = reinterpret_cast(object); - - ST* st = reinterpret_cast(data); - VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle); - - for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i) - { - data.setMaterialTexture(atomic->geometry->matList.materials[i], st->_tex, - st->_filter); - } + pData->SetMatTexture(atomic->geometry->matList.materials[i], pTex); } - return object; - }, &st); - } + } + return object; + }, &bindData); }); } -void Paint::ResetNodeColor(CVehicle* pVeh, std::string node_name) +void PaintMgr::ResetColor(CVehicle* pVeh) { RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent; - NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame) { - const std::string name = GetFrameNodeName(frame); - - if (node_name == "Default" || node_name == name) + RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* { - RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* + if (object->type == rpATOMIC) { - if (object->type == rpATOMIC) - { - RpAtomic* atomic = reinterpret_cast(object); - VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle); + RpAtomic* pAtomic = reinterpret_cast(object); + PaintData* pData = reinterpret_cast(data); - for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i) - data.resetMaterialColor(atomic->geometry->matList.materials[i]); + for (int i = 0; i < pAtomic->geometry->matList.numMaterials; ++i) + { + pData->ResetMatColor(pAtomic->geometry->matList.materials[i]); } - return object; - }, nullptr); - } + } + return object; + }, &m_VehPaint.Get(pVeh)); }); } -void Paint::ResetNodeTexture(CVehicle* pVeh, std::string node_name) +void PaintMgr::ResetTexture(CVehicle* pVeh) { RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent; - NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame) { - const std::string name = GetFrameNodeName(frame); - - if (node_name == "Default" || node_name == name) + RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* { - RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* + if (object->type == rpATOMIC) { - if (object->type == rpATOMIC) + RpAtomic* pAtomic = reinterpret_cast(object); + PaintData* pData = reinterpret_cast(data); + + for (int i = 0; i < pAtomic->geometry->matList.numMaterials; ++i) { - RpAtomic* atomic = reinterpret_cast(object); - - VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle); - - for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i) - data.resetMaterialTexture(atomic->geometry->matList.materials[i]); + pData->ResetMatTexture(pAtomic->geometry->matList.materials[i]); } - return object; - }, nullptr); - } + } + return object; + }, &m_VehPaint.Get(pVeh)); }); + m_VehPaint.Get(pVeh).m_nTextureName = ""; } \ No newline at end of file diff --git a/src/custom/paint_sa.h b/src/custom/paint_sa.h index 065309b..1cb3f9c 100644 --- a/src/custom/paint_sa.h +++ b/src/custom/paint_sa.h @@ -1,85 +1,79 @@ -// Portion of this source is taken from MoonAdditions https://github.com/THE-FYP/MoonAdditions - -// Copyright (c) 2012 DK22Pac -// Copyright (c) 2017 FYP -// Copyright (c) 2021 Grinch_ - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - #pragma once -#include +#include "pch.h" +#include "interface/icheat.hpp" +/* + Vehicle color & texturring implementation class for GTA: San Andreas -class Paint + TODO: Implement for VC & 3 too (maybe) + Dunno how it'd work with the d3d8to9 wrapper +*/ +class PaintMgr : public ICheat { private: - // store vehicle specific data - struct VehData + struct PaintData { - struct MaterialProperties + struct MatInfo { - MaterialProperties() : - _color{0, 0, 0, 0}, - _recolor(false), - _retexture(false), - _geometry(nullptr), - _originalColor{0, 0, 0, 0}, - _originalTexture(nullptr), - _originalGeometryFlags(0) - { - } - - RwRGBA _color; - RwTexture* _texture; - bool _recolor; - bool _retexture; - RpGeometry* _geometry; - RwRGBA _originalColor; - RwTexture* _originalTexture; - RwInt32 _originalGeometryFlags; + bool m_bRecolor = false; + bool m_bRetexture = false; + RwRGBA m_nColor = {0, 0, 0, 0}; + RwRGBA m_nOriginalColor = {0, 0, 0, 0}; + RwTexture* m_pTexture = nullptr; + RwTexture* m_pOriginalTexture = nullptr; + RpGeometry* m_pGeometry = nullptr; + RwInt32 m_nOriginalGeometryFlags = 0; }; + uchar m_nCarColors[4]; // carcols color IDs (primary, secondary, tertiary, quaternary) + std::string m_nTextureName = ""; // current applied texture name - // carcols color id - uchar primary_color = 0; - uchar secondary_color = 0; - std::unordered_map materialProperties; + std::unordered_map m_nMapInfoList; - VehData(CVehicle* veh) + PaintData(CVehicle* pVeh) { - primary_color = veh->m_nPrimaryColor; - secondary_color = veh->m_nSecondaryColor; + m_nCarColors[0] = pVeh->m_nPrimaryColor; + m_nCarColors[1] = pVeh->m_nSecondaryColor; + m_nCarColors[2] = pVeh->m_nTertiaryColor; + m_nCarColors[3] = pVeh->m_nQuaternaryColor; } - void setMaterialColor(RpMaterial* material, RpGeometry* geometry, RwRGBA color, bool filter_mat = false); - void setMaterialTexture(RpMaterial* material, RwTexture* texture, bool filter_mat = false); - void resetMaterialColor(RpMaterial* material); - void resetMaterialTexture(RpMaterial* material); + // Resets applied material colors + void ResetMatColor(RpMaterial* pMat); + + // Resets applied material textures + void ResetMatTexture(RpMaterial* pMat); + + // Sets the material color to provided value + void SetMatColor(RpMaterial* pMat, RpGeometry* pGeo, RwRGBA color); + + // Sets the material to provided texture + void SetMatTexture(RpMaterial* pMat, RwTexture* pTex); }; - static inline VehicleExtendedData m_VehData; - static void NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function func); + VehicleExtendedData m_VehPaint; + + friend class IFeature; + PaintMgr(); + PaintMgr(PaintMgr&); public: - static inline ResourceStore m_TextureData { "textures", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) }; + ResourceStore m_TextureData { "textures", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) }; - static void InjectHooks(); - static void GenerateNodeList(CVehicle* pVeh, std::vector& names_vec, std::string& selected); - static void SetNodeColor(CVehicle* pVeh, std::string node_name, CRGBA color, bool filter_mat = false); - static void SetNodeTexture(CVehicle* pVeh, std::string node_name, std::string texturename, bool filter_mat = false); - static void ResetNodeColor(CVehicle* veh, std::string node_name); - static void ResetNodeTexture(CVehicle* pVeh, std::string node_name); + // Returns internal data structure + PaintData &GetData(CVehicle* pVeh); + + // Resets applied applied colors + void ResetColor(CVehicle* pVeh); + + // Resets appllied textures + void ResetTexture(CVehicle* pVeh); + + // Applies color to vehicle + void SetColor(CVehicle* pVeh, CRGBA color); + + // Sets vehicle carcol colors + void SetCarcols(CVehicle *pVeh, uint primary, uint secondary, uint tertiary, uint quaternary, bool reset = true); + + // Applies texture to vehicle + void SetTexture(CVehicle* pVeh, std::string name); }; + +extern PaintMgr& Paint; \ No newline at end of file diff --git a/src/custom/vehcustmzr.cpp b/src/custom/vehcustmzr.cpp new file mode 100644 index 0000000..5f0681c --- /dev/null +++ b/src/custom/vehcustmzr.cpp @@ -0,0 +1,639 @@ +#include "pch.h" +#include "vehcustmzr.h" +#include "utils/widget.h" +#include "custom/filehandler.h" + +#ifdef GTASA +#include "custom/neon_sa.h" +#include "custom/paint_sa.h" +#endif + +static std::vector m_HandlingFlagNames = // 32 flags +{ + "1G_BOOST", "2G_BOOST", "NPC_ANTI_ROLL", "NPC_NEUTRAL_HANDL", "NO_HANDBRAKE", "STEER_REARWHEELS", + "HB_REARWHEEL_STEER", "ALT_STEER_OPT", + "WHEEL_F_NARROW2", "WHEEL_F_NARROW", "WHEEL_F_WIDE", "WHEEL_F_WIDE2", "WHEEL_R_NARROW2", "WHEEL_R_NARROW", + "WHEEL_R_WIDE", "WHEEL_R_WIDE2", + "HYDRAULIC_GEOM", "HYDRAULIC_INST", "HYDRAULIC_NONE", "NOS_INST", "OFFROAD_ABILITY", "OFFROAD_ABILITY2", + "HALOGEN_LIGHTS", "PROC_REARWHEEL_1ST", + "USE_MAXSP_LIMIT", "LOW_RIDER", "STREET_RACER", "SWINGING_CHASSIS", "Unused 1", "Unused 2", "Unused 3", + "Unused 4" +}; + +static std::vector m_ModelFlagNames = // 32 flags +{ + "IS_VAN", "IS_BUS", "IS_LOW", "IS_BIG", "REVERSE_BONNET", "HANGING_BOOT", "TAILGATE_BOOT", "NOSWING_BOOT", + "NO_DOORS", "TANDEM_SEATS", + "SIT_IN_BOAT", "CONVERTIBLE", "NO_EXHAUST", "DOUBLE_EXHAUST", "NO1FPS_LOOK_BEHIND", "FORCE_DOOR_CHECK", + "AXLE_F_NOTILT", "AXLE_F_SOLID", "AXLE_F_MCPHERSON", + "AXLE_F_REVERSE", "AXLE_R_NOTILT", "AXLE_R_SOLID", "AXLE_R_MCPHERSON", "AXLE_R_REVERSE", "IS_BIKE", "IS_HELI", + "IS_PLANE", "IS_BOAT", "BOUNCE_PANELS", + "DOUBLE_RWHEELS", "FORCE_GROUND_CLEARANCE", "IS_HATCHBAC1K" +}; + +VehCustmzrMgr& VehMod = VehCustmzrMgr::Get(); + +void VehCustmzrMgr::AddComponent(const std::string& component, const bool display_message) +{ + try + { + CPlayerPed* player = FindPlayerPed(); + int icomp = std::stoi(component); + int hveh = CPools::GetVehicleRef(player->m_pVehicle); + + CStreaming::RequestModel(icomp, eStreamingFlags::PRIORITY_REQUEST); + CStreaming::LoadAllRequestedModels(true); + player->m_pVehicle->AddVehicleUpgrade(icomp); + CStreaming::SetModelIsDeletable(icomp); + + if (display_message) + { + Util::SetMessage("Component added"); + } + } + catch (...) + { + Log::Print("Failed to add component to vehicle {}", component); + } +} + + +void VehCustmzrMgr::RemoveComponent(const std::string& component, const bool display_message) +{ + try + { + CPlayerPed* player = FindPlayerPed(); + int icomp = std::stoi(component); + int hveh = CPools::GetVehicleRef(player->m_pVehicle); + + player->m_pVehicle->RemoveVehicleUpgrade(icomp); + + if (display_message) + { + Util::SetMessage("Component removed"); + } + } + catch (...) + { + Log::Print("Failed to remove component from vehicle {}", component); + } +} + +VehCustmzrMgr& VehCustmzr = VehCustmzrMgr::Get(); + +VehCustmzrMgr::VehCustmzrMgr() +{ + m_CustomizeData.m_bAllowRemoveAll = true; + FileHandler::FetchColorData(m_ColorData); + +#ifdef GTASA + FileHandler::FetchHandlingID(m_VehicleIDE); + + Events::processScriptsEvent += [this] + { + uint timer = CTimer::m_snTimeInMilliseconds; + CPlayerPed* pPlayer = FindPlayerPed(); + CVehicle* pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); + + if (pPlayer && Util::IsInCar()) + { + if (m_Neon.m_bRainbowEffect && timer - m_Neon.m_nRainbowTimer > 50) + { + int red, green, blue; + + Util::RainbowValues(red, green, blue, 0.25); + Neon.Install(pVeh, red, green, blue); + m_Neon.m_nRainbowTimer = timer; + } + } + + // Traffic neons + if (m_Neon.m_bApplyOnTraffic && timer - m_Neon.m_nTrafficTimer > 1000) + { + for (CVehicle* veh : CPools::ms_pVehiclePool) + { + int chance = 0; + + if (veh->m_nVehicleClass == CLASS_NORMAL) // Normal + { + chance = Random(1, 20); + } + + if (veh->m_nVehicleClass == CLASS_RICHFAMILY) // Rich family + { + chance = Random(1, 4); + } + + if (veh->m_nVehicleClass == CLASS_EXECUTIVE) // Executive + { + chance = Random(1, 3); + } + + if (chance == 1 && !Neon.IsInstalled(veh) && veh->m_pDriver != pPlayer) + { + Neon.Install(veh, Random(0, 255), Random(0, 255), Random(0, 255)); + } + } + m_Neon.m_nTrafficTimer = timer; + } + + if (m_Nitro.m_bEnabled && FindPlayerVehicle(-1, false)->m_nVehicleSubClass == VEHICLE_AUTOMOBILE) + { + patch::Set(0x969165, 0, true); // All cars have nitro + patch::Set(0x96918B, 0, true); // All taxis have nitro + + if (KeyPressed(VK_LBUTTON)) + { + if (!m_Nitro.m_bCompAdded) + { + AddComponent("1010", false); + m_Nitro.m_bCompAdded = true; + } + } + else + { + if (m_Nitro.m_bCompAdded) + { + RemoveComponent("1010", false); + m_Nitro.m_bCompAdded = false; + } + } + } + }; +#endif +} + +void VehCustmzrMgr::ApplyCustomizations(std::string& cat, std::string& key, std::string& val) +{ + CVehicle *pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); + if (pVeh) + { + int model = pVeh->m_nModelIndex; + std::string name = Util::GetCarName(model); + std::string index = std::format("{}.{}.", name, key); + + // colors + toml::array *temp = m_CustomizeData.m_pData->GetArray((index + "ColorMat").c_str()); + CRGBA col {static_cast(temp->at(0).value_or(0)), static_cast(temp->at(1).value_or(0)), + static_cast(temp->at(2).value_or(0)), static_cast(temp->at(3).value_or(0))}; + Paint.SetColor(pVeh, col); + + temp = m_CustomizeData.m_pData->GetArray((index + "ColorCarcols").c_str()); + Paint.SetCarcols(pVeh, temp->at(0).value_or(0), temp->at(1).value_or(0), temp->at(2).value_or(0), temp->at(3).value_or(0), false); + + std::string texture = m_CustomizeData.m_pData->Get((index + "Texture").c_str(), ""); + if (texture != "") + { + Paint.SetTexture(pVeh, texture); + } + + // neons + temp = m_CustomizeData.m_pData->GetArray((index + "NeonColor").c_str()); + Neon.Install(pVeh, temp->at(0).value_or(0), temp->at(1).value_or(0), temp->at(2).value_or(0)); + bool pulsing = m_CustomizeData.m_pData->Get((index + "NeonPulsing").c_str(), false); + Neon.SetPulsing(pVeh, pulsing); + + // tunes + temp = m_CustomizeData.m_pData->GetArray((index + "Tunes").c_str()); + for (size_t i = 0; i < temp->size(); ++i) + { + int compId = temp->at(i).value_or(-1); + if (compId != -1) + { + AddComponent(std::to_string(compId), false); + } + } + + // paintjob + int count; + int hVeh = CPools::GetVehicleRef(pVeh); + Command(hVeh, &count); + if (count > 0) + { + int val = m_CustomizeData.m_pData->Get((index + "PaintJob").c_str(), -1); + Command(hVeh, val); + } + } +} + +void VehCustmzrMgr::SaveCustomizations() +{ + CVehicle *pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); + if (pVeh) + { + int model = pVeh->m_nModelIndex; + std::string name = Util::GetCarName(model); + std::string index = std::format("{}.{} - {}.", name, name, m_nLabel); + + // Save colors + auto paintData = Paint.Get().GetData(pVeh); + RwRGBA color = {0, 0, 0, 0}; + + // get a proper mat color + for (auto item : paintData.m_nMapInfoList) + { + if (item.second.m_nColor.alpha != 0) + { + color = item.second.m_nColor; + break; + } + } + toml::array ColArr {color.red, color.green, color.blue, color.alpha}; + m_CustomizeData.m_pData->Set((index + "ColorMat").c_str(), ColArr); + + toml::array CarcolsArr {paintData.m_nCarColors[0], paintData.m_nCarColors[1], + paintData.m_nCarColors[2], paintData.m_nCarColors[3]}; + m_CustomizeData.m_pData->Set((index + "ColorCarcols").c_str(), CarcolsArr); + m_CustomizeData.m_pData->Set((index + "Texture").c_str(), paintData.m_nTextureName); + + // Save neons + auto neonData = Neon.Get().GetData(pVeh); + toml::array neonArr {neonData.m_Color.r, neonData.m_Color.g, neonData.m_Color.b}; + m_CustomizeData.m_pData->Set((index + "NeonColor").c_str(), neonArr); + m_CustomizeData.m_pData->Set((index + "NeonPulsing").c_str(), neonData.m_bPulsing); + + + // Save tunes + toml::array tune; + for (size_t i = 0; i < IM_ARRAYSIZE(pVeh->m_anUpgrades); ++i) + { + int compId = pVeh->m_anUpgrades[i]; + if (compId != -1) + { + tune.push_back(compId); + } + } + m_CustomizeData.m_pData->Set((index + "Tunes").c_str(), tune); + + // Save paintjob + int count, curpJob; + int hVeh = CPools::GetVehicleRef(pVeh); + Command(hVeh, &count); + if (count > 0) + { + Command(hVeh, &curpJob); + m_CustomizeData.m_pData->Set((index + "PaintJob").c_str(), curpJob); + } + m_CustomizeData.m_pData->Save(); + m_CustomizeData.UpdateSearchList(); + } +} + +void VehCustmzrMgr::Draw() +{ + CVehicle* pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); + int hVeh = CPools::GetVehicleRef(pVeh); + + if (ImGui::BeginTabItem(TEXT("Vehicle.Color"))) + { +#ifdef GTASA + ImGui::Spacing(); + if (ImGui::Button(TEXT("Vehicle.ResetColor"), ImVec2(Widget::CalcSize()))) + { + Paint.ResetColor(pVeh); + Util::SetMessage(TEXT("Vehicle.ResetColorMSG")); + } + ImGui::Spacing(); + + static float colpicker[3]; + if (ImGui::ColorEdit3(TEXT("Vehicle.ColorPicker"), colpicker)) + { + uchar r = colpicker[0] * 255; + uchar g = colpicker[1] * 255; + uchar b = colpicker[2] * 255; + Paint.SetColor(pVeh, { r, g, b, 255 }); + } +#endif + + ImGui::Spacing(); + + static int colorType = 0; +#ifdef GTASA + ImGui::Combo(TEXT("Vehicle.ColorType"), &colorType, "Primary\0Secondary\0Tertiary\0Quaternary\0"); +#else + ImGui::Combo(TEXT("Vehicle.ColorType"), &colorType, "Primary\0Secondary\0"); +#endif + ImGui::Spacing(); + ImGui::Text(TEXT("Vehicle.SelectPreset")); + ImGui::Spacing(); + + int count = (int)m_ColorData.size(); + + ImVec2 size = Widget::CalcSize(); + int btnsInRow = ImGui::GetWindowContentRegionWidth() / (size.y * 2); + int btnSize = (ImGui::GetWindowContentRegionWidth() - int(ImGuiStyleVar_ItemSpacing) * (btnsInRow - + 0.6 * btnsInRow)) / btnsInRow; + + ImGui::BeginChild("Colorss"); + + for (int colorId = 0; colorId < count; ++colorId) + { + if (Widget::ColorBtn(colorId, m_ColorData[colorId], ImVec2(btnSize, btnSize))) + { + *(uint8_replacement*)(int(pVeh) + BY_GAME(0x433, 0x19F, 0x19B) + colorType + 1) = colorId; + } + + if ((colorId + 1) % btnsInRow != 0) + { + ImGui::SameLine(0.0, 4.0); + } + } + + ImGui::EndChild(); + ImGui::EndTabItem(); + } + +#ifdef GTASA + if (gRenderer != eRenderer::DirectX11) + { + CVehicle* pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); + + if (ImGui::BeginTabItem(TEXT("Vehicle.NeonsTab"))) + { + int model = pVeh->m_nModelIndex; + ImGui::Spacing(); + if (ImGui::Button(TEXT("Vehicle.RemoveNeon"), ImVec2(Widget::CalcSize()))) + { + Neon.Remove(pVeh); + Util::SetMessage(TEXT("Vehicle.RemoveNeonMSG")); + } + + ImGui::Spacing(); + ImGui::Columns(2, NULL, false); + + bool pulsing = Neon.IsPulsingEnabled(pVeh); + if (Widget::Checkbox(TEXT("Vehicle.PulsingNeon"), &pulsing)) + { + Neon.SetPulsing(pVeh, pulsing); + } + + Widget::Checkbox(TEXT("Vehicle.RainbowNeon"), &m_Neon.m_bRainbowEffect, TEXT("Vehicle.RainbowNeonMSG")); + ImGui::NextColumn(); + Widget::Checkbox(TEXT("Vehicle.TrafficNeon"), &m_Neon.m_bApplyOnTraffic, TEXT("Vehicle.TrafficNeonMSG")); + ImGui::Columns(1); + + ImGui::Spacing(); + static float colorPicker[3] {0, 0, 0}; + if (ImGui::ColorEdit3(TEXT("Vehicle.ColorPicker"), colorPicker)) + { + int r = static_cast(colorPicker[0] * 255); + int g = static_cast(colorPicker[1] * 255); + int b = static_cast(colorPicker[2] * 255); + + Neon.Install(pVeh, r, g, b); + } + + ImGui::Spacing(); + ImGui::Text(TEXT("Vehicle.SelectPreset")); + + int count = (int)m_ColorData.size(); + ImVec2 size = Widget::CalcSize(); + int btnsInRow = ImGui::GetWindowContentRegionWidth() / (size.y * 2); + int btnSize = (ImGui::GetWindowContentRegionWidth() - int(ImGuiStyleVar_ItemSpacing) * (btnsInRow - + 0.6 * btnsInRow)) / btnsInRow; + + ImGui::BeginChild("Neonss"); + + for (int color_id = 0; color_id < count; ++color_id) + { + auto& color = m_ColorData[color_id]; + if (Widget::ColorBtn(color_id, color, ImVec2(btnSize, btnSize))) + { + int r = static_cast(color[0] * 255); + int g = static_cast(color[1] * 255); + int b = static_cast(color[2] * 255); + + Neon.Install(pVeh, r, g, b); + } + + if ((color_id + 1) % btnsInRow != 0) + { + ImGui::SameLine(0.0, 4.0); + } + } + + ImGui::EndChild(); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem(TEXT("Vehicle.TextureTab"))) + { + ImGui::Spacing(); + if (ImGui::Button(TEXT("Vehicle.ResetTexture"), ImVec2(Widget::CalcSize()))) + { + Paint.ResetTexture(pVeh); + Util::SetMessage(TEXT("Vehicle.ResetTextureMSG")); + } + ImGui::Spacing(); + + ImGui::Columns(2, NULL, false); + ImGui::NextColumn(); + int maxpjob, curpjob; + Command(hVeh, &maxpjob); + + if (maxpjob > 0) + { + Command(hVeh, &curpjob); + + if (ImGui::ArrowButton("Left", ImGuiDir_Left)) + { + curpjob -= 1; + if (curpjob < -1) + { + curpjob = maxpjob - 1; + } + Command(hVeh, curpjob); + } + ImGui::SameLine(); + ImGui::Text("%s: %d",TEXT("Vehicle.Paintjob"), curpjob+2); + ImGui::SameLine(); + if (ImGui::ArrowButton("Right", ImGuiDir_Right)) + { + curpjob += 1; + if (curpjob > maxpjob) + { + curpjob = -1; + } + Command(hVeh, curpjob); + } + + ImGui::Spacing(); + } + ImGui::Columns(1); + ImGui::Spacing(); + Widget::ImageList(Paint.m_TextureData, + [this](std::string& str) + { + Paint.SetTexture(FindPlayerPed()->m_pVehicle, str); + }, + [](std::string& str) + { + return str; + }, nullptr, [](){}); + + ImGui::EndTabItem(); + } + } + if (ImGui::BeginTabItem(TEXT("Vehicle.TuneTab"))) + { + ImGui::Spacing(); + Widget::ImageList(m_TuneData, + [this](std::string& str) + { + AddComponent(str); + }, + // [](std::string& str) + // { + // RemoveComponent(str); + // }, + [](std::string& str) + { + return str; + }, + [](std::string& str) + { + return ((bool(*)(int, CVehicle*))0x49B010)(std::stoi(str), FindPlayerPed()->m_pVehicle); + }); + + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem(TEXT("Vehicle.HandlingTab"))) + { + ImGui::Spacing(); + // https://github.com/multitheftauto/mtasa-blue/blob/16769b8d1c94e2b9fe6323dcba46d1305f87a190/Client/game_sa/CModelInfoSA.h#L213 + CBaseModelInfo* pInfo = CModelInfo::GetModelInfo(FindPlayerVehicle(-1, false)->m_nModelIndex); + int handlingID = patch::Get((int)pInfo + 74, false); // CBaseModelInfo + 74 = handlingID + tHandlingData *pHandlingData = reinterpret_cast(0xC2B9DC + (handlingID * 224)); // sizeof(tHandlingData) = 224 + + if (ImGui::Button(TEXT("Vehicle.ResetHandling"), ImVec2(Widget::CalcSize(3)))) + { + gHandlingDataMgr.LoadHandlingData(); + Util::SetMessage(TEXT("Vehicle.ResetHandlingMSG")); + } + + ImGui::SameLine(); + + if (ImGui::Button(TEXT("Vehicle.SaveFile"), ImVec2(Widget::CalcSize(3)))) + { + FileHandler::GenerateHandlingFile(pHandlingData, m_VehicleIDE); + Util::SetMessage(TEXT("Vehicle.SaveFileMSG")); + } + + ImGui::SameLine(); + + if (ImGui::Button(TEXT("Vehicle.ReadMore"), ImVec2(Widget::CalcSize(3)))) + { + ShellExecute(NULL, "open", "https://projectcerbera.com/gta/sa/tutorials/handling", NULL, NULL, + SW_SHOWNORMAL); + } + + ImGui::Spacing(); + + ImGui::BeginChild("HandlingChild"); + + std::vector abs{ {TEXT("Vehicle.On"), 1}, {TEXT("Vehicle.Off"), 0} }; + Widget::EditRadioBtnAddr(TEXT("Vehicle.Abs"), (int)&pHandlingData->m_bABS, abs); + + Widget::EditAddr(TEXT("Vehicle.ADM"), (int)&pHandlingData->m_fSuspensionAntiDiveMultiplier, 0.0f, 0.0f, 1.0f); + Widget::EditAddr(TEXT("Vehicle.AnimGroup"), (int)&pHandlingData->m_nAnimGroup, 0, 0, 20); + Widget::EditAddr(TEXT("Vehicle.BrakeBias"), (int)&pHandlingData->m_fBrakeBias, 0.0f, 0.0f, 1.0f); + + // Brake deceleration calculation + float BrakeDeceleration = pHandlingData->m_fBrakeDeceleration * 2500; + Widget::EditAddr(TEXT("Vehicle.BrakeDecel"), (int)&pHandlingData->m_fBrakeDeceleration, 0.0f, 0.0f, 20.0f, 2500.0f); + pHandlingData->m_fBrakeDeceleration = BrakeDeceleration / 2500; + + Widget::EditAddr(TEXT("Vehicle.CemterMassX"), (int)&pHandlingData->m_vecCentreOfMass.x, -10.0f, -10.0f, 10.0f); + Widget::EditAddr(TEXT("Vehicle.CemterMassY"), (int)&pHandlingData->m_vecCentreOfMass.y, -10.0f, -10.0f, 10.0f); + Widget::EditAddr(TEXT("Vehicle.CemterMassZ"), (int)&pHandlingData->m_vecCentreOfMass.z, -10.0f, -10.0f, 10.0f); + + // CDM calculations + float factor = (1.0 / pHandlingData->m_fMass); + float fCDM = pHandlingData->m_fCollisionDamageMultiplier / (2000.0f * factor); + Widget::EditAddr(TEXT("Vehicle.CDM"), (int)&fCDM, 0.0f, 0.0f, 1.0f, 0.3381f); + pHandlingData->m_fCollisionDamageMultiplier = factor * fCDM * 2000.0f; + + Widget::EditAddr(TEXT("Vehicle.DampingLvl"), (int)&pHandlingData->m_fSuspensionDampingLevel, -10.0f, -10.0f, 10.0f); // test later + Widget::EditAddr(TEXT("Vehicle.DragMult"), (int)&pHandlingData->m_fDragMult, 0.0f, 0.0f, 30.0f); + + std::vector drive_type + { + {TEXT("Vehicle.FrontWheelDrive"), 70}, + {TEXT("Vehicle.RearWheelDrive"), 82}, + {TEXT("Vehicle.FourWheelDrive"), 52} + }; + Widget::EditRadioBtnAddr(TEXT("Vehicle.DriveType"), (int)&pHandlingData->m_transmissionData.m_nDriveType, drive_type); + + // Engine acceleration calculation + float fEngineAcceleration = pHandlingData->m_transmissionData.m_fEngineAcceleration * 12500; + Widget::EditAddr(TEXT("Vehicle.EngineAccel"), (int)&fEngineAcceleration, 0.0f, 0.0f, 49.0f, 12500.0f); + pHandlingData->m_transmissionData.m_fEngineAcceleration = fEngineAcceleration / 12500; + + + Widget::EditAddr(TEXT("Vehicle.EngineInertia"), (int)&pHandlingData->m_transmissionData.m_fEngineInertia, 0.1f, 0.1f, 400.0f); + + std::vector engine_type + { + {TEXT("Vehicle.Petrol"), 80}, {TEXT("Vehicle.Diesel"), 68}, {TEXT("Vehicle.Electric"), 69} + }; + Widget::EditRadioBtnAddr(TEXT("Vehicle.EngineType"), (int)&pHandlingData->m_transmissionData.m_nEngineType, engine_type); + + std::vector lights + { + {TEXT("Vehicle.Long"), 0}, {TEXT("Vehicle.Small"), 1}, + {TEXT("Vehicle.Big"), 2}, {TEXT("Vehicle.Tall"), 3} + }; + Widget::EditRadioBtnAddr(TEXT("Vehicle.FrontLights"), (int)&pHandlingData->m_nFrontLights, lights); + + Widget::EditAddr(TEXT("Vehicle.ForceLevel"), (int)&pHandlingData->m_fSuspensionForceLevel, -10.0f, -10.0f, 10.0f); // test later + + Widget::EditBits(TEXT("Vehicle.HandlingFlags"), (int)&pHandlingData->m_nHandlingFlags, m_HandlingFlagNames); + + Widget::EditAddr(TEXT("Vehicle.HighSpeedDamping"), (int)&pHandlingData->m_fSuspensionDampingLevel, -10.0f, -10.0f, 10.0f); // test later + Widget::EditAddr(TEXT("Vehicle.LowerKimit"), (int)&pHandlingData->m_fSuspensionLowerLimit, -10.0f, -10.0f, 10.0f); // test later + Widget::EditAddr(TEXT("Vehicle.Mass"), (int)&pHandlingData->m_fMass, 1.0f, 1.0f, 50000.0f); + + // Max Velocity calculation + int MaxVelocity = pHandlingData->m_transmissionData.m_fMaxGearVelocity / *(float*)0xC2B9BC; + Widget::EditAddr(TEXT("Vehicle.MaxVelocity"), (int)&MaxVelocity, 1.0f, 1.0f, 1000.0f); + pHandlingData->m_transmissionData.m_fMaxGearVelocity = MaxVelocity * (*(float*)0xC2B9BC); + + Widget::EditBits(TEXT("Vehicle.ModelFlags"), (int)&pHandlingData->m_nModelFlags, m_ModelFlagNames); + + Widget::EditAddr(TEXT("Vehicle.MonValue"), (int)&pHandlingData->m_nMonetaryValue, 1, 1, 100000); + Widget::EditAddr(TEXT("Vehicle.NumGears"), (int)&pHandlingData->m_transmissionData.m_nNumberOfGears, 1, 1, 10); + Widget::EditAddr(TEXT("Vehicle.PercentSubmerged"), (int)&pHandlingData->m_nPercentSubmerged, 10, 10, 120); + + Widget::EditRadioBtnAddr(TEXT("Vehicle.RearLights"), (int)&pHandlingData->m_nRearLights, lights); + + Widget::EditAddr(TEXT("Vehicle.SeatOffset"), (int)&pHandlingData->m_fSeatOffsetDistance, 0.0f, 0.0f, 1.0f); + Widget::EditAddr(TEXT("Vehicle.SteeringLock"), (int)&pHandlingData->m_fSteeringLock, 10.0f, 10.0f, 50.0f); + Widget::EditAddr(TEXT("Vehicle.SuspensionBias"), (int)&pHandlingData->m_fSuspensionBiasBetweenFrontAndRear, 0.0f, 0.0f, 1.0f); + Widget::EditAddr(TEXT("Vehicle.TractionBias"), (int)&pHandlingData->m_fTractionBias, 0.0f, 0.0f, 1.0f); + Widget::EditAddr(TEXT("Vehicle.TractionLoss"), (int)&pHandlingData->m_fTractionLoss, 0.0f, 0.0f, 1.0f); + Widget::EditAddr(TEXT("Vehicle.TractionMul"), (int)&pHandlingData->m_fTractionMultiplier, 0.5f, 0.5f, 2.0f); + Widget::EditAddr(TEXT("Vehicle.TurnMass"), (int)&pHandlingData->m_fTurnMass, 20.0f, 20.0f, 1000.0f); // test later + Widget::EditAddr(TEXT("Vehicle.UpperLimit"), (int)&pHandlingData->m_fSuspensionUpperLimit, -1.0f, -1.0f, 1.0f); + + ImGui::EndChild(); + + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem(TEXT("Vehicle.Save"))) + { + ImGui::Spacing(); + ImGui::InputText(TEXT("Menu.Name"), m_nLabel, IM_ARRAYSIZE(m_nLabel)); + ImGui::Spacing(); + if (ImGui::Button(TEXT("Vehicle.CutomizeSave"), Widget::CalcSize())) + { + SaveCustomizations(); + Util::SetMessage(TEXT("Vehicle.CutomizeSaveMSG")); + } + ImGui::Spacing(); + ImGui::Spacing(); + + Widget::DataList(m_CustomizeData, fArg3Wrapper(VehCustmzr.ApplyCustomizations)); + ImGui::EndTabItem(); + } +#endif +} \ No newline at end of file diff --git a/src/custom/vehcustmzr.h b/src/custom/vehcustmzr.h new file mode 100644 index 0000000..4e6f023 --- /dev/null +++ b/src/custom/vehcustmzr.h @@ -0,0 +1,49 @@ +#pragma once +#include "interface/ifeature.hpp" + +/* + VehPaintMgr Class + Handles neon, textures for vehicle +*/ +class VehCustmzrMgr : public IFeature +{ +private: + ResourceStore m_TuneData { "components", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) }; + ResourceStore m_CustomizeData { "customizations", eResourceType::TYPE_TEXT }; + std::vector> m_ColorData; // vehicle color data from carcols.dat + std::map m_VehicleIDE; + char m_nLabel[32] {"Untitled"}; + + struct + { + float m_fColorPicker[3] { 0, 0, 0 }; + bool m_bRainbowEffect; + unsigned int m_nRainbowTimer; + bool m_bApplyOnTraffic; + unsigned int m_nTrafficTimer; + } m_Neon; + + friend class IFeature; + VehCustmzrMgr(); + VehCustmzrMgr(const VehCustmzrMgr&); + + // Add/ Remove vehicle mod + void AddComponent(const std::string& component, bool displayMessage = true); + void RemoveComponent(const std::string& component, bool displayMessage = true); + + // customization + void ApplyCustomizations(std::string& cat, std::string& key, std::string& val); + void SaveCustomizations(); + +public: + struct + { + bool m_bEnabled; + bool m_bCompAdded; + } m_Nitro; + + // Draw color, neon & texture tabs + void Draw(); +}; + +extern VehCustmzrMgr& VehCustmzr; \ No newline at end of file diff --git a/src/custom/vehmod_sa.cpp b/src/custom/vehmod_sa.cpp deleted file mode 100644 index 025a098..0000000 --- a/src/custom/vehmod_sa.cpp +++ /dev/null @@ -1,255 +0,0 @@ -#include "pch.h" -#include "vehmod_sa.h" -#include "utils/widget.h" -#include "filehandler.h" - -static std::vector m_HandlingFlagNames = // 32 flags -{ - "1G_BOOST", "2G_BOOST", "NPC_ANTI_ROLL", "NPC_NEUTRAL_HANDL", "NO_HANDBRAKE", "STEER_REARWHEELS", - "HB_REARWHEEL_STEER", "ALT_STEER_OPT", - "WHEEL_F_NARROW2", "WHEEL_F_NARROW", "WHEEL_F_WIDE", "WHEEL_F_WIDE2", "WHEEL_R_NARROW2", "WHEEL_R_NARROW", - "WHEEL_R_WIDE", "WHEEL_R_WIDE2", - "HYDRAULIC_GEOM", "HYDRAULIC_INST", "HYDRAULIC_NONE", "NOS_INST", "OFFROAD_ABILITY", "OFFROAD_ABILITY2", - "HALOGEN_LIGHTS", "PROC_REARWHEEL_1ST", - "USE_MAXSP_LIMIT", "LOW_RIDER", "STREET_RACER", "SWINGING_CHASSIS", "Unused 1", "Unused 2", "Unused 3", - "Unused 4" -}; - -static std::vector m_ModelFlagNames = // 32 flags -{ - "IS_VAN", "IS_BUS", "IS_LOW", "IS_BIG", "REVERSE_BONNET", "HANGING_BOOT", "TAILGATE_BOOT", "NOSWING_BOOT", - "NO_DOORS", "TANDEM_SEATS", - "SIT_IN_BOAT", "CONVERTIBLE", "NO_EXHAUST", "DOUBLE_EXHAUST", "NO1FPS_LOOK_BEHIND", "FORCE_DOOR_CHECK", - "AXLE_F_NOTILT", "AXLE_F_SOLID", "AXLE_F_MCPHERSON", - "AXLE_F_REVERSE", "AXLE_R_NOTILT", "AXLE_R_SOLID", "AXLE_R_MCPHERSON", "AXLE_R_REVERSE", "IS_BIKE", "IS_HELI", - "IS_PLANE", "IS_BOAT", "BOUNCE_PANELS", - "DOUBLE_RWHEELS", "FORCE_GROUND_CLEARANCE", "IS_HATCHBAC1K" -}; - -VehModMgr& VehMod = VehModMgr::Get(); - -VehModMgr::VehModMgr() -{ - FileHandler::FetchHandlingID(m_VehicleIDE); - - Events::processScriptsEvent += [this]() - { - if (m_Nitro.m_bEnabled && FindPlayerVehicle(-1, false)->m_nVehicleSubClass == VEHICLE_AUTOMOBILE) - { - patch::Set(0x969165, 0, true); // All cars have nitro - patch::Set(0x96918B, 0, true); // All taxis have nitro - - if (KeyPressed(VK_LBUTTON)) - { - if (!m_Nitro.m_bCompAdded) - { - AddComponent("1010", false); - m_Nitro.m_bCompAdded = true; - } - } - else - { - if (m_Nitro.m_bCompAdded) - { - RemoveComponent("1010", false); - m_Nitro.m_bCompAdded = false; - } - } - } - }; -} - -void VehModMgr::AddComponent(const std::string& component, const bool display_message) -{ - try - { - CPlayerPed* player = FindPlayerPed(); - int icomp = std::stoi(component); - int hveh = CPools::GetVehicleRef(player->m_pVehicle); - - CStreaming::RequestModel(icomp, eStreamingFlags::PRIORITY_REQUEST); - CStreaming::LoadAllRequestedModels(true); - player->m_pVehicle->AddVehicleUpgrade(icomp); - CStreaming::SetModelIsDeletable(icomp); - - if (display_message) - { - Util::SetMessage("Component added"); - } - } - catch (...) - { - Log::Print("Failed to add component to vehicle {}", component); - } -} - - -void VehModMgr::RemoveComponent(const std::string& component, const bool display_message) -{ - try - { - CPlayerPed* player = FindPlayerPed(); - int icomp = std::stoi(component); - int hveh = CPools::GetVehicleRef(player->m_pVehicle); - - player->m_pVehicle->RemoveVehicleUpgrade(icomp); - - if (display_message) - { - Util::SetMessage("Component removed"); - } - } - catch (...) - { - Log::Print("Failed to remove component from vehicle {}", component); - } -} - -void VehModMgr::Draw() -{ - if (ImGui::BeginTabItem(TEXT("Vehicle.TuneTab"))) - { - ImGui::Spacing(); - Widget::ImageList(m_TuneData, - [this](std::string& str) - { - AddComponent(str); - }, - // [](std::string& str) - // { - // RemoveComponent(str); - // }, - [](std::string& str) - { - return str; - }, - [](std::string& str) - { - return ((bool(*)(int, CVehicle*))0x49B010)(std::stoi(str), FindPlayerPed()->m_pVehicle); - }); - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem(TEXT("Vehicle.HandlingTab"))) - { - ImGui::Spacing(); - // https://github.com/multitheftauto/mtasa-blue/blob/16769b8d1c94e2b9fe6323dcba46d1305f87a190/Client/game_sa/CModelInfoSA.h#L213 - CBaseModelInfo* pInfo = CModelInfo::GetModelInfo(FindPlayerVehicle(-1, false)->m_nModelIndex); - int handlingID = patch::Get((int)pInfo + 74, false); // CBaseModelInfo + 74 = handlingID - tHandlingData *pHandlingData = reinterpret_cast(0xC2B9DC + (handlingID * 224)); // sizeof(tHandlingData) = 224 - - if (ImGui::Button(TEXT("Vehicle.ResetHandling"), ImVec2(Widget::CalcSize(3)))) - { - gHandlingDataMgr.LoadHandlingData(); - Util::SetMessage(TEXT("Vehicle.ResetHandlingMSG")); - } - - ImGui::SameLine(); - - if (ImGui::Button(TEXT("Vehicle.SaveFile"), ImVec2(Widget::CalcSize(3)))) - { - FileHandler::GenerateHandlingFile(pHandlingData, m_VehicleIDE); - Util::SetMessage(TEXT("Vehicle.SaveFileMSG")); - } - - ImGui::SameLine(); - - if (ImGui::Button(TEXT("Vehicle.ReadMore"), ImVec2(Widget::CalcSize(3)))) - { - ShellExecute(NULL, "open", "https://projectcerbera.com/gta/sa/tutorials/handling", NULL, NULL, - SW_SHOWNORMAL); - } - - ImGui::Spacing(); - - ImGui::BeginChild("HandlingChild"); - - std::vector abs{ {TEXT("Vehicle.On"), 1}, {TEXT("Vehicle.Off"), 0} }; - Widget::EditRadioBtnAddr(TEXT("Vehicle.Abs"), (int)&pHandlingData->m_bABS, abs); - - Widget::EditAddr(TEXT("Vehicle.ADM"), (int)&pHandlingData->m_fSuspensionAntiDiveMultiplier, 0.0f, 0.0f, 1.0f); - Widget::EditAddr(TEXT("Vehicle.AnimGroup"), (int)&pHandlingData->m_nAnimGroup, 0, 0, 20); - Widget::EditAddr(TEXT("Vehicle.BrakeBias"), (int)&pHandlingData->m_fBrakeBias, 0.0f, 0.0f, 1.0f); - - // Brake deceleration calculation - float BrakeDeceleration = pHandlingData->m_fBrakeDeceleration * 2500; - Widget::EditAddr(TEXT("Vehicle.BrakeDecel"), (int)&pHandlingData->m_fBrakeDeceleration, 0.0f, 0.0f, 20.0f, 2500.0f); - pHandlingData->m_fBrakeDeceleration = BrakeDeceleration / 2500; - - Widget::EditAddr(TEXT("Vehicle.CemterMassX"), (int)&pHandlingData->m_vecCentreOfMass.x, -10.0f, -10.0f, 10.0f); - Widget::EditAddr(TEXT("Vehicle.CemterMassY"), (int)&pHandlingData->m_vecCentreOfMass.y, -10.0f, -10.0f, 10.0f); - Widget::EditAddr(TEXT("Vehicle.CemterMassZ"), (int)&pHandlingData->m_vecCentreOfMass.z, -10.0f, -10.0f, 10.0f); - - // CDM calculations - float factor = (1.0 / pHandlingData->m_fMass); - float fCDM = pHandlingData->m_fCollisionDamageMultiplier / (2000.0f * factor); - Widget::EditAddr(TEXT("Vehicle.CDM"), (int)&fCDM, 0.0f, 0.0f, 1.0f, 0.3381f); - pHandlingData->m_fCollisionDamageMultiplier = factor * fCDM * 2000.0f; - - Widget::EditAddr(TEXT("Vehicle.DampingLvl"), (int)&pHandlingData->m_fSuspensionDampingLevel, -10.0f, -10.0f, 10.0f); // test later - Widget::EditAddr(TEXT("Vehicle.DragMult"), (int)&pHandlingData->m_fDragMult, 0.0f, 0.0f, 30.0f); - - std::vector drive_type - { - {TEXT("Vehicle.FrontWheelDrive"), 70}, - {TEXT("Vehicle.RearWheelDrive"), 82}, - {TEXT("Vehicle.FourWheelDrive"), 52} - }; - Widget::EditRadioBtnAddr(TEXT("Vehicle.DriveType"), (int)&pHandlingData->m_transmissionData.m_nDriveType, drive_type); - - // Engine acceleration calculation - float fEngineAcceleration = pHandlingData->m_transmissionData.m_fEngineAcceleration * 12500; - Widget::EditAddr(TEXT("Vehicle.EngineAccel"), (int)&fEngineAcceleration, 0.0f, 0.0f, 49.0f, 12500.0f); - pHandlingData->m_transmissionData.m_fEngineAcceleration = fEngineAcceleration / 12500; - - - Widget::EditAddr(TEXT("Vehicle.EngineInertia"), (int)&pHandlingData->m_transmissionData.m_fEngineInertia, 0.1f, 0.1f, 400.0f); - - std::vector engine_type - { - {TEXT("Vehicle.Petrol"), 80}, {TEXT("Vehicle.Diesel"), 68}, {TEXT("Vehicle.Electric"), 69} - }; - Widget::EditRadioBtnAddr(TEXT("Vehicle.EngineType"), (int)&pHandlingData->m_transmissionData.m_nEngineType, engine_type); - - std::vector lights - { - {TEXT("Vehicle.Long"), 0}, {TEXT("Vehicle.Small"), 1}, - {TEXT("Vehicle.Big"), 2}, {TEXT("Vehicle.Tall"), 3} - }; - Widget::EditRadioBtnAddr(TEXT("Vehicle.FrontLights"), (int)&pHandlingData->m_nFrontLights, lights); - - Widget::EditAddr(TEXT("Vehicle.ForceLevel"), (int)&pHandlingData->m_fSuspensionForceLevel, -10.0f, -10.0f, 10.0f); // test later - - Widget::EditBits(TEXT("Vehicle.HandlingFlags"), (int)&pHandlingData->m_nHandlingFlags, m_HandlingFlagNames); - - Widget::EditAddr(TEXT("Vehicle.HighSpeedDamping"), (int)&pHandlingData->m_fSuspensionDampingLevel, -10.0f, -10.0f, 10.0f); // test later - Widget::EditAddr(TEXT("Vehicle.LowerKimit"), (int)&pHandlingData->m_fSuspensionLowerLimit, -10.0f, -10.0f, 10.0f); // test later - Widget::EditAddr(TEXT("Vehicle.Mass"), (int)&pHandlingData->m_fMass, 1.0f, 1.0f, 50000.0f); - - // Max Velocity calculation - int MaxVelocity = pHandlingData->m_transmissionData.m_fMaxGearVelocity / *(float*)0xC2B9BC; - Widget::EditAddr(TEXT("Vehicle.MaxVelocity"), (int)&MaxVelocity, 1.0f, 1.0f, 1000.0f); - pHandlingData->m_transmissionData.m_fMaxGearVelocity = MaxVelocity * (*(float*)0xC2B9BC); - - Widget::EditBits(TEXT("Vehicle.ModelFlags"), (int)&pHandlingData->m_nModelFlags, m_ModelFlagNames); - - Widget::EditAddr(TEXT("Vehicle.MonValue"), (int)&pHandlingData->m_nMonetaryValue, 1, 1, 100000); - Widget::EditAddr(TEXT("Vehicle.NumGears"), (int)&pHandlingData->m_transmissionData.m_nNumberOfGears, 1, 1, 10); - Widget::EditAddr(TEXT("Vehicle.PercentSubmerged"), (int)&pHandlingData->m_nPercentSubmerged, 10, 10, 120); - - Widget::EditRadioBtnAddr(TEXT("Vehicle.RearLights"), (int)&pHandlingData->m_nRearLights, lights); - - Widget::EditAddr(TEXT("Vehicle.SeatOffset"), (int)&pHandlingData->m_fSeatOffsetDistance, 0.0f, 0.0f, 1.0f); - Widget::EditAddr(TEXT("Vehicle.SteeringLock"), (int)&pHandlingData->m_fSteeringLock, 10.0f, 10.0f, 50.0f); - Widget::EditAddr(TEXT("Vehicle.SuspensionBias"), (int)&pHandlingData->m_fSuspensionBiasBetweenFrontAndRear, 0.0f, 0.0f, 1.0f); - Widget::EditAddr(TEXT("Vehicle.TractionBias"), (int)&pHandlingData->m_fTractionBias, 0.0f, 0.0f, 1.0f); - Widget::EditAddr(TEXT("Vehicle.TractionLoss"), (int)&pHandlingData->m_fTractionLoss, 0.0f, 0.0f, 1.0f); - Widget::EditAddr(TEXT("Vehicle.TractionMul"), (int)&pHandlingData->m_fTractionMultiplier, 0.5f, 0.5f, 2.0f); - Widget::EditAddr(TEXT("Vehicle.TurnMass"), (int)&pHandlingData->m_fTurnMass, 20.0f, 20.0f, 1000.0f); // test later - Widget::EditAddr(TEXT("Vehicle.UpperLimit"), (int)&pHandlingData->m_fSuspensionUpperLimit, -1.0f, -1.0f, 1.0f); - - ImGui::EndChild(); - - ImGui::EndTabItem(); - } -} \ No newline at end of file diff --git a/src/custom/vehmod_sa.h b/src/custom/vehmod_sa.h deleted file mode 100644 index d5eed5e..0000000 --- a/src/custom/vehmod_sa.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "pch.h" -#include "interface/ifeature.hpp" - -/* - VehModMgr Class - Handles tunes & handling -*/ -class VehModMgr : public IFeature -{ -private: - ResourceStore m_TuneData { "components", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) }; - std::map m_VehicleIDE; - - // Add/ Remove vehicle mod - void AddComponent(const std::string& component, bool displayMessage = true); - void RemoveComponent(const std::string& component, bool displayMessage = true); - - friend class IFeature; - VehModMgr(); - VehModMgr(const VehModMgr&); - -public: - struct - { - bool m_bEnabled; - bool m_bCompAdded; - } m_Nitro; - - // Draw custom skins handler code - void Draw(); -}; - -extern VehModMgr& VehMod; \ No newline at end of file diff --git a/src/custom/vehpaint.cpp b/src/custom/vehpaint.cpp deleted file mode 100644 index c274ddb..0000000 --- a/src/custom/vehpaint.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include "pch.h" -#include "vehpaint.h" -#include "utils/widget.h" -#include "custom/filehandler.h" - -#ifdef GTASA -#include "custom/neon_sa.h" -#include "custom/paint_sa.h" -#endif - -VehPaintMgr& VehPaint = VehPaintMgr::Get(); - -VehPaintMgr::VehPaintMgr() -{ - FileHandler::FetchColorData(m_ColorData); - -#ifdef GTASA - Paint::InjectHooks(); - - Events::processScriptsEvent += [this] - { - uint timer = CTimer::m_snTimeInMilliseconds; - CPlayerPed* pPlayer = FindPlayerPed(); - CVehicle* pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); - - if (pPlayer && Util::IsInCar()) - { - if (m_Neon.m_bRainbowEffect && timer - m_Neon.m_nRainbowTimer > 50) - { - int red, green, blue; - - Util::RainbowValues(red, green, blue, 0.25); - Neon.Install(pVeh, red, green, blue); - m_Neon.m_nRainbowTimer = timer; - } - } - - // Traffic neons - if (m_Neon.m_bApplyOnTraffic && timer - m_Neon.m_nTrafficTimer > 1000) - { - for (CVehicle* veh : CPools::ms_pVehiclePool) - { - int chance = 0; - - if (veh->m_nVehicleClass == CLASS_NORMAL) // Normal - { - chance = Random(1, 20); - } - - if (veh->m_nVehicleClass == CLASS_RICHFAMILY) // Rich family - { - chance = Random(1, 4); - } - - if (veh->m_nVehicleClass == CLASS_EXECUTIVE) // Executive - { - chance = Random(1, 3); - } - - if (chance == 1 && !Neon.IsInstalled(veh) && veh->m_pDriver != pPlayer) - { - Neon.Install(veh, Random(0, 255), Random(0, 255), Random(0, 255)); - } - } - m_Neon.m_nTrafficTimer = timer; - } - }; -#endif -} - -void VehPaintMgr::Draw() -{ - CVehicle* pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); - int hVeh = CPools::GetVehicleRef(pVeh); - - if (ImGui::BeginTabItem(TEXT("Vehicle.Color"))) - { -#ifdef GTASA - Paint::GenerateNodeList(pVeh, m_Paint.m_vecNames, m_Paint.m_Selected); - - ImGui::Spacing(); - if (ImGui::Button(TEXT("Vehicle.ResetColor"), ImVec2(Widget::CalcSize()))) - { - Paint::ResetNodeColor(pVeh, m_Paint.m_Selected); - Util::SetMessage(TEXT("Vehicle.ResetColorMSG")); - } - ImGui::Spacing(); - - Widget::ListBox(TEXT("Vehicle.Component"), m_Paint.m_vecNames, m_Paint.m_Selected); - - if (ImGui::ColorEdit3(TEXT("Vehicle.ColorPicker"), m_Paint.m_fColorPicker)) - { - uchar r = m_Paint.m_fColorPicker[0] * 255; - uchar g = m_Paint.m_fColorPicker[1] * 255; - uchar b = m_Paint.m_fColorPicker[2] * 255; - Paint::SetNodeColor(pVeh, m_Paint.m_Selected, { r, g, b, 255 }, m_Paint.m_bMatFilter); - } -#endif - - ImGui::Spacing(); - ImGui::Columns(2, NULL, false); - -#ifdef GTASA - ImGui::Checkbox(TEXT("Vehicle.MatFilter"), &m_Paint.m_bMatFilter); - ImGui::RadioButton(TEXT("Vehicle.Primary"), &m_Paint.m_nRadioButton, 1); - ImGui::RadioButton(TEXT("Vehicle.Secondary"), &m_Paint.m_nRadioButton, 2); - ImGui::NextColumn(); - ImGui::NewLine(); - ImGui::RadioButton(TEXT("Vehicle.Tertiary"), &m_Paint.m_nRadioButton, 3); - ImGui::RadioButton(TEXT("Vehicle.Quaternary"), &m_Paint.m_nRadioButton, 4); -#else - ImGui::RadioButton(TEXT("Vehicle.Primary"), &m_Paint.m_nRadioButton, 1); - ImGui::NextColumn(); - ImGui::RadioButton(TEXT("Vehicle.Secondary"), &m_Paint.m_nRadioButton, 2); -#endif - ImGui::Spacing(); - ImGui::Columns(1); - ImGui::Text(TEXT("Vehicle.SelectPreset")); - ImGui::Spacing(); - - int count = (int)m_ColorData.size(); - - ImVec2 size = Widget::CalcSize(); - int btnsInRow = ImGui::GetWindowContentRegionWidth() / (size.y * 2); - int btnSize = (ImGui::GetWindowContentRegionWidth() - int(ImGuiStyleVar_ItemSpacing) * (btnsInRow - - 0.6 * btnsInRow)) / btnsInRow; - - ImGui::BeginChild("Colorss"); - - for (int colorId = 0; colorId < count; ++colorId) - { - if (Widget::ColorBtn(colorId, m_ColorData[colorId], ImVec2(btnSize, btnSize))) - { - *(uint8_replacement*)(int(pVeh) + BY_GAME(0x433, 0x19F, 0x19B) + m_Paint.m_nRadioButton) = colorId; - } - - if ((colorId + 1) % btnsInRow != 0) - { - ImGui::SameLine(0.0, 4.0); - } - } - - ImGui::EndChild(); - ImGui::EndTabItem(); - } - -#ifdef GTASA - if (gRenderer != eRenderer::DirectX11) - { - CVehicle* pVeh = BY_GAME(FindPlayerVehicle(-1, false), FindPlayerVehicle(), FindPlayerVehicle()); - - if (ImGui::BeginTabItem(TEXT("Vehicle.NeonsTab"))) - { - int model = pVeh->m_nModelIndex; - ImGui::Spacing(); - if (ImGui::Button(TEXT("Vehicle.RemoveNeon"), ImVec2(Widget::CalcSize()))) - { - Neon.Remove(pVeh); - Util::SetMessage(TEXT("Vehicle.RemoveNeonMSG")); - } - - ImGui::Spacing(); - ImGui::Columns(2, NULL, false); - - bool pulsing = Neon.IsPulsingEnabled(pVeh); - if (Widget::Checkbox(TEXT("Vehicle.PulsingNeon"), &pulsing)) - { - Neon.SetPulsing(pVeh, pulsing); - } - - Widget::Checkbox(TEXT("Vehicle.RainbowNeon"), &m_Neon.m_bRainbowEffect, TEXT("Vehicle.RainbowNeonMSG")); - ImGui::NextColumn(); - Widget::Checkbox(TEXT("Vehicle.TrafficNeon"), &m_Neon.m_bApplyOnTraffic, TEXT("Vehicle.TrafficNeonMSG")); - ImGui::Columns(1); - - ImGui::Spacing(); - - if (ImGui::ColorEdit3(TEXT("Vehicle.ColorPicker"), m_Neon.m_fColorPicker)) - { - int r = static_cast(m_Neon.m_fColorPicker[0] * 255); - int g = static_cast(m_Neon.m_fColorPicker[1] * 255); - int b = static_cast(m_Neon.m_fColorPicker[2] * 255); - - Neon.Install(pVeh, r, g, b); - } - - - ImGui::Spacing(); - ImGui::Text(TEXT("Vehicle.SelectPreset")); - - int count = (int)m_ColorData.size(); - ImVec2 size = Widget::CalcSize(); - int btnsInRow = ImGui::GetWindowContentRegionWidth() / (size.y * 2); - int btnSize = (ImGui::GetWindowContentRegionWidth() - int(ImGuiStyleVar_ItemSpacing) * (btnsInRow - - 0.6 * btnsInRow)) / btnsInRow; - - ImGui::BeginChild("Neonss"); - - for (int color_id = 0; color_id < count; ++color_id) - { - auto& color = m_ColorData[color_id]; - if (Widget::ColorBtn(color_id, color, ImVec2(btnSize, btnSize))) - { - int r = static_cast(color[0] * 255); - int g = static_cast(color[1] * 255); - int b = static_cast(color[2] * 255); - - Neon.Install(pVeh, r, g, b); - } - - if ((color_id + 1) % btnsInRow != 0) - { - ImGui::SameLine(0.0, 4.0); - } - } - - ImGui::EndChild(); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(TEXT("Vehicle.TextureTab"))) - { - Paint::GenerateNodeList(pVeh, m_Paint.m_vecNames, m_Paint.m_Selected); - - ImGui::Spacing(); - if (ImGui::Button(TEXT("Vehicle.ResetTexture"), ImVec2(Widget::CalcSize()))) - { - Paint::ResetNodeTexture(pVeh, m_Paint.m_Selected); - Util::SetMessage(TEXT("Vehicle.ResetTextureMSG")); - } - ImGui::Spacing(); - - Widget::ListBox(TEXT("Vehicle.Component"), m_Paint.m_vecNames, m_Paint.m_Selected); - ImGui::Spacing(); - - ImGui::Columns(2, NULL, false); - ImGui::Checkbox(TEXT("Vehicle.MatFilter"), &m_Paint.m_bMatFilter); - ImGui::NextColumn(); - int maxpjob, curpjob; - Command(hVeh, &maxpjob); - - if (maxpjob > 0) - { - Command(hVeh, &curpjob); - - if (ImGui::ArrowButton("Left", ImGuiDir_Left)) - { - curpjob -= 1; - if (curpjob < -1) - { - curpjob = maxpjob - 1; - } - Command(hVeh, curpjob); - } - ImGui::SameLine(); - ImGui::Text("%s: %d",TEXT("Vehicle.Paintjob"), curpjob+2); - ImGui::SameLine(); - if (ImGui::ArrowButton("Right", ImGuiDir_Right)) - { - curpjob += 1; - if (curpjob > maxpjob) - { - curpjob = -1; - } - Command(hVeh, curpjob); - } - - ImGui::Spacing(); - } - ImGui::Columns(1); - ImGui::Spacing(); - Widget::ImageList(Paint::m_TextureData, - [this](std::string& str) - { - Paint::SetNodeTexture(FindPlayerPed()->m_pVehicle, m_Paint.m_Selected, str, - m_Paint.m_bMatFilter); - }, - [](std::string& str) - { - return str; - }, nullptr, [](){}); - - ImGui::EndTabItem(); - } - } -#endif -} \ No newline at end of file diff --git a/src/custom/vehpaint.h b/src/custom/vehpaint.h deleted file mode 100644 index eeab1bc..0000000 --- a/src/custom/vehpaint.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "interface/ifeature.hpp" - -/* - VehPaintMgr Class - Handles neon, textures for vehicle -*/ -class VehPaintMgr : public IFeature -{ -private: - std::vector> m_ColorData; // vehicle color data from carcols.dat - - struct - { - float m_fColorPicker[3] { 0, 0, 0 }; - bool m_bRainbowEffect; - unsigned int m_nRainbowTimer; - bool m_bApplyOnTraffic; - unsigned int m_nTrafficTimer; - } m_Neon; - - struct - { - bool m_bMatFilter = true; - int m_nRadioButton = 1; - float m_fColorPicker[3] { 0, 0, 0 }; - std::vector m_vecNames{"Default"}; - std::string m_Selected = "Default"; - } m_Paint; - - - friend class IFeature; - VehPaintMgr(); - VehPaintMgr(const VehPaintMgr&); - -public: - - // Draw color, neon & texture tabs - void Draw(); -}; - -extern VehPaintMgr& VehPaint; \ No newline at end of file diff --git a/src/pages/menu.cpp b/src/pages/menu.cpp index 9ec4c0a..1c23da9 100644 --- a/src/pages/menu.cpp +++ b/src/pages/menu.cpp @@ -347,12 +347,6 @@ void MenuPage::Draw() ImGui::TableNextColumn(); ImGui::Text("III & VC animation code"); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("DKPac22"); - ImGui::TableNextColumn(); - ImGui::Text("Plugin SDK, vehicle texture code"); - ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Junior-Djjr"); diff --git a/src/pages/vehicle.cpp b/src/pages/vehicle.cpp index 49fa0b0..26757bc 100644 --- a/src/pages/vehicle.cpp +++ b/src/pages/vehicle.cpp @@ -7,14 +7,9 @@ #include "utils/widget.h" #include "utils/util.h" #include "custom/filehandler.h" -#include "custom/vehpaint.h" +#include "custom/vehcustmzr.h" #include "custom/autodrive.h" -#ifdef GTASA -#include -#include "custom/vehmod_sa.h" -#endif - VehiclePage& vehiclePage = VehiclePage::Get(); VehiclePage::VehiclePage() : IPage(ePageID::Vehicle, "Window.VehiclePage", true) @@ -591,7 +586,7 @@ void VehiclePage::Draw() Widget::CheckboxAddr(TEXT("Vehicle.PerfectHandling"), 0x96914C); Widget::CheckboxAddr(TEXT("Vehicle.TankMode"), 0x969164); - Widget::Checkbox(TEXT("Vehicle.InfNitro"), &VehMod.m_Nitro.m_bEnabled, TEXT("Vehicle.InfNitroTip")); + Widget::Checkbox(TEXT("Vehicle.InfNitro"), &VehCustmzr.m_Nitro.m_bEnabled, TEXT("Vehicle.InfNitroTip")); if (Widget::Checkbox(TEXT("Vehicle.FlipNoBurn"), &m_bVehFlipNoBurn, TEXT("Vehicle.FlipNoBurnTip"))) { // MixSets (Link2012) @@ -1006,18 +1001,28 @@ void VehiclePage::Draw() if (pPlayer->m_pVehicle && bPlayerInCar) { CVehicle* veh = FindPlayerPed()->m_pVehicle; - int hveh = CPools::GetVehicleRef(veh); + int hveh = CPools::GetVehicleRef(veh); +#ifdef GTASA + if (ImGui::BeginTabItem(TEXT("Vehicle.Customize"))) + { + ImGui::Spacing(); + + if (ImGui::BeginTabBar("CustomizeTab")) + { + VehCustmzr.Draw(); + ImGui::EndTabBar(); + } + ImGui::EndTabItem(); + } +#else + VehCustmzr.Draw(); +#endif if (ImGui::BeginTabItem(TEXT("Vehicle.AutoDrive"))) { ImGui::Spacing(); AutoDrive.Draw(); ImGui::EndTabItem(); } - - VehPaint.Draw(); -#ifdef GTASA - VehMod.Draw(); -#endif } ImGui::EndTabBar(); } diff --git a/src/utils/datastore.cpp b/src/utils/datastore.cpp index 9991fc3..8691fdc 100644 --- a/src/utils/datastore.cpp +++ b/src/utils/datastore.cpp @@ -65,7 +65,11 @@ void DataStore::RemoveKey(const char* key, const char* entry) noexcept { if (pTable) { - (*pTable).at_path(key).as_table()->erase(entry); + Table *tbl = pTable->at_path(key).as_table(); + if (tbl) + { + tbl->erase(entry); + } } } diff --git a/src/utils/datastore.h b/src/utils/datastore.h index 5bd88e4..c807b33 100644 --- a/src/utils/datastore.h +++ b/src/utils/datastore.h @@ -17,7 +17,8 @@ private: std::string path; public: - typedef toml::table Table; + using Table = toml::table; + using Array = toml::array; DataStore(const char* fileName, bool isPathPredefined = false) noexcept; @@ -61,6 +62,25 @@ public: return defaultVal; } + Array* GetArray(const char* key) noexcept + { + if (pTable) + { + Array *tbl = (*pTable).at_path(key).as_array(); + if (tbl) + { + return tbl; + } + else + { + pTable->insert(key, Table()); + return (*pTable).at_path(key).as_array(); + + } + } + return nullptr; + } + Table* GetTable(const char* key) noexcept { if (pTable) diff --git a/src/utils/resourcestore.cpp b/src/utils/resourcestore.cpp index 02646ff..12e80e6 100644 --- a/src/utils/resourcestore.cpp +++ b/src/utils/resourcestore.cpp @@ -227,7 +227,7 @@ void ResourceStore::UpdateSearchList(bool favourites, fRtnArg1_t getNameFunc, fR lookup.key = std::string(key.str()); if (m_Filter.PassFilter(lookup.key.c_str())) { - lookup.cat = "Favourites"; + lookup.cat = cat.str(); lookup.val = val.value_or("Unkonwn"); m_nSearchList.push_back(std::move(lookup)); } diff --git a/src/utils/resourcestore.h b/src/utils/resourcestore.h index 2a8aeb3..10c89ff 100644 --- a/src/utils/resourcestore.h +++ b/src/utils/resourcestore.h @@ -82,6 +82,7 @@ public: eResourceType m_Type; ImVec2 m_ImageSize; std::vector> m_ImagesList; + bool m_bAllowRemoveAll = false; // allows removing items from non custom categories ResourceStore(const char* text, eResourceType type = TYPE_IMAGE, ImVec2 imageSize = ImVec2(64, 64)); diff --git a/src/utils/widget.cpp b/src/utils/widget.cpp index 1ddb868..4e1b3e8 100644 --- a/src/utils/widget.cpp +++ b/src/utils/widget.cpp @@ -146,9 +146,9 @@ void DrawClippedList(ResourceStore& data, fArg3_t clickFunc, bool favourites, bo } if (!favourites && ImGui::MenuItem(TEXT("Menu.Remove"))) { - if (contextMenu.root == "Custom") + if (contextMenu.root == "Custom" || data.m_bAllowRemoveAll) { - data.m_pData->RemoveKey("Custom", contextMenu.key.c_str()); + data.m_pData->RemoveKey(contextMenu.root.c_str(), contextMenu.key.c_str()); data.m_pData->RemoveKey("Favourites", contextMenu.key.c_str()); data.m_pData->Save(); data.UpdateSearchList();