Add saving paintjob, texture, color #73 refactore code

This commit is contained in:
Grinch_ 2022-09-23 20:48:08 +06:00
parent c4d7ec9318
commit 79727292a8
18 changed files with 1036 additions and 963 deletions

View File

@ -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"

View File

@ -183,8 +183,6 @@ static RwTexture* LoadTextureFromMemory(char* data, unsigned int size)
NeonMgr::NeonMgr()
{
Events::vehicleRenderEvent += [this](CVehicle* pVeh)
{
if (m_bEnabled)
{
NeonData* data = &m_VehNeon.Get(pVeh);
if (data->m_bNeonInstalled && !pVeh->IsUpsideDown())
@ -225,7 +223,6 @@ NeonMgr::NeonMgr()
}
}
}
}
};
}
@ -269,3 +266,8 @@ void NeonMgr::Remove(CVehicle* pVeh)
{
m_VehNeon.Get(pVeh).m_bNeonInstalled = false;
}
NeonMgr::NeonData NeonMgr::GetData(CVehicle *pVeh)
{
return m_VehNeon.Get(pVeh);
}

View File

@ -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);

View File

@ -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 <NodeName.h>
#include "../../include/kiero/minhook/MinHook.h"
void Paint::InjectHooks()
{
static bool init;
PaintMgr& Paint = PaintMgr::Get();
if (init)
// This works with silentpatch
static auto oEntityRender = (void(__fastcall*)(CVehicle*))0x534310;
void __fastcall hkEntityRender(CVehicle* pVeh)
{
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)
{
if (it.second._recolor)
{
it.second._originalColor = it.first->color;
it.first->color = it.second._color;
it.second._originalGeometryFlags = it.second._geometry->flags;
it.second._geometry->flags |= rpGEOMETRYMODULATEMATERIALCOLOR;
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;
}
if (it.second._retexture)
/*
Applying our custom material colors here
*/
for (auto& it : data.m_nMapInfoList)
{
auto tex = it.second._texture;
if (it.second.m_bRecolor)
{
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._originalTexture = it.first->texture;
it.second.m_pOriginalTexture = 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<AddressList<0x55332A, H_CALL>, PRIORITY_BEFORE, ArgPickN<CVehicle*, 0>, 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<AddressList<0x55332A, H_CALL>, PRIORITY_BEFORE, ArgPickN<CVehicle*, 0>, 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)
// stop trigger reset
if (!reset)
{
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))
{
matProps._recolor = true;
matProps._color = color;
matProps._geometry = geometry;
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)
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<void(RwFrame*)> func)
PaintMgr::PaintData& PaintMgr::GetData(CVehicle *pVeh)
{
return m_VehPaint.Get(pVeh);
}
static void NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function<void(RwFrame*)> 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<std::string>& names_vec, std::string& selected)
struct BindData
{
static int vehModel = 0;
if (vehModel == pVeh->m_nModelIndex)
void *pData;
CRGBA color;
RwTexture* pTexture;
};
void PaintMgr::SetColor(CVehicle* pVeh, CRGBA color)
{
return;
}
// 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)
{
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
{
const std::string name = GetFrameNodeName(frame);
struct ST
{
CRGBA _color;
bool _filter;
} st;
st._color = color;
st._filter = filter_mat;
if (node_name == "Default" || node_name == name)
RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent;
NodeWrapperRecursive(pFrame, pVeh, [&](RwFrame* frame)
{
BindData bindData { &m_VehPaint.Get(pVeh), color };
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
{
if (object->type == rpATOMIC)
{
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
ST* st = reinterpret_cast<ST*>(data);
CRGBA* color = &st->_color;
bool filter_mat = st->_filter;
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
BindData* bind = reinterpret_cast<BindData*>(data);
CRGBA color = bind->color;
PaintData* pData = reinterpret_cast<PaintData*>(bind->pData);
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);
}
}, &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
{
RwTexture* _tex;
bool _filter;
} st;
st._tex = texture;
st._filter = filter_mat;
if (node_name == "Default" || node_name == name)
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*
{
if (object->type == rpATOMIC)
{
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
ST* st = reinterpret_cast<ST*>(data);
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
BindData* bind = reinterpret_cast<BindData*>(data);
RwTexture *pTex = bind->pTexture;
PaintData* pData = reinterpret_cast<PaintData*>(bind->pData);
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);
}
}, &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*
{
if (object->type == rpATOMIC)
{
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(object);
PaintData* pData = reinterpret_cast<PaintData*>(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);
}
}, &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*
{
if (object->type == rpATOMIC)
{
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(object);
PaintData* pData = reinterpret_cast<PaintData*>(data);
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]);
for (int i = 0; i < pAtomic->geometry->matList.numMaterials; ++i)
{
pData->ResetMatTexture(pAtomic->geometry->matList.materials[i]);
}
}
return object;
}, nullptr);
}
}, &m_VehPaint.Get(pVeh));
});
m_VehPaint.Get(pVeh).m_nTextureName = "";
}

View File

@ -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 <vector>
#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<PaintMgr>
{
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)
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
std::unordered_map<RpMaterial*, MatInfo> m_nMapInfoList;
PaintData(CVehicle* pVeh)
{
m_nCarColors[0] = pVeh->m_nPrimaryColor;
m_nCarColors[1] = pVeh->m_nSecondaryColor;
m_nCarColors[2] = pVeh->m_nTertiaryColor;
m_nCarColors[3] = pVeh->m_nQuaternaryColor;
}
RwRGBA _color;
RwTexture* _texture;
bool _recolor;
bool _retexture;
RpGeometry* _geometry;
RwRGBA _originalColor;
RwTexture* _originalTexture;
RwInt32 _originalGeometryFlags;
// 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);
};
VehicleExtendedData<PaintData> m_VehPaint;
// carcols color id
uchar primary_color = 0;
uchar secondary_color = 0;
std::unordered_map<RpMaterial*, MaterialProperties> materialProperties;
VehData(CVehicle* veh)
{
primary_color = veh->m_nPrimaryColor;
secondary_color = veh->m_nSecondaryColor;
}
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);
};
static inline VehicleExtendedData<VehData> m_VehData;
static void NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function<void(RwFrame*)> func);
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<std::string>& 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;

639
src/custom/vehcustmzr.cpp Normal file
View File

@ -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<std::string> 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<std::string> 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<eLogLevel::Warn>("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<eLogLevel::Warn>("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<BYTE>(0x969165, 0, true); // All cars have nitro
patch::Set<BYTE>(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<uchar>(temp->at(0).value_or(0)), static_cast<uchar>(temp->at(1).value_or(0)),
static_cast<uchar>(temp->at(2).value_or(0)), static_cast<uchar>(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<Commands::GET_NUM_AVAILABLE_PAINTJOBS>(hVeh, &count);
if (count > 0)
{
int val = m_CustomizeData.m_pData->Get((index + "PaintJob").c_str(), -1);
Command<Commands::GIVE_VEHICLE_PAINTJOB>(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<Commands::GET_NUM_AVAILABLE_PAINTJOBS>(hVeh, &count);
if (count > 0)
{
Command<Commands::GET_CURRENT_VEHICLE_PAINTJOB>(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<int>(colorPicker[0] * 255);
int g = static_cast<int>(colorPicker[1] * 255);
int b = static_cast<int>(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<int>(color[0] * 255);
int g = static_cast<int>(color[1] * 255);
int b = static_cast<int>(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<Commands::GET_NUM_AVAILABLE_PAINTJOBS>(hVeh, &maxpjob);
if (maxpjob > 0)
{
Command<Commands::GET_CURRENT_VEHICLE_PAINTJOB>(hVeh, &curpjob);
if (ImGui::ArrowButton("Left", ImGuiDir_Left))
{
curpjob -= 1;
if (curpjob < -1)
{
curpjob = maxpjob - 1;
}
Command<Commands::GIVE_VEHICLE_PAINTJOB>(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<Commands::GIVE_VEHICLE_PAINTJOB>(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<WORD>((int)pInfo + 74, false); // CBaseModelInfo + 74 = handlingID
tHandlingData *pHandlingData = reinterpret_cast<tHandlingData*>(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<Widget::BindInfo> 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<BYTE>(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<Widget::BindInfo> 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<Widget::BindInfo> 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<Widget::BindInfo> 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<int>(TEXT("Vehicle.MonValue"), (int)&pHandlingData->m_nMonetaryValue, 1, 1, 100000);
Widget::EditAddr<BYTE>(TEXT("Vehicle.NumGears"), (int)&pHandlingData->m_transmissionData.m_nNumberOfGears, 1, 1, 10);
Widget::EditAddr<BYTE>(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
}

49
src/custom/vehcustmzr.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include "interface/ifeature.hpp"
/*
VehPaintMgr Class
Handles neon, textures for vehicle
*/
class VehCustmzrMgr : public IFeature<VehCustmzrMgr>
{
private:
ResourceStore m_TuneData { "components", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) };
ResourceStore m_CustomizeData { "customizations", eResourceType::TYPE_TEXT };
std::vector<std::vector<float>> m_ColorData; // vehicle color data from carcols.dat
std::map<int, std::string> 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;

View File

@ -1,255 +0,0 @@
#include "pch.h"
#include "vehmod_sa.h"
#include "utils/widget.h"
#include "filehandler.h"
static std::vector<std::string> 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<std::string> 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<BYTE>(0x969165, 0, true); // All cars have nitro
patch::Set<BYTE>(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<eLogLevel::Warn>("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<eLogLevel::Warn>("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<WORD>((int)pInfo + 74, false); // CBaseModelInfo + 74 = handlingID
tHandlingData *pHandlingData = reinterpret_cast<tHandlingData*>(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<Widget::BindInfo> 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<BYTE>(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<Widget::BindInfo> 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<Widget::BindInfo> 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<Widget::BindInfo> 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<int>(TEXT("Vehicle.MonValue"), (int)&pHandlingData->m_nMonetaryValue, 1, 1, 100000);
Widget::EditAddr<BYTE>(TEXT("Vehicle.NumGears"), (int)&pHandlingData->m_transmissionData.m_nNumberOfGears, 1, 1, 10);
Widget::EditAddr<BYTE>(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();
}
}

View File

@ -1,34 +0,0 @@
#pragma once
#include "pch.h"
#include "interface/ifeature.hpp"
/*
VehModMgr Class
Handles tunes & handling
*/
class VehModMgr : public IFeature<VehModMgr>
{
private:
ResourceStore m_TuneData { "components", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) };
std::map<int, std::string> 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;

View File

@ -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<int>(m_Neon.m_fColorPicker[0] * 255);
int g = static_cast<int>(m_Neon.m_fColorPicker[1] * 255);
int b = static_cast<int>(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<int>(color[0] * 255);
int g = static_cast<int>(color[1] * 255);
int b = static_cast<int>(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<Commands::GET_NUM_AVAILABLE_PAINTJOBS>(hVeh, &maxpjob);
if (maxpjob > 0)
{
Command<Commands::GET_CURRENT_VEHICLE_PAINTJOB>(hVeh, &curpjob);
if (ImGui::ArrowButton("Left", ImGuiDir_Left))
{
curpjob -= 1;
if (curpjob < -1)
{
curpjob = maxpjob - 1;
}
Command<Commands::GIVE_VEHICLE_PAINTJOB>(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<Commands::GIVE_VEHICLE_PAINTJOB>(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
}

View File

@ -1,42 +0,0 @@
#pragma once
#include "interface/ifeature.hpp"
/*
VehPaintMgr Class
Handles neon, textures for vehicle
*/
class VehPaintMgr : public IFeature<VehPaintMgr>
{
private:
std::vector<std::vector<float>> 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<std::string> 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;

View File

@ -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");

View File

@ -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 <tHandlingData.h>
#include "custom/vehmod_sa.h"
#endif
VehiclePage& vehiclePage = VehiclePage::Get();
VehiclePage::VehiclePage()
: IPage<VehiclePage>(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)
@ -1007,17 +1002,27 @@ void VehiclePage::Draw()
{
CVehicle* veh = FindPlayerPed()->m_pVehicle;
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();
}

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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<std::string>("Unkonwn");
m_nSearchList.push_back(std::move(lookup));
}

View File

@ -82,6 +82,7 @@ public:
eResourceType m_Type;
ImVec2 m_ImageSize;
std::vector<std::unique_ptr<TextureResource>> 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));

View File

@ -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();