Engineer Navbot Initial release
This commit is contained in:
parent
7103733e4f
commit
f76aced3ae
@ -41,6 +41,10 @@ extern int stored_buttons;
|
|||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
extern bool freecam_is_toggled;
|
extern bool freecam_is_toggled;
|
||||||
#endif
|
#endif
|
||||||
|
namespace hacks::tf2::misc_aimbot
|
||||||
|
{
|
||||||
|
bool ShouldHitBuilding(CachedEntity *ent);
|
||||||
|
}
|
||||||
namespace hooked_methods
|
namespace hooked_methods
|
||||||
{
|
{
|
||||||
void sendAchievementKv(int value);
|
void sendAchievementKv(int value);
|
||||||
|
@ -18,7 +18,7 @@ void CreateMove();
|
|||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
void DrawText();
|
void DrawText();
|
||||||
#endif
|
#endif
|
||||||
|
int getCarriedBuilding();
|
||||||
extern int last_number;
|
extern int last_number;
|
||||||
|
|
||||||
extern float last_bucket;
|
extern float last_bucket;
|
||||||
|
@ -6,6 +6,7 @@ namespace hacks::tf2::NavBot
|
|||||||
bool init(bool first_cm);
|
bool init(bool first_cm);
|
||||||
namespace task
|
namespace task
|
||||||
{
|
{
|
||||||
|
|
||||||
enum task : uint8_t
|
enum task : uint8_t
|
||||||
{
|
{
|
||||||
none = 0,
|
none = 0,
|
||||||
@ -15,28 +16,49 @@ enum task : uint8_t
|
|||||||
ammo,
|
ammo,
|
||||||
dispenser,
|
dispenser,
|
||||||
followbot,
|
followbot,
|
||||||
outofbounds
|
outofbounds,
|
||||||
|
engineer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum engineer_task : uint8_t
|
||||||
|
{
|
||||||
|
nothing = 0,
|
||||||
|
// Build a new building
|
||||||
|
goto_build_spot,
|
||||||
|
// Go to an existing building
|
||||||
|
goto_building,
|
||||||
|
build_building,
|
||||||
|
// Originally were going to be added seperately, but we already have autorepair and upgrade seperately
|
||||||
|
// upgrade_building,
|
||||||
|
// repair_building,
|
||||||
|
upgradeorrepair_building,
|
||||||
|
// Well time to just run at people and gun them (Rip old name: YEEEEEEEEEEEEEEHAW)
|
||||||
|
staynear_engineer
|
||||||
|
};
|
||||||
|
|
||||||
|
extern engineer_task current_engineer_task;
|
||||||
|
|
||||||
struct Task
|
struct Task
|
||||||
{
|
{
|
||||||
task id;
|
task id;
|
||||||
int priority;
|
int priority;
|
||||||
Task(task _id)
|
Task(task id)
|
||||||
{
|
{
|
||||||
id = _id;
|
this->id = id;
|
||||||
priority = _id == none ? 0 : 5;
|
priority = id == none ? 0 : 5;
|
||||||
}
|
}
|
||||||
Task(task _id, int _priority)
|
Task(task id, int priority)
|
||||||
{
|
{
|
||||||
id = _id;
|
this->id = id;
|
||||||
priority = _priority;
|
this->priority = priority;
|
||||||
}
|
}
|
||||||
operator task()
|
operator task()
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
constexpr std::array<task, 2> blocking_tasks{ followbot, outofbounds };
|
|
||||||
|
constexpr std::array<task, 3> blocking_tasks{ followbot, outofbounds, engineer };
|
||||||
extern Task current_task;
|
extern Task current_task;
|
||||||
} // namespace task
|
} // namespace task
|
||||||
struct bot_class_config
|
struct bot_class_config
|
||||||
|
@ -109,7 +109,7 @@ static ConVar *teammatesPushaway{ nullptr };
|
|||||||
|
|
||||||
int getCarriedBuilding()
|
int getCarriedBuilding()
|
||||||
{
|
{
|
||||||
if (CE_BYTE(LOCAL_E, netvar.m_bCarryingObject))
|
if (CE_INT(LOCAL_E, netvar.m_bCarryingObject))
|
||||||
return HandleToIDX(CE_INT(LOCAL_E, netvar.m_hCarriedObject));
|
return HandleToIDX(CE_INT(LOCAL_E, netvar.m_hCarriedObject));
|
||||||
for (int i = 1; i < MAX_ENTITIES; i++)
|
for (int i = 1; i < MAX_ENTITIES; i++)
|
||||||
{
|
{
|
||||||
@ -118,7 +118,7 @@ int getCarriedBuilding()
|
|||||||
continue;
|
continue;
|
||||||
if (HandleToIDX(CE_INT(ent, netvar.m_hBuilder)) != LOCAL_E->m_IDX)
|
if (HandleToIDX(CE_INT(ent, netvar.m_hBuilder)) != LOCAL_E->m_IDX)
|
||||||
continue;
|
continue;
|
||||||
if (!CE_BYTE(ent, netvar.m_bPlacing))
|
if (!CE_INT(ent, netvar.m_bPlacing))
|
||||||
continue;
|
continue;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ static settings::Int autoupgrade_sentry_level("autoupgrade.sentry.level", "3");
|
|||||||
static settings::Int autoupgrade_dispenser_level("autoupgrade.dispenser.level", "3");
|
static settings::Int autoupgrade_dispenser_level("autoupgrade.dispenser.level", "3");
|
||||||
static settings::Int autoupgrade_teleport_level("autoupgrade.teleport.level", "2");
|
static settings::Int autoupgrade_teleport_level("autoupgrade.teleport.level", "2");
|
||||||
|
|
||||||
static bool ShouldHitBuilding(CachedEntity *ent)
|
bool ShouldHitBuilding(CachedEntity *ent)
|
||||||
{
|
{
|
||||||
if (!autoupgrade_enabled && !autorepair_enabled)
|
if (!autoupgrade_enabled && !autorepair_enabled)
|
||||||
return false;
|
return false;
|
||||||
@ -386,7 +386,7 @@ static void BuildingAimbot()
|
|||||||
int cur_ammo = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
|
int cur_ammo = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
|
||||||
float wrench_range = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
|
float wrench_range = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
|
||||||
// Center is further away than actual hit range for buildings, so add some more
|
// Center is further away than actual hit range for buildings, so add some more
|
||||||
wrench_range += (float) 50.f;
|
wrench_range += 50.f;
|
||||||
|
|
||||||
if (cur_ammo == 0)
|
if (cur_ammo == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include "Aimbot.hpp"
|
#include "Aimbot.hpp"
|
||||||
#include "FollowBot.hpp"
|
#include "FollowBot.hpp"
|
||||||
#include "soundcache.hpp"
|
#include "soundcache.hpp"
|
||||||
|
#include "Misc.hpp"
|
||||||
|
#include "MiscTemporary.hpp"
|
||||||
|
|
||||||
namespace hacks::tf2::NavBot
|
namespace hacks::tf2::NavBot
|
||||||
{
|
{
|
||||||
@ -13,6 +15,7 @@ static settings::Boolean enabled("navbot.enabled", "false");
|
|||||||
static settings::Boolean stay_near("navbot.stay-near", "true");
|
static settings::Boolean stay_near("navbot.stay-near", "true");
|
||||||
static settings::Boolean heavy_mode("navbot.other-mode", "false");
|
static settings::Boolean heavy_mode("navbot.other-mode", "false");
|
||||||
static settings::Boolean spy_mode("navbot.spy-mode", "false");
|
static settings::Boolean spy_mode("navbot.spy-mode", "false");
|
||||||
|
static settings::Boolean engineer_mode("navbot.engineer-mode", "false");
|
||||||
static settings::Boolean get_health("navbot.get-health-and-ammo", "true");
|
static settings::Boolean get_health("navbot.get-health-and-ammo", "true");
|
||||||
static settings::Float jump_distance("navbot.autojump.trigger-distance", "300");
|
static settings::Float jump_distance("navbot.autojump.trigger-distance", "300");
|
||||||
static settings::Boolean autojump("navbot.autojump.enabled", "false");
|
static settings::Boolean autojump("navbot.autojump.enabled", "false");
|
||||||
@ -22,30 +25,82 @@ static settings::Int spy_ignore_time("navbot.spy-ignore-time", "5000");
|
|||||||
// -Forward declarations-
|
// -Forward declarations-
|
||||||
bool init(bool first_cm);
|
bool init(bool first_cm);
|
||||||
static bool navToSniperSpot();
|
static bool navToSniperSpot();
|
||||||
|
static bool navToBuildingSpot();
|
||||||
static bool stayNear();
|
static bool stayNear();
|
||||||
static bool getDispenserHealthAndAmmo();
|
static bool stayNearEngineer();
|
||||||
static bool getHealthAndAmmo();
|
static bool getDispenserHealthAndAmmo(int metal = -1);
|
||||||
|
static bool getHealthAndAmmo(int metal = -1);
|
||||||
static void autoJump();
|
static void autoJump();
|
||||||
static void updateSlot();
|
static void updateSlot();
|
||||||
|
static void update_building_spots();
|
||||||
|
static bool engineerLogic();
|
||||||
static std::pair<CachedEntity *, float> getNearestPlayerDistance(bool vischeck = true);
|
static std::pair<CachedEntity *, float> getNearestPlayerDistance(bool vischeck = true);
|
||||||
|
using task::current_engineer_task;
|
||||||
using task::current_task;
|
using task::current_task;
|
||||||
|
|
||||||
// -Variables-
|
// -Variables-
|
||||||
static std::vector<std::pair<CNavArea *, Vector>> sniper_spots;
|
static std::vector<std::pair<CNavArea *, Vector>> sniper_spots;
|
||||||
|
static std::vector<std::pair<CNavArea *, Vector>> building_spots;
|
||||||
|
static std::vector<CNavArea *> blacklisted_build_spots;
|
||||||
|
// Our Buildings. We need this so we can remove them on object_destroyed.
|
||||||
|
static std::vector<CachedEntity *> local_buildings;
|
||||||
|
// Needed for blacklisting
|
||||||
|
static CNavArea *current_build_area;
|
||||||
// How long should the bot wait until pathing again?
|
// How long should the bot wait until pathing again?
|
||||||
static Timer wait_until_path{};
|
static Timer wait_until_path{};
|
||||||
|
// Engineer version of above
|
||||||
|
static Timer wait_until_path_engineer{};
|
||||||
// Time before following target cloaked spy again
|
// Time before following target cloaked spy again
|
||||||
static std::array<Timer, PLAYER_ARRAY_SIZE> spy_cloak{};
|
static std::array<Timer, PLAYER_ARRAY_SIZE> spy_cloak{};
|
||||||
// Don't spam spy path thanks
|
// Don't spam spy path thanks
|
||||||
static Timer spy_path{};
|
static Timer spy_path{};
|
||||||
|
// Big wait between updating Engineer building spots
|
||||||
|
static Timer engineer_update{};
|
||||||
|
// Recheck Building Area
|
||||||
|
static Timer engineer_recheck{};
|
||||||
|
// Timer for resetting Build attempts
|
||||||
|
static Timer build_timer{};
|
||||||
|
// Timer for wait between rotation and checking if we can place
|
||||||
|
static Timer rotation_timer{};
|
||||||
|
// Uses to check how long until we should resend the "build" command
|
||||||
|
static Timer build_command_timer{};
|
||||||
|
// Dispenser Nav cooldown
|
||||||
|
static Timer dispenser_nav_timer{};
|
||||||
|
// Last Yaw used for building
|
||||||
|
static float build_current_rotation = -180.0f;
|
||||||
|
// How many times have we tried to place?
|
||||||
|
static int build_attempts = 0;
|
||||||
|
// Enum for Building types
|
||||||
|
enum Building
|
||||||
|
{
|
||||||
|
None = -1,
|
||||||
|
Dispenser = 0,
|
||||||
|
TP_Entrace,
|
||||||
|
Sentry,
|
||||||
|
TP_Exit
|
||||||
|
};
|
||||||
|
|
||||||
|
// Successfully built? (Unknown == Bot is still trying to build and isn't sure if it will work or not yet)
|
||||||
|
enum success_build
|
||||||
|
{
|
||||||
|
Failure = 0,
|
||||||
|
Unknown,
|
||||||
|
Success
|
||||||
|
};
|
||||||
|
|
||||||
// What is the bot currently doing
|
// What is the bot currently doing
|
||||||
namespace task
|
namespace task
|
||||||
{
|
{
|
||||||
Task current_task = task::none;
|
Task current_task = task::none;
|
||||||
}
|
engineer_task current_engineer_task = engineer_task::nothing;
|
||||||
|
} // namespace task
|
||||||
|
|
||||||
constexpr bot_class_config DIST_SPY{ 10.0f, 50.0f, 1000.0f };
|
constexpr bot_class_config DIST_SPY{ 10.0f, 50.0f, 1000.0f };
|
||||||
constexpr bot_class_config DIST_OTHER{ 100.0f, 200.0f, 300.0f };
|
constexpr bot_class_config DIST_OTHER{ 100.0f, 200.0f, 300.0f };
|
||||||
constexpr bot_class_config DIST_SNIPER{ 1000.0f, 1500.0f, 3000.0f };
|
constexpr bot_class_config DIST_SNIPER{ 1000.0f, 1500.0f, 3000.0f };
|
||||||
|
constexpr bot_class_config DIST_ENGINEER{ 600.0f, 1000.0f, 2500.0f };
|
||||||
|
// Gunslinger Engineers really don't cate at all
|
||||||
|
constexpr bot_class_config DIST_GUNSLINGER_ENGINEER{ 100.0f, 300.0f, 500.0f };
|
||||||
|
|
||||||
static void CreateMove()
|
static void CreateMove()
|
||||||
{
|
{
|
||||||
@ -65,17 +120,41 @@ static void CreateMove()
|
|||||||
autoJump();
|
autoJump();
|
||||||
if (primary_only)
|
if (primary_only)
|
||||||
updateSlot();
|
updateSlot();
|
||||||
|
if (engineer_mode)
|
||||||
|
{
|
||||||
|
if (LOCAL_E && CE_GOOD(LOCAL_E))
|
||||||
|
{
|
||||||
|
for (int i = g_IEngine->GetMaxClients() + 1; i < MAX_ENTITIES; i++)
|
||||||
|
{
|
||||||
|
CachedEntity *ent = ENTITY(i);
|
||||||
|
if (!ent || CE_BAD(ent) || ent->m_bEnemy() || !ent->m_bAlivePlayer())
|
||||||
|
continue;
|
||||||
|
if (HandleToIDX(CE_INT(ent, netvar.m_hBuilder)) != LOCAL_E->m_IDX)
|
||||||
|
continue;
|
||||||
|
if (std::find(local_buildings.begin(), local_buildings.end(), ent) == local_buildings.end())
|
||||||
|
local_buildings.push_back(ent);
|
||||||
|
}
|
||||||
|
update_building_spots();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (get_health)
|
if (get_health)
|
||||||
{
|
{
|
||||||
if (getDispenserHealthAndAmmo())
|
int metal = 0;
|
||||||
|
if (engineer_mode)
|
||||||
|
metal = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
|
||||||
|
if ((dispenser_nav_timer.test_and_set(1000) && getDispenserHealthAndAmmo(metal)))
|
||||||
return;
|
return;
|
||||||
if (getHealthAndAmmo())
|
if (getHealthAndAmmo(metal))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (engineer_mode)
|
||||||
|
if (engineerLogic())
|
||||||
|
return;
|
||||||
|
|
||||||
if (blocking)
|
if (blocking)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Spy can just walk into the enemy
|
// Spy can just walk into the enemy
|
||||||
if (spy_mode)
|
if (spy_mode)
|
||||||
{
|
{
|
||||||
@ -127,6 +206,8 @@ bool init(bool first_cm)
|
|||||||
return false;
|
return false;
|
||||||
if (!inited)
|
if (!inited)
|
||||||
{
|
{
|
||||||
|
blacklisted_build_spots.clear();
|
||||||
|
local_buildings.clear();
|
||||||
sniper_spots.clear();
|
sniper_spots.clear();
|
||||||
// Add all sniper spots to vector
|
// Add all sniper spots to vector
|
||||||
for (auto &area : nav::navfile->m_areas)
|
for (auto &area : nav::navfile->m_areas)
|
||||||
@ -140,6 +221,128 @@ bool init(bool first_cm)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct area_struct
|
||||||
|
{
|
||||||
|
// The Area
|
||||||
|
CNavArea *area;
|
||||||
|
// Distance away from enemies
|
||||||
|
float min_distance;
|
||||||
|
// Valid enemies to area
|
||||||
|
std::vector<Vector *> enemy_list;
|
||||||
|
|
||||||
|
CNavArea *first()
|
||||||
|
{
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
float second()
|
||||||
|
{
|
||||||
|
return min_distance;
|
||||||
|
}
|
||||||
|
std::vector<Vector *> third()
|
||||||
|
{
|
||||||
|
return enemy_list;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void update_building_spots()
|
||||||
|
{
|
||||||
|
if (engineer_update.test_and_set(10000))
|
||||||
|
{
|
||||||
|
building_spots.clear();
|
||||||
|
// Store in here to reduce operations
|
||||||
|
std::vector<Vector> enemy_positions;
|
||||||
|
// Stores valid areas, the float is the minimum distance away from enemies, needed for sorting later
|
||||||
|
std::vector<area_struct> areas;
|
||||||
|
|
||||||
|
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
|
||||||
|
{
|
||||||
|
CachedEntity *ent = ENTITY(i);
|
||||||
|
// Grab only Enemies and only if they are in soundcache
|
||||||
|
if (!ent || CE_INVALID(ent) || !ent->m_bAlivePlayer() || !ent->m_bEnemy())
|
||||||
|
continue;
|
||||||
|
if (ent->m_vecDormantOrigin())
|
||||||
|
enemy_positions.push_back(*ent->m_vecDormantOrigin());
|
||||||
|
}
|
||||||
|
auto config = &DIST_ENGINEER;
|
||||||
|
if (HasWeapon(LOCAL_E, 142))
|
||||||
|
config = &DIST_GUNSLINGER_ENGINEER;
|
||||||
|
for (auto &area : nav::navfile->m_areas)
|
||||||
|
{
|
||||||
|
// Blacklisted for building
|
||||||
|
if (std::find(blacklisted_build_spots.begin(), blacklisted_build_spots.end(), &area) != blacklisted_build_spots.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// These positions we should vischeck
|
||||||
|
std::vector<Vector *> vischeck_positions;
|
||||||
|
|
||||||
|
// Minimum distance the area was away from enemies
|
||||||
|
float min_dist_away = FLT_MAX;
|
||||||
|
|
||||||
|
// Area Center
|
||||||
|
auto area_pos = area.m_center;
|
||||||
|
// Don't want to instantly hit the floor
|
||||||
|
area_pos.z += 42.0f;
|
||||||
|
|
||||||
|
// Found enemy below min/above max range away from area
|
||||||
|
bool enemy_found = false;
|
||||||
|
bool out_of_reach = true;
|
||||||
|
|
||||||
|
for (auto &pos : enemy_positions)
|
||||||
|
{
|
||||||
|
auto dist = area_pos.DistTo(pos);
|
||||||
|
if (dist < config->min)
|
||||||
|
{
|
||||||
|
enemy_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Found someone within min and max range
|
||||||
|
if (dist < config->max)
|
||||||
|
{
|
||||||
|
out_of_reach = false;
|
||||||
|
// Should vischeck this one
|
||||||
|
vischeck_positions.push_back(&pos);
|
||||||
|
if (dist < min_dist_away)
|
||||||
|
min_dist_away = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Too close/Too far away
|
||||||
|
if (enemy_found || out_of_reach)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Area is valid (Distance wise)
|
||||||
|
areas.push_back({ &area, min_dist_away, vischeck_positions });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort, be as close to preferred as possible
|
||||||
|
std::sort(areas.begin(), areas.end(), [&](area_struct a, area_struct b) { return std::abs(a.second() - config->preferred) < std::abs(b.second() - config->preferred); });
|
||||||
|
|
||||||
|
// Still need to do vischeck stuff
|
||||||
|
for (auto &area : areas)
|
||||||
|
{
|
||||||
|
// Is the enemy able to see the area?
|
||||||
|
bool can_see_area = false;
|
||||||
|
// Area Center
|
||||||
|
auto area_pos = area.first()->m_center;
|
||||||
|
// Don't want to instantly hit the floor
|
||||||
|
area_pos.z += 42.0f;
|
||||||
|
// Loop all valid enemies
|
||||||
|
for (auto pos : area.third())
|
||||||
|
{
|
||||||
|
if (IsVectorVisible(area_pos, *pos))
|
||||||
|
{
|
||||||
|
can_see_area = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Someone can see the area. Abort! (Gunslinger Engineer does not care)
|
||||||
|
if (can_see_area && !HasWeapon(LOCAL_E, 142))
|
||||||
|
continue;
|
||||||
|
// All good!
|
||||||
|
building_spots.push_back(std::pair<CNavArea *, Vector>(area.first(), area.first()->m_center));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool navToSniperSpot()
|
static bool navToSniperSpot()
|
||||||
{
|
{
|
||||||
// Don't path if you already have commands. But also don't error out.
|
// Don't path if you already have commands. But also don't error out.
|
||||||
@ -165,6 +368,315 @@ static bool navToSniperSpot()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool navToBuildingSpot()
|
||||||
|
{
|
||||||
|
// Don't path if you already have commands. But also don't error out.
|
||||||
|
if (!nav::ReadyForCommands || (current_task != task::engineer && current_task != task::none))
|
||||||
|
return true;
|
||||||
|
// Wait a bit before pathing again
|
||||||
|
if (!wait_until_path_engineer.test_and_set(2000))
|
||||||
|
return false;
|
||||||
|
// Max 10 attempts
|
||||||
|
for (int attempts = 0; attempts < 10 && attempts < building_spots.size(); attempts++)
|
||||||
|
{
|
||||||
|
// Get a Building spot
|
||||||
|
auto &area = building_spots[attempts];
|
||||||
|
// Check if spot is considered safe (no sentry, no sticky)
|
||||||
|
if (!nav::isSafe(area.first))
|
||||||
|
continue;
|
||||||
|
// Try to nav there
|
||||||
|
if (nav::navTo(area.second, 1, true, true, false))
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 1 };
|
||||||
|
current_build_area = area.first;
|
||||||
|
current_engineer_task = task::engineer_task::goto_build_spot;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Building selectBuilding()
|
||||||
|
{
|
||||||
|
int metal = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
|
||||||
|
int metal_sentry = 130;
|
||||||
|
|
||||||
|
// We have a mini sentry, costs less
|
||||||
|
if (HasWeapon(LOCAL_E, 142))
|
||||||
|
metal_sentry = 100;
|
||||||
|
|
||||||
|
// Do we already have these?
|
||||||
|
bool sentry_built = false;
|
||||||
|
bool dispenser_built = false;
|
||||||
|
// Loop all buildings
|
||||||
|
for (auto &building : local_buildings)
|
||||||
|
{
|
||||||
|
if (building->m_iClassID() == CL_CLASS(CObjectSentrygun))
|
||||||
|
sentry_built = true;
|
||||||
|
else if (building->m_iClassID() == CL_CLASS(CObjectDispenser))
|
||||||
|
dispenser_built = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metal >= metal_sentry && !sentry_built)
|
||||||
|
return Sentry;
|
||||||
|
else if (metal >= 100 && !dispenser_built)
|
||||||
|
return Dispenser;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static success_build buildBuilding()
|
||||||
|
{
|
||||||
|
int metal = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
|
||||||
|
// Out of Metal
|
||||||
|
if (metal < 100)
|
||||||
|
return Failure;
|
||||||
|
// Last building
|
||||||
|
static Building last_building = selectBuilding();
|
||||||
|
// Get best building to build right now
|
||||||
|
Building building = selectBuilding();
|
||||||
|
// Reset Rotation on these conditions
|
||||||
|
if (building != last_building || build_timer.check(1000))
|
||||||
|
{
|
||||||
|
// We changed the target building, means it was successful!
|
||||||
|
if (!build_timer.check(1000))
|
||||||
|
return Success;
|
||||||
|
|
||||||
|
// No building
|
||||||
|
if (building == None)
|
||||||
|
return Failure;
|
||||||
|
|
||||||
|
build_attempts = 0;
|
||||||
|
build_current_rotation = -180.0f;
|
||||||
|
build_timer.update();
|
||||||
|
}
|
||||||
|
// No building
|
||||||
|
if (building == None)
|
||||||
|
return Failure;
|
||||||
|
|
||||||
|
last_building = building;
|
||||||
|
build_timer.update();
|
||||||
|
if (rotation_timer.test_and_set(300))
|
||||||
|
{
|
||||||
|
// Look slightly downwards for building process
|
||||||
|
current_user_cmd->viewangles.x = 20.0f;
|
||||||
|
// Set Yaw
|
||||||
|
current_user_cmd->viewangles.y = build_current_rotation;
|
||||||
|
// Rotate
|
||||||
|
build_current_rotation += 20.0f;
|
||||||
|
build_attempts++;
|
||||||
|
// Put building in hand if not already
|
||||||
|
if (hacks::shared::misc::getCarriedBuilding() == -1 && build_command_timer.test_and_set(50))
|
||||||
|
g_IEngine->ClientCmd_Unrestricted(format("build ", building).c_str());
|
||||||
|
}
|
||||||
|
else if (rotation_timer.check(200))
|
||||||
|
{
|
||||||
|
if (hacks::shared::misc::getCarriedBuilding() != -1)
|
||||||
|
{
|
||||||
|
int carried_building = hacks::shared::misc::getCarriedBuilding();
|
||||||
|
// It works! Place building
|
||||||
|
if (CE_INT(ENTITY(carried_building), netvar.m_bCanPlace))
|
||||||
|
current_user_cmd->buttons |= IN_ATTACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Bad area
|
||||||
|
if (build_attempts >= 14)
|
||||||
|
{
|
||||||
|
blacklisted_build_spots.push_back(current_build_area);
|
||||||
|
return Failure;
|
||||||
|
}
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool navToBuilding(CachedEntity *target = nullptr)
|
||||||
|
{
|
||||||
|
if (local_buildings.size())
|
||||||
|
{
|
||||||
|
int priority = 5;
|
||||||
|
if (current_engineer_task == task::staynear_engineer)
|
||||||
|
priority = 7;
|
||||||
|
// Just grab target and nav there
|
||||||
|
if (target)
|
||||||
|
if (nav::navTo(target->m_vecOrigin(), priority))
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::goto_building;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Nav to random building
|
||||||
|
for (auto &building : local_buildings)
|
||||||
|
{
|
||||||
|
if (nav::navTo(building->m_vecOrigin(), priority))
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::goto_building;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool engineerLogic()
|
||||||
|
{
|
||||||
|
std::vector<CachedEntity *> new_building_list;
|
||||||
|
for (auto &building : local_buildings)
|
||||||
|
if (CE_VALID(building) && !CE_INT(building, netvar.m_bPlacing))
|
||||||
|
new_building_list.push_back(building);
|
||||||
|
local_buildings = new_building_list;
|
||||||
|
|
||||||
|
// Overwrites and Not yet running engineer task
|
||||||
|
if ((current_task != task::engineer || current_engineer_task == task::engineer_task::nothing || current_engineer_task == task::engineer_task::staynear_engineer) && current_task != task::health && current_task != task::ammo)
|
||||||
|
{
|
||||||
|
// Already have a building
|
||||||
|
if (local_buildings.size())
|
||||||
|
{
|
||||||
|
int metal = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
|
||||||
|
if (metal)
|
||||||
|
for (auto &building : local_buildings)
|
||||||
|
// Hey hit the building thanks (gunslinger engineer shouldn't care)
|
||||||
|
if (hacks::tf2::misc_aimbot::ShouldHitBuilding(building) && !HasWeapon(LOCAL_E, 142))
|
||||||
|
{
|
||||||
|
if (navToBuilding(building))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's terrify some people (gunslinger engineer)
|
||||||
|
if (HasWeapon(LOCAL_E, 142))
|
||||||
|
stayNearEngineer();
|
||||||
|
|
||||||
|
else if (selectBuilding() != None)
|
||||||
|
{
|
||||||
|
// If we're near our buildings and have the metal, build another one
|
||||||
|
for (auto &building : local_buildings)
|
||||||
|
if (building->m_vecOrigin().DistTo(LOCAL_E->m_vecOrigin()) <= 300.0f)
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::build_building;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// We're too far away, go to building
|
||||||
|
else if (navToBuilding())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If it's metal we're missing, get some metal
|
||||||
|
else if (metal < 100)
|
||||||
|
{
|
||||||
|
if ((dispenser_nav_timer.test_and_set(1000) && getDispenserHealthAndAmmo(metal)) || getHealthAndAmmo(metal))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Else just Roam around the map and kill people
|
||||||
|
else if (stayNearEngineer())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Nav to a Building spot
|
||||||
|
else if (navToBuildingSpot())
|
||||||
|
{
|
||||||
|
engineer_recheck.update();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Metal
|
||||||
|
int metal = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
|
||||||
|
if ((dispenser_nav_timer.test_and_set(1000) && getDispenserHealthAndAmmo(metal)) || getHealthAndAmmo(metal))
|
||||||
|
return true;
|
||||||
|
switch (current_engineer_task)
|
||||||
|
{
|
||||||
|
// Upgrade/repair
|
||||||
|
case (task::engineer_task::upgradeorrepair_building):
|
||||||
|
{
|
||||||
|
if (metal)
|
||||||
|
if (local_buildings.size())
|
||||||
|
for (auto &building : local_buildings)
|
||||||
|
{
|
||||||
|
if (building->m_vecOrigin().DistTo(LOCAL_E->m_vecOrigin()) <= 300.0f)
|
||||||
|
{
|
||||||
|
if (hacks::tf2::misc_aimbot::ShouldHitBuilding(building))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
}
|
||||||
|
// Going to existing building
|
||||||
|
case (task::engineer_task::goto_building):
|
||||||
|
{
|
||||||
|
if (nav::ReadyForCommands)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
if (local_buildings.size())
|
||||||
|
for (auto &building : local_buildings)
|
||||||
|
{
|
||||||
|
if (building->m_vecOrigin().DistTo(LOCAL_E->m_vecOrigin()) <= 300.0f)
|
||||||
|
{
|
||||||
|
if (metal && hacks::tf2::misc_aimbot::ShouldHitBuilding(building))
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::upgradeorrepair_building;
|
||||||
|
}
|
||||||
|
if (current_engineer_task != task::engineer_task::upgradeorrepair_building)
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
break;
|
||||||
|
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Going to spot to build
|
||||||
|
case (task::engineer_task::goto_build_spot):
|
||||||
|
{
|
||||||
|
// We Arrived, time to (try) to build!
|
||||||
|
if (nav::ReadyForCommands)
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::build_building;
|
||||||
|
}
|
||||||
|
else if (engineer_recheck.test_and_set(15000))
|
||||||
|
{
|
||||||
|
if (navToBuildingSpot())
|
||||||
|
{
|
||||||
|
engineer_recheck.update();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Build building
|
||||||
|
case (task::engineer_task::build_building):
|
||||||
|
{
|
||||||
|
auto status = buildBuilding();
|
||||||
|
// Failed, Get a new Task
|
||||||
|
if (status == Failure)
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
}
|
||||||
|
else if (status == Success)
|
||||||
|
{
|
||||||
|
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Still building
|
||||||
|
else if (status == Unknown)
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static std::pair<CachedEntity *, float> getNearestPlayerDistance(bool vischeck)
|
static std::pair<CachedEntity *, float> getNearestPlayerDistance(bool vischeck)
|
||||||
{
|
{
|
||||||
float distance = FLT_MAX;
|
float distance = FLT_MAX;
|
||||||
@ -204,7 +716,7 @@ static bool isValidNearPosition(Vector vec, Vector target, const bot_class_confi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if began pathing
|
// Returns true if began pathing
|
||||||
static bool stayNearPlayer(CachedEntity *&ent, const bot_class_config &config, CNavArea *&result)
|
static bool stayNearPlayer(CachedEntity *&ent, const bot_class_config &config, CNavArea *&result, bool engineer = false)
|
||||||
{
|
{
|
||||||
if (!CE_VALID(ent))
|
if (!CE_VALID(ent))
|
||||||
return false;
|
return false;
|
||||||
@ -244,8 +756,14 @@ static bool stayNearPlayer(CachedEntity *&ent, const bot_class_config &config, C
|
|||||||
{
|
{
|
||||||
if (nav::navTo(i->m_center, 7, true, false))
|
if (nav::navTo(i->m_center, 7, true, false))
|
||||||
{
|
{
|
||||||
result = i;
|
result = i;
|
||||||
current_task = task::stay_near;
|
if (engineer)
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::staynear_engineer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current_task = task::stay_near;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,8 +773,14 @@ static bool stayNearPlayer(CachedEntity *&ent, const bot_class_config &config, C
|
|||||||
auto it = select_randomly(areas.begin(), areas.end());
|
auto it = select_randomly(areas.begin(), areas.end());
|
||||||
if (nav::navTo((*it.base())->m_center, 7, true, false))
|
if (nav::navTo((*it.base())->m_center, 7, true, false))
|
||||||
{
|
{
|
||||||
result = *it.base();
|
result = *it.base();
|
||||||
current_task = task::stay_near;
|
if (engineer)
|
||||||
|
{
|
||||||
|
current_task = { task::engineer, 4 };
|
||||||
|
current_engineer_task = task::staynear_engineer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current_task = task::stay_near;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,6 +824,95 @@ static bool stayNearPlayers(const bot_class_config &config, CachedEntity *&resul
|
|||||||
}
|
}
|
||||||
} // namespace stayNearHelpers
|
} // namespace stayNearHelpers
|
||||||
|
|
||||||
|
// stayNear()'s Little Texan brother
|
||||||
|
static bool stayNearEngineer()
|
||||||
|
{
|
||||||
|
static CachedEntity *last_target = nullptr;
|
||||||
|
static CNavArea *last_area = nullptr;
|
||||||
|
|
||||||
|
// What distances do we have to use?
|
||||||
|
const bot_class_config *config = &DIST_OTHER;
|
||||||
|
|
||||||
|
// Check if someone is too close to us and then target them instead
|
||||||
|
std::pair<CachedEntity *, float> nearest = getNearestPlayerDistance();
|
||||||
|
if (nearest.first && nearest.first != last_target && nearest.second < config->min)
|
||||||
|
if (stayNearHelpers::stayNearPlayer(nearest.first, *config, last_area, true))
|
||||||
|
{
|
||||||
|
last_target = nearest.first;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool valid_dormant = false;
|
||||||
|
if (CE_VALID(last_target) && RAW_ENT(last_target)->IsDormant())
|
||||||
|
{
|
||||||
|
if (last_target->m_vecDormantOrigin())
|
||||||
|
valid_dormant = true;
|
||||||
|
}
|
||||||
|
if (current_task == task::stay_near)
|
||||||
|
{
|
||||||
|
static Timer invalid_area_time{};
|
||||||
|
static Timer invalid_target_time{};
|
||||||
|
// Do we already have a stay near target? Check if its still good.
|
||||||
|
if (CE_GOOD(last_target) || valid_dormant)
|
||||||
|
invalid_target_time.update();
|
||||||
|
else
|
||||||
|
invalid_area_time.update();
|
||||||
|
// Check if we still have LOS and are close enough/far enough
|
||||||
|
Vector position;
|
||||||
|
if (CE_GOOD(last_target) || valid_dormant)
|
||||||
|
{
|
||||||
|
position = *last_target->m_vecDormantOrigin();
|
||||||
|
}
|
||||||
|
if ((CE_GOOD(last_target) || valid_dormant) && stayNearHelpers::isValidNearPosition(last_area->m_center, position, *config))
|
||||||
|
invalid_area_time.update();
|
||||||
|
|
||||||
|
if ((CE_GOOD(last_target) || valid_dormant) && (!g_pPlayerResource->isAlive(last_target->m_IDX) || !last_target->m_bEnemy() || !player_tools::shouldTarget(last_target) || !spy_cloak[last_target->m_IDX].check(*spy_ignore_time) || (hacks::shared::aimbot::ignore_cloak && IsPlayerInvisible(last_target))))
|
||||||
|
{
|
||||||
|
if (hacks::shared::aimbot::ignore_cloak && IsPlayerInvisible(last_target))
|
||||||
|
spy_cloak[last_target->m_IDX].update();
|
||||||
|
nav::clearInstructions();
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
}
|
||||||
|
else if (invalid_area_time.test_and_set(300))
|
||||||
|
{
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
}
|
||||||
|
else if (invalid_target_time.test_and_set(5000))
|
||||||
|
{
|
||||||
|
current_engineer_task = task::engineer_task::nothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Are we doing nothing? Check if our current location can still attack our
|
||||||
|
// last target
|
||||||
|
if (current_engineer_task != task::engineer_task::staynear_engineer && (CE_GOOD(last_target) || valid_dormant) && g_pPlayerResource->isAlive(last_target->m_IDX) && last_target->m_bEnemy())
|
||||||
|
{
|
||||||
|
if (hacks::shared::aimbot::ignore_cloak && IsPlayerInvisible(last_target))
|
||||||
|
spy_cloak[last_target->m_IDX].update();
|
||||||
|
if (spy_cloak[last_target->m_IDX].check(*spy_ignore_time))
|
||||||
|
{
|
||||||
|
Vector position = *last_target->m_vecDormantOrigin();
|
||||||
|
|
||||||
|
if (stayNearHelpers::isValidNearPosition(g_pLocalPlayer->v_Origin, position, *config))
|
||||||
|
return true;
|
||||||
|
// If not, can we try pathing to our last target again?
|
||||||
|
if (stayNearHelpers::stayNearPlayer(last_target, *config, last_area, true))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
last_target = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Timer wait_until_stay_near{};
|
||||||
|
if (current_task == task::engineer_task::staynear_engineer)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (wait_until_stay_near.test_and_set(1000))
|
||||||
|
{
|
||||||
|
// We're doing nothing? Do something!
|
||||||
|
return stayNearHelpers::stayNearPlayers(*config, last_target, last_area);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Main stay near function
|
// Main stay near function
|
||||||
static bool stayNear()
|
static bool stayNear()
|
||||||
{
|
{
|
||||||
@ -445,7 +1058,7 @@ static std::vector<Vector> getDispensers()
|
|||||||
return dispensers;
|
return dispensers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool getDispenserHealthAndAmmo()
|
static bool getDispenserHealthAndAmmo(int metal)
|
||||||
{
|
{
|
||||||
// Timeout for standing next to dispenser
|
// Timeout for standing next to dispenser
|
||||||
static Timer dispenser_timeout{};
|
static Timer dispenser_timeout{};
|
||||||
@ -453,6 +1066,12 @@ static bool getDispenserHealthAndAmmo()
|
|||||||
static Timer dispenser_cooldown{};
|
static Timer dispenser_cooldown{};
|
||||||
float health = static_cast<float>(LOCAL_E->m_iHealth()) / LOCAL_E->m_iMaxHealth();
|
float health = static_cast<float>(LOCAL_E->m_iHealth()) / LOCAL_E->m_iMaxHealth();
|
||||||
bool lowAmmo = hasLowAmmo();
|
bool lowAmmo = hasLowAmmo();
|
||||||
|
if (metal != -1)
|
||||||
|
{
|
||||||
|
lowAmmo = metal < 100 && selectBuilding() == None;
|
||||||
|
if (current_engineer_task == task::engineer_task::upgradeorrepair_building)
|
||||||
|
lowAmmo = metal == 0;
|
||||||
|
}
|
||||||
// Check if we should cancel this task
|
// Check if we should cancel this task
|
||||||
if (current_task == task::dispenser)
|
if (current_task == task::dispenser)
|
||||||
{
|
{
|
||||||
@ -510,10 +1129,16 @@ static bool getDispenserHealthAndAmmo()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool getHealthAndAmmo()
|
static bool getHealthAndAmmo(int metal)
|
||||||
{
|
{
|
||||||
float health = static_cast<float>(LOCAL_E->m_iHealth()) / LOCAL_E->m_iMaxHealth();
|
float health = static_cast<float>(LOCAL_E->m_iHealth()) / LOCAL_E->m_iMaxHealth();
|
||||||
bool lowAmmo = hasLowAmmo();
|
bool lowAmmo = hasLowAmmo();
|
||||||
|
if (metal != -1)
|
||||||
|
{
|
||||||
|
lowAmmo = metal < 100 && selectBuilding() == None;
|
||||||
|
if (current_engineer_task == task::engineer_task::upgradeorrepair_building)
|
||||||
|
lowAmmo = metal == 0;
|
||||||
|
}
|
||||||
// Check if we should cancel this task
|
// Check if we should cancel this task
|
||||||
if (current_task == task::health || current_task == task::ammo)
|
if (current_task == task::health || current_task == task::ammo)
|
||||||
{
|
{
|
||||||
@ -643,6 +1268,18 @@ static slots getBestSlot(slots active_slot)
|
|||||||
else
|
else
|
||||||
return secondary;
|
return secondary;
|
||||||
}
|
}
|
||||||
|
case tf_engineer:
|
||||||
|
{
|
||||||
|
// Use wrench, because we are trying to build
|
||||||
|
if (current_task == task::engineer)
|
||||||
|
{
|
||||||
|
if (current_engineer_task == task::engineer_task::build_building)
|
||||||
|
return active_slot;
|
||||||
|
if (current_engineer_task == task::engineer_task::upgradeorrepair_building)
|
||||||
|
return melee;
|
||||||
|
}
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if (nearest.second <= 400)
|
if (nearest.second <= 400)
|
||||||
@ -674,7 +1311,36 @@ static void updateSlot()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static InitRoutine runinit([]() { EC::Register(EC::CreateMove, CreateMove, "navbot", EC::early); });
|
class ObjectDestroyListener : public IGameEventListener2
|
||||||
|
{
|
||||||
|
virtual void FireGameEvent(IGameEvent *event)
|
||||||
|
{
|
||||||
|
if (!isHackActive() || !engineer_mode)
|
||||||
|
return;
|
||||||
|
// Get index of destroyed object
|
||||||
|
int index = event->GetInt("index");
|
||||||
|
// Destroyed Entity
|
||||||
|
CachedEntity *ent = ENTITY(index);
|
||||||
|
// Get Entry in the vector
|
||||||
|
auto it = std::find(local_buildings.begin(), local_buildings.end(), ent);
|
||||||
|
// If found, erase
|
||||||
|
if (it != local_buildings.end())
|
||||||
|
local_buildings.erase(it);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectDestroyListener &listener()
|
||||||
|
{
|
||||||
|
static ObjectDestroyListener object{};
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
static InitRoutine runinit([]() {
|
||||||
|
g_IEventManager2->AddListener(&listener(), "object_destroyed", false);
|
||||||
|
EC::Register(EC::CreateMove, CreateMove, "navbot", EC::early);
|
||||||
|
EC::Register(
|
||||||
|
EC::Shutdown, []() { g_IEventManager2->RemoveListener(&listener()); }, "navbot_shutdown");
|
||||||
|
});
|
||||||
|
|
||||||
void change(settings::VariableBase<bool> &, bool)
|
void change(settings::VariableBase<bool> &, bool)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user