Merge pull request #895 from nullworks/lightcat

Engineer Bot stuff and simple-ipc updates
This commit is contained in:
LightCat 2020-02-02 21:56:19 +01:00 committed by GitHub
commit 23c46143f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1316 additions and 118 deletions

2
config_data/teamspam.txt Normal file
View File

@ -0,0 +1,2 @@
CAT
HOOK

View File

@ -2,6 +2,7 @@
<TabContainer width="fill" height="fill">
<Include path="nullifiedcat/misc/anticheat.xml"/>
<Include path="nullifiedcat/misc/autoequip.xml"/>
<Include path="nullifiedcat/misc/buildings.xml"/>
<Include path="nullifiedcat/misc/announcer.xml"/>
<Include path="nullifiedcat/misc/spyalert.xml"/>
<Include path="nullifiedcat/misc/collective.xml"/>

View File

@ -0,0 +1,26 @@
<Tab name="Buildings" padding="4 4 4 4">
<Box padding="12 6 6 6" width="content" height="content" name="Buildings">.
<List width="200">
<AutoVariable width="fill" target="autorepair.enabled" label="Enable Auto Repair"/>
<AutoVariable width="fill" target="autorepair.silent" label="Silent"/>
<AutoVariable width="fill" target="autorepair.sentry" label="Repair Sentry"/>
<AutoVariable width="fill" target="autorepair.dispenser" label="Repair Dispenser"/>
<AutoVariable width="fill" target="autorepair.teleport" label="Repair Teleporter"/>
<LabeledObject width="fill" label="Priority">
<Select target="autorepair.priority">
<Option name="None" value="0"/>
<Option name="Sentry" value="1"/>
<Option name="Dispenser" value="2"/>
<Option name="Teleport" value="3"/>
</Select>
</LabeledObject>
<AutoVariable width="fill" target="autoupgrade.enabled" label="Enable Auto Upgrade"/>
<AutoVariable width="fill" target="autoupgrade.sentry" label="Upgrade Sentry"/>
<AutoVariable width="fill" target="autoupgrade.sentry.level" label="Upgrade Sentry Level" min="1" max="3"/>
<AutoVariable width="fill" target="autoupgrade.dispenser" label="Upgrade Dispenser"/>
<AutoVariable width="fill" target="autoupgrade.dispenser.level" label="Upgrade Dispenser Level" min="1" max="3"/>
<AutoVariable width="fill" target="autoupgrade.teleport" label="Upgrade Teleporter"/>
<AutoVariable width="fill" target="autoupgrade.teleport.level" label="Upgrade Teleporter Level" min="1" max="3"/>
</List>
</Box>
</Tab>

View File

@ -33,7 +33,7 @@
<AutoVariable width="fill" target="misc.pathing.draw" label="Draw Path"/>
<AutoVariable width="fill" target="misc.pathing.look-at-path" label="Look at Path"/>
<AutoVariable width="fill" target="navbot.enabled" label="Enable Navbot"/>
<!--AutoVariable width="fill" target="navbot.scout-mode" label="Enable Scout Mode"/-->
<AutoVariable width="fill" target="navbot.engineer-mode" label="Enable Engineer mode">
<AutoVariable width="fill" target="navbot.get-health-and-ammo" label="Get Health and Ammo"/>
<AutoVariable width="fill" target="navbot.spy-mode" label="Enable Spy Mode"/>
<AutoVariable width="fill" target="navbot.other-mode" label="General Mode"/>

View File

@ -32,6 +32,8 @@ extern settings::Boolean clean_chat;
extern settings::Boolean clean_screenshots;
extern settings::Boolean crypt_chat;
extern settings::Boolean nolerp;
extern float backup_lerp;
extern settings::Boolean disable_visuals;
extern settings::Int print_r;
extern settings::Int print_g;
@ -41,6 +43,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

@ -61,6 +61,7 @@ extern CHud *g_CHUD;
extern ISteamClient *g_ISteamClient;
extern ISteamFriends *g_ISteamFriends;
extern IVEngineClient013 *g_IEngine;
extern void *demoplayer;
extern IEngineSound *g_ISoundEngine;
extern vgui::ISurface *g_ISurface;
extern vgui::IPanel *g_IPanel;

2
include/core/logging.hpp Executable file → Normal file
View File

@ -18,4 +18,4 @@ void Initialize();
void Shutdown();
void Info(const char *fmt, ...);
void File(const char *fmt, ...);
}
} // namespace logging

View File

@ -45,6 +45,15 @@ public:
offset_t iTeamNum;
offset_t iFlags;
offset_t iHealth;
// sentry
offset_t m_iAmmoShells; // sentry shells
offset_t m_iAmmoRockets; // use only with if (GetLevel() == 3)
offset_t m_iSentryState; // sentry state
// dispenser
offset_t m_iAmmoMetal; // dispenser metal reserve
offset_t iLifeState;
offset_t iCond;
offset_t iCond1;
@ -54,7 +63,11 @@ public:
offset_t vViewOffset;
offset_t hActiveWeapon;
offset_t flChargedDamage;
offset_t iUpgradeLevel;
// any building
offset_t m_iUpgradeMetal; // upgrade metal on any building
offset_t m_flPercentageConstructed; // use only with if (IsBuilding())
offset_t iUpgradeLevel; // any building
offset_t m_hBuilder;
offset_t m_bCanPlace;
offset_t m_iObjectType;
@ -62,12 +75,15 @@ public:
offset_t m_bHasSapper;
offset_t m_bPlacing;
offset_t m_bBuilding;
offset_t m_iTeleState;
// teleporter
offset_t m_iTeleState; // teleport state [1 = idle, 2 = active, 3 = teleporting, 4 = charging]
offset_t m_flTeleRechargeTime;
offset_t m_flTeleCurrentRechargeDuration;
offset_t m_iTeleTimesUsed;
offset_t m_flTeleYawToExit;
offset_t m_bMatchBuilding;
offset_t iPipeType;
offset_t iBuildingHealth;
offset_t iBuildingMaxHealth;

View File

@ -176,6 +176,10 @@ struct offsets
{
return PlatformOffset(47, undefined, 47);
}
static constexpr uint32_t IsPlayingTimeDemo()
{
return PlatformOffset(8, undefined, 8);
}
static constexpr uint32_t RegisterFileWhitelist()
{
return PlatformOffset(94, undefined, undefined);

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

@ -36,6 +36,11 @@ constexpr float RADPI = 57.295779513082f;
#include <core/sdk.hpp>
#define TICK_INTERVAL (g_GlobalVars->interval_per_tick)
#define TIME_TO_TICKS(dt) ((int) (0.5f + (float) (dt) / TICK_INTERVAL))
#define TICKS_TO_TIME(t) (TICK_INTERVAL * (t))
#define ROUND_TO_TICKS(t) (TICK_INTERVAL * TIME_TO_TICKS(t))
// typedef void ( *FnCommandCallback_t )( const CCommand &command );
template <typename Iter, typename RandomGenerator> Iter select_randomly(Iter start, Iter end, RandomGenerator &g)

View File

@ -59,6 +59,7 @@ extern VMTHook client;
extern VMTHook inventory;
extern VMTHook chathud;
extern VMTHook engine;
extern VMTHook demoplayer;
extern VMTHook netchannel;
extern VMTHook firebullets;
extern VMTHook clientdll;

View File

@ -58,6 +58,7 @@ DECLARE_HOOKED_METHOD(Paint, void, IEngineVGui *, PaintMode_t);
DECLARE_HOOKED_METHOD(FireEvent, bool, IGameEventManager2 *, IGameEvent *, bool);
DECLARE_HOOKED_METHOD(FireEventClientSide, bool, IGameEventManager2 *, IGameEvent *);
// g_IEngine
DECLARE_HOOKED_METHOD(IsPlayingTimeDemo, bool, void *);
DECLARE_HOOKED_METHOD(ServerCmdKeyValues, void, IVEngineClient013 *, KeyValues *);
#if ENABLE_VISUALS || ENABLE_TEXTMODE
// vgui::IPanel

0
include/localplayer.hpp Executable file → Normal file
View File

View File

@ -24,6 +24,12 @@ settings::Boolean clean_chat{ "chat.clean", "false" };
settings::Boolean crypt_chat{ "chat.crypto", "true" };
settings::Boolean clean_screenshots{ "visual.clean-screenshots", "false" };
#if ENABLE_TEXTMODE
settings::Boolean nolerp{ "misc.no-lerp", "true" };
#else
settings::Boolean nolerp{ "misc.no-lerp", "false" };
#endif
float backup_lerp = 0.0f;
static settings::Boolean no_zoom{ "remove.scope", "false" };
settings::Boolean disable_visuals{ "visual.disable", "false" };
settings::Int print_r{ "print.rgb.r", "183" };
@ -35,7 +41,7 @@ void color_callback(settings::VariableBase<int> &, int)
{
menu_color = Color(*print_r, *print_g, *print_b, 255);
}
static InitRoutine color_init([]() {
static InitRoutine misc_init([]() {
print_r.installChangeCallback(color_callback);
print_g.installChangeCallback(color_callback);
print_b.installChangeCallback(color_callback);
@ -43,4 +49,36 @@ static InitRoutine color_init([]() {
static BytePatch patch(gSignatures.GetClientSignature, "81 EC ? ? ? ? A1 ? ? ? ? 8B 7D 08 8B 10 89 04 24 FF 92", 0x0, { 0x5B, 0x5E, 0x5F, 0x5D, 0xC3 });
after ? patch.Patch() : patch.Shutdown();
});
nolerp.installChangeCallback([](settings::VariableBase<bool> &, bool after) {
if (!after)
{
if (backup_lerp)
{
cl_interp->SetValue(backup_lerp);
backup_lerp = 0.0f;
}
}
else
{
backup_lerp = cl_interp->GetFloat();
// We should adjust cl_interp to be as low as possible
if (cl_interp->GetFloat() > 0.152f)
cl_interp->SetValue(0.152f);
}
});
EC::Register(
EC::Shutdown,
[]() {
if (backup_lerp)
{
cl_interp->SetValue(backup_lerp);
backup_lerp = 0.0f;
}
},
"misctemp_shutdown");
#if ENABLE_TEXTMODE
// Ensure that we trigger the callback for textmode builds
nolerp = false;
nolerp = true;
#endif
});

View File

@ -97,7 +97,8 @@ std::optional<colors::rgba_t> forceEspColor(CachedEntity *entity)
void onKilledBy(unsigned id)
{
if (!shouldTargetSteamId(id))
auto &pl = playerlist::AccessData(id);
if (!shouldTargetSteamId(id) && !playerlist::IsFriendly(pl.state))
{
// We ignored the gamer, but they still shot us
if (betrayal_list.find(id) == betrayal_list.end())
@ -110,4 +111,32 @@ void onKilledBy(CachedEntity *entity)
{
onKilledBy(entity->player_info.friendsID);
}
class PlayerToolsEventListener : public IGameEventListener2
{
void FireGameEvent(IGameEvent *event) override
{
int killer_id = g_IEngine->GetPlayerForUserID(event->GetInt("attacker"));
int victim_id = g_IEngine->GetPlayerForUserID(event->GetInt("userid"));
if (victim_id == g_IEngine->GetLocalPlayer())
{
onKilledBy(ENTITY(killer_id));
return;
}
}
};
PlayerToolsEventListener &listener()
{
static PlayerToolsEventListener object{};
return object;
}
static InitRoutine register_event([]() {
g_IEventManager2->AddListener(&listener(), "player_death", false);
EC::Register(
EC::Shutdown, []() { g_IEventManager2->RemoveListener(&listener()); }, "playerlist_shutdown");
});
} // namespace player_tools

View File

@ -22,6 +22,7 @@ IVModelRender *g_IVModelRender = nullptr;
ISteamClient *g_ISteamClient = nullptr;
ISteamFriends *g_ISteamFriends = nullptr;
IVEngineClient013 *g_IEngine = nullptr;
void *demoplayer = nullptr;
IEngineSound *g_ISoundEngine = nullptr;
vgui::ISurface *g_ISurface = nullptr;
vgui::IPanel *g_IPanel = nullptr;
@ -93,6 +94,7 @@ void CreateInterfaces()
{
g_ICvar = BruteforceInterface<ICvar>("VEngineCvar", sharedobj::vstdlib());
g_IEngine = BruteforceInterface<IVEngineClient013>("VEngineClient", sharedobj::engine());
demoplayer = **(unsigned ***) (gSignatures.GetEngineSignature("89 15 ? ? ? ? BA ? ? ? ? 83 38 01") + 2);
g_ISoundEngine = BruteforceInterface<IEngineSound>("IEngineSoundClient", sharedobj::engine());
g_AppID = g_IEngine->GetAppID();
g_IEntityList = BruteforceInterface<IClientEntityList>("VClientEntityList", sharedobj::client());

View File

@ -30,6 +30,7 @@ void NetVars::Init()
this->m_Collision = gNetvars.get_offset("DT_BaseEntity", "m_Collision");
this->m_flSimulationTime = gNetvars.get_offset("DT_BaseEntity", "m_flSimulationTime");
this->m_angRotation = gNetvars.get_offset("DT_BaseEntity", "m_angRotation");
IF_GAME(IsTF2())
{
res_iTeam = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_iTeam");
@ -76,6 +77,20 @@ void NetVars::Init()
this->iCond3 = gNetvars.get_offset("DT_TFPlayer", "m_Shared", "m_nPlayerCondEx3");
this->iClass = gNetvars.get_offset("DT_TFPlayer", "m_PlayerClass", "m_iClass");
this->flChargedDamage = gNetvars.get_offset("DT_TFSniperRifle", "SniperRifleLocalData", "m_flChargedDamage");
// sentry
this->m_iAmmoShells = gNetvars.get_offset("DT_ObjectSentrygun", "m_iAmmoShells");
this->m_iAmmoRockets = gNetvars.get_offset("DT_ObjectSentrygun", "m_iAmmoRockets");
this->m_iSentryState = gNetvars.get_offset("DT_ObjectSentrygun", "m_iState");
// dispenser
this->m_iAmmoMetal = gNetvars.get_offset("DT_ObjectDispenser", "m_iAmmoMetal");
// any building
this->m_iUpgradeMetal = gNetvars.get_offset("DT_BaseObject", "m_iUpgradeMetal");
this->m_flPercentageConstructed = gNetvars.get_offset("DT_BaseObject", "m_flPercentageConstructed");
// any building
this->iUpgradeLevel = gNetvars.get_offset("DT_BaseObject", "m_iUpgradeLevel");
this->m_hBuilder = gNetvars.get_offset("DT_BaseObject", "m_hBuilder");
this->m_bCanPlace = gNetvars.get_offset("DT_BaseObject", "m_bServerOverridePlacement");
@ -84,14 +99,16 @@ void NetVars::Init()
this->m_bHasSapper = gNetvars.get_offset("DT_BaseObject", "m_bHasSapper");
this->m_bPlacing = gNetvars.get_offset("DT_BaseObject", "m_bPlacing");
this->m_bMiniBuilding = gNetvars.get_offset("DT_BaseObject", "m_bMiniBuilding");
// teleporter
this->m_iTeleState = gNetvars.get_offset("DT_ObjectTeleporter", "m_iState");
this->m_flTeleRechargeTime = gNetvars.get_offset("DT_ObjectTeleporter", "m_flRechargeTime");
this->m_flTeleCurrentRechargeDuration = gNetvars.get_offset("DT_ObjectTeleporter", "m_flCurrentRechargeDuration");
this->m_iTeleTimesUsed = gNetvars.get_offset("DT_ObjectTeleporter", "m_iTimesUsed");
this->m_flTeleYawToExit = gNetvars.get_offset("DT_ObjectTeleporter", "m_flYawToExit");
this->m_bMatchBuilding = gNetvars.get_offset("DT_ObjectTeleporter", "m_bMatchBuilding");
this->m_DmgRadius = gNetvars.get_offset("DT_BaseGrenade", "m_DmgRadius");
this->m_DmgRadius = gNetvars.get_offset("DT_BaseGrenade", "m_DmgRadius");
this->iPipeType = gNetvars.get_offset("DT_TFProjectile_Pipebomb", "m_iType");
this->iBuildingHealth = gNetvars.get_offset("DT_BaseObject", "m_iHealth");
this->iBuildingMaxHealth = gNetvars.get_offset("DT_BaseObject", "m_iMaxHealth");

View File

@ -255,6 +255,10 @@ void hack::Hook()
hooks::engine.HookMethod(HOOK_ARGS(ServerCmdKeyValues));
hooks::engine.Apply();
hooks::demoplayer.Set(demoplayer);
hooks::demoplayer.HookMethod(HOOK_ARGS(IsPlayingTimeDemo));
hooks::demoplayer.Apply();
hooks::eventmanager2.Set(g_IEventManager2);
hooks::eventmanager2.HookMethod(HOOK_ARGS(FireEvent));
hooks::eventmanager2.HookMethod(HOOK_ARGS(FireEventClientSide));

View File

@ -826,6 +826,9 @@ void Aim(CachedEntity *entity)
if (silent && !slow_aim)
g_pLocalPlayer->bUseSilentAngles = true;
// Set tick count to target's (backtrack messes with this)
if (!bt::isBacktrackEnabled && nolerp && entity->m_IDX <= g_IEngine->GetMaxClients())
current_user_cmd->tick_count = TIME_TO_TICKS(CE_FLOAT(entity, netvar.m_flSimulationTime));
// Finish function
return;
}

View File

@ -375,7 +375,8 @@ void ProcessUserCmd(CUserCmd *cmd)
static int ticksUntilSwap = 0;
static bool swap = true;
if (ticksUntilSwap > 0 && *yaw_mode != 18) {
if (ticksUntilSwap > 0 && *yaw_mode != 18)
{
swap = true;
ticksUntilSwap = 0;
}
@ -492,7 +493,8 @@ void ProcessUserCmd(CUserCmd *cmd)
}
break;
case 18: // Fake sideways
if (g_pLocalPlayer->isFakeAngleCM && ticksUntilSwap--) {
if (g_pLocalPlayer->isFakeAngleCM && ticksUntilSwap--)
{
ticksUntilSwap = UniformRandomInt(*yaw_sideways_min, *yaw_sideways_max);
swap = !swap;
}

View File

@ -23,7 +23,6 @@ static settings::Boolean auto_disguise{ "misc.autodisguise", "true" };
static settings::Int abandon_if_ipc_bots_gte{ "cat-bot.abandon-if.ipc-bots-gte", "0" };
static settings::Int abandon_if_humans_lte{ "cat-bot.abandon-if.humans-lte", "0" };
static settings::Int abandon_if_players_lte{ "cat-bot.abandon-if.players-lte", "0" };
static settings::Int mark_human_threshold{ "cat-bot.mark-human-after-kills", "2" };
static settings::Boolean micspam{ "cat-bot.micspam.enable", "false" };
static settings::Int micspam_on{ "cat-bot.micspam.interval-on", "3" };
@ -74,6 +73,7 @@ int globerr(const char *path, int eerrno)
// let glob() keep going
return 0;
}
bool hasEnding(std::string const &fullString, std::string const &ending)
{
if (fullString.length() >= ending.length())
@ -191,24 +191,6 @@ void on_killed_by(int userid)
}
}
}
if (!catbotmode)
return;
CachedEntity *player = ENTITY(g_IEngine->GetPlayerForUserID(userid));
if (CE_BAD(player))
return;
unsigned steamID = player->player_info.friendsID;
if (playerlist::AccessData(steamID).state != playerlist::k_EState::CAT)
return;
// if (human_detecting_map[steamID].has_bot_name)
human_detecting_map[steamID].treacherous_kills++;
logging::Info("Treacherous kill #%d: %s [U:1:%u]", human_detecting_map[steamID].treacherous_kills, player->player_info.name, player->player_info.friendsID);
if (human_detecting_map[steamID].treacherous_kills >= *mark_human_threshold)
playerlist::ChangeState(steamID, playerlist::k_EState::RAGE, true);
}
void do_random_votekick()
@ -531,7 +513,7 @@ CatBotEventListener &listener()
class CatBotEventListener2 : public IGameEventListener2
{
void FireGameEvent(IGameEvent *event) override
void FireGameEvent(IGameEvent *) override
{
// vote for current map if catbot mode and autovote is on
if (catbotmode && autovote_map)

View File

@ -39,6 +39,9 @@ static settings::Boolean god_mode{ "misc.god-mode", "false" };
static settings::Boolean debug_info{ "misc.debug-info", "false" };
static settings::Boolean no_homo{ "misc.no-homo", "true" };
static settings::Boolean show_spectators{ "misc.show-spectators", "false" };
static settings::Boolean misc_drawhitboxes{ "misc.draw-hitboxes", "false" };
// Useful for debugging with showlagcompensation
static settings::Boolean misc_drawhitboxes_dead{ "misc.draw-hitboxes.dead-players", "false" };
#endif
#if !ENFORCE_STREAM_SAFETY && ENABLE_VISUALS
@ -109,7 +112,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,15 +121,57 @@ 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;
}
return -1;
}
#if ENABLE_VISUALS
struct wireframe_data
{
Vector raw_min;
Vector raw_max;
Vector rotation;
Vector origin;
};
std::vector<wireframe_data> wireframe_queue;
void QueueWireframeHitboxes(hitbox_cache::EntityHitboxCache &hb_cache)
{
for (int i = 0; i < hb_cache.GetNumHitboxes(); i++)
{
auto hb = hb_cache.GetHitbox(i);
Vector raw_min = hb->bbox->bbmin;
Vector raw_max = hb->bbox->bbmax;
auto transform = hb_cache.GetBones()[hb->bbox->bone];
Vector rotation;
Vector origin;
MatrixAngles(transform, *(QAngle *) &rotation, origin);
wireframe_queue.push_back(wireframe_data{ raw_min, raw_max, rotation, origin });
}
}
void DrawWireframeHitbox(wireframe_data data)
{
g_IVDebugOverlay->AddBoxOverlay2(data.origin, data.raw_min, data.raw_max, VectorToQAngle(data.rotation), Color(0, 0, 0, 0), Color(255, 0, 0, 255), g_GlobalVars->interval_per_tick * 2);
}
#endif
void CreateMove()
{
#if ENABLE_VISUALS
if (misc_drawhitboxes)
{
for (int i = 0; i <= g_IEngine->GetMaxClients(); i++)
{
auto ent = ENTITY(i);
if (CE_INVALID(ent) || ent == LOCAL_E || (!misc_drawhitboxes_dead && !ent->m_bAlivePlayer()))
continue;
QueueWireframeHitboxes(ent->hitboxes);
}
}
#endif
if (current_user_cmd->command_number)
last_number = current_user_cmd->command_number;
// AntiAfk That after a certian time without movement keys depressed, causes
@ -222,8 +267,14 @@ void CreateMove()
#if ENABLE_VISUALS
// Timer ussr{};
void DrawText()
void Draw()
{
if (misc_drawhitboxes)
{
for (auto &entry : wireframe_queue)
DrawWireframeHitbox(entry);
wireframe_queue.clear();
}
/*if (ussr.test_and_set(207000))
{
g_ISurface->PlaySound()
@ -773,7 +824,7 @@ static InitRoutine init([]() {
EC::Register(EC::Shutdown, Shutdown, "draw_local_player", EC::average);
EC::Register(EC::CreateMove, CreateMove, "cm_misc_hacks", EC::average);
#if ENABLE_VISUALS
EC::Register(EC::Draw, DrawText, "draw_misc_hacks", EC::average);
EC::Register(EC::Draw, Draw, "draw_misc_hacks", EC::average);
#if !ENFORCE_STREAM_SAFETY
if (render_zoomed)
tryPatchLocalPlayerShouldDraw(true);

View File

@ -18,6 +18,7 @@ static settings::Int sandwichaim_aimkey_mode{ "sandwichaim.aimkey-mode", "0" };
float sandwich_speed = 350.0f;
float grav = 0.25f;
int prevent = -1;
std::pair<CachedEntity *, Vector> FindBestEnt(bool teammate, bool Predict, bool zcheck)
{
CachedEntity *bestent = nullptr;
@ -190,9 +191,11 @@ static void SandwichAim()
g_pLocalPlayer->bUseSilentAngles = true;
}
}
static bool charge_aimbotted = false;
static settings::Boolean charge_aim{ "chargeaim.enable", "false" };
static settings::Button charge_key{ "chargeaim.key", "<null>" };
static void ChargeAimbot()
{
charge_aimbotted = false;
@ -269,12 +272,243 @@ static void SapperAimbot()
}
}
}
// auto repair
static settings::Int autorepair_priority{ "autorepair.priority", "0" };
static settings::Boolean autorepair_enabled("autorepair.enabled", "false");
static settings::Boolean autorepair_silent("autorepair.silent", "true");
static settings::Boolean autorepair_repair_sentry("autorepair.sentry", "false");
static settings::Boolean autorepair_repair_dispenser("autorepair.dispenser", "false");
static settings::Boolean autorepair_repair_teleport("autorepair.teleport", "false");
// auto upgrade
static settings::Boolean autoupgrade_enabled("autoupgrade.enabled", "false");
static settings::Boolean autoupgrade_sentry("autoupgrade.sentry", "false");
static settings::Boolean autoupgrade_dispenser("autoupgrade.dispenser", "false");
static settings::Boolean autoupgrade_teleport("autoupgrade.teleport", "false");
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");
bool ShouldHitBuilding(CachedEntity *ent)
{
if (!autoupgrade_enabled && !autorepair_enabled)
return false;
// Current Metal
int cur_ammo = CE_INT(LOCAL_E, netvar.m_iAmmo + 12);
// Autorepair is on
if (autorepair_enabled)
{
// Special Sentry logic
if (ent->m_iClassID() == CL_CLASS(CObjectSentrygun))
{
// Current sentry ammo
int sentry_ammo = CE_INT(ent, netvar.m_iAmmoShells);
// Max Sentry ammo
int max_ammo = 0;
// Set Ammo depending on level
switch (CE_INT(ent, netvar.iUpgradeLevel))
{
case 1:
max_ammo = 150;
break;
case 2:
case 3:
max_ammo = 200;
}
// Sentry needs ammo
if (sentry_ammo < max_ammo)
return true;
}
// Buildings needs to be repaired
if (cur_ammo && ent->m_iHealth() != ent->m_iMaxHealth())
return true;
}
// Autoupgrade is on
if (autoupgrade_enabled)
{
// Upgrade lvel
int upgrade_level = CE_INT(ent, netvar.iUpgradeLevel);
// Don't upgrade mini sentries
if (CE_BYTE(ent, netvar.m_bMiniBuilding))
return false;
// Rvar to check
int level = 0;
// Pick The right rvar to check depending on building type
switch (ent->m_iClassID())
{
case CL_CLASS(CObjectSentrygun):
// Enabled check
if (!autoupgrade_sentry)
return false;
level = *autoupgrade_sentry_level;
break;
case CL_CLASS(CObjectDispenser):
// Enabled check
if (!autoupgrade_dispenser)
return false;
level = *autoupgrade_dispenser_level;
break;
case CL_CLASS(CObjectTeleporter):
// Enabled check
if (!autoupgrade_teleport)
return false;
level = *autoupgrade_teleport_level;
break;
}
// Can be upgraded
if (upgrade_level < level && cur_ammo)
return true;
}
return false;
}
CachedEntity *targetBuilding(bool priority)
{
float wrench_range = re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
CachedEntity *target = nullptr;
float distance = FLT_MAX;
for (int i = 0; i < entity_cache::max; i++)
{
CachedEntity *ent = ENTITY(i);
if (CE_BAD(ent))
continue;
// can't exactly repair Merasmus
if (ent->m_Type() != ENTITY_BUILDING)
continue;
// yes, repair enemy buildings
if (ent->m_bEnemy())
continue;
float new_distance = g_pLocalPlayer->v_Eye.DistTo(GetBuildingPosition(ent));
// Further away than old one
if (new_distance >= distance)
continue;
auto id = ent->m_iClassID();
switch (id)
{
case CL_CLASS(CObjectSentrygun):
{
if (priority && *autorepair_priority != 1)
continue;
// Don't repair sentries
if (!autorepair_repair_sentry && !autoupgrade_sentry)
continue;
break;
}
case CL_CLASS(CObjectDispenser):
{
if (priority && *autorepair_priority != 2)
continue;
// Repair Dispensers check
if (!autorepair_repair_dispenser && !autoupgrade_dispenser)
continue;
break;
}
case CL_CLASS(CObjectTeleporter):
{
if (priority && *autorepair_priority != 3)
continue;
// Repair Teleporters check
if (!autorepair_repair_teleport && !autoupgrade_teleport)
continue;
break;
}
default:
continue;
}
float s_distance = ent->m_vecOrigin().DistTo(LOCAL_E->m_vecOrigin());
if (!ShouldHitBuilding(ent))
continue;
if (s_distance > wrench_range)
continue;
target = ent;
distance = new_distance;
}
return target;
};
static void BuildingAimbot()
{
if (!autorepair_enabled && !autoupgrade_enabled)
return;
if (CE_BAD(LOCAL_E) || CE_BAD(LOCAL_W))
return;
CachedEntity *target = nullptr;
// Metal
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 += 50.f;
if (cur_ammo == 0)
return;
if (g_pLocalPlayer->clazz != tf_engineer)
return;
if (GetWeaponMode() != weaponmode::weapon_melee)
return;
// Get priority buildings
if (autorepair_priority)
target = targetBuilding(true);
// No building found
if (!target)
targetBuilding(false);
// We have a target
if (target)
{
Vector angle = GetAimAtAngles(g_pLocalPlayer->v_Eye, GetBuildingPosition(target));
Vector forward = GetForwardVector(g_pLocalPlayer->v_Eye, angle, wrench_range);
trace_t trace;
if (IsEntityVectorVisible(target, forward, MASK_SHOT, &trace))
{
if (trace.DidHit() && (IClientEntity *) trace.m_pEnt == RAW_ENT(target))
{
current_user_cmd->viewangles = angle;
if (autorepair_silent)
g_pLocalPlayer->bUseSilentAngles = true;
current_user_cmd->buttons |= IN_ATTACK;
}
}
}
}
static void CreateMove()
{
SandwichAim();
ChargeAimbot();
SapperAimbot();
BuildingAimbot();
}
static InitRoutine init([]() {

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,31 +25,88 @@ 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;
}
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 care at all
constexpr bot_class_config DIST_GUNSLINGER_ENGINEER{ 100.0f, 300.0f, 500.0f };
inline bool HasGunslinger(CachedEntity *ent)
{
return HasWeapon(ent, 142);
}
static void CreateMove()
{
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || !LOCAL_E->m_bAlivePlayer())
@ -65,17 +125,42 @@ static void CreateMove()
autoJump();
if (primary_only)
updateSlot();
if (engineer_mode)
{
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;
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 = -1;
if (engineer_mode && g_pLocalPlayer->clazz == tf_engineer)
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 (blocking)
// Engineer stuff
if (engineer_mode && g_pLocalPlayer->clazz == tf_engineer)
if (engineerLogic())
return;
if (blocking)
return;
// Spy can just walk into the enemy
if (spy_mode)
{
@ -127,6 +212,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 +227,115 @@ bool init(bool first_cm)
return true;
}
struct area_struct
{
// The Area
CNavArea *navarea;
// Distance away from enemies
float min_distance;
// Valid enemies to area
std::vector<Vector *> 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 (HasGunslinger(LOCAL_E))
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.min_distance - config->preferred) < std::abs(b.min_distance - 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.navarea->m_center;
// Don't want to instantly hit the floor
area_pos.z += 42.0f;
// Loop all valid enemies
for (auto pos : area.enemy_list)
{
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 && !HasGunslinger(LOCAL_E))
continue;
// All good!
building_spots.push_back(std::pair<CNavArea *, Vector>(area.navarea, area.navarea->m_center));
}
}
}
static bool navToSniperSpot()
{
// Don't path if you already have commands. But also don't error out.
@ -165,6 +361,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, 5, true, true, false))
{
current_task = { task::engineer, 5 };
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 (HasGunslinger(LOCAL_E))
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, true))
{
current_task = { task::engineer, priority };
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, true))
{
current_task = { task::engineer, priority };
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) && !HasGunslinger(LOCAL_E))
{
if (navToBuilding(building))
return true;
}
// Let's terrify some people (gunslinger engineer)
if (HasGunslinger(LOCAL_E))
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 +709,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;
@ -245,6 +750,12 @@ static bool stayNearPlayer(CachedEntity *&ent, const bot_class_config &config, C
if (nav::navTo(i->m_center, 7, true, false))
{
result = i;
if (engineer)
{
current_task = { task::engineer, 4 };
current_engineer_task = task::staynear_engineer;
}
else
current_task = task::stay_near;
return true;
}
@ -256,6 +767,12 @@ static bool stayNearPlayer(CachedEntity *&ent, const bot_class_config &config, C
if (nav::navTo((*it.base())->m_center, 7, true, false))
{
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 +817,97 @@ 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?
bot_class_config config = DIST_ENGINEER;
if (HasGunslinger(LOCAL_E))
config = DIST_GUNSLINGER_ENGINEER;
// 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(4000))
{
// We're doing nothing? Do something!
return stayNearHelpers::stayNearPlayers(config, last_target, last_area);
}
return false;
}
// Main stay near function
static bool stayNear()
{
@ -439,13 +1047,17 @@ static std::vector<Vector> getDispensers()
continue;
if (CE_BYTE(ent, netvar.m_bHasSapper))
continue;
if (CE_BYTE(ent, netvar.m_bBuilding))
continue;
if (CE_BYTE(ent, netvar.m_bPlacing))
continue;
dispensers.push_back(*ent->m_vecDormantOrigin());
}
std::sort(dispensers.begin(), dispensers.end(), [](Vector &a, Vector &b) { return g_pLocalPlayer->v_Origin.DistTo(a) < g_pLocalPlayer->v_Origin.DistTo(b); });
return dispensers;
}
static bool getDispenserHealthAndAmmo()
static bool getDispenserHealthAndAmmo(int metal)
{
// Timeout for standing next to dispenser
static Timer dispenser_timeout{};
@ -453,6 +1065,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 +1128,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)
{
@ -551,7 +1175,7 @@ static bool getHealthAndAmmo()
continue;
healthpacks.push_back(ent->m_vecOrigin());
}
std::sort(healthpacks.begin(), healthpacks.end(), [](Vector &a, Vector &b) { return g_pLocalPlayer->v_Origin.DistTo(a) > g_pLocalPlayer->v_Origin.DistTo(b); });
std::sort(healthpacks.begin(), healthpacks.end(), [](Vector &a, Vector &b) { return g_pLocalPlayer->v_Origin.DistTo(a) < g_pLocalPlayer->v_Origin.DistTo(b); });
for (auto &pack : healthpacks)
{
if (nav::navTo(pack, health < 0.64f || lowAmmo ? 10 : 3, true, false))
@ -572,7 +1196,7 @@ static bool getHealthAndAmmo()
continue;
ammopacks.push_back(ent->m_vecOrigin());
}
std::sort(ammopacks.begin(), ammopacks.end(), [](Vector &a, Vector &b) { return g_pLocalPlayer->v_Origin.DistTo(a) > g_pLocalPlayer->v_Origin.DistTo(b); });
std::sort(ammopacks.begin(), ammopacks.end(), [](Vector &a, Vector &b) { return g_pLocalPlayer->v_Origin.DistTo(a) < g_pLocalPlayer->v_Origin.DistTo(b); });
for (auto &pack : ammopacks)
{
if (nav::navTo(pack, health < 0.64f || lowAmmo ? 9 : 3, true, false))
@ -643,6 +1267,19 @@ static slots getBestSlot(slots active_slot)
else
return secondary;
}
case tf_engineer:
{
if (current_task == task::engineer)
{
// We cannot build the building if we keep switching away from the PDA
if (current_engineer_task == task::engineer_task::build_building)
return active_slot;
// Use wrench to repair/upgrade
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)
{

View File

@ -7,6 +7,7 @@
#include <hacks/Spam.hpp>
#include <settings/Bool.hpp>
#include <settings/String.hpp>
#include "common.hpp"
#include "MiscTemporary.hpp"
@ -18,6 +19,7 @@ static settings::String filename{ "spam.filename", "spam.txt" };
static settings::Int spam_delay{ "spam.delay", "800" };
static settings::Int voicecommand_spam{ "spam.voicecommand", "0" };
static settings::Boolean teamname_spam{ "spam.teamname", "0" };
static settings::String teamname_file{ "spam.teamname.file", "teamspam.txt" };
static settings::Boolean team_only{ "spam.teamchat", "false" };
static int last_index;
@ -234,6 +236,11 @@ bool FormatSpamMessage(std::string &message)
return SubstituteQueries(message);
}
// What to spam
static std::vector<std::string> teamspam_text = { "CAT", "HOOK" };
// Current spam index
static int current_teamspam_idx = 0;
void createMove()
{
IF_GAME(IsTF2())
@ -243,16 +250,13 @@ void createMove()
{
if (!(g_GlobalVars->tickcount % 10))
{
static bool teamname_swap = false;
if (teamname_swap)
if (teamspam_text.size())
{
teamname_swap = false;
g_IEngine->ServerCmd("tournament_teamname Cat");
}
else
{
teamname_swap = true;
g_IEngine->ServerCmd("tournament_teamname Hook");
g_IEngine->ServerCmd(format("tournament_teamname ", teamspam_text.at(current_teamspam_idx)).c_str());
current_teamspam_idx++;
// We've hit the end of the vector
if (current_teamspam_idx == teamspam_text.size())
current_teamspam_idx = 0;
}
}
}
@ -391,10 +395,34 @@ const std::vector<std::string> builtin_nonecore = { "NULL CORE - REDUCE YOUR RIS
const std::vector<std::string> builtin_lmaobox = { "GET GOOD, GET LMAOBOX!", "LMAOBOX - WAY TO THE TOP", "WWW.LMAOBOX.NET - BEST FREE TF2 HACK!" };
const std::vector<std::string> builtin_lithium = { "CHECK OUT www.YouTube.com/c/DurRud FOR MORE INFORMATION!", "PWNING AIMBOTS WITH OP ANTI-AIMS SINCE 2015 - LITHIUMCHEAT", "STOP GETTING MAD AND STABILIZE YOUR MOOD WITH LITHIUMCHEAT!", "SAVE YOUR MONEY AND GET LITHIUMCHEAT! IT IS FREE!", "GOT ROLLED BY LITHIUM? HEY, THAT MEANS IT'S TIME TO GET LITHIUMCHEAT!!" };
void teamspam_reload(std::string after)
{
// Clear spam vector
teamspam_text.clear();
// Reset Spam idx
current_teamspam_idx = 0;
if (after != "")
{
static TextFile teamspam;
if (teamspam.TryLoad(after))
{
teamspam_text = teamspam.lines;
for (auto &text : teamspam_text)
ReplaceSpecials(text);
}
}
}
void teamspam_reload_command()
{
teamspam_reload(*teamname_file);
}
static InitRoutine EC([]() {
teamname_file.installChangeCallback([](settings::VariableBase<std::string> &, std::string after) { teamspam_reload(after); });
EC::Register(EC::CreateMove, createMove, "spam", EC::average);
init();
});
static CatCommand reload_ts("teamspam_reload", "Relaod teamspam file", teamspam_reload_command);
static CatCommand reload_cc("spam_reload", "Reload spam file", hacks::shared::spam::reloadSpamFile);
} // namespace hacks::shared::spam

View File

@ -518,7 +518,7 @@ void ReplaceString(std::string &input, const std::string &what, const std::strin
void ReplaceSpecials(std::string &str)
{
int val, i;
int val;
size_t c = 0, len = str.size();
for (int i = 0; i + c < len; ++i)
{
@ -685,6 +685,58 @@ bool GetHitbox(CachedEntity *entity, int hb, Vector &out)
return true;
}
void MatrixGetColumn(const matrix3x4_t &in, int column, Vector &out)
{
out.x = in[0][column];
out.y = in[1][column];
out.z = in[2][column];
}
inline void MatrixAngles(const matrix3x4_t &matrix, float *angles)
{
float forward[3];
float left[3];
float up[3];
//
// Extract the basis vectors from the matrix. Since we only need the Z
// component of the up vector, we don't get X and Y.
//
forward[0] = matrix[0][0];
forward[1] = matrix[1][0];
forward[2] = matrix[2][0];
left[0] = matrix[0][1];
left[1] = matrix[1][1];
left[2] = matrix[2][1];
up[2] = matrix[2][2];
float xyDist = std::sqrt(forward[0] * forward[0] + forward[1] * forward[1]);
// enough here to get angles?
if (xyDist > 0.001f)
{
// (yaw) y = ATAN( forward.y, forward.x ); -- in our space, forward is the X axis
angles[1] = RAD2DEG(atan2f(forward[1], forward[0]));
// (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
angles[0] = RAD2DEG(atan2f(-forward[2], xyDist));
// (roll) z = ATAN( left.z, up.z );
angles[2] = RAD2DEG(atan2f(left[2], up[2]));
}
else
{
// (yaw) y = ATAN( -left.x, left.y ); -- forward is mostly z, so use right for yaw
angles[1] = RAD2DEG(atan2f(-left[0], left[1]));
// (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
angles[0] = RAD2DEG(atan2f(-forward[2], xyDist));
// Assume no roll in this case as one degree of freedom has been lost (i.e. yaw == roll)
angles[2] = 0;
}
}
void VectorAngles(Vector &forward, Vector &angles)
{
float tmp, yaw, pitch;
@ -1485,7 +1537,6 @@ void ChangeName(std::string name)
ch->SendNetMsg(setname, false);
}
}
const char *powerups[] = { "STRENGTH", "RESISTANCE", "VAMPIRE", "REFLECT", "HASTE", "REGENERATION", "PRECISION", "AGILITY", "KNOCKOUT", "KING", "PLAGUE", "SUPERNOVA", "CRITS" };
const std::string classes[] = { "Scout", "Sniper", "Soldier", "Demoman", "Medic", "Heavy", "Pyro", "Spy", "Engineer" };

View File

@ -105,6 +105,7 @@ VMTHook client{};
VMTHook inventory{};
VMTHook chathud{};
VMTHook engine{};
VMTHook demoplayer{};
VMTHook netchannel{};
VMTHook firebullets{};
VMTHook clientdll{};

View File

@ -127,13 +127,7 @@ namespace hooked_methods
{
DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUserCmd *cmd)
{
#define TICK_INTERVAL (g_GlobalVars->interval_per_tick)
#define TIME_TO_TICKS(dt) ((int) (0.5f + (float) (dt) / TICK_INTERVAL))
#define TICKS_TO_TIME(t) (TICK_INTERVAL * (t))
#define ROUND_TO_TICKS(t) (TICK_INTERVAL * TIME_TO_TICKS(t))
volatile uintptr_t **fp;
__asm__ volatile("mov %%ebp, %0" : "=r"(fp));
bSendPackets = reinterpret_cast<bool *>(**fp - 8);
bSendPackets = reinterpret_cast<bool *>((uintptr_t) __builtin_frame_address(2) - 8);
g_Settings.is_create_move = true;
bool time_replaced, ret, speedapplied;
@ -396,19 +390,6 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
ret = false;
}
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
{
CachedEntity *ent = ENTITY(i);
if (CE_GOOD(LOCAL_E))
if (ent == LOCAL_E)
continue;
if (CE_BAD(ent) || !ent->m_bAlivePlayer())
continue;
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
if (NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) <= 1.5f)
continue;
}
g_pLocalPlayer->UpdateEnd();
}
@ -419,6 +400,18 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
}
g_pLocalPlayer->bAttackLastTick = (cmd->buttons & IN_ATTACK);
g_Settings.is_create_move = false;
if (nolerp && !hacks::shared::backtrack::isBacktrackEnabled)
{
static const ConVar *pUpdateRate = g_pCVar->FindVar("cl_updaterate");
if (!pUpdateRate)
pUpdateRate = g_pCVar->FindVar("cl_updaterate");
else
{
float interp = MAX(cl_interp->GetFloat(), cl_interp_ratio->GetFloat() / pUpdateRate->GetFloat());
cmd->tick_count += TIME_TO_TICKS(interp);
}
}
return ret;
}
} // namespace hooked_methods

View File

@ -11,6 +11,18 @@ static settings::Boolean enable_debug_servercmd{ "debug.servercmdkeyvalues", "fa
namespace hooked_methods
{
std::vector<KeyValues *> Iterate(KeyValues *event, int depth);
DEFINE_HOOKED_METHOD(IsPlayingTimeDemo, bool, void *_this)
{
if (nolerp)
{
uintptr_t ret_addr = (uintptr_t) __builtin_return_address(1);
static auto wanted_addr = gSignatures.GetClientSignature("84 C0 0F 85 ? ? ? ? E9 ? ? ? ? 8D 76 00 C6 05");
if (ret_addr == wanted_addr && CE_GOOD(LOCAL_E) && LOCAL_E->m_bAlivePlayer())
return true;
}
return original::IsPlayingTimeDemo(_this);
}
DEFINE_HOOKED_METHOD(ServerCmdKeyValues, void, IVEngineClient013 *_this, KeyValues *kv)
{
if (!enable_debug_servercmd)