diff --git a/data/menu/nullifiedcat/catbot.xml b/data/menu/nullifiedcat/catbot.xml
index 7ff8d279..d5bff376 100755
--- a/data/menu/nullifiedcat/catbot.xml
+++ b/data/menu/nullifiedcat/catbot.xml
@@ -39,7 +39,7 @@
-
+
@@ -54,13 +54,27 @@
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/include/hacks/NavBot.hpp b/include/hacks/NavBot.hpp
index 4b12c2c4..ce4ade47 100644
--- a/include/hacks/NavBot.hpp
+++ b/include/hacks/NavBot.hpp
@@ -8,5 +8,6 @@
namespace hacks::tf2::NavBot
{
void Init();
+void initonce();
void CreateMove();
-}
\ No newline at end of file
+} // namespace hacks::tf2::NavBot
\ No newline at end of file
diff --git a/src/hacks/Aimbot.cpp b/src/hacks/Aimbot.cpp
index 88358f94..f71e99c1 100644
--- a/src/hacks/Aimbot.cpp
+++ b/src/hacks/Aimbot.cpp
@@ -159,14 +159,15 @@ void CreateMove()
// If zoomed only is on, check if zoomed
if (zoomed_only && g_pLocalPlayer->holding_sniper_rifle)
{
- if (!g_pLocalPlayer->bZoomed && !(current_user_cmd->buttons & IN_ATTACK))
+ if (!g_pLocalPlayer->bZoomed &&
+ !(current_user_cmd->buttons & IN_ATTACK))
return;
}
// Minigun spun up handler
if (g_pLocalPlayer->weapon()->m_iClassID() == CL_CLASS(CTFMinigun))
{
int weapon_state =
- CE_INT(g_pLocalPlayer->weapon(), netvar.iWeaponState);
+ CE_INT(g_pLocalPlayer->weapon(), netvar.iWeaponState);
// If user setting for autospin isnt true, then we check if minigun
// is already zoomed
if ((weapon_state == MinigunState_t::AC_STATE_IDLE ||
diff --git a/src/hacks/AutoReflect.cpp b/src/hacks/AutoReflect.cpp
index c202079b..ff941ee3 100644
--- a/src/hacks/AutoReflect.cpp
+++ b/src/hacks/AutoReflect.cpp
@@ -38,7 +38,8 @@ void CreateMove()
return;
// Check if player is using a flame thrower
- if (g_pLocalPlayer->weapon()->m_iClassID() != CL_CLASS(CTFFlameThrower) && CE_INT(LOCAL_W, netvar.iItemDefinitionIndex) != 528)
+ if (g_pLocalPlayer->weapon()->m_iClassID() != CL_CLASS(CTFFlameThrower) &&
+ CE_INT(LOCAL_W, netvar.iItemDefinitionIndex) != 528)
return;
// Check for phlogistinator, which is item 594
diff --git a/src/hacks/Backtrack.cpp b/src/hacks/Backtrack.cpp
index 71e68a20..5d735d0f 100644
--- a/src/hacks/Backtrack.cpp
+++ b/src/hacks/Backtrack.cpp
@@ -259,16 +259,22 @@ bool ValidTick(BacktrackData &i, CachedEntity *ent)
return true;
if (istickinvalid[ent->m_IDX][i.index])
return false;
- if (hacks::shared::aimbot::IsBacktracking()) {
- if (IsVectorVisible(g_pLocalPlayer->v_Eye, i.hitboxes[head].center, true))
- if (fabsf(NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) * 1000.0f -
- getLatency() - i.simtime * 1000.0f) <= 200.0f) {
+ if (hacks::shared::aimbot::IsBacktracking())
+ {
+ if (IsVectorVisible(g_pLocalPlayer->v_Eye, i.hitboxes[head].center,
+ true))
+ if (fabsf(NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) *
+ 1000.0f -
+ getLatency() - i.simtime * 1000.0f) <= 200.0f)
+ {
istickvalid[ent->m_IDX][i.index] = true;
return true;
}
}
- else if (fabsf(NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) * 1000.0f -
- getLatency() - i.simtime * 1000.0f) <= 200.0f) {
+ else if (fabsf(NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) *
+ 1000.0f -
+ getLatency() - i.simtime * 1000.0f) <= 200.0f)
+ {
istickvalid[ent->m_IDX][i.index] = true;
return true;
}
diff --git a/src/hacks/FollowBot.cpp b/src/hacks/FollowBot.cpp
index f711ed84..7c621b9a 100644
--- a/src/hacks/FollowBot.cpp
+++ b/src/hacks/FollowBot.cpp
@@ -142,8 +142,8 @@ int ClassPriority(CachedEntity *ent)
{
switch (g_pPlayerResource->GetClass(ent))
{
- if (g_pPlayerResource->GetClass(ent) == tf_spy)
- return 0;
+ case tf_spy:
+ return 0;
case tf_engineer:
return 1;
case tf_medic:
@@ -223,11 +223,11 @@ void WorldTick()
Vector indirectOrigin =
VischeckCorner(LOCAL_E, entity, *follow_activation / 2,
true); // get the corner location that the
- // future target is visible from
+ // future target is visible from
std::pair corners;
if (!indirectOrigin.z &&
entity->m_IDX == lastent) // if we couldn't find it, run
- // wallcheck instead
+ // wallcheck instead
{
corners = VischeckWall(LOCAL_E, entity,
float(follow_activation) / 2, true);
@@ -314,13 +314,13 @@ void WorldTick()
Vector indirectOrigin =
VischeckCorner(LOCAL_E, entity, 250,
true); // get the corner location that the
- // future target is visible from
+ // future target is visible from
std::pair corners;
corners.first.z = 0;
corners.second.z = 0;
if (!indirectOrigin.z &&
entity->m_IDX == lastent) // if we couldn't find it, run
- // wallcheck instead
+ // wallcheck instead
{
corners = VischeckWall(LOCAL_E, entity, 250, true);
if (!corners.first.z || !corners.second.z)
@@ -580,4 +580,4 @@ static CatCommand
}
});
#endif
-} // namespace hacks::shared::followbot
+} // namespace hacks::shared::followbot
\ No newline at end of file
diff --git a/src/hacks/NavBot.cpp b/src/hacks/NavBot.cpp
index b10173cd..a4fb4d1d 100644
--- a/src/hacks/NavBot.cpp
+++ b/src/hacks/NavBot.cpp
@@ -2,7 +2,10 @@
// Created by bencat07 on 17.08.18.
//
#include "common.hpp"
+#include
+#include
#include "navparser.hpp"
+#include "FollowBot.hpp"
#include "NavBot.hpp"
namespace hacks::tf2::NavBot
@@ -12,6 +15,45 @@ static settings::Bool spy_mode("navbot.spy-mode", "false");
static settings::Bool heavy_mode("navbot.heavy-mode", "false");
static settings::Bool primary_only("navbot.primary-only", "true");
+static settings::Bool enable_fb{ "navbot.medbot", "false" };
+static settings::Bool roambot{ "navbot.roaming", "true" };
+static settings::Float follow_activation{ "navbot.max-range", "1000" };
+static settings::Bool mimic_slot{ "navbot.mimic-slot", "false" };
+static settings::Bool always_medigun{ "navbot.always-medigun", "false" };
+static settings::Bool sync_taunt{ "navbot.taunt-sync", "false" };
+static settings::Bool change_tar{ "navbot.change-roaming-target", "false" };
+static settings::Bool autojump{ "navbot.jump-if-stuck", "true" };
+static settings::Bool afk{ "navbot.switch-afk", "true" };
+static settings::Int afktime{ "navbot.afk-time", "15000" };
+
+unsigned steamid = 0x0;
+CatCommand follow_steam("navbot_steam", "Follow Steam Id",
+ [](const CCommand &args) {
+ if (args.ArgC() < 1)
+ {
+ steamid = 0x0;
+ return;
+ }
+ steamid = atol(args.Arg(1));
+ });
+Timer lastTaunt{}; // time since taunt was last executed, used to avoid kicks
+Timer lastJump{};
+std::array afkTicks; // for how many ms the player hasn't been moving
+
+void checkAFK()
+{
+ for (int i = 0; i < g_GlobalVars->maxClients; i++)
+ {
+ auto entity = ENTITY(i);
+ if (CE_BAD(entity))
+ continue;
+ if (!CE_VECTOR(entity, netvar.vVelocity).IsZero(60.0f))
+ {
+ afkTicks[i].update();
+ }
+ }
+}
+
bool HasLowAmmo()
{
int *weapon_list =
@@ -115,6 +157,12 @@ void Init()
hide.IsExposed())
sniper_spots.push_back(hide.m_pos);
}
+void initonce()
+{
+ for (int i = 0; i < afkTicks.size(); i++)
+ afkTicks[i].update();
+ return;
+}
Timer slot_timer{};
void UpdateSlot()
@@ -140,13 +188,14 @@ void UpdateSlot()
Timer cdr{};
Timer cd2{};
Timer cd3{};
+int follow_target = 0;
void CreateMove()
{
- if (!enable || !nav::Prepare())
+ if ((!enable && !enable_fb) || !nav::Prepare())
return;
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
return;
- if (primary_only)
+ if (primary_only && enable)
UpdateSlot();
if (HasLowHealth() && cdr.test_and_set(5000))
{
@@ -164,47 +213,287 @@ void CreateMove()
nav::NavTo(ammo->m_vecOrigin(), true, true, 6);
}
}
- if (!nav::ReadyForCommands && !spy_mode && !heavy_mode)
- cd3.update();
- bool isready = (spy_mode || heavy_mode) ? 1 : nav::ReadyForCommands;
- int waittime = (spy_mode || heavy_mode) ? 100 : 5000;
- if (isready && cd3.test_and_set(waittime))
+ if (enable)
{
- if (!spy_mode && !heavy_mode)
- {
+ if (!nav::ReadyForCommands && !spy_mode && !heavy_mode)
cd3.update();
- Vector random_spot;
- if (sniper_spots.empty())
- {
- if (cd2.test_and_set(5000))
- Init();
- return;
- }
- int rng = rand() % sniper_spots.size();
- random_spot = sniper_spots.at(rng);
- if (random_spot.z)
- nav::NavTo(random_spot, true, true);
- }
- else if (cdr.check(5000))
+ bool isready = (spy_mode || heavy_mode) ? 1 : nav::ReadyForCommands;
+ int waittime = (spy_mode || heavy_mode) ? 100 : 5000;
+ if (isready && cd3.test_and_set(waittime))
{
- CachedEntity *tar = NearestEnemy();
- if (CE_BAD(tar))
+ if (!spy_mode && !heavy_mode)
{
+ cd3.update();
Vector random_spot;
- if (sniper_spots.empty())
- {
- if (cd2.test_and_set(5000))
- Init();
+ if (cd2.test_and_set(20000))
+ Init();
+ if (!sniper_spots.size())
return;
- }
int rng = rand() % sniper_spots.size();
random_spot = sniper_spots.at(rng);
if (random_spot.z)
- nav::NavTo(random_spot, false);
+ nav::NavTo(random_spot, true, true);
+ }
+ else if (cdr.check(5000))
+ {
+ CachedEntity *tar = NearestEnemy();
+ if (CE_BAD(tar))
+ {
+ Vector random_spot;
+ if (cd2.test_and_set(20000))
+ Init();
+ if (!sniper_spots.size())
+ return;
+ int rng = rand() % sniper_spots.size();
+ random_spot = sniper_spots.at(rng);
+ if (random_spot.z)
+ nav::NavTo(random_spot, false);
+ return;
+ }
+ nav::NavTo(tar->m_vecOrigin(), false);
+ }
+ }
+ }
+ else if (enable_fb)
+ {
+ // We need a local player to control
+ if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
+ {
+ follow_target = 0;
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true, false, 4);
+ return;
+ }
+
+ if (afk)
+ checkAFK();
+
+ // Still good check
+ if (follow_target)
+ if (CE_BAD(ENTITY(follow_target)))
+ follow_target = 0;
+
+ if (!follow_target)
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true,
+ false); // no target == no path
+ // Target Selection
+ if (steamid)
+ {
+ // Find a target with the steam id, as it is prioritized
+ auto ent_count = HIGHEST_ENTITY;
+ for (int i = 0; i < ent_count; i++)
+ {
+ auto entity = ENTITY(i);
+ if (CE_BAD(entity)) // Exist + dormant
+ continue;
+ if (i == follow_target)
+ break;
+ if (entity->m_Type() != ENTITY_PLAYER)
+ continue;
+ if (steamid != entity->player_info.friendsID) // steamid check
+ continue;
+
+ if (!entity->m_bAlivePlayer()) // Dont follow dead players
+ continue;
+ follow_target = entity->m_IDX;
+ break;
+ }
+ }
+ // If we dont have a follow target from that, we look again for someone
+ // else who is suitable
+ if ((!follow_target || change_tar ||
+ (hacks::shared::followbot::ClassPriority(ENTITY(follow_target)) <
+ 6 &&
+ ENTITY(follow_target)->player_info.friendsID != steamid)) &&
+ roambot)
+ {
+ // Try to get a new target
+ auto ent_count = g_IEngine->GetMaxClients();
+ for (int i = 0; i < ent_count; i++)
+ {
+ auto entity = ENTITY(i);
+ if (CE_BAD(entity)) // Exist + dormant
+ continue;
+ if (entity->m_Type() != ENTITY_PLAYER)
+ continue;
+ if (entity == LOCAL_E) // Follow self lol
+ continue;
+ if (entity->m_bEnemy())
+ continue;
+ if (afk &&
+ afkTicks[i].check(int(afktime))) // don't follow target that
+ // was determined afk
+ continue;
+ if (IsPlayerDisguised(entity) || IsPlayerInvisible(entity))
+ continue;
+ if (!entity->m_bAlivePlayer()) // Dont follow dead players
+ continue;
+ if (follow_activation &&
+ entity->m_flDistance() > (float) follow_activation)
+ continue;
+ const model_t *model =
+ ENTITY(follow_target)->InternalEntity()->GetModel();
+ // FIXME follow cart/point
+ /*if (followcart && model &&
+ (lagexploit::pointarr[0] || lagexploit::pointarr[1] ||
+ lagexploit::pointarr[2] || lagexploit::pointarr[3] ||
+ lagexploit::pointarr[4]) &&
+ (model == lagexploit::pointarr[0] ||
+ model == lagexploit::pointarr[1] ||
+ model == lagexploit::pointarr[2] ||
+ model == lagexploit::pointarr[3] ||
+ model == lagexploit::pointarr[4]))
+ follow_target = entity->m_IDX;*/
+ if (entity->m_Type() != ENTITY_PLAYER)
+ continue;
+ // favor closer entitys
+ if (follow_target &&
+ ENTITY(follow_target)->m_flDistance() <
+ entity->m_flDistance()) // favor closer entitys
+ continue;
+ // check if new target has a higher priority than current target
+ if (hacks::shared::followbot::ClassPriority(
+ ENTITY(follow_target)) >=
+ hacks::shared::followbot::ClassPriority(ENTITY(i)))
+ continue;
+ // ooooo, a target
+ follow_target = i;
+ afkTicks[i].update(); // set afk time to 0
+ }
+ }
+ // last check for entity before we continue
+ if (!follow_target)
+ {
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true, false, 4);
+ return;
+ }
+
+ CachedEntity *followtar = ENTITY(follow_target);
+ // wtf is this needed
+ if (CE_BAD(followtar) || !followtar->m_bAlivePlayer())
+ {
+ follow_target = 0;
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true, false, 4);
+ return;
+ }
+ // Check if we are following a disguised/spy
+ if (IsPlayerDisguised(followtar) || IsPlayerInvisible(followtar))
+ {
+ follow_target = 0;
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true, false, 4);
+ return;
+ }
+ // check if target is afk
+ if (afk)
+ {
+ if (afkTicks[follow_target].check(int(afktime)))
+ {
+ follow_target = 0;
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true, false, 4);
return;
}
- nav::NavTo(tar->m_vecOrigin(), false);
}
+
+ // Update timer on new target
+ static Timer idle_time{};
+ if (nav::ReadyForCommands)
+ idle_time.update();
+
+ // If the player is close enough, we dont need to follow the path
+ auto tar_orig = followtar->m_vecOrigin();
+ auto loc_orig = LOCAL_E->m_vecOrigin();
+ auto dist_to_target = loc_orig.DistTo(tar_orig);
+ if (!CE_VECTOR(followtar, netvar.vVelocity).IsZero(20.0f))
+ idle_time.update();
+
+ // Tauntsync
+ if (sync_taunt && HasCondition(followtar) &&
+ lastTaunt.test_and_set(1000))
+ g_IEngine->ClientCmd("taunt");
+
+ // Check for jump
+ if (autojump && lastJump.check(1000) && idle_time.check(2000))
+ {
+ current_user_cmd->buttons |= IN_JUMP;
+ lastJump.update();
+ }
+ // Check if still moving. 70 HU = Sniper Zoomed Speed
+ if (idle_time.check(3000) &&
+ CE_VECTOR(g_pLocalPlayer->entity, netvar.vVelocity).IsZero(60.0f))
+ {
+ follow_target = 0;
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true, false, 4);
+ return;
+ }
+ // Basic idle check
+ if (idle_time.test_and_set(5000))
+ {
+ follow_target = 0;
+ nav::NavTo(LOCAL_E->m_vecOrigin(), true, false, 4);
+ return;
+ }
+
+ static float last_slot_check = 0.0f;
+ if (g_GlobalVars->curtime < last_slot_check)
+ last_slot_check = 0.0f;
+ if (follow_target && (always_medigun || mimic_slot) &&
+ (g_GlobalVars->curtime - last_slot_check > 1.0f) &&
+ !g_pLocalPlayer->life_state &&
+ !CE_BYTE(ENTITY(follow_target), netvar.iLifeState))
+ {
+
+ // We are checking our slot so reset the timer
+ last_slot_check = g_GlobalVars->curtime;
+
+ // Get the follow targets active weapon
+ int owner_weapon_eid =
+ (CE_INT(ENTITY(follow_target), netvar.hActiveWeapon) & 0xFFF);
+ IClientEntity *owner_weapon =
+ g_IEntityList->GetClientEntity(owner_weapon_eid);
+
+ // If both the follow targets and the local players weapons arnt
+ // null or
+ // dormant
+ if (owner_weapon && CE_GOOD(g_pLocalPlayer->weapon()))
+ {
+
+ // IsBaseCombatWeapon()
+ if (re::C_BaseCombatWeapon::IsBaseCombatWeapon(
+ RAW_ENT(g_pLocalPlayer->weapon())) &&
+ re::C_BaseCombatWeapon::IsBaseCombatWeapon(owner_weapon))
+ {
+
+ // Get the players slot numbers and store in some vars
+ int my_slot = re::C_BaseCombatWeapon::GetSlot(
+ RAW_ENT(g_pLocalPlayer->weapon()));
+ int owner_slot =
+ re::C_BaseCombatWeapon::GetSlot(owner_weapon);
+
+ // If the local player is a medic and user settings allow,
+ // then
+ // keep the medigun out
+ if (g_pLocalPlayer->clazz == tf_medic && always_medigun)
+ {
+ if (my_slot != 1)
+ {
+ g_IEngine->ExecuteClientCmd("slot2");
+ }
+
+ // Else we attemt to keep our weapon mimiced with our
+ // follow
+ // target
+ }
+ else
+ {
+ if (my_slot != owner_slot)
+ {
+ g_IEngine->ExecuteClientCmd(
+ format("slot", owner_slot + 1).c_str());
+ }
+ }
+ }
+ }
+ }
+ nav::NavTo(tar_orig, false, true, 5);
}
}
} // namespace hacks::tf2::NavBot
diff --git a/src/hooks/CreateMove.cpp b/src/hooks/CreateMove.cpp
index 2bbffea3..0456379d 100644
--- a/src/hooks/CreateMove.cpp
+++ b/src/hooks/CreateMove.cpp
@@ -128,6 +128,7 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time,
{
DelayTimer.update();
hacks::tf2::NavBot::Init();
+ hacks::tf2::NavBot::initonce();
firstcm = false;
}
tickcount++;
diff --git a/src/hooks/DispatchUserMessage.cpp b/src/hooks/DispatchUserMessage.cpp
index f16750e2..0e8e66db 100644
--- a/src/hooks/DispatchUserMessage.cpp
+++ b/src/hooks/DispatchUserMessage.cpp
@@ -182,7 +182,7 @@ DEFINE_HOOKED_METHOD(DispatchUserMessage, bool, void *this_, int type,
retrun = true;
lastfilter = format(filter);
lastname = format(name);
- gitgud.update();
+ gitgud.update();
}
}
}
@@ -210,7 +210,7 @@ DEFINE_HOOKED_METHOD(DispatchUserMessage, bool, void *this_, int type,
retrun = true;
lastfilter = format(filter);
lastname = format(name);
- gitgud.update();
+ gitgud.update();
}
}
}
diff --git a/src/hooks/SendDatagram.cpp b/src/hooks/SendDatagram.cpp
index 71154e6c..e70bf1af 100644
--- a/src/hooks/SendDatagram.cpp
+++ b/src/hooks/SendDatagram.cpp
@@ -13,23 +13,25 @@ DEFINE_HOOKED_METHOD(SendDatagram, int, INetChannel *ch, bf_write *buf)
#if !LAGBOT_MODE
int in;
int state;
- if (CE_GOOD(LOCAL_E)) {
- in = ch->m_nInSequenceNr;
+ if (CE_GOOD(LOCAL_E))
+ {
+ in = ch->m_nInSequenceNr;
state = ch->m_nInReliableState;
float latencysend =
- round((round((hacks::shared::backtrack::getLatency() - 0.5f) /
- 15.1515151515f) -
- 0.5f) *
- 15.1515151515f);
+ round((round((hacks::shared::backtrack::getLatency() - 0.5f) /
+ 15.1515151515f) -
+ 0.5f) *
+ 15.1515151515f);
hacks::shared::backtrack::AddLatencyToNetchan(ch, latencysend);
}
#endif
int ret = original::SendDatagram(ch, buf);
#if !LAGBOT_MODE
- if (CE_GOOD(LOCAL_E)) {
- ch->m_nInSequenceNr = in;
+ if (CE_GOOD(LOCAL_E))
+ {
+ ch->m_nInSequenceNr = in;
ch->m_nInReliableState = state;
}