Type 2 connections for navbot (vischeck)
This commit is contained in:
parent
d5c135df89
commit
126cd0028e
@ -24,32 +24,22 @@ bool Prepare();
|
||||
void CreateMove();
|
||||
void Draw();
|
||||
|
||||
struct inactivityTracker
|
||||
size_t FindInVector(size_t id);
|
||||
|
||||
class inactivityTracker
|
||||
{
|
||||
// Map for storing inactivity per connection
|
||||
std::map<std::pair<int, int>, unsigned int> inactives;
|
||||
|
||||
void reset()
|
||||
std::map<std::pair<int, int>, std::pair<int, unsigned int>> inactives;
|
||||
bool vischeckConnection(std::pair<int, int> &connection)
|
||||
{
|
||||
// inactives.clear();
|
||||
Vector begin = areas.at(FindInVector(connection.first)).m_center;
|
||||
Vector end = areas.at(FindInVector(connection.second)).m_center;
|
||||
begin.z += 72;
|
||||
end.z += 72;
|
||||
bool result = IsVectorVisible(begin, end, false);
|
||||
return result;
|
||||
}
|
||||
void addTime(std::pair<int, int> connection, Timer &timer,
|
||||
bool &resetPather)
|
||||
{
|
||||
if (inactives.find(connection) == inactives.end())
|
||||
{
|
||||
inactives[connection] = 0;
|
||||
}
|
||||
inactives[connection] =
|
||||
inactives[connection] +
|
||||
(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - timer.last)
|
||||
.count());
|
||||
if (inactives[connection] >= 5000)
|
||||
resetPather = true;
|
||||
}
|
||||
void addTime(std::pair<Vector, Vector> connection, Timer &timer,
|
||||
bool &resetPather)
|
||||
std::pair<int, int> VectorToId(std::pair<Vector, Vector> &connection)
|
||||
{
|
||||
CNavArea *currnode = nullptr;
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
@ -61,7 +51,7 @@ struct inactivityTracker
|
||||
}
|
||||
}
|
||||
if (!currnode)
|
||||
return;
|
||||
return { -1, -1 };
|
||||
|
||||
CNavArea *nextnode = nullptr;
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
@ -73,21 +63,197 @@ struct inactivityTracker
|
||||
}
|
||||
}
|
||||
if (!nextnode)
|
||||
return;
|
||||
return { -1, -1 };
|
||||
|
||||
for (auto i : currnode->m_connections)
|
||||
{
|
||||
if (i.area->m_id == nextnode->m_id)
|
||||
{
|
||||
addTime(std::pair{ currnode->m_id, nextnode->m_id }, timer,
|
||||
resetPather);
|
||||
return;
|
||||
return { currnode->m_id, nextnode->m_id };
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int getTime(std::pair<int, int> connection)
|
||||
|
||||
public:
|
||||
void reset()
|
||||
{
|
||||
for (auto i : inactives)
|
||||
{
|
||||
// What is this tf
|
||||
i.second.second = 0;
|
||||
}
|
||||
}
|
||||
bool IsIgnored(std::pair<int, int> connection)
|
||||
{
|
||||
if (inactives.find(connection) == inactives.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto &pair = inactives.at(connection);
|
||||
if (pair.second >= 5000)
|
||||
{
|
||||
pair.first = 1;
|
||||
return true;
|
||||
}
|
||||
if (pair.first == 2 && !vischeckConnection(connection))
|
||||
{
|
||||
logging::Info(
|
||||
"Ignored a connection due to type 2 connection type.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddTime(std::pair<int, int> connection, Timer &timer,
|
||||
bool &resetPather)
|
||||
{
|
||||
if (inactives.find(connection) == inactives.end())
|
||||
{
|
||||
inactives[connection] = { 0, 0 };
|
||||
}
|
||||
auto &pair = inactives.at(connection);
|
||||
|
||||
pair.second = pair.second +
|
||||
(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - timer.last)
|
||||
.count());
|
||||
if (pair.second >= 5000)
|
||||
resetPather = true;
|
||||
}
|
||||
void AddTime(std::pair<Vector, Vector> connection, Timer &timer,
|
||||
bool &resetPather)
|
||||
{
|
||||
auto pair = VectorToId(connection);
|
||||
if (pair.first == -1 || pair.second == -1)
|
||||
return;
|
||||
AddTime(pair, timer, resetPather);
|
||||
}
|
||||
bool CheckType2(std::pair<int, int> connection)
|
||||
{
|
||||
// Fix calls vischeckConnection too often
|
||||
if (inactives.find(connection) == inactives.end())
|
||||
{
|
||||
inactives[connection] = { 0, 0 };
|
||||
}
|
||||
auto pair = inactives.at(connection);
|
||||
switch (pair.first)
|
||||
{
|
||||
case 0:
|
||||
if (!vischeckConnection(connection))
|
||||
{
|
||||
inactives[connection].first = 2;
|
||||
return true;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool CheckType2(std::pair<Vector, Vector> connection)
|
||||
{
|
||||
auto pair = VectorToId(connection);
|
||||
if (pair.first == -1 || pair.second == -1)
|
||||
return false;
|
||||
return CheckType2(pair);
|
||||
}
|
||||
}; // namespace nav
|
||||
|
||||
struct MAP : public micropather::Graph
|
||||
{
|
||||
std::unique_ptr<micropather::MicroPather> pather;
|
||||
// Maps already utilize dynamic allocation and we don't need a custom
|
||||
// constructor
|
||||
inactivityTracker inactiveTracker;
|
||||
Vector GetClosestCornerToArea(CNavArea *CornerOf, CNavArea *Target)
|
||||
{
|
||||
std::array<Vector, 4> corners;
|
||||
corners.at(0) = CornerOf->m_nwCorner; // NW
|
||||
corners.at(1) = CornerOf->m_seCorner; // SE
|
||||
corners.at(2) = Vector{ CornerOf->m_seCorner.x, CornerOf->m_nwCorner.y,
|
||||
CornerOf->m_nwCorner.z }; // NE
|
||||
corners.at(3) = Vector{ CornerOf->m_nwCorner.x, CornerOf->m_seCorner.y,
|
||||
CornerOf->m_seCorner.z }; // SW
|
||||
|
||||
Vector bestVec;
|
||||
float bestDist = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < corners.size(); i++)
|
||||
{
|
||||
float dist = corners.at(i).DistTo(Target->m_center);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
bestVec = corners.at(i);
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
Vector bestVec2;
|
||||
float bestDist2 = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < corners.size(); i++)
|
||||
{
|
||||
if (corners.at(i) == bestVec2)
|
||||
continue;
|
||||
float dist = corners.at(i).DistTo(Target->m_center);
|
||||
if (dist < bestDist2)
|
||||
{
|
||||
bestVec2 = corners.at(i);
|
||||
bestDist2 = dist;
|
||||
}
|
||||
}
|
||||
return (bestVec + bestVec2) / 2;
|
||||
}
|
||||
|
||||
float GetZBetweenAreas(CNavArea *start, CNavArea *end)
|
||||
{
|
||||
float z1 = GetClosestCornerToArea(start, end).z;
|
||||
float z2 = GetClosestCornerToArea(end, start).z;
|
||||
|
||||
return z2 - z1;
|
||||
}
|
||||
// Function required by MicroPather for getting an estimated cost
|
||||
float LeastCostEstimate(void *stateStart, void *stateEnd)
|
||||
{
|
||||
CNavArea *start = static_cast<CNavArea *>(stateStart);
|
||||
CNavArea *end = static_cast<CNavArea *>(stateEnd);
|
||||
float dist = start->m_center.DistTo(end->m_center);
|
||||
return dist;
|
||||
}
|
||||
// Function required by MicroPather to retrieve neighbours and their
|
||||
// associated costs.
|
||||
void AdjacentCost(void *state, MP_VECTOR<micropather::StateCost> *adjacent)
|
||||
{
|
||||
CNavArea *area = static_cast<CNavArea *>(state);
|
||||
auto &neighbours = area->m_connections;
|
||||
for (auto i : neighbours)
|
||||
{
|
||||
if (GetZBetweenAreas(area, i.area) > 42)
|
||||
continue;
|
||||
if (inactiveTracker.IsIgnored(
|
||||
std::pair{ area->m_id, i.area->m_id }))
|
||||
continue;
|
||||
micropather::StateCost cost;
|
||||
cost.state =
|
||||
static_cast<void *>(&areas.at(FindInVector(i.area->m_id)));
|
||||
cost.cost = area->m_center.DistTo(i.area->m_center);
|
||||
adjacent->push_back(cost);
|
||||
}
|
||||
}
|
||||
void PrintStateInfo(void *state)
|
||||
{
|
||||
CNavArea *area = static_cast<CNavArea *>(state);
|
||||
logging::Info(format(area->m_center.x, " ", area->m_center.y, " ",
|
||||
area->m_center.z)
|
||||
.c_str());
|
||||
}
|
||||
MAP(size_t size)
|
||||
{
|
||||
pather =
|
||||
std::make_unique<micropather::MicroPather>(this, size, 6, true);
|
||||
}
|
||||
|
||||
~MAP()
|
||||
{
|
||||
return inactives[connection];
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,12 +8,15 @@ std::vector<CNavArea> areas;
|
||||
bool init = false;
|
||||
static bool pathfinding = true;
|
||||
bool ReadyForCommands = false;
|
||||
inactivityTracker inactiveTracker;
|
||||
static settings::Bool enabled{ "misc.pathing", "true" };
|
||||
static settings::Bool draw{ "misc.pathing.draw", "false" };
|
||||
static bool threadingFinished = false;
|
||||
static std::atomic<bool> threadingFinished;
|
||||
|
||||
// Todo fix
|
||||
static std::unique_ptr<MAP> TF2MAP;
|
||||
|
||||
// Function to get place in Vector by connection ID
|
||||
// Todo: find an alternative for this, maybe a map for storing ptrs to the
|
||||
// std::vector?
|
||||
size_t FindInVector(size_t id)
|
||||
{
|
||||
for (size_t i = 0; i < areas.size(); i++)
|
||||
@ -23,110 +26,6 @@ size_t FindInVector(size_t id)
|
||||
}
|
||||
}
|
||||
|
||||
struct MAP : public micropather::Graph
|
||||
{
|
||||
std::unique_ptr<micropather::MicroPather> pather;
|
||||
// Function to check if a connection is ignored
|
||||
bool IsIgnored(size_t currState, size_t connectionID)
|
||||
{
|
||||
if (inactiveTracker.getTime({ currState, connectionID }) >= 5000)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
Vector GetClosestCornerToArea(CNavArea *CornerOf, CNavArea *Target)
|
||||
{
|
||||
std::array<Vector, 4> corners;
|
||||
corners.at(0) = CornerOf->m_nwCorner; // NW
|
||||
corners.at(1) = CornerOf->m_seCorner; // SE
|
||||
corners.at(2) = Vector{ CornerOf->m_seCorner.x, CornerOf->m_nwCorner.y,
|
||||
CornerOf->m_nwCorner.z }; // NE
|
||||
corners.at(3) = Vector{ CornerOf->m_nwCorner.x, CornerOf->m_seCorner.y,
|
||||
CornerOf->m_seCorner.z }; // SW
|
||||
|
||||
Vector bestVec;
|
||||
float bestDist = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < corners.size(); i++)
|
||||
{
|
||||
float dist = corners.at(i).DistTo(Target->m_center);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
bestVec = corners.at(i);
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
Vector bestVec2;
|
||||
float bestDist2 = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < corners.size(); i++)
|
||||
{
|
||||
if (corners.at(i) == bestVec2)
|
||||
continue;
|
||||
float dist = corners.at(i).DistTo(Target->m_center);
|
||||
if (dist < bestDist2)
|
||||
{
|
||||
bestVec2 = corners.at(i);
|
||||
bestDist2 = dist;
|
||||
}
|
||||
}
|
||||
return (bestVec + bestVec2) / 2;
|
||||
}
|
||||
|
||||
float GetZBetweenAreas(CNavArea *start, CNavArea *end)
|
||||
{
|
||||
float z1 = GetClosestCornerToArea(start, end).z;
|
||||
float z2 = GetClosestCornerToArea(end, start).z;
|
||||
|
||||
return z2 - z1;
|
||||
}
|
||||
// Function required by MicroPather for getting an estimated cost
|
||||
float LeastCostEstimate(void *stateStart, void *stateEnd)
|
||||
{
|
||||
CNavArea *start = static_cast<CNavArea *>(stateStart);
|
||||
CNavArea *end = static_cast<CNavArea *>(stateEnd);
|
||||
float dist = start->m_center.DistTo(end->m_center);
|
||||
return dist;
|
||||
}
|
||||
// Function required by MicroPather to retrieve neighbours and their
|
||||
// associated costs.
|
||||
void AdjacentCost(void *state, MP_VECTOR<micropather::StateCost> *adjacent)
|
||||
{
|
||||
CNavArea *area = static_cast<CNavArea *>(state);
|
||||
auto &neighbours = area->m_connections;
|
||||
for (auto i : neighbours)
|
||||
{
|
||||
if (GetZBetweenAreas(area, i.area) > 42)
|
||||
continue;
|
||||
if (IsIgnored(area->m_id, i.area->m_id))
|
||||
continue;
|
||||
micropather::StateCost cost;
|
||||
cost.state =
|
||||
static_cast<void *>(&areas.at(FindInVector(i.area->m_id)));
|
||||
cost.cost = area->m_center.DistTo(i.area->m_center);
|
||||
adjacent->push_back(cost);
|
||||
}
|
||||
}
|
||||
void PrintStateInfo(void *state)
|
||||
{
|
||||
CNavArea *area = static_cast<CNavArea *>(state);
|
||||
logging::Info(format(area->m_center.x, " ", area->m_center.y, " ",
|
||||
area->m_center.z)
|
||||
.c_str());
|
||||
}
|
||||
MAP(size_t size)
|
||||
{
|
||||
pather =
|
||||
std::make_unique<micropather::MicroPather>(this, size, 6, true);
|
||||
}
|
||||
|
||||
~MAP()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static std::unique_ptr<MAP> TF2MAP;
|
||||
|
||||
void Init()
|
||||
{
|
||||
// Get NavFile location
|
||||
@ -161,10 +60,11 @@ void Init()
|
||||
size = 7000;
|
||||
// Initiate "Map", contains micropather object
|
||||
TF2MAP = std::make_unique<MAP>(size);
|
||||
TF2MAP->inactiveTracker.reset();
|
||||
}
|
||||
if (!areas.empty())
|
||||
pathfinding = true;
|
||||
threadingFinished = true;
|
||||
threadingFinished.store(true);
|
||||
}
|
||||
|
||||
static std::string lastmap;
|
||||
@ -175,6 +75,7 @@ bool Prepare()
|
||||
return false;
|
||||
if (!init)
|
||||
{
|
||||
// Don't reinit if same map
|
||||
if (lastmap == g_IEngine->GetLevelName())
|
||||
{
|
||||
init = true;
|
||||
@ -184,12 +85,14 @@ bool Prepare()
|
||||
lastmap = g_IEngine->GetLevelName();
|
||||
pathfinding = false;
|
||||
init = true;
|
||||
threadingFinished = false;
|
||||
threadingFinished.store(false);
|
||||
// Parsing CNavFile takes time, run it in a seperate thread
|
||||
std::thread initer(Init);
|
||||
// We need to either detach or join to avoid std::terminate
|
||||
initer.detach();
|
||||
}
|
||||
}
|
||||
if (!pathfinding || !threadingFinished)
|
||||
if (!pathfinding || !threadingFinished.load())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -211,7 +114,7 @@ int findClosestNavSquare(Vector vec)
|
||||
// Make sure we're not stuck on the same area for too long
|
||||
if (std::count(findClosestNavSquare_localAreas.begin(),
|
||||
findClosestNavSquare_localAreas.end(), i) < 3)
|
||||
overlapping.emplace_back( i, &areas.at(i) );
|
||||
overlapping.emplace_back(i, &areas.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,8 +193,8 @@ static Timer lastJump{};
|
||||
static std::vector<Vector> crumbs;
|
||||
// Bot will keep trying to get to the target even if it fails a few times
|
||||
static bool ensureArrival;
|
||||
// Priority value for current instructions, only higher priority can overwrite
|
||||
// itlocalAreas
|
||||
// Priority value for current instructions, only higher or equal priorites can
|
||||
// overwrite it
|
||||
int priority = 0;
|
||||
static Vector lastArea = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
@ -315,12 +218,13 @@ bool NavTo(Vector dest, bool navToLocalCenter, bool persistent,
|
||||
if (!crumbs.empty())
|
||||
{
|
||||
bool reset = false;
|
||||
inactiveTracker.addTime({ lastArea, crumbs.at(0) }, inactivity, reset);
|
||||
TF2MAP->inactiveTracker.AddTime({ lastArea, crumbs.at(0) }, inactivity,
|
||||
reset);
|
||||
if (reset)
|
||||
TF2MAP->pather->Reset();
|
||||
}
|
||||
crumbs.clear();
|
||||
crumbs = std::move(path);
|
||||
crumbs = std::move(path);
|
||||
lastArea = crumbs.at(0);
|
||||
if (!navToLocalCenter && crumbs.size() > 1)
|
||||
crumbs.erase(crumbs.begin());
|
||||
@ -337,21 +241,43 @@ void clearInstructions()
|
||||
}
|
||||
|
||||
static Timer ignoreReset{};
|
||||
static Timer patherReset{};
|
||||
// Function for removing ignores
|
||||
void clearIgnores()
|
||||
{
|
||||
if (ignoreReset.test_and_set(180000))
|
||||
if (!TF2MAP || !TF2MAP->pather)
|
||||
return;
|
||||
if (ignoreReset.test_and_set(120000))
|
||||
TF2MAP->inactiveTracker.reset();
|
||||
if (patherReset.test_and_set(30000))
|
||||
TF2MAP->pather->Reset();
|
||||
}
|
||||
|
||||
void Repath()
|
||||
{
|
||||
if (ensureArrival)
|
||||
{
|
||||
inactiveTracker.reset();
|
||||
if (TF2MAP && TF2MAP->pather)
|
||||
TF2MAP->pather->Reset();
|
||||
logging::Info("Pathing: NavBot inactive for too long. Ignoring "
|
||||
"connection and finding another path...");
|
||||
// Throwaway int
|
||||
int i1, i2;
|
||||
// Find a new path
|
||||
crumbs = findPath(g_pLocalPlayer->v_Origin, crumbs.back(), i1, i2);
|
||||
}
|
||||
else
|
||||
{
|
||||
logging::Info(
|
||||
"Pathing: NavBot inactive for too long. Canceling tasks and "
|
||||
"ignoring connection...");
|
||||
// Wait for new instructions
|
||||
crumbs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Main movement function, gets path from NavTo
|
||||
void CreateMove()
|
||||
{
|
||||
if (!enabled || !threadingFinished)
|
||||
if (!enabled || !threadingFinished.load())
|
||||
return;
|
||||
if (CE_BAD(LOCAL_E))
|
||||
return;
|
||||
@ -386,30 +312,26 @@ void CreateMove()
|
||||
if (crumbs.at(0).z - g_pLocalPlayer->v_Origin.z > 18 &&
|
||||
lastJump.test_and_set(200))
|
||||
current_user_cmd->buttons |= IN_JUMP;
|
||||
// Check if were dealing with a type 2 connection
|
||||
if (inactivity.check(3000) &&
|
||||
TF2MAP->inactiveTracker.CheckType2({ lastArea, crumbs.at(0) }))
|
||||
{
|
||||
logging::Info("Pathing: Type 2 connection detected!");
|
||||
TF2MAP->pather->Reset();
|
||||
Repath();
|
||||
inactivity.update();
|
||||
return;
|
||||
}
|
||||
// If inactive for too long
|
||||
if (inactivity.check(5000))
|
||||
{
|
||||
// Ignore connection
|
||||
bool i3 = false;
|
||||
inactiveTracker.addTime({ lastArea, crumbs.at(0) }, inactivity, i3);
|
||||
if (i3)
|
||||
bool resetPather = false;
|
||||
TF2MAP->inactiveTracker.AddTime({ lastArea, crumbs.at(0) }, inactivity,
|
||||
resetPather);
|
||||
if (resetPather)
|
||||
TF2MAP->pather->Reset();
|
||||
if (ensureArrival)
|
||||
{
|
||||
logging::Info("Pathing: NavBot inactive for too long. Ignoring "
|
||||
"connection and finding another path...");
|
||||
// Find a new path
|
||||
int i1, i2;
|
||||
crumbs = findPath(g_pLocalPlayer->v_Origin, crumbs.back(), i1, i2);
|
||||
}
|
||||
else
|
||||
{
|
||||
logging::Info(
|
||||
"Pathing: NavBot inactive for too long. Canceling tasks and "
|
||||
"ignoring connection...");
|
||||
// Wait for new instructions
|
||||
crumbs.clear();
|
||||
}
|
||||
Repath();
|
||||
inactivity.update();
|
||||
return;
|
||||
}
|
||||
@ -490,15 +412,18 @@ CatCommand navpath("nav_path", "Debug nav path", [](const CCommand &args) {
|
||||
}
|
||||
});
|
||||
|
||||
CatCommand navpathnolocal("nav_path_nolocal", "Debug nav path", [](const CCommand &args) {
|
||||
if (NavTo(loc, false, true, 50 + priority))
|
||||
{
|
||||
logging::Info("Pathing: Success! Walking to path...");
|
||||
}
|
||||
else
|
||||
{
|
||||
logging::Info("Pathing: Failed!");
|
||||
}
|
||||
});
|
||||
// Clang format pls
|
||||
CatCommand navpathnolocal("nav_path_nolocal", "Debug nav path",
|
||||
[](const CCommand &args) {
|
||||
if (NavTo(loc, false, true, 50 + priority))
|
||||
{
|
||||
logging::Info(
|
||||
"Pathing: Success! Walking to path...");
|
||||
}
|
||||
else
|
||||
{
|
||||
logging::Info("Pathing: Failed!");
|
||||
}
|
||||
});
|
||||
|
||||
} // namespace nav
|
||||
|
Reference in New Issue
Block a user