diff --git a/src/services/orbital_drone/orbital_drone.hpp b/src/services/orbital_drone/orbital_drone.hpp index 0a1b79cd..ed2f7937 100644 --- a/src/services/orbital_drone/orbital_drone.hpp +++ b/src/services/orbital_drone/orbital_drone.hpp @@ -9,9 +9,8 @@ namespace big bool m_lock; Entity m_lock_ent; - Vector3 m_ground_pos; Vector3 m_start_pos; - + int m_scaleform; bool m_should_tp; @@ -23,15 +22,17 @@ namespace big void tick(); bool initialized() - { return m_initialized; }; + { + return m_initialized; + }; + + Vector3 m_ground_pos; private: void cam_nav(); void detect_player(Entity ent); void orbital_cannon_explosion(); - }; inline orbital_drone g_orbital_drone_service; - } \ No newline at end of file diff --git a/src/services/squad_spawner/squad_spawner.cpp b/src/services/squad_spawner/squad_spawner.cpp index acf31796..f1b994cb 100644 --- a/src/services/squad_spawner/squad_spawner.cpp +++ b/src/services/squad_spawner/squad_spawner.cpp @@ -1,6 +1,7 @@ #include "squad_spawner.hpp" #include "gta/joaat.hpp" +#include "services/vehicle/persist_car_service.hpp" #include "util/math.hpp" #include "util/pathfind.hpp" #include "util/ped.hpp" @@ -75,19 +76,69 @@ namespace big std::pair squad_spawner::spawn_squad_vehicle(squad s) { - auto handle = vehicle::spawn(rage::joaat(s.m_vehicle_model), s.m_spawn_pos, s.m_spawn_heading); + std::pair veh; + if (!s.does_squad_have_persistent_vehicle()) + { + veh.first = vehicle::spawn(rage::joaat(s.m_vehicle_model), s.m_spawn_pos, s.m_spawn_heading); + } + else + { + const auto persistent_vehicles = persist_car_service::list_files(); - VEHICLE::SET_VEHICLE_ON_GROUND_PROPERLY(handle, 5); - ENTITY::SET_ENTITY_INVINCIBLE(handle, s.m_veh_invincibility); - VEHICLE::SET_VEHICLE_ENGINE_ON(handle, true, true, false); + for (auto c : persistent_vehicles) + { + if (c == s.m_persistent_vehicle) + { + veh.first = persist_car_service::load_vehicle(c); + ENTITY::SET_ENTITY_COORDS(veh.first, s.m_spawn_pos.x, s.m_spawn_pos.y, s.m_spawn_pos.z, 0, 0, 0, 1); + break; + } + } + } - return std::pair(handle, reinterpret_cast(g_pointers->m_gta.m_handle_to_ptr(handle))); + veh.second = (CVehicle*)g_pointers->m_gta.m_handle_to_ptr(veh.first); + + VEHICLE::SET_VEHICLE_ON_GROUND_PROPERLY(veh.first, 5); + ENTITY::SET_ENTITY_INVINCIBLE(veh.first, s.m_veh_invincibility); + VEHICLE::SET_VEHICLE_ENGINE_ON(veh.first, true, true, false); + ENTITY::SET_ENTITY_HEADING(veh.first, s.m_spawn_heading); + + if (s.m_max_vehicle) + vehicle::max_vehicle(veh.first); + + return veh; } + bool find_road(squad& s) + { + const Vector3 original_pos = s.m_spawn_pos; + const Vector3 target_ped_pos = ENTITY::GET_ENTITY_COORDS(s.current_target_ped, false); + Vector3 south, north, chosen_pos; + + //Initial search for an actual road + pathfind::find_closest_road(s.m_spawn_pos, &south, &north); + + //Check which is closer + if (math::distance_between_vectors(south, s.m_spawn_pos) <= math::distance_between_vectors(north, s.m_spawn_pos)) + chosen_pos = south; + else + chosen_pos = north; + + //Get a node to specify heading + for (int i = 0; i < 100; i++) + { + if (pathfind::find_closest_vehicle_node_favour_direction(chosen_pos, target_ped_pos, s.m_spawn_pos, s.m_spawn_heading, 0, i) && math::distance_between_vectors(target_ped_pos, s.m_spawn_pos) >= s.m_spawn_distance) + return true; + } + + s.m_spawn_pos = original_pos; + return false; + } bool squad_spawner::find_suitable_spawn_pos(squad& s) { - Hash veh_model_hash = rage::joaat(s.m_vehicle_model); + const Vector3 original_pos = s.m_spawn_pos; + Hash veh_model_hash = rage::joaat(s.m_vehicle_model); int node_search_flag = (VEHICLE::IS_THIS_MODEL_A_BOAT(veh_model_hash) || VEHICLE::IS_THIS_MODEL_A_JETSKI(veh_model_hash)) ? 2 : 0; Vector3 new_pos = s.m_spawn_pos; @@ -102,21 +153,48 @@ namespace big } } + const float original_spawn_distance = s.m_spawn_distance; + static auto reset_spawn_pos_to_offset = [&]() -> void { Ped player_ped_handle = g_pointers->m_gta.m_ptr_to_handle(s.target->get_ped()); s.m_spawn_pos = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(player_ped_handle, 0, -7, 0); - //LOG(INFO) << "Squad spawner: No suitable spot found, spawning at an offset"; g_notification_service->push_warning("Squad Spawner", "No suitable spot found, spawning at an offset"); }; - for (int i = 0; i < 10; i++) + static auto is_pos_valid = [&]() -> bool { + return math::distance_between_vectors(new_pos, s.m_spawn_pos) < (s.m_spawn_distance + 50.f) && new_pos != s.m_spawn_pos; + }; + + static auto find_location = [&]() -> void { + for (int i = 0; i < 10; i++) + { + if (pathfind::find_random_location_in_vicinity_precise(s.m_spawn_pos, new_pos, s.m_spawn_heading, node_search_flag, s.m_spawn_distance, 200, true)) + break; + } + }; + + //Use spawn distance to find a position ahead of target and limit the vicinity + if (s.m_spawn_ahead) { - if (pathfind::find_random_location_in_vicinity_precise(s.m_spawn_pos, new_pos, s.m_spawn_heading, node_search_flag, s.m_spawn_distance, 200)) - break; + s.m_spawn_pos = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(s.current_target_ped, 0.0, s.m_spawn_distance, 0.0); + s.m_spawn_distance = 5.f; + } + + //Actual algorithm to find a nice spot + find_location(); + + //Try and find a road + if (s.m_favour_roads) + { + if (find_road(s) && is_pos_valid()) + return true; + + //If the road search failed, revert to original method + find_location(); } //Reset if all searches failed with an allowance of up to 50.0f - if (math::distance_between_vectors(new_pos, s.m_spawn_pos) > (s.m_spawn_distance + 50.f) || new_pos == s.m_spawn_pos) + if (!is_pos_valid()) { reset_spawn_pos_to_offset(); return false; @@ -137,8 +215,9 @@ namespace big return false; } - if (std::string(s.m_name).empty()){ - s.m_name = std::string(std::to_string(s.m_squad_size) + std::string("_").append(s.m_ped_model).append("_").append(std::to_string(s.m_internal_id))); + if (std::string(s.m_name).empty()) + { + s.m_name = std::string(std::to_string(s.m_squad_size) + std::string("_").append(s.m_ped_model).append("_").append(std::to_string(s.m_internal_id))); } Hash veh_model_hash = rage::joaat(s.m_vehicle_model); @@ -231,6 +310,7 @@ namespace big if (s.has_squad_spawned()) { m_active_squads.push_back(s); + script::get_current()->yield(100ms); return true; } return false; diff --git a/src/services/squad_spawner/squad_spawner.hpp b/src/services/squad_spawner/squad_spawner.hpp index 683e32c4..41b65c22 100644 --- a/src/services/squad_spawner/squad_spawner.hpp +++ b/src/services/squad_spawner/squad_spawner.hpp @@ -57,19 +57,23 @@ namespace big std::string m_ped_model; std::string m_weapon_model; std::string m_vehicle_model; + std::string m_persistent_vehicle = "None"; //The connection to persistent vehicle is done by the file name bool m_ped_invincibility; bool m_veh_invincibility; bool m_ped_proofs[5]; // 0 headshot, 1 bullet, 2 flame, 3 melee, 4 explosion float m_ped_health; //Leave at 0 to default float m_ped_armor; //Leave at 0 to default - float m_ped_accuracy; + float m_ped_accuracy = 50.f; float m_spawn_distance; - int m_squad_size; - eSquadSpawnDistance m_spawn_distance_mode; - eCombatAbilityLevel m_combat_ability_level; + int m_squad_size = 1; + eSquadSpawnDistance m_spawn_distance_mode = eSquadSpawnDistance::CLOSEBY; + eCombatAbilityLevel m_combat_ability_level = eCombatAbilityLevel::AVERAGE; bool m_stay_in_veh; bool m_spawn_behind_same_velocity; //Spawns behind a moving target with the same velocity as the targets vehicle bool m_disperse; //Spawns attackers that are on foot on seperate positions + bool m_spawn_ahead; + bool m_favour_roads; + bool m_max_vehicle; /* Leave vehicle_model empty to spawn a squad on foot @@ -78,16 +82,17 @@ namespace big Leave spawn_distance at 0 to let the spawn_distance_mode to handle it */ squad(){}; - squad(std::string name, std::string ped_model, std::string weapon_model, std::string vehicle_model, int squad_size, bool ped_invincibility = false, bool veh_invincibility = false, bool ped_proofs[5] = {}, float ped_health = 0, float ped_armor = 0, float spawn_distance = 0, float ped_accuracy = 50.f, eSquadSpawnDistance spawn_distance_mode = eSquadSpawnDistance::CLOSEBY, eCombatAbilityLevel combat_ability_level = eCombatAbilityLevel::AVERAGE, bool stay_in_veh = false, bool spawn_behind_same_velocity = false, std::string description = "", bool disperse = false) + squad(std::string name, std::string ped_model, std::string weapon_model, std::string vehicle_model, int squad_size, bool ped_invincibility = false, bool veh_invincibility = false, bool ped_proofs[5] = {}, float ped_health = 0, float ped_armor = 0, float spawn_distance = 0, float ped_accuracy = 50.f, eSquadSpawnDistance spawn_distance_mode = eSquadSpawnDistance::CLOSEBY, eCombatAbilityLevel combat_ability_level = eCombatAbilityLevel::AVERAGE, bool stay_in_veh = false, bool spawn_behind_same_velocity = false, std::string description = "", bool disperse = false, bool spawn_ahead = false, bool favour_roads = false, bool max_vehicle = false, std::string persistent_vehicle = "None") { m_internal_id = ++m_instance_count; - m_name = name; - m_description = description; - m_ped_model = ped_model; - m_weapon_model = weapon_model; - m_vehicle_model = vehicle_model; - m_squad_size = squad_size; + m_name = name; + m_description = description; + m_ped_model = ped_model; + m_weapon_model = weapon_model; + m_vehicle_model = vehicle_model; + m_persistent_vehicle = persistent_vehicle; + m_squad_size = squad_size; m_ped_invincibility = ped_invincibility; m_veh_invincibility = veh_invincibility; @@ -104,6 +109,9 @@ namespace big m_stay_in_veh = stay_in_veh; m_spawn_behind_same_velocity = spawn_behind_same_velocity; m_disperse = disperse; + m_spawn_ahead = spawn_ahead; + m_favour_roads = favour_roads; + m_max_vehicle = max_vehicle; } int get_id() const @@ -113,12 +121,17 @@ namespace big bool does_squad_have_description() { - return !std::string(m_description).empty(); + return !m_description.empty(); } bool does_squad_have_vehicle() { - return !std::string(m_vehicle_model).empty(); + return !m_vehicle_model.empty() || does_squad_have_persistent_vehicle(); + } + + bool does_squad_have_persistent_vehicle() + { + return m_persistent_vehicle != "None"; } bool should_override_health() @@ -190,7 +203,7 @@ namespace big NLOHMANN_JSON_SERIALIZE_ENUM(eSquadSpawnDistance, {{eSquadSpawnDistance::CUSTOM, "custom"}, {eSquadSpawnDistance::ON_TARGET, "on target"}, {eSquadSpawnDistance::CLOSEBY, "closeby"}, {eSquadSpawnDistance::MODERATELY_DISTANCED, "moderately distanced"}, {eSquadSpawnDistance::FAR_AWAY, "far away"}}) - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(squad, m_name, m_description, m_ped_model, m_weapon_model, m_vehicle_model, m_ped_invincibility, m_veh_invincibility, m_ped_proofs, m_ped_health, m_ped_armor, m_ped_accuracy, m_spawn_distance, m_squad_size, m_spawn_distance_mode, m_combat_ability_level, m_stay_in_veh, m_spawn_behind_same_velocity, m_disperse); + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(squad, m_name, m_description, m_ped_model, m_weapon_model, m_vehicle_model, m_ped_invincibility, m_veh_invincibility, m_ped_proofs, m_ped_health, m_ped_armor, m_ped_accuracy, m_spawn_distance, m_squad_size, m_spawn_distance_mode, m_combat_ability_level, m_stay_in_veh, m_spawn_behind_same_velocity, m_disperse, m_spawn_ahead, m_favour_roads, m_max_vehicle, m_persistent_vehicle); class squad_spawner { diff --git a/src/services/squad_spawner/squad_spawner_save_files.cpp b/src/services/squad_spawner/squad_spawner_save_files.cpp index 54800129..18d3ed1b 100644 --- a/src/services/squad_spawner/squad_spawner_save_files.cpp +++ b/src/services/squad_spawner/squad_spawner_save_files.cpp @@ -27,11 +27,8 @@ namespace big read.close(); } squad new_squad{}; - LOG(INFO) << "TEST1"; from_json(j, new_squad); - LOG(INFO) << "TEST2"; g_squad_spawner_service.m_templates.push_back(new_squad); - LOG(INFO) << "TEST3"; } } success = true; @@ -99,7 +96,7 @@ namespace big m_templates.push_back(squad("Shotgunners", "g_m_y_lost_03", "WEAPON_AUTOSHOTGUN", "daemon", 2, false, false, ped_proofs, 0, 0, 0, 65, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::AVERAGE, false, false, "A duo biker gang that utilize sawn off shotguns on their Harley's")); m_templates.push_back(squad("Machete jesus", "u_m_m_jesus_01", "WEAPON_MACHETE", "", 1, false, false, ped_proofs_annoying, 0, 0, 0, 10, eSquadSpawnDistance::CLOSEBY, eCombatAbilityLevel::AVERAGE, false, false, "Christ has had enough of the sins")); m_templates.push_back(squad("Annoying security guard", "mp_m_securoguard_01", "WEAPON_STUNGUN_MP", "", 1, false, false, ped_proofs_annoying, 0, 0, 0, 100, eSquadSpawnDistance::CLOSEBY, eCombatAbilityLevel::PROFESSIONAL, false, false, "The mall security guard with superiority issues")); - m_templates.push_back(squad("Heavy attack choppers", "s_m_y_swat_01", "WEAPON_MG", "valkyrie", 4, false, false, ped_proofs, 0, 0, 0, 100, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::PROFESSIONAL, false, false, "Very deadly attack chopper eqquiped with a cannon")); + m_templates.push_back(squad("Heavy attack chopper", "s_m_y_swat_01", "WEAPON_MG", "valkyrie", 4, false, false, ped_proofs, 0, 0, 0, 100, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::PROFESSIONAL, false, false, "Very deadly attack chopper eqquiped with a cannon")); m_templates.push_back(squad("Fighter jet", "s_m_m_pilot_02", "WEAPON_UNARMED", "lazer", 1, false, false, ped_proofs, 0, 0, 0, 100, eSquadSpawnDistance::FAR_AWAY, eCombatAbilityLevel::PROFESSIONAL, false, false, "Tedious yet precise form of attack with a Fighter jet")); m_templates.push_back(squad("Mobile squad", "s_m_m_highsec_01", "WEAPON_MICROSMG", "komoda", 4, false, false, ped_proofs, 0, 0, 0, 100, eSquadSpawnDistance::FAR_AWAY, eCombatAbilityLevel::PROFESSIONAL, true, true, "This squad makes use of 'Vehicle catchup'")); m_templates.push_back(squad("Altruists", "a_m_m_acult_01", "WEAPON_SNSPISTOL", "", 8, false, false, ped_proofs, 0, 0, 0, 100, eSquadSpawnDistance::CLOSEBY, eCombatAbilityLevel::PROFESSIONAL, false, false, "Cannibals from the alrtuist cult will surround the victim using 'Disperse'", true)); diff --git a/src/util/pathfind.hpp b/src/util/pathfind.hpp index b377d709..ceb4fe58 100644 --- a/src/util/pathfind.hpp +++ b/src/util/pathfind.hpp @@ -75,6 +75,28 @@ namespace big::pathfind return false; } + /* + Same as find_closest_vehicle_node but will favour nodes that are facing the facecoords with their heading + */ + inline bool find_closest_vehicle_node_favour_direction(Vector3 coords, Vector3 facecoords, Vector3& outcoords, float& outheading, int flag, int nth = 1, float zMeasureMult = 3.f, float zTolerance = 0.f) + { + if (load_path_nodes(coords)) + return PATHFIND::GET_NTH_CLOSEST_VEHICLE_NODE_FAVOUR_DIRECTION(coords.x, + coords.y, + coords.z, + facecoords.x, + facecoords.y, + facecoords.z, + nth, + &outcoords, + &outheading, + flag, + zMeasureMult, + zTolerance); + else + return false; + } + inline bool find_random_vehicle_node(Vector3 center, Vector3& outcoords, float radius, bool avoid_dead_ends, bool avoid_highways, int min_lanes = 0) { int node_id; @@ -100,7 +122,7 @@ namespace big::pathfind } } - inline bool find_random_location_in_vicinity(Vector3 coords, Vector3& outcoords, float& outheading, int flag, int vicinity) + inline bool find_random_location_in_vicinity(Vector3 coords, Vector3& outcoords, float& outheading, int flag, int vicinity, bool favour_heading_to_original_coords = false) { outcoords = coords; @@ -108,7 +130,7 @@ namespace big::pathfind Vector3 changed_coords = outcoords; - if (!find_closest_vehicle_node(outcoords, outcoords, outheading, flag) || math::distance_between_vectors(outcoords, coords) > vicinity || math::distance_between_vectors(outcoords, coords) < (vicinity / 2)) + if ((favour_heading_to_original_coords ? !find_closest_vehicle_node_favour_direction(outcoords, outcoords, outcoords, outheading, flag) : !find_closest_vehicle_node(outcoords, outcoords, outheading, flag)) || math::distance_between_vectors(outcoords, coords) > vicinity || math::distance_between_vectors(outcoords, coords) < (vicinity / 2)) { outcoords = coords; @@ -126,7 +148,7 @@ namespace big::pathfind Param precision goes up to a value of 200 meaning how many positions it will try and filter from Might prove resource demanding based on hardware */ - inline bool find_random_location_in_vicinity_precise(Vector3 coords, Vector3& outcoords, float& outheading, int flag, float vicinity, int precision = 50) + inline bool find_random_location_in_vicinity_precise(Vector3 coords, Vector3& outcoords, float& outheading, int flag, float vicinity, int precision = 50, bool favour_heading_to_original_coords = false) { if (precision > 200) precision = 200; @@ -137,7 +159,7 @@ namespace big::pathfind for (int i = 0; i < precision; i++) { Vector3 new_pos{}; - find_random_location_in_vicinity(coords, new_pos, outheading, flag, vicinity); + find_random_location_in_vicinity(coords, new_pos, outheading, flag, vicinity, favour_heading_to_original_coords); found_locations.push_back(new_pos); } @@ -158,4 +180,12 @@ namespace big::pathfind return outcoords != coords; } + + /* + Will give you two vectors representing the road extremes of the road closest to the given coords. + */ + inline bool find_closest_road(Vector3 coords, Vector3* south_end, Vector3* north_end, int* south_bound_lanes = nullptr, int* north_bound_lanes = nullptr, float* width_between_directional_lanes = nullptr, bool ignore_disabled_nodes = true, float min_lenght = 10.f, float min_lanes = 1) + { + return PATHFIND::GET_CLOSEST_ROAD(coords.x, coords.y, coords.z, min_lenght, min_lanes, south_end, north_end, south_bound_lanes, north_bound_lanes, width_between_directional_lanes, ignore_disabled_nodes); + } } \ No newline at end of file diff --git a/src/views/world/view_squad_spawner.cpp b/src/views/world/view_squad_spawner.cpp index f7b31d2f..7ad0fd6c 100644 --- a/src/views/world/view_squad_spawner.cpp +++ b/src/views/world/view_squad_spawner.cpp @@ -1,6 +1,8 @@ -#include "services/gta_data/gta_data_service.hpp" -#include "services/squad_spawner/squad_spawner.hpp" #include "misc/cpp/imgui_stdlib.h" +#include "services/gta_data/gta_data_service.hpp" +#include "services/orbital_drone/orbital_drone.hpp" +#include "services/squad_spawner/squad_spawner.hpp" +#include "services/vehicle/persist_car_service.hpp" #include "views/view.hpp" namespace big @@ -109,7 +111,7 @@ namespace big ImGui::Spacing(); ImGui::PushItemWidth(250); - + components::input_text_with_hint("##name", "Name", &new_template.m_name); components::input_text_with_hint("##pedmodel", "Ped model", &new_template.m_ped_model); @@ -117,7 +119,7 @@ namespace big return pair.second.m_name == new_template.m_ped_model; }); - if (!std::string(new_template.m_ped_model).empty() && ped_found == g_gta_data_service->peds().end()) + if (!new_template.m_ped_model.empty() && ped_found == g_gta_data_service->peds().end()) { if (ImGui::ListBoxHeader("##pedlist", ImVec2(250, 200))) { @@ -137,9 +139,7 @@ namespace big } } - components::input_text_with_hint("##vehmodel", - "Vehicle model", - &new_template.m_vehicle_model); + components::input_text_with_hint("##vehmodel", "Vehicle model", &new_template.m_vehicle_model); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Leave empty to spawn on foot"); @@ -167,9 +167,7 @@ namespace big } } - components::input_text_with_hint("##weapmodel", - "Weapon model", - &new_template.m_weapon_model); + components::input_text_with_hint("##weapmodel", "Weapon model", &new_template.m_weapon_model); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Leave empty to spawn unarmed, beware that a player can only attain 3 melee attackers at a time"); @@ -177,7 +175,7 @@ namespace big return pair.second.m_name == new_template.m_weapon_model; }); - if (!std::string(new_template.m_weapon_model).empty() && weap_found == g_gta_data_service->weapons().end()) + if (!new_template.m_weapon_model.empty() && weap_found == g_gta_data_service->weapons().end()) { if (ImGui::ListBoxHeader("##weaplist", ImVec2(250, 200))) { @@ -223,25 +221,29 @@ namespace big }); components::button("Reset Fields", [] { - new_template.m_spawn_distance_mode = (eSquadSpawnDistance)1; - new_template.m_combat_ability_level = (eCombatAbilityLevel)2; - new_template.m_name[0] = '\0'; - new_template.m_description[0] = '\0'; - new_template.m_ped_model[0] = '\0'; - new_template.m_vehicle_model[0] = '\0'; - new_template.m_weapon_model[0] = '\0'; - new_template.m_squad_size = 0; - new_template.m_ped_invincibility = 0; - new_template.m_veh_invincibility = 0; - new_template.m_ped_health = 0; - new_template.m_ped_armor = 0; - new_template.m_ped_accuracy = 0; - new_template.m_spawn_distance = 0; + new_template.m_spawn_distance_mode = eSquadSpawnDistance::CLOSEBY; + new_template.m_combat_ability_level = eCombatAbilityLevel::AVERAGE; + new_template.m_name.clear(); + new_template.m_description.clear(); + new_template.m_ped_model.clear(); + new_template.m_vehicle_model.clear(); + new_template.m_weapon_model.clear(); + new_template.m_persistent_vehicle = "None"; + new_template.m_squad_size = 1; + new_template.m_ped_invincibility = 0; + new_template.m_veh_invincibility = 0; + new_template.m_ped_health = 0; + new_template.m_ped_armor = 0; + new_template.m_ped_accuracy = 50; + new_template.m_spawn_distance = 0; for (int i = 0; i < sizeof(new_template.m_ped_proofs) / sizeof(new_template.m_ped_proofs[0]); i++) new_template.m_ped_proofs[i] = false; new_template.m_stay_in_veh = 0; new_template.m_spawn_behind_same_velocity = 0; new_template.m_disperse = 0; + new_template.m_spawn_ahead = 0; + new_template.m_favour_roads = 0; + new_template.m_max_vehicle = 0; }); ImGui::EndGroup(); @@ -250,6 +252,12 @@ namespace big { ImGui::BeginGroup(); //Toggleables + ImGui::Checkbox("Spawn ahead", &new_template.m_spawn_ahead); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Will use the distance specified and apply it in a forward direction to find a position ahead of the target"); + ImGui::Checkbox("Favour roads", &new_template.m_favour_roads); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Will try and find a road first"); ImGui::Checkbox("Disperse", &new_template.m_disperse); if (ImGui::IsItemHovered()) ImGui::SetTooltip("If the squad is on foot, will scatter units within the spawn distance"); @@ -257,6 +265,12 @@ namespace big if (ImGui::IsItemHovered()) ImGui::SetTooltip("Will spawn the mobile squad behind the target with identical velocity if applicable.\nOnly for squads with a vehicle."); ImGui::Checkbox("Stay In Vehicle", &new_template.m_stay_in_veh); + ImGui::Checkbox("Vehicle mods maxed", &new_template.m_max_vehicle); + + ImGui::EndGroup(); + ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Checkbox("Ped God Mode", &new_template.m_ped_invincibility); ImGui::Checkbox("Vehicle God Mode", &new_template.m_veh_invincibility); ImGui::Checkbox("Headshot Proof", &new_template.m_ped_proofs[0]); @@ -268,6 +282,7 @@ namespace big ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); //Slideables + ImGui::PushItemWidth(200); ImGui::Text("Ped Health"); ImGui::SliderFloat("##pedhealth", &new_template.m_ped_health, 100, 2000); @@ -293,6 +308,19 @@ namespace big } ImGui::EndCombo(); } + + ImGui::Text("Persistent vehicle"); + if (ImGui::BeginCombo("##persistent_vehicle", new_template.m_persistent_vehicle.data())) + { + if (ImGui::Selectable("None", new_template.m_persistent_vehicle == "None")) + new_template.m_persistent_vehicle = "None"; + for (auto& p : persist_car_service::list_files()) + { + if (ImGui::Selectable(p.data(), p == new_template.m_persistent_vehicle)) + new_template.m_persistent_vehicle = p; + } + ImGui::EndCombo(); + } ImGui::PopItemWidth(); ImGui::EndGroup(); @@ -334,12 +362,43 @@ namespace big }; components::button("Spawn Squad", [] { + try{ + if (check_validity(false)) - g_squad_spawner_service.spawn_squad({new_template.m_name, new_template.m_ped_model, new_template.m_weapon_model, new_template.m_vehicle_model, new_template.m_squad_size, new_template.m_ped_invincibility, new_template.m_veh_invincibility, new_template.m_ped_proofs, new_template.m_ped_health, new_template.m_ped_armor, new_template.m_spawn_distance, new_template.m_ped_accuracy, new_template.m_spawn_distance_mode, new_template.m_combat_ability_level, new_template.m_stay_in_veh, new_template.m_spawn_behind_same_velocity, new_template.m_description, new_template.m_disperse}, + g_squad_spawner_service.spawn_squad({ + new_template.m_name, + new_template.m_ped_model, + new_template.m_weapon_model, + new_template.m_vehicle_model, + new_template.m_squad_size, + new_template.m_ped_invincibility, + new_template.m_veh_invincibility, + new_template.m_ped_proofs, + new_template.m_ped_health, + new_template.m_ped_armor, + new_template.m_spawn_distance, + new_template.m_ped_accuracy, + new_template.m_spawn_distance_mode, + new_template.m_combat_ability_level, + new_template.m_stay_in_veh, + new_template.m_spawn_behind_same_velocity, + new_template.m_description, + new_template.m_disperse, + new_template.m_spawn_ahead, + new_template.m_favour_roads, + new_template.m_max_vehicle, + new_template.m_persistent_vehicle}, victim, - false, - {}); + new_template.m_spawn_distance_mode == eSquadSpawnDistance::CUSTOM, + g_orbital_drone_service.m_ground_pos); + + } + catch (std::exception e) + { + LOG(WARNING) << "Exception while spawning squad " << e.what(); + } }); + ImGui::SameLine(); components::button("Save", [] { if (check_validity(true) && !check_if_exists(new_template.m_name))