mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2024-12-22 20:17:24 +08:00
Squad Spawner (#1250)
This commit is contained in:
parent
de251b2e57
commit
5299fe52ef
@ -3,7 +3,7 @@ include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
gtav_classes
|
||||
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
|
||||
GIT_TAG 4f76d41ff4c39c296606238cbfa9fd4bceee3a54
|
||||
GIT_TAG 57548ccd8742f0355fc674e8f4dedee119d640e5
|
||||
GIT_PROGRESS TRUE
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "services/context_menu/context_menu_service.hpp"
|
||||
#include "services/orbital_drone/orbital_drone.hpp"
|
||||
#include "services/vehicle/vehicle_control_service.hpp"
|
||||
#include "services/squad_spawner/squad_spawner.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
|
||||
|
||||
@ -217,4 +218,14 @@ namespace big
|
||||
script::get_current()->yield();
|
||||
}
|
||||
}
|
||||
|
||||
void backend::squad_spawner()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
g_squad_spawner_service.tick();
|
||||
|
||||
script::get_current()->yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,5 +20,6 @@ namespace big
|
||||
static void world_loop();
|
||||
static void orbital_drone();
|
||||
static void vehicle_control();
|
||||
static void squad_spawner();
|
||||
};
|
||||
}
|
||||
|
@ -1919,3 +1919,16 @@ enum class eVehicleSeats
|
||||
OUTSIDE_LEFT,
|
||||
OUTSIDE_RIGHT,
|
||||
};
|
||||
|
||||
enum class eCombatAbilityLevel{
|
||||
POOR,
|
||||
AVERAGE,
|
||||
PROFESSIONAL
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(eCombatAbilityLevel,
|
||||
{
|
||||
{eCombatAbilityLevel::POOR, "poor"},
|
||||
{eCombatAbilityLevel::AVERAGE, "average"},
|
||||
{eCombatAbilityLevel::PROFESSIONAL, "professional"}
|
||||
})
|
||||
|
@ -22,6 +22,8 @@ namespace big
|
||||
static void nav_item(std::pair<tabs, navigation_struct>&, int);
|
||||
|
||||
static void input_text_with_hint(const std::string_view label, const std::string_view hint, char* buf, size_t buf_size, ImGuiInputTextFlags_ flag = ImGuiInputTextFlags_None, std::function<void()> cb = nullptr);
|
||||
static void input_text_with_hint(const std::string_view label, const std::string_view hint, std::string* buf, ImGuiInputTextFlags_ flag = ImGuiInputTextFlags_None, std::function<void()> cb = nullptr);
|
||||
|
||||
static void input_text(const std::string_view label, char* buf, size_t buf_size, ImGuiInputTextFlags_ flag = ImGuiInputTextFlags_None, std::function<void()> cb = nullptr);
|
||||
|
||||
static bool selectable(const std::string_view, bool);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "fiber_pool.hpp"
|
||||
#include "gui/components/components.hpp"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include "natives.hpp"
|
||||
|
||||
namespace big
|
||||
@ -15,4 +16,16 @@ namespace big
|
||||
PAD::DISABLE_ALL_CONTROL_ACTIONS(0);
|
||||
});
|
||||
}
|
||||
|
||||
void components::input_text_with_hint(const std::string_view label, const std::string_view hint, std::string* buf, ImGuiInputTextFlags_ flag, std::function<void()> cb)
|
||||
{
|
||||
if (ImGui::InputTextWithHint(label.data(), hint.data(), buf, flag))
|
||||
if (cb)
|
||||
g_fiber_pool->queue_job(std::move(cb));
|
||||
|
||||
if (ImGui::IsItemActive())
|
||||
g_fiber_pool->queue_job([] {
|
||||
PAD::DISABLE_ALL_CONTROL_ACTIONS(0);
|
||||
});
|
||||
}
|
||||
}
|
@ -26,8 +26,10 @@
|
||||
#include "services/player_database/player_database_service.hpp"
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "services/script_patcher/script_patcher_service.hpp"
|
||||
#include "services/squad_spawner/squad_spawner.hpp"
|
||||
#include "services/vehicle/handling_service.hpp"
|
||||
#include "services/vehicle/vehicle_control_service.hpp"
|
||||
#include "services/squad_spawner/squad_spawner.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
@ -121,6 +123,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
g_script_mgr.add_script(std::make_unique<script>(&backend::world_loop, "World"));
|
||||
g_script_mgr.add_script(std::make_unique<script>(&backend::orbital_drone, "Orbital Drone"));
|
||||
g_script_mgr.add_script(std::make_unique<script>(&backend::vehicle_control, "Vehicle control"));
|
||||
g_script_mgr.add_script(std::make_unique<script>(&backend::squad_spawner, "Squad spawner"));
|
||||
g_script_mgr.add_script(std::make_unique<script>(&context_menu_service::context_menu, "Context Menu"));
|
||||
|
||||
LOG(INFO) << "Scripts registered.";
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "gta/enums.hpp"
|
||||
#include "math.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "math.hpp"
|
||||
#include "script.hpp"
|
||||
#include "gta/enums.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
|
||||
namespace big::pathfind
|
||||
@ -12,7 +14,8 @@ namespace big::pathfind
|
||||
|
||||
inline bool load_path_nodes(Vector3 coords)
|
||||
{
|
||||
if(PATHFIND::ARE_NODES_LOADED_FOR_AREA(coords.x, coords.y, coords.z, coords.y)) return true;
|
||||
if (PATHFIND::ARE_NODES_LOADED_FOR_AREA(coords.x, coords.y, coords.z, coords.y))
|
||||
return true;
|
||||
|
||||
PATHFIND::REQUEST_PATH_NODES_IN_AREA_THIS_FRAME(coords.x, coords.y, coords.z, coords.y);
|
||||
|
||||
@ -27,7 +30,8 @@ namespace big::pathfind
|
||||
|
||||
inline bool load_navmesh_area(Vector3 coords, float radius)
|
||||
{
|
||||
if(PATHFIND::ARE_ALL_NAVMESH_REGIONS_LOADED()) return true;
|
||||
if (PATHFIND::ARE_ALL_NAVMESH_REGIONS_LOADED())
|
||||
return true;
|
||||
|
||||
PATHFIND::ADD_NAVMESH_REQUIRED_REGION(coords.x, coords.z, radius);
|
||||
|
||||
@ -71,37 +75,87 @@ namespace big::pathfind
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool find_random_location_in_vicinity(Vector3 coords, Vector3& outcoords, float& outheading, int flag, int vicinity)
|
||||
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;
|
||||
if (load_path_nodes(center))
|
||||
return PATHFIND::GET_RANDOM_VEHICLE_NODE(center.x, center.y, center.z, radius, 0, avoid_dead_ends, avoid_highways, &outcoords, &node_id);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int rand1 = rand() % 4;
|
||||
Vector3 changedcoords = coords;
|
||||
inline void apply_distance_to_random_direction(Vector3& outcoords, float distance)
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(0, 1);
|
||||
|
||||
switch (rand1)
|
||||
if (dis(gen))
|
||||
{
|
||||
case 1: changedcoords.x += rand() % vicinity + vicinity / 2; break;
|
||||
case 2: changedcoords.x -= rand() % vicinity + vicinity / 2; break;
|
||||
case 3: changedcoords.y += rand() % vicinity + vicinity / 2; break;
|
||||
case 4: changedcoords.y -= rand() % vicinity + vicinity / 2; break;
|
||||
}
|
||||
|
||||
find_closest_vehicle_node(changedcoords, outcoords, outheading, flag);
|
||||
|
||||
if (math::distance_between_vectors(outcoords, changedcoords) > vicinity)
|
||||
{
|
||||
if (find_safe_pos_ped(changedcoords, outcoords, true, flag))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
outcoords = coords;
|
||||
return false;
|
||||
}
|
||||
|
||||
dis(gen) ? outcoords.x += distance : outcoords.x -= distance;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
{
|
||||
dis(gen) ? outcoords.y += distance : outcoords.y -= distance;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
inline bool find_random_location_in_vicinity(Vector3 coords, Vector3& outcoords, float& outheading, int flag, int vicinity)
|
||||
{
|
||||
outcoords = coords;
|
||||
|
||||
apply_distance_to_random_direction(outcoords, vicinity);
|
||||
|
||||
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))
|
||||
{
|
||||
outcoords = coords;
|
||||
|
||||
if (!pathfind::find_safe_pos_ped(changed_coords, outcoords, false, 0))
|
||||
{
|
||||
outcoords = coords;
|
||||
}
|
||||
}
|
||||
|
||||
return outcoords != coords;
|
||||
}
|
||||
|
||||
/*
|
||||
The precision means the algorithm will try and get a position as close to the desired distance as possible
|
||||
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)
|
||||
{
|
||||
if (precision > 200)
|
||||
precision = 200;
|
||||
|
||||
std::vector<Vector3> found_locations{};
|
||||
|
||||
//Find random positions
|
||||
for (int i = 0; i < precision; i++)
|
||||
{
|
||||
Vector3 new_pos{};
|
||||
find_random_location_in_vicinity(coords, new_pos, outheading, flag, vicinity);
|
||||
found_locations.push_back(new_pos);
|
||||
}
|
||||
|
||||
Vector3 best_location = found_locations[0];
|
||||
//Measure the distance of the position to the given vicinity distance
|
||||
static float distance_to_vicinity = std::abs(vicinity - math::distance_between_vectors(best_location, coords));
|
||||
for (auto l : found_locations)
|
||||
{
|
||||
float new_distance_to_vicinity = std::abs(vicinity - math::distance_between_vectors(l, coords));
|
||||
//If the new distance is smaller, that means we have a position that is closer to the edge
|
||||
if (new_distance_to_vicinity < distance_to_vicinity)
|
||||
{
|
||||
distance_to_vicinity = new_distance_to_vicinity;
|
||||
best_location = l;
|
||||
}
|
||||
}
|
||||
outcoords = best_location;
|
||||
|
||||
return outcoords != coords;
|
||||
}
|
||||
}
|
@ -67,6 +67,10 @@ namespace big
|
||||
NETWORK::SHUTDOWN_AND_LOAD_MOST_RECENT_SAVE();
|
||||
});
|
||||
|
||||
components::button("Remove Black Screen", [] {
|
||||
CAM::DO_SCREEN_FADE_IN(0);
|
||||
});
|
||||
|
||||
components::button("Tp To Safe Pos", [] {
|
||||
Vector3 safepos{};
|
||||
float heading;
|
||||
|
@ -47,6 +47,7 @@ namespace big
|
||||
static void fun_vehicle();
|
||||
static void vehicle_control();
|
||||
static void spawn_ped();
|
||||
static void squad_spawner();
|
||||
static void time_and_weather();
|
||||
static void spoofing();
|
||||
static void teleport();
|
||||
|
349
src/views/world/view_squad_spawner.cpp
Normal file
349
src/views/world/view_squad_spawner.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
#include "services/gta_data/gta_data_service.hpp"
|
||||
#include "services/squad_spawner/squad_spawner.hpp"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
|
||||
void view::squad_spawner()
|
||||
{
|
||||
const char* const spawn_distance_modes[5]{"Custom", "On target", "Closeby", "Moderately distanced", "Far away"};
|
||||
const char* const combat_ability_levels[3]{"Poor", "Average", "Professional"};
|
||||
|
||||
static squad new_template{};
|
||||
static player_ptr victim = g_player_service->get_selected();
|
||||
|
||||
ImGui::Text("Victim");
|
||||
ImGui::SetNextItemWidth(200);
|
||||
if (ImGui::BeginCombo("##victim", victim->get_name()))
|
||||
{
|
||||
auto self = g_player_service->get_self();
|
||||
if (ImGui::Selectable(self->get_name(), self->id() == victim->id()))
|
||||
victim = self;
|
||||
for (auto p : g_player_service->players() | std::ranges::views::values)
|
||||
{
|
||||
if (ImGui::Selectable(p->get_name(), p->id() == victim->id()))
|
||||
{
|
||||
victim = p;
|
||||
if (g.player.spectating)
|
||||
g_player_service->set_selected(victim);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Checkbox("SPECTATE"_T.data(), &g.player.spectating))
|
||||
{
|
||||
g_player_service->set_selected(victim);
|
||||
};
|
||||
|
||||
if (victim->id() != g_player_service->get_selected()->id() && victim->is_valid())
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.3f, 1.0f));
|
||||
ImGui::Text("Warning: Victim and selected player are not the same");
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
static squad deletion_squad;
|
||||
|
||||
if (!std::string(deletion_squad.m_name).empty())
|
||||
ImGui::OpenPopup("##deletesquad");
|
||||
|
||||
if (ImGui::BeginPopupModal("##deletesquad"))
|
||||
{
|
||||
ImGui::Text("Are you sure you want to delete %s?", deletion_squad.m_name);
|
||||
|
||||
if (ImGui::Button("Yes"))
|
||||
{
|
||||
g_squad_spawner_service.delete_squad(deletion_squad);
|
||||
deletion_squad.m_name = "";
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("No"))
|
||||
{
|
||||
deletion_squad.m_name = "";
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
|
||||
ImGui::SetNextItemWidth(200);
|
||||
if (ImGui::BeginCombo("Choose From Templates", "Templates"))
|
||||
{
|
||||
components::button("Fetch Custom Squads", [] {
|
||||
g_squad_spawner_service.fetch_squads();
|
||||
});
|
||||
|
||||
for (auto& temp : g_squad_spawner_service.m_templates)
|
||||
{
|
||||
if (ImGui::Selectable(temp.m_name.data()))
|
||||
{
|
||||
if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
|
||||
{
|
||||
deletion_squad = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_template = temp;
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered() && temp.does_squad_have_description())
|
||||
ImGui::SetTooltip(temp.m_description.data());
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::BeginGroup(); //Main variables
|
||||
|
||||
ImGui::Text("Squad Details");
|
||||
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);
|
||||
|
||||
auto ped_found = std::find_if(g_gta_data_service->peds().begin(), g_gta_data_service->peds().end(), [=](const auto& pair) {
|
||||
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 (ImGui::ListBoxHeader("##pedlist", ImVec2(250, 200)))
|
||||
{
|
||||
for (auto& p : g_gta_data_service->peds() | std::ranges::views::values)
|
||||
{
|
||||
std::string p_model = p.m_name;
|
||||
std::string filter = new_template.m_ped_model;
|
||||
std::transform(p_model.begin(), p_model.end(), p_model.begin(), ::tolower);
|
||||
std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
|
||||
if (p_model.find(filter) != std::string::npos && ImGui::Selectable(p.m_name))
|
||||
{
|
||||
new_template.m_ped_model = p.m_name;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::ListBoxFooter();
|
||||
}
|
||||
}
|
||||
|
||||
components::input_text_with_hint("##vehmodel",
|
||||
"Vehicle model",
|
||||
&new_template.m_vehicle_model);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Leave empty to spawn on foot");
|
||||
|
||||
auto veh_found = std::find_if(g_gta_data_service->vehicles().begin(), g_gta_data_service->vehicles().end(), [=](const auto& pair) {
|
||||
return pair.second.m_name == new_template.m_vehicle_model;
|
||||
});
|
||||
|
||||
if (!new_template.m_vehicle_model.empty() && veh_found == g_gta_data_service->vehicles().end())
|
||||
{
|
||||
if (ImGui::ListBoxHeader("##vehlist", ImVec2(250, 200)))
|
||||
{
|
||||
for (auto& p : g_gta_data_service->vehicles() | std::ranges::views::values)
|
||||
{
|
||||
std::string p_model = p.m_name;
|
||||
std::string filter = new_template.m_vehicle_model;
|
||||
std::transform(p_model.begin(), p_model.end(), p_model.begin(), ::tolower);
|
||||
std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
|
||||
if (p_model.find(filter) != std::string::npos && ImGui::Selectable(p.m_name))
|
||||
{
|
||||
new_template.m_vehicle_model = p.m_name;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::ListBoxFooter();
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
auto weap_found = std::find_if(g_gta_data_service->weapons().begin(), g_gta_data_service->weapons().end(), [=](const auto& pair) {
|
||||
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 (ImGui::ListBoxHeader("##weaplist", ImVec2(250, 200)))
|
||||
{
|
||||
for (auto& p : g_gta_data_service->weapons() | std::ranges::views::values)
|
||||
{
|
||||
std::string p_model = p.m_name;
|
||||
std::string filter = new_template.m_weapon_model;
|
||||
std::transform(p_model.begin(), p_model.end(), p_model.begin(), ::tolower);
|
||||
std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
|
||||
if (p_model.find(filter) != std::string::npos && ImGui::Selectable(p.m_name))
|
||||
{
|
||||
new_template.m_weapon_model = p.m_name;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::ListBoxFooter();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Spawn Distance");
|
||||
if (ImGui::BeginCombo("##spawndistance", spawn_distance_modes[(int)new_template.m_spawn_distance_mode]))
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (ImGui::Selectable(spawn_distance_modes[i], (int)new_template.m_spawn_distance_mode == i))
|
||||
new_template.m_spawn_distance_mode = (eSquadSpawnDistance)i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::Text("Squad Size");
|
||||
ImGui::SliderInt("##squadsize", &new_template.m_squad_size, 1, 8);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup(); //General actions
|
||||
|
||||
ImGui::Text("Actions");
|
||||
ImGui::Spacing();
|
||||
components::button(std::string("Terminate " + std::to_string(g_squad_spawner_service.m_active_squads.size()) + " squads"), [] {
|
||||
g_squad_spawner_service.terminate_squads();
|
||||
});
|
||||
|
||||
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;
|
||||
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;
|
||||
});
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::Spacing();
|
||||
if (ImGui::TreeNode("Advanced Options"))
|
||||
{
|
||||
ImGui::BeginGroup(); //Toggleables
|
||||
|
||||
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");
|
||||
ImGui::Checkbox("Vehicle catch up", &new_template.m_spawn_behind_same_velocity);
|
||||
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("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]);
|
||||
ImGui::Checkbox("Bullet Proof", &new_template.m_ped_proofs[1]);
|
||||
ImGui::Checkbox("Flame Proof", &new_template.m_ped_proofs[2]);
|
||||
ImGui::Checkbox("Melee Proof", &new_template.m_ped_proofs[3]);
|
||||
ImGui::Checkbox("Explosion Proof", &new_template.m_ped_proofs[4]);
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup(); //Slideables
|
||||
ImGui::PushItemWidth(200);
|
||||
ImGui::Text("Ped Health");
|
||||
ImGui::SliderFloat("##pedhealth", &new_template.m_ped_health, 100, 2000);
|
||||
ImGui::Text("Ped Armor");
|
||||
ImGui::SliderFloat("##pedarmor", &new_template.m_ped_armor, 0, 2000);
|
||||
ImGui::Text("Ped Accuracy");
|
||||
ImGui::SliderFloat("##pedaccuracy", &new_template.m_ped_accuracy, 0, 100);
|
||||
ImGui::Text("Custom Spawn Distance");
|
||||
ImGui::SliderFloat("##customspawndistance", &new_template.m_spawn_distance, 10, 500);
|
||||
ImGui::EndGroup();
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Leave these values at 0 to default, except for accuracy.");
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup(); //Chooseables
|
||||
ImGui::Text("Combat Ability");
|
||||
if (ImGui::BeginCombo("##combatability", combat_ability_levels[(int)new_template.m_combat_ability_level]))
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (ImGui::Selectable(combat_ability_levels[i], (int)new_template.m_combat_ability_level == i))
|
||||
new_template.m_combat_ability_level = (eCombatAbilityLevel)i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
|
||||
components::input_text_with_hint("##new_template.m_description",
|
||||
"Squad new_template.m_description",
|
||||
&new_template.m_description);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
static auto check_validity = [=](bool save) -> bool {
|
||||
if (!victim->is_valid() && !save)
|
||||
{
|
||||
g_notification_service->push_error("Squad spawner", "Choose a victim first");
|
||||
return false;
|
||||
}
|
||||
if (std::string(new_template.m_ped_model).empty())
|
||||
{
|
||||
g_notification_service->push_error("Squad spawner", "A ped model is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
static auto check_if_exists = [=](std::string squad_name) -> bool {
|
||||
bool exists = false;
|
||||
|
||||
for (auto& s : g_squad_spawner_service.m_templates)
|
||||
{
|
||||
if (s.m_name.compare(squad_name) == 0)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return exists;
|
||||
};
|
||||
|
||||
components::button("Spawn Squad", [] {
|
||||
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},
|
||||
victim,
|
||||
false,
|
||||
{});
|
||||
});
|
||||
ImGui::SameLine();
|
||||
components::button("Save", [] {
|
||||
if (check_validity(true) && !check_if_exists(new_template.m_name))
|
||||
g_squad_spawner_service.save_squad(new_template);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user