Refactoring (#246)

add: yb_chat_percent, yb_camping_time_[min/max], yb_danger_factor[min/max], yb_chat_percent cvars
fix: possible crash with difficulty 0
fix: debug_goal should not be used as last history
fix:  startup on old hlds, because of missing pfnGetFileSize
fix: crash with out-bounds read
change: again tweaked some aiming code
change: player avoidance code so bots will jump less when stuck
change: max followers is just /4 of max players
refactor: redone distance between vectors
refactor: remove magic numbers in graph.add function
This commit is contained in:
jeefo 2021-09-20 13:50:00 +03:00 committed by GitHub
commit 6f912eb056
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 214 additions and 177 deletions

View file

@ -8,7 +8,10 @@
#include <yapb.h>
ConVar cv_whose_your_daddy ("yb_whose_your_daddy", "0", "Enables or disables extra hard difficulty for bots.");
ConVar cv_debug_heuristic_type ("yb_debug_heuristic_type", "0", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f);
ConVar cv_path_heuristic_mode ("yb_path_heuristic_mode", "4", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f);
ConVar cv_path_danger_factor_min ("yb_path_danger_factor_min", "200", "Lower bound of danger factor that used to add additional danger to path based on practice.", true, 100.0f, 2400.0f);
ConVar cv_path_danger_factor_max ("yb_path_danger_factor_max", "400", "Upper bound of danger factor that used to add additional danger to path based on practice.", true, 200.0f, 4800.0f);
int Bot::findBestGoal () {
@ -180,7 +183,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
int count = 0;
for (auto &point : graph.m_goalPoints) {
float distance = (graph[point].origin - pev->origin).lengthSq ();
float distance = graph[point].origin.distanceSq (pev->origin);
if (distance > cr::square (1024.0f)) {
continue;
@ -295,6 +298,10 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
edict_t *hindrance = nullptr;
float distance = cr::square (300.0f);
if (getCurrentTaskId () == Task::Attack || getCurrentTaskId () == Task::SeekCover || isOnLadder ()) {
return false;
}
// find nearest player to bot
for (const auto &client : util.getClients ()) {
@ -312,7 +319,7 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
if (client.team != m_team || client.ent == ent ()) {
continue;
}
auto nearest = (client.ent->v.origin - pev->origin).lengthSq ();
auto nearest = client.ent->v.origin.distanceSq (pev->origin);
if (nearest < cr::square (pev->maxspeed) && nearest < distance) {
hindrance = client.ent;
@ -324,7 +331,7 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
if (!hindrance) {
return false;
}
const float interval = getFrameInterval ();
const float interval = getFrameInterval () * 4.0f;
// use our movement angles, try to predict where we should be next frame
Vector right, forward;
@ -335,8 +342,8 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
predict += right * m_strafeSpeed * interval;
predict += pev->velocity * interval;
auto movedDistance = (hindrance->v.origin - predict).lengthSq ();
auto nextFrameDistance = ((hindrance->v.origin + hindrance->v.velocity * interval) - pev->origin).lengthSq ();
auto movedDistance = hindrance->v.origin.distanceSq (predict);
auto nextFrameDistance = pev->origin.distanceSq (hindrance->v.origin + hindrance->v.velocity * interval);
// is player that near now or in future that we need to steer away?
if (movedDistance <= cr::square (48.0f) || (distance <= cr::square (56.0f) && nextFrameDistance < distance)) {
@ -349,13 +356,11 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
else {
setStrafeSpeed (normal, -pev->maxspeed);
}
#if 0
if (distance < cr::square (56.0f)) {
if ((dir | forward.get2d ()) < 0.0f)
if ((dir | forward.get2d ()) < 0.0f) {
m_moveSpeed = -pev->maxspeed;
}
}
#endif
return true;
}
return false;
@ -365,9 +370,12 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// if avoiding someone do not consider stuck
TraceResult tr {};
doPlayerAvoidance (dirNormal);
m_isStuck = doPlayerAvoidance (dirNormal);
m_isStuck = false;
if (m_isStuck) {
resetCollision ();
return;
}
// Standing still, no need to check?
// FIXME: doesn't care for ladder movement (handled separately) should be included in some way
@ -655,7 +663,7 @@ bool Bot::updateNavigation () {
}
m_destOrigin = m_pathOrigin + pev->view_ofs;
float nodeDistance = (pev->origin - m_pathOrigin).length ();
float nodeDistance = pev->origin.distance (m_pathOrigin);
// this node has additional travel flags - care about them
if (m_currentTravelFlags & PathFlag::Jump) {
@ -714,7 +722,7 @@ bool Bot::updateNavigation () {
if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && strncmp (tr.pHit->v.classname.chars (), "func_door", 9) == 0) {
// if the door is near enough...
if ((game.getEntityOrigin (tr.pHit) - pev->origin).lengthSq () < 2500.0f) {
if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < 2500.0f) {
ignoreCollision (); // don't consider being stuck
if (rg.chance (50)) {
@ -795,7 +803,7 @@ bool Bot::updateNavigation () {
}
// needs precise placement - check if we get past the point
if (desiredDistance < 22.0f && nodeDistance < 30.0f && (pev->origin + (pev->velocity * getFrameInterval ()) - m_pathOrigin).lengthSq () >= cr::square (nodeDistance)) {
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * getFrameInterval ()) >= cr::square (nodeDistance)) {
desiredDistance = nodeDistance + 1.0f;
}
@ -837,7 +845,7 @@ bool Bot::updateNavigation () {
// bot within 'hearable' bomb tick noises?
if (!bombOrigin.empty ()) {
float distance = (bombOrigin - graph[taskTarget].origin).length ();
float distance = bombOrigin.distance (graph[taskTarget].origin);
if (distance > 512.0f) {
if (rg.chance (50) && !graph.isVisited (taskTarget)) {
@ -930,7 +938,7 @@ bool Bot::updateLiftHandling () {
m_destOrigin = m_liftTravelPos;
// check if we enough to destination
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) {
if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait ();
// need to wait our following teammate ?
@ -984,7 +992,7 @@ bool Bot::updateLiftHandling () {
if (needWaitForTeammate) {
m_destOrigin = m_liftTravelPos;
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) {
if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait ();
}
}
@ -1015,7 +1023,7 @@ bool Bot::updateLiftHandling () {
m_liftState = LiftState::TravelingBy;
m_liftUsageTime = game.time () + 14.0f;
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) {
if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait ();
}
}
@ -1025,7 +1033,7 @@ bool Bot::updateLiftHandling () {
if (m_liftState == LiftState::TravelingBy) {
m_destOrigin = Vector (m_liftTravelPos.x, m_liftTravelPos.y, pev->origin.z);
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) {
if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait ();
}
}
@ -1042,7 +1050,7 @@ bool Bot::updateLiftHandling () {
m_destOrigin = pev->origin;
}
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) {
if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait ();
}
}
@ -1075,7 +1083,7 @@ bool Bot::updateLiftHandling () {
m_destOrigin = button->v.origin;
}
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) {
if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait ();
}
}
@ -1106,7 +1114,7 @@ bool Bot::updateLiftHandling () {
}
}
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) {
if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait ();
}
}
@ -1204,8 +1212,12 @@ void Bot::findShortestPath (int srcIndex, int destIndex) {
void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath::Fast */) {
// this function finds a path from srcIndex to destIndex
auto dangerFactor = [&] () -> float {
return rg.get (cv_path_danger_factor_min.float_ (), cv_path_danger_factor_max.float_ ()) * 2.0f / cr::clamp (m_difficulty, 1, 3);
};
// least kills and number of nodes to goal for a team
auto gfunctionKillsDist = [] (int team, int currentIndex, int parentIndex) -> float {
auto gfunctionKillsDist = [&dangerFactor] (int team, int currentIndex, int parentIndex) -> float {
if (parentIndex == kInvalidNodeIndex) {
return 0.0f;
}
@ -1221,7 +1233,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
if (current.flags & NodeFlag::Crouch) {
cost *= 1.5f;
}
return cost;
return cost + dangerFactor ();
};
// least kills and number of nodes to goal for a team
@ -1238,7 +1250,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
};
// least kills to goal for a team
auto gfunctionKills = [] (int team, int currentIndex, int) -> float {
auto gfunctionKills = [&dangerFactor] (int team, int currentIndex, int) -> float {
auto cost = static_cast <float> (graph.getDangerDamage (team, currentIndex, currentIndex));
const auto &current = graph[currentIndex];
@ -1251,7 +1263,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
if (current.flags & NodeFlag::Crouch) {
cost *= 1.5f;
}
return cost + 0.5f;
return cost + dangerFactor () + 1.0f;
};
// least kills to goal for a team
@ -1310,7 +1322,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
float y = cr::abs (start.origin.y - goal.origin.y);
float z = cr::abs (start.origin.z - goal.origin.z);
switch (cv_debug_heuristic_type.int_ ()) {
switch (cv_path_heuristic_mode.int_ ()) {
case 0:
default:
return cr::max (cr::max (x, y), z); // chebyshev distance
@ -1326,7 +1338,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
// euclidean based distance
float euclidean = cr::powf (cr::powf (x, 2.0f) + cr::powf (y, 2.0f) + cr::powf (z, 2.0f), 0.5f);
if (cv_debug_heuristic_type.int_ () == 4) {
if (cv_path_heuristic_mode.int_ () == 4) {
return 1000.0f * (cr::ceilf (euclidean) - euclidean);
}
return euclidean;
@ -1343,7 +1355,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
// none heuristic
auto hfunctionNone = [&hfunctionPathDist] (int index, int startIndex, int goalIndex) -> float {
return hfunctionPathDist (index, startIndex, goalIndex) / 128.0f * 10.0f;
return hfunctionPathDist (index, startIndex, goalIndex) / (128.0f * 10.0f);
};
if (!graph.exists (srcIndex)) {
@ -1578,7 +1590,7 @@ bool Bot::findBestNearestNode () {
}
// if we're still here, find some close nodes
float distance = (pev->origin - graph[at].origin).lengthSq ();
float distance = pev->origin.distanceSq (graph[at].origin);
if (distance < lessDist[0]) {
lessDist[2] = lessDist[1];
@ -1650,7 +1662,7 @@ float Bot::getReachTime () {
// calculate 'real' time that we need to get from one node to another
if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) {
float distance = (graph[m_previousNodes[0]].origin - graph[m_currentNodeIndex].origin).length ();
float distance = graph[m_previousNodes[0]].origin.distance (graph[m_currentNodeIndex].origin);
// caclulate estimated time
if (pev->maxspeed <= 0.0f) {
@ -1735,7 +1747,7 @@ int Bot::findNearestNode () {
if (at == m_currentNodeIndex) {
continue;
}
float distance = (graph[at].origin - pev->origin).lengthSq ();
float distance = graph[at].origin.distanceSq (pev->origin);
if (distance < minimum) {
@ -1763,7 +1775,7 @@ int Bot::findBombNode () {
const auto &audible = isBombAudible ();
// take the nearest to bomb nodes instead of goal if close enough
if ((pev->origin - bomb).lengthSq () < cr::square (96.0f)) {
if (pev->origin.distanceSq (bomb) < cr::square (96.0f)) {
int node = graph.getNearest (bomb, 420.0f);
m_bombSearchOverridden = true;
@ -1786,7 +1798,7 @@ int Bot::findBombNode () {
// find nearest goal node either to bomb (if "heard" or player)
for (auto &point : goals) {
float distance = (graph[point].origin - bomb).lengthSq ();
float distance = bomb.distanceSq (graph[point].origin);
// check if we got more close distance
if (distance < lastDistance) {
@ -1888,7 +1900,7 @@ int Bot::findDefendNode (const Vector &origin) {
IntArray found;
for (int i = 0; i < graph.length (); ++i) {
if ((graph[i].origin - origin).lengthSq () <= cr::square (1248.0f) && !graph.isVisible (i, posIndex) && !isOccupiedNode (i)) {
if (origin.distanceSq (graph[i].origin) <= cr::square (1248.0f) && !graph.isVisible (i, posIndex) && !isOccupiedNode (i)) {
found.push (i);
}
}
@ -1911,7 +1923,7 @@ int Bot::findDefendNode (const Vector &origin) {
int Bot::findCoverNode (float maxDistance) {
// this function tries to find a good cover node if bot wants to hide
const float enemyMax = (m_lastEnemyOrigin - pev->origin).length ();
const float enemyMax = m_lastEnemyOrigin.distance (pev->origin);
// do not move to a position near to the enemy
if (maxDistance > enemyMax) {
@ -2172,7 +2184,7 @@ bool Bot::advanceMovement () {
src = path.origin;
dst = next.origin;
jumpDistance = (path.origin - next.origin).length ();
jumpDistance = path.origin.distance (next.origin);
willJump = true;
break;
@ -2661,7 +2673,7 @@ bool Bot::isDeadlyMove (const Vector &to) {
}
float lastHeight = tr.flFraction * 1000.0f; // height from ground
float distance = (to - check).length (); // distance from goal
float distance = to.distance (check); // distance from goal
if (distance <= 30.0f && lastHeight > 150.0f) {
return true;
@ -2686,7 +2698,7 @@ bool Bot::isDeadlyMove (const Vector &to) {
return true;
}
lastHeight = height;
distance = (to - check).length (); // distance from goal
distance = to.distance (check); // distance from goal
}
return false;
}
@ -2774,13 +2786,13 @@ int Bot::findCampingDirection () {
if (count < 3) {
indices[count] = i;
distTab[count] = (pev->origin - path.origin).lengthSq ();
distTab[count] = pev->origin.distanceSq (path.origin);
visibility[count] = path.vis.crouch + path.vis.stand;
++count;
}
else {
float distance = (pev->origin - path.origin).lengthSq ();
float distance = pev->origin.distanceSq (path.origin);
uint16 visBits = path.vis.crouch + path.vis.stand;
for (int j = 0; j < 3; ++j) {
@ -3005,14 +3017,14 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) {
}
// do not check clients far away from us
if ((pev->origin - client.origin).lengthSq () > cr::square (320.0f)) {
if (pev->origin.distanceSq (client.origin) > cr::square (320.0f)) {
continue;
}
if (needZeroVelocity && client.ent->v.velocity.length2d () > 0.0f) {
continue;
}
auto length = (graph[index].origin - client.origin).lengthSq ();
auto length = client.origin.distanceSq (graph[index].origin);
if (length < cr::clamp (cr::square (graph[index].radius), cr::square (60.0f), cr::square (90.0f))) {
return true;
@ -3049,7 +3061,7 @@ edict_t *Bot::lookupButton (const char *target) {
// check if this place safe
if (!isDeadlyMove (pos)) {
float distance = (pev->origin - pos).lengthSq ();
float distance = pev->origin.distanceSq (pos);
// check if we got more close button
if (distance <= nearest) {
@ -3073,7 +3085,7 @@ bool Bot::isReachableNode (int index) {
const Vector &dst = graph[index].origin;
// is the destination close enough?
if ((dst - src).lengthSq () >= cr::square (320.0f)) {
if (dst.distanceSq (src) >= cr::square (320.0f)) {
return false;
}
@ -3081,7 +3093,7 @@ bool Bot::isReachableNode (int index) {
if (isOccupiedNode (index, true)) {
return true;
}
float ladderDist = (dst - src).length2d ();
float ladderDist = dst.distance2d (src);
TraceResult tr {};
game.testLine (src, dst, TraceIgnore::Monsters, ent (), &tr);
@ -3112,6 +3124,9 @@ bool Bot::isReachableNode (int index) {
}
bool Bot::isBannedNode (int index) {
if (graph.exists (cv_debug_goal.int_ ())) {
return false;
}
for (const auto &node : m_goalHistory) {
if (node == index) {
return true;