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"
|
||||
Weather = "Weather"
|
||||
WeatherID = "Weather ID"
|
||||
WeatherIDText = "Sets weather by IDs (0-255). Don't touch unless you know what you're doing!"
|
||||
WeatherIDText = "Sets weather by IDs (0-255)."
|
||||
|
||||
[Main]
|
||||
TranslationLanguage = "English"
|
||||
@ -472,9 +472,16 @@ CemterMassZ = "Centre of mass Z"
|
||||
Cheap = "Cheap"
|
||||
Color = "Color"
|
||||
ColorPicker = "Color picker"
|
||||
ColorType = "Color type"
|
||||
ColProof = "Collision proof"
|
||||
Component = "Component"
|
||||
Country = "Country"
|
||||
Customize = "Customize"
|
||||
CustomizeLabel = "Customization label"
|
||||
CutomizeClear = "Clear customizations"
|
||||
CutomizeClearMSG = "Cleared customizations"
|
||||
CutomizeSave = "Save customizations"
|
||||
CutomizeSaveMSG = "Saved customizations"
|
||||
Damage = "Damage"
|
||||
DampingLvl = "Damping level"
|
||||
DensityMul = "Density multiplier"
|
||||
@ -583,6 +590,7 @@ ResetHandling = "Reset handling"
|
||||
ResetHandlingMSG = "Handling reset successfully"
|
||||
ResetTexture = "Reset texture"
|
||||
ResetTextureMSG = "Texture reset successfully"
|
||||
Save = "Save"
|
||||
SaveFile = "Save to file"
|
||||
SaveFileMSG = "Handling saved successfully"
|
||||
SeatOffset = "Seat offset"
|
||||
|
@ -184,45 +184,42 @@ NeonMgr::NeonMgr()
|
||||
{
|
||||
Events::vehicleRenderEvent += [this](CVehicle* pVeh)
|
||||
{
|
||||
if (m_bEnabled)
|
||||
NeonData* data = &m_VehNeon.Get(pVeh);
|
||||
if (data->m_bNeonInstalled && !pVeh->IsUpsideDown())
|
||||
{
|
||||
NeonData* data = &m_VehNeon.Get(pVeh);
|
||||
if (data->m_bNeonInstalled && !pVeh->IsUpsideDown())
|
||||
if (!m_pNeonTexture)
|
||||
{
|
||||
if (!m_pNeonTexture)
|
||||
m_pNeonTexture = LoadTextureFromMemory((char*)neon_mask, sizeof(neon_mask));
|
||||
}
|
||||
|
||||
CVector Pos = CModelInfo::GetModelInfo(pVeh->m_nModelIndex)->m_pColModel->m_boundBox.m_vecMin;
|
||||
CVector center = pVeh->TransformFromObjectSpace(CVector(0.0f, 0.0f, 0.0f));
|
||||
CVector up = pVeh->TransformFromObjectSpace(CVector(0.0f, -Pos.y - data->m_fVal, 0.0f)) - center;
|
||||
CVector right = pVeh->TransformFromObjectSpace(CVector(Pos.x + data->m_fVal, 0.0f, 0.0f)) - center;
|
||||
CShadows::StoreShadowToBeRendered(5, m_pNeonTexture, ¢er, up.x, up.y, right.x, right.y, 180, data->m_Color.r,
|
||||
data->m_Color.g, data->m_Color.b, 2.0f, false, 1.0f, 0, true);
|
||||
|
||||
if (data->m_bPulsing)
|
||||
{
|
||||
size_t delta = CTimer::m_snTimeInMilliseconds - CTimer::m_snPreviousTimeInMilliseconds;
|
||||
|
||||
if (data->m_fVal < 0.0f)
|
||||
{
|
||||
m_pNeonTexture = LoadTextureFromMemory((char*)neon_mask, sizeof(neon_mask));
|
||||
data->m_bIncrement = true;
|
||||
}
|
||||
|
||||
CVector Pos = CModelInfo::GetModelInfo(pVeh->m_nModelIndex)->m_pColModel->m_boundBox.m_vecMin;
|
||||
CVector center = pVeh->TransformFromObjectSpace(CVector(0.0f, 0.0f, 0.0f));
|
||||
CVector up = pVeh->TransformFromObjectSpace(CVector(0.0f, -Pos.y - data->m_fVal, 0.0f)) - center;
|
||||
CVector right = pVeh->TransformFromObjectSpace(CVector(Pos.x + data->m_fVal, 0.0f, 0.0f)) - center;
|
||||
CShadows::StoreShadowToBeRendered(5, m_pNeonTexture, ¢er, up.x, up.y, right.x, right.y, 180, data->m_Color.r,
|
||||
data->m_Color.g, data->m_Color.b, 2.0f, false, 1.0f, 0, true);
|
||||
|
||||
if (data->m_bPulsing)
|
||||
if (data->m_fVal > 0.3f)
|
||||
{
|
||||
size_t delta = CTimer::m_snTimeInMilliseconds - CTimer::m_snPreviousTimeInMilliseconds;
|
||||
data->m_bIncrement = false;
|
||||
}
|
||||
|
||||
if (data->m_fVal < 0.0f)
|
||||
{
|
||||
data->m_bIncrement = true;
|
||||
}
|
||||
|
||||
if (data->m_fVal > 0.3f)
|
||||
{
|
||||
data->m_bIncrement = false;
|
||||
}
|
||||
|
||||
if (data->m_bIncrement)
|
||||
{
|
||||
data->m_fVal += 0.0003f * delta;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->m_fVal -= 0.0003f * delta;
|
||||
}
|
||||
if (data->m_bIncrement)
|
||||
{
|
||||
data->m_fVal += 0.0003f * delta;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->m_fVal -= 0.0003f * delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -269,3 +266,8 @@ void NeonMgr::Remove(CVehicle* pVeh)
|
||||
{
|
||||
m_VehNeon.Get(pVeh).m_bNeonInstalled = false;
|
||||
}
|
||||
|
||||
NeonMgr::NeonData NeonMgr::GetData(CVehicle *pVeh)
|
||||
{
|
||||
return m_VehNeon.Get(pVeh);
|
||||
}
|
@ -38,6 +38,9 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
// Returns internal neon data
|
||||
NeonData GetData(CVehicle *pVeh);
|
||||
|
||||
// Installs neons with color
|
||||
void Install(CVehicle* veh, int r, int g, int b);
|
||||
|
||||
|
@ -1,321 +1,293 @@
|
||||
// Portion of this source is taken from MoonAdditions https://github.com/THE-FYP/MoonAdditions
|
||||
|
||||
// Copyright (c) 2012 DK22Pac
|
||||
// Copyright (c) 2017 FYP
|
||||
// Copyright (c) 2021 Grinch_
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "pch.h"
|
||||
#include "paint_sa.h"
|
||||
#include "utils/util.h"
|
||||
#include <NodeName.h>
|
||||
#include "../../include/kiero/minhook/MinHook.h"
|
||||
|
||||
void Paint::InjectHooks()
|
||||
PaintMgr& Paint = PaintMgr::Get();
|
||||
|
||||
// This works with silentpatch
|
||||
static auto oEntityRender = (void(__fastcall*)(CVehicle*))0x534310;
|
||||
void __fastcall hkEntityRender(CVehicle* pVeh)
|
||||
{
|
||||
static bool init;
|
||||
|
||||
if (init)
|
||||
if (!pVeh || pVeh->m_nType != ENTITY_TYPE_VEHICLE)
|
||||
{
|
||||
oEntityRender(pVeh);
|
||||
return;
|
||||
}
|
||||
auto& data = Paint.GetData(pVeh);
|
||||
|
||||
Events::vehicleRenderEvent.before += [](CVehicle* pVeh)
|
||||
/*
|
||||
Game colors won't ve visible over material colors
|
||||
So reset material colors & apply game colors here
|
||||
*/
|
||||
if (pVeh->m_nPrimaryColor != data.m_nCarColors[0]
|
||||
|| pVeh->m_nSecondaryColor != data.m_nCarColors[1])
|
||||
{
|
||||
VehData& data = m_VehData.Get(pVeh);
|
||||
|
||||
// reset custom color if color id changed
|
||||
if (pVeh->m_nPrimaryColor != data.primary_color
|
||||
|| pVeh->m_nSecondaryColor != data.secondary_color)
|
||||
for (auto& it : data.m_nMapInfoList)
|
||||
{
|
||||
for (auto& it : data.materialProperties)
|
||||
data.resetMaterialColor(it.first);
|
||||
|
||||
data.primary_color = pVeh->m_nPrimaryColor;
|
||||
data.secondary_color = pVeh->m_nSecondaryColor;
|
||||
data.ResetMatColor(it.first);
|
||||
}
|
||||
|
||||
for (auto& it : data.materialProperties)
|
||||
data.m_nCarColors[0] = pVeh->m_nPrimaryColor;
|
||||
data.m_nCarColors[1] = pVeh->m_nSecondaryColor;
|
||||
data.m_nCarColors[2] = pVeh->m_nTertiaryColor;
|
||||
data.m_nCarColors[3] = pVeh->m_nQuaternaryColor;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Applying our custom material colors here
|
||||
*/
|
||||
for (auto& it : data.m_nMapInfoList)
|
||||
{
|
||||
if (it.second.m_bRecolor)
|
||||
{
|
||||
if (it.second._recolor)
|
||||
it.second.m_nOriginalColor = it.first->color;
|
||||
it.first->color = it.second.m_nColor;
|
||||
it.second.m_nOriginalGeometryFlags = it.second.m_pGeometry->flags;
|
||||
it.second.m_pGeometry->flags |= rpGEOMETRYMODULATEMATERIALCOLOR;
|
||||
}
|
||||
|
||||
if (it.second.m_bRetexture)
|
||||
{
|
||||
auto tex = it.second.m_pTexture;
|
||||
if (tex)
|
||||
{
|
||||
it.second._originalColor = it.first->color;
|
||||
it.first->color = it.second._color;
|
||||
it.second._originalGeometryFlags = it.second._geometry->flags;
|
||||
it.second._geometry->flags |= rpGEOMETRYMODULATEMATERIALCOLOR;
|
||||
it.second.m_pOriginalTexture = it.first->texture;
|
||||
it.first->texture = tex;
|
||||
}
|
||||
if (it.second._retexture)
|
||||
else
|
||||
{
|
||||
auto tex = it.second._texture;
|
||||
if (tex)
|
||||
{
|
||||
it.second._originalTexture = it.first->texture;
|
||||
it.first->texture = tex;
|
||||
}
|
||||
else
|
||||
{
|
||||
it.second._retexture = false;
|
||||
}
|
||||
it.second.m_bRetexture = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
oEntityRender(pVeh);
|
||||
};
|
||||
|
||||
PaintMgr::PaintMgr()
|
||||
{
|
||||
MH_Initialize();
|
||||
MH_CreateHook((void*)0x534310, hkEntityRender, (void**)&oEntityRender);
|
||||
MH_EnableHook((void*)0x534310);
|
||||
|
||||
// This doesn't work for helicopters? SilentPatch?
|
||||
static ThiscallEvent<AddressList<0x55332A, H_CALL>, PRIORITY_BEFORE, ArgPickN<CVehicle*, 0>, void(CVehicle*)> vehicleResetAfterRender;
|
||||
vehicleResetAfterRender += [this](CVehicle* pVeh)
|
||||
{
|
||||
PaintData& data = m_VehPaint.Get(pVeh);
|
||||
for (auto& it : data.m_nMapInfoList)
|
||||
{
|
||||
if (it.second.m_bRecolor)
|
||||
{
|
||||
it.first->color = it.second.m_nOriginalColor;
|
||||
it.second.m_pGeometry->flags = it.second.m_nOriginalGeometryFlags;
|
||||
}
|
||||
if (it.second.m_bRetexture)
|
||||
{
|
||||
it.first->texture = it.second.m_pOriginalTexture;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ThiscallEvent<AddressList<0x55332A, H_CALL>, PRIORITY_BEFORE, ArgPickN<CVehicle*, 0>, void(CVehicle*)> vehicleResetAfterRender;
|
||||
vehicleResetAfterRender += [](CVehicle* pVeh)
|
||||
{
|
||||
for (auto& it : m_VehData.Get(pVeh).materialProperties)
|
||||
{
|
||||
if (it.second._recolor)
|
||||
{
|
||||
it.first->color = it.second._originalColor;
|
||||
it.second._geometry->flags = it.second._originalGeometryFlags;
|
||||
}
|
||||
if (it.second._retexture)
|
||||
{
|
||||
it.first->texture = it.second._originalTexture;
|
||||
}
|
||||
}
|
||||
};
|
||||
init = true;
|
||||
}
|
||||
|
||||
void Paint::VehData::setMaterialColor(RpMaterial* material, RpGeometry* geometry, RwRGBA color, bool filter_mat)
|
||||
void PaintMgr::SetCarcols(CVehicle *pVeh, uint primary, uint secondary, uint tertiary, uint quaternary, bool reset)
|
||||
{
|
||||
auto& matProps = materialProperties[material];
|
||||
*(uint8_replacement*)(int(pVeh) + 0x433 + 1) = primary;
|
||||
*(uint8_replacement*)(int(pVeh) + 0x433 + 2) = secondary;
|
||||
*(uint8_replacement*)(int(pVeh) + 0x433 + 3) = tertiary;
|
||||
*(uint8_replacement*)(int(pVeh) + 0x433 + 4) = quaternary;
|
||||
|
||||
if (!filter_mat
|
||||
|| (material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00)
|
||||
|| (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
|
||||
// stop trigger reset
|
||||
if (!reset)
|
||||
{
|
||||
matProps._recolor = true;
|
||||
matProps._color = color;
|
||||
matProps._geometry = geometry;
|
||||
auto& data = Paint.GetData(pVeh);
|
||||
data.m_nCarColors[0] = *(uint8_replacement*)(int(pVeh) + 0x433 + 1);
|
||||
data.m_nCarColors[1] = *(uint8_replacement*)(int(pVeh) + 0x433 + 2);
|
||||
data.m_nCarColors[2] = *(uint8_replacement*)(int(pVeh) + 0x433 + 3);
|
||||
data.m_nCarColors[3] = *(uint8_replacement*)(int(pVeh) + 0x433 + 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PaintMgr::PaintData::SetMatColor(RpMaterial* material, RpGeometry* geometry, RwRGBA color)
|
||||
{
|
||||
auto& matInfo = m_nMapInfoList[material];
|
||||
if ((material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00)
|
||||
|| (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
|
||||
{
|
||||
matInfo.m_bRecolor = true;
|
||||
matInfo.m_nColor = color;
|
||||
matInfo.m_pGeometry = geometry;
|
||||
}
|
||||
}
|
||||
|
||||
void Paint::VehData::setMaterialTexture(RpMaterial* material, RwTexture* texture, bool filter_mat)
|
||||
void PaintMgr::PaintData::SetMatTexture(RpMaterial* material, RwTexture* texture)
|
||||
{
|
||||
auto& matProps = materialProperties[material];
|
||||
|
||||
if (!filter_mat
|
||||
|| (material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00)
|
||||
|| (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
|
||||
auto& matInfo = m_nMapInfoList[material];
|
||||
if ((material->color.red == 0x3C && material->color.green == 0xFF && material->color.blue == 0x00)
|
||||
|| (material->color.red == 0xFF && material->color.green == 0x00 && material->color.blue == 0xAF))
|
||||
{
|
||||
matProps._retexture = true;
|
||||
matProps._texture = texture;
|
||||
matInfo.m_bRetexture = true;
|
||||
matInfo.m_pTexture = texture;
|
||||
}
|
||||
}
|
||||
|
||||
void Paint::VehData::resetMaterialColor(RpMaterial* material)
|
||||
void PaintMgr::PaintData::ResetMatColor(RpMaterial* material)
|
||||
{
|
||||
auto& matProps = materialProperties[material];
|
||||
matProps._recolor = false;
|
||||
matProps._color = {0, 0, 0, 0};
|
||||
auto& matInfo = m_nMapInfoList[material];
|
||||
matInfo.m_bRecolor = false;
|
||||
matInfo.m_nColor = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
void Paint::VehData::resetMaterialTexture(RpMaterial* material)
|
||||
void PaintMgr::PaintData::ResetMatTexture(RpMaterial* material)
|
||||
{
|
||||
auto& matProps = materialProperties[material];
|
||||
matProps._retexture = false;
|
||||
matProps._texture = nullptr;
|
||||
auto& matInfo = m_nMapInfoList[material];
|
||||
matInfo.m_bRetexture = false;
|
||||
matInfo.m_pTexture = nullptr;
|
||||
}
|
||||
|
||||
void Paint::NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function<void(RwFrame*)> func)
|
||||
PaintMgr::PaintData& PaintMgr::GetData(CVehicle *pVeh)
|
||||
{
|
||||
return m_VehPaint.Get(pVeh);
|
||||
}
|
||||
|
||||
static void NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function<void(RwFrame*)> func)
|
||||
{
|
||||
if (frame)
|
||||
{
|
||||
func(frame);
|
||||
|
||||
if (RwFrame* newFrame = frame->child)
|
||||
{
|
||||
NodeWrapperRecursive(newFrame, pVeh, func);
|
||||
}
|
||||
if (RwFrame* newFrame = frame->next)
|
||||
{
|
||||
NodeWrapperRecursive(newFrame, pVeh, func);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Paint::GenerateNodeList(CVehicle* pVeh, std::vector<std::string>& names_vec, std::string& selected)
|
||||
struct BindData
|
||||
{
|
||||
static int vehModel = 0;
|
||||
if (vehModel == pVeh->m_nModelIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void *pData;
|
||||
CRGBA color;
|
||||
RwTexture* pTexture;
|
||||
};
|
||||
|
||||
// reset to default
|
||||
names_vec.clear();
|
||||
names_vec.push_back("Default");
|
||||
selected = "Default";
|
||||
|
||||
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||
|
||||
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
||||
{
|
||||
const std::string name = GetFrameNodeName(frame);
|
||||
if (!(std::find(names_vec.begin(), names_vec.end(), name) != names_vec.end()))
|
||||
{
|
||||
names_vec.push_back(name);
|
||||
}
|
||||
});
|
||||
vehModel = pVeh->m_nModelIndex;
|
||||
}
|
||||
|
||||
void Paint::SetNodeColor(CVehicle* pVeh, std::string node_name, CRGBA color, bool filter_mat)
|
||||
void PaintMgr::SetColor(CVehicle* pVeh, CRGBA color)
|
||||
{
|
||||
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||
|
||||
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
||||
RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||
NodeWrapperRecursive(pFrame, pVeh, [&](RwFrame* frame)
|
||||
{
|
||||
const std::string name = GetFrameNodeName(frame);
|
||||
|
||||
struct ST
|
||||
BindData bindData { &m_VehPaint.Get(pVeh), color };
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
{
|
||||
CRGBA _color;
|
||||
bool _filter;
|
||||
} st;
|
||||
|
||||
st._color = color;
|
||||
st._filter = filter_mat;
|
||||
|
||||
if (node_name == "Default" || node_name == name)
|
||||
{
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
if (object->type == rpATOMIC)
|
||||
{
|
||||
if (object->type == rpATOMIC)
|
||||
RpAtomic* atomic = reinterpret_cast<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);
|
||||
|
||||
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);
|
||||
pData->SetMatColor(atomic->geometry->matList.materials[i], atomic->geometry,
|
||||
{color.r, color.g, color.b, color.a});
|
||||
}
|
||||
return object;
|
||||
}, &st);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}, &bindData);
|
||||
});
|
||||
}
|
||||
|
||||
void Paint::SetNodeTexture(CVehicle* pVeh, std::string node_name, std::string texturename, bool filter_mat)
|
||||
void PaintMgr::SetTexture(CVehicle* pVeh, std::string name)
|
||||
{
|
||||
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||
RwTexture* texture = nullptr;
|
||||
RwFrame* pFrame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||
RwTexture* pTexture = nullptr;
|
||||
|
||||
// find the texture
|
||||
for (auto const& tex : m_TextureData.m_ImagesList)
|
||||
{
|
||||
if (tex.get()->m_FileName == texturename)
|
||||
if (tex.get()->m_FileName == name)
|
||||
{
|
||||
texture = tex.get()->m_pRwTexture;
|
||||
pTexture = tex.get()->m_pRwTexture;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
||||
if (!pTexture)
|
||||
{
|
||||
const std::string name = GetFrameNodeName(frame);
|
||||
return;
|
||||
}
|
||||
|
||||
struct ST
|
||||
m_VehPaint.Get(pVeh).m_nTextureName = name;
|
||||
NodeWrapperRecursive(pFrame, pVeh, [&](RwFrame* frame)
|
||||
{
|
||||
BindData bindData { &m_VehPaint.Get(pVeh), {}, pTexture };
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
{
|
||||
RwTexture* _tex;
|
||||
bool _filter;
|
||||
} st;
|
||||
|
||||
st._tex = texture;
|
||||
st._filter = filter_mat;
|
||||
|
||||
if (node_name == "Default" || node_name == name)
|
||||
{
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
if (object->type == rpATOMIC)
|
||||
{
|
||||
if (object->type == rpATOMIC)
|
||||
RpAtomic* atomic = reinterpret_cast<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);
|
||||
|
||||
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);
|
||||
}
|
||||
pData->SetMatTexture(atomic->geometry->matList.materials[i], pTex);
|
||||
}
|
||||
return object;
|
||||
}, &st);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}, &bindData);
|
||||
});
|
||||
}
|
||||
|
||||
void Paint::ResetNodeColor(CVehicle* pVeh, std::string node_name)
|
||||
void PaintMgr::ResetColor(CVehicle* pVeh)
|
||||
{
|
||||
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||
|
||||
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
||||
{
|
||||
const std::string name = GetFrameNodeName(frame);
|
||||
|
||||
if (node_name == "Default" || node_name == name)
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
{
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
if (object->type == rpATOMIC)
|
||||
{
|
||||
if (object->type == rpATOMIC)
|
||||
{
|
||||
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
|
||||
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
|
||||
RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(object);
|
||||
PaintData* pData = reinterpret_cast<PaintData*>(data);
|
||||
|
||||
for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i)
|
||||
data.resetMaterialColor(atomic->geometry->matList.materials[i]);
|
||||
for (int i = 0; i < pAtomic->geometry->matList.numMaterials; ++i)
|
||||
{
|
||||
pData->ResetMatColor(pAtomic->geometry->matList.materials[i]);
|
||||
}
|
||||
return object;
|
||||
}, nullptr);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}, &m_VehPaint.Get(pVeh));
|
||||
});
|
||||
}
|
||||
|
||||
void Paint::ResetNodeTexture(CVehicle* pVeh, std::string node_name)
|
||||
void PaintMgr::ResetTexture(CVehicle* pVeh)
|
||||
{
|
||||
RwFrame* frame = (RwFrame*)pVeh->m_pRwClump->object.parent;
|
||||
|
||||
NodeWrapperRecursive(frame, pVeh, [&](RwFrame* frame)
|
||||
{
|
||||
const std::string name = GetFrameNodeName(frame);
|
||||
|
||||
if (node_name == "Default" || node_name == name)
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
{
|
||||
RwFrameForAllObjects(frame, [](RwObject* object, void* data) -> RwObject*
|
||||
if (object->type == rpATOMIC)
|
||||
{
|
||||
if (object->type == rpATOMIC)
|
||||
RpAtomic* pAtomic = reinterpret_cast<RpAtomic*>(object);
|
||||
PaintData* pData = reinterpret_cast<PaintData*>(data);
|
||||
|
||||
for (int i = 0; i < pAtomic->geometry->matList.numMaterials; ++i)
|
||||
{
|
||||
RpAtomic* atomic = reinterpret_cast<RpAtomic*>(object);
|
||||
|
||||
VehData& data = m_VehData.Get(FindPlayerPed()->m_pVehicle);
|
||||
|
||||
for (int i = 0; i < atomic->geometry->matList.numMaterials; ++i)
|
||||
data.resetMaterialTexture(atomic->geometry->matList.materials[i]);
|
||||
pData->ResetMatTexture(pAtomic->geometry->matList.materials[i]);
|
||||
}
|
||||
return object;
|
||||
}, nullptr);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}, &m_VehPaint.Get(pVeh));
|
||||
});
|
||||
m_VehPaint.Get(pVeh).m_nTextureName = "";
|
||||
}
|
@ -1,85 +1,79 @@
|
||||
// Portion of this source is taken from MoonAdditions https://github.com/THE-FYP/MoonAdditions
|
||||
|
||||
// Copyright (c) 2012 DK22Pac
|
||||
// Copyright (c) 2017 FYP
|
||||
// Copyright (c) 2021 Grinch_
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "pch.h"
|
||||
#include "interface/icheat.hpp"
|
||||
/*
|
||||
Vehicle color & texturring implementation class for GTA: San Andreas
|
||||
|
||||
class Paint
|
||||
TODO: Implement for VC & 3 too (maybe)
|
||||
Dunno how it'd work with the d3d8to9 wrapper
|
||||
*/
|
||||
class PaintMgr : public ICheat<PaintMgr>
|
||||
{
|
||||
private:
|
||||
// store vehicle specific data
|
||||
struct VehData
|
||||
struct PaintData
|
||||
{
|
||||
struct MaterialProperties
|
||||
struct MatInfo
|
||||
{
|
||||
MaterialProperties() :
|
||||
_color{0, 0, 0, 0},
|
||||
_recolor(false),
|
||||
_retexture(false),
|
||||
_geometry(nullptr),
|
||||
_originalColor{0, 0, 0, 0},
|
||||
_originalTexture(nullptr),
|
||||
_originalGeometryFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
RwRGBA _color;
|
||||
RwTexture* _texture;
|
||||
bool _recolor;
|
||||
bool _retexture;
|
||||
RpGeometry* _geometry;
|
||||
RwRGBA _originalColor;
|
||||
RwTexture* _originalTexture;
|
||||
RwInt32 _originalGeometryFlags;
|
||||
bool m_bRecolor = false;
|
||||
bool m_bRetexture = false;
|
||||
RwRGBA m_nColor = {0, 0, 0, 0};
|
||||
RwRGBA m_nOriginalColor = {0, 0, 0, 0};
|
||||
RwTexture* m_pTexture = nullptr;
|
||||
RwTexture* m_pOriginalTexture = nullptr;
|
||||
RpGeometry* m_pGeometry = nullptr;
|
||||
RwInt32 m_nOriginalGeometryFlags = 0;
|
||||
};
|
||||
uchar m_nCarColors[4]; // carcols color IDs (primary, secondary, tertiary, quaternary)
|
||||
std::string m_nTextureName = ""; // current applied texture name
|
||||
|
||||
// carcols color id
|
||||
uchar primary_color = 0;
|
||||
uchar secondary_color = 0;
|
||||
std::unordered_map<RpMaterial*, MaterialProperties> materialProperties;
|
||||
std::unordered_map<RpMaterial*, MatInfo> m_nMapInfoList;
|
||||
|
||||
VehData(CVehicle* veh)
|
||||
PaintData(CVehicle* pVeh)
|
||||
{
|
||||
primary_color = veh->m_nPrimaryColor;
|
||||
secondary_color = veh->m_nSecondaryColor;
|
||||
m_nCarColors[0] = pVeh->m_nPrimaryColor;
|
||||
m_nCarColors[1] = pVeh->m_nSecondaryColor;
|
||||
m_nCarColors[2] = pVeh->m_nTertiaryColor;
|
||||
m_nCarColors[3] = pVeh->m_nQuaternaryColor;
|
||||
}
|
||||
|
||||
void setMaterialColor(RpMaterial* material, RpGeometry* geometry, RwRGBA color, bool filter_mat = false);
|
||||
void setMaterialTexture(RpMaterial* material, RwTexture* texture, bool filter_mat = false);
|
||||
void resetMaterialColor(RpMaterial* material);
|
||||
void resetMaterialTexture(RpMaterial* material);
|
||||
// Resets applied material colors
|
||||
void ResetMatColor(RpMaterial* pMat);
|
||||
|
||||
// Resets applied material textures
|
||||
void ResetMatTexture(RpMaterial* pMat);
|
||||
|
||||
// Sets the material color to provided value
|
||||
void SetMatColor(RpMaterial* pMat, RpGeometry* pGeo, RwRGBA color);
|
||||
|
||||
// Sets the material to provided texture
|
||||
void SetMatTexture(RpMaterial* pMat, RwTexture* pTex);
|
||||
};
|
||||
static inline VehicleExtendedData<VehData> m_VehData;
|
||||
static void NodeWrapperRecursive(RwFrame* frame, CVehicle* pVeh, std::function<void(RwFrame*)> func);
|
||||
VehicleExtendedData<PaintData> m_VehPaint;
|
||||
|
||||
friend class IFeature;
|
||||
PaintMgr();
|
||||
PaintMgr(PaintMgr&);
|
||||
|
||||
public:
|
||||
static inline ResourceStore m_TextureData { "textures", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) };
|
||||
ResourceStore m_TextureData { "textures", eResourceType::TYPE_IMAGE_TEXT, ImVec2(100, 80) };
|
||||
|
||||
static void InjectHooks();
|
||||
static void GenerateNodeList(CVehicle* pVeh, std::vector<std::string>& names_vec, std::string& selected);
|
||||
static void SetNodeColor(CVehicle* pVeh, std::string node_name, CRGBA color, bool filter_mat = false);
|
||||
static void SetNodeTexture(CVehicle* pVeh, std::string node_name, std::string texturename, bool filter_mat = false);
|
||||
static void ResetNodeColor(CVehicle* veh, std::string node_name);
|
||||
static void ResetNodeTexture(CVehicle* pVeh, std::string node_name);
|
||||
// Returns internal data structure
|
||||
PaintData &GetData(CVehicle* pVeh);
|
||||
|
||||
// Resets applied applied colors
|
||||
void ResetColor(CVehicle* pVeh);
|
||||
|
||||
// Resets appllied textures
|
||||
void ResetTexture(CVehicle* pVeh);
|
||||
|
||||
// Applies color to vehicle
|
||||
void SetColor(CVehicle* pVeh, CRGBA color);
|
||||
|
||||
// Sets vehicle carcol colors
|
||||
void SetCarcols(CVehicle *pVeh, uint primary, uint secondary, uint tertiary, uint quaternary, bool reset = true);
|
||||
|
||||
// Applies texture to vehicle
|
||||
void SetTexture(CVehicle* pVeh, std::string name);
|
||||
};
|
||||
|
||||
extern PaintMgr& Paint;
|
639
src/custom/vehcustmzr.cpp
Normal file
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::Text("III & VC animation code");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("DKPac22");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Plugin SDK, vehicle texture code");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Junior-Djjr");
|
||||
|
@ -7,14 +7,9 @@
|
||||
#include "utils/widget.h"
|
||||
#include "utils/util.h"
|
||||
#include "custom/filehandler.h"
|
||||
#include "custom/vehpaint.h"
|
||||
#include "custom/vehcustmzr.h"
|
||||
#include "custom/autodrive.h"
|
||||
|
||||
#ifdef GTASA
|
||||
#include <tHandlingData.h>
|
||||
#include "custom/vehmod_sa.h"
|
||||
#endif
|
||||
|
||||
VehiclePage& vehiclePage = VehiclePage::Get();
|
||||
VehiclePage::VehiclePage()
|
||||
: IPage<VehiclePage>(ePageID::Vehicle, "Window.VehiclePage", true)
|
||||
@ -591,7 +586,7 @@ void VehiclePage::Draw()
|
||||
Widget::CheckboxAddr(TEXT("Vehicle.PerfectHandling"), 0x96914C);
|
||||
Widget::CheckboxAddr(TEXT("Vehicle.TankMode"), 0x969164);
|
||||
|
||||
Widget::Checkbox(TEXT("Vehicle.InfNitro"), &VehMod.m_Nitro.m_bEnabled, TEXT("Vehicle.InfNitroTip"));
|
||||
Widget::Checkbox(TEXT("Vehicle.InfNitro"), &VehCustmzr.m_Nitro.m_bEnabled, TEXT("Vehicle.InfNitroTip"));
|
||||
if (Widget::Checkbox(TEXT("Vehicle.FlipNoBurn"), &m_bVehFlipNoBurn, TEXT("Vehicle.FlipNoBurnTip")))
|
||||
{
|
||||
// MixSets (Link2012)
|
||||
@ -1007,17 +1002,27 @@ void VehiclePage::Draw()
|
||||
{
|
||||
CVehicle* veh = FindPlayerPed()->m_pVehicle;
|
||||
int hveh = CPools::GetVehicleRef(veh);
|
||||
#ifdef GTASA
|
||||
if (ImGui::BeginTabItem(TEXT("Vehicle.Customize")))
|
||||
{
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::BeginTabBar("CustomizeTab"))
|
||||
{
|
||||
VehCustmzr.Draw();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
#else
|
||||
VehCustmzr.Draw();
|
||||
#endif
|
||||
if (ImGui::BeginTabItem(TEXT("Vehicle.AutoDrive")))
|
||||
{
|
||||
ImGui::Spacing();
|
||||
AutoDrive.Draw();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
VehPaint.Draw();
|
||||
#ifdef GTASA
|
||||
VehMod.Draw();
|
||||
#endif
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
@ -65,7 +65,11 @@ void DataStore::RemoveKey(const char* key, const char* entry) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
{
|
||||
(*pTable).at_path(key).as_table()->erase(entry);
|
||||
Table *tbl = pTable->at_path(key).as_table();
|
||||
if (tbl)
|
||||
{
|
||||
tbl->erase(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,8 @@ private:
|
||||
std::string path;
|
||||
|
||||
public:
|
||||
typedef toml::table Table;
|
||||
using Table = toml::table;
|
||||
using Array = toml::array;
|
||||
|
||||
DataStore(const char* fileName, bool isPathPredefined = false) noexcept;
|
||||
|
||||
@ -61,6 +62,25 @@ public:
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
Array* GetArray(const char* key) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
{
|
||||
Array *tbl = (*pTable).at_path(key).as_array();
|
||||
if (tbl)
|
||||
{
|
||||
return tbl;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTable->insert(key, Table());
|
||||
return (*pTable).at_path(key).as_array();
|
||||
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Table* GetTable(const char* key) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
|
@ -227,7 +227,7 @@ void ResourceStore::UpdateSearchList(bool favourites, fRtnArg1_t getNameFunc, fR
|
||||
lookup.key = std::string(key.str());
|
||||
if (m_Filter.PassFilter(lookup.key.c_str()))
|
||||
{
|
||||
lookup.cat = "Favourites";
|
||||
lookup.cat = cat.str();
|
||||
lookup.val = val.value_or<std::string>("Unkonwn");
|
||||
m_nSearchList.push_back(std::move(lookup));
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ public:
|
||||
eResourceType m_Type;
|
||||
ImVec2 m_ImageSize;
|
||||
std::vector<std::unique_ptr<TextureResource>> m_ImagesList;
|
||||
bool m_bAllowRemoveAll = false; // allows removing items from non custom categories
|
||||
|
||||
ResourceStore(const char* text, eResourceType type = TYPE_IMAGE, ImVec2 imageSize = ImVec2(64, 64));
|
||||
|
||||
|
@ -146,9 +146,9 @@ void DrawClippedList(ResourceStore& data, fArg3_t clickFunc, bool favourites, bo
|
||||
}
|
||||
if (!favourites && ImGui::MenuItem(TEXT("Menu.Remove")))
|
||||
{
|
||||
if (contextMenu.root == "Custom")
|
||||
if (contextMenu.root == "Custom" || data.m_bAllowRemoveAll)
|
||||
{
|
||||
data.m_pData->RemoveKey("Custom", contextMenu.key.c_str());
|
||||
data.m_pData->RemoveKey(contextMenu.root.c_str(), contextMenu.key.c_str());
|
||||
data.m_pData->RemoveKey("Favourites", contextMenu.key.c_str());
|
||||
data.m_pData->Save();
|
||||
data.UpdateSearchList();
|
||||
|
Loading…
Reference in New Issue
Block a user