Rewrite navbot/navparser

Fix and improve capture and pathing logic

Fixes some bugs with the capture logic and makes bots able to prioritize areas properly and fixes crouch jumps
Apply 1 suggestion(s) to 1 file(s)
Combine all Capture related files into one file

attempt at remaking navbot

big improvements, we're slowly getting somewhere!

Switch from bounding box traces to double traces

lightcat changes

Many improvements. Vischeck cache, etc

Fix bug related to the Dropdown code

WIP

i dont even know what this is, just comitting so the current state is out there
Improve navigation and fix a ton of bugs

- Fix findClosestNavArea
- Apply Dropdowns properly
- Improve pathing logic to not hit walls (unless nav mesh is stupid)
- Don't try to jump with revved Minigun
Add runtime vischecking and repathing

Also make the priority system work
Update debug message to reclect center point and properly restore z

Fix a bunch of issues in the nav logic

Account for brass beast speed

Skip a crumb when possible

Fix jump height + add logic to prevent getting stuck

Add very basic navbot behaviour and improve stuck detection system

- Look at path
- Get Health
- Get Ammo
- Roam
Improve Navbot to a semi-usable level

- Universal Blacklist system
- Re-added stay near
- Avoid walking into enemy range
- Snipe Sentries
Fix some garbage prioritizing in aimbot

More Navbot changes

- Readd Autojump
- Readd best weapon logic
- Make Staynear take way less resources
- Make bot ignore free blacklist if Ubered/Invincible
Make sentry priority better

Add capping and fix water movement

Make bots escape danger and add a way to draw danger

Fix a few things (See Desc)

- Make roaming no longer path around the entire map, but instead choose nearby spots.
- Make enemy danger not include People marked as CAT/Friend (Don't bump into them either though)
- Fix inaccurate visuals on blacklist
Make snipers bots more aggressive

Fix antiaim being enabled with no pitch and yaw

this casued issues with look at path for example
Fix issues with aimbot + look at path

Tweak navbot Settings and fix followbot

Make ammo/health search take dispensers into account

Co-authored-by: TotallyNotElite <totallynotelite@gmail.com>
This commit is contained in:
BenCat07 2020-12-28 12:44:25 +01:00 committed by TotallyNotElite
parent 4f1c4bde57
commit 39d2a82c2d
30 changed files with 3196 additions and 2088 deletions

View File

@ -30,20 +30,25 @@
</Box>
<Box padding="12 6 6 6" width="content" height="content" name="NavBot" y="65">
<List width="150">
<AutoVariable width="fill" target="misc.pathing" label="Enable pathing"/>
<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="nav.enabled" label="Enable pathing"/>
<AutoVariable width="fill" target="nav.draw" label="Draw path"/>
<AutoVariable width="fill" target="nav.look-at-path" label="Look at path"/>
<AutoVariable width="fill" target="navbot.enabled" label="Enable NavBot"/>
<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"/>
<AutoVariable width="fill" target="navbot.capture-objectives" label="Capture objectives" tooltip="Automatically capture objectives (CTF,CP and PL only)"/>
<!--AutoVariable width="fill" target="navbot.engineer-mode" label="Enable Engineer mode"/-->
<AutoVariable width="fill" target="navbot.search-health" label="Search health"/>
<AutoVariable width="fill" target="navbot.search-ammo" label="Search ammo"/>
<AutoVariable width="fill" target="navbot.escape-danger" label="Escape danger"/>
<AutoVariable width="fill" target="navbot.escape-danger.slight-danger.capping" label="Safe capping" tooltip="Make bots run pre-emptively from capture points in case of danger"/>
<AutoVariable width="fill" target="navbot.escape-danger.ctf-cap" label="Escape danger w. intel" tooltip="Also try to escape from danger when carrying intel."/>
<!--AutoVariable width="fill" target="navbot.spy-mode" label="Enable Spy mode"/>
<AutoVariable width="fill" target="navbot.other-mode" label="General mode"/-->
<AutoVariable width="fill" target="navbot.primary-only" label="Best weapon only"/>
<AutoVariable width="fill" target="navbot.autojump.enabled" label="Enable autojump"/>
<AutoVariable width="fill" target="navbot.autojump.trigger-distance" label="Jump distance"/>
<!--AutoVariable width="fill" target="navbot.target-sentry" label="Try to target sentries"/-->
<AutoVariable width="fill" target="navbot.stay-near" label="Zone enemies (stay near)"/>
<AutoVariable width="fill" target="navbot.spy-ignore-time" label="Spy ignore time"/>
<AutoVariable width="fill" target="navbot.snipe-sentries" label="Try to target sentries"/>
<AutoVariable width="fill" target="navbot.snipe-sentries.shortrange" label="Scout/Pyro sentry mode" tooltip="Try to target sentries as scout/pyro aswell. Usually not recommended."/>
<AutoVariable width="fill" target="navbot.stay-near" label="Stalk enemies" tooltip="Try to outrange as sniper and close distance as scout/heavy"/>
<!-- <!AutoVariable width="fill" target="navbot.take-teleporters" label="take teleporters"/> -->
</List>
</Box>

@ -1 +1 @@
Subproject commit 5f2940c4707127db1581e5708eecca1d6130610f
Subproject commit 55ae8047a24203cec834f54fd67ee6d571f497f6

85
include/CaptureLogic.hpp Normal file
View File

@ -0,0 +1,85 @@
#pragma once
#include <entitycache.hpp>
// Tf2 flag types
enum ETFFlagType
{
TF_FLAGTYPE_CTF = 0,
TF_FLAGTYPE_ATTACK_DEFEND,
TF_FLAGTYPE_TERRITORY_CONTROL,
TF_FLAGTYPE_INVADE,
TF_FLAGTYPE_RESOURCE_CONTROL,
TF_FLAGTYPE_ROBOT_DESTRUCTION,
TF_FLAGTYPE_PLAYER_DESTRUCTION
//
// ADD NEW ITEMS HERE TO AVOID BREAKING DEMOS
//
};
// Flag Drop status
enum ETFFlagStatus
{
TF_FLAGINFO_HOME = 0,
TF_FLAGINFO_STOLEN,
TF_FLAGINFO_DROPPED
};
struct flag_info
{
CachedEntity *ent{ nullptr };
std::optional<Vector> spawn_pos;
int team{ TEAM_UNK };
flag_info(){};
flag_info(CachedEntity *ent, Vector spawn_pos, int team)
{
this->ent = ent;
this->spawn_pos = spawn_pos;
this->team = team;
}
};
struct pl_info
{
CachedEntity *ent;
std::optional<Vector> position;
pl_info(){};
};
#define MAX_CONTROL_POINTS 8
#define MAX_PREVIOUS_POINTS 3
struct cp_info
{
// Index in the ObjectiveResource
int cp_index{ -1 };
std::optional<Vector> position;
// For BLU and RED to show who can and who cannnot cap
std::array<bool, 2> can_cap{};
cp_info(){};
};
namespace flagcontroller
{
// Use incase you don't get the needed information from the functions below
flag_info getFlag(int team);
Vector getPosition(CachedEntity *flag);
std::optional<Vector> getPosition(int team);
CachedEntity *getCarrier(CachedEntity *flag);
CachedEntity *getCarrier(int team);
ETFFlagStatus getStatus(CachedEntity *flag);
ETFFlagStatus getStatus(int team);
} // namespace flagcontroller
namespace plcontroller
{
// Get the closest Control Payload
std::optional<Vector> getClosestPayload(Vector source, int team);
} // namespace plcontroller
namespace cpcontroller
{
// Get the closest Control Point we can cap
std::optional<Vector> getClosestControlPoint(Vector source, int team);
} // namespace cpcontroller

View File

@ -73,7 +73,6 @@
#include "core/sharedobj.hpp"
#include "init.hpp"
#include "reclasses/reclasses.hpp"
#include <CNavFile.h>
#include "HookTools.hpp"
#include "bytepatch.hpp"

View File

@ -83,6 +83,7 @@ public:
offset_t m_bPlacing;
offset_t m_bBuilding;
offset_t m_bPlasmaDisable;
offset_t m_bCarryDeploy;
// teleporter
offset_t m_iTeleState; // teleport state [1 = idle, 2 = active, 3 = teleporting, 4 = charging]
@ -205,6 +206,19 @@ public:
offset_t m_iHealingAssist_Resource;
offset_t m_iPlayerLevel_Resource;
offset_t m_nFlagType;
offset_t m_nFlagStatus;
offset_t m_bTeamCanCap;
offset_t m_iNumControlPoints;
offset_t m_vCPPositions;
offset_t m_iOwningTeam;
offset_t m_bCPLocked;
offset_t m_bPlayingMiniRounds;
offset_t m_bInMiniRound;
offset_t m_iPreviousPoints;
offset_t m_iBaseControlPoints;
offset_t m_iPlayerIndex;
offset_t m_hTargetPlayer;
};

View File

@ -36,6 +36,7 @@ bool BacktrackVisCheck(CachedEntity *entity);
void Reset();
// Stuff to make storing functions easy
bool isAiming();
CachedEntity *CurrentTarget();
bool ShouldAim();
CachedEntity *RetrieveBestTarget(bool aimkey_state);

View File

@ -1,4 +1,4 @@
#pragma once
/*#pragma once
#include <array>
#include <stdint.h>
@ -19,7 +19,8 @@ enum task : uint8_t
dispenser,
followbot,
outofbounds,
engineer
engineer,
capture
};
enum engineer_task : uint8_t
@ -70,3 +71,4 @@ struct bot_class_config
float max;
};
} // namespace hacks::tf2::NavBot
*/

View File

@ -202,7 +202,7 @@ float GetFov(Vector ang, Vector src, Vector dst);
void ReplaceString(std::string &input, const std::string &what, const std::string &with_what);
void ReplaceSpecials(std::string &input);
std::pair<float, float> ComputeMove(const Vector &a, const Vector &b);
Vector ComputeMove(const Vector &a, const Vector &b);
std::pair<float, float> ComputeMovePrecise(const Vector &a, const Vector &b);
void WalkTo(const Vector &vector);

View File

@ -34,6 +34,8 @@ public:
char life_state;
int clazz;
bool bZoomed;
bool bRevving;
bool bRevved;
float flZoomBegin;
bool holding_sniper_rifle;
bool holding_sapper;

View File

@ -1,42 +1,101 @@
#pragma once
#include <memory>
#include "mathlib/vector.h"
#include <vector>
#include "CNavFile.h"
class CNavFile;
class CNavArea;
namespace nav
enum Priority_list
{
enum init_status : uint8_t
{
off = 0,
unavailable,
initing,
on
patrol = 5,
lowprio_health,
staynear,
snipe_sentry,
followbot,
ammo,
capture,
health,
danger,
};
// Call prepare first and check its return value
extern std::unique_ptr<CNavFile> navfile;
namespace navparser
{
constexpr float PLAYER_WIDTH = 49;
constexpr float HALF_PLAYER_WIDTH = PLAYER_WIDTH / 2.0f;
constexpr float PLAYER_JUMP_HEIGHT = 72.0f;
// Current path priority
extern int curr_priority;
// Check if ready to recieve another NavTo (to avoid overwriting of
// instructions)
extern bool ReadyForCommands;
// Ignore. For level init only
extern std::atomic<init_status> status;
#define TICKCOUNT_TIMESTAMP(seconds) (g_GlobalVars->tickcount + int(seconds / g_GlobalVars->interval_per_tick))
// Nav to vector
bool navTo(const Vector &destination, int priority = 5, bool should_repath = true, bool nav_to_local = true, bool is_repath = false);
// Find closest to vector area
CNavArea *findClosestNavSquare(const Vector &vec);
// Check and init navparser
bool prepare();
// Clear current path
void clearInstructions();
// Check if area is safe from stickies and sentries
bool isSafe(CNavArea *area);
// Basic Blacklist reasons, you can add your own externally and use them
enum BlacklistReason_enum
{
SENTRY,
STICKY,
ENEMY_NORMAL,
ENEMY_DORMANT,
// Always last
BLACKLIST_LENGTH
};
} // namespace nav
class BlacklistReason
{
public:
BlacklistReason_enum value;
int time = 0;
void operator=(BlacklistReason_enum const &reason)
{
this->value = reason;
}
BlacklistReason()
{
this->value = (BlacklistReason_enum) -1;
this->time = 0;
}
BlacklistReason(BlacklistReason_enum reason)
{
this->value = reason;
this->time = 0;
}
BlacklistReason(BlacklistReason_enum reason, int time)
{
this->value = reason;
this->time = time;
}
};
struct Crumb
{
CNavArea *navarea;
Vector vec;
};
namespace NavEngine
{
// Is the Nav engine ready to run?
bool isReady();
// Are we currently pathing?
bool isPathing();
CNavFile *getNavFile();
// Get closest nav square to target vector
CNavArea *findClosestNavSquare(const Vector origin);
// Get the path nodes
std::vector<Crumb> *getCrumbs();
bool navTo(const Vector &destination, int priority = 5, bool should_repath = true, bool nav_to_local = true, bool is_repath = true);
// Use when something unexpected happens, e.g. vischeck fails
void abandonPath();
// Use to cancel pathing completely
void cancelPath();
// Return the whole thing
std::unordered_map<CNavArea *, BlacklistReason> *getFreeBlacklist();
// Return a specific category, we keep the same indexes to provide single element erasing
std::unordered_map<CNavArea *, BlacklistReason> getFreeBlacklist(BlacklistReason reason);
// Clear whole blacklist
void clearFreeBlacklist();
// Clear by category
void clearFreeBlacklist(BlacklistReason reason);
extern int current_priority;
} // namespace NavEngine
} // namespace navparser

View File

@ -13,4 +13,6 @@ public:
int roundmode; // 48 | 4 bytes | 52
int pad1[1]; // 52 | 4 bytes | 56
int winning_team; // 56 | 4 bytes | 60
char pad2[974]; // 60 | 974 bytes | 1034
bool isPVEMode; // 1034 | 1 byte | 1035
};

View File

@ -1,4 +1,5 @@
set(files "${CMAKE_CURRENT_LIST_DIR}/angles.cpp"
"${CMAKE_CURRENT_LIST_DIR}/CaptureLogic.cpp"
"${CMAKE_CURRENT_LIST_DIR}/chatlog.cpp"
"${CMAKE_CURRENT_LIST_DIR}/chatstack.cpp"
"${CMAKE_CURRENT_LIST_DIR}/conditions.cpp"

492
src/CaptureLogic.cpp Normal file
View File

@ -0,0 +1,492 @@
#include "CaptureLogic.hpp"
#include "common.hpp"
namespace flagcontroller
{
std::array<flag_info, 2> flags;
bool is_ctf = true;
// Check if a flag is good or not
bool isGoodFlag(CachedEntity *flag)
{
if (CE_INVALID(flag) || flag->m_iClassID() != CL_CLASS(CCaptureFlag))
return false;
return true;
}
void Update()
{
// Not ctf, no need to update
if (!is_ctf)
return;
// Find flags if missing
if (!flags[0].ent || !flags[1].ent)
for (int i = g_IEngine->GetMaxClients() + 1; i < MAX_ENTITIES; i++)
{
CachedEntity *ent = ENTITY(i);
// We cannot identify a bad entity as a flag due to the unreliability of it
if (CE_BAD(ent) || ent->m_iClassID() != CL_CLASS(CCaptureFlag))
continue;
// Store flags
if (!flags[0].ent)
flags[0].ent = ent;
else if (ent != flags[0].ent)
flags[1].ent = ent;
}
// Update flag data
for (auto &flag : flags)
{
// Not inited
if (!flag.ent)
continue;
// Bad Flag, reset
if (!isGoodFlag(flag.ent))
{
flag = flag_info();
continue;
}
// Cannot use "bad" flag, but it is still potentially valid
if (CE_BAD(flag.ent))
continue;
int flag_type = CE_INT(flag.ent, netvar.m_nFlagType);
// Only CTF support for now
if (flag_type != TF_FLAGTYPE_CTF)
continue;
// Assign team if missing
if (flag.team == TEAM_UNK)
flag.team = flag.ent->m_iTeam();
// Assign spawn point if it is missing and the flag is at spawn
if (!flag.spawn_pos)
{
int flag_status = CE_INT(flag.ent, netvar.m_nFlagStatus);
// Flag is home
if (flag_status == TF_FLAGINFO_HOME)
flag.spawn_pos = flag.ent->m_vecOrigin();
}
}
}
void LevelInit()
{
// Resez everything
for (auto &flag : flags)
flag = flag_info();
is_ctf = true;
}
// Get the info for the flag
flag_info getFlag(int team)
{
for (auto &flag : flags)
{
if (flag.team == team)
return flag;
}
// None found
return flag_info();
}
// Get the Position of a flag on a specific team
Vector getPosition(CachedEntity *flag)
{
return flag->m_vecOrigin();
}
std::optional<Vector> getPosition(int team)
{
auto flag = getFlag(team);
if (isGoodFlag(flag.ent))
return getPosition(flag.ent);
// No good flag
return std::nullopt;
}
// Get the person carrying the flag
CachedEntity *getCarrier(CachedEntity *flag)
{
int entidx = HandleToIDX(CE_INT(flag, netvar.m_hOwnerEntity));
// None/Invalid
if (IDX_BAD(entidx))
return nullptr;
CachedEntity *carrier = ENTITY(entidx);
// Carrier is invalid
if (CE_BAD(carrier) || carrier->m_Type() != ENTITY_PLAYER)
return nullptr;
return carrier;
}
// Wrapper for when you don't have the entity
CachedEntity *getCarrier(int team)
{
auto flag = getFlag(team);
// Only use good flags
if (isGoodFlag(flag.ent))
return getCarrier(flag.ent);
return nullptr;
}
// Get the status of the flag (Home, being carried, dropped)
ETFFlagStatus getStatus(CachedEntity *flag)
{
return (ETFFlagStatus) CE_INT(flag, netvar.m_nFlagStatus);
}
ETFFlagStatus getStatus(int team)
{
auto flag = getFlag(team);
// Only use good flags
if (isGoodFlag(flag.ent))
return getStatus(flag.ent);
// Mark as home if nothing is found
return TF_FLAGINFO_HOME;
}
} // namespace flagcontroller
namespace plcontroller
{
// Array that controls all the payloads for each team. Red team is first, then comes blue team.
static std::array<std::vector<CachedEntity *>, 2> payloads;
static Timer update_payloads{};
void Update()
{
// We should update the payload list
if (update_payloads.test_and_set(3000))
{
// Reset entries
for (auto &entry : payloads)
entry.clear();
for (int i = g_IEngine->GetMaxClients() + 1; i < MAX_ENTITIES; i++)
{
CachedEntity *ent = ENTITY(i);
// Not the object we need or invalid (team)
if (CE_BAD(ent) || ent->m_iClassID() != CL_CLASS(CObjectCartDispenser) || ent->m_iTeam() < TEAM_RED || ent->m_iTeam() > TEAM_BLU)
continue;
int team = ent->m_iTeam();
// Add new entry for the team
payloads.at(team - TEAM_RED).push_back(ent);
}
}
}
std::optional<Vector> getClosestPayload(Vector source, int team)
{
// Invalid team
if (team < TEAM_RED || team > TEAM_BLU)
return std::nullopt;
// Convert to index
int index = team - TEAM_RED;
auto entry = payloads[index];
float best_distance = FLT_MAX;
std::optional<Vector> best_pos;
// Find best payload
for (auto payload : entry)
{
if (CE_BAD(payload) || payload->m_iClassID() != CL_CLASS(CObjectCartDispenser))
continue;
if (payload->m_vecOrigin().DistTo(source) < best_distance)
{
best_pos = payload->m_vecOrigin();
best_distance = payload->m_vecOrigin().DistTo(source);
}
}
return best_pos;
}
void LevelInit()
{
for (auto &entry : payloads)
entry.clear();
}
} // namespace plcontroller
namespace cpcontroller
{
std::array<cp_info, MAX_CONTROL_POINTS> controlpoint_data;
CachedEntity *objective_resource = nullptr;
struct point_ignore
{
std::string mapname;
int point_idx;
point_ignore(std::string str, int idx) : mapname{ str }, point_idx{ idx } {};
};
// TODO: Find a way to fix these edge-cases.
// clang-format off
std::array<point_ignore, 1> ignore_points
{
point_ignore("cp_steel", 4)
};
// clang-format on
// This function updates the Entity used for the Object resource
void UpdateObjectiveResource()
{
// Already set and valid
if (CE_GOOD(objective_resource) && objective_resource->m_iClassID() == CL_CLASS(CTFObjectiveResource))
return;
// Find ObjectiveResource and gamerules
for (int i = g_IEngine->GetMaxClients() + 1; i < MAX_ENTITIES; i++)
{
CachedEntity *ent = ENTITY(i);
if (CE_BAD(ent) || ent->m_iClassID() != CL_CLASS(CTFObjectiveResource))
continue;
// Found it
objective_resource = ent;
break;
}
}
// A Bunch of defines for netvars that don't deserve their own function
#define GET_NUM_CONTROL_POINTS() (CE_INT(objective_resource, netvar.m_iNumControlPoints))
#define GET_OWNING_TEAM(index) ((&CE_INT(objective_resource, netvar.m_iOwningTeam))[index])
#define GET_BASE_CONTROL_POINT_FOR_TEAM(team) ((&CE_INT(objective_resource, netvar.m_iBaseControlPoints))[team])
#define GET_CP_LOCKED(index) ((&CE_VAR(objective_resource, netvar.m_bCPLocked, bool))[index])
#define IN_MINI_ROUND(index) ((&CE_VAR(objective_resource, netvar.m_bInMiniRound, bool))[index])
bool TeamCanCapPoint(int index, int team)
{
int arr_index = index + team * MAX_CONTROL_POINTS;
return (&CE_VAR(objective_resource, netvar.m_bTeamCanCap, bool))[arr_index];
}
int GetPreviousPointForPoint(int index, int team, int previndex)
{
int iIntIndex = previndex + (index * MAX_PREVIOUS_POINTS) + (team * MAX_CONTROL_POINTS * MAX_PREVIOUS_POINTS);
return (&CE_INT(objective_resource, netvar.m_iPreviousPoints))[iIntIndex];
}
int GetFarthestOwnedControlPoint(int team)
{
int iOwnedEnd = GET_BASE_CONTROL_POINT_FOR_TEAM(team);
if (iOwnedEnd == -1)
return -1;
int iNumControlPoints = GET_NUM_CONTROL_POINTS();
int iWalk = 1;
int iEnemyEnd = iNumControlPoints - 1;
if (iOwnedEnd != 0)
{
iWalk = -1;
iEnemyEnd = 0;
}
// Walk towards the other side, and find the farthest owned point
int iFarthestPoint = iOwnedEnd;
for (int iPoint = iOwnedEnd; iPoint != iEnemyEnd; iPoint += iWalk)
{
// If we've hit a point we don't own, we're done
if (GET_OWNING_TEAM(iPoint) != team)
break;
iFarthestPoint = iPoint;
}
return iFarthestPoint;
}
// Can we cap this point?
bool isPointUseable(int index, int team)
{
// We Own it, can't cap it
if (GET_OWNING_TEAM(index) == team)
return false;
// Can we cap the point?
if (!TeamCanCapPoint(index, team))
return false;
// We are playing a sectioned map, check if the CP is in it
if (CE_VAR(objective_resource, netvar.m_bPlayingMiniRounds, bool) && !IN_MINI_ROUND(index))
return false;
// Is the point locked?
if (GET_CP_LOCKED(index))
return false;
// Linear cap means that it won't require previous points, bail
static auto tf_caplinear = g_ICvar->FindVar("tf_caplinear");
if (tf_caplinear && !tf_caplinear->GetBool())
return true;
// Any previous points necessary?
int iPointNeeded = GetPreviousPointForPoint(index, team, 0);
// Points set to require themselves are always cappable
if (iPointNeeded == index)
return true;
// No required points specified? Require all previous points.
if (iPointNeeded == -1)
{
// No Mini rounds
if (!CE_VAR(objective_resource, netvar.m_bPlayingMiniRounds, bool))
{
// No custom previous point, team must own all previous points
int iFarthestPoint = GetFarthestOwnedControlPoint(team);
return (abs(iFarthestPoint - index) <= 1);
}
// We got a section map
else
{
// Tf2 itself does not seem to have any more code for this, so here goes
return true;
}
}
// Loop through each previous point and see if the team owns it
for (int iPrevPoint = 0; iPrevPoint < MAX_PREVIOUS_POINTS; iPrevPoint++)
{
iPointNeeded = GetPreviousPointForPoint(index, team, iPrevPoint);
if (iPointNeeded != -1)
{
// We don't own the needed points
if (GET_OWNING_TEAM(iPointNeeded) != team)
return false;
}
}
return true;
}
// Don't constantly update the cap status
static Timer capstatus_update{};
// Update the control points
void UpdateControlPoints()
{
// No objective ressource, can't run
if (!objective_resource)
return;
int num_cp = CE_INT(objective_resource, netvar.m_iNumControlPoints);
// No control points
if (!num_cp)
return;
// Clear the invalid controlpoints
if (num_cp <= MAX_CONTROL_POINTS)
for (int i = num_cp; i < MAX_CONTROL_POINTS; i++)
controlpoint_data.at(i) = cp_info();
for (int i = 0; i < num_cp; i++)
{
auto &data = controlpoint_data.at(i);
data.cp_index = i;
// Update position (m_vCPPositions[index])
data.position = (&CE_VAR(objective_resource, netvar.m_vCPPositions, Vector))[i];
}
if (capstatus_update.test_and_set(1000))
for (int i = 0; i < num_cp; i++)
{
auto &data = controlpoint_data.at(i);
// Check accessibility for both teams, requires alot of checks
data.can_cap.at(0) = isPointUseable(i, TEAM_RED);
data.can_cap.at(1) = isPointUseable(i, TEAM_BLU);
}
}
// Get the closest controlpoint to cap
std::optional<Vector> getClosestControlPoint(Vector source, int team)
{
// No resource for it
if (!objective_resource)
return std::nullopt;
// Check if it's a cp map
static auto tf_gamemode_cp = g_ICvar->FindVar("tf_gamemode_cp");
if (!tf_gamemode_cp)
{
tf_gamemode_cp = g_ICvar->FindVar("tf_gamemode_cp");
return std::nullopt;
}
if (!tf_gamemode_cp->GetBool())
return std::nullopt;
// Map team to 0-1 and check If Valid
int team_idx = team - TEAM_RED;
if (team_idx < 0 || team_idx > 1)
return std::nullopt;
// No controlpoints
if (!GET_NUM_CONTROL_POINTS())
return std::nullopt;
int ignore_index = -1;
// Do the points need checking because of the map?
auto levelname = GetLevelName();
for (auto &ignore : ignore_points)
{
// Try to find map name in bad point array
if (levelname.find(ignore.mapname) != levelname.npos)
ignore_index = ignore.point_idx;
}
// Find the best and closest control point
std::optional<Vector> best_cp;
float best_distance = FLT_MAX;
for (auto &cp : controlpoint_data)
{
// Ignore this point
if (cp.cp_index == ignore_index)
continue;
// They can cap
if (cp.can_cap.at(team_idx))
{
// Is it closer?
if (cp.position && (*cp.position).DistTo(source) < best_distance)
{
best_distance = (*cp.position).DistTo(source);
best_cp = cp.position;
}
}
}
return best_cp;
}
void LevelInit()
{
for (auto &cp : controlpoint_data)
cp = cp_info();
objective_resource = nullptr;
}
void Update()
{
UpdateControlPoints();
UpdateObjectiveResource();
}
} // namespace cpcontroller
// Main handlers
void CreateMove()
{
flagcontroller::Update();
plcontroller::Update();
cpcontroller::Update();
}
void LevelInit()
{
flagcontroller::LevelInit();
plcontroller::LevelInit();
cpcontroller::LevelInit();
}
static InitRoutine init([]() {
EC::Register(EC::CreateMove, CreateMove, "capturelogic_update");
EC::Register(EC::LevelInit, LevelInit, "capturelogic_levelinit");
});

View File

@ -0,0 +1,259 @@
#include "common.hpp"
#include "controlpointcontroller.hpp"
namespace cpcontroller
{
std::array<cp_info, MAX_CONTROL_POINTS> controlpoint_data;
CachedEntity *objective_resource = nullptr;
struct point_ignore
{
std::string mapname;
int point_idx;
point_ignore(std::string str, int idx) : mapname{ str }, point_idx{ idx } {};
};
// TODO: Find a way to fix these edge-cases.
// clang-format off
std::array<point_ignore, 1> ignore_points
{
point_ignore("cp_steel", 4)
};
// clang-format on
// This function updates the Entity used for the Object resource
void UpdateObjectiveResource()
{
// Already set and valid
if (CE_GOOD(objective_resource) && objective_resource->m_iClassID() == CL_CLASS(CTFObjectiveResource))
return;
// Find ObjectiveResource and gamerules
for (int i = g_IEngine->GetMaxClients() + 1; i < MAX_ENTITIES; i++)
{
CachedEntity *ent = ENTITY(i);
if (CE_BAD(ent) || ent->m_iClassID() != CL_CLASS(CTFObjectiveResource))
continue;
// Found it
objective_resource = ent;
break;
}
}
// A Bunch of defines for netvars that don't deserve their own function
#define GET_NUM_CONTROL_POINTS() (CE_INT(objective_resource, netvar.m_iNumControlPoints))
#define GET_OWNING_TEAM(index) ((&CE_INT(objective_resource, netvar.m_iOwningTeam))[index])
#define GET_BASE_CONTROL_POINT_FOR_TEAM(team) ((&CE_INT(objective_resource, netvar.m_iBaseControlPoints))[team])
#define GET_CP_LOCKED(index) ((&CE_VAR(objective_resource, netvar.m_bCPLocked, bool))[index])
#define IN_MINI_ROUND(index) ((&CE_VAR(objective_resource, netvar.m_bInMiniRound, bool))[index])
bool TeamCanCapPoint(int index, int team)
{
int arr_index = index + team * MAX_CONTROL_POINTS;
return (&CE_VAR(objective_resource, netvar.m_bTeamCanCap, bool))[arr_index];
}
int GetPreviousPointForPoint(int index, int team, int previndex)
{
int iIntIndex = previndex + (index * MAX_PREVIOUS_POINTS) + (team * MAX_CONTROL_POINTS * MAX_PREVIOUS_POINTS);
return (&CE_INT(objective_resource, netvar.m_iPreviousPoints))[iIntIndex];
}
int GetFarthestOwnedControlPoint(int team)
{
int iOwnedEnd = GET_BASE_CONTROL_POINT_FOR_TEAM(team);
if (iOwnedEnd == -1)
return -1;
int iNumControlPoints = GET_NUM_CONTROL_POINTS();
int iWalk = 1;
int iEnemyEnd = iNumControlPoints - 1;
if (iOwnedEnd != 0)
{
iWalk = -1;
iEnemyEnd = 0;
}
// Walk towards the other side, and find the farthest owned point
int iFarthestPoint = iOwnedEnd;
for (int iPoint = iOwnedEnd; iPoint != iEnemyEnd; iPoint += iWalk)
{
// If we've hit a point we don't own, we're done
if (GET_OWNING_TEAM(iPoint) != team)
break;
iFarthestPoint = iPoint;
}
return iFarthestPoint;
}
// Can we cap this point?
bool isPointUseable(int index, int team)
{
// We Own it, can't cap it
if (GET_OWNING_TEAM(index) == team)
return false;
// Can we cap the point?
if (!TeamCanCapPoint(index, team))
return false;
// We are playing a sectioned map, check if the CP is in it
if (CE_VAR(objective_resource, netvar.m_bPlayingMiniRounds, bool) && !IN_MINI_ROUND(index))
return false;
// Is the point locked?
if (GET_CP_LOCKED(index))
return false;
// Linear cap means that it won't require previous points, bail
static auto tf_caplinear = g_ICvar->FindVar("tf_caplinear");
if (tf_caplinear && !tf_caplinear->GetBool())
return true;
// Any previous points necessary?
int iPointNeeded = GetPreviousPointForPoint(index, team, 0);
// Points set to require themselves are always cappable
if (iPointNeeded == index)
return true;
// No required points specified? Require all previous points.
if (iPointNeeded == -1)
{
// No Mini rounds
if (!CE_VAR(objective_resource, netvar.m_bPlayingMiniRounds, bool))
{
// No custom previous point, team must own all previous points
int iFarthestPoint = GetFarthestOwnedControlPoint(team);
return (abs(iFarthestPoint - index) <= 1);
}
// We got a section map
else
{
// Tf2 itself does not seem to have any more code for this, so here goes
return true;
}
}
// Loop through each previous point and see if the team owns it
for (int iPrevPoint = 0; iPrevPoint < MAX_PREVIOUS_POINTS; iPrevPoint++)
{
iPointNeeded = GetPreviousPointForPoint(index, team, iPrevPoint);
if (iPointNeeded != -1)
{
// We don't own the needed points
if (GET_OWNING_TEAM(iPointNeeded) != team)
return false;
}
}
return true;
}
// Don't constantly update the cap status
static Timer capstatus_update{};
// Update the control points
void UpdateControlPoints()
{
// No objective ressource, can't run
if (!objective_resource)
return;
int num_cp = CE_INT(objective_resource, netvar.m_iNumControlPoints);
// No control points
if (!num_cp)
return;
// Clear the invalid controlpoints
if (num_cp <= MAX_CONTROL_POINTS)
for (int i = num_cp; i < MAX_CONTROL_POINTS; i++)
controlpoint_data.at(i) = cp_info();
for (int i = 0; i < num_cp; i++)
{
auto &data = controlpoint_data.at(i);
data.cp_index = i;
// Update position (m_vCPPositions[index])
data.position = (&CE_VAR(objective_resource, netvar.m_vCPPositions, Vector))[i];
}
if (capstatus_update.test_and_set(1000))
for (int i = 0; i < num_cp; i++)
{
auto &data = controlpoint_data.at(i);
// Check accessibility for both teams, requires alot of checks
data.can_cap.at(0) = isPointUseable(i, TEAM_RED);
data.can_cap.at(1) = isPointUseable(i, TEAM_BLU);
}
}
// Get the closest controlpoint to cap
std::optional<Vector> getClosestControlPoint(Vector source, int team)
{
// No resource for it
if (!objective_resource)
return std::nullopt;
// Check if it's a cp map
static auto tf_gamemode_cp = g_ICvar->FindVar("tf_gamemode_cp");
if (!tf_gamemode_cp)
{
tf_gamemode_cp = g_ICvar->FindVar("tf_gamemode_cp");
return std::nullopt;
}
if (!tf_gamemode_cp->GetBool())
return std::nullopt;
// Map team to 0-1 and check If Valid
int team_idx = team - TEAM_RED;
if (team_idx < 0 || team_idx > 1)
return std::nullopt;
// No controlpoints
if (!GET_NUM_CONTROL_POINTS())
return std::nullopt;
int ignore_index = -1;
// Do the points need checking because of the map?
auto levelname = GetLevelName();
for (auto &ignore : ignore_points)
{
// Try to find map name in bad point array
if (levelname.find(ignore.mapname) != levelname.npos)
ignore_index = ignore.point_idx;
}
// Find the best and closest control point
std::optional<Vector> best_cp;
float best_distance = FLT_MAX;
for (auto &cp : controlpoint_data)
{
// Ignore this point
if (cp.cp_index == ignore_index)
continue;
// They can cap
if (cp.can_cap.at(team_idx))
{
// Is it closer?
if (cp.position && (*cp.position).DistTo(source) < best_distance)
{
best_distance = (*cp.position).DistTo(source);
best_cp = cp.position;
}
}
}
return best_cp;
}
void LevelInit()
{
for (auto &cp : controlpoint_data)
cp = cp_info();
objective_resource = nullptr;
}
static InitRoutine init([]() {
EC::Register(EC::CreateMove, UpdateObjectiveResource, "cpcontroller_updateent");
EC::Register(EC::CreateMove, UpdateControlPoints, "cpcontroller_updatecp");
EC::Register(EC::LevelInit, LevelInit, "levelinit_cocontroller");
});
} // namespace cpcontroller

View File

@ -70,7 +70,6 @@ void logging::Info(const char *fmt, ...)
va_end(list);
if (size < 0)
return;
Log(result, false);
#endif
}

View File

@ -111,6 +111,17 @@ void NetVars::Init()
this->m_bMiniBuilding = gNetvars.get_offset("DT_BaseObject", "m_bMiniBuilding");
this->m_bPlasmaDisable = gNetvars.get_offset("DT_BaseObject", "m_bPlasmaDisable");
// 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");
this->m_bBuilding = gNetvars.get_offset("DT_BaseObject", "m_bBuilding");
this->m_bCarryDeploy = gNetvars.get_offset("DT_BaseObject", "m_bCarryDeploy");
this->m_iObjectType = gNetvars.get_offset("DT_BaseObject", "m_iObjectType");
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");
@ -119,6 +130,21 @@ void NetVars::Init()
this->m_flTeleYawToExit = gNetvars.get_offset("DT_ObjectTeleporter", "m_flYawToExit");
this->m_bMatchBuilding = gNetvars.get_offset("DT_ObjectTeleporter", "m_bMatchBuilding");
// CTF Flag
this->m_nFlagType = gNetvars.get_offset("DT_CaptureFlag", "m_nType");
this->m_nFlagStatus = gNetvars.get_offset("DT_CaptureFlag", "m_nFlagStatus");
// ObjectiveResource
this->m_bTeamCanCap = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_bTeamCanCap");
this->m_iNumControlPoints = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_iNumControlPoints");
this->m_vCPPositions = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_vCPPositions[0]");
this->m_iOwningTeam = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_iOwner");
this->m_bCPLocked = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_bCPLocked");
this->m_bPlayingMiniRounds = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_bPlayingMiniRounds");
this->m_bInMiniRound = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_bInMiniRound");
this->m_iPreviousPoints = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_iPreviousPoints");
this->m_iBaseControlPoints = gNetvars.get_offset("DT_BaseTeamObjectiveResource", "m_iBaseControlPoints");
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");

156
src/flagcontroller.cpp Normal file
View File

@ -0,0 +1,156 @@
#include "flagcontroller.hpp"
#include "common.hpp"
namespace flagcontroller
{
std::array<flag_info, 2> flags;
bool is_ctf = true;
// Check if a flag is good or not
bool isGoodFlag(CachedEntity *flag)
{
if (CE_INVALID(flag) || flag->m_iClassID() != CL_CLASS(CCaptureFlag))
return false;
return true;
}
void Update()
{
// Not ctf, no need to update
if (!is_ctf)
return;
// Find flags if missing
if (!flags[0].ent || !flags[1].ent)
for (int i = g_IEngine->GetMaxClients() + 1; i < MAX_ENTITIES; i++)
{
CachedEntity *ent = ENTITY(i);
// We cannot identify a bad entity as a flag due to the unreliability of it
if (CE_BAD(ent) || ent->m_iClassID() != CL_CLASS(CCaptureFlag))
continue;
// Store flags
if (!flags[0].ent)
flags[0].ent = ent;
else if (ent != flags[0].ent)
flags[1].ent = ent;
}
// Update flag data
for (auto &flag : flags)
{
// Not inited
if (!flag.ent)
continue;
// Bad Flag, reset
if (!isGoodFlag(flag.ent))
{
flag = flag_info();
continue;
}
// Cannot use "bad" flag, but it is still potentially valid
if (CE_BAD(flag.ent))
continue;
int flag_type = CE_INT(flag.ent, netvar.m_nFlagType);
// Only CTF support for now
if (flag_type != TF_FLAGTYPE_CTF)
continue;
// Assign team if missing
if (flag.team == TEAM_UNK)
flag.team = flag.ent->m_iTeam();
// Assign spawn point if it is missing and the flag is at spawn
if (!flag.spawn_pos)
{
int flag_status = CE_INT(flag.ent, netvar.m_nFlagStatus);
// Flag is home
if (flag_status == TF_FLAGINFO_HOME)
flag.spawn_pos = flag.ent->m_vecOrigin();
}
}
}
void LevelInit()
{
// Resez everything
for (auto &flag : flags)
flag = flag_info();
is_ctf = true;
}
// Get the info for the flag
flag_info getFlag(int team)
{
for (auto &flag : flags)
{
if (flag.team == team)
return flag;
}
// None found
return flag_info();
}
// Get the Position of a flag on a specific team
Vector getPosition(CachedEntity *flag)
{
return flag->m_vecOrigin();
}
std::optional<Vector> getPosition(int team)
{
auto flag = getFlag(team);
if (isGoodFlag(flag.ent))
return getPosition(flag.ent);
// No good flag
return std::nullopt;
}
// Get the person carrying the flag
CachedEntity *getCarrier(CachedEntity *flag)
{
int entidx = HandleToIDX(CE_INT(flag, netvar.m_hOwnerEntity));
// None/Invalid
if (IDX_BAD(entidx))
return nullptr;
CachedEntity *carrier = ENTITY(entidx);
// Carrier is invalid
if (CE_BAD(carrier) || carrier->m_Type() != ENTITY_PLAYER)
return nullptr;
return carrier;
}
// Wrapper for when you don't have the entity
CachedEntity *getCarrier(int team)
{
auto flag = getFlag(team);
// Only use good flags
if (isGoodFlag(flag.ent))
return getCarrier(flag.ent);
return nullptr;
}
// Get the status of the flag (Home, being carried, dropped)
ETFFlagStatus getStatus(CachedEntity *flag)
{
return (ETFFlagStatus) CE_INT(flag, netvar.m_nFlagStatus);
}
ETFFlagStatus getStatus(int team)
{
auto flag = getFlag(team);
// Only use good flags
if (isGoodFlag(flag.ent))
return getStatus(flag.ent);
// Mark as home if nothing is found
return TF_FLAGINFO_HOME;
}
static InitRoutine init([]() {
EC::Register(EC::CreateMove, Update, "flagcontroller_update");
EC::Register(EC::LevelInit, LevelInit, "flagcontroller_levelinit");
});
} // namespace flagcontroller

View File

@ -223,6 +223,7 @@ static void doAutoZoom(bool target_found)
// Current Entity
CachedEntity *target_last = 0;
bool aimed_this_tick = false;
// If slow aimbot allows autoshoot
bool slow_can_shoot = false;
@ -243,6 +244,8 @@ static void CreateMove()
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
enable = false;
aimed_this_tick = false;
if (!enable)
{
target_last = nullptr;
@ -989,6 +992,7 @@ void Aim(CachedEntity *entity)
if (data)
hacks::tf2::backtrack::SetBacktrackData(entity, *data);
}
aimed_this_tick = true;
// Finish function
return;
}
@ -1522,6 +1526,12 @@ float EffectiveTargetingRange()
return (float) max_range;
}
// Used mostly by navbot to not accidentally look at path when aiming
bool isAiming()
{
return aimed_this_tick;
}
// A function used by gui elements to determine the current target
CachedEntity *CurrentTarget()
{

View File

@ -369,6 +369,9 @@ void ProcessUserCmd(CUserCmd *cmd)
return;
if (!ShouldAA(cmd))
return;
// Not running
if (!pitch && !yaw)
return;
static bool keepmode = true;
keepmode = !keepmode;
float &p = cmd->viewangles.x;

View File

@ -327,7 +327,7 @@ Upgradeinfo PickUpgrade()
}
static std::vector<Posinfo> spot_list;
// Upgrade Navigation
void NavUpgrade()
/*void NavUpgrade()
{
std::string lvlname = g_IEngine->GetLevelName();
std::vector<Posinfo> potential_spots{};
@ -359,6 +359,7 @@ void NavUpgrade()
return;
}
}
static bool run = false;
static Timer run_delay;
static Timer buy_upgrade;
@ -479,16 +480,17 @@ void MvM_Autoupgrade(KeyValues *event)
run_delay.update();
}
}
*/
void SendNetMsg(INetMessage &msg)
{
/*
if (!strcmp(msg.GetName(), "clc_CmdKeyValues"))
{
if ((KeyValues *) (((unsigned *) &msg)[4]))
MvM_Autoupgrade((KeyValues *) (((unsigned *) &msg)[4]));
}*/
}
}
/*
class CatBotEventListener : public IGameEventListener2
{
void FireGameEvent(IGameEvent *event) override
@ -509,7 +511,7 @@ CatBotEventListener &listener()
{
static CatBotEventListener object{};
return object;
}
}*/
class CatBotEventListener2 : public IGameEventListener2
{
@ -912,7 +914,7 @@ void update()
void init()
{
g_IEventManager2->AddListener(&listener(), "player_death", false);
// g_IEventManager2->AddListener(&listener(), "player_death", false);
g_IEventManager2->AddListener(&listener2(), "vote_maps_changed", false);
}
@ -924,7 +926,7 @@ void level_init()
void shutdown()
{
g_IEventManager2->RemoveListener(&listener());
// g_IEventManager2->RemoveListener(&listener());
g_IEventManager2->RemoveListener(&listener2());
}

View File

@ -37,8 +37,6 @@ static settings::Boolean ignore_textmode{ "follow-bot.ignore-textmode", "true" }
static settings::Boolean mimic_crouch{ "follow-bot.mimic-crouch", "true" };
static settings::Boolean autozoom_if_idle{ "follow-bot.autozoom-if-idle", "true" };
namespace nb = hacks::tf2::NavBot;
static Timer navBotInterval{};
static unsigned steamid = 0x0;
@ -270,7 +268,7 @@ static bool startFollow(CachedEntity *entity, bool useNavbot)
}
if (useNavbot)
{
if (nav::navTo(entity->m_vecOrigin(), 8, true, false))
if (navparser::NavEngine::navTo(entity->m_vecOrigin(), Priority_list::followbot, true, false))
{
navtarget = true;
return true;
@ -310,14 +308,14 @@ static void cm()
if (!enable || CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
{
follow_target = 0;
if (nb::task::current_task == nb::task::followbot)
nb::task::current_task = nb::task::none;
if (navparser::NavEngine::current_priority == Priority_list::followbot)
navparser::NavEngine::cancelPath();
return;
}
if (!inited)
init();
if (nb::task::current_task == nb::task::health || nb::task::current_task == nb::task::ammo)
if (navparser::NavEngine::current_priority > Priority_list::followbot)
{
follow_target = 0;
return;
@ -343,7 +341,7 @@ static void cm()
crouch_timer.update();
}
bool isNavBotCM = navBotInterval.test_and_set(3000) && nav::prepare();
bool isNavBotCM = navBotInterval.test_and_set(3000) && navparser::NavEngine::isReady();
bool foundPreferredTarget = false;
// Target Selection
@ -467,18 +465,6 @@ static void cm()
}
if (entity->m_bEnemy())
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;*/
// favor closer entitys
if (CE_GOOD(entity))
{
@ -512,7 +498,7 @@ static void cm()
if (navtarget)
{
auto ent = ENTITY(follow_target);
if (!nav::prepare())
if (!navparser::NavEngine::isReady())
{
follow_target = 0;
navtarget = 0;
@ -531,7 +517,7 @@ static void cm()
}
if (pos && navtimer.test_and_set(800))
{
if (nav::navTo(*pos, 8, true, false))
if (navparser::NavEngine::navTo(*pos, Priority_list::followbot, true, false))
navinactivity.update();
}
}
@ -539,7 +525,6 @@ static void cm()
{
follow_target = 0;
}
nb::task::current_task = nb::task::followbot;
return;
}
}
@ -547,13 +532,11 @@ static void cm()
// last check for entity before we continue
if (!follow_target)
{
if (nb::task::current_task == nb::task::followbot)
nb::task::current_task = nb::task::none;
if (navparser::NavEngine::current_priority == Priority_list::followbot)
navparser::NavEngine::cancelPath();
return;
}
nb::task::current_task = nb::task::followbot;
nav::clearInstructions();
navparser::NavEngine::cancelPath();
CachedEntity *followtar = ENTITY(follow_target);
// wtf is this needed

View File

@ -630,7 +630,7 @@ void DumpRecvTable(CachedEntity *ent, RecvTable *table, int depth, const char *f
logging::Info("TABLE %s IN DEPTH %d: %s [0x%04x] = %i | %u | %hd | %hu", table ? table->GetName() : "none", depth, prop->GetName(), prop->GetOffset(), CE_INT(ent, acc_offset + prop->GetOffset()), CE_VAR(ent, acc_offset + prop->GetOffset(), unsigned int), CE_VAR(ent, acc_offset + prop->GetOffset(), short), CE_VAR(ent, acc_offset + prop->GetOffset(), unsigned short));
break;
case SendPropType::DPT_String:
logging::Info("TABLE %s IN DEPTH %d: %s [0x%04x] = %s", table ? table->GetName() : "none", depth, prop->GetName(), prop->GetOffset(), CE_VAR(ent, prop->GetOffset(), char *));
logging::Info("TABLE %s IN DEPTH %d: %s [0x%04x] = 'not yet supported'", table ? table->GetName() : "none", depth, prop->GetName(), prop->GetOffset());
break;
case SendPropType::DPT_Vector:
logging::Info("TABLE %s IN DEPTH %d: %s [0x%04x] = (%f, %f, %f)", table ? table->GetName() : "none", depth, prop->GetName(), prop->GetOffset(), CE_FLOAT(ent, acc_offset + prop->GetOffset()), CE_FLOAT(ent, acc_offset + prop->GetOffset() + 4), CE_FLOAT(ent, acc_offset + prop->GetOffset() + 8));

File diff suppressed because it is too large Load Diff

View File

@ -120,8 +120,9 @@ void WalkTo(const Vector &vector)
// Calculate how to get to a vector
auto result = ComputeMove(LOCAL_E->m_vecOrigin(), vector);
// Push our move to usercmd
current_user_cmd->forwardmove = result.first;
current_user_cmd->sidemove = result.second;
current_user_cmd->forwardmove = result.x;
current_user_cmd->sidemove = result.y;
current_user_cmd->upmove = result.z;
}
// Function to get the corner location that a vischeck to an entity is possible
@ -436,7 +437,6 @@ bool canReachVector(Vector loc, Vector dest)
std::string GetLevelName()
{
std::string name(g_IEngine->GetLevelName());
size_t slash = name.find('/');
if (slash == std::string::npos)
@ -465,11 +465,11 @@ std::pair<float, float> ComputeMovePrecise(const Vector &a, const Vector &b)
return { cos(yaw) * speed, -sin(yaw) * speed };
}
std::pair<float, float> ComputeMove(const Vector &a, const Vector &b)
Vector ComputeMove(const Vector &a, const Vector &b)
{
Vector diff = (b - a);
if (diff.Length() == 0.0f)
return { 0, 0 };
return Vector(0.0f);
const float x = diff.x;
const float y = diff.y;
Vector vsilent(x, y, 0);
@ -477,9 +477,18 @@ std::pair<float, float> ComputeMove(const Vector &a, const Vector &b)
Vector ang;
VectorAngles(vsilent, ang);
float yaw = DEG2RAD(ang.y - current_user_cmd->viewangles.y);
float pitch = DEG2RAD(ang.x - current_user_cmd->viewangles.x);
if (g_pLocalPlayer->bUseSilentAngles)
{
yaw = DEG2RAD(ang.y - g_pLocalPlayer->v_OrigViewangles.y);
return { cos(yaw) * 450.0f, -sin(yaw) * 450.0f };
pitch = DEG2RAD(ang.x - g_pLocalPlayer->v_OrigViewangles.x);
}
Vector move = { cos(yaw) * 450.0f, -sin(yaw) * 450.0f, -cos(pitch) * 450.0f };
// Only apply upmove in water
if (!(g_ITrace->GetPointContents(g_pLocalPlayer->v_Eye) & CONTENTS_WATER))
move.z = current_user_cmd->upmove;
return move;
}
ConCommand *CreateConCommand(const char *name, FnCommandCallback_t callback, const char *help)
@ -1446,7 +1455,6 @@ Vector GetForwardVector(Vector origin, Vector viewangles, float distance, Cached
// Compensate for punch angle
if (punch_entity && should_correct_punch)
angle += VectorToQAngle(CE_VECTOR(punch_entity, netvar.vecPunchAngle));
trace_t trace;
sy = sinf(DEG2RAD(angle[1]));
cy = cosf(DEG2RAD(angle[1]));

View File

@ -239,10 +239,6 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
if (firstcm)
{
DelayTimer.update();
// hacks::tf2::NavBot::Init();
// hacks::tf2::NavBot::initonce();
nav::status = nav::off;
hacks::tf2::NavBot::init(true);
if (identify)
{
sendIdentifyMessage(false);

View File

@ -88,14 +88,23 @@ void LocalPlayer::Update()
holding_sniper_rifle = false;
holding_sapper = false;
weapon_melee_damage_tick = false;
bRevving = false;
bRevved = false;
wep = weapon();
if (CE_GOOD(wep))
{
weapon_mode = GetWeaponModeloc();
if (wep->m_iClassID() == CL_CLASS(CTFSniperRifle) || wep->m_iClassID() == CL_CLASS(CTFSniperRifleDecap))
holding_sniper_rifle = true;
if (wep->m_iClassID() == CL_CLASS(CTFWeaponBuilder) || wep->m_iClassID() == CL_CLASS(CTFWeaponSapper))
else if (wep->m_iClassID() == CL_CLASS(CTFWeaponBuilder) || wep->m_iClassID() == CL_CLASS(CTFWeaponSapper))
holding_sapper = true;
else if (wep->m_iClassID() == CL_CLASS(CTFMinigun))
{
if (CE_INT(LOCAL_W, netvar.iWeaponState) == 2 || CE_INT(LOCAL_W, netvar.iWeaponState) == 1)
bRevving = true;
else if (CE_INT(LOCAL_W, netvar.iWeaponState) == 3)
bRevved = true;
}
// Detect when a melee hit will result in damage, useful for aimbot and antiaim
if (CE_FLOAT(wep, netvar.flNextPrimaryAttack) > g_GlobalVars->curtime && weapon_mode == weapon_melee)
{

File diff suppressed because it is too large Load Diff

69
src/payloadcontroller.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "payloadcontroller.hpp"
#include "common.hpp"
namespace plcontroller
{
// Array that controls all the payloads for each team. Red team is first, then comes blue team.
static std::array<std::vector<CachedEntity *>, 2> payloads;
static Timer update_payloads{};
void Update()
{
// We should update the payload list
if (update_payloads.test_and_set(3000))
{
// Reset entries
for (auto &entry : payloads)
entry.clear();
for (int i = g_IEngine->GetMaxClients() + 1; i < MAX_ENTITIES; i++)
{
CachedEntity *ent = ENTITY(i);
// Not the object we need or invalid (team)
if (CE_BAD(ent) || ent->m_iClassID() != CL_CLASS(CObjectCartDispenser) || ent->m_iTeam() < TEAM_RED || ent->m_iTeam() > TEAM_BLU)
continue;
int team = ent->m_iTeam();
// Add new entry for the team
payloads.at(team - TEAM_RED).push_back(ent);
}
}
}
std::optional<Vector> getClosestPayload(Vector source, int team)
{
// Invalid team
if (team < TEAM_RED || team > TEAM_BLU)
return std::nullopt;
// Convert to index
int index = team - TEAM_RED;
auto entry = payloads[index];
float best_distance = FLT_MAX;
std::optional<Vector> best_pos;
// Find best payload
for (auto payload : entry)
{
if (CE_BAD(payload) || payload->m_iClassID() != CL_CLASS(CObjectCartDispenser))
continue;
if (payload->m_vecOrigin().DistTo(source) < best_distance)
{
best_pos = payload->m_vecOrigin();
best_distance = payload->m_vecOrigin().DistTo(source);
}
}
return best_pos;
}
void LevelInit()
{
for (auto &entry : payloads)
entry.clear();
}
static InitRoutine init([]() {
EC::Register(EC::CreateMove, Update, "plcreatemove");
EC::Register(EC::LevelInit, LevelInit, "levelinit_plcontroller");
});
} // namespace plcontroller

View File

@ -226,11 +226,15 @@ struct migration_struct
};
/* clang-format off */
// Use one per line, from -> to
static std::array<migration_struct, 4> migrations({
static std::array<migration_struct, 8> migrations({
migration_struct{ "misc.semi-auto", "misc.full-auto" },
migration_struct{ "cat-bot.abandon-if.bots-gte", "cat-bot.abandon-if.ipc-bots-gte" },
migration_struct{ "votelogger.partysay-casts", "votelogger.chat.casts" },
migration_struct{ "votelogger.partysay-casts.f1-only", "votelogger.chat.casts.f1-only" }
migration_struct{ "votelogger.partysay-casts.f1-only", "votelogger.chat.casts.f1-only" },
migration_struct{ "misc.pathing", "nav.enabled" },
migration_struct{ "misc.pathing.draw", "nav.draw" },
migration_struct{ "misc.pathing.log", "nav.log"},
migration_struct{ "misc.pathing.look-at-path", "nav.look-at-path"}
});
/* clang-format on */
void settings::SettingsReader::finishString(bool complete)

View File

@ -22,11 +22,22 @@ int GetScoreForEntity(CachedEntity *entity)
{
if (!entity)
return 0;
// TODO
if (entity->m_Type() == ENTITY_BUILDING)
{
if (entity->m_iClassID() == CL_CLASS(CObjectSentrygun))
{
bool is_strong_class = g_pLocalPlayer->clazz == tf_heavy || g_pLocalPlayer->clazz == tf_soldier;
if (is_strong_class)
{
float distance = (g_pLocalPlayer->v_Origin - entity->m_vecOrigin()).Length();
if (distance < 400.0f)
return 120;
else if (distance < 1100.0f)
return 60;
return 30;
}
return 1;
}
return 0;
@ -94,7 +105,7 @@ int GetScoreForEntity(CachedEntity *entity)
total = 999;
if (IsSentryBuster(entity))
total = 0;
if (clazz == tf_medic)
total = 999; // TODO only for mvm
if (clazz == tf_medic && g_pGameRules->isPVEMode)
total = 999;
return total;
}