Squad Spawner (#1250)
This commit is contained in:
@ -28,6 +28,7 @@ namespace big
|
||||
|
||||
WORLD,
|
||||
SPAWN_PED,
|
||||
SQUAD_SPAWNER,
|
||||
CREATOR,
|
||||
TRAIN,
|
||||
BLACKHOLE,
|
||||
@ -116,6 +117,7 @@ namespace big
|
||||
view::world,
|
||||
{
|
||||
{tabs::SPAWN_PED, {"GUI_TAB_SPAWN_PED", view::spawn_ped}},
|
||||
{tabs::SQUAD_SPAWNER, {"Squad spawner", view::squad_spawner}},
|
||||
{tabs::CREATOR, {"GUI_TAB_CREATOR", view::creator}},
|
||||
{tabs::TRAIN, {"GUI_TAB_TRAIN", view::train}},
|
||||
{tabs::BLACKHOLE, {"GUI_TAB_BLACKHOLE", view::blackhole}},
|
||||
|
259
src/services/squad_spawner/squad_spawner.cpp
Normal file
259
src/services/squad_spawner/squad_spawner.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
#include "squad_spawner.hpp"
|
||||
|
||||
#include "gta/joaat.hpp"
|
||||
#include "util/math.hpp"
|
||||
#include "util/pathfind.hpp"
|
||||
#include "util/ped.hpp"
|
||||
#include "util/vehicle.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
|
||||
squad_member squad_spawner::spawn_squad_member(squad s)
|
||||
{
|
||||
auto handle = ped::spawn(ePedType::PED_TYPE_CIVMALE, rage::joaat(s.m_ped_model), 0, s.m_spawn_pos, 0, true);
|
||||
|
||||
if (entity::take_control_of(handle))
|
||||
{
|
||||
PED::SET_BLOCKING_OF_NON_TEMPORARY_EVENTS(handle, true);
|
||||
PED::SET_PED_CAN_BE_DRAGGED_OUT(handle, false);
|
||||
PED::SET_RAGDOLL_BLOCKING_FLAGS(handle, 1 | 16); //Block player ragdoll impacts, bullet and colission
|
||||
if (s.m_ped_proofs[4])
|
||||
PED::SET_RAGDOLL_BLOCKING_FLAGS(handle, 128); //Check for melee proof and disable corresponding ragdoll
|
||||
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 2, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 4, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 5, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 21, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 23, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 25, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 26, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 28, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 46, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 58, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 59, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 69, true);
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(handle, 70, true);
|
||||
|
||||
PED::SET_PED_CONFIG_FLAG(handle, 26, true);
|
||||
PED::SET_PED_CONFIG_FLAG(handle, 42, true);
|
||||
PED::SET_PED_CONFIG_FLAG(handle, 44, true);
|
||||
PED::SET_PED_CONFIG_FLAG(handle, 45, false);
|
||||
PED::SET_PED_CONFIG_FLAG(handle, 229, true);
|
||||
PED::SET_PED_CONFIG_FLAG(handle, 241, true);
|
||||
PED::SET_PED_CONFIG_FLAG(handle, 268, true);
|
||||
|
||||
ENTITY::SET_ENTITY_PROOFS(handle, s.m_ped_proofs[1], s.m_ped_proofs[2], s.m_ped_proofs[4], false, s.m_ped_proofs[3], 0, 0, 0);
|
||||
PED::SET_PED_SUFFERS_CRITICAL_HITS(handle, !s.m_ped_proofs[0]); //Headshot bool is true to disable, hence the unary prefix '!'
|
||||
TASK::SET_PED_PATH_MAY_ENTER_WATER(handle, true);
|
||||
TASK::SET_PED_PATH_PREFER_TO_AVOID_WATER(handle, true);
|
||||
|
||||
if (s.should_override_health())
|
||||
{
|
||||
PED::SET_PED_MAX_HEALTH(handle, s.m_ped_health);
|
||||
ENTITY::SET_ENTITY_HEALTH(handle, s.m_ped_health, 0);
|
||||
}
|
||||
if (s.should_override_armor())
|
||||
{
|
||||
PED::SET_PED_ARMOUR(handle, s.m_ped_armor);
|
||||
}
|
||||
|
||||
if (s.does_squad_have_vehicle() && s.m_weapon_model != "WEAPON_UNARMED")
|
||||
{
|
||||
WEAPON::GIVE_WEAPON_TO_PED(handle, rage::joaat("WEAPON_MICROSMG"), 999, false, false);
|
||||
}
|
||||
|
||||
WEAPON::GIVE_WEAPON_TO_PED(handle, rage::joaat(s.m_weapon_model), 999, false, true);
|
||||
PED::SET_PED_ACCURACY(handle, s.m_ped_accuracy);
|
||||
PED::SET_PED_COMBAT_ABILITY(handle, (int)s.m_combat_ability_level);
|
||||
ENTITY::SET_ENTITY_INVINCIBLE(handle, s.m_ped_invincibility);
|
||||
HUD::SET_PED_HAS_AI_BLIP(handle, true);
|
||||
HUD::SET_PED_AI_BLIP_FORCED_ON(handle, true);
|
||||
}
|
||||
return squad_member(handle, reinterpret_cast<CPed*>(g_pointers->m_gta.m_handle_to_ptr(handle)), -1);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
return std::pair<Vehicle, CVehicle*>(handle, reinterpret_cast<CVehicle*>(g_pointers->m_gta.m_handle_to_ptr(handle)));
|
||||
}
|
||||
|
||||
|
||||
bool squad_spawner::find_suitable_spawn_pos(squad& s)
|
||||
{
|
||||
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;
|
||||
|
||||
if (!s.should_override_spawn_distance())
|
||||
{
|
||||
switch (s.m_spawn_distance_mode)
|
||||
{
|
||||
case eSquadSpawnDistance::CLOSEBY: s.m_spawn_distance = 25.f; break;
|
||||
case eSquadSpawnDistance::FAR_AWAY: s.m_spawn_distance = 100.f; break;
|
||||
case eSquadSpawnDistance::ON_TARGET: s.m_spawn_distance = 10.f; break;
|
||||
case eSquadSpawnDistance::MODERATELY_DISTANCED: s.m_spawn_distance = 70.f; break;
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
//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)
|
||||
{
|
||||
reset_spawn_pos_to_offset();
|
||||
return false;
|
||||
}
|
||||
|
||||
s.m_spawn_pos = new_pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool squad_spawner::spawn_squad(squad s, player_ptr target_player, bool override_spawn_pos = false, Vector3 custom_pos = {})
|
||||
{
|
||||
s.target = target_player;
|
||||
|
||||
if (!s.target->get_net_game_player() || s.m_squad_size < 1 || !STREAMING::IS_MODEL_VALID(rage::joaat(s.m_ped_model)))
|
||||
{
|
||||
g_notification_service->push_error("Squad spawner", "Error spawning squad");
|
||||
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)));
|
||||
}
|
||||
|
||||
Hash veh_model_hash = rage::joaat(s.m_vehicle_model);
|
||||
s.current_target_ped = g_pointers->m_gta.m_ptr_to_handle(s.target->get_ped());
|
||||
float heading;
|
||||
|
||||
//Check if squad size is suitable in case a vehicle is defined
|
||||
if (s.does_squad_have_vehicle())
|
||||
{
|
||||
if (VEHICLE::GET_VEHICLE_MODEL_NUMBER_OF_SEATS(veh_model_hash) < s.m_squad_size)
|
||||
{
|
||||
s.m_squad_size = VEHICLE::GET_VEHICLE_MODEL_NUMBER_OF_SEATS(veh_model_hash);
|
||||
g_notification_service->push_warning("Squad Spawner", "The squad vehicle has insufficient seats, decreasing the squad size");
|
||||
}
|
||||
}
|
||||
|
||||
//Decide spawn location
|
||||
if (!override_spawn_pos)
|
||||
{
|
||||
s.m_spawn_pos = ENTITY::GET_ENTITY_COORDS(s.current_target_ped, true);
|
||||
|
||||
if (s.m_spawn_distance_mode == eSquadSpawnDistance::ON_TARGET)
|
||||
{
|
||||
s.m_spawn_pos = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(s.current_target_ped, 0.0, -7.f, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_squad_spawner_service.find_suitable_spawn_pos(s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.m_spawn_pos = custom_pos;
|
||||
}
|
||||
|
||||
if (VEHICLE::IS_THIS_MODEL_A_PLANE(veh_model_hash) || VEHICLE::IS_THIS_MODEL_A_HELI(veh_model_hash))
|
||||
s.m_spawn_pos.z += 50.f;
|
||||
|
||||
//Spawn squad vehicle
|
||||
if (s.does_squad_have_vehicle())
|
||||
{
|
||||
std::pair<Vehicle, CVehicle*> squad_veh = squad_spawner::spawn_squad_vehicle(s);
|
||||
s.m_veh_handle = squad_veh.first;
|
||||
s.m_veh_ptr = squad_veh.second;
|
||||
}
|
||||
bool veh_spawned = ENTITY::DOES_ENTITY_EXIST(s.m_veh_handle);
|
||||
|
||||
//Spawn squad members
|
||||
const Vector3 original_pos = s.m_spawn_pos;
|
||||
for (int i = 0; i < s.m_squad_size; i++)
|
||||
{
|
||||
//Find random position for each consecutive member if disperse is enabled
|
||||
if (i > 0 && s.m_disperse && !s.does_squad_have_vehicle())
|
||||
squad_spawner::find_suitable_spawn_pos(s);
|
||||
|
||||
s.m_members.push_back(squad_spawner::spawn_squad_member(s));
|
||||
|
||||
//Catch position change of Disperse and revert
|
||||
s.m_spawn_pos = original_pos;
|
||||
|
||||
if (entity::take_control_of(s.m_members[i].handle))
|
||||
{
|
||||
if (veh_spawned)
|
||||
PED::SET_PED_INTO_VEHICLE(s.m_members[i].handle, s.m_veh_handle, (i - 1));
|
||||
PED::SET_PED_RELATIONSHIP_GROUP_HASH(s.m_members[i].handle, RAGE_JOAAT("HATES_PLAYER"));
|
||||
|
||||
squad_spawner::build_and_perform_sequence(s, i);
|
||||
|
||||
if (s.m_stay_in_veh)
|
||||
PED::SET_PED_COMBAT_ATTRIBUTES(s.m_members[i].handle, 3, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.m_spawn_behind_same_velocity && s.does_squad_have_vehicle() && veh_spawned && target_player->get_current_vehicle())
|
||||
{
|
||||
Vehicle target_vehicle = g_pointers->m_gta.m_ptr_to_handle(target_player->get_current_vehicle());
|
||||
|
||||
if (ENTITY::GET_ENTITY_SPEED(target_vehicle) > 25.f && entity::take_control_of(s.m_veh_handle))
|
||||
{
|
||||
Vector3 velocity = ENTITY::GET_ENTITY_VELOCITY(target_vehicle);
|
||||
Vector3 behindpos = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(target_vehicle, 0.f, -7.f, 0.f);
|
||||
float heading = ENTITY::GET_ENTITY_HEADING(target_vehicle);
|
||||
|
||||
ENTITY::SET_ENTITY_COORDS(s.m_veh_handle, behindpos.x, behindpos.y, behindpos.z, 0, 0, 0, false);
|
||||
ENTITY::SET_ENTITY_HEADING(s.m_veh_handle, heading);
|
||||
ENTITY::SET_ENTITY_VELOCITY(s.m_veh_handle, velocity.x, velocity.y, velocity.z);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.has_squad_spawned())
|
||||
{
|
||||
m_active_squads.push_back(s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void squad_spawner::tick()
|
||||
{
|
||||
for (auto& s : m_active_squads)
|
||||
{
|
||||
if (s.is_squad_alive())
|
||||
{
|
||||
for (auto& m : s.m_members)
|
||||
{
|
||||
//This can be used to track attacker progress regarding their task. Anything above -1 means it is in progress.
|
||||
m.task_sequence_progress = TASK::GET_SEQUENCE_PROGRESS(m.handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::erase_if(g_squad_spawner_service.m_active_squads, [s](squad s_) {
|
||||
return s.get_id() == s_.get_id();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
226
src/services/squad_spawner/squad_spawner.hpp
Normal file
226
src/services/squad_spawner/squad_spawner.hpp
Normal file
@ -0,0 +1,226 @@
|
||||
#pragma once
|
||||
|
||||
#include "fiber_pool.hpp"
|
||||
#include "gta/enums.hpp"
|
||||
#include "natives.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
|
||||
struct squad_member
|
||||
{
|
||||
Ped handle;
|
||||
CPed* ptr;
|
||||
int task_sequence_progress = -1;
|
||||
int task_sequence = 0;
|
||||
|
||||
~squad_member()
|
||||
{
|
||||
TASK::CLEAR_SEQUENCE_TASK(&task_sequence);
|
||||
}
|
||||
|
||||
Vector3 get_pos()
|
||||
{
|
||||
rage::fvector3* pos{};
|
||||
if (ptr && ptr->m_navigation && ptr->m_navigation->get_position())
|
||||
pos = ptr->m_navigation->get_position();
|
||||
else
|
||||
return {};
|
||||
return {pos->x, pos->y, pos->z};
|
||||
}
|
||||
|
||||
bool is_ped_alive()
|
||||
{
|
||||
return ENTITY::DOES_ENTITY_EXIST(handle) && !ENTITY::IS_ENTITY_DEAD(handle, false);
|
||||
}
|
||||
};
|
||||
|
||||
enum class eSquadSpawnDistance
|
||||
{
|
||||
CUSTOM,
|
||||
ON_TARGET,
|
||||
CLOSEBY,
|
||||
MODERATELY_DISTANCED,
|
||||
FAR_AWAY
|
||||
};
|
||||
|
||||
/*
|
||||
This struct is ambiguous in its employment, hence the division of its content.
|
||||
It serves as a template for the UI and an active object to track and modify dynamically.
|
||||
The constuctor is only meant to initialize its static variables and increment the Id.
|
||||
*/
|
||||
struct squad
|
||||
{
|
||||
//Static variables
|
||||
std::string m_name;
|
||||
std::string m_description;
|
||||
std::string m_ped_model;
|
||||
std::string m_weapon_model;
|
||||
std::string m_vehicle_model;
|
||||
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_spawn_distance;
|
||||
int m_squad_size;
|
||||
eSquadSpawnDistance m_spawn_distance_mode;
|
||||
eCombatAbilityLevel m_combat_ability_level;
|
||||
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
|
||||
|
||||
/*
|
||||
Leave vehicle_model empty to spawn a squad on foot
|
||||
Ped proofs array is indexed as follows; 0 headshot, 1 bullet, 2 flame, 3 melee, 4 explosion
|
||||
Leave health and armor at 0 to default
|
||||
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)
|
||||
{
|
||||
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_ped_invincibility = ped_invincibility;
|
||||
m_veh_invincibility = veh_invincibility;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
m_ped_proofs[i] = ped_proofs[i];
|
||||
|
||||
m_ped_health = ped_health;
|
||||
m_ped_armor = ped_armor;
|
||||
m_spawn_distance = spawn_distance;
|
||||
m_ped_accuracy = ped_accuracy;
|
||||
m_spawn_distance_mode = spawn_distance_mode;
|
||||
m_combat_ability_level = combat_ability_level;
|
||||
m_stay_in_veh = stay_in_veh;
|
||||
m_spawn_behind_same_velocity = spawn_behind_same_velocity;
|
||||
m_disperse = disperse;
|
||||
}
|
||||
|
||||
int get_id() const
|
||||
{
|
||||
return m_internal_id;
|
||||
}
|
||||
|
||||
bool does_squad_have_description()
|
||||
{
|
||||
return !std::string(m_description).empty();
|
||||
}
|
||||
|
||||
bool does_squad_have_vehicle()
|
||||
{
|
||||
return !std::string(m_vehicle_model).empty();
|
||||
}
|
||||
|
||||
bool should_override_health()
|
||||
{
|
||||
if (m_ped_health > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool should_override_armor()
|
||||
{
|
||||
if (m_ped_armor > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool should_override_spawn_distance()
|
||||
{
|
||||
if (m_spawn_distance > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_squad_spawned()
|
||||
{
|
||||
return m_members.size() > 0;
|
||||
}
|
||||
|
||||
bool is_squad_alive()
|
||||
{
|
||||
for (auto& p : m_members)
|
||||
if (p.is_ped_alive())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
squad_member get_a_member_thats_alive()
|
||||
{
|
||||
for (auto& p : m_members)
|
||||
if (p.is_ped_alive())
|
||||
return p;
|
||||
return {};
|
||||
}
|
||||
|
||||
//Dynamic variables
|
||||
std::vector<squad_member> m_members{};
|
||||
player_ptr target = nullptr;
|
||||
Ped current_target_ped = 0;
|
||||
Vehicle m_veh_handle = 0;
|
||||
CVehicle* m_veh_ptr = nullptr;
|
||||
Vector3 m_spawn_pos{};
|
||||
float m_spawn_heading = 0;
|
||||
|
||||
int m_internal_id = 0;
|
||||
|
||||
Vector3 get_veh_pos()
|
||||
{
|
||||
rage::fvector3* pos{};
|
||||
if (m_veh_ptr && m_veh_ptr->m_navigation && m_veh_ptr->m_navigation->get_position())
|
||||
pos = m_veh_ptr->m_navigation->get_position();
|
||||
else
|
||||
return {};
|
||||
return {pos->x, pos->y, pos->z};
|
||||
}
|
||||
|
||||
private:
|
||||
inline static int m_instance_count;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
class squad_spawner
|
||||
{
|
||||
public:
|
||||
std::vector<squad> m_templates;
|
||||
std::vector<squad> m_active_squads;
|
||||
|
||||
squad_spawner()
|
||||
{
|
||||
load_default_templates();
|
||||
}
|
||||
|
||||
private:
|
||||
bool find_suitable_spawn_pos(squad&);
|
||||
squad_member spawn_squad_member(squad);
|
||||
std::pair<Vehicle, CVehicle*> spawn_squad_vehicle(squad);
|
||||
|
||||
public:
|
||||
void load_default_templates();
|
||||
std::filesystem::path get_file_path();
|
||||
bool spawn_squad(squad, player_ptr target_player, bool override_spawn_pos, Vector3 custom_pos);
|
||||
bool save_squad(squad);
|
||||
bool delete_squad(squad);
|
||||
bool fetch_squads();
|
||||
void tick();
|
||||
|
||||
void terminate_squads();
|
||||
void terminate_squad(squad*);
|
||||
void build_and_perform_sequence(squad&, int member);
|
||||
};
|
||||
|
||||
inline squad_spawner g_squad_spawner_service;
|
||||
}
|
80
src/services/squad_spawner/squad_spawner_actions.cpp
Normal file
80
src/services/squad_spawner/squad_spawner_actions.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "squad_spawner.hpp"
|
||||
#include "util/entity.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
|
||||
void squad_spawner::terminate_squads()
|
||||
{
|
||||
for (auto& s : m_active_squads)
|
||||
{
|
||||
if (entity::take_control_of(s.m_veh_handle))
|
||||
{
|
||||
entity::delete_entity(s.m_veh_handle);
|
||||
}
|
||||
|
||||
for (auto& m : s.m_members)
|
||||
{
|
||||
if (entity::take_control_of(m.handle))
|
||||
{
|
||||
ENTITY::SET_ENTITY_HEALTH(m.handle, 0, false);
|
||||
entity::delete_entity(m.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void squad_spawner::terminate_squad(squad* s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
if (entity::take_control_of(s->m_veh_handle))
|
||||
{
|
||||
entity::delete_entity(s->m_veh_handle);
|
||||
}
|
||||
|
||||
for (auto& m : s->m_members)
|
||||
{
|
||||
if (entity::take_control_of(m.handle))
|
||||
{
|
||||
ENTITY::SET_ENTITY_HEALTH(m.handle, 0, false);
|
||||
entity::delete_entity(m.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void squad_spawner::build_and_perform_sequence(squad& s, int member)
|
||||
{
|
||||
TASK::OPEN_SEQUENCE_TASK(&s.m_members[member].task_sequence);
|
||||
|
||||
if (member == 0 && s.does_squad_have_vehicle())
|
||||
{
|
||||
if (VEHICLE::IS_THIS_MODEL_A_CAR(rage::joaat(s.m_vehicle_model)))
|
||||
{
|
||||
TASK::TASK_VEHICLE_MISSION_PED_TARGET(0, s.m_veh_handle, s.current_target_ped, s.m_stay_in_veh ? 6 : 4, 100.f, 786468, 12.f, 5.f, true);
|
||||
if (!s.m_stay_in_veh)
|
||||
TASK::TASK_LEAVE_ANY_VEHICLE(0, 0, 0);
|
||||
}
|
||||
else if (VEHICLE::IS_THIS_MODEL_A_HELI(rage::joaat(s.m_vehicle_model)))
|
||||
{
|
||||
TASK::TASK_HELI_MISSION(0, s.m_veh_handle, 0, s.current_target_ped, 0, 0, 0, 6, 200.f, 30.f, -1, 50.f, 20.f, -1, 128 | 4096);
|
||||
VEHICLE::SET_HELI_BLADES_FULL_SPEED(s.m_veh_handle);
|
||||
}
|
||||
else if (VEHICLE::IS_THIS_MODEL_A_PLANE(rage::joaat(s.m_vehicle_model)))
|
||||
{
|
||||
TASK::TASK_PLANE_MISSION(0, s.m_veh_handle, 0, s.current_target_ped, 0, 0, 0, 6, 300, 35.f, -1, 100.f, 20.f, true);
|
||||
VEHICLE::SET_VEHICLE_FORWARD_SPEED(s.m_veh_handle, 30.f);
|
||||
}
|
||||
else if (VEHICLE::IS_THIS_MODEL_A_BOAT(rage::joaat(s.m_vehicle_model)))
|
||||
{
|
||||
TASK::TASK_BOAT_MISSION(0, s.m_veh_handle, 0, s.current_target_ped, 0, 0, 0, 6, 300, 786468, 10.f, 7);
|
||||
}
|
||||
}
|
||||
|
||||
TASK::TASK_COMBAT_PED(0, s.current_target_ped, 67108864, 16); //flag 67108864 should prevent peds from attaining other targets
|
||||
TASK::SET_SEQUENCE_TO_REPEAT(s.m_members[member].task_sequence, 1);
|
||||
TASK::CLOSE_SEQUENCE_TASK(s.m_members[member].task_sequence);
|
||||
TASK::TASK_PERFORM_SEQUENCE(s.m_members[member].handle, s.m_members[member].task_sequence);
|
||||
}
|
||||
}
|
107
src/services/squad_spawner/squad_spawner_save_files.cpp
Normal file
107
src/services/squad_spawner/squad_spawner_save_files.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
#include "squad_spawner.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
|
||||
std::filesystem::path squad_spawner::get_file_path()
|
||||
{
|
||||
return g_file_manager->get_project_folder("squad_spawner").get_path();
|
||||
}
|
||||
|
||||
bool squad_spawner::fetch_squads()
|
||||
{
|
||||
g_squad_spawner_service.m_templates.clear();
|
||||
bool success = false;
|
||||
std::ifstream read;
|
||||
try
|
||||
{
|
||||
for (const auto& path : std::filesystem::directory_iterator(get_file_path()))
|
||||
{
|
||||
nlohmann::json j;
|
||||
if (path.path().extension() == ".json")
|
||||
{
|
||||
read.open(path.path(), std::ifstream::in);
|
||||
if (read.is_open())
|
||||
{
|
||||
read >> j;
|
||||
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;
|
||||
}
|
||||
catch (std::exception e)
|
||||
{
|
||||
LOG(WARNING) << "Squad Spawner fetching files failed: " << e.what();
|
||||
}
|
||||
|
||||
g_squad_spawner_service.load_default_templates();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool squad_spawner::save_squad(squad s)
|
||||
{
|
||||
for (auto s_ : g_squad_spawner_service.m_templates)
|
||||
if (s_.m_name.compare(s.m_name) == 0)
|
||||
return false;
|
||||
|
||||
std::ofstream write;
|
||||
std::string savename = s.m_name;
|
||||
savename.append(".json");
|
||||
std::filesystem::path path = get_file_path() / savename;
|
||||
nlohmann::json j;
|
||||
to_json(j, s);
|
||||
write.open(path, std::ofstream::out | std::ofstream::trunc);
|
||||
try
|
||||
{
|
||||
if (write.is_open())
|
||||
{
|
||||
write << std::setw(4) << j << std::endl;
|
||||
write.close();
|
||||
g_notification_service->push("Squad spawner", std::string("Succesfully saved ").append(s.m_name));
|
||||
fetch_squads();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (std::exception e)
|
||||
{
|
||||
LOG(WARNING) << "Squad Spawner saving squad failed: " << e.what();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool squad_spawner::delete_squad(squad s)
|
||||
{
|
||||
std::string savename = s.m_name;
|
||||
savename.append(".json");
|
||||
std::filesystem::path path = get_file_path() / savename;
|
||||
std::filesystem::remove(path);
|
||||
return fetch_squads();
|
||||
}
|
||||
|
||||
void squad_spawner::load_default_templates()
|
||||
{
|
||||
bool ped_proofs[5] = {0, 0, 0, 0, 0};
|
||||
bool ped_proofs_annoying[5] = {1, 0, 0, 1, 1};
|
||||
|
||||
m_templates.push_back(squad("Swat team", "s_m_y_swat_01", "WEAPON_SMG", "riot", 4, false, false, ped_proofs, 400, 400, 0, 75, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::PROFESSIONAL, false, false, "An elite team of swat operatives that will quickly swarm the target"));
|
||||
m_templates.push_back(squad("Secret service", "s_m_m_highsec_01", "WEAPON_CARBINERIFLE", "oracle", 4, false, false, ped_proofs, 0, 200, 0, 75, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::PROFESSIONAL, false, false, "MIB"));
|
||||
m_templates.push_back(squad("Ballas Gang", "ig_ballasog", "WEAPON_MICROSMG", "chino2", 2, false, false, ped_proofs, 0, 0, 0, 50, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::POOR, true, false, "A classic driveby from the local Ballas gang"));
|
||||
m_templates.push_back(squad("Grove Gang", "g_m_y_famca_01", "WEAPON_PISTOL", "chino", 2, false, false, ped_proofs, 0, 0, 0, 50, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::POOR, true, false, "A classic driveby from the local Grove gang"));
|
||||
m_templates.push_back(squad("Robbers", "g_m_m_chicold_01", "WEAPON_SMG_MK2", "baller5", 4, false, false, ped_proofs, 0, 0, 0, 65, eSquadSpawnDistance::MODERATELY_DISTANCED, eCombatAbilityLevel::AVERAGE, false, false, "Mysterious mercenaries that hide behind snow masks"));
|
||||
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("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));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user