diff --git a/src/function_types.hpp b/src/function_types.hpp index 234643a8..8dd3ef0d 100644 --- a/src/function_types.hpp +++ b/src/function_types.hpp @@ -210,4 +210,6 @@ namespace big::functions using get_ped_seat = CGetPedSeatReturnClass*(*)(PVOID seat_info, CPed* ped); using received_clone_remove = void (*)(CNetworkObjectMgr*, CNetGamePlayer*, CNetGamePlayer*, int16_t, uint32_t); + + using can_create_vehicle = bool (*)(); } diff --git a/src/gta_pointers.hpp b/src/gta_pointers.hpp index 050470e1..3531570f 100644 --- a/src/gta_pointers.hpp +++ b/src/gta_pointers.hpp @@ -365,6 +365,8 @@ namespace big functions::received_clone_remove m_received_clone_remove; CWeaponInfoManager* m_weapon_info_manager; + + functions::can_create_vehicle m_can_create_vehicle; }; #pragma pack(pop) static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned"); diff --git a/src/hooking/hooking.cpp b/src/hooking/hooking.cpp index d3537f1a..af3ce54a 100644 --- a/src/hooking/hooking.cpp +++ b/src/hooking/hooking.cpp @@ -140,6 +140,8 @@ namespace big detour_hook_helper::add("RCR", g_pointers->m_gta.m_received_clone_remove); + detour_hook_helper::add("CCV", g_pointers->m_gta.m_can_create_vehicle); + g_hooking = this; } diff --git a/src/hooking/hooking.hpp b/src/hooking/hooking.hpp index 9587f0ac..da9e5d8a 100644 --- a/src/hooking/hooking.hpp +++ b/src/hooking/hooking.hpp @@ -191,6 +191,8 @@ namespace big static bool sync_reader_serialize_vec3(void* _this, rage::fvector3* vec, float divisor, int size); static bool sync_reader_serialize_vec3_signed(void* _this, rage::fvector3* vec, float divisor, int size); static bool sync_reader_serialize_array(void* _this, void* array, int size); + + static bool can_create_vehicle(); }; class minhook_keepalive diff --git a/src/hooks/script/can_create_vehicle.cpp b/src/hooks/script/can_create_vehicle.cpp new file mode 100644 index 00000000..95c07c94 --- /dev/null +++ b/src/hooks/script/can_create_vehicle.cpp @@ -0,0 +1,10 @@ +#include "hooking/hooking.hpp" +#include "gta/pools.hpp" + +namespace big +{ + bool hooks::can_create_vehicle() + { + return (**g_pointers->m_gta.m_vehicle_pool)->m_item_count < (**g_pointers->m_gta.m_vehicle_pool)->m_size; + } +} diff --git a/src/pointers.cpp b/src/pointers.cpp index 6c839af4..4172fdfc 100644 --- a/src/pointers.cpp +++ b/src/pointers.cpp @@ -1772,6 +1772,15 @@ namespace big { g_pointers->m_gta.m_weapon_info_manager = ptr.add(3).rip().sub(72).as(); } + }, + // Can Create Vehicle + { + "CCV", + "8B 0D ? ? ? ? 39 0D ? ? ? ? 0F 9C C0", + [](memory::handle ptr) + { + g_pointers->m_gta.m_can_create_vehicle = ptr.as(); + } } >(); // don't leave a trailing comma at the end diff --git a/src/services/model_preview/model_preview_service.cpp b/src/services/model_preview/model_preview_service.cpp index 8a5f8d37..d645d466 100644 --- a/src/services/model_preview/model_preview_service.cpp +++ b/src/services/model_preview/model_preview_service.cpp @@ -22,107 +22,92 @@ namespace big void model_preview_service::show_ped(Hash hash) { - m_ped_clone = 0; - m_veh_model_hash = 0; - m_veh_owned_mods.clear(); + if (m_running && m_ped_model_hash != hash) + { + stop_preview(); + return; + } - if (m_ped_model_hash != hash) + if (!m_running) { m_ped_model_hash = hash; - - if (m_ped_model_hash != 0) - { - m_new_model = true; - - preview_loop(); - } } + + preview_loop(); } void model_preview_service::show_ped(Hash hash, Ped clone) { - m_veh_model_hash = 0; - m_veh_owned_mods.clear(); + if (m_running && (m_ped_model_hash != hash || m_ped_clone != clone )) + { + stop_preview(); + return; + } - if (m_ped_model_hash != hash || m_ped_clone != clone) + if (!m_running) { m_ped_model_hash = hash; - m_ped_clone = clone; - - if (m_ped_model_hash != 0) - { - m_new_model = true; - - preview_loop(); - } + m_ped_clone = clone; } + + preview_loop(); } void model_preview_service::show_vehicle(Hash hash, bool spawn_max) { - m_ped_model_hash = 0; - m_ped_clone = 0; - m_veh_owned_mods.clear(); + if (m_running && m_veh_model_hash != hash) + { + stop_preview(); + return; + } - if (m_veh_model_hash != hash || m_veh_spawn_max != spawn_max) + if (!m_running) { m_veh_model_hash = hash; - m_current_persisted_vehicle_name.clear(); - - if (m_veh_model_hash != 0) - { - m_veh_spawn_max = spawn_max; - m_new_model = true; - - preview_loop(); - } } + + preview_loop(); } void model_preview_service::show_vehicle(const std::map& owned_mods, bool spawn_max) { - m_ped_model_hash = 0; - m_ped_clone = 0; - m_current_persisted_vehicle_name.clear(); - - if (m_veh_spawn_max != spawn_max || m_veh_owned_mods.size() != owned_mods.size() - || !std::equal(m_veh_owned_mods.begin(), m_veh_owned_mods.end(), owned_mods.begin())) + if (m_running && m_veh_model_hash != owned_mods.find(MOD_MODEL_HASH)->second) { - m_veh_owned_mods.clear(); + stop_preview(); + return; + } + if (!m_running) + { auto hash_item = owned_mods.find(MOD_MODEL_HASH); m_veh_model_hash = hash_item->second; - - if (m_veh_model_hash != 0) - { - m_veh_owned_mods.insert(owned_mods.begin(), owned_mods.end()); - m_veh_spawn_max = spawn_max; - m_new_model = true; - - preview_loop(); - } + m_veh_owned_mods.insert(owned_mods.begin(), owned_mods.end()); + m_veh_spawn_max = spawn_max; } + + preview_loop(); } void model_preview_service::show_vehicle_persisted(std::string vehicle_name) { - m_ped_model_hash = 0; - m_ped_clone = 0; - m_veh_model_hash = 0; - - if (m_current_persisted_vehicle_name != vehicle_name) + if (m_running && m_current_persisted_vehicle_name != vehicle_name) + { + stop_preview(); + return; + } + + if (!m_running) { m_current_persisted_vehicle_name = vehicle_name; - m_new_model = true; - - preview_loop(); } + + preview_loop(); } void model_preview_service::preview_loop() { - if (m_running || m_loop_running) + if (m_running) { return; } @@ -130,28 +115,15 @@ namespace big m_running = true; g_fiber_pool->queue_job([this] { - m_loop_running = true; - m_heading = 0; - start_time = std::chrono::steady_clock::now(); + m_heading = 0.f; + m_rotation_start_time = std::chrono::steady_clock::now(); - while (g_running && m_running && g_gui->is_open() && (m_ped_model_hash || m_veh_model_hash || !m_current_persisted_vehicle_name.empty())) + while (!m_shutdown_preview && g_running && g_gui->is_open() ) { - Vector3 location; - - if (m_ped_model_hash) - { - location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 5.f, -.5f); - } - else if (m_veh_model_hash || !m_current_persisted_vehicle_name.empty()) - { - location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 10.f, .5f); - } + Vector3 location{}; if (m_current_ent == 0) { - m_new_model = false; - location.z = -10.f; - if (m_ped_model_hash) { m_current_ent = ped::spawn(ePedType::PED_TYPE_ARMY, m_ped_model_hash, m_ped_clone, location, 0.f, false); @@ -174,7 +146,7 @@ namespace big } else if (!m_current_persisted_vehicle_name.empty()) { - m_current_ent = persist_car_service::load_vehicle(m_current_persisted_vehicle_name, g.persist_car.persist_vehicle_sub_folder, Vector3()); + m_current_ent = persist_car_service::preview_vehicle(m_current_persisted_vehicle_name, g.persist_car.persist_vehicle_sub_folder, location); } if (m_current_ent) @@ -191,23 +163,28 @@ namespace big OBJECT::SET_OBJECT_ALLOW_LOW_LOD_BUOYANCY(m_current_ent, false); } } - else if (m_new_model) - { - entity::delete_entity(m_current_ent, true); - } else { - if (const int alpha = ENTITY::GET_ENTITY_ALPHA(m_current_ent); alpha < 255) + if (m_ped_model_hash) { - ENTITY::SET_ENTITY_ALPHA(m_current_ent, std::min(255, alpha + 20), false); + location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 5.f, -.5f); + } + else + { + location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 10.f, .5f); } - - ENTITY::SET_ENTITY_HEADING(m_current_ent, m_heading); - ENTITY::SET_ENTITY_COORDS(m_current_ent, location.x, location.y, location.z, 0, 0, 0, 0); } + if (auto alpha = ENTITY::GET_ENTITY_ALPHA(m_current_ent); alpha < 255) + { + ENTITY::SET_ENTITY_ALPHA(m_current_ent, std::min(255, alpha + 20), false); + } + + ENTITY::SET_ENTITY_HEADING(m_current_ent, m_heading); + ENTITY::SET_ENTITY_COORDS(m_current_ent, location.x, location.y, location.z, 0, 0, 0, 0); + auto now = std::chrono::steady_clock::now(); - auto elapsed_time = std::chrono::duration_cast(now - start_time).count() / 1000.0; // Convert to seconds + auto elapsed_time = std::chrono::duration_cast(now - m_rotation_start_time).count() / 1000.0; // Convert to seconds m_heading = (elapsed_time / 10.0) * 360.0; // Rotate 360 degrees every 10 seconds m_heading = fmod(m_heading, 360.0); // Ensure rotation is always between 0 and 360 @@ -215,21 +192,27 @@ namespace big script::get_current()->yield(); } - entity::delete_entity(m_current_ent, true); + ENTITY::DELETE_ENTITY(&m_current_ent); - m_current_ent = 0; - m_ped_model_hash = 0; - m_veh_model_hash = 0; - m_veh_owned_mods.clear(); - m_current_persisted_vehicle_name.clear(); - m_running = false; - m_loop_running = false; + clear_data(); }); } + void model_preview_service::clear_data() + { + m_veh_owned_mods.clear(); + m_ped_model_hash = {}; + m_veh_model_hash = {}; + m_ped_clone = {}; + m_current_persisted_vehicle_name = {}; + m_shutdown_preview = false; + m_running = false; + m_current_ent = NULL; + } + void model_preview_service::stop_preview() { - m_veh_owned_mods.clear(); - m_running = false; + if (m_running) + m_shutdown_preview = true; } } diff --git a/src/services/model_preview/model_preview_service.hpp b/src/services/model_preview/model_preview_service.hpp index e3154b65..34084873 100644 --- a/src/services/model_preview/model_preview_service.hpp +++ b/src/services/model_preview/model_preview_service.hpp @@ -5,24 +5,21 @@ namespace big { class model_preview_service { - Entity m_current_ent = 0; + Entity m_current_ent{}; + float m_heading{}; + + Hash m_ped_model_hash{}; + Hash m_veh_model_hash{}; + Ped m_ped_clone{}; + std::string m_current_persisted_vehicle_name; - Hash m_veh_model_hash = 0; std::map m_veh_owned_mods; bool m_veh_spawn_max = false; - Hash m_ped_model_hash = 0; - Ped m_ped_clone = 0; - - bool m_new_model = false; - float m_heading = 0.f; - bool m_loop_running = false; - bool m_running = false; - - std::string m_current_persisted_vehicle_name; - - std::chrono::time_point start_time; + bool m_running = false; + bool m_shutdown_preview = false; + std::chrono::time_point m_rotation_start_time; public: model_preview_service(); ~model_preview_service(); @@ -34,12 +31,11 @@ namespace big void show_vehicle(const std::map& owned_mods, bool spawn_max); void show_vehicle_persisted(std::string vehicle_name); void show_vehicle(Vehicle veh); + void stop_preview(); private: + void clear_data(); void preview_loop(); - - public: - void stop_preview(); }; inline model_preview_service* g_model_preview_service{}; diff --git a/src/services/vehicle/persist_car_service.cpp b/src/services/vehicle/persist_car_service.cpp index bf2dfa74..743107bc 100644 --- a/src/services/vehicle/persist_car_service.cpp +++ b/src/services/vehicle/persist_car_service.cpp @@ -27,6 +27,28 @@ namespace big file_stream.close(); } + Vehicle persist_car_service::preview_vehicle(std::string_view file_name, std::string folder_name, const std::optional& spawn_coords) + { + const auto file = check_vehicle_folder(folder_name).get_file(file_name); + + std::ifstream file_stream(file.get_path()); + + nlohmann::json vehicle_json; + + try + { + file_stream >> vehicle_json; + file_stream.close(); + } + catch (std::exception& e) + { + g_notification_service.push_warning("PERSIST_CAR_TITLE"_T.data(), "Failed to load JSON file"); + return NULL; + } + + return spawn_vehicle_json(vehicle_json, self::ped, spawn_coords, true); + } + Vehicle persist_car_service::load_vehicle(std::string_view file_name, std::string folder_name, const std::optional& spawn_coords) { const auto file = check_vehicle_folder(folder_name).get_file(file_name); @@ -188,16 +210,16 @@ namespace big return vehicle; } - Vehicle persist_car_service::spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional& spawn_coords) + Vehicle persist_car_service::spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional& spawn_coords, bool is_preview) { const Hash vehicle_hash = vehicle_json[vehicle_model_hash_key]; const Vector3& spawn_location = spawn_coords.has_value() ? spawn_coords.value() : vehicle::get_spawn_location(g.persist_car.spawn_inside, vehicle_hash); const float spawn_heading = ENTITY::GET_ENTITY_HEADING(self::ped); Vehicle vehicle = self::veh; - if (spawn_coords.has_value() || (!spawn_coords.has_value() && ENTITY::GET_ENTITY_MODEL(vehicle) != vehicle_hash)) + if (is_preview || (!is_preview && ENTITY::GET_ENTITY_MODEL(vehicle) != vehicle_hash)) { - vehicle = big::vehicle::spawn(vehicle_hash, spawn_location, spawn_heading); + vehicle = big::vehicle::spawn(vehicle_hash, spawn_location, spawn_heading, !is_preview); if (spawn_location.x + spawn_location.y + spawn_location.z != 0) script::get_current()->yield(); //This is needed to wait for the engine to instantiate things like the radio station so it won't overwrite it on the next frame. diff --git a/src/services/vehicle/persist_car_service.hpp b/src/services/vehicle/persist_car_service.hpp index 2633cde0..69fe0b22 100644 --- a/src/services/vehicle/persist_car_service.hpp +++ b/src/services/vehicle/persist_car_service.hpp @@ -13,6 +13,7 @@ namespace big static Vehicle clone_ped_car(Ped ped, Vehicle vehicle); static void save_vehicle(Vehicle vehicle, std::string_view file_name, std::string folder_name); static Vehicle load_vehicle(std::string_view file_name, std::string folder_name = "", const std::optional& = std::nullopt); + static Vehicle preview_vehicle(std::string_view file_name, std::string folder_name = "", const std::optional& = std::nullopt); static void delete_vehicle(std::string_view file_name, std::string folder_name); private: @@ -68,7 +69,7 @@ namespace big static Vehicle spawn_vehicle_full(nlohmann::json vehicle_json, Ped ped, const std::optional& spawn_coords = std::nullopt); static Vehicle spawn_vehicle(nlohmann::json vehicle_json, Ped ped, const std::optional& spawn_coords); - static Vehicle spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional& spawn_coords = std::nullopt); + static Vehicle spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped, const std::optional& spawn_coords = std::nullopt, bool is_preview = false); static nlohmann::json get_full_vehicle_json(Vehicle vehicle); diff --git a/src/util/entity.cpp b/src/util/entity.cpp index 4360ea31..8001c50a 100644 --- a/src/util/entity.cpp +++ b/src/util/entity.cpp @@ -26,7 +26,10 @@ namespace big::entity void delete_entity(Entity& ent, bool force) { if (!ENTITY::DOES_ENTITY_EXIST(ent)) + { + ent = NULL; return; + } if (!force && !take_control_of(ent)) { LOG(VERBOSE) << "Failed to take control of entity before deleting"; diff --git a/src/util/vehicle.cpp b/src/util/vehicle.cpp index 550bf540..bf1eae8d 100644 --- a/src/util/vehicle.cpp +++ b/src/util/vehicle.cpp @@ -354,9 +354,9 @@ namespace big::vehicle } - Vehicle clone_from_owned_mods(std::map owned_mods, Vector3 location, float heading, bool is_networked) + Vehicle clone_from_owned_mods(std::map owned_mods, Vector3 location, float heading, bool is_networked, bool is_script_vehicle) { - auto vehicle = spawn(owned_mods[MOD_MODEL_HASH], location, heading, is_networked); + auto vehicle = spawn(owned_mods[MOD_MODEL_HASH], location, heading, is_networked, is_script_vehicle); if (vehicle == 0) { return 0; diff --git a/src/util/vehicle.hpp b/src/util/vehicle.hpp index 008c308b..3595ba91 100644 --- a/src/util/vehicle.hpp +++ b/src/util/vehicle.hpp @@ -29,7 +29,7 @@ namespace big::vehicle Vehicle spawn(Hash hash, Vector3 location, float heading, bool is_networked = true, bool script_veh = false); Vehicle clone_from_vehicle_data(std::map& data, Vector3 location, float heading); std::map get_owned_mods_from_vehicle_idx(script_global vehicle_idx); - Vehicle clone_from_owned_mods(std::map owned_mods, Vector3 location, float heading, bool is_networked = true); + Vehicle clone_from_owned_mods(std::map owned_mods, Vector3 location, float heading, bool is_networked = true, bool is_script_vehicle = false); std::map get_owned_mods_from_vehicle(Vehicle vehicle); void teleport_into_vehicle(Vehicle veh); void max_vehicle(Vehicle veh);