Major NavBot improvements

- Don't mark World as Potentially Passable
- Don't walk into enemy spawn
- Don't try to exit spawn as blue in setup time
- Don't run to other, blocked sections of the map
This commit is contained in:
BenCat07 2020-05-21 17:10:39 +02:00
parent 29fdc394e6
commit 0b70fc1b50
14 changed files with 200 additions and 10 deletions

View File

@ -54,6 +54,10 @@ public:
// dispenser
offset_t m_iAmmoMetal; // dispenser metal reserve
// round timer
offset_t m_nSetupTimeLength;
offset_t m_nState;
offset_t iLifeState;
offset_t iCond;
offset_t iCond1;

View File

@ -113,6 +113,8 @@ void fClampAngle(Vector &qaAng);
// const char* MakeInfoString(IClientEntity* player);
bool GetProjectileData(CachedEntity *weapon, float &speed, float &gravity);
bool IsVectorVisible(Vector a, Vector b, bool enviroment_only = false, CachedEntity *self = LOCAL_E, unsigned int mask = MASK_SHOT_HULL);
// A Special function for navparser to check if a Vector is visible.
bool IsVectorVisibleNavigation(Vector a, Vector b, CachedEntity *self = LOCAL_E, unsigned int mask = MASK_SHOT_HULL);
Vector GetForwardVector(Vector origin, Vector viewangles, float distance);
Vector GetForwardVector(float distance);
CachedEntity *getClosestEntity(Vector vec);

View File

@ -22,6 +22,11 @@ public:
typedef bool (*fn_t)(IClientEntity *);
return vfunc<fn_t>(self, offsets::PlatformOffset(184, offsets::undefined, 184), 0)(self);
}
inline static bool ShouldCollide(IClientEntity *self, int collisionGroup, int contentsMask)
{
typedef bool (*fn_t)(IClientEntity *, int, int);
return vfunc<fn_t>(self, offsets::PlatformOffset(198, offsets::undefined, 198), 0)(self, collisionGroup, contentsMask);
}
inline static int &m_nPredictionRandomSeed()
{
static int placeholder = 0;

View File

@ -0,0 +1,17 @@
#pragma once
enum round_states
{
RT_STATE_SETUP = 0,
RT_STATE_NORMAL
};
class CTeamRoundTimer
{
public:
int GetSetupTimeLength();
round_states GetRoundState();
void Update();
int entity;
};
extern CTeamRoundTimer *g_pTeamRoundTimer;

View File

@ -41,6 +41,18 @@ public:
void SetSelf(IClientEntity *self);
virtual TraceType_t GetTraceType() const;
};
class FilterNavigation : public ITraceFilter
{
public:
IClientEntity *m_pSelf;
public:
virtual ~FilterNavigation();
FilterNavigation();
virtual bool ShouldHitEntity(IHandleEntity *entity, int mask);
void SetSelf(IClientEntity *self);
virtual TraceType_t GetTraceType() const;
};
class FilterNoEntity : public ITraceFilter
{
@ -72,6 +84,7 @@ public:
extern FilterDefault filter_default;
extern FilterNoPlayer filter_no_player;
extern FilterNavigation filter_navigation;
extern FilterNoEntity filter_no_entity;
extern FilterPenetration filter_penetration;
} // namespace trace

View File

@ -23,6 +23,7 @@ set(files "${CMAKE_CURRENT_LIST_DIR}/angles.cpp"
"${CMAKE_CURRENT_LIST_DIR}/sconvars.cpp"
"${CMAKE_CURRENT_LIST_DIR}/soundcache.cpp"
"${CMAKE_CURRENT_LIST_DIR}/targethelper.cpp"
"${CMAKE_CURRENT_LIST_DIR}/teamroundtimer.cpp"
"${CMAKE_CURRENT_LIST_DIR}/textmode.cpp"
"${CMAKE_CURRENT_LIST_DIR}/tfmm.cpp"
"${CMAKE_CURRENT_LIST_DIR}/trace.cpp"

View File

@ -88,6 +88,10 @@ void NetVars::Init()
// dispenser
this->m_iAmmoMetal = gNetvars.get_offset("DT_ObjectDispenser", "m_iAmmoMetal");
// Round timer
this->m_nSetupTimeLength = gNetvars.get_offset("DT_TeamRoundTimer", "m_nSetupTimeLength");
this->m_nState = gNetvars.get_offset("DT_TeamRoundTimer", "m_nState");
// any building
this->m_iUpgradeMetal = gNetvars.get_offset("DT_BaseObject", "m_iUpgradeMetal");
this->m_flPercentageConstructed = gNetvars.get_offset("DT_BaseObject", "m_flPercentageConstructed");

View File

@ -21,6 +21,7 @@
#include <pwd.h>
#include <hacks/hacklist.hpp>
#include "teamroundtimer.hpp"
#if EXTERNAL_DRAWING
#include "xoverlay.h"
#endif
@ -336,6 +337,7 @@ free(logname);*/
InitNetVars();
g_pLocalPlayer = new LocalPlayer();
g_pPlayerResource = new TFPlayerResource();
g_pTeamRoundTimer = new CTeamRoundTimer();
velocity::Init();
playerlist::Load();

View File

@ -7,6 +7,7 @@
#include "soundcache.hpp"
#include "Misc.hpp"
#include "MiscTemporary.hpp"
#include "teamroundtimer.hpp"
namespace hacks::tf2::NavBot
{
@ -120,6 +121,19 @@ static void CreateMove()
wait_until_path.update();
else
current_task = task::none;
// Check if we should path at all
if (!blocking)
{
round_states round_state = g_pTeamRoundTimer->GetRoundState();
// Still in setuptime, if on fitting team, then do not path yet
if (round_state == RT_STATE_SETUP && g_pLocalPlayer->team == TEAM_BLU)
{
// Clear instructions
if (!nav::ReadyForCommands)
nav::clearInstructions();
return;
}
}
if (autojump)
autoJump();

View File

@ -1338,7 +1338,6 @@ netvar.iHealth));
bool IsVectorVisible(Vector origin, Vector target, bool enviroment_only, CachedEntity *self, unsigned int mask)
{
if (!enviroment_only)
{
trace_t trace_visible;
@ -1363,6 +1362,18 @@ bool IsVectorVisible(Vector origin, Vector target, bool enviroment_only, CachedE
}
}
bool IsVectorVisibleNavigation(Vector origin, Vector target, CachedEntity *self, unsigned int mask)
{
trace_t trace_visible;
Ray_t ray;
trace::filter_no_entity.SetSelf(RAW_ENT(self));
ray.Init(origin, target);
PROF_SECTION(IEVV_TraceRay);
g_ITrace->TraceRay(ray, mask, &trace::filter_navigation, &trace_visible);
return (trace_visible.fraction == 1.0f);
}
void WhatIAmLookingAt(int *result_eindex, Vector *result_pos)
{
Ray_t ray;

View File

@ -15,6 +15,7 @@
#include <hacks/AntiAntiAim.hpp>
#include "NavBot.hpp"
#include "HookTools.hpp"
#include "teamroundtimer.hpp"
#include "HookedMethods.hpp"
#include "PreDataUpdate.hpp"
@ -242,6 +243,10 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
PROF_SECTION(CM_PlayerResource);
g_pPlayerResource->Update();
}
{
PROF_SECTION(CM_TeamTimer);
g_pTeamRoundTimer->Update();
}
{
PROF_SECTION(CM_LocalPlayer);
g_pLocalPlayer->Update();

View File

@ -33,6 +33,8 @@ enum ignore_status : uint8_t
const_ignored,
// LOS between areas is given
vischeck_success,
// LOS if we ignore entities
vischeck_blockedentity,
// No LOS between areas
vischeck_failed,
// Failed to actually walk thru connection
@ -95,13 +97,22 @@ float getZBetweenAreas(CNavArea *start, CNavArea *end)
static std::unordered_map<std::pair<CNavArea *, CNavArea *>, ignoredata, boost::hash<std::pair<CNavArea *, CNavArea *>>> ignores;
namespace ignoremanager
{
static bool vischeck(CNavArea *begin, CNavArea *end)
static ignore_status vischeck(CNavArea *begin, CNavArea *end)
{
Vector first = begin->m_center;
Vector second = end->m_center;
first.z += 70;
second.z += 70;
return IsVectorVisible(first, second, true, LOCAL_E, MASK_PLAYERSOLID);
ignore_status status = vischeck_failed;
// Is world blocking it?
if (IsVectorVisibleNavigation(first, second, LOCAL_E, MASK_PLAYERSOLID))
{
status = vischeck_success;
// Is something else blocking it?
if (!IsVectorVisible(first, second, true, LOCAL_E, MASK_PLAYERSOLID))
status = vischeck_blockedentity;
}
return status;
}
static ignore_status runIgnoreChecks(CNavArea *begin, CNavArea *end)
{
@ -110,10 +121,7 @@ static ignore_status runIgnoreChecks(CNavArea *begin, CNavArea *end)
return const_ignored;
if (!vischecks)
return vischeck_success;
if (vischeck(begin, end))
return vischeck_success;
else
return vischeck_failed;
return vischeck(begin, end);
}
static void updateDanger()
{
@ -250,12 +258,21 @@ static void checkPath()
ignoredata &data = ignores[{ begin, end }];
if (data.status == vischeck_failed)
return;
if (!vischeck(begin, end))
if (data.status == vischeck_blockedentity && vischeckBlock)
return;
auto vis_status = vischeck(begin, end);
if (vis_status == vischeck_failed)
{
data.status = vischeck_failed;
data.ignoreTimeout.update();
perform_repath = true;
}
else if (vis_status == vischeck_blockedentity && vischeckBlock)
{
data.status = vischeck_blockedentity;
data.ignoreTimeout.update();
perform_repath = true;
}
else if (ignores[{ end, nullptr }].status == danger_found)
{
perform_repath = true;
@ -274,7 +291,7 @@ static int isIgnored(CNavArea *begin, CNavArea *end)
status = runIgnoreChecks(begin, end);
if (status == vischeck_success)
return 0;
else if (status == vischeck_failed)
else if (status == vischeck_blockedentity && !vischeckBlock)
return 1;
else
return 2;
@ -352,6 +369,7 @@ static void updateIgnores()
}
break;
case vischeck_failed:
case vischeck_blockedentity:
case vischeck_success:
default:
if (i.second.ignoreTimeout.check(30000))
@ -510,7 +528,7 @@ CNavArea *findClosestNavSquare(const Vector &vec)
bestSquare = &i;
}
// Check if we are within x and y bounds of an area
if (ovBestDist >= dist || !i.IsOverlapping(vec) || !IsVectorVisible(vec, i.m_center, true, LOCAL_E, MASK_PLAYERSOLID))
if (ovBestDist >= dist || !i.IsOverlapping(vec) || !IsVectorVisibleNavigation(vec, i.m_center, LOCAL_E, MASK_PLAYERSOLID))
{
continue;
}
@ -628,6 +646,7 @@ static void cm()
return;
}
ignoremanager::updateIgnores();
auto crumb = crumbs.begin();
const Vector *crumb_vec;
// Crumbs empty, prepare for next instruction

38
src/teamroundtimer.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "common.hpp"
#include "teamroundtimer.hpp"
int CTeamRoundTimer::GetSetupTimeLength()
{
IClientEntity *ent;
ent = g_IEntityList->GetClientEntity(entity);
if (!ent || ent->GetClientClass()->m_ClassID != CL_CLASS(CTeamRoundTimer))
return -1;
return NET_INT(ent, netvar.m_nSetupTimeLength);
};
round_states CTeamRoundTimer::GetRoundState()
{
IClientEntity *ent;
ent = g_IEntityList->GetClientEntity(entity);
if (!ent || ent->GetClientClass()->m_ClassID != CL_CLASS(CTeamRoundTimer))
return RT_STATE_NORMAL;
int state = NET_INT(ent, netvar.m_nState);
return state == 1 ? RT_STATE_NORMAL : RT_STATE_SETUP;
};
void CTeamRoundTimer::Update()
{
IClientEntity *ent;
entity = 0;
for (int i = 0; i <= HIGHEST_ENTITY; i++)
{
ent = g_IEntityList->GetClientEntity(i);
if (ent && ent->GetClientClass()->m_ClassID == CL_CLASS(CTeamRoundTimer))
{
entity = i;
return;
}
}
}
CTeamRoundTimer *g_pTeamRoundTimer{ nullptr };

View File

@ -108,6 +108,60 @@ TraceType_t trace::FilterNoPlayer::GetTraceType() const
{
return TRACE_EVERYTHING;
}
/* Navigation filter */
trace::FilterNavigation::FilterNavigation()
{
m_pSelf = nullptr;
}
trace::FilterNavigation::~FilterNavigation(){};
void trace::FilterNavigation::SetSelf(IClientEntity *self)
{
if (self == nullptr)
{
logging::Info("nullptr in FilterNavigation::SetSelf");
return;
}
m_pSelf = self;
}
#define MOVEMENT_COLLISION_GROUP 8
#define RED_CONTENTS_MASK 0x800
#define BLU_CONTENTS_MASK 0x1000
bool trace::FilterNavigation::ShouldHitEntity(IHandleEntity *handle, int mask)
{
IClientEntity *entity;
ClientClass *clazz;
if (!handle)
return false;
entity = (IClientEntity *) handle;
clazz = entity->GetClientClass();
// Ignore everything that is not the world or a CBaseEntity
if (entity->entindex() != 0 && clazz->m_ClassID != CL_CLASS(CBaseEntity))
{
// Besides respawn room areas, we want to explicitly ignore those if they are not on our team
if (clazz->m_ClassID == CL_CLASS(CFuncRespawnRoomVisualizer))
if (CE_GOOD(LOCAL_E) && (g_pLocalPlayer->team == TEAM_RED || g_pLocalPlayer->team == TEAM_BLU))
{
// If we can't collide, hit it
if (!re::C_BaseEntity::ShouldCollide(entity, MOVEMENT_COLLISION_GROUP, g_pLocalPlayer->team == TEAM_RED ? RED_CONTENTS_MASK : BLU_CONTENTS_MASK))
return true;
}
return false;
}
return true;
}
TraceType_t trace::FilterNavigation::GetTraceType() const
{
return TRACE_EVERYTHING;
}
/* No-Entity filter */
trace::FilterNoEntity::FilterNoEntity()
@ -215,5 +269,6 @@ void trace::FilterPenetration::Reset()
trace::FilterDefault trace::filter_default{};
trace::FilterNoPlayer trace::filter_no_player{};
trace::FilterNavigation trace::filter_navigation{};
trace::FilterNoEntity trace::filter_no_entity{};
trace::FilterPenetration trace::filter_penetration{};