Add Engineer Bot support (#1524)
* readd engiebot to navbot, WIP * Slot changes for engie * Fix some Engi Bot problems Co-authored-by: aUniqueUser <ds458@pm.me>
This commit is contained in:
parent
1359e37131
commit
b6a0b65c72
@ -17,6 +17,7 @@ enum Priority_list
|
|||||||
ammo,
|
ammo,
|
||||||
capture,
|
capture,
|
||||||
prio_melee,
|
prio_melee,
|
||||||
|
engineer,
|
||||||
health,
|
health,
|
||||||
danger,
|
danger,
|
||||||
};
|
};
|
||||||
@ -36,6 +37,7 @@ enum BlacklistReason_enum
|
|||||||
STICKY,
|
STICKY,
|
||||||
ENEMY_NORMAL,
|
ENEMY_NORMAL,
|
||||||
ENEMY_DORMANT,
|
ENEMY_DORMANT,
|
||||||
|
BAD_BUILDING_SPOT,
|
||||||
// Always last
|
// Always last
|
||||||
BLACKLIST_LENGTH
|
BLACKLIST_LENGTH
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "Aimbot.hpp"
|
#include "Aimbot.hpp"
|
||||||
#include "navparser.hpp"
|
#include "navparser.hpp"
|
||||||
#include "MiscAimbot.hpp"
|
#include "MiscAimbot.hpp"
|
||||||
|
#include "Misc.hpp"
|
||||||
|
|
||||||
namespace hacks::tf2::NavBot
|
namespace hacks::tf2::NavBot
|
||||||
{
|
{
|
||||||
@ -33,6 +34,7 @@ static settings::Int blacklist_delay("navbot.proximity-blacklist.delay", "500");
|
|||||||
static settings::Boolean blacklist_dormat("navbot.proximity-blacklist.dormant", "false");
|
static settings::Boolean blacklist_dormat("navbot.proximity-blacklist.dormant", "false");
|
||||||
static settings::Int blacklist_delay_dormat("navbot.proximity-blacklist.delay-dormant", "1000");
|
static settings::Int blacklist_delay_dormat("navbot.proximity-blacklist.delay-dormant", "1000");
|
||||||
static settings::Int blacklist_slightdanger_limit("navbot.proximity-blacklist.slight-danger.amount", "2");
|
static settings::Int blacklist_slightdanger_limit("navbot.proximity-blacklist.slight-danger.amount", "2");
|
||||||
|
static settings::Boolean engie_mode("navbot.engineer-mode", "true");
|
||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
static settings::Boolean draw_danger("navbot.draw-danger", "false");
|
static settings::Boolean draw_danger("navbot.draw-danger", "false");
|
||||||
#endif
|
#endif
|
||||||
@ -53,16 +55,20 @@ struct bot_class_config
|
|||||||
bool prefer_far;
|
bool prefer_far;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr bot_class_config CONFIG_SHORT_RANGE = { 140.0f, 400.0f, 600.0f, false };
|
constexpr bot_class_config CONFIG_SHORT_RANGE = { 140.0f, 400.0f, 600.0f, false };
|
||||||
constexpr bot_class_config CONFIG_MID_RANGE = { 200.0f, 500.0f, 3000.0f, true };
|
constexpr bot_class_config CONFIG_MID_RANGE = { 200.0f, 500.0f, 3000.0f, true };
|
||||||
constexpr bot_class_config CONFIG_LONG_RANGE = { 300.0f, 500.0f, 4000.0f, true };
|
constexpr bot_class_config CONFIG_LONG_RANGE = { 300.0f, 500.0f, 4000.0f, true };
|
||||||
bot_class_config selected_config = CONFIG_MID_RANGE;
|
constexpr bot_class_config CONFIG_ENGINEER = { 200.0f, 500.0f, 3000.0f, false };
|
||||||
|
constexpr bot_class_config CONFIG_GUNSLINGER_ENGINEER = { 50.0f, 300.0f, 2000.0f, false };
|
||||||
|
bot_class_config selected_config = CONFIG_MID_RANGE;
|
||||||
|
|
||||||
static Timer health_cooldown{};
|
static Timer health_cooldown{};
|
||||||
static Timer ammo_cooldown{};
|
static Timer ammo_cooldown{};
|
||||||
// Should we search health at all?
|
// Should we search health at all?
|
||||||
bool shouldSearchHealth(bool low_priority = false)
|
bool shouldSearchHealth(bool low_priority = false)
|
||||||
{
|
{
|
||||||
|
if (!search_health)
|
||||||
|
return false;
|
||||||
// Check if being gradually healed in any way
|
// Check if being gradually healed in any way
|
||||||
if (HasCondition<TFCond_Healing>(LOCAL_E))
|
if (HasCondition<TFCond_Healing>(LOCAL_E))
|
||||||
return false;
|
return false;
|
||||||
@ -78,13 +84,15 @@ bool shouldSearchHealth(bool low_priority = false)
|
|||||||
// Should we search ammo at all?
|
// Should we search ammo at all?
|
||||||
bool shouldSearchAmmo()
|
bool shouldSearchAmmo()
|
||||||
{
|
{
|
||||||
|
if (!search_ammo)
|
||||||
|
return false;
|
||||||
if (CE_BAD(LOCAL_W))
|
if (CE_BAD(LOCAL_W))
|
||||||
return false;
|
return false;
|
||||||
// Priority too high
|
// Priority too high
|
||||||
if (navparser::NavEngine::current_priority > ammo)
|
if (navparser::NavEngine::current_priority > ammo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int *weapon_list = (int *) ((uint64_t)(RAW_ENT(LOCAL_E)) + netvar.hMyWeapons);
|
int *weapon_list = (int *) ((uint64_t) (RAW_ENT(LOCAL_E)) + netvar.hMyWeapons);
|
||||||
if (!weapon_list)
|
if (!weapon_list)
|
||||||
return false;
|
return false;
|
||||||
if (g_pLocalPlayer->holding_sniper_rifle && CE_INT(LOCAL_E, netvar.m_iAmmo + 4) <= 5)
|
if (g_pLocalPlayer->holding_sniper_rifle && CE_INT(LOCAL_E, netvar.m_iAmmo + 4) <= 5)
|
||||||
@ -188,12 +196,13 @@ bool getHealth(bool low_priority = false)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool was_force = false;
|
||||||
// Find ammo if needed
|
// Find ammo if needed
|
||||||
bool getAmmo()
|
bool getAmmo(bool force = false)
|
||||||
{
|
{
|
||||||
if (!ammo_cooldown.check(1000))
|
if (!force && !ammo_cooldown.check(1000))
|
||||||
return navparser::NavEngine::current_priority == ammo;
|
return navparser::NavEngine::current_priority == ammo;
|
||||||
if (shouldSearchAmmo())
|
if (force || shouldSearchAmmo())
|
||||||
{
|
{
|
||||||
// Already pathing, only try to repath every 2s
|
// Already pathing, only try to repath every 2s
|
||||||
if (navparser::NavEngine::current_priority == ammo)
|
if (navparser::NavEngine::current_priority == ammo)
|
||||||
@ -202,6 +211,8 @@ bool getAmmo()
|
|||||||
if (!repath_timer.test_and_set(2000))
|
if (!repath_timer.test_and_set(2000))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
was_force = false;
|
||||||
auto ammopacks = getEntities({ ITEM_AMMO_SMALL, ITEM_AMMO_MEDIUM, ITEM_AMMO_LARGE });
|
auto ammopacks = getEntities({ ITEM_AMMO_SMALL, ITEM_AMMO_MEDIUM, ITEM_AMMO_LARGE });
|
||||||
auto dispensers = getDispensers();
|
auto dispensers = getDispensers();
|
||||||
|
|
||||||
@ -217,10 +228,13 @@ bool getAmmo()
|
|||||||
for (auto ammopack : total_ents)
|
for (auto ammopack : total_ents)
|
||||||
// If we succeeed, don't try to path to other packs
|
// If we succeeed, don't try to path to other packs
|
||||||
if (navparser::NavEngine::navTo(ammopack->m_vecOrigin(), ammo, true, ammopack->m_vecOrigin().DistToSqr(g_pLocalPlayer->v_Origin) > 200.0f * 200.0f))
|
if (navparser::NavEngine::navTo(ammopack->m_vecOrigin(), ammo, true, ammopack->m_vecOrigin().DistToSqr(g_pLocalPlayer->v_Origin) > 200.0f * 200.0f))
|
||||||
|
{
|
||||||
|
was_force = force;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
ammo_cooldown.update();
|
ammo_cooldown.update();
|
||||||
}
|
}
|
||||||
else if (navparser::NavEngine::current_priority == ammo)
|
else if (navparser::NavEngine::current_priority == ammo && !was_force)
|
||||||
navparser::NavEngine::cancelPath();
|
navparser::NavEngine::cancelPath();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -245,6 +259,199 @@ void refreshSniperSpots()
|
|||||||
sniper_spots.emplace_back(hiding_spot.m_pos);
|
sniper_spots.emplace_back(hiding_spot.m_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::pair<CachedEntity *, float> getNearestPlayerDistance()
|
||||||
|
{
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
CachedEntity *best_ent = nullptr;
|
||||||
|
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
|
||||||
|
{
|
||||||
|
CachedEntity *ent = ENTITY(i);
|
||||||
|
if (CE_VALID(ent) && ent->m_vecDormantOrigin() && ent->m_bAlivePlayer() && ent->m_bEnemy() && g_pLocalPlayer->v_Origin.DistTo(ent->m_vecOrigin()) < distance && player_tools::shouldTarget(ent) && !IsPlayerInvisible(ent))
|
||||||
|
{
|
||||||
|
distance = g_pLocalPlayer->v_Origin.DistTo(*ent->m_vecDormantOrigin());
|
||||||
|
best_ent = ent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { best_ent, distance };
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<Vector> building_spots;
|
||||||
|
|
||||||
|
inline bool HasGunslinger(CachedEntity *ent)
|
||||||
|
{
|
||||||
|
return HasWeapon(ent, 142);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isEngieMode()
|
||||||
|
{
|
||||||
|
return *engie_mode && g_pLocalPlayer->clazz == tf_engineer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlacklistedFromBuilding(CNavArea *area)
|
||||||
|
{
|
||||||
|
// FIXME: Better way of doing this ?
|
||||||
|
for (auto blacklisted_area : *navparser::NavEngine::getFreeBlacklist())
|
||||||
|
{
|
||||||
|
if (blacklisted_area.first == area && blacklisted_area.second.value == navparser::BlacklistReason_enum::BAD_BUILDING_SPOT)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Timer refresh_buildingspots_timer;
|
||||||
|
void refreshBuildingSpots(bool force = false)
|
||||||
|
{
|
||||||
|
if (!isEngieMode())
|
||||||
|
return;
|
||||||
|
if (force || refresh_buildingspots_timer.test_and_set(HasGunslinger(LOCAL_E) ? 1000 : 5000))
|
||||||
|
{
|
||||||
|
building_spots.clear();
|
||||||
|
std::optional<Vector> target;
|
||||||
|
|
||||||
|
auto our_flag = flagcontroller::getFlag(g_pLocalPlayer->team);
|
||||||
|
target = our_flag.spawn_pos;
|
||||||
|
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
auto nearest = getNearestPlayerDistance();
|
||||||
|
if (CE_GOOD(nearest.first))
|
||||||
|
target = *nearest.first->m_vecDormantOrigin();
|
||||||
|
if (!target)
|
||||||
|
target = LOCAL_E->m_vecOrigin();
|
||||||
|
}
|
||||||
|
if (target)
|
||||||
|
{
|
||||||
|
// Search all nav areas for valid spots
|
||||||
|
for (auto &area : navparser::NavEngine::getNavFile()->m_areas)
|
||||||
|
{
|
||||||
|
// Blacklisted :(
|
||||||
|
if (BlacklistedFromBuilding(&area))
|
||||||
|
continue;
|
||||||
|
// BUG Ahead, these flag checks dont seem to work for me :/
|
||||||
|
// Don't try to build in spawn lol
|
||||||
|
if ((area.m_TFattributeFlags & TF_NAV_SPAWN_ROOM_RED) != 0 || (area.m_TFattributeFlags & TF_NAV_SPAWN_ROOM_BLUE) != 0 || (area.m_TFattributeFlags & TF_NAV_SPAWN_ROOM_EXIT) != 0)
|
||||||
|
continue;
|
||||||
|
if ((area.m_TFattributeFlags & TF_NAV_SENTRY_SPOT) != 0)
|
||||||
|
building_spots.emplace_back(area.m_center);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto &hiding_spot : area.m_hidingSpots)
|
||||||
|
if (hiding_spot.HasGoodCover())
|
||||||
|
building_spots.emplace_back(hiding_spot.m_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort by distance to nearest, lower is better
|
||||||
|
// TODO: This isnt really optimal, need a dif way to where it is a good distance from enemies but also bots dont build in the same spot
|
||||||
|
std::sort(building_spots.begin(), building_spots.end(),
|
||||||
|
[target](Vector a, Vector b)
|
||||||
|
{
|
||||||
|
if (!HasGunslinger(LOCAL_E))
|
||||||
|
{
|
||||||
|
auto a_dist = a.DistTo(*target);
|
||||||
|
auto b_dist = b.DistTo(*target);
|
||||||
|
|
||||||
|
// Penalty for being in danger ranges
|
||||||
|
if (a_dist + 100.0f < selected_config.min_full_danger)
|
||||||
|
a_dist += 4000.0f;
|
||||||
|
if (b_dist + 100.0f < selected_config.min_full_danger)
|
||||||
|
b_dist += 4000.0f;
|
||||||
|
|
||||||
|
if (a_dist + 1000.0f < selected_config.min_slight_danger)
|
||||||
|
a_dist += 1500.0f;
|
||||||
|
if (b_dist + 1000.0f < selected_config.min_slight_danger)
|
||||||
|
b_dist += 1500.0f;
|
||||||
|
|
||||||
|
return a_dist < b_dist;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return a.DistTo(*target) < b.DistTo(*target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CachedEntity *mySentry = nullptr;
|
||||||
|
static CachedEntity *myDispenser = nullptr;
|
||||||
|
|
||||||
|
void refreshLocalBuildings()
|
||||||
|
{
|
||||||
|
if (isEngieMode())
|
||||||
|
{
|
||||||
|
mySentry = nullptr;
|
||||||
|
myDispenser = nullptr;
|
||||||
|
if (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;
|
||||||
|
auto cid = ent->m_iClassID();
|
||||||
|
if (cid != CL_CLASS(CObjectSentrygun) && cid != CL_CLASS(CObjectDispenser))
|
||||||
|
continue;
|
||||||
|
if (HandleToIDX(CE_INT(ent, netvar.m_hBuilder)) != LOCAL_E->m_IDX)
|
||||||
|
continue;
|
||||||
|
if (CE_INT(ent, netvar.m_bPlacing))
|
||||||
|
continue;
|
||||||
|
if (cid == CL_CLASS(CObjectSentrygun))
|
||||||
|
mySentry = ent;
|
||||||
|
else if (cid == CL_CLASS(CObjectDispenser))
|
||||||
|
myDispenser = ent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector current_building_spot;
|
||||||
|
static bool navToSentrySpot()
|
||||||
|
{
|
||||||
|
static Timer wait_until_path_sentry;
|
||||||
|
// Wait a bit before pathing again
|
||||||
|
if (!wait_until_path_sentry.test_and_set(300))
|
||||||
|
return false;
|
||||||
|
// Try to nav to our existing sentry spot
|
||||||
|
if (CE_GOOD(mySentry) && mySentry->m_bAlivePlayer() && mySentry->m_vecDormantOrigin())
|
||||||
|
{
|
||||||
|
// Don't overwrite current nav
|
||||||
|
if (navparser::NavEngine::current_priority == engineer)
|
||||||
|
return true;
|
||||||
|
if (navparser::NavEngine::navTo(*mySentry->m_vecDormantOrigin(), engineer))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mySentry = nullptr;
|
||||||
|
|
||||||
|
// No building spots
|
||||||
|
if (building_spots.empty())
|
||||||
|
return false;
|
||||||
|
// Don't overwrite current nav
|
||||||
|
if (navparser::NavEngine::current_priority == engineer)
|
||||||
|
return false;
|
||||||
|
// Max 10 attempts
|
||||||
|
for (int attempts = 0; attempts < 10 && attempts < building_spots.size(); ++attempts)
|
||||||
|
{
|
||||||
|
// Get a semi-random building spot to still keep distance preferrance
|
||||||
|
auto random_offset = RandomInt(0, std::min(3, (int) building_spots.size()));
|
||||||
|
|
||||||
|
Vector random;
|
||||||
|
|
||||||
|
// Wrap around
|
||||||
|
if (attempts - random_offset < 0)
|
||||||
|
random = building_spots[building_spots.size() + (attempts - random_offset)];
|
||||||
|
else
|
||||||
|
random = building_spots[attempts - random_offset];
|
||||||
|
|
||||||
|
// Try to nav there
|
||||||
|
if (navparser::NavEngine::navTo(random, engineer))
|
||||||
|
{
|
||||||
|
current_building_spot = random;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
enum slots
|
enum slots
|
||||||
{
|
{
|
||||||
primary = 1,
|
primary = 1,
|
||||||
@ -754,6 +961,146 @@ bool snipeSentries()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum building
|
||||||
|
{
|
||||||
|
dispenser = 0,
|
||||||
|
sentry = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
static int build_attempts = 0;
|
||||||
|
static bool buildBuilding(int building)
|
||||||
|
{
|
||||||
|
// Blacklist this spot and refresh the building spots
|
||||||
|
if (build_attempts >= 15)
|
||||||
|
{
|
||||||
|
(*navparser::NavEngine::getFreeBlacklist())[navparser::NavEngine::findClosestNavSquare(g_pLocalPlayer->v_Origin)] = navparser::BlacklistReason_enum::BAD_BUILDING_SPOT;
|
||||||
|
refreshBuildingSpots(true);
|
||||||
|
current_building_spot.Invalidate();
|
||||||
|
build_attempts = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Make sure we have right amount of ammo
|
||||||
|
int required = (HasGunslinger(LOCAL_E) || building == dispenser) ? 100 : 130;
|
||||||
|
if (CE_INT(LOCAL_E, netvar.m_iAmmo + 12) < required)
|
||||||
|
return getAmmo(true);
|
||||||
|
|
||||||
|
// Try to build! we are close enough
|
||||||
|
if (current_building_spot.IsValid() && current_building_spot.DistTo(g_pLocalPlayer->v_Origin) <= (building == dispenser ? 500.0f : 200.0f))
|
||||||
|
{
|
||||||
|
// TODO: Rotate our angle to a valid building spot ? also rotate building itself to face enemies ?
|
||||||
|
current_user_cmd->viewangles.x = 20.0f;
|
||||||
|
current_user_cmd->viewangles.y += 2.0f;
|
||||||
|
|
||||||
|
// Gives us 4 1/2 seconds to build
|
||||||
|
static Timer attempt_timer;
|
||||||
|
if (attempt_timer.test_and_set(300))
|
||||||
|
build_attempts++;
|
||||||
|
|
||||||
|
if (hacks::shared::misc::getCarriedBuilding() == -1)
|
||||||
|
{
|
||||||
|
static Timer command_timer;
|
||||||
|
if (command_timer.test_and_set(100))
|
||||||
|
g_IEngine->ClientCmd_Unrestricted(strfmt("build %d", building).get());
|
||||||
|
}
|
||||||
|
else if (CE_INT(ENTITY(hacks::shared::misc::getCarriedBuilding()), netvar.m_bCanPlace))
|
||||||
|
current_user_cmd->buttons |= IN_ATTACK;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return navToSentrySpot();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool buildingNeedsToBeSmacked(CachedEntity *ent)
|
||||||
|
{
|
||||||
|
if (CE_BAD(ent))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (CE_INT(ent, netvar.iUpgradeLevel) != 3 || ent->m_iHealth() / ent->m_iMaxHealth() <= 0.80f)
|
||||||
|
return true;
|
||||||
|
if (ent->m_iClassID() == CL_CLASS(CObjectSentrygun))
|
||||||
|
{
|
||||||
|
int max_ammo = 0;
|
||||||
|
switch (CE_INT(ent, netvar.iUpgradeLevel))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
max_ammo = 150;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
max_ammo = 200;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CE_INT(ent, netvar.m_iAmmoShells) / max_ammo <= 0.50f;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool smackBuilding(CachedEntity *ent)
|
||||||
|
{
|
||||||
|
if (CE_BAD(ent))
|
||||||
|
return false;
|
||||||
|
if (!CE_INT(LOCAL_E, netvar.m_iAmmo + 12))
|
||||||
|
return getAmmo(true);
|
||||||
|
|
||||||
|
if (ent->m_flDistance() <= 100.0f && g_pLocalPlayer->weapon_mode == weapon_melee)
|
||||||
|
{
|
||||||
|
AimAt(g_pLocalPlayer->v_Eye, GetBuildingPosition(ent), current_user_cmd);
|
||||||
|
current_user_cmd->buttons |= IN_ATTACK;
|
||||||
|
}
|
||||||
|
else if (navparser::NavEngine::current_priority != engineer)
|
||||||
|
return navparser::NavEngine::navTo(*ent->m_vecDormantOrigin(), engineer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool runEngineerLogic()
|
||||||
|
{
|
||||||
|
if (!isEngieMode())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Already have a sentry
|
||||||
|
if (CE_VALID(mySentry) && mySentry->m_bAlivePlayer())
|
||||||
|
{
|
||||||
|
if (HasGunslinger(LOCAL_E))
|
||||||
|
{
|
||||||
|
// Too far away, destroy it
|
||||||
|
// BUG Ahead, building isnt destroyed lol
|
||||||
|
if (mySentry->m_flDistance() >= 1800.0f)
|
||||||
|
{
|
||||||
|
// If we have a valid building
|
||||||
|
if (mySentry->m_Type() == CL_CLASS(CObjectSentrygun))
|
||||||
|
g_IEngine->ClientCmd_Unrestricted("destroy 2");
|
||||||
|
}
|
||||||
|
// Return false so we run another task
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try to smack sentry first
|
||||||
|
if (buildingNeedsToBeSmacked(mySentry))
|
||||||
|
return smackBuilding(mySentry);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We put dispenser by sentry
|
||||||
|
if (CE_BAD(myDispenser))
|
||||||
|
return buildBuilding(dispenser);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We already have a dispenser, see if it needs to be smacked
|
||||||
|
if (buildingNeedsToBeSmacked(myDispenser))
|
||||||
|
return smackBuilding(myDispenser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// Try to build a sentry
|
||||||
|
return buildBuilding(sentry);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
enum capture_type
|
enum capture_type
|
||||||
{
|
{
|
||||||
no_capture,
|
no_capture,
|
||||||
@ -928,7 +1275,7 @@ bool doRoam()
|
|||||||
navparser::NavEngine::cancelPath();
|
navparser::NavEngine::cancelPath();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (navparser::NavEngine::navTo(*target, patrol))
|
if (navparser::NavEngine::navTo(*target, patrol, true, navparser::NavEngine::current_priority != patrol))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -970,9 +1317,12 @@ bool escapeDanger()
|
|||||||
auto *local_nav = navparser::NavEngine::findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
auto *local_nav = navparser::NavEngine::findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
||||||
auto blacklist = navparser::NavEngine::getFreeBlacklist();
|
auto blacklist = navparser::NavEngine::getFreeBlacklist();
|
||||||
|
|
||||||
// In danger, try to run
|
// In danger, try to run (besides if it's a building spot, don't run away from that)
|
||||||
if (blacklist->find(local_nav) != blacklist->end())
|
if (blacklist->find(local_nav) != blacklist->end())
|
||||||
{
|
{
|
||||||
|
if ((*blacklist)[local_nav].value == navparser::BlacklistReason_enum::BAD_BUILDING_SPOT)
|
||||||
|
return false;
|
||||||
|
|
||||||
static CNavArea *target_area = nullptr;
|
static CNavArea *target_area = nullptr;
|
||||||
// Already running and our target is still valid
|
// Already running and our target is still valid
|
||||||
if (navparser::NavEngine::current_priority == danger && blacklist->find(target_area) == blacklist->end())
|
if (navparser::NavEngine::current_priority == danger && blacklist->find(target_area) == blacklist->end())
|
||||||
@ -1010,22 +1360,6 @@ bool escapeDanger()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<CachedEntity *, float> getNearestPlayerDistance()
|
|
||||||
{
|
|
||||||
float distance = FLT_MAX;
|
|
||||||
CachedEntity *best_ent = nullptr;
|
|
||||||
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
|
|
||||||
{
|
|
||||||
CachedEntity *ent = ENTITY(i);
|
|
||||||
if (CE_VALID(ent) && ent->m_vecDormantOrigin() && ent->m_bAlivePlayer() && ent->m_bEnemy() && g_pLocalPlayer->v_Origin.DistTo(ent->m_vecOrigin()) < distance && player_tools::shouldTarget(ent) && !IsPlayerInvisible(ent))
|
|
||||||
{
|
|
||||||
distance = g_pLocalPlayer->v_Origin.DistTo(*ent->m_vecDormantOrigin());
|
|
||||||
best_ent = ent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { best_ent, distance };
|
|
||||||
}
|
|
||||||
|
|
||||||
static int slot = primary;
|
static int slot = primary;
|
||||||
|
|
||||||
static void autoJump(std::pair<CachedEntity *, float> &nearest)
|
static void autoJump(std::pair<CachedEntity *, float> &nearest)
|
||||||
@ -1091,6 +1425,20 @@ static slots getBestSlot(slots active_slot, std::pair<CachedEntity *, float> &ne
|
|||||||
else
|
else
|
||||||
return primary;
|
return primary;
|
||||||
}
|
}
|
||||||
|
case tf_engineer:
|
||||||
|
{
|
||||||
|
if (((CE_GOOD(mySentry) && mySentry->m_flDistance() <= 300) || (CE_GOOD(myDispenser) && myDispenser->m_flDistance() <= 500)) || (current_building_spot.IsValid() && current_building_spot.DistTo(g_pLocalPlayer->v_Origin) <= 500.0f))
|
||||||
|
{
|
||||||
|
if (active_slot >= melee && navparser::NavEngine::current_priority != prio_melee)
|
||||||
|
return active_slot;
|
||||||
|
else
|
||||||
|
return melee;
|
||||||
|
}
|
||||||
|
else if (nearest.second <= 500)
|
||||||
|
return primary;
|
||||||
|
else
|
||||||
|
return secondary;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if (nearest.second <= 400)
|
if (nearest.second <= 400)
|
||||||
@ -1129,6 +1477,8 @@ void CreateMove()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
refreshSniperSpots();
|
refreshSniperSpots();
|
||||||
|
refreshLocalBuildings();
|
||||||
|
refreshBuildingSpots();
|
||||||
|
|
||||||
if (danger_config_custom)
|
if (danger_config_custom)
|
||||||
{
|
{
|
||||||
@ -1143,6 +1493,9 @@ void CreateMove()
|
|||||||
case tf_heavy:
|
case tf_heavy:
|
||||||
selected_config = CONFIG_SHORT_RANGE;
|
selected_config = CONFIG_SHORT_RANGE;
|
||||||
break;
|
break;
|
||||||
|
case tf_engineer:
|
||||||
|
selected_config = isEngieMode() ? HasGunslinger(LOCAL_E) ? CONFIG_GUNSLINGER_ENGINEER : CONFIG_ENGINEER : CONFIG_SHORT_RANGE;
|
||||||
|
break;
|
||||||
case tf_sniper:
|
case tf_sniper:
|
||||||
selected_config = g_pLocalPlayer->weapon()->m_iClassID() == CL_CLASS(CTFCompoundBow) ? CONFIG_MID_RANGE : CONFIG_LONG_RANGE;
|
selected_config = g_pLocalPlayer->weapon()->m_iClassID() == CL_CLASS(CTFCompoundBow) ? CONFIG_MID_RANGE : CONFIG_LONG_RANGE;
|
||||||
break;
|
break;
|
||||||
@ -1166,6 +1519,9 @@ void CreateMove()
|
|||||||
// If we aren't getting health, get ammo
|
// If we aren't getting health, get ammo
|
||||||
else if (getAmmo())
|
else if (getAmmo())
|
||||||
return;
|
return;
|
||||||
|
// Try to run engineer logic
|
||||||
|
else if (runEngineerLogic())
|
||||||
|
return;
|
||||||
else if (meleeAttack(slot, nearest))
|
else if (meleeAttack(slot, nearest))
|
||||||
return;
|
return;
|
||||||
// Try to capture objectives
|
// Try to capture objectives
|
||||||
@ -1225,14 +1581,16 @@ void Draw()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static InitRoutine init([]() {
|
static InitRoutine init(
|
||||||
EC::Register(EC::CreateMove, CreateMove, "navbot_cm");
|
[]()
|
||||||
EC::Register(EC::CreateMoveWarp, CreateMove, "navbot_cm");
|
{
|
||||||
EC::Register(EC::LevelInit, LevelInit, "navbot_levelinit");
|
EC::Register(EC::CreateMove, CreateMove, "navbot_cm");
|
||||||
|
EC::Register(EC::CreateMoveWarp, CreateMove, "navbot_cm");
|
||||||
|
EC::Register(EC::LevelInit, LevelInit, "navbot_levelinit");
|
||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
EC::Register(EC::Draw, Draw, "navbot_draw");
|
EC::Register(EC::Draw, Draw, "navbot_draw");
|
||||||
#endif
|
#endif
|
||||||
LevelInit();
|
LevelInit();
|
||||||
});
|
});
|
||||||
|
|
||||||
} // namespace hacks::tf2::NavBot
|
} // namespace hacks::tf2::NavBot
|
||||||
|
@ -520,6 +520,8 @@ bool navTo(const Vector &destination, int priority, bool should_repath, bool nav
|
|||||||
// Don't path, priority is too low
|
// Don't path, priority is too low
|
||||||
if (priority < current_priority)
|
if (priority < current_priority)
|
||||||
return false;
|
return false;
|
||||||
|
if (log_pathing)
|
||||||
|
logging::Info("Priority: %d", priority);
|
||||||
|
|
||||||
CNavArea *start_area = map->findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
CNavArea *start_area = map->findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
||||||
CNavArea *dest_area = map->findClosestNavSquare(destination);
|
CNavArea *dest_area = map->findClosestNavSquare(destination);
|
||||||
@ -630,6 +632,21 @@ static void followCrumbs()
|
|||||||
if (!(entry <= 0.01f && entry >= -0.01f))
|
if (!(entry <= 0.01f && entry >= -0.01f))
|
||||||
reset_z = false;
|
reset_z = false;
|
||||||
}
|
}
|
||||||
|
if (reset_z)
|
||||||
|
{
|
||||||
|
reset_z = false;
|
||||||
|
|
||||||
|
Ray_t ray;
|
||||||
|
trace_t trace;
|
||||||
|
Vector end = g_pLocalPlayer->v_Origin;
|
||||||
|
end.z -= 100.0f;
|
||||||
|
|
||||||
|
ray.Init(g_pLocalPlayer->v_Origin, end, RAW_ENT(LOCAL_E)->GetCollideable()->OBBMins(), RAW_ENT(LOCAL_E)->GetCollideable()->OBBMaxs());
|
||||||
|
g_ITrace->TraceRay(ray, MASK_PLAYERSOLID, &trace::filter_default, &trace);
|
||||||
|
// Only reset if we are standing on a building
|
||||||
|
if (trace.DidHit() && trace.m_pEnt && ENTITY(((IClientEntity *) trace.m_pEnt)->entindex())->m_Type() == ENTITY_BUILDING)
|
||||||
|
reset_z = true;
|
||||||
|
}
|
||||||
|
|
||||||
Vector current_vec = crumbs[0].vec;
|
Vector current_vec = crumbs[0].vec;
|
||||||
if (reset_z)
|
if (reset_z)
|
||||||
@ -1006,64 +1023,74 @@ static CatCommand nav_path("nav_path", "Debug nav path", []() { NavEngine::navTo
|
|||||||
|
|
||||||
static CatCommand nav_path_noreapth("nav_path_norepath", "Debug nav path", []() { NavEngine::navTo(loc, 20, false, true, false); });
|
static CatCommand nav_path_noreapth("nav_path_norepath", "Debug nav path", []() { NavEngine::navTo(loc, 20, false, true, false); });
|
||||||
|
|
||||||
static CatCommand nav_init("nav_init", "Reload nav mesh", []() {
|
static CatCommand nav_init("nav_init", "Reload nav mesh",
|
||||||
NavEngine::map.reset();
|
[]()
|
||||||
NavEngine::LevelInit();
|
{
|
||||||
});
|
NavEngine::map.reset();
|
||||||
|
NavEngine::LevelInit();
|
||||||
|
});
|
||||||
|
|
||||||
static CatCommand nav_debug_check("nav_debug_check", "Perform nav checks between two areas. First area: cat_nav_set Second area: Your location while running this command.", []() {
|
static CatCommand nav_debug_check("nav_debug_check", "Perform nav checks between two areas. First area: cat_nav_set Second area: Your location while running this command.",
|
||||||
if (!NavEngine::isReady())
|
[]()
|
||||||
return;
|
{
|
||||||
auto next = NavEngine::map->findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
if (!NavEngine::isReady())
|
||||||
auto current = NavEngine::map->findClosestNavSquare(loc);
|
return;
|
||||||
|
auto next = NavEngine::map->findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
||||||
|
auto current = NavEngine::map->findClosestNavSquare(loc);
|
||||||
|
|
||||||
auto points = determinePoints(current, next);
|
auto points = determinePoints(current, next);
|
||||||
|
|
||||||
points.center = handleDropdown(points.center, points.next);
|
points.center = handleDropdown(points.center, points.next);
|
||||||
|
|
||||||
// Too high for us to jump!
|
// Too high for us to jump!
|
||||||
if (points.center_next.z - points.center.z > PLAYER_JUMP_HEIGHT)
|
if (points.center_next.z - points.center.z > PLAYER_JUMP_HEIGHT)
|
||||||
|
{
|
||||||
|
return logging::Info("Nav: Area too high!");
|
||||||
|
}
|
||||||
|
|
||||||
|
points.current.z += PLAYER_JUMP_HEIGHT;
|
||||||
|
points.center.z += PLAYER_JUMP_HEIGHT;
|
||||||
|
points.next.z += PLAYER_JUMP_HEIGHT;
|
||||||
|
|
||||||
|
if (IsPlayerPassableNavigation(points.current, points.center) && IsPlayerPassableNavigation(points.center, points.next))
|
||||||
|
{
|
||||||
|
logging::Info("Nav: Area is player passable!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logging::Info("Nav: Area is NOT player passable! %.2f,%.2f,%.2f %.2f,%.2f,%.2f %.2f,%.2f,%.2f", points.current.x, points.current.y, points.current.z, points.center.x, points.center.y, points.center.z, points.next.x, points.next.y, points.next.z);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
static CatCommand nav_debug_blacklist("nav_debug_blacklist", "Blacklist connection between two areas for 30s. First area: cat_nav_set Second area: Your location while running this command.",
|
||||||
|
[]()
|
||||||
|
{
|
||||||
|
if (!NavEngine::isReady())
|
||||||
|
return;
|
||||||
|
auto next = NavEngine::map->findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
||||||
|
auto current = NavEngine::map->findClosestNavSquare(loc);
|
||||||
|
|
||||||
|
std::pair<CNavArea *, CNavArea *> key(current, next);
|
||||||
|
NavEngine::map->vischeck_cache[key].expire_tick = TICKCOUNT_TIMESTAMP(30);
|
||||||
|
NavEngine::map->vischeck_cache[key].vischeck_state = false;
|
||||||
|
NavEngine::map->pather.Reset();
|
||||||
|
logging::Info("Nav: Connection %d->%d Blacklisted.", current->m_id, next->m_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
static InitRoutine init(
|
||||||
|
[]()
|
||||||
{
|
{
|
||||||
return logging::Info("Nav: Area too high!");
|
EC::Register(EC::CreateMove_NoEnginePred, NavEngine::CreateMove, "navengine_cm");
|
||||||
}
|
EC::Register(EC::LevelInit, NavEngine::LevelInit, "navengine_levelinit");
|
||||||
|
|
||||||
points.current.z += PLAYER_JUMP_HEIGHT;
|
|
||||||
points.center.z += PLAYER_JUMP_HEIGHT;
|
|
||||||
points.next.z += PLAYER_JUMP_HEIGHT;
|
|
||||||
|
|
||||||
if (IsPlayerPassableNavigation(points.current, points.center) && IsPlayerPassableNavigation(points.center, points.next))
|
|
||||||
{
|
|
||||||
logging::Info("Nav: Area is player passable!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logging::Info("Nav: Area is NOT player passable! %.2f,%.2f,%.2f %.2f,%.2f,%.2f %.2f,%.2f,%.2f", points.current.x, points.current.y, points.current.z, points.center.x, points.center.y, points.center.z, points.next.x, points.next.y, points.next.z);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
static CatCommand nav_debug_blacklist("nav_debug_blacklist", "Blacklist connection between two areas for 30s. First area: cat_nav_set Second area: Your location while running this command.", []() {
|
|
||||||
if (!NavEngine::isReady())
|
|
||||||
return;
|
|
||||||
auto next = NavEngine::map->findClosestNavSquare(g_pLocalPlayer->v_Origin);
|
|
||||||
auto current = NavEngine::map->findClosestNavSquare(loc);
|
|
||||||
|
|
||||||
std::pair<CNavArea *, CNavArea *> key(current, next);
|
|
||||||
NavEngine::map->vischeck_cache[key].expire_tick = TICKCOUNT_TIMESTAMP(30);
|
|
||||||
NavEngine::map->vischeck_cache[key].vischeck_state = false;
|
|
||||||
NavEngine::map->pather.Reset();
|
|
||||||
logging::Info("Nav: Connection %d->%d Blacklisted.", current->m_id, next->m_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
static InitRoutine init([]() {
|
|
||||||
EC::Register(EC::CreateMove_NoEnginePred, NavEngine::CreateMove, "navengine_cm");
|
|
||||||
EC::Register(EC::LevelInit, NavEngine::LevelInit, "navengine_levelinit");
|
|
||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
EC::Register(EC::Draw, NavEngine::Draw, "navengine_draw");
|
EC::Register(EC::Draw, NavEngine::Draw, "navengine_draw");
|
||||||
#endif
|
#endif
|
||||||
enabled.installChangeCallback([](settings::VariableBase<bool> &, bool after) {
|
enabled.installChangeCallback(
|
||||||
if (after && g_IEngine->IsInGame())
|
[](settings::VariableBase<bool> &, bool after)
|
||||||
NavEngine::LevelInit();
|
{
|
||||||
|
if (after && g_IEngine->IsInGame())
|
||||||
|
NavEngine::LevelInit();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
} // namespace navparser
|
} // namespace navparser
|
||||||
|
@ -52,7 +52,7 @@ bool trace::FilterDefault::ShouldHitEntity(IHandleEntity *handle, int mask)
|
|||||||
if (m_pSelf)
|
if (m_pSelf)
|
||||||
{
|
{
|
||||||
// If what we hit is an enemy it does not matter
|
// If what we hit is an enemy it does not matter
|
||||||
if (entity && CE_VALID(ENTITY(entity->entindex())) && ENTITY(entity->entindex())->m_iTeam() == ENTITY(m_pSelf->entindex())->m_iTeam())
|
if (entity && entity->entindex() != 0 && CE_VALID(ENTITY(entity->entindex())) && ENTITY(entity->entindex())->m_iTeam() == ENTITY(m_pSelf->entindex())->m_iTeam())
|
||||||
{
|
{
|
||||||
auto ent = ENTITY(m_pSelf->entindex());
|
auto ent = ENTITY(m_pSelf->entindex());
|
||||||
if (CE_GOOD(ent) && ent->m_bAlivePlayer())
|
if (CE_GOOD(ent) && ent->m_bAlivePlayer())
|
||||||
|
Reference in New Issue
Block a user