Merge branch 'nav_improve' into octopus-merge
This commit is contained in:
commit
962dc06492
@ -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]
|
||||||
@ -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;
|
||||||
|
@ -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
|
||||||
|
*/
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
@ -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));
|
||||||
|
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)
|
||||||
@ -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]));
|
||||||
|
@ -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);
|
||||||
|
@ -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