2023-04-05 19:54:29 +02:00
|
|
|
#include "vehicle_control_service.hpp"
|
|
|
|
|
|
|
|
#include "backend/looped_command.hpp"
|
|
|
|
#include "gui.hpp"
|
|
|
|
#include "natives.hpp"
|
|
|
|
#include "pointers.hpp"
|
|
|
|
#include "util/pathfind.hpp"
|
|
|
|
#include "util/ped.hpp"
|
|
|
|
#include "util/vehicle.hpp"
|
2023-11-04 00:22:07 +01:00
|
|
|
#include "util/mobile.hpp"
|
2023-04-05 19:54:29 +02:00
|
|
|
|
|
|
|
namespace big
|
|
|
|
{
|
|
|
|
class vehicle_control_command : looped_command
|
|
|
|
{
|
|
|
|
using looped_command::looped_command;
|
|
|
|
|
|
|
|
virtual void on_enable() override
|
|
|
|
{
|
|
|
|
g_gui->override_mouse(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void on_tick() override
|
|
|
|
{
|
|
|
|
if (!g_gui->mouse_override())
|
|
|
|
{
|
|
|
|
g_gui->override_mouse(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void on_disable() override
|
|
|
|
{
|
|
|
|
g_gui->override_mouse(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
vehicle_control_command g_vehicle_control("vehiclecontrol", "Vehicle controller", "Enables/Disables the vehicle controller.",
|
|
|
|
g.window.vehicle_control.opened);
|
|
|
|
|
|
|
|
void update_controlled_vehicle_doors(controlled_vehicle& veh)
|
|
|
|
{
|
|
|
|
vehicle_door veh_door{};
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_VEHICLE_DOORS; i++)
|
|
|
|
{
|
|
|
|
if (!VEHICLE::GET_IS_DOOR_VALID(veh.handle, i))
|
|
|
|
{
|
|
|
|
veh.doors[i].valid = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
veh_door.valid = true;
|
|
|
|
veh_door.id = (eDoorId)i;
|
2023-12-29 16:07:00 +00:00
|
|
|
veh_door.lock_state = (eVehicleLockState)VEHICLE::GET_VEHICLE_INDIVIDUAL_DOOR_LOCK_STATUS(veh.handle, i);
|
2023-04-05 19:54:29 +02:00
|
|
|
veh_door.open = VEHICLE::GET_VEHICLE_DOOR_ANGLE_RATIO(veh.handle, i) > 0;
|
|
|
|
veh_door.doorAngle = VEHICLE::GET_VEHICLE_DOOR_ANGLE_RATIO(veh.handle, i);
|
|
|
|
|
|
|
|
veh.doors[i] = veh_door;
|
|
|
|
}
|
|
|
|
|
2023-12-29 16:07:00 +00:00
|
|
|
veh.lock_state = (eVehicleLockState)VEHICLE::GET_VEHICLE_DOOR_LOCK_STATUS(veh.handle);
|
2023-04-05 19:54:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void update_controlled_vehicle_lights(controlled_vehicle& veh)
|
|
|
|
{
|
2023-12-29 16:07:00 +00:00
|
|
|
VEHICLE::GET_VEHICLE_LIGHTS_STATE(veh.handle, (BOOL*)&veh.headlights, (BOOL*)&veh.highbeams);
|
2023-04-05 19:54:29 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
veh.neons[i] = VEHICLE::GET_VEHICLE_NEON_ENABLED(veh.handle, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
controlled_vehicle vehicle_control::update_vehicle(Vehicle veh)
|
|
|
|
{
|
2023-11-04 00:22:07 +01:00
|
|
|
if(!ENTITY::DOES_ENTITY_EXIST(veh))
|
|
|
|
return {};
|
|
|
|
|
2023-04-05 19:54:29 +02:00
|
|
|
controlled_vehicle new_veh{};
|
|
|
|
|
|
|
|
new_veh.handle = veh;
|
2023-04-14 18:54:07 +02:00
|
|
|
new_veh.ptr = (CVehicle*)g_pointers->m_gta.m_handle_to_ptr(veh);
|
2023-04-05 19:54:29 +02:00
|
|
|
strcpy(new_veh.model_name, HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(VEHICLE::GET_DISPLAY_NAME_FROM_VEHICLE_MODEL(ENTITY::GET_ENTITY_MODEL(veh))));
|
2023-12-29 16:07:00 +00:00
|
|
|
new_veh.door_count = VEHICLE::GET_NUMBER_OF_VEHICLE_DOORS(veh);
|
|
|
|
new_veh.lock_state = (eVehicleLockState)VEHICLE::GET_VEHICLE_DOOR_LOCK_STATUS(veh);
|
|
|
|
new_veh.is_convertible = VEHICLE::IS_VEHICLE_A_CONVERTIBLE(veh, 0);
|
2023-11-04 00:22:07 +01:00
|
|
|
|
2023-04-05 19:54:29 +02:00
|
|
|
update_controlled_vehicle_doors(new_veh);
|
|
|
|
update_controlled_vehicle_lights(new_veh);
|
|
|
|
|
|
|
|
return new_veh;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vehicle_control::keep_controlled_vehicle_data_updated(controlled_vehicle& veh)
|
|
|
|
{
|
|
|
|
if (!m_controlled_vehicle_exists)
|
|
|
|
return;
|
|
|
|
|
|
|
|
update_controlled_vehicle_doors(veh);
|
|
|
|
update_controlled_vehicle_lights(veh);
|
|
|
|
|
2023-12-29 16:07:00 +00:00
|
|
|
veh.convertible_state = VEHICLE::GET_CONVERTIBLE_ROOF_STATE(veh.handle);
|
2023-04-05 19:54:29 +02:00
|
|
|
veh.engine = VEHICLE::GET_IS_VEHICLE_ENGINE_RUNNING(veh.handle);
|
2023-11-04 00:22:07 +01:00
|
|
|
|
2023-04-05 19:54:29 +02:00
|
|
|
if (g.window.vehicle_control.render_distance_on_veh
|
|
|
|
&& math::distance_between_vectors(self::pos, ENTITY::GET_ENTITY_COORDS(m_controlled_vehicle.handle, true)) > 10.f)
|
|
|
|
vehicle_control::render_distance_on_vehicle();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Imitated from freemode.c script, findable by searching for MISC::GET_HASH_KEY("BONEMASK_HEAD_NECK_AND_R_ARM");
|
|
|
|
* Script uses TASK::TASK_SCRIPTED_ANIMATION but can be dissected to use as follows
|
|
|
|
*/
|
2023-11-04 00:22:07 +01:00
|
|
|
void vehicle_control::vehicle_operation(std::function<void()> operation)
|
2023-04-05 19:54:29 +02:00
|
|
|
{
|
2023-11-04 00:22:07 +01:00
|
|
|
if (g.window.vehicle_control.operation_animation)
|
2023-04-05 19:54:29 +02:00
|
|
|
{
|
2023-11-04 00:22:07 +01:00
|
|
|
ped::ped_play_animation(self::ped, VEH_OP_ANIM_DICT, VEH_OP_ANIM, 4, -4, -1, 48, 0, false);
|
|
|
|
AUDIO::PLAY_SOUND_FROM_ENTITY(-1, "Remote_Control_Fob", self::ped, "PI_Menu_Sounds", true, 0);
|
|
|
|
script::get_current()->yield(100ms);
|
|
|
|
for (int i = 0; i < 35 && ENTITY::IS_ENTITY_PLAYING_ANIM(self::ped, VEH_OP_ANIM_DICT, VEH_OP_ANIM, 3); i++)
|
|
|
|
{
|
|
|
|
script::get_current()->yield(10ms);
|
|
|
|
}
|
2023-04-05 19:54:29 +02:00
|
|
|
}
|
2023-07-05 23:51:19 +02:00
|
|
|
|
2023-11-04 00:22:07 +01:00
|
|
|
if (entity::take_control_of(m_controlled_vehicle.handle))
|
|
|
|
operation();
|
2023-04-05 19:54:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void vehicle_control::driver_tick()
|
|
|
|
{
|
|
|
|
if (!m_controlled_vehicle_exists)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ENTITY::DOES_ENTITY_EXIST(m_driver))
|
|
|
|
{
|
|
|
|
int task = 0;
|
|
|
|
for (int i = 0; i < ped::task_names.size(); i++)
|
|
|
|
{
|
|
|
|
if (TASK::GET_IS_TASK_ACTIVE(m_driver, i))
|
|
|
|
task = i;
|
|
|
|
}
|
|
|
|
strcpy(m_currentask, ped::task_names.at(task));
|
|
|
|
|
|
|
|
if (!m_driver_performing_task)
|
|
|
|
{
|
|
|
|
if (entity::take_control_of(m_driver))
|
|
|
|
{
|
|
|
|
pathfind::remove_navmesh_required_areas();
|
|
|
|
entity::delete_entity(m_driver);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (math::distance_between_vectors(m_destination, ENTITY::GET_ENTITY_COORDS(m_driver, true)) < 10.f || math::distance_between_vectors(self::pos, ENTITY::GET_ENTITY_COORDS(m_driver, true)) < 10.f)
|
|
|
|
{
|
|
|
|
m_driver_performing_task = false;
|
|
|
|
VEHICLE::BRING_VEHICLE_TO_HALT(m_controlled_vehicle.handle, 6.f, 5, false);
|
|
|
|
pathfind::remove_navmesh_required_areas();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(m_currentask, "CTaskInVehicleBasic") == 0)
|
|
|
|
{
|
|
|
|
TASK::TASK_VEHICLE_DRIVE_TO_COORD(m_driver,
|
|
|
|
m_controlled_vehicle.handle,
|
|
|
|
m_destination.x,
|
|
|
|
m_destination.y,
|
|
|
|
m_destination.z,
|
|
|
|
100.f,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
786468,
|
|
|
|
4.f,
|
|
|
|
5.f);
|
|
|
|
//LOG(INFO) << "Navmesh probably failed, issiuing regular task ";
|
2024-03-23 00:04:49 +01:00
|
|
|
g_notification_service.push_warning("VEHICLE_CONTROLLER"_T.data(),
|
2023-06-05 15:46:20 -04:00
|
|
|
"VEHICLE_CONTROLLER_TRY_ALT_PATHFINDING"_T.data());
|
2023-04-05 19:54:29 +02:00
|
|
|
script::get_current()->yield(500ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_distance_to_destination =
|
|
|
|
math::distance_between_vectors(m_destination, ENTITY::GET_ENTITY_COORDS(m_controlled_vehicle.handle, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool vehicle_control::ensure_driver()
|
|
|
|
{
|
|
|
|
if (!m_controlled_vehicle_exists)
|
|
|
|
{
|
|
|
|
if (ENTITY::DOES_ENTITY_EXIST(m_driver) && entity::take_control_of(m_driver))
|
|
|
|
{
|
|
|
|
entity::delete_entity(m_driver);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!ENTITY::DOES_ENTITY_EXIST(m_driver))
|
|
|
|
{
|
|
|
|
//LOG(INFO) << "Driver didnt exist, creating one";
|
|
|
|
m_driver = ped::spawn(PED_TYPE_CIVMALE,
|
2024-03-12 09:42:11 +01:00
|
|
|
"s_m_y_devinsec_01"_J,
|
2023-04-05 19:54:29 +02:00
|
|
|
-1,
|
|
|
|
ENTITY::GET_ENTITY_COORDS(m_controlled_vehicle.handle, 1),
|
|
|
|
0,
|
|
|
|
true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entity::take_control_of(m_driver))
|
|
|
|
{
|
|
|
|
if (VEHICLE::GET_PED_IN_VEHICLE_SEAT(m_controlled_vehicle.handle, (int)eVehicleSeats::DRIVER, true) != m_driver)
|
|
|
|
{
|
|
|
|
//LOG(INFO) << "Driver wasnt in the driver seat";
|
|
|
|
PED::SET_PED_INTO_VEHICLE(m_driver, m_controlled_vehicle.handle, (int)eVehicleSeats::DRIVER);
|
|
|
|
}
|
|
|
|
|
|
|
|
//LOG(INFO) << "Changing driver attributes";
|
|
|
|
ENTITY::SET_ENTITY_INVINCIBLE(m_driver, true);
|
|
|
|
ENTITY::SET_ENTITY_VISIBLE(m_driver, false, false);
|
|
|
|
PED::SET_PED_COMBAT_ATTRIBUTES(m_driver, 3, false);
|
|
|
|
PED::SET_PED_COMBAT_ATTRIBUTES(m_driver, 1, true);
|
|
|
|
PED::SET_PED_COMBAT_ATTRIBUTES(m_driver, 58, true);
|
|
|
|
PED::SET_PED_CONFIG_FLAG(m_driver, 294, true);
|
|
|
|
PED::SET_PED_CONFIG_FLAG(m_driver, 208, true);
|
|
|
|
PED::SET_PED_CONFIG_FLAG(m_driver, 229, true);
|
|
|
|
PED::SET_PED_CONFIG_FLAG(m_driver, 422, true);
|
|
|
|
PED::SET_PED_CONFIG_FLAG(m_driver, 123, true);
|
|
|
|
PED::SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(m_driver, true);
|
|
|
|
PED::SET_PED_CAN_BE_DRAGGED_OUT(m_driver, false);
|
|
|
|
PED::SET_PED_CAN_BE_SHOT_IN_VEHICLE(m_driver, false);
|
|
|
|
PED::SET_DRIVER_ABILITY(m_driver, 1);
|
|
|
|
PED::SET_DRIVER_RACING_MODIFIER(m_driver, 1);
|
|
|
|
|
|
|
|
PED::CLEAR_RELATIONSHIP_BETWEEN_GROUPS(0, PED::GET_PED_RELATIONSHIP_GROUP_HASH(m_driver), PED::GET_PED_RELATIONSHIP_GROUP_HASH(self::ped));
|
|
|
|
PED::SET_RELATIONSHIP_BETWEEN_GROUPS(0, PED::GET_PED_GROUP_INDEX(m_driver), PED::GET_PED_GROUP_INDEX(self::ped));
|
|
|
|
PED::SET_RELATIONSHIP_BETWEEN_GROUPS(0, PED::GET_PED_GROUP_INDEX(self::ped), PED::GET_PED_GROUP_INDEX(m_driver));
|
|
|
|
PED::SET_PED_AS_GROUP_MEMBER(m_driver, PED::GET_PED_GROUP_INDEX(self::ped));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ENTITY::DOES_ENTITY_EXIST(m_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vehicle_control::render_distance_on_vehicle()
|
|
|
|
{
|
|
|
|
if (ENTITY::DOES_ENTITY_EXIST(m_controlled_vehicle.handle))
|
|
|
|
{
|
|
|
|
Vector3 veh_pos = ENTITY::GET_ENTITY_COORDS(m_controlled_vehicle.handle, true);
|
|
|
|
Vector2 screen_pos{};
|
|
|
|
GRAPHICS::GET_SCREEN_COORD_FROM_WORLD_COORD(veh_pos.x, veh_pos.y, veh_pos.z, &screen_pos.x, &screen_pos.y);
|
|
|
|
|
|
|
|
HUD::BEGIN_TEXT_COMMAND_DISPLAY_TEXT("STRING");
|
|
|
|
HUD::SET_TEXT_FONT(4);
|
|
|
|
HUD::SET_TEXT_SCALE(0.3f, 0.463);
|
|
|
|
HUD::SET_TEXT_COLOUR(0, 150, 0, 255);
|
|
|
|
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(
|
|
|
|
std::to_string(math::distance_between_vectors(self::pos, veh_pos)).data());
|
|
|
|
HUD::END_TEXT_COMMAND_DISPLAY_TEXT(screen_pos.x, screen_pos.y, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool vehicle_control::find_suitable_destination_near_player(Vector3& outcoords, float& heading)
|
|
|
|
{
|
|
|
|
Vector3 original_coords = outcoords;
|
|
|
|
float original_heading = heading;
|
|
|
|
pathfind::find_closest_vehicle_node(self::pos, outcoords, heading, 0);
|
|
|
|
for (int i = 0; i < 10 && math::distance_between_vectors(self::pos, outcoords) < 3.f; i++)
|
|
|
|
{
|
|
|
|
pathfind::find_closest_vehicle_node(self::pos, outcoords, heading, 0, i);
|
|
|
|
//LOG(INFO) << "Node too close to player, iterating next closest";
|
|
|
|
}
|
|
|
|
//LOG(INFO) << "Searched for closest vehicle node";
|
|
|
|
if (math::distance_between_vectors(self::pos, outcoords) > 7.f)
|
|
|
|
{
|
|
|
|
//LOG(INFO) << "Node was too far, trying to find safe ped pos";
|
|
|
|
if (!pathfind::find_safe_pos_ped(self::pos, outcoords, true, 8) || math::distance_between_vectors(self::pos, outcoords) > 30.f)
|
|
|
|
{
|
|
|
|
//LOG(INFO) << "Couldnt find a safe ped pos";
|
|
|
|
outcoords = original_coords;
|
|
|
|
heading = original_heading;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vehicle_control::summon_vehicle()
|
|
|
|
{
|
|
|
|
if (!m_controlled_vehicle_exists
|
|
|
|
|| math::distance_between_vectors(self::pos, ENTITY::GET_ENTITY_COORDS(m_controlled_vehicle.handle, true)) < 13.f)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Vector3 behind_pos = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, -4.f, 0.f);
|
|
|
|
|
|
|
|
if (ensure_driver() && ENTITY::HAS_ENTITY_CLEAR_LOS_TO_ENTITY_IN_FRONT(self::ped, m_driver))
|
|
|
|
{
|
|
|
|
behind_pos = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 4.f, 0.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_driver_performing_task = true;
|
|
|
|
|
|
|
|
if (ensure_driver())
|
|
|
|
{
|
|
|
|
Vector3 destination{};
|
|
|
|
float heading{};
|
|
|
|
|
|
|
|
if (vehicle_control::find_suitable_destination_near_player(destination, heading))
|
|
|
|
{
|
|
|
|
//LOG(INFO) << "Suitable destination found";
|
2024-03-23 00:04:49 +01:00
|
|
|
g_notification_service.push_success("VEHICLE_CONTROLLER"_T.data(),
|
2023-06-05 15:46:20 -04:00
|
|
|
"VEHICLE_CONTROLLER_FOUND_LOCATION"_T.data());
|
2023-04-05 19:54:29 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//LOG(INFO) << "Couldn't find suitable destionation, defaulting to offset of player\nThis might go wrong";
|
2024-03-23 00:04:49 +01:00
|
|
|
g_notification_service.push_error("VEHICLE_CONTROLLER"_T.data(),
|
2023-06-05 15:46:20 -04:00
|
|
|
"VEHICLE_CONTROLLER_FORCE_PATHFINDING"_T.data());
|
2023-04-05 19:54:29 +02:00
|
|
|
destination = behind_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_destination = destination;
|
|
|
|
|
|
|
|
Vector3 nav_mesh_region = ENTITY::GET_ENTITY_COORDS(m_driver, true);
|
|
|
|
|
|
|
|
if (pathfind::load_navmesh_area(nav_mesh_region, m_distance_to_destination))
|
|
|
|
{
|
|
|
|
TASK::TASK_VEHICLE_GOTO_NAVMESH(m_driver,
|
|
|
|
m_controlled_vehicle.handle,
|
|
|
|
destination.x,
|
|
|
|
destination.y,
|
|
|
|
destination.z,
|
|
|
|
100.f,
|
|
|
|
786468,
|
|
|
|
4.f);
|
|
|
|
PED::SET_PED_KEEP_TASK(m_driver, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//LOG(INFO) << "Navmesh load failed";
|
2024-03-23 00:04:49 +01:00
|
|
|
g_notification_service.push_error("VEHICLE_CONTROLLER"_T.data(), "VEHICLE_CONTROLLER_NAVMESH_FAILURE"_T.data());
|
2023-04-05 19:54:29 +02:00
|
|
|
m_driver_performing_task = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-04 00:22:07 +01:00
|
|
|
void vehicle_control::get_last_driven_vehicle()
|
2023-04-05 19:54:29 +02:00
|
|
|
{
|
|
|
|
//Check in memory for vehicle
|
|
|
|
if (g_local_player)
|
|
|
|
{
|
|
|
|
if (g_local_player->m_vehicle)
|
|
|
|
{
|
2023-04-14 18:54:07 +02:00
|
|
|
if (m_controlled_vehicle.handle != g_pointers->m_gta.m_ptr_to_handle(g_local_player->m_vehicle))
|
2023-04-05 19:54:29 +02:00
|
|
|
m_controlled_vehicle = vehicle_control::update_vehicle(self::veh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Manually check if vehicle has changed
|
|
|
|
if (ENTITY::DOES_ENTITY_EXIST(self::veh) && self::veh != m_controlled_vehicle.handle)
|
|
|
|
{
|
|
|
|
m_controlled_vehicle = vehicle_control::update_vehicle(self::veh);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Manual check if there is a last driven vehicle
|
|
|
|
if (!m_controlled_vehicle_exists && ENTITY::DOES_ENTITY_EXIST(VEHICLE::GET_LAST_DRIVEN_VEHICLE()))
|
|
|
|
m_controlled_vehicle = vehicle_control::update_vehicle(VEHICLE::GET_LAST_DRIVEN_VEHICLE());
|
2023-11-04 00:22:07 +01:00
|
|
|
}
|
2023-04-05 19:54:29 +02:00
|
|
|
|
2023-11-04 00:22:07 +01:00
|
|
|
void vehicle_control::get_personal_vehicle()
|
|
|
|
{
|
|
|
|
if(ENTITY::DOES_ENTITY_EXIST(mobile::mechanic::get_personal_vehicle()))
|
|
|
|
m_controlled_vehicle = vehicle_control::update_vehicle(mobile::mechanic::get_personal_vehicle());
|
|
|
|
}
|
|
|
|
|
|
|
|
void vehicle_control::get_closest_vehicle()
|
|
|
|
{
|
|
|
|
if(PED::IS_PED_IN_ANY_VEHICLE(self::ped, true))
|
|
|
|
m_controlled_vehicle = vehicle_control::update_vehicle(PED::GET_VEHICLE_PED_IS_USING(self::ped));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_controlled_vehicle = vehicle_control::update_vehicle(vehicle::get_closest_to_location(self::pos, 1000));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void vehicle_control::tick()
|
|
|
|
{
|
|
|
|
m_controlled_vehicle_exists = m_controlled_vehicle.ptr && ENTITY::DOES_ENTITY_EXIST(m_controlled_vehicle.handle);
|
|
|
|
|
|
|
|
driver_tick();
|
2023-04-05 19:54:29 +02:00
|
|
|
keep_controlled_vehicle_data_updated(m_controlled_vehicle);
|
2023-11-04 00:22:07 +01:00
|
|
|
|
|
|
|
if (!g.window.vehicle_control.opened)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (m_selection_mode)
|
|
|
|
{
|
|
|
|
case eControlledVehSelectionMode::LAST_DRIVEN: get_last_driven_vehicle(); break;
|
|
|
|
case eControlledVehSelectionMode::PERSONAL: get_personal_vehicle(); break;
|
|
|
|
case eControlledVehSelectionMode::CLOSEST: get_closest_vehicle(); break;
|
|
|
|
}
|
2023-04-05 19:54:29 +02:00
|
|
|
}
|
2024-03-12 09:42:11 +01:00
|
|
|
}
|