Squad spawner improvements (#1329)

* New Fun UI for self with Super hero fly & bulletshield
* Added missing hotkey global
* New debug window for animations
* Some more context menu additions
* Changed private variable to public for Orbital drone
* Added max vehicle & favour roads & spawn ahead & orbital drone coords on custom spawn mode & persisten vehicle integration
* Removed Log entry
* Removed some more log entries
* Some small tweaks
* Added persistent vehicle to the logic
This commit is contained in:
DayibBaba 2023-05-10 23:29:16 +02:00 committed by GitHub
parent 49235195b5
commit 34c37b2042
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 248 additions and 68 deletions

View File

@ -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;
}

View File

@ -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<Vehicle, CVehicle*> 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<Vehicle, CVehicle*> 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<Vehicle, CVehicle*>(handle, reinterpret_cast<CVehicle*>(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;

View File

@ -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
{

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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))