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:
parent
4f1c4bde57
commit
39d2a82c2d
@ -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>
|
||||||
|
2
external/TF2_NavFile_Reader
vendored
2
external/TF2_NavFile_Reader
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 5f2940c4707127db1581e5708eecca1d6130610f
|
Subproject commit 55ae8047a24203cec834f54fd67ee6d571f497f6
|
85
include/CaptureLogic.hpp
Normal file
85
include/CaptureLogic.hpp
Normal 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
|
@ -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"
|
||||||
|
|
||||||
|
@ -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]
|
||||||
@ -205,6 +206,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;
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
*/
|
@ -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 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);
|
||||||
|
|
||||||
|
@ -34,6 +34,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;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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
492
src/CaptureLogic.cpp
Normal 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");
|
||||||
|
});
|
259
src/controlpointcontroller.cpp
Normal file
259
src/controlpointcontroller.cpp
Normal 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
|
@ -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);
|
||||||
|
@ -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
156
src/flagcontroller.cpp
Normal 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
|
@ -223,6 +223,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;
|
||||||
@ -243,6 +244,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;
|
||||||
@ -989,6 +992,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;
|
||||||
}
|
}
|
||||||
@ -1522,6 +1526,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()
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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));
|
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));
|
||||||
|
2246
src/hacks/NavBot.cpp
2246
src/hacks/NavBot.cpp
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||||
@ -1446,7 +1455,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]));
|
||||||
|
@ -239,10 +239,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);
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
1633
src/navparser.cpp
1633
src/navparser.cpp
File diff suppressed because it is too large
Load Diff
69
src/payloadcontroller.cpp
Normal file
69
src/payloadcontroller.cpp
Normal 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
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user