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" VerySunny = "Very sunny"
Weather = "Weather" Weather = "Weather"
WeatherID = "Weather ID" 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] [Main]
TranslationLanguage = "English" TranslationLanguage = "English"
@ -472,9 +472,16 @@ CemterMassZ = "Centre of mass Z"
Cheap = "Cheap" Cheap = "Cheap"
Color = "Color" Color = "Color"
ColorPicker = "Color picker" ColorPicker = "Color picker"
ColorType = "Color type"
ColProof = "Collision proof" ColProof = "Collision proof"
Component = "Component" Component = "Component"
Country = "Country" Country = "Country"
Customize = "Customize"
CustomizeLabel = "Customization label"
CutomizeClear = "Clear customizations"
CutomizeClearMSG = "Cleared customizations"
CutomizeSave = "Save customizations"
CutomizeSaveMSG = "Saved customizations"
Damage = "Damage" Damage = "Damage"
DampingLvl = "Damping level" DampingLvl = "Damping level"
DensityMul = "Density multiplier" DensityMul = "Density multiplier"
@ -583,6 +590,7 @@ ResetHandling = "Reset handling"
ResetHandlingMSG = "Handling reset successfully" ResetHandlingMSG = "Handling reset successfully"
ResetTexture = "Reset texture" ResetTexture = "Reset texture"
ResetTextureMSG = "Texture reset successfully" ResetTextureMSG = "Texture reset successfully"
Save = "Save"
SaveFile = "Save to file" SaveFile = "Save to file"
SaveFileMSG = "Handling saved successfully" SaveFileMSG = "Handling saved successfully"
SeatOffset = "Seat offset" SeatOffset = "Seat offset"

View File

@ -184,45 +184,42 @@ NeonMgr::NeonMgr()
{ {
Events::vehicleRenderEvent += [this](CVehicle* pVeh) 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 (!m_pNeonTexture)
if (data->m_bNeonInstalled && !pVeh->IsUpsideDown())
{ {
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, &center, 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; if (data->m_fVal > 0.3f)
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, &center, 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; data->m_bIncrement = false;
}
if (data->m_fVal < 0.0f) if (data->m_bIncrement)
{ {
data->m_bIncrement = true; data->m_fVal += 0.0003f * delta;
} }
else
if (data->m_fVal > 0.3f) {
{ data->m_fVal -= 0.0003f * delta;
data->m_bIncrement = false;
}
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) void NeonMgr::Remove(CVehicle* pVeh)
{ {
m_VehNeon.Get(pVeh).m_bNeonInstalled = false; 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: public:
// Returns internal neon data
NeonData GetData(CVehicle *pVeh);
// Installs neons with color // Installs neons with color
void Install(CVehicle* veh, int r, int g, int b); 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 "pch.h"
#include "paint_sa.h" #include "paint_sa.h"
#include "utils/util.h" #include "utils/util.h"
#include <NodeName.h> #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 (!pVeh || pVeh->m_nType != ENTITY_TYPE_VEHICLE)
if (init)
{ {
oEntityRender(pVeh);
return; 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); for (auto& it : data.m_nMapInfoList)
// reset custom color if color id changed
if (pVeh->m_nPrimaryColor != data.primary_color
|| pVeh->m_nSecondaryColor != data.secondary_color)
{ {
for (auto& it : data.materialProperties) data.ResetMatColor(it.first);
data.resetMaterialColor(it.first);
data.primary_color = pVeh->m_nPrimaryColor;
data.secondary_color = pVeh->m_nSecondaryColor;
} }
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.second.m_pOriginalTexture = it.first->texture;
it.first->color = it.second._color; it.first->texture = tex;
it.second._originalGeometryFlags = it.second._geometry->flags;
it.second._geometry->flags |= rpGEOMETRYMODULATEMATERIALCOLOR;
} }
if (it.second._retexture) else
{ {
auto tex = it.second._texture; it.second.m_bRetexture = false;
if (tex) }
{ }
it.second._originalTexture = it.first->texture; }
it.first->texture = tex; oEntityRender(pVeh);
} };
else
{ PaintMgr::PaintMgr()
it.second._retexture = false; {
} 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 // stop trigger reset
|| (material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00) if (!reset)
|| (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
{ {
matProps._recolor = true; auto& data = Paint.GetData(pVeh);
matProps._color = color; data.m_nCarColors[0] = *(uint8_replacement*)(int(pVeh) + 0x433 + 1);
matProps._geometry = geometry; 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]; auto& matInfo = m_nMapInfoList[material];
if ((material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00)
if (!filter_mat || (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
|| (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; matInfo.m_bRetexture = true;
matProps._texture = texture; matInfo.m_pTexture = texture;
} }
} }
void Paint::VehData::resetMaterialColor(RpMaterial* material) void PaintMgr::PaintData::ResetMatColor(RpMaterial* material)
{ {
auto& matProps = materialProperties[material]; auto& matInfo = m_nMapInfoList[material];
matProps._recolor = false; matInfo.m_bRecolor = false;
matProps._color = {0, 0, 0, 0}; matInfo.m_nColor = {0, 0, 0, 0};
} }
void Paint::VehData::resetMaterialTexture(RpMaterial* material) void PaintMgr::PaintData::ResetMatTexture(RpMaterial* material)
{ {
auto& matProps = materialProperties[material]; auto& matInfo = m_nMapInfoList[material];
matProps._retexture = false; matInfo.m_bRetexture = false;
matProps._texture = nullptr; 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) if (frame)
{ {
func(frame); func(frame);
if (RwFrame* newFrame = frame->child) if (RwFrame* newFrame = frame->child)
{
NodeWrapperRecursive(newFrame, pVeh, func); NodeWrapperRecursive(newFrame, pVeh, func);
}
if (RwFrame* newFrame = frame->next) if (RwFrame* newFrame = frame->next)
{
NodeWrapperRecursive(newFrame, pVeh, func); NodeWrapperRecursive(newFrame, pVeh, func);
}
} }
return; return;
} }
void Paint::GenerateNodeList(CVehicle* pVeh, std::vector<std::string>& names_vec, std::string& selected) struct BindData
{ {
static int vehModel = 0; void *pData;
if (vehModel == pVeh->m_nModelIndex) CRGBA color;
{ RwTexture* pTexture;
return; };
}
// reset to default void PaintMgr::SetColor(CVehicle* pVeh, CRGBA color)
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; RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent;
NodeWrapperRecursive(pFrame, pVeh, [&](RwFrame* frame)
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
{ {
const std::string name = GetFrameNodeName(frame); BindData bindData { &m_VehPaint.Get(pVeh), color };
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
struct ST
{ {
CRGBA _color; if (object->type == rpATOMIC)
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) RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
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)
{ {
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object); pData->SetMatColor(atomic->geometry->matList.materials[i], atomic->geometry,
{color.r, color.g, color.b, color.a});
ST* st = reinterpret_cast<ST*>(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);
} }
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; RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent;
RwTexture* texture = nullptr; RwTexture* pTexture = nullptr;
// find the texture
for (auto const& tex : m_TextureData.m_ImagesList) 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; 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; if (object->type == rpATOMIC)
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) RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
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)
{ {
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object); pData->SetMatTexture(atomic->geometry->matList.materials[i], pTex);
ST* st = reinterpret_cast<ST*>(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);
}
} }
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; RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame) NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
{ {
const std::string name = GetFrameNodeName(frame); RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
if (node_name == "Default" || node_name == name)
{ {
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* if (object->type == rpATOMIC)
{ {
if (object->type == rpATOMIC) RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(object);
{ PaintData* pData = reinterpret_cast<PaintData*>(data);
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i) for (int i = 0; i < pAtomic->geometry->matList.numMaterials; ++i)
data.resetMaterialColor(atomic->geometry->matList.materials[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; RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame) NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
{ {
const std::string name = GetFrameNodeName(frame); RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
if (node_name == "Default" || node_name == name)
{ {
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject* if (object->type == rpATOMIC)
{ {
if (object->type == rpATOMIC) RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(object);
PaintData* pData = reinterpret_cast<PaintData*>(data);
for (int i = 0; i < pAtomic->geometry->matList.numMaterials; ++i)
{ {
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object); pData->ResetMatTexture(pAtomic->geometry->matList.materials[i]);
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]);
} }
return object; }
}, nullptr); return object;
} }, &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 #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: private:
// store vehicle specific data struct PaintData
struct VehData
{ {
struct MaterialProperties struct MatInfo
{ {
MaterialProperties() : bool m_bRecolor = false;
_color{0, 0, 0, 0}, bool m_bRetexture = false;
_recolor(false), RwRGBA m_nColor = {0, 0, 0, 0};
_retexture(false), RwRGBA m_nOriginalColor = {0, 0, 0, 0};
_geometry(nullptr), RwTexture* m_pTexture = nullptr;
_originalColor{0, 0, 0, 0}, RwTexture* m_pOriginalTexture = nullptr;
_originalTexture(nullptr), RpGeometry* m_pGeometry = nullptr;
_originalGeometryFlags(0) RwInt32 m_nOriginalGeometryFlags = 0;
{
}
RwRGBA _color;
RwTexture* _texture;
bool _recolor;
bool _retexture;
RpGeometry* _geometry;
RwRGBA _originalColor;
RwTexture* _originalTexture;
RwInt32 _originalGeometryFlags;
}; };
uchar m_nCarColors[4]; // carcols color IDs (primary, secondary, tertiary, quaternary)
std::string m_nTextureName = ""; // current applied texture name
// carcols color id std::unordered_map<RpMaterial*, MatInfo> m_nMapInfoList;
uchar primary_color = 0;
uchar secondary_color = 0;
std::unordered_map<RpMaterial*, MaterialProperties> materialProperties;
VehData(CVehicle* veh) PaintData(CVehicle* pVeh)
{ {
primary_color = veh->m_nPrimaryColor; m_nCarColors[0] = pVeh->m_nPrimaryColor;
secondary_color = veh->m_nSecondaryColor; 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); // Resets applied material colors
void setMaterialTexture(RpMaterial* material, RwTexture* texture, bool filter_mat = false); void ResetMatColor(RpMaterial* pMat);
void resetMaterialColor(RpMaterial* material);
void resetMaterialTexture(RpMaterial* material); // 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<VehData> m_VehData; VehicleExtendedData<PaintData> m_VehPaint;
static void NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function<void(RwFrame*)> func);
friend class IFeature;
PaintMgr();
PaintMgr(PaintMgr&);
public: 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(); // Returns internal data structure
static void GenerateNodeList(CVehicle* pVeh, std::vector<std::string>& names_vec, std::string& selected); PaintData &GetData(CVehicle* pVeh);
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); // Resets applied applied colors
static void ResetNodeColor(CVehicle* veh, std::string node_name); void ResetColor(CVehicle* pVeh);
static void ResetNodeTexture(CVehicle* pVeh, std::string node_name);
// 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::TableNextColumn();
ImGui::Text("III & VC animation code"); 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::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Junior-Djjr"); ImGui::Text("Junior-Djjr");

View File

@ -7,14 +7,9 @@
#include "utils/widget.h" #include "utils/widget.h"
#include "utils/util.h" #include "utils/util.h"
#include "custom/filehandler.h" #include "custom/filehandler.h"
#include "custom/vehpaint.h" #include "custom/vehcustmzr.h"
#include "custom/autodrive.h" #include "custom/autodrive.h"
#ifdef GTASA
#include <tHandlingData.h>
#include "custom/vehmod_sa.h"
#endif
VehiclePage& vehiclePage = VehiclePage::Get(); VehiclePage& vehiclePage = VehiclePage::Get();
VehiclePage::VehiclePage() VehiclePage::VehiclePage()
: IPage<VehiclePage>(ePageID::Vehicle, "Window.VehiclePage", true) : IPage<VehiclePage>(ePageID::Vehicle, "Window.VehiclePage", true)
@ -591,7 +586,7 @@ void VehiclePage::Draw()
Widget::CheckboxAddr(TEXT("Vehicle.PerfectHandling"), 0x96914C); Widget::CheckboxAddr(TEXT("Vehicle.PerfectHandling"), 0x96914C);
Widget::CheckboxAddr(TEXT("Vehicle.TankMode"), 0x969164); 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"))) if (Widget::Checkbox(TEXT("Vehicle.FlipNoBurn"), &m_bVehFlipNoBurn, TEXT("Vehicle.FlipNoBurnTip")))
{ {
// MixSets (Link2012) // MixSets (Link2012)
@ -1006,18 +1001,28 @@ void VehiclePage::Draw()
if (pPlayer->m_pVehicle && bPlayerInCar) if (pPlayer->m_pVehicle && bPlayerInCar)
{ {
CVehicle* veh = FindPlayerPed()->m_pVehicle; 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"))) if (ImGui::BeginTabItem(TEXT("Vehicle.AutoDrive")))
{ {
ImGui::Spacing(); ImGui::Spacing();
AutoDrive.Draw(); AutoDrive.Draw();
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
VehPaint.Draw();
#ifdef GTASA
VehMod.Draw();
#endif
} }
ImGui::EndTabBar(); ImGui::EndTabBar();
} }

View File

@ -65,7 +65,11 @@ void DataStore::RemoveKey(const char* key, const char* entry) noexcept
{ {
if (pTable) 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; std::string path;
public: public:
typedef toml::table Table; using Table = toml::table;
using Array = toml::array;
DataStore(const char* fileName, bool isPathPredefined = false) noexcept; DataStore(const char* fileName, bool isPathPredefined = false) noexcept;
@ -61,6 +62,25 @@ public:
return defaultVal; 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 Table* GetTable(const char* key) noexcept
{ {
if (pTable) if (pTable)

View File

@ -227,7 +227,7 @@ void ResourceStore::UpdateSearchList(bool favourites, fRtnArg1_t getNameFunc, fR
lookup.key = std::string(key.str()); lookup.key = std::string(key.str());
if (m_Filter.PassFilter(lookup.key.c_str())) if (m_Filter.PassFilter(lookup.key.c_str()))
{ {
lookup.cat = "Favourites"; lookup.cat = cat.str();
lookup.val = val.value_or<std::string>("Unkonwn"); lookup.val = val.value_or<std::string>("Unkonwn");
m_nSearchList.push_back(std::move(lookup)); m_nSearchList.push_back(std::move(lookup));
} }

View File

@ -82,6 +82,7 @@ public:
eResourceType m_Type; eResourceType m_Type;
ImVec2 m_ImageSize; ImVec2 m_ImageSize;
std::vector<std::unique_ptr<TextureResource>> m_ImagesList; 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)); 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 (!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->RemoveKey("Favourites", contextMenu.key.c_str());
data.m_pData->Save(); data.m_pData->Save();
data.UpdateSearchList(); data.UpdateSearchList();