mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
feat(vehicle): Personal Vehicle Spawner (#579)
* feat(vehicle): Personal Vehicle Spawner * Add Vinewood Club Garage & Clone PV * Use different approach for clone PV * some fixes * Stuff * Update lua bindings
This commit is contained in:
@@ -0,0 +1,517 @@
|
||||
#include "PersonalVehicles.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/backend/ScriptMgr.hpp"
|
||||
#include "game/backend/Self.hpp"
|
||||
#include "game/gta/data/VehicleValues.hpp"
|
||||
#include "game/gta/Natives.hpp"
|
||||
#include "game/gta/ScriptFunction.hpp"
|
||||
#include "game/gta/ScriptLocal.hpp"
|
||||
#include "game/gta/ScriptGlobal.hpp"
|
||||
#include "game/gta/Stats.hpp"
|
||||
#include "types/script/globals/GPBD_FM.hpp"
|
||||
#include "types/script/globals/MPSV.hpp"
|
||||
#include "types/script/globals/FreemodeGeneral.hpp"
|
||||
#include "types/script/globals/g_SavedMPGlobals.hpp"
|
||||
|
||||
#define MAX_GARAGE_NUM 33
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
// TO-DO: Use script functions for these instead?
|
||||
|
||||
static int GetPropertyGarageOffset(int property)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case 0: return 13 * 0;
|
||||
case 1: return 13 * 1;
|
||||
case 2: return 13 * 2;
|
||||
case 3: return 13 * 3;
|
||||
case 4: return 13 * 4;
|
||||
//Property 5 is not used.
|
||||
case 6: return 65;
|
||||
case 7: return 75;
|
||||
case 8: return 88;
|
||||
case 9: return 108;
|
||||
case 10: return 128;
|
||||
case 11: return 148;
|
||||
case 12: return 159;
|
||||
case 13: return 179;
|
||||
case 14: return 191;
|
||||
case 15: return 192;
|
||||
case 16: return 202;
|
||||
case 17: return 212;
|
||||
case 18: return 227;
|
||||
case 19: return 237;
|
||||
case 20: return 247;
|
||||
case 21: return 258;
|
||||
case 22: return 268;
|
||||
case 23: return 281;
|
||||
case 24: return 294;
|
||||
case 25: return 307;
|
||||
case 26: return 317;
|
||||
case 27: return 337;
|
||||
case 28: return 350;
|
||||
case 29: return 363;
|
||||
case 30: return 415; // The Vinewood Club Garage
|
||||
case 31: return 515;
|
||||
case 32: return 537;
|
||||
case MAX_GARAGE_NUM + 0: return 156; // Mobile Operations Center
|
||||
case MAX_GARAGE_NUM + 1: return 224; // Nightclub B1
|
||||
case MAX_GARAGE_NUM + 2: return 223; // Terrorbyte
|
||||
case MAX_GARAGE_NUM + 3: return 278; // Kosatka
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int GetPropertyGarageSize(int property)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case MAX_GARAGE_NUM + 0: // Mobile Operations Center
|
||||
case MAX_GARAGE_NUM + 2: // Terrorbyte
|
||||
case MAX_GARAGE_NUM + 3: // Kosatka
|
||||
case 14: return 1;
|
||||
case 31: return 2;
|
||||
case MAX_GARAGE_NUM + 1: return 3; // Nightclub B1
|
||||
case 11: return 8;
|
||||
case 6:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 32:
|
||||
case 25: return 10;
|
||||
case 13: return 11;
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 7:
|
||||
case 23:
|
||||
case 24:
|
||||
case 27:
|
||||
case 28: return 13;
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 12:
|
||||
case 26: return 20;
|
||||
case 29: return 50;
|
||||
case 30: return 100; // The Vinewood Club Garage
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int GetPropertyStatState(int property)
|
||||
{
|
||||
std::string stat = "";
|
||||
switch (property)
|
||||
{
|
||||
case 0: stat = "MPX_PROPERTY_HOUSE"; break;
|
||||
case 1: stat = "MPX_MULTI_PROPERTY_1"; break;
|
||||
case 2: stat = "MPX_MULTI_PROPERTY_2"; break;
|
||||
case 3: stat = "MPX_MULTI_PROPERTY_3"; break;
|
||||
case 4: stat = "MPX_MULTI_PROPERTY_4"; break;
|
||||
case 5: stat = "MPX_PROP_OFFICE"; break;
|
||||
case 6: stat = "MPX_PROP_CLUBHOUSE"; break;
|
||||
case 7: stat = "MPX_MULTI_PROPERTY_5"; break;
|
||||
case 8: stat = "MPX_PROP_OFFICE_GAR1"; break;
|
||||
case 9: stat = "MPX_PROP_OFFICE_GAR2"; break;
|
||||
case 10: stat = "MPX_PROP_OFFICE_GAR3"; break;
|
||||
case 11: stat = "MPX_PROP_IE_WAREHOUSE"; break;
|
||||
case 12: stat = "MPX_PROP_HANGAR"; break;
|
||||
case 13: stat = "MPX_PROP_DEFUNCBASE"; break;
|
||||
case 14: stat = "MPX_PROP_NIGHTCLUB"; break;
|
||||
case 15: stat = "MPX_PROP_MEGAWARE_GAR1"; break;
|
||||
case 16: stat = "MPX_PROP_MEGAWARE_GAR2"; break;
|
||||
case 17: stat = "MPX_PROP_MEGAWARE_GAR3"; break;
|
||||
case 18: stat = "MPX_PROP_ARENAWARS_GAR1"; break;
|
||||
case 19: stat = "MPX_PROP_ARENAWARS_GAR2"; break;
|
||||
case 20: stat = "MPX_PROP_ARENAWARS_GAR3"; break;
|
||||
case 21: stat = "MPX_PROP_CASINO_GAR1"; break;
|
||||
case 22: stat = "MPX_PROP_ARCADE_GAR1"; break;
|
||||
case 23: stat = "MPX_MULTI_PROPERTY_6"; break;
|
||||
case 24: stat = "MPX_MULTI_PROPERTY_7"; break;
|
||||
case 25: stat = "MPX_PROP_AUTO_SHOP"; break;
|
||||
case 26: stat = "MPX_PROP_SECURITY_OFFICE_GAR"; break;
|
||||
case 27: stat = "MPX_MULTI_PROPERTY_8"; break;
|
||||
case 28: stat = "MPX_MULTI_PROPERTY_9"; break;
|
||||
case 29: stat = "MPX_MULTSTOREY_GAR_OWNED"; break;
|
||||
case 31: stat = "MPX_PROP_BAIL_OFFICE"; break;
|
||||
case 32: stat = "MPX_PROP_HACKER_DEN"; break;
|
||||
case 30:
|
||||
case MAX_GARAGE_NUM + 0:
|
||||
case MAX_GARAGE_NUM + 1:
|
||||
case MAX_GARAGE_NUM + 2:
|
||||
case MAX_GARAGE_NUM + 3: return 1;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
return Stats::GetInt(stat);
|
||||
}
|
||||
|
||||
static std::string GetStaticPropertyName(int property, int garageSlotIterator)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case 12: // Hangar
|
||||
{
|
||||
auto id = GPBD_FM::Get()->Entries[Self::GetPlayer().GetId()].PropertyData.HangarData.Index;
|
||||
switch (id)
|
||||
{
|
||||
case 1: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_HANGAR_1"); // LSIA Hangar 1
|
||||
case 2: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_HANGAR_2"); // LSIA Hangar A17
|
||||
case 3: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_HANGAR_3"); // Fort Zancudo Hangar A2
|
||||
case 4: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_HANGAR_4"); // Fort Zancudo Hangar 3497
|
||||
case 5: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_HANGAR_5"); // Fort Zancudo Hangar 3499
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 13: // Facility
|
||||
{
|
||||
auto id = GPBD_FM::Get()->Entries[Self::GetPlayer().GetId()].PropertyData.DefunctBaseData.Index;
|
||||
switch (id)
|
||||
{
|
||||
case 1: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_1"); // Grand Senora Desert Facility
|
||||
case 2: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_2"); // Route 68 Facility
|
||||
case 3: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_3"); // Sandy Shores Facility
|
||||
case 4: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_4"); // Mount Gordo Facility
|
||||
case 5: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_6"); // Paleto Bay Facility
|
||||
case 6: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_7"); // Lago Zancudo Facility
|
||||
case 7: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_8"); // Zancudo River Facility
|
||||
case 8: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_9"); // Ron Alternates Wind Farm Facility
|
||||
case 9: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_DBASE_10"); // Land Act Reservoir Facility
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 14: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_BHUB_CLUBG"); // Nightclub Service Entrance
|
||||
case 15: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_BHUB_GAR1"); // Nightclub B2
|
||||
case 16: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_BHUB_GAR2"); // Nightclub B3
|
||||
case 17: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_BHUB_GAR3"); // Nightclub B4
|
||||
case 18: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("ARENA_GAR_F0"); // Arena Workshop
|
||||
case 19: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("ARENA_GAR_F1"); // Arena Workshop B1
|
||||
case 20: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("ARENA_GAR_F2"); // Arena Workshop B2
|
||||
case 21: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CASINO_GARNAME"); // Casino Penthouse
|
||||
case 22: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("ARCADE_GARNAME"); // Arcade
|
||||
case 25: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("AUT_SHP_GAR"); // Auto Shop
|
||||
case 26: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("FIXER_GARNAME"); // Agency
|
||||
case 29: // Eclipse Blvd Garage
|
||||
{
|
||||
int level = (garageSlotIterator - 1) / 10;
|
||||
switch (level)
|
||||
{
|
||||
case 0: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MSG_B1"); // Eclipse Blvd Garage B1
|
||||
case 1: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MSG_B2"); // Eclipse Blvd Garage B2
|
||||
case 2: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MSG_B3"); // Eclipse Blvd Garage B3
|
||||
case 3: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MSG_B4"); // Eclipse Blvd Garage B4
|
||||
case 4: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MSG_B5"); // Eclipse Blvd Garage B5
|
||||
}
|
||||
}
|
||||
case 30: // The Vinewood Club Garage
|
||||
{
|
||||
int level = (garageSlotIterator - 1) / 10;
|
||||
switch (level)
|
||||
{
|
||||
case 0: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("VPG_B1"); // Basement Level 1
|
||||
case 1: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("VPG_B2"); // Basement Level 2
|
||||
case 2: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("VPG_B3"); // Basement Level 3
|
||||
case 3: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("VPG_B4"); // Basement Level 4
|
||||
case 4: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("VPG_B5"); // Basement Level 5
|
||||
}
|
||||
}
|
||||
case 31: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("BO_GARNAME"); // Bail Office
|
||||
case 32: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("HD_GARNAME"); // Garment Factory
|
||||
case MAX_GARAGE_NUM + 0: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("GRTRUCK"); // Mobile Operations Center
|
||||
case MAX_GARAGE_NUM + 1: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_BHUB_GAR0"); // Nightclub B1
|
||||
case MAX_GARAGE_NUM + 2: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_BHUB_CLUBT"); // Terrorbyte
|
||||
case MAX_GARAGE_NUM + 3: return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("MP_BHUB_SUB"); // Kosatka
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
PersonalVehicles::PersonalVehicle::PersonalVehicle(int id, MPSV_Entry* data) :
|
||||
m_Id(id),
|
||||
m_Data(data)
|
||||
{
|
||||
m_Model = m_Data->VehicleModel;
|
||||
m_Plate = m_Data->NumberPlateText;
|
||||
m_Name = std::format("{} ({})", HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(VEHICLE::GET_DISPLAY_NAME_FROM_VEHICLE_MODEL(m_Model)), m_Plate);
|
||||
|
||||
SetGarage();
|
||||
}
|
||||
|
||||
int PersonalVehicles::PersonalVehicle::GetId()
|
||||
{
|
||||
return m_Id;
|
||||
}
|
||||
|
||||
MPSV_Entry* PersonalVehicles::PersonalVehicle::GetData()
|
||||
{
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
joaat_t PersonalVehicles::PersonalVehicle::GetModel()
|
||||
{
|
||||
return m_Model;
|
||||
}
|
||||
|
||||
std::string PersonalVehicles::PersonalVehicle::GetPlate()
|
||||
{
|
||||
return m_Plate;
|
||||
}
|
||||
|
||||
std::string PersonalVehicles::PersonalVehicle::GetName()
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
|
||||
std::string PersonalVehicles::PersonalVehicle::GetGarage()
|
||||
{
|
||||
return m_Garage;
|
||||
}
|
||||
|
||||
void PersonalVehicles::PersonalVehicle::SetGarage()
|
||||
{
|
||||
for (int propertyIterator = 0; propertyIterator < MAX_GARAGE_NUM + 4; propertyIterator++)
|
||||
{
|
||||
auto propertyStatState = GetPropertyStatState(propertyIterator);
|
||||
if (propertyStatState > 0)
|
||||
{
|
||||
auto garageSize = GetPropertyGarageSize(propertyIterator);
|
||||
auto garageOffset = GetPropertyGarageOffset(propertyIterator);
|
||||
for (int garageSlotIterator = 1; garageSlotIterator <= garageSize; garageSlotIterator++)
|
||||
{
|
||||
auto itemInSlot = *ScriptGlobal(1940530).At(garageOffset).At(garageSlotIterator).As<int*>() - 1;
|
||||
if (itemInSlot == m_Id)
|
||||
{
|
||||
auto staticPropertyString = GetStaticPropertyName(propertyIterator, garageSlotIterator);
|
||||
if (staticPropertyString.empty())
|
||||
{
|
||||
m_Garage = HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(ScriptGlobal(1312335).At(propertyStatState, 1951).At(16).As<const char*>());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Garage = staticPropertyString;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PersonalVehicles::PersonalVehicle::Despawn()
|
||||
{
|
||||
if (auto veh = GetCurrentHandle(); veh.IsValid())
|
||||
{
|
||||
veh.BringToHalt();
|
||||
|
||||
m_Data->PersonalVehicleFlags.Clear(ePersonalVehicleFlags::TRIGGER_SPAWN_TOGGLE);
|
||||
for (int i = 0; veh.IsValid(); i++)
|
||||
{
|
||||
ScriptMgr::Yield(100ms);
|
||||
if (i > 30)
|
||||
{
|
||||
LOG(WARNING) << "Despawn() Timed out despawning Personal Vehicle.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PersonalVehicles::PersonalVehicle::Repair()
|
||||
{
|
||||
if (m_Data->PersonalVehicleFlags.IsSet(ePersonalVehicleFlags::DESTROYED) && m_Data->PersonalVehicleFlags.IsSet(ePersonalVehicleFlags::HAS_INSURANCE))
|
||||
{
|
||||
m_Data->PersonalVehicleFlags.Clear(ePersonalVehicleFlags::DESTROYED);
|
||||
m_Data->PersonalVehicleFlags.Clear(ePersonalVehicleFlags::IMPOUNDED);
|
||||
m_Data->PersonalVehicleFlags.Clear(ePersonalVehicleFlags::UNK2);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PersonalVehicles::PersonalVehicle::Request(bool bring)
|
||||
{
|
||||
if (auto freemodeGeneral = FreemodeGeneral::Get())
|
||||
{
|
||||
if (freemodeGeneral->RequestedPersonalVehicleId != -1)
|
||||
return false;
|
||||
|
||||
if (!GetCurrent()->Despawn())
|
||||
return false;
|
||||
|
||||
Repair();
|
||||
|
||||
ScriptMgr::Yield(100ms);
|
||||
|
||||
if (bring)
|
||||
freemodeGeneral->NodeDistanceCheck = 0;
|
||||
freemodeGeneral->PersonalVehicleRequested = TRUE; // am_pi_menu also sets the field 956 but I guess we don't need that
|
||||
freemodeGeneral->Exec1Impound = FALSE; // not really sure what this does
|
||||
freemodeGeneral->RequestedPersonalVehicleId = m_Id;
|
||||
|
||||
ScriptMgr::Yield(100ms);
|
||||
|
||||
*ScriptLocal("freemode"_J, 19447).At(176).As<int*>() = 0;
|
||||
|
||||
if (bring)
|
||||
{
|
||||
for (int i = 0; !GetCurrentHandle().IsValid(); i++)
|
||||
{
|
||||
ScriptMgr::Yield(100ms);
|
||||
if (i > 30)
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto veh = GetCurrentHandle(); veh.IsValid())
|
||||
{
|
||||
auto coords = Self::GetPed().GetPosition();
|
||||
auto heading = Self::GetPed().GetHeading();
|
||||
veh.SetPosition(coords);
|
||||
veh.SetHeading(heading);
|
||||
veh.SetOnGroundProperly();
|
||||
Self::GetPed().SetInVehicle(veh);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Vehicle PersonalVehicles::PersonalVehicle::Clone(rage::fvector3 coords, float heading)
|
||||
{
|
||||
if (auto veh = Vehicle::Create(m_Model, coords, heading))
|
||||
{
|
||||
auto oldVal = m_Data->IsPersonalVehicle;
|
||||
|
||||
m_Data->IsPersonalVehicle = 0;
|
||||
|
||||
// This sometimes fails to apply the crew logo since ADD_VEHICLE_EMBLEM needs to be called in a loop
|
||||
static ScriptFunction applyMPSVData("freemode"_J, ScriptPointer("ApplyMPSVData", "5D ? ? ? 38 2A 71").Add(1).Rip());
|
||||
applyMPSVData.Call<void>(veh.GetHandle(), m_Data, true, true, false);
|
||||
|
||||
m_Data->IsPersonalVehicle = oldVal;
|
||||
return veh;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<PersonalVehicles::PersonalVehicle> PersonalVehicles::GetCurrentImpl()
|
||||
{
|
||||
auto savedMPGlobals = g_SavedMPGlobals::Get();
|
||||
auto MPSV = MPSV::Get();
|
||||
if (savedMPGlobals && MPSV)
|
||||
{
|
||||
auto id = savedMPGlobals->Entries[0].GeneralSaved.LastSavedCar;
|
||||
auto data = &MPSV->Entries[id];
|
||||
return std::make_unique<PersonalVehicle>(id, data);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vehicle PersonalVehicles::GetCurrentHandleImpl()
|
||||
{
|
||||
if (auto freemodeGeneral = FreemodeGeneral::Get())
|
||||
{
|
||||
if (auto veh = freemodeGeneral->PersonalVehicleIndex; veh != -1)
|
||||
return Vehicle(veh);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PersonalVehicles::UpdateImpl()
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - m_LastUpdate) < 10s)
|
||||
return;
|
||||
|
||||
m_LastUpdate = std::chrono::high_resolution_clock::now();
|
||||
|
||||
FiberPool::Push([] {
|
||||
RegisterVehicles();
|
||||
RegisterGarages();
|
||||
});
|
||||
}
|
||||
|
||||
void PersonalVehicles::RegisterVehiclesImpl()
|
||||
{
|
||||
if (auto mpsv = MPSV::Get())
|
||||
{
|
||||
const auto arraySize = *(int*)mpsv;
|
||||
|
||||
for (int i = 0; i < arraySize; i++)
|
||||
{
|
||||
if (i % 100 == 0)
|
||||
ScriptMgr::Yield();
|
||||
|
||||
auto data = &MPSV::Get()->Entries[i];
|
||||
|
||||
const auto model = data->VehicleModel;
|
||||
const auto& it = m_PVLookup.find(i);
|
||||
const auto exists = it != m_PVLookup.end();
|
||||
|
||||
if (STREAMING::IS_MODEL_A_VEHICLE(model))
|
||||
{
|
||||
auto veh = std::make_unique<PersonalVehicle>(i, data);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
if (veh->GetName() != it->second)
|
||||
{
|
||||
m_PersonalVehicles.erase(it->second);
|
||||
|
||||
it->second = veh->GetName();
|
||||
m_PersonalVehicles.emplace(veh->GetName(), std::move(veh));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PersonalVehicles[veh->GetName()]->SetGarage();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
m_PVLookup.emplace(i, veh->GetName());
|
||||
m_PersonalVehicles.emplace(veh->GetName(), std::move(veh));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exists)
|
||||
{
|
||||
m_PersonalVehicles.erase(it->second);
|
||||
m_PVLookup.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PersonalVehicles::RegisterGaragesImpl()
|
||||
{
|
||||
m_Garages.clear();
|
||||
for (const auto& [name, ptr] : GetPersonalVehicles())
|
||||
{
|
||||
if (auto garageName = ptr->GetGarage(); !garageName.empty())
|
||||
{
|
||||
m_Garages.emplace(garageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
#include "core/util/Joaat.hpp"
|
||||
#include "game/gta/Vehicle.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
struct MPSV_Entry;
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class PersonalVehicles
|
||||
{
|
||||
public:
|
||||
class PersonalVehicle
|
||||
{
|
||||
int m_Id;
|
||||
MPSV_Entry* m_Data;
|
||||
joaat_t m_Model;
|
||||
std::string m_Plate;
|
||||
std::string m_Name;
|
||||
std::string m_Garage;
|
||||
|
||||
public:
|
||||
PersonalVehicle(int id, MPSV_Entry* data);
|
||||
|
||||
int GetId();
|
||||
MPSV_Entry* GetData();
|
||||
joaat_t GetModel();
|
||||
std::string GetPlate();
|
||||
std::string GetName();
|
||||
std::string GetGarage();
|
||||
void SetGarage();
|
||||
|
||||
bool Despawn();
|
||||
bool Repair();
|
||||
bool Request(bool bring = false);
|
||||
Vehicle Clone(rage::fvector3 coords, float heading);
|
||||
};
|
||||
|
||||
static std::unique_ptr<PersonalVehicle> GetCurrent()
|
||||
{
|
||||
return GetInstance().GetCurrentImpl();
|
||||
}
|
||||
|
||||
static Vehicle GetCurrentHandle()
|
||||
{
|
||||
return GetInstance().GetCurrentHandleImpl();
|
||||
}
|
||||
|
||||
static std::map<std::string, std::unique_ptr<PersonalVehicle>>& GetPersonalVehicles()
|
||||
{
|
||||
return GetInstance().m_PersonalVehicles;
|
||||
}
|
||||
|
||||
static std::set<std::string>& GetGarages()
|
||||
{
|
||||
return GetInstance().m_Garages;
|
||||
}
|
||||
|
||||
static void Update()
|
||||
{
|
||||
GetInstance().UpdateImpl();
|
||||
}
|
||||
|
||||
static void RegisterGarages()
|
||||
{
|
||||
GetInstance().RegisterGaragesImpl();
|
||||
}
|
||||
|
||||
static void RegisterVehicles()
|
||||
{
|
||||
GetInstance().RegisterVehiclesImpl();
|
||||
}
|
||||
|
||||
private:
|
||||
static PersonalVehicles& GetInstance()
|
||||
{
|
||||
static PersonalVehicles instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::unique_ptr<PersonalVehicle> GetCurrentImpl();
|
||||
Vehicle GetCurrentHandleImpl();
|
||||
void UpdateImpl();
|
||||
void RegisterVehiclesImpl();
|
||||
void RegisterGaragesImpl();
|
||||
|
||||
std::map<std::string, std::unique_ptr<PersonalVehicle>> m_PersonalVehicles;
|
||||
std::map<int, std::string> m_PVLookup;
|
||||
std::set<std::string> m_Garages;
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_LastUpdate;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "game/backend/PersonalVehicles.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class DespawnPersonalVehicle : public Command
|
||||
{
|
||||
using Command::Command;
|
||||
|
||||
virtual void OnCall() override
|
||||
{
|
||||
if (!*Pointers.IsSessionStarted)
|
||||
return;
|
||||
|
||||
PersonalVehicles::GetCurrent()->Despawn();
|
||||
}
|
||||
};
|
||||
|
||||
static DespawnPersonalVehicle _DespawnPersonalVehicle{"despawnpv", "Despawn Personal Vehicle", "Returns your current Personal Vehicle to storage."};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/frontend/Notifications.hpp"
|
||||
#include "types/script/globals/MPSV.hpp"
|
||||
#include "game/backend/PersonalVehicles.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
|
||||
namespace YimMenu::Features
|
||||
{
|
||||
@@ -10,27 +11,21 @@ namespace YimMenu::Features
|
||||
|
||||
virtual void OnCall() override
|
||||
{
|
||||
if (auto mpsv = MPSV::Get())
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < *(int*)mpsv; i++)
|
||||
{
|
||||
if (mpsv->Entries[i].PersonalVehicleFlags.IsSet(1) && mpsv->Entries[i].PersonalVehicleFlags.IsSet(2))
|
||||
{
|
||||
mpsv->Entries[i].PersonalVehicleFlags.Clear(1);
|
||||
mpsv->Entries[i].PersonalVehicleFlags.Clear(6);
|
||||
mpsv->Entries[i].PersonalVehicleFlags.Clear(16);
|
||||
mpsv->Entries[i].PersonalVehicleFlags.Set(0);
|
||||
mpsv->Entries[i].PersonalVehicleFlags.Set(11);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (!*Pointers.IsSessionStarted)
|
||||
return;
|
||||
|
||||
if (count > 0)
|
||||
Notifications::Show("Fix All Vehicles", std::format("{} vehicles fixed.", count), NotificationType::Success);
|
||||
else
|
||||
Notifications::Show("Fix All Vehicles", "No vehicles to fix.");
|
||||
int count = 0;
|
||||
for (const auto& it : PersonalVehicles::GetPersonalVehicles())
|
||||
{
|
||||
const auto& personalVeh = it.second;
|
||||
if (personalVeh->Repair())
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
Notifications::Show("Fix All Vehicles", std::format("{} vehicles fixed.", count), NotificationType::Success);
|
||||
else
|
||||
Notifications::Show("Fix All Vehicles", "No vehicles to fix.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "game/backend/PersonalVehicles.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class RequestPersonalVehicle : public Command
|
||||
{
|
||||
using Command::Command;
|
||||
|
||||
virtual void OnCall() override
|
||||
{
|
||||
if (!*Pointers.IsSessionStarted)
|
||||
return;
|
||||
|
||||
PersonalVehicles::GetCurrent()->Request();
|
||||
}
|
||||
};
|
||||
|
||||
static RequestPersonalVehicle _RequestPersonalVehicle{"requestpv", "Request Personal Vehicle", "Requests your current Personal Vehicle."};
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace YimMenu::Submenus
|
||||
auto main = std::make_shared<Category>("Main");
|
||||
|
||||
auto globals = std::make_shared<Group>("Globals");
|
||||
auto tools = std::make_shared<Group>("Tools", 1);
|
||||
auto tools = std::make_shared<Group>("Tools", 2);
|
||||
auto misc = std::make_shared<Group>("Misc");
|
||||
|
||||
globals->AddItem(std::make_shared<BoolCommandItem>("vehiclegodmode"_J, "Godmode"));
|
||||
@@ -26,6 +26,8 @@ namespace YimMenu::Submenus
|
||||
tools->AddItem(std::make_shared<CommandItem>("repairvehicle"_J));
|
||||
tools->AddItem(std::make_shared<CommandItem>("fixallvehicles"_J));
|
||||
tools->AddItem(std::make_shared<CommandItem>("callmechanic"_J));
|
||||
tools->AddItem(std::make_shared<CommandItem>("requestpv"_J));
|
||||
tools->AddItem(std::make_shared<CommandItem>("despawnpv"_J));
|
||||
tools->AddItem(std::make_shared<CommandItem>("savepersonalvehicle"_J));
|
||||
|
||||
misc->AddItem(std::make_shared<BoolCommandItem>("speedometer"_J));
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
#include "core/commands/BoolCommand.hpp"
|
||||
#include "core/backend/ScriptMgr.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/frontend/Notifications.hpp"
|
||||
#include "game/backend/Self.hpp"
|
||||
#include "game/backend/PersonalVehicles.hpp"
|
||||
#include "game/gta/data/Vehicles.hpp"
|
||||
#include "game/gta/Vehicle.hpp"
|
||||
#include "game/gta/Natives.hpp"
|
||||
|
||||
namespace YimMenu::Submenus
|
||||
{
|
||||
static BoolCommand spawnInsideVehicle{"spawninsideveh", "Spawn Inside", "Spawn inside the vehicle."};
|
||||
static BoolCommand spawnVehicleMaxed{"spawnvehmaxed", "Spawn Maxed", "Spawn the vehicle maxed."};
|
||||
static BoolCommand spawnInsidePersonalVehicle{"spawninsidepv", "Spawn Inside", "Spawn inside the personal vehicle."};
|
||||
static BoolCommand spawnClonePersonalVehicle{"spawnclonepv", "Spawn Clone", "Spawn a clone of the persone vehicle."};
|
||||
|
||||
static Vector3 GetVehicleSpawnLoc(joaat_t hash, bool spawnInside)
|
||||
{
|
||||
@@ -31,15 +34,18 @@ namespace YimMenu::Submenus
|
||||
return ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(Self::GetPed().GetHandle(), 0.f, y_offset, 0.f);
|
||||
}
|
||||
|
||||
std::shared_ptr<Category> BuildSpawnVehicleMenu()
|
||||
std::shared_ptr<TabItem> RenderSpawnNewVehicle()
|
||||
{
|
||||
auto menu = std::make_shared<Category>("Spawn");
|
||||
auto tab = std::make_shared<TabItem>("New Vehicle");
|
||||
|
||||
auto spawn = std::make_shared<Group>("Spawn");
|
||||
auto settings = std::make_shared<Group>("Settings");
|
||||
|
||||
static std::vector<std::string> vehicleNames{};
|
||||
static std::vector<int> vehicleClasses{};
|
||||
static int selectedClass{-1};
|
||||
|
||||
menu->AddItem(std::make_unique<ImGuiItem>([] {
|
||||
spawn->AddItem(std::make_unique<ImGuiItem>([] {
|
||||
static bool init = [] {
|
||||
FiberPool::Push([] {
|
||||
std::unordered_map<std::string, int> nameCount;
|
||||
@@ -96,40 +102,44 @@ namespace YimMenu::Submenus
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
const int visible = std::min(10, static_cast<int>(vehicleNames.size()));
|
||||
const int visible = std::min(20, static_cast<int>(vehicleNames.size()));
|
||||
const float height = visible * ImGui::GetTextLineHeightWithSpacing();
|
||||
if (ImGui::BeginListBox("##vehicles", {300.f, height}))
|
||||
{
|
||||
std::string lower = search;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
for (int veh = 0; veh < vehicleNames.size(); veh++)
|
||||
if (vehicleNames.empty())
|
||||
{
|
||||
if (vehicleNames[veh].empty())
|
||||
continue;
|
||||
|
||||
auto hash = g_VehicleHashes[veh];
|
||||
auto name = vehicleNames[veh];
|
||||
auto lowerName = name;
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
||||
|
||||
bool matchesSearch = lowerName.find(lower) != std::string::npos;
|
||||
bool matchesClass = selectedClass == -1 || vehicleClasses[veh] == selectedClass;
|
||||
if (matchesSearch && matchesClass)
|
||||
ImGui::Text("Natives not cached yet.");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string lower = search;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
for (int veh = 0; veh < vehicleNames.size(); veh++)
|
||||
{
|
||||
ImGui::PushID(hash);
|
||||
if (ImGui::Selectable(name.c_str()))
|
||||
auto hash = g_VehicleHashes[veh];
|
||||
auto name = vehicleNames[veh];
|
||||
auto lowerName = name;
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
||||
|
||||
bool matchesSearch = lowerName.find(lower) != std::string::npos;
|
||||
bool matchesClass = selectedClass == -1 || vehicleClasses[veh] == selectedClass;
|
||||
if (matchesSearch && matchesClass)
|
||||
{
|
||||
FiberPool::Push([hash] {
|
||||
auto handle = Vehicle::Create(hash, GetVehicleSpawnLoc(hash, spawnInsideVehicle.GetState()), Self::GetPed().GetHeading());
|
||||
ImGui::PushID(hash);
|
||||
if (ImGui::Selectable(name.c_str()))
|
||||
{
|
||||
FiberPool::Push([hash] {
|
||||
auto handle = Vehicle::Create(hash, GetVehicleSpawnLoc(hash, spawnInsideVehicle.GetState()), Self::GetPed().GetHeading());
|
||||
|
||||
if (spawnInsideVehicle.GetState())
|
||||
Self::GetPed().SetInVehicle(handle);
|
||||
if (spawnInsideVehicle.GetState())
|
||||
Self::GetPed().SetInVehicle(handle);
|
||||
|
||||
if (spawnVehicleMaxed.GetState())
|
||||
handle.Upgrade();
|
||||
});
|
||||
if (spawnVehicleMaxed.GetState())
|
||||
handle.Upgrade();
|
||||
});
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +147,126 @@ namespace YimMenu::Submenus
|
||||
}
|
||||
}));
|
||||
|
||||
menu->AddItem(std::make_shared<BoolCommandItem>("spawninsideveh"_J));
|
||||
menu->AddItem(std::make_shared<BoolCommandItem>("spawnvehmaxed"_J));
|
||||
settings->AddItem(std::make_shared<BoolCommandItem>("spawninsideveh"_J));
|
||||
settings->AddItem(std::make_shared<BoolCommandItem>("spawnvehmaxed"_J));
|
||||
|
||||
tab->AddItem(spawn);
|
||||
tab->AddItem(settings);
|
||||
return tab;
|
||||
}
|
||||
|
||||
std::shared_ptr<TabItem> RenderSpawnPersonalVehicle()
|
||||
{
|
||||
auto tab = std::make_shared<TabItem>("Personal Vehicle");
|
||||
|
||||
auto spawn = std::make_shared<Group>("Spawn");
|
||||
auto settings = std::make_shared<Group>("Settings");
|
||||
|
||||
static std::string selectedGarageStr{""};
|
||||
|
||||
spawn->AddItem(std::make_unique<ImGuiItem>([] {
|
||||
if (!*Pointers.IsSessionStarted)
|
||||
return ImGui::TextDisabled("Join GTA Online.");
|
||||
|
||||
PersonalVehicles::Update();
|
||||
|
||||
static char search[64];
|
||||
ImGui::SetNextItemWidth(300.f);
|
||||
ImGui::InputTextWithHint("Name", "Search", search, sizeof(search));
|
||||
|
||||
ImGui::SetNextItemWidth(300.f);
|
||||
if (ImGui::BeginCombo("Garage", selectedGarageStr.empty() ? "All" : selectedGarageStr.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("All", selectedGarageStr.empty()))
|
||||
{
|
||||
selectedGarageStr.clear();
|
||||
}
|
||||
for (auto garage : PersonalVehicles::GetGarages())
|
||||
{
|
||||
if (ImGui::Selectable(garage.c_str(), garage == selectedGarageStr))
|
||||
{
|
||||
selectedGarageStr = garage;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
const int visible = std::min(20, static_cast<int>(PersonalVehicles::GetPersonalVehicles().size()));
|
||||
const float height = visible * ImGui::GetTextLineHeightWithSpacing();
|
||||
if (ImGui::BeginListBox("##personalvehicles", {300.f, height}))
|
||||
{
|
||||
if (PersonalVehicles::GetPersonalVehicles().empty())
|
||||
{
|
||||
ImGui::Text("Stats not loaded yet.");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string lowerSearch = search;
|
||||
std::transform(lowerSearch.begin(), lowerSearch.end(), lowerSearch.begin(), tolower);
|
||||
for (const auto& it : PersonalVehicles::GetPersonalVehicles())
|
||||
{
|
||||
const auto& label = it.first;
|
||||
const auto& personalVeh = it.second;
|
||||
|
||||
auto lowerName = label;
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
||||
|
||||
bool matchesSearch = lowerName.find(lowerSearch) != std::string::npos;
|
||||
bool matchesGarage = selectedGarageStr.empty() || personalVeh->GetGarage() == selectedGarageStr;
|
||||
if (matchesSearch && matchesGarage)
|
||||
{
|
||||
ImGui::PushID(personalVeh->GetId());
|
||||
if (ImGui::Selectable(label.c_str()))
|
||||
{
|
||||
FiberPool::Push([&personalVeh] {
|
||||
if (spawnClonePersonalVehicle.GetState())
|
||||
{
|
||||
auto coords = GetVehicleSpawnLoc(personalVeh->GetModel(), spawnInsidePersonalVehicle.GetState());
|
||||
auto heading = Self::GetPed().GetHeading();
|
||||
auto handle = personalVeh->Clone(coords, heading);
|
||||
|
||||
if (spawnInsidePersonalVehicle.GetState())
|
||||
Self::GetPed().SetInVehicle(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!personalVeh->Request(spawnInsidePersonalVehicle.GetState()))
|
||||
Notifications::Show("Spawn Personal Vehicle", "Failed to spawn Personal Vehicle.", NotificationType::Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}));
|
||||
|
||||
settings->AddItem(std::make_shared<BoolCommandItem>("spawninsidepv"_J));
|
||||
settings->AddItem(std::make_shared<BoolCommandItem>("spawnclonepv"_J));
|
||||
|
||||
|
||||
tab->AddItem(spawn);
|
||||
tab->AddItem(settings);
|
||||
return tab;
|
||||
}
|
||||
|
||||
std::shared_ptr<Category> BuildSpawnVehicleMenu()
|
||||
{
|
||||
auto menu = std::make_shared<Category>("Spawn");
|
||||
|
||||
menu->AddItem(std::make_shared<ImGuiItem>([] {
|
||||
ImGui::BeginTabBar("Spawn");
|
||||
}));
|
||||
menu->AddItem(RenderSpawnNewVehicle());
|
||||
menu->AddItem(RenderSpawnPersonalVehicle());
|
||||
menu->AddItem(std::make_shared<ImGuiItem>([] {
|
||||
ImGui::EndTabBar();
|
||||
}));
|
||||
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,20 @@ namespace YimMenu
|
||||
VEHICLE::SET_REDUCED_SUSPENSION_FORCE(GetHandle(), lower);
|
||||
}
|
||||
|
||||
void Vehicle::BringToHalt(float distance, int duration)
|
||||
{
|
||||
ENTITY_ASSERT_VALID();
|
||||
|
||||
VEHICLE::BRING_VEHICLE_TO_HALT(GetHandle(), distance, duration, FALSE);
|
||||
}
|
||||
|
||||
bool Vehicle::SetOnGroundProperly()
|
||||
{
|
||||
ENTITY_ASSERT_VALID();
|
||||
|
||||
return VEHICLE::SET_VEHICLE_ON_GROUND_PROPERLY(GetHandle(), 5.f);
|
||||
}
|
||||
|
||||
std::string Vehicle::GetFullName()
|
||||
{
|
||||
auto model = ENTITY::GET_ENTITY_MODEL(GetHandle());
|
||||
|
||||
@@ -36,6 +36,9 @@ namespace YimMenu
|
||||
// stance
|
||||
void LowerStance(bool lower);
|
||||
|
||||
void BringToHalt(float distance = 1.0f, int duration = 1);
|
||||
bool SetOnGroundProperly();
|
||||
|
||||
std::string GetFullName();
|
||||
};
|
||||
}
|
||||
@@ -100,6 +100,18 @@ namespace YimMenu::Lua
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int BringToHalt(lua_State* state)
|
||||
{
|
||||
GetObject<YimMenu::Vehicle>(state, 1).BringToHalt(luaL_checknumber(state, 2), luaL_checkinteger(state, 3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SetOnGroundProperly(lua_State* state)
|
||||
{
|
||||
lua_pushboolean(state, GetObject<YimMenu::Vehicle>(state, 1).SetOnGroundProperly());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GetFullName(lua_State* state)
|
||||
{
|
||||
auto name = GetObject<YimMenu::Vehicle>(state, 1).GetFullName();
|
||||
@@ -126,6 +138,8 @@ namespace YimMenu::Lua
|
||||
SetFunction(state, IsBoostActive, "is_boost_active");
|
||||
SetFunction(state, SetBoostCharge, "set_boost_charge");
|
||||
SetFunction(state, LowerStance, "lower_stance");
|
||||
SetFunction(state, BringToHalt, "bring_to_halt");
|
||||
SetFunction(state, SetOnGroundProperly, "set_on_ground_properly");
|
||||
SetFunction(state, GetFullName, "get_full_name");
|
||||
}
|
||||
lua_setfield(state, -2, "__index"); // prototype
|
||||
|
||||
@@ -58,7 +58,15 @@ struct FreemodeGeneral
|
||||
{
|
||||
uint64_t PAD_0000[301];
|
||||
VEHICLE_INDEX PersonalVehicleIndex; // 301
|
||||
uint64_t PAD_0302[6553];
|
||||
uint64_t PAD_0302[653];
|
||||
SCR_BOOL PersonalVehicleRequested; // 955
|
||||
uint64_t PAD_0956[13];
|
||||
SCR_BOOL NodeDistanceCheck; // 969
|
||||
uint64_t PAD_0970[48];
|
||||
SCR_INT RequestedPersonalVehicleId; // 1018
|
||||
uint64_t PAD_1019[2];
|
||||
SCR_BOOL Exec1Impound; // 1021
|
||||
uint64_t PAD_1022[5833];
|
||||
STREET_DEALERS StreetDealers; // 6855
|
||||
uint64_t PAD_6879[71];
|
||||
DAILY_RESET_DATA DailyReset; // 6950
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
#pragma once
|
||||
#include "types/script/types.hpp"
|
||||
|
||||
enum class ePersonalVehicleFlags
|
||||
{
|
||||
TRIGGER_SPAWN_TOGGLE = 0,
|
||||
DESTROYED = 1,
|
||||
HAS_INSURANCE = 2,
|
||||
UNK0 = 3,
|
||||
IMPOUNDED = 6,
|
||||
UNK1 = 10,
|
||||
SPAWN_AT_MORS_MUTUAL = 11,
|
||||
UNK2 = 16
|
||||
};
|
||||
|
||||
struct MPSV_Entry
|
||||
{
|
||||
SCR_INT NumberPlateTextIndex; // 0 (SET/GET_VEHICLE_NUMBER_PLATE_TEXT)
|
||||
@@ -41,7 +53,7 @@ struct MPSV_Entry
|
||||
SCR_INT ImportExportVehicle; // 101
|
||||
SCR_INT TyreType; // 102
|
||||
SCR_BITSET<uint64_t> Flags3; // 103 (flag 0 is set if the MLJ decor exists)
|
||||
SCR_BITSET<uint64_t> PersonalVehicleFlags; // 104
|
||||
SCR_BITSET<ePersonalVehicleFlags> PersonalVehicleFlags; // 104
|
||||
SCR_INT ImpoundTime; // 105 (MP_STAT_MPSV_IMPOUND_TIME)
|
||||
SCR_INT PremiumPaid; // 106 (MP_STAT_MPSV_PREMIUM_PAID)
|
||||
TEXT_LABEL_63 DestroyerName; // 107
|
||||
|
||||
Reference in New Issue
Block a user