Add saving paintjob, texture, color #73 refactore code
This commit is contained in:
parent
c4d7ec9318
commit
79727292a8
@ -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"
|
||||||
|
@ -183,8 +183,6 @@ static RwTexture* LoadTextureFromMemory(char* data, unsigned int size)
|
|||||||
NeonMgr::NeonMgr()
|
NeonMgr::NeonMgr()
|
||||||
{
|
{
|
||||||
Events::vehicleRenderEvent += [this](CVehicle* pVeh)
|
Events::vehicleRenderEvent += [this](CVehicle* pVeh)
|
||||||
{
|
|
||||||
if (m_bEnabled)
|
|
||||||
{
|
{
|
||||||
NeonData* data = &m_VehNeon.Get(pVeh);
|
NeonData* data = &m_VehNeon.Get(pVeh);
|
||||||
if (data->m_bNeonInstalled && !pVeh->IsUpsideDown())
|
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;
|
m_VehNeon.Get(pVeh).m_bNeonInstalled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NeonMgr::NeonData NeonMgr::GetData(CVehicle *pVeh)
|
||||||
|
{
|
||||||
|
return m_VehNeon.Get(pVeh);
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
{
|
|
||||||
static bool init;
|
|
||||||
|
|
||||||
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;
|
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;
|
||||||
if (it.second._recolor)
|
data.m_nCarColors[2] = pVeh->m_nTertiaryColor;
|
||||||
{
|
data.m_nCarColors[3] = pVeh->m_nQuaternaryColor;
|
||||||
it.second._originalColor = it.first->color;
|
|
||||||
it.first->color = it.second._color;
|
|
||||||
it.second._originalGeometryFlags = it.second._geometry->flags;
|
|
||||||
it.second._geometry->flags |= rpGEOMETRYMODULATEMATERIALCOLOR;
|
|
||||||
}
|
}
|
||||||
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)
|
if (tex)
|
||||||
{
|
{
|
||||||
it.second._originalTexture = it.first->texture;
|
it.second.m_pOriginalTexture = it.first->texture;
|
||||||
it.first->texture = tex;
|
it.first->texture = tex;
|
||||||
}
|
}
|
||||||
else
|
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
|
// stop trigger reset
|
||||||
|| (material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00)
|
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))
|
|| (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
|
||||||
{
|
{
|
||||||
matProps._recolor = true;
|
matInfo.m_bRecolor = true;
|
||||||
matProps._color = color;
|
matInfo.m_nColor = color;
|
||||||
matProps._geometry = geometry;
|
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 == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00)
|
|
||||||
|| (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
|
|| (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;
|
||||||
|
};
|
||||||
|
|
||||||
|
void PaintMgr::SetColor(CVehicle* pVeh, CRGBA color)
|
||||||
{
|
{
|
||||||
return;
|
RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||||
}
|
NodeWrapperRecursive(pFrame, pVeh, [&](RwFrame* frame)
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
|
BindData bindData { &m_VehPaint.Get(pVeh), color };
|
||||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||||
{
|
{
|
||||||
if (object->type == rpATOMIC)
|
if (object->type == rpATOMIC)
|
||||||
{
|
{
|
||||||
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
|
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
|
||||||
|
|
||||||
ST* st = reinterpret_cast<ST*>(data);
|
BindData* bind = reinterpret_cast<BindData*>(data);
|
||||||
CRGBA* color = &st->_color;
|
CRGBA color = bind->color;
|
||||||
bool filter_mat = st->_filter;
|
PaintData* pData = reinterpret_cast<PaintData*>(bind->pData);
|
||||||
|
|
||||||
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
|
|
||||||
|
|
||||||
for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i)
|
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;
|
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;
|
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)
|
||||||
RwTexture* _tex;
|
|
||||||
bool _filter;
|
|
||||||
} st;
|
|
||||||
|
|
||||||
st._tex = texture;
|
|
||||||
st._filter = filter_mat;
|
|
||||||
|
|
||||||
if (node_name == "Default" || node_name == name)
|
|
||||||
{
|
{
|
||||||
|
BindData bindData { &m_VehPaint.Get(pVeh), {}, pTexture };
|
||||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||||
{
|
{
|
||||||
if (object->type == rpATOMIC)
|
if (object->type == rpATOMIC)
|
||||||
{
|
{
|
||||||
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
|
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
|
||||||
|
|
||||||
ST* st = reinterpret_cast<ST*>(data);
|
BindData* bind = reinterpret_cast<BindData*>(data);
|
||||||
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
|
RwTexture *pTex = bind->pTexture;
|
||||||
|
PaintData* pData = reinterpret_cast<PaintData*>(bind->pData);
|
||||||
|
|
||||||
for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i)
|
for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i)
|
||||||
{
|
{
|
||||||
data.setMaterialTexture(atomic->geometry->matList.materials[i], st->_tex,
|
pData->SetMatTexture(atomic->geometry->matList.materials[i], pTex);
|
||||||
st->_filter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return object;
|
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;
|
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||||
|
|
||||||
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
||||||
{
|
|
||||||
const std::string name = GetFrameNodeName(frame);
|
|
||||||
|
|
||||||
if (node_name == "Default" || node_name == name)
|
|
||||||
{
|
{
|
||||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||||
{
|
{
|
||||||
if (object->type == rpATOMIC)
|
if (object->type == rpATOMIC)
|
||||||
{
|
{
|
||||||
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
|
RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(object);
|
||||||
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
|
PaintData* pData = reinterpret_cast<PaintData*>(data);
|
||||||
|
|
||||||
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;
|
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;
|
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||||
|
|
||||||
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
||||||
{
|
|
||||||
const std::string name = GetFrameNodeName(frame);
|
|
||||||
|
|
||||||
if (node_name == "Default" || node_name == name)
|
|
||||||
{
|
{
|
||||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||||
{
|
{
|
||||||
if (object->type == rpATOMIC)
|
if (object->type == rpATOMIC)
|
||||||
{
|
{
|
||||||
RpAtomic* atomic = reinterpret_cast<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 < pAtomic->geometry->matList.numMaterials; ++i)
|
||||||
|
{
|
||||||
for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i)
|
pData->ResetMatTexture(pAtomic->geometry->matList.materials[i]);
|
||||||
data.resetMaterialTexture(atomic->geometry->matList.materials[i]);
|
}
|
||||||
}
|
}
|
||||||
return object;
|
return object;
|
||||||
}, nullptr);
|
}, &m_VehPaint.Get(pVeh));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
m_VehPaint.Get(pVeh).m_nTextureName = "";
|
||||||
}
|
}
|
@ -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;
|
||||||
|
};
|
||||||
|
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;
|
// Resets applied material colors
|
||||||
RwTexture* _texture;
|
void ResetMatColor(RpMaterial* pMat);
|
||||||
bool _recolor;
|
|
||||||
bool _retexture;
|
// Resets applied material textures
|
||||||
RpGeometry* _geometry;
|
void ResetMatTexture(RpMaterial* pMat);
|
||||||
RwRGBA _originalColor;
|
|
||||||
RwTexture* _originalTexture;
|
// Sets the material color to provided value
|
||||||
RwInt32 _originalGeometryFlags;
|
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
|
friend class IFeature;
|
||||||
uchar primary_color = 0;
|
PaintMgr();
|
||||||
uchar secondary_color = 0;
|
PaintMgr(PaintMgr&);
|
||||||
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);
|
|
||||||
|
|
||||||
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
639
src/custom/vehcustmzr.cpp
Normal 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
49
src/custom/vehcustmzr.h
Normal 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;
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
@ -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
|
|
||||||
}
|
|
@ -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;
|
|
@ -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");
|
||||||
|
@ -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)
|
||||||
@ -1007,17 +1002,27 @@ void VehiclePage::Draw()
|
|||||||
{
|
{
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user