Engineer Navbot Initial release

This commit is contained in:
BenCat07 2020-01-06 13:29:02 +01:00
parent 7103733e4f
commit f76aced3ae
6 changed files with 720 additions and 28 deletions

View File

@ -41,6 +41,10 @@ extern int stored_buttons;
#if ENABLE_VISUALS
extern bool freecam_is_toggled;
#endif
namespace hacks::tf2::misc_aimbot
{
bool ShouldHitBuilding(CachedEntity *ent);
}
namespace hooked_methods
{
void sendAchievementKv(int value);

View File

@ -18,7 +18,7 @@ void CreateMove();
#if ENABLE_VISUALS
void DrawText();
#endif
int getCarriedBuilding();
extern int last_number;
extern float last_bucket;

View File

@ -6,6 +6,7 @@ namespace hacks::tf2::NavBot
bool init(bool first_cm);
namespace task
{
enum task : uint8_t
{
none = 0,
@ -15,28 +16,49 @@ enum task : uint8_t
ammo,
dispenser,
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
{
task id;
int priority;
Task(task _id)
Task(task id)
{
id = _id;
priority = _id == none ? 0 : 5;
this->id = id;
priority = id == none ? 0 : 5;
}
Task(task _id, int _priority)
Task(task id, int priority)
{
id = _id;
priority = _priority;
this->id = id;
this->priority = priority;
}
operator task()
{
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;
} // namespace task
struct bot_class_config

View File

@ -109,7 +109,7 @@ static ConVar *teammatesPushaway{ nullptr };
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));
for (int i = 1; i < MAX_ENTITIES; i++)
{
@ -118,7 +118,7 @@ int getCarriedBuilding()
continue;
if (HandleToIDX(CE_INT(ent, netvar.m_hBuilder)) != LOCAL_E->m_IDX)
continue;
if (!CE_BYTE(ent, netvar.m_bPlacing))
if (!CE_INT(ent, netvar.m_bPlacing))
continue;
return i;
}

View File

@ -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_teleport_level("autoupgrade.teleport.level", "2");
static bool ShouldHitBuilding(CachedEntity *ent)
bool ShouldHitBuilding(CachedEntity *ent)
{
if (!autoupgrade_enabled && !autorepair_enabled)
return false;
@ -386,7 +386,7 @@ static void BuildingAimbot()
int cur_ammo = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
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
wrench_range += (float) 50.f;
wrench_range += 50.f;
if (cur_ammo == 0)
return;

View File

@ -5,6 +5,8 @@
#include "Aimbot.hpp"
#include "FollowBot.hpp"
#include "soundcache.hpp"
#include "Misc.hpp"
#include "MiscTemporary.hpp"
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 heavy_mode("navbot.other-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::Float jump_distance("navbot.autojump.trigger-distance", "300");
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-
bool init(bool first_cm);
static bool navToSniperSpot();
static bool navToBuildingSpot();
static bool stayNear();
static bool getDispenserHealthAndAmmo();
static bool getHealthAndAmmo();
static bool stayNearEngineer();
static bool getDispenserHealthAndAmmo(int metal = -1);
static bool getHealthAndAmmo(int metal = -1);
static void autoJump();
static void updateSlot();
static void update_building_spots();
static bool engineerLogic();
static std::pair<CachedEntity *, float> getNearestPlayerDistance(bool vischeck = true);
using task::current_engineer_task;
using task::current_task;
// -Variables-
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?
static Timer wait_until_path{};
// Engineer version of above
static Timer wait_until_path_engineer{};
// Time before following target cloaked spy again
static std::array<Timer, PLAYER_ARRAY_SIZE> spy_cloak{};
// Don't spam spy path thanks
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
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_OTHER{ 100.0f, 200.0f, 300.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()
{
@ -65,17 +120,41 @@ static void CreateMove()
autoJump();
if (primary_only)
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 (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;
if (getHealthAndAmmo())
if (getHealthAndAmmo(metal))
return;
}
if (engineer_mode)
if (engineerLogic())
return;
if (blocking)
return;
// Spy can just walk into the enemy
if (spy_mode)
{
@ -127,6 +206,8 @@ bool init(bool first_cm)
return false;
if (!inited)
{
blacklisted_build_spots.clear();
local_buildings.clear();
sniper_spots.clear();
// Add all sniper spots to vector
for (auto &area : nav::navfile->m_areas)
@ -140,6 +221,128 @@ bool init(bool first_cm)
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()
{
// Don't path if you already have commands. But also don't error out.
@ -165,6 +368,315 @@ static bool navToSniperSpot()
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)
{
float distance = FLT_MAX;
@ -204,7 +716,7 @@ static bool isValidNearPosition(Vector vec, Vector target, const bot_class_confi
}
// 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))
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))
{
result = i;
current_task = task::stay_near;
result = i;
if (engineer)
{
current_task = { task::engineer, 4 };
current_engineer_task = task::staynear_engineer;
}
else
current_task = task::stay_near;
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());
if (nav::navTo((*it.base())->m_center, 7, true, false))
{
result = *it.base();
current_task = task::stay_near;
result = *it.base();
if (engineer)
{
current_task = { task::engineer, 4 };
current_engineer_task = task::staynear_engineer;
}
else
current_task = task::stay_near;
return true;
}
}
@ -300,6 +824,95 @@ static bool stayNearPlayers(const bot_class_config &config, CachedEntity *&resul
}
} // 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
static bool stayNear()
{
@ -445,7 +1058,7 @@ static std::vector<Vector> getDispensers()
return dispensers;
}
static bool getDispenserHealthAndAmmo()
static bool getDispenserHealthAndAmmo(int metal)
{
// Timeout for standing next to dispenser
static Timer dispenser_timeout{};
@ -453,6 +1066,12 @@ static bool getDispenserHealthAndAmmo()
static Timer dispenser_cooldown{};
float health = static_cast<float>(LOCAL_E->m_iHealth()) / LOCAL_E->m_iMaxHealth();
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
if (current_task == task::dispenser)
{
@ -510,10 +1129,16 @@ static bool getDispenserHealthAndAmmo()
return false;
}
static bool getHealthAndAmmo()
static bool getHealthAndAmmo(int metal)
{
float health = static_cast<float>(LOCAL_E->m_iHealth()) / LOCAL_E->m_iMaxHealth();
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
if (current_task == task::health || current_task == task::ammo)
{
@ -643,6 +1268,18 @@ static slots getBestSlot(slots active_slot)
else
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:
{
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)
{