From bdb307125f215c950d3ac4c5e6915e4070d08ec3 Mon Sep 17 00:00:00 2001 From: gir489 <100792176+gir489returns@users.noreply.github.com> Date: Sat, 17 Aug 2024 06:03:26 -0400 Subject: [PATCH] Added ownership checks to the delete_vehicle check. (#3552) * Added ownership checks to the delete_vehicle check. * Redesigned delete_vehicle protections. * Cleaned up code. * Fixed vehicle repaired/vehicle godmode code not running if the player was offline/did not have a personal vehicle deployed. * Fixed Chinese being a simp. (Closes #3581) * GTAV-Classes: bump version --- cmake/gtav-classes.cmake | 2 +- .../looped/vehicle/keep_vehicle_repaired.cpp | 7 +- src/backend/looped/vehicle/vehicle_god.cpp | 259 +++++----- src/core/data/language_codes.hpp | 2 +- .../protections/received_clone_remove.cpp | 37 +- src/util/entity.cpp | 2 +- src/util/mobile.hpp | 445 +++++++++--------- 7 files changed, 392 insertions(+), 362 deletions(-) diff --git a/cmake/gtav-classes.cmake b/cmake/gtav-classes.cmake index 78b091fa..079284b4 100644 --- a/cmake/gtav-classes.cmake +++ b/cmake/gtav-classes.cmake @@ -3,7 +3,7 @@ include(FetchContent) FetchContent_Declare( gtav_classes GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git - GIT_TAG b9b832ab00c95a731f8472f696c5d026a29fd767 + GIT_TAG aebd69542e58fab8975da76c3e555b122ddef5d6 GIT_PROGRESS TRUE CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/src/backend/looped/vehicle/keep_vehicle_repaired.cpp b/src/backend/looped/vehicle/keep_vehicle_repaired.cpp index 90670ccf..13497c47 100644 --- a/src/backend/looped/vehicle/keep_vehicle_repaired.cpp +++ b/src/backend/looped/vehicle/keep_vehicle_repaired.cpp @@ -18,9 +18,12 @@ namespace big return; } - if (!entity::take_control_of(veh, 0)) + if (*g_pointers->m_gta.m_is_session_started) { - return; + if (!entity::take_control_of(veh, 0)) + { + return; + } } if (VEHICLE::GET_DOES_VEHICLE_HAVE_DAMAGE_DECALS(veh)) diff --git a/src/backend/looped/vehicle/vehicle_god.cpp b/src/backend/looped/vehicle/vehicle_god.cpp index 7f9e8cf9..07e288f8 100644 --- a/src/backend/looped/vehicle/vehicle_god.cpp +++ b/src/backend/looped/vehicle/vehicle_god.cpp @@ -1,135 +1,126 @@ -#include "backend/looped/looped.hpp" -#include "backend/looped_command.hpp" -#include "natives.hpp" -#include "util/mobile.hpp" - -namespace big -{ - class vehicle_godmode_internal : looped_command - { - using looped_command::looped_command; - - static constexpr uint8_t deform_god_bit = 1 << 4; - - struct orig_veh_data - { - uint8_t m_deform_god = 0; - uint32_t m_damage_bits = 0; - uint32_t m_last_damage_bits = 0; - }; - - std::unordered_map m_orig_veh_datas; - - void restore_original_vehicle_data(CVehicle* vehicle) - { - if (m_orig_veh_datas.contains(vehicle)) - { - vehicle->m_deform_god = m_orig_veh_datas[vehicle].m_deform_god; - vehicle->m_damage_bits = m_orig_veh_datas[vehicle].m_damage_bits; - - m_orig_veh_datas.erase(vehicle); - } - } - - bool restore_vehicle_data_when_leaving_it(CVehicle* vehicle) - { - const auto is_not_in_vehicle = !PED::GET_PED_CONFIG_FLAG(self::ped, 62, false); - if (is_not_in_vehicle) - { - restore_original_vehicle_data(vehicle); - - return true; - } - - return false; - } - - void save_original_veh_data(CVehicle* vehicle) - { - if (!m_orig_veh_datas.contains(vehicle)) - { - m_orig_veh_datas[vehicle].m_deform_god = vehicle->m_deform_god; - m_orig_veh_datas[vehicle].m_damage_bits = vehicle->m_damage_bits; - } - } - - void apply_godmode_to_vehicle(CVehicle* vehicle, bool personal_vehicle = false) - { - if (vehicle == nullptr) - { - return; - } - - if (!personal_vehicle) - { - if (restore_vehicle_data_when_leaving_it(vehicle)) - { - return; - } - } - - save_original_veh_data(vehicle); - - if (g.vehicle.god_mode || g.vehicle.proof_collision) - { - vehicle->m_deform_god &= ~(deform_god_bit); - } - else - { - vehicle->m_deform_god |= deform_god_bit; - } - - uint32_t bits = g.vehicle.proof_mask; - uint32_t changed_bits = bits ^ m_orig_veh_datas[vehicle].m_last_damage_bits; - uint32_t changed_or_enabled_bits = bits | changed_bits; - - if (changed_or_enabled_bits) - { - uint32_t unchanged_bits = vehicle->m_damage_bits & ~changed_or_enabled_bits; - vehicle->m_damage_bits = unchanged_bits | bits; - m_orig_veh_datas[vehicle].m_last_damage_bits = bits; - } - } - - CVehicle* get_personal_vehicle() - { - Vehicle personal_vehicle = mobile::mechanic::get_personal_vehicle(); - if (ENTITY::DOES_ENTITY_EXIST(personal_vehicle)) - { - return reinterpret_cast(g_pointers->m_gta.m_handle_to_ptr(personal_vehicle)); - } - - return nullptr; - } - - virtual void on_tick() override - { - if (g_local_player) - { - const auto personal_vehicle = get_personal_vehicle(); - apply_godmode_to_vehicle(personal_vehicle, true); - apply_godmode_to_vehicle(g_local_player->m_vehicle, personal_vehicle == g_local_player->m_vehicle); - } - } - - virtual void on_disable() override - { - if (g_local_player) - { - if (g_local_player->m_vehicle) - restore_original_vehicle_data(g_local_player->m_vehicle); - - const auto personal_vehicle = get_personal_vehicle(); - if (personal_vehicle) - { - restore_original_vehicle_data(personal_vehicle); - } - } - } - }; - - static bool true_ref = true; - vehicle_godmode_internal g_vehicle_godmode_internal("$$vehgodmode", "", "", true_ref); - bool_command g_vehicle_godmode("vehgodmode", "VEHICLE_GOD", "VEHICLE_GOD_DESC", - g.vehicle.god_mode); +#include "backend/looped/looped.hpp" +#include "backend/looped_command.hpp" +#include "natives.hpp" +#include "util/mobile.hpp" + +namespace big +{ + class vehicle_godmode_internal : looped_command + { + using looped_command::looped_command; + + static constexpr uint8_t deform_god_bit = 1 << 4; + + struct orig_veh_data + { + uint8_t m_deform_god = 0; + uint32_t m_damage_bits = 0; + uint32_t m_last_damage_bits = 0; + }; + + std::unordered_map m_orig_veh_datas; + + void restore_original_vehicle_data(CVehicle* vehicle) + { + if (m_orig_veh_datas.contains(vehicle)) + { + vehicle->m_deform_god = m_orig_veh_datas[vehicle].m_deform_god; + vehicle->m_damage_bits = m_orig_veh_datas[vehicle].m_damage_bits; + + m_orig_veh_datas.erase(vehicle); + } + } + + bool restore_vehicle_data_when_leaving_it(CVehicle* vehicle) + { + const auto is_not_in_vehicle = !PED::GET_PED_CONFIG_FLAG(self::ped, 62, false); + if (is_not_in_vehicle) + { + restore_original_vehicle_data(vehicle); + + return true; + } + + return false; + } + + void save_original_veh_data(CVehicle* vehicle) + { + if (!m_orig_veh_datas.contains(vehicle)) + { + m_orig_veh_datas[vehicle].m_deform_god = vehicle->m_deform_god; + m_orig_veh_datas[vehicle].m_damage_bits = vehicle->m_damage_bits; + } + } + + void apply_godmode_to_vehicle(CVehicle* vehicle, bool personal_vehicle = false) + { + if (vehicle == nullptr) + { + return; + } + + if (!personal_vehicle) + { + if (restore_vehicle_data_when_leaving_it(vehicle)) + { + return; + } + } + + save_original_veh_data(vehicle); + + if (g.vehicle.god_mode || g.vehicle.proof_collision) + { + vehicle->m_deform_god &= ~(deform_god_bit); + } + else + { + vehicle->m_deform_god |= deform_god_bit; + } + + uint32_t bits = g.vehicle.proof_mask; + uint32_t changed_bits = bits ^ m_orig_veh_datas[vehicle].m_last_damage_bits; + uint32_t changed_or_enabled_bits = bits | changed_bits; + + if (changed_or_enabled_bits) + { + uint32_t unchanged_bits = vehicle->m_damage_bits & ~changed_or_enabled_bits; + vehicle->m_damage_bits = unchanged_bits | bits; + m_orig_veh_datas[vehicle].m_last_damage_bits = bits; + } + } + + virtual void on_tick() override + { + if (g_local_player) + { + const auto personal_vehicle = mobile::mechanic::get_personal_cvehicle(); + if (personal_vehicle) + { + apply_godmode_to_vehicle(personal_vehicle, true); + } + apply_godmode_to_vehicle(g_local_player->m_vehicle, personal_vehicle == g_local_player->m_vehicle); + } + } + + virtual void on_disable() override + { + if (g_local_player) + { + if (g_local_player->m_vehicle) + restore_original_vehicle_data(g_local_player->m_vehicle); + + if (const auto personal_vehicle = mobile::mechanic::get_personal_cvehicle()) + { + restore_original_vehicle_data(personal_vehicle); + } + } + } + }; + + static bool true_ref = true; + vehicle_godmode_internal g_vehicle_godmode_internal("$$vehgodmode", "", "", true_ref); + bool_command g_vehicle_godmode("vehgodmode", "VEHICLE_GOD", "VEHICLE_GOD_DESC", + g.vehicle.god_mode); } \ No newline at end of file diff --git a/src/core/data/language_codes.hpp b/src/core/data/language_codes.hpp index 81bd3268..73785b81 100644 --- a/src/core/data/language_codes.hpp +++ b/src/core/data/language_codes.hpp @@ -16,6 +16,6 @@ namespace big {eGameLanguage::TRADITIONAL_CHINESE, "Chinese (Traditional)"}, {eGameLanguage::JAPANESE, "Japanese"}, {eGameLanguage::MEXICAN_SPANISH, "Spanish (Mexico)"}, - {eGameLanguage::SIMPLIFIED_CHINESE, "Chinese (Simpified)"}, + {eGameLanguage::SIMPLIFIED_CHINESE, "Chinese (Simplified)"}, }; } diff --git a/src/hooks/protections/received_clone_remove.cpp b/src/hooks/protections/received_clone_remove.cpp index d588000a..9b2aa5ad 100644 --- a/src/hooks/protections/received_clone_remove.cpp +++ b/src/hooks/protections/received_clone_remove.cpp @@ -1,4 +1,5 @@ #include "hooking/hooking.hpp" +#include "util/mobile.hpp" #include "util/notify.hpp" namespace big @@ -12,16 +13,40 @@ namespace big return; } - if (g_local_player && g_local_player->m_vehicle && g_local_player->m_vehicle->m_net_object - && object_id == g_local_player->m_vehicle->m_net_object->m_object_id) [[unlikely]] + if (const auto personal_vehicle = mobile::mechanic::get_personal_cvehicle()) { - if (!NETWORK::NETWORK_IS_ACTIVITY_SESSION()) //If we're in Freemode. + if (auto net_obj = personal_vehicle->m_net_object) { - if (auto plyr = g_player_service->get_by_id(src->m_player_id)) + if (object_id == net_obj->m_object_id && entity::network_has_control_of_entity(net_obj)) { - g.reactions.delete_vehicle.process(plyr); + /*LOG(VERBOSE) << "DELETION ORDER FOR PERSONAL VEHICLE: " << net_obj->m_object_id << " m_is_remote: " << net_obj->m_is_remote + << " m_bubble: " << net_obj->m_bubble << " m_owner_id: " << net_obj->m_owner_id + << " m_wants_to_be_owner: " << net_obj->m_wants_to_be_owner;*/ + if (auto plyr = g_player_service->get_by_id(src->m_player_id)) + { + g.reactions.delete_vehicle.process(plyr); + } + return; + } + } + } + + if (self::veh != 0) + { + if (const auto current_vehicle = g_local_player->m_vehicle) + { + auto net_obj = current_vehicle->m_net_object; + if (object_id == net_obj->m_object_id && entity::network_has_control_of_entity(net_obj)) + { + /*LOG(VERBOSE) << "DELETION ORDER FOR CURRENT VEHICLE: " << net_obj->m_object_id + << " m_is_remote: " << net_obj->m_is_remote << " m_bubble: " << net_obj->m_bubble + << " m_owner_id: " << net_obj->m_owner_id << " m_wants_to_be_owner: " << net_obj->m_wants_to_be_owner;*/ + if (auto plyr = g_player_service->get_by_id(src->m_player_id)) + { + g.reactions.delete_vehicle.process(plyr); + } + return; } - return; } } diff --git a/src/util/entity.cpp b/src/util/entity.cpp index 92f7848e..7fc4b5f1 100644 --- a/src/util/entity.cpp +++ b/src/util/entity.cpp @@ -159,7 +159,7 @@ namespace big::entity bool network_has_control_of_entity(rage::netObject* net_object) { - return !net_object || !net_object->m_next_owner_id && (net_object->m_control_id == -1); + return !net_object || !net_object->m_is_remote && (net_object->m_wants_to_be_owner == -1); } bool take_control_of(Entity ent, int timeout) diff --git a/src/util/mobile.hpp b/src/util/mobile.hpp index 11cc751a..6e64f0a4 100644 --- a/src/util/mobile.hpp +++ b/src/util/mobile.hpp @@ -1,217 +1,228 @@ -#pragma once -#include "core/enums.hpp" -#include "core/scr_globals.hpp" -#include "globals.hpp" -#include "gta_util.hpp" -#include "misc.hpp" -#include "natives.hpp" -#include "notify.hpp" -#include "script.hpp" -#include "core/scr_globals.hpp" -#include "script_local.hpp" -#include "vehicle.hpp" - -namespace big::mobile -{ - namespace util - { - int get_current_personal_vehicle(); // forward declare - inline void despawn_current_personal_vehicle() - { - misc::clear_bits(scr_globals::vehicle_global.at(get_current_personal_vehicle(), 142).at(103).as(), eVehicleFlags::TRIGGER_SPAWN_TOGGLE); - } - - inline int get_current_personal_vehicle() - { - return *scr_globals::stats.at(0, 5568).at(681).at(2).as(); - } - } - - namespace merry_weather - { - inline void request_ammo_drop() - { - *scr_globals::freemode_global.at(906).as() = 1; - } - - inline void request_boat_pickup() - { - *scr_globals::freemode_global.at(907).as() = 1; - } - - inline void request_helicopter_pickup() - { - *scr_globals::freemode_global.at(908).as() = 1; - } - - inline void request_backup_helicopter() - { - *scr_globals::freemode_global.at(4506).as() = 1; - } - - inline void request_airstrike() - { - *scr_globals::freemode_global.at(4507).as() = 1; - } - } - - namespace mors_mutual - { - inline bool fix_index(int veh_idx, bool spawn_veh = false) - { - bool can_be_fixed = misc::has_bits_set(scr_globals::vehicle_global.at(veh_idx, 142).at(103).as(), eVehicleFlags::DESTROYED | eVehicleFlags::HAS_INSURANCE); - - if (can_be_fixed) - { - misc::clear_bits(scr_globals::vehicle_global.at(veh_idx, 142).at(103).as(), eVehicleFlags::DESTROYED | eVehicleFlags::IMPOUNDED | eVehicleFlags::UNK2); - - if (spawn_veh) - { - misc::set_bits(scr_globals::vehicle_global.at(veh_idx, 142).at(103).as(), eVehicleFlags::TRIGGER_SPAWN_TOGGLE | eVehicleFlags::SPAWN_AT_MORS_MUTUAL); - } - } - return can_be_fixed; - } - - inline int fix_all() - { - int fixed_count = 0; - - const int arr_size = *scr_globals::vehicle_global.as(); - for (int i = 0; i < arr_size; i++) - if (fix_index(i, true)) - fixed_count++; - - return fixed_count; - } - } - - namespace ceo_abilities - { - inline void request_bullshark_testosterone() - { - *scr_globals::freemode_properties.at(3733).as() = 1; - } - - inline void request_ballistic_armor() //i think this is a ceo ability atleast? - { - *scr_globals::freemode_global.at(906).as() = 1; - } - } - - namespace services - { - inline void request_avenger() - { - *scr_globals::freemode_global.at(953).as() = 1; - } - - inline void request_kosatka() - { - *scr_globals::freemode_global.at(975).as() = 1; - } - - inline void request_mobile_operations_center() - { - *scr_globals::freemode_global.at(945).as() = 1; - } - - inline void request_terrorbyte() - { - *scr_globals::freemode_global.at(958).as() = 1; - } - - inline void request_acidlab() - { - *scr_globals::freemode_global.at(959).as() = 1; - } - - inline void request_acidlab_bike() - { - *scr_globals::freemode_global.at(1009).as() = 1; - } - } - - namespace mechanic - { - inline Vehicle get_personal_vehicle() - { - return *scr_globals::freemode_global.at(301).as(); - } - - inline void summon_vehicle_by_index(int veh_idx) - { - if (*scr_globals::freemode_global.at(1000).as() != -1) - return g_notification_service.push_warning("VEHICLE"_T.data(), "VEHICLE_MECHANIC_BUSY"_T.data()); - - if (g.clone_pv.spawn_inside && self::veh) - TASK::CLEAR_PED_TASKS_IMMEDIATELY(PLAYER::PLAYER_PED_ID()); - - // despawn current veh - util::despawn_current_personal_vehicle(); - mors_mutual::fix_index(veh_idx); - - script::get_current()->yield(100ms); - - // only do this when spawn inside is enabled otherwise the vehicle will spawn relatively far away from players - if (g.clone_pv.spawn_inside) - { - *scr_globals::freemode_global.at(957).as() = 1; // disable vehicle node distance check - } - *scr_globals::freemode_global.at(943).as() = 1; // tell freemode to spawn our vehicle - *scr_globals::freemode_global.at(1003).as() = 0; // required - *scr_globals::freemode_global.at(1000).as() = veh_idx; - - script::get_current()->yield(100ms); - - GtaThread* freemode_thread = gta_util::find_script_thread("freemode"_J); - if (freemode_thread) - { - // regex to find this shit easily - // \(func_\d{3}\(&\(uParam0->f_\d{3}\), \d+000, 0\) \|\| func - // or if you prefer a string "VD_FAIL4" - // or if you really prefer an image https://i.imgur.com/K8vMILe.png - *scr_locals::freemode::mobile.set(freemode_thread).at(176).as() = 0; // spawn vehicle instantly - } - - // blocking call till vehicle is delivered - notify::busy_spinner("Delivering vehicle...", scr_globals::freemode_global.at(1000).as(), -1); - - if (g.clone_pv.spawn_inside) - { - vehicle::bring(get_personal_vehicle(), self::pos, true); - } - } - } - - namespace mobile_misc - { - inline void request_taxi() - { - *scr_globals::freemode_global.at(868).as() = 1; - } - - inline void request_gun_van() - { - auto local_pos = self::pos; - auto forward_vector = ENTITY::GET_ENTITY_FORWARD_VECTOR(self::ped); - Vector3 spawn_point; - - if (MISC::FIND_SPAWN_POINT_IN_DIRECTION(local_pos.x, - local_pos.y, - local_pos.z, - forward_vector.x, - forward_vector.y, - forward_vector.z, - 25.f, - &spawn_point)) - { - *scr_globals::gun_van.as() = spawn_point; - - return g_notification_service.push_success("GUI_TAB_MOBILE"_T.data(), "REQUEST_GUN_VAN_NOTIFY_SUCCESS"_T.data()); - } - - g_notification_service.push_warning("GUI_TAB_MOBILE"_T.data(), "REQUEST_GUN_VAN_NOTIFY_FAILED"_T.data()); - } - } -} +#pragma once +#include "core/enums.hpp" +#include "core/scr_globals.hpp" +#include "globals.hpp" +#include "gta_util.hpp" +#include "misc.hpp" +#include "natives.hpp" +#include "notify.hpp" +#include "script.hpp" +#include "core/scr_globals.hpp" +#include "script_local.hpp" +#include "vehicle.hpp" + +namespace big::mobile +{ + namespace util + { + int get_current_personal_vehicle(); // forward declare + inline void despawn_current_personal_vehicle() + { + misc::clear_bits(scr_globals::vehicle_global.at(get_current_personal_vehicle(), 142).at(103).as(), eVehicleFlags::TRIGGER_SPAWN_TOGGLE); + } + + inline int get_current_personal_vehicle() + { + return *scr_globals::stats.at(0, 5568).at(681).at(2).as(); + } + } + + namespace merry_weather + { + inline void request_ammo_drop() + { + *scr_globals::freemode_global.at(906).as() = 1; + } + + inline void request_boat_pickup() + { + *scr_globals::freemode_global.at(907).as() = 1; + } + + inline void request_helicopter_pickup() + { + *scr_globals::freemode_global.at(908).as() = 1; + } + + inline void request_backup_helicopter() + { + *scr_globals::freemode_global.at(4506).as() = 1; + } + + inline void request_airstrike() + { + *scr_globals::freemode_global.at(4507).as() = 1; + } + } + + namespace mors_mutual + { + inline bool fix_index(int veh_idx, bool spawn_veh = false) + { + bool can_be_fixed = misc::has_bits_set(scr_globals::vehicle_global.at(veh_idx, 142).at(103).as(), eVehicleFlags::DESTROYED | eVehicleFlags::HAS_INSURANCE); + + if (can_be_fixed) + { + misc::clear_bits(scr_globals::vehicle_global.at(veh_idx, 142).at(103).as(), eVehicleFlags::DESTROYED | eVehicleFlags::IMPOUNDED | eVehicleFlags::UNK2); + + if (spawn_veh) + { + misc::set_bits(scr_globals::vehicle_global.at(veh_idx, 142).at(103).as(), eVehicleFlags::TRIGGER_SPAWN_TOGGLE | eVehicleFlags::SPAWN_AT_MORS_MUTUAL); + } + } + return can_be_fixed; + } + + inline int fix_all() + { + int fixed_count = 0; + + const int arr_size = *scr_globals::vehicle_global.as(); + for (int i = 0; i < arr_size; i++) + if (fix_index(i, true)) + fixed_count++; + + return fixed_count; + } + } + + namespace ceo_abilities + { + inline void request_bullshark_testosterone() + { + *scr_globals::freemode_properties.at(3733).as() = 1; + } + + inline void request_ballistic_armor() //i think this is a ceo ability atleast? + { + *scr_globals::freemode_global.at(906).as() = 1; + } + } + + namespace services + { + inline void request_avenger() + { + *scr_globals::freemode_global.at(953).as() = 1; + } + + inline void request_kosatka() + { + *scr_globals::freemode_global.at(975).as() = 1; + } + + inline void request_mobile_operations_center() + { + *scr_globals::freemode_global.at(945).as() = 1; + } + + inline void request_terrorbyte() + { + *scr_globals::freemode_global.at(958).as() = 1; + } + + inline void request_acidlab() + { + *scr_globals::freemode_global.at(959).as() = 1; + } + + inline void request_acidlab_bike() + { + *scr_globals::freemode_global.at(1009).as() = 1; + } + } + + namespace mechanic + { + inline Vehicle get_personal_vehicle() + { + return *scr_globals::freemode_global.at(301).as(); + } + + inline CVehicle* get_personal_cvehicle() + { + Vehicle personal_vehicle = get_personal_vehicle(); + if (personal_vehicle != -1 && ENTITY::DOES_ENTITY_EXIST(personal_vehicle)) + { + return reinterpret_cast(g_pointers->m_gta.m_handle_to_ptr(personal_vehicle)); + } + + return nullptr; + } + + inline void summon_vehicle_by_index(int veh_idx) + { + if (*scr_globals::freemode_global.at(1000).as() != -1) + return g_notification_service.push_warning("VEHICLE"_T.data(), "VEHICLE_MECHANIC_BUSY"_T.data()); + + if (g.clone_pv.spawn_inside && self::veh) + TASK::CLEAR_PED_TASKS_IMMEDIATELY(PLAYER::PLAYER_PED_ID()); + + // despawn current veh + util::despawn_current_personal_vehicle(); + mors_mutual::fix_index(veh_idx); + + script::get_current()->yield(100ms); + + // only do this when spawn inside is enabled otherwise the vehicle will spawn relatively far away from players + if (g.clone_pv.spawn_inside) + { + *scr_globals::freemode_global.at(957).as() = 1; // disable vehicle node distance check + } + *scr_globals::freemode_global.at(943).as() = 1; // tell freemode to spawn our vehicle + *scr_globals::freemode_global.at(1003).as() = 0; // required + *scr_globals::freemode_global.at(1000).as() = veh_idx; + + script::get_current()->yield(100ms); + + GtaThread* freemode_thread = gta_util::find_script_thread("freemode"_J); + if (freemode_thread) + { + // regex to find this shit easily + // \(func_\d{3}\(&\(uParam0->f_\d{3}\), \d+000, 0\) \|\| func + // or if you prefer a string "VD_FAIL4" + // or if you really prefer an image https://i.imgur.com/K8vMILe.png + *scr_locals::freemode::mobile.set(freemode_thread).at(176).as() = 0; // spawn vehicle instantly + } + + // blocking call till vehicle is delivered + notify::busy_spinner("Delivering vehicle...", scr_globals::freemode_global.at(1000).as(), -1); + + if (g.clone_pv.spawn_inside) + { + vehicle::bring(get_personal_vehicle(), self::pos, true); + } + } + } + + namespace mobile_misc + { + inline void request_taxi() + { + *scr_globals::freemode_global.at(868).as() = 1; + } + + inline void request_gun_van() + { + auto local_pos = self::pos; + auto forward_vector = ENTITY::GET_ENTITY_FORWARD_VECTOR(self::ped); + Vector3 spawn_point; + + if (MISC::FIND_SPAWN_POINT_IN_DIRECTION(local_pos.x, + local_pos.y, + local_pos.z, + forward_vector.x, + forward_vector.y, + forward_vector.z, + 25.f, + &spawn_point)) + { + *scr_globals::gun_van.as() = spawn_point; + + return g_notification_service.push_success("GUI_TAB_MOBILE"_T.data(), "REQUEST_GUN_VAN_NOTIFY_SUCCESS"_T.data()); + } + + g_notification_service.push_warning("GUI_TAB_MOBILE"_T.data(), "REQUEST_GUN_VAN_NOTIFY_FAILED"_T.data()); + } + } +}