Merge branch 'nav_improve' into octopus-merge

This commit is contained in:
TotallyNotElite 2020-12-28 12:49:49 +01:00
commit 962dc06492
30 changed files with 3193 additions and 2091 deletions

View File

@ -30,20 +30,25 @@
</Box> </Box>
<Box padding="12 6 6 6" width="content" height="content" name="NavBot" y="65"> <Box padding="12 6 6 6" width="content" height="content" name="NavBot" y="65">
<List width="150"> <List width="150">
<AutoVariable width="fill" target="misc.pathing" label="Enable pathing"/> <AutoVariable width="fill" target="nav.enabled" label="Enable pathing"/>
<AutoVariable width="fill" target="misc.pathing.draw" label="Draw path"/> <AutoVariable width="fill" target="nav.draw" label="Draw path"/>
<AutoVariable width="fill" target="misc.pathing.look-at-path" label="Look at 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.enabled" label="Enable NavBot"/>
<AutoVariable width="fill" target="navbot.engineer-mode" label="Enable Engineer 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.get-health-and-ammo" label="Get health and ammo"/> <!--AutoVariable width="fill" target="navbot.engineer-mode" label="Enable Engineer mode"/-->
<AutoVariable width="fill" target="navbot.spy-mode" label="Enable Spy mode"/> <AutoVariable width="fill" target="navbot.search-health" label="Search health"/>
<AutoVariable width="fill" target="navbot.other-mode" label="General mode"/> <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.primary-only" label="Best weapon only"/>
<AutoVariable width="fill" target="navbot.autojump.enabled" label="Enable autojump"/> <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.autojump.trigger-distance" label="Jump distance"/>
<!--AutoVariable width="fill" target="navbot.target-sentry" label="Try to target sentries"/--> <AutoVariable width="fill" target="navbot.snipe-sentries" label="Try to target sentries"/>
<AutoVariable width="fill" target="navbot.stay-near" label="Zone enemies (stay near)"/> <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.spy-ignore-time" label="Spy ignore time"/> <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"/> --> <!-- <!AutoVariable width="fill" target="navbot.take-teleporters" label="take teleporters"/> -->
</List> </List>
</Box> </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 "core/sharedobj.hpp"
#include "init.hpp" #include "init.hpp"
#include "reclasses/reclasses.hpp" #include "reclasses/reclasses.hpp"
#include <CNavFile.h>
#include "HookTools.hpp" #include "HookTools.hpp"
#include "bytepatch.hpp" #include "bytepatch.hpp"

View File

@ -83,6 +83,7 @@ public:
offset_t m_bPlacing; offset_t m_bPlacing;
offset_t m_bBuilding; offset_t m_bBuilding;
offset_t m_bPlasmaDisable; offset_t m_bPlasmaDisable;
offset_t m_bCarryDeploy;
// teleporter // teleporter
offset_t m_iTeleState; // teleport state [1 = idle, 2 = active, 3 = teleporting, 4 = charging] offset_t m_iTeleState; // teleport state [1 = idle, 2 = active, 3 = teleporting, 4 = charging]
@ -204,6 +205,19 @@ public:
offset_t m_iHealingAssist_Resource; offset_t m_iHealingAssist_Resource;
offset_t m_iPlayerLevel_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_iPlayerIndex;
offset_t m_hTargetPlayer; offset_t m_hTargetPlayer;
offset_t m_flResetTime; offset_t m_flResetTime;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,42 +1,101 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "mathlib/vector.h" #include <vector>
#include "CNavFile.h"
class CNavFile; enum Priority_list
class CNavArea;
namespace nav
{ {
patrol = 5,
enum init_status : uint8_t lowprio_health,
{ staynear,
off = 0, snipe_sentry,
unavailable, followbot,
initing, ammo,
on capture,
health,
danger,
}; };
// Call prepare first and check its return value namespace navparser
extern std::unique_ptr<CNavFile> navfile; {
constexpr float PLAYER_WIDTH = 49;
constexpr float HALF_PLAYER_WIDTH = PLAYER_WIDTH / 2.0f;
constexpr float PLAYER_JUMP_HEIGHT = 72.0f;
// Current path priority #define TICKCOUNT_TIMESTAMP(seconds) (g_GlobalVars->tickcount + int(seconds / g_GlobalVars->interval_per_tick))
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;
// Nav to vector // Basic Blacklist reasons, you can add your own externally and use them
bool navTo(const Vector &destination, int priority = 5, bool should_repath = true, bool nav_to_local = true, bool is_repath = false); enum BlacklistReason_enum
// Find closest to vector area {
CNavArea *findClosestNavSquare(const Vector &vec); SENTRY,
// Check and init navparser STICKY,
bool prepare(); ENEMY_NORMAL,
// Clear current path ENEMY_DORMANT,
void clearInstructions(); // Always last
// Check if area is safe from stickies and sentries BLACKLIST_LENGTH
bool isSafe(CNavArea *area); };
} // 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 roundmode; // 48 | 4 bytes | 52
int pad1[1]; // 52 | 4 bytes | 56 int pad1[1]; // 52 | 4 bytes | 56
int winning_team; // 56 | 4 bytes | 60 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" set(files "${CMAKE_CURRENT_LIST_DIR}/angles.cpp"
"${CMAKE_CURRENT_LIST_DIR}/CaptureLogic.cpp"
"${CMAKE_CURRENT_LIST_DIR}/chatlog.cpp" "${CMAKE_CURRENT_LIST_DIR}/chatlog.cpp"
"${CMAKE_CURRENT_LIST_DIR}/chatstack.cpp" "${CMAKE_CURRENT_LIST_DIR}/chatstack.cpp"
"${CMAKE_CURRENT_LIST_DIR}/conditions.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

@ -68,9 +68,8 @@ void logging::Info(const char *fmt, ...)
// Fill buffer // Fill buffer
int size = vsnprintf(result, 512, fmt, list); int size = vsnprintf(result, 512, fmt, list);
va_end(list); va_end(list);
if(size < 0) if (size < 0)
return; return;
Log(result, false); Log(result, false);
#endif #endif
} }
@ -88,7 +87,7 @@ void logging::File(const char *fmt, ...)
// Fill buffer // Fill buffer
int size = vsnprintf(result, 512, fmt, list); int size = vsnprintf(result, 512, fmt, list);
va_end(list); va_end(list);
if(size < 0) if (size < 0)
return; return;
Log(result, true); Log(result, true);

View File

@ -111,6 +111,17 @@ void NetVars::Init()
this->m_bMiniBuilding = gNetvars.get_offset("DT_BaseObject", "m_bMiniBuilding"); this->m_bMiniBuilding = gNetvars.get_offset("DT_BaseObject", "m_bMiniBuilding");
this->m_bPlasmaDisable = gNetvars.get_offset("DT_BaseObject", "m_bPlasmaDisable"); 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 // teleporter
this->m_iTeleState = gNetvars.get_offset("DT_ObjectTeleporter", "m_iState"); this->m_iTeleState = gNetvars.get_offset("DT_ObjectTeleporter", "m_iState");
this->m_flTeleRechargeTime = gNetvars.get_offset("DT_ObjectTeleporter", "m_flRechargeTime"); 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_flTeleYawToExit = gNetvars.get_offset("DT_ObjectTeleporter", "m_flYawToExit");
this->m_bMatchBuilding = gNetvars.get_offset("DT_ObjectTeleporter", "m_bMatchBuilding"); 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->m_DmgRadius = gNetvars.get_offset("DT_BaseGrenade", "m_DmgRadius");
this->iPipeType = gNetvars.get_offset("DT_TFProjectile_Pipebomb", "m_iType"); this->iPipeType = gNetvars.get_offset("DT_TFProjectile_Pipebomb", "m_iType");
this->iBuildingHealth = gNetvars.get_offset("DT_BaseObject", "m_iHealth"); 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

@ -217,6 +217,7 @@ static void doAutoZoom(bool target_found)
// Current Entity // Current Entity
CachedEntity *target_last = 0; CachedEntity *target_last = 0;
bool aimed_this_tick = false;
// If slow aimbot allows autoshoot // If slow aimbot allows autoshoot
bool slow_can_shoot = false; bool slow_can_shoot = false;
@ -237,6 +238,8 @@ static void CreateMove()
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W)) if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
enable = false; enable = false;
aimed_this_tick = false;
if (!enable) if (!enable)
{ {
target_last = nullptr; target_last = nullptr;
@ -982,6 +985,7 @@ void Aim(CachedEntity *entity)
if (data) if (data)
hacks::tf2::backtrack::SetBacktrackData(entity, *data); hacks::tf2::backtrack::SetBacktrackData(entity, *data);
} }
aimed_this_tick = true;
// Finish function // Finish function
return; return;
} }
@ -1515,6 +1519,12 @@ float EffectiveTargetingRange()
return (float) max_range; 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 // A function used by gui elements to determine the current target
CachedEntity *CurrentTarget() CachedEntity *CurrentTarget()
{ {

View File

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

View File

@ -327,7 +327,7 @@ Upgradeinfo PickUpgrade()
} }
static std::vector<Posinfo> spot_list; static std::vector<Posinfo> spot_list;
// Upgrade Navigation // Upgrade Navigation
void NavUpgrade() /*void NavUpgrade()
{ {
std::string lvlname = g_IEngine->GetLevelName(); std::string lvlname = g_IEngine->GetLevelName();
std::vector<Posinfo> potential_spots{}; std::vector<Posinfo> potential_spots{};
@ -359,6 +359,7 @@ void NavUpgrade()
return; return;
} }
} }
static bool run = false; static bool run = false;
static Timer run_delay; static Timer run_delay;
static Timer buy_upgrade; static Timer buy_upgrade;
@ -479,16 +480,17 @@ void MvM_Autoupgrade(KeyValues *event)
run_delay.update(); run_delay.update();
} }
} }
*/
void SendNetMsg(INetMessage &msg) void SendNetMsg(INetMessage &msg)
{ {
/*
if (!strcmp(msg.GetName(), "clc_CmdKeyValues")) if (!strcmp(msg.GetName(), "clc_CmdKeyValues"))
{ {
if ((KeyValues *) (((unsigned *) &msg)[4])) if ((KeyValues *) (((unsigned *) &msg)[4]))
MvM_Autoupgrade((KeyValues *) (((unsigned *) &msg)[4])); MvM_Autoupgrade((KeyValues *) (((unsigned *) &msg)[4]));
} }*/
} }
/*
class CatBotEventListener : public IGameEventListener2 class CatBotEventListener : public IGameEventListener2
{ {
void FireGameEvent(IGameEvent *event) override void FireGameEvent(IGameEvent *event) override
@ -509,7 +511,7 @@ CatBotEventListener &listener()
{ {
static CatBotEventListener object{}; static CatBotEventListener object{};
return object; return object;
} }*/
class CatBotEventListener2 : public IGameEventListener2 class CatBotEventListener2 : public IGameEventListener2
{ {
@ -912,7 +914,7 @@ void update()
void init() void init()
{ {
g_IEventManager2->AddListener(&listener(), "player_death", false); // g_IEventManager2->AddListener(&listener(), "player_death", false);
g_IEventManager2->AddListener(&listener2(), "vote_maps_changed", false); g_IEventManager2->AddListener(&listener2(), "vote_maps_changed", false);
} }
@ -924,7 +926,7 @@ void level_init()
void shutdown() void shutdown()
{ {
g_IEventManager2->RemoveListener(&listener()); // g_IEventManager2->RemoveListener(&listener());
g_IEventManager2->RemoveListener(&listener2()); 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 mimic_crouch{ "follow-bot.mimic-crouch", "true" };
static settings::Boolean autozoom_if_idle{ "follow-bot.autozoom-if-idle", "true" }; static settings::Boolean autozoom_if_idle{ "follow-bot.autozoom-if-idle", "true" };
namespace nb = hacks::tf2::NavBot;
static Timer navBotInterval{}; static Timer navBotInterval{};
static unsigned steamid = 0x0; static unsigned steamid = 0x0;
@ -270,7 +268,7 @@ static bool startFollow(CachedEntity *entity, bool useNavbot)
} }
if (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; navtarget = true;
return true; return true;
@ -310,14 +308,14 @@ static void cm()
if (!enable || CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W)) if (!enable || CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || CE_BAD(LOCAL_W))
{ {
follow_target = 0; follow_target = 0;
if (nb::task::current_task == nb::task::followbot) if (navparser::NavEngine::current_priority == Priority_list::followbot)
nb::task::current_task = nb::task::none; navparser::NavEngine::cancelPath();
return; return;
} }
if (!inited) if (!inited)
init(); 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; follow_target = 0;
return; return;
@ -343,7 +341,7 @@ static void cm()
crouch_timer.update(); 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; bool foundPreferredTarget = false;
// Target Selection // Target Selection
@ -467,18 +465,6 @@ static void cm()
} }
if (entity->m_bEnemy()) if (entity->m_bEnemy())
continue; 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 // favor closer entitys
if (CE_GOOD(entity)) if (CE_GOOD(entity))
{ {
@ -512,7 +498,7 @@ static void cm()
if (navtarget) if (navtarget)
{ {
auto ent = ENTITY(follow_target); auto ent = ENTITY(follow_target);
if (!nav::prepare()) if (!navparser::NavEngine::isReady())
{ {
follow_target = 0; follow_target = 0;
navtarget = 0; navtarget = 0;
@ -531,7 +517,7 @@ static void cm()
} }
if (pos && navtimer.test_and_set(800)) 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(); navinactivity.update();
} }
} }
@ -539,7 +525,6 @@ static void cm()
{ {
follow_target = 0; follow_target = 0;
} }
nb::task::current_task = nb::task::followbot;
return; return;
} }
} }
@ -547,13 +532,11 @@ static void cm()
// last check for entity before we continue // last check for entity before we continue
if (!follow_target) if (!follow_target)
{ {
if (nb::task::current_task == nb::task::followbot) if (navparser::NavEngine::current_priority == Priority_list::followbot)
nb::task::current_task = nb::task::none; navparser::NavEngine::cancelPath();
return; return;
} }
navparser::NavEngine::cancelPath();
nb::task::current_task = nb::task::followbot;
nav::clearInstructions();
CachedEntity *followtar = ENTITY(follow_target); CachedEntity *followtar = ENTITY(follow_target);
// wtf is this needed // wtf is this needed

View File

@ -638,7 +638,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)); 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; break;
case SendPropType::DPT_String: 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; break;
case SendPropType::DPT_Vector: 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)); 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 // Calculate how to get to a vector
auto result = ComputeMove(LOCAL_E->m_vecOrigin(), vector); auto result = ComputeMove(LOCAL_E->m_vecOrigin(), vector);
// Push our move to usercmd // Push our move to usercmd
current_user_cmd->forwardmove = result.first; current_user_cmd->forwardmove = result.x;
current_user_cmd->sidemove = result.second; 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 // 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 GetLevelName()
{ {
std::string name(g_IEngine->GetLevelName()); std::string name(g_IEngine->GetLevelName());
size_t slash = name.find('/'); size_t slash = name.find('/');
if (slash == std::string::npos) if (slash == std::string::npos)
@ -465,21 +465,30 @@ std::pair<float, float> ComputeMovePrecise(const Vector &a, const Vector &b)
return { cos(yaw) * speed, -sin(yaw) * speed }; 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); Vector diff = (b - a);
if (diff.Length() == 0.0f) if (diff.Length() == 0.0f)
return { 0, 0 }; return Vector(0.0f);
const float x = diff.x; const float x = diff.x;
const float y = diff.y; const float y = diff.y;
Vector vsilent(x, y, 0); Vector vsilent(x, y, 0);
float speed = sqrt(vsilent.x * vsilent.x + vsilent.y * vsilent.y); float speed = sqrt(vsilent.x * vsilent.x + vsilent.y * vsilent.y);
Vector ang; Vector ang;
VectorAngles(vsilent, ang); VectorAngles(vsilent, ang);
float yaw = DEG2RAD(ang.y - current_user_cmd->viewangles.y); float yaw = DEG2RAD(ang.y - current_user_cmd->viewangles.y);
float pitch = DEG2RAD(ang.x - current_user_cmd->viewangles.x);
if (g_pLocalPlayer->bUseSilentAngles) if (g_pLocalPlayer->bUseSilentAngles)
yaw = DEG2RAD(ang.y - g_pLocalPlayer->v_OrigViewangles.y); {
return { cos(yaw) * 450.0f, -sin(yaw) * 450.0f }; yaw = DEG2RAD(ang.y - g_pLocalPlayer->v_OrigViewangles.y);
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) ConCommand *CreateConCommand(const char *name, FnCommandCallback_t callback, const char *help)
@ -1462,7 +1471,6 @@ Vector GetForwardVector(Vector origin, Vector viewangles, float distance, Cached
// Compensate for punch angle // Compensate for punch angle
if (punch_entity && should_correct_punch) if (punch_entity && should_correct_punch)
angle += VectorToQAngle(CE_VECTOR(punch_entity, netvar.vecPunchAngle)); angle += VectorToQAngle(CE_VECTOR(punch_entity, netvar.vecPunchAngle));
trace_t trace;
sy = sinf(DEG2RAD(angle[1])); sy = sinf(DEG2RAD(angle[1]));
cy = cosf(DEG2RAD(angle[1])); cy = cosf(DEG2RAD(angle[1]));

View File

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

View File

@ -88,14 +88,23 @@ void LocalPlayer::Update()
holding_sniper_rifle = false; holding_sniper_rifle = false;
holding_sapper = false; holding_sapper = false;
weapon_melee_damage_tick = false; weapon_melee_damage_tick = false;
bRevving = false;
bRevved = false;
wep = weapon(); wep = weapon();
if (CE_GOOD(wep)) if (CE_GOOD(wep))
{ {
weapon_mode = GetWeaponModeloc(); weapon_mode = GetWeaponModeloc();
if (wep->m_iClassID() == CL_CLASS(CTFSniperRifle) || wep->m_iClassID() == CL_CLASS(CTFSniperRifleDecap)) if (wep->m_iClassID() == CL_CLASS(CTFSniperRifle) || wep->m_iClassID() == CL_CLASS(CTFSniperRifleDecap))
holding_sniper_rifle = true; 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; 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 // 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) 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 */ /* clang-format off */
// Use one per line, from -> to // 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{ "misc.semi-auto", "misc.full-auto" },
migration_struct{ "cat-bot.abandon-if.bots-gte", "cat-bot.abandon-if.ipc-bots-gte" }, 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", "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 */ /* clang-format on */
void settings::SettingsReader::finishString(bool complete) void settings::SettingsReader::finishString(bool complete)

View File

@ -22,11 +22,22 @@ int GetScoreForEntity(CachedEntity *entity)
{ {
if (!entity) if (!entity)
return 0; return 0;
// TODO
if (entity->m_Type() == ENTITY_BUILDING) if (entity->m_Type() == ENTITY_BUILDING)
{ {
if (entity->m_iClassID() == CL_CLASS(CObjectSentrygun)) 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 1;
} }
return 0; return 0;
@ -94,7 +105,7 @@ int GetScoreForEntity(CachedEntity *entity)
total = 999; total = 999;
if (IsSentryBuster(entity)) if (IsSentryBuster(entity))
total = 0; total = 0;
if (clazz == tf_medic) if (clazz == tf_medic && g_pGameRules->isPVEMode)
total = 999; // TODO only for mvm total = 999;
return total; return total;
} }