aim: verify camp angles from nav data before using them

aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
This commit is contained in:
jeefo 2023-04-07 14:46:49 +03:00
commit 29c00565dc
No known key found for this signature in database
GPG key ID: 927BCA0779BEA8ED
31 changed files with 1395 additions and 1305 deletions

View file

@ -12,8 +12,8 @@ ConVar cv_path_danger_factor_min ("yb_path_danger_factor_min", "200", "Lower bou
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 () {
auto pushToHistroy = [&] (int32 goal) -> int32 {
m_goalHistory.push (goal);
auto pushToHistroy = [&] (int32_t goal) -> int32_t {
m_nodeHistory.push (goal);
return goal;
};
@ -188,12 +188,9 @@ int Bot::findBestGoalWhenBombAction () {
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, timeMidBlowup, true); // push camp task on to stack
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, result, timeMidBlowup, true); // push move command
if (path.vis.crouch <= path.vis.stand) {
m_campButtons |= IN_DUCK;
}
else {
m_campButtons &= ~IN_DUCK;
}
// decide to duck or not to duck
selectCampButtons (result);
if (rg.chance (90)) {
pushChatterMessage (Chatter::DefendingBombsite);
}
@ -259,8 +256,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
postprocessGoals (graph.m_goalPoints, goalChoices);
}
}
else if (tactic == 4 && !graph.m_rescuePoints.empty ()) // rescue goal
{
else if (tactic == 4 && !graph.m_rescuePoints.empty ()) {
// force ct with hostage(s) to select closest rescue goal
float minDist = kInfiniteDistance;
int count = 0;
@ -286,7 +282,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
}
if (!graph.exists (m_currentNodeIndex)) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
if (goalChoices[0] == kInvalidNodeIndex) {
@ -320,7 +316,7 @@ void Bot::postprocessGoals (const IntArray &goals, int result[]) {
for (int index = 0; index < 4; ++index) {
auto goal = goals.random ();
if (searchCount <= 8 && (isBannedNode (goal) || m_prevGoalIndex == goal || ((result[0] == goal || result[1] == goal || result[2] == goal || result[3] == goal) && goals.length () > 4)) && !isOccupiedNode (goal)) {
if (searchCount <= 8 && (m_prevGoalIndex == goal || ((result[0] == goal || result[1] == goal || result[2] == goal || result[3] == goal) && goals.length () > 4)) && !isOccupiedNode (goal)) {
if (index > 0) {
index--;
}
@ -370,13 +366,14 @@ void Bot::ignoreCollision () {
m_prevOrigin = pev->origin;
}
bool Bot::doPlayerAvoidance (const Vector &normal) {
edict_t *hindrance = nullptr;
float distance = cr::square (300.0f);
void Bot::doPlayerAvoidance (const Vector &normal) {
m_hindrance = nullptr;
float distance = cr::square (348.0f);
if (getCurrentTaskId () == Task::Attack || getCurrentTaskId () == Task::SeekCover || isOnLadder ()) {
return false;
if (getCurrentTaskId () == Task::Attack || isOnLadder ()) {
return;
}
const auto ownPrio = bots.getPlayerPriority (ent ());
// find nearest player to bot
for (const auto &client : util.getClients ()) {
@ -391,28 +388,29 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
continue;
}
// skip if it's not standing
if (client.ent->v.flags & FL_DUCKING) {
continue;
}
// our team, alive and not myself?
if (client.team != m_team || client.ent == ent ()) {
continue;
}
const auto otherPrio = bots.getPlayerPriority (client.ent);
// give some priorities to bot avoidance
if (ownPrio > otherPrio) {
continue;
}
auto nearest = client.ent->v.origin.distanceSq (pev->origin);
if (nearest < cr::square (pev->maxspeed) && nearest < distance) {
hindrance = client.ent;
m_hindrance = client.ent;
distance = nearest;
}
}
// found somebody?
if (!hindrance) {
return false;
if (game.isNullEntity (m_hindrance)) {
return;
}
const float interval = getFrameInterval () * 4.0f;
const float interval = m_frameInterval * 7.25f;
// use our movement angles, try to predict where we should be next frame
Vector right, forward;
@ -423,44 +421,39 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
predict += right * m_strafeSpeed * interval;
predict += pev->velocity * interval;
auto movedDistance = hindrance->v.origin.distanceSq (predict);
auto nextFrameDistance = pev->origin.distanceSq (hindrance->v.origin + hindrance->v.velocity * interval);
auto movedDistance = m_hindrance->v.origin.distanceSq (predict);
auto nextFrameDistance = pev->origin.distanceSq (m_hindrance->v.origin + m_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)) {
auto dir = (pev->origin - hindrance->v.origin).normalize2d ();
auto dir = (pev->origin - m_hindrance->v.origin).normalize2d ();
// to start strafing, we have to first figure out if the target is on the left side or right side
if ((dir | right.get2d ()) > 0.0f) {
if ((dir | right.normalize2d ()) > 0.0f) {
setStrafeSpeed (normal, pev->maxspeed);
}
else {
setStrafeSpeed (normal, -pev->maxspeed);
}
if (distance < cr::square (56.0f)) {
if ((dir | forward.get2d ()) < 0.0f) {
if (distance < cr::square (76.0f)) {
if ((dir | forward.normalize2d ()) < 0.0f) {
m_moveSpeed = -pev->maxspeed;
}
}
return true;
}
return false;
}
void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// if avoiding someone do not consider stuck
TraceResult tr {};
m_isStuck = doPlayerAvoidance (dirNormal);
float checkSpeed = isDucking () ? 4.0f : 10.0f;
if (m_isStuck) {
resetCollision ();
return;
}
m_isStuck = false;
// Standing still, no need to check?
// FIXME: doesn't care for ladder movement (handled separately) should be included in some way
if ((m_moveSpeed >= 10.0f || m_strafeSpeed >= 10.0f) && m_lastCollTime < game.time () && m_seeEnemyTime + 0.8f < game.time () && getCurrentTaskId () != Task::Attack) {
// standing still, no need to check?
if ((m_moveSpeed >= checkSpeed || m_strafeSpeed >= checkSpeed) && m_lastCollTime < game.time ()) {
// didn't we move enough previously?
if (movedDistance < 2.0f && m_prevSpeed >= 20.0f) {
@ -504,7 +497,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// bot is stuc, but not yet decided what to do?
if (m_collisionState == CollisionState::Undecided) {
uint32 bits = 0;
uint32_t bits = 0;
if (isOnLadder ()) {
bits |= CollisionProbe::Strafe;
@ -515,7 +508,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// collision check allowed if not flying through the air
if (isOnFloor () || isOnLadder () || isInWater ()) {
uint32 state[kMaxCollideMoves * 2 + 1] {};
uint32_t state[kMaxCollideMoves * 2 + 1] {};
int i = 0;
Vector src {}, dst {};
@ -623,7 +616,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
}
}
}
if (pev->flags & FL_DUCKING) {
if (isDucking ()) {
src = pev->origin;
}
else {
@ -724,13 +717,105 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
}
}
void Bot::moveToGoal () {
findValidNode ();
bool prevLadder = false;
if (graph.exists (m_previousNodes[0])) {
if (graph[m_previousNodes[0]].flags & NodeFlag::Ladder) {
prevLadder = true;
}
}
// press duck button if we need to
if ((m_pathFlags & NodeFlag::Crouch) && !(m_pathFlags & (NodeFlag::Camp | NodeFlag::Goal))) {
pev->button |= IN_DUCK;
}
m_lastUsedNodesTime = game.time ();
// press jump button if we need to leave the ladder
if (!(m_pathFlags & NodeFlag::Ladder) && prevLadder && isOnFloor () && isOnLadder () && m_moveSpeed > 50.0f && pev->velocity.length () < 50.0f) {
pev->button |= IN_JUMP;
m_jumpTime = game.time () + 1.0f;
}
if (m_pathFlags & NodeFlag::Ladder) {
if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) {
if (!prevLadder) {
m_moveSpeed = pev->origin.distance (m_pathOrigin);
}
else {
m_moveSpeed = 150.0f;
}
if (m_moveSpeed < 150.0f) {
m_moveSpeed = 150.0f;
}
else if (m_moveSpeed > pev->maxspeed) {
m_moveSpeed = pev->maxspeed;
}
}
}
// special movement for swimming here
if (isInWater ()) {
// check if we need to go forward or back press the correct buttons
if (isInFOV (m_destOrigin - getEyesPos ()) > 90.0f) {
pev->button |= IN_BACK;
}
else {
pev->button |= IN_FORWARD;
}
if (m_moveAngles.x > 60.0f) {
pev->button |= IN_DUCK;
}
else if (m_moveAngles.x < -60.0f) {
pev->button |= IN_JUMP;
}
}
}
void Bot::translateInput () {
if (m_duckTime >= game.time ()) {
pev->button |= IN_DUCK;
}
if (pev->button & IN_JUMP) {
m_jumpTime = game.time ();
}
if (m_jumpTime + 0.85f > game.time ()) {
if (!isOnFloor () && !isInWater () && !isOnLadder ()) {
pev->button |= IN_DUCK;
}
}
if (!(pev->button & (IN_FORWARD | IN_BACK))) {
if (m_moveSpeed > 0.0f) {
pev->button |= IN_FORWARD;
}
else if (m_moveSpeed < 0.0f) {
pev->button |= IN_BACK;
}
}
if (!(pev->button & (IN_MOVELEFT | IN_MOVERIGHT))) {
if (m_strafeSpeed > 0.0f) {
pev->button |= IN_MOVERIGHT;
}
else if (m_strafeSpeed < 0.0f) {
pev->button |= IN_MOVELEFT;
}
}
}
bool Bot::updateNavigation () {
// this function is a main path navigation
// check if we need to find a node...
if (m_currentNodeIndex == kInvalidNodeIndex) {
findValidNode ();
m_pathOrigin = m_path->origin;
// if graph node radius non zero vary origin a bit depending on the body angles
if (m_path->radius > 0.0f) {
@ -749,7 +834,9 @@ bool Bot::updateNavigation () {
// if bot's on the ground or on the ladder we're free to jump. actually setting the correct velocity is cheating.
// pressing the jump button gives the illusion of the bot actual jumping.
if (isOnFloor () || isOnLadder ()) {
pev->velocity = m_desiredVelocity;
if (m_desiredVelocity.length2d () > 0.0f) {
pev->velocity = m_desiredVelocity + m_desiredVelocity * 0.05f;
}
pev->button |= IN_JUMP;
m_jumpFinished = true;
@ -759,10 +846,16 @@ bool Bot::updateNavigation () {
}
else if (!cv_jasonmode.bool_ () && usesKnife () && isOnFloor ()) {
selectBestWeapon ();
// if jump distance was big enough, cooldown a little
if (m_jumpDistance > 180.0f) {
startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.45f, false);
}
m_jumpDistance = 0.0f;
}
}
if (m_path->flags & NodeFlag::Ladder) {
if (m_pathFlags & NodeFlag::Ladder) {
if (!m_pathWalk.empty ()) {
if (m_pathWalk.hasNext ()) {
if (graph[m_pathWalk.next ()].flags & NodeFlag::Ladder || isOnLadder ()) {
@ -776,10 +869,10 @@ bool Bot::updateNavigation () {
m_pathOrigin.z += pev->origin.z - m_pathOrigin.z;
}
if (m_pathOrigin.z > (pev->origin.z + 16.0f)) {
m_pathOrigin = m_path->origin - Vector (0.0f, 0.0f, 16.0f);
m_pathOrigin = m_pathOrigin - Vector (0.0f, 0.0f, 16.0f);
}
if (m_pathOrigin.z < (pev->origin.z - 16.0f)) {
m_pathOrigin = m_path->origin + Vector (0.0f, 0.0f, 16.0f);
m_pathOrigin = m_pathOrigin + Vector (0.0f, 0.0f, 16.0f);
}
}
m_destOrigin = m_pathOrigin;
@ -813,7 +906,7 @@ bool Bot::updateNavigation () {
}
}
else {
game.testHull (getEyesPos (), m_path->origin, TraceIgnore::Monsters, pev->flags & FL_DUCKING ? head_hull : human_hull, ent (), &tr);
game.testHull (getEyesPos (), m_pathOrigin, TraceIgnore::Monsters, isDucking () ? head_hull : human_hull, ent (), &tr);
// someone is above or below us and is using the ladder already
if (tr.pHit == client.ent && cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) {
@ -843,7 +936,7 @@ bool Bot::updateNavigation () {
}
if (foundGround) {
if (getCurrentTaskId () != Task::MoveToPosition || !cr::fequal (getTask ()->desire, TaskPri::PlantBomb)) {
m_currentNodeIndex = m_previousNodes[0];
changeNodeIndex (m_previousNodes[0]);
startTask (Task::MoveToPosition, TaskPri::PlantBomb, previousNode, 0.0f, true);
}
break;
@ -856,7 +949,7 @@ bool Bot::updateNavigation () {
}
// special lift handling (code merged from podbotmm)
if (m_path->flags & NodeFlag::Lift) {
if (m_pathFlags & NodeFlag::Lift) {
if (updateLiftHandling ()) {
if (!updateLiftStates ()) {
return false;
@ -929,16 +1022,16 @@ bool Bot::updateNavigation () {
}
float desiredDistance = 8.0f;
float nodeDistance = pev->origin.distance2d (m_pathOrigin);
float nodeDistance = pev->origin.distance (m_pathOrigin);
// initialize the radius for a special node type, where the node is considered to be reached
if (m_path->flags & NodeFlag::Lift) {
if (m_pathFlags & NodeFlag::Lift) {
desiredDistance = 50.0f;
}
else if ((pev->flags & FL_DUCKING) || (m_path->flags & NodeFlag::Goal)) {
else if (isDucking () || (m_pathFlags & NodeFlag::Goal)) {
desiredDistance = 25.0f;
}
else if (m_path->flags & NodeFlag::Ladder) {
else if (m_pathFlags & NodeFlag::Ladder) {
desiredDistance = 24.0f;
}
else if (m_currentTravelFlags & PathFlag::Jump) {
@ -963,7 +1056,7 @@ bool Bot::updateNavigation () {
}
// needs precise placement - check if we get past the point
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * getFrameInterval ()) >= cr::square (nodeDistance)) {
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= cr::square (nodeDistance)) {
desiredDistance = nodeDistance + 1.0f;
}
@ -1048,7 +1141,7 @@ bool Bot::updateLiftHandling () {
};
// trace line to door
game.testLine (pev->origin, m_path->origin, TraceIgnore::Everything, ent (), &tr);
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr);
if (tr.flFraction < 1.0f && tr.pHit && strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) {
if (m_liftState == LiftState::None) {
@ -1059,7 +1152,7 @@ bool Bot::updateLiftHandling () {
}
// trace line down
game.testLine (m_path->origin, m_path->origin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
game.testLine (m_path->origin, m_pathOrigin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
// if trace result shows us that it is a lift
if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && (strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_train") == 0) && !liftClosedDoorExists) {
@ -1067,7 +1160,7 @@ bool Bot::updateLiftHandling () {
if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) {
m_liftEntity = tr.pHit;
m_liftState = LiftState::EnteringIn;
m_liftTravelPos = m_path->origin;
m_liftTravelPos = m_pathOrigin;
m_liftUsageTime = game.time () + 5.0f;
}
}
@ -1289,7 +1382,7 @@ bool Bot::updateLiftHandling () {
m_liftUsageTime = 0.0f;
clearSearchNodes ();
findBestNearestNode ();
findNextBestNode ();
if (graph.exists (m_previousNodes[2])) {
findPath (m_currentNodeIndex, m_previousNodes[2], FindPath::Fast);
@ -1302,7 +1395,7 @@ bool Bot::updateLiftHandling () {
}
bool Bot::updateLiftStates () {
if (!game.isNullEntity (m_liftEntity) && !(m_path->flags & NodeFlag::Lift)) {
if (!game.isNullEntity (m_liftEntity) && !(m_pathFlags & NodeFlag::Lift)) {
if (m_liftState == LiftState::TravelingBy) {
m_liftState = LiftState::Leaving;
m_liftUsageTime = game.time () + 10.0f;
@ -1324,14 +1417,14 @@ bool Bot::updateLiftStates () {
if (graph.exists (m_previousNodes[0])) {
if (!(graph[m_previousNodes[0]].flags & NodeFlag::Lift)) {
changePointIndex (m_previousNodes[0]);
changeNodeIndex (m_previousNodes[0]);
}
else {
findBestNearestNode ();
findNextBestNode ();
}
}
else {
findBestNearestNode ();
findNextBestNode ();
}
return false;
}
@ -1630,9 +1723,9 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
auto childRoute = &m_routes[child.index];
// calculate the F value as F = G + H
float g = curRoute->g + gcalc (m_team, child.index, currentIndex);
float h = hcalc (child.index, srcIndex, destIndex);
float f = g + h;
const float g = curRoute->g + gcalc (m_team, child.index, currentIndex);
const float h = hcalc (child.index, srcIndex, destIndex);
const float f = g + h;
if (childRoute->state == RouteState::New || childRoute->f > f) {
// put the current child into open list
@ -1674,7 +1767,7 @@ int Bot::findAimingNode (const Vector &to, int &pathLength) {
// return the most distant node which is seen from the bot to the target and is within count
if (m_currentNodeIndex == kInvalidNodeIndex) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
int destIndex = graph.getNearest (to);
@ -1704,7 +1797,7 @@ int Bot::findAimingNode (const Vector &to, int &pathLength) {
return bestIndex;
}
bool Bot::findBestNearestNode () {
bool Bot::findNextBestNode () {
// this function find a node in the near of the bot if bot had lost his path of pathfinder needs
// to be restarted over again.
@ -1714,7 +1807,7 @@ bool Bot::findBestNearestNode () {
int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex , kInvalidNodeIndex };
auto &bucket = graph.getNodesInBucket (pev->origin);
int numToSkip = cr::clamp (rg.get (0, 3), 0, static_cast <int> (bucket.length () / 2));
int numToSkip = cr::clamp (rg.get (0, 2), 0, static_cast <int> (bucket.length () / 2));
for (const int at : bucket) {
bool skip = !!(at == m_currentNodeIndex);
@ -1748,7 +1841,7 @@ bool Bot::findBestNearestNode () {
}
// ignore non-reacheable nodes...
if (!isReachableNode (at) || isBannedNode (at)) {
if (!isReachableNode (at)) {
continue;
}
@ -1809,25 +1902,14 @@ bool Bot::findBestNearestNode () {
if (selected == kInvalidNodeIndex) {
selected = findNearestNode ();
}
m_goalHistory.push (selected);
changePointIndex (selected);
m_nodeHistory.push (selected);
changeNodeIndex (selected);
return true;
}
float Bot::getReachTime () {
auto task = getCurrentTaskId ();
float estimatedTime = 0.0f;
switch (task) {
case Task::Pause:
case Task::Camp:
case Task::Hide:
return 0.0f;
default:
estimatedTime = 2.8f; // time to reach next node
}
float Bot::getEstimatedNodeReachTime () {
float estimatedTime = 2.8f;
// calculate 'real' time that we need to get from one node to another
if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) {
@ -1840,7 +1922,7 @@ float Bot::getReachTime () {
else {
estimatedTime = 3.0f * distance / pev->maxspeed;
}
bool longTermReachability = (m_path->flags & NodeFlag::Crouch) || (m_path->flags & NodeFlag::Ladder) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK);
bool longTermReachability = (m_pathFlags & NodeFlag::Crouch) || (m_pathFlags & NodeFlag::Ladder) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK);
// check for special nodes, that can slowdown our movement
if (longTermReachability) {
@ -1848,7 +1930,7 @@ float Bot::getReachTime () {
}
estimatedTime = cr::clamp (estimatedTime, 2.0f, longTermReachability ? 8.0f : 5.0f);
}
return estimatedTime;
return estimatedTime + m_frameInterval;
}
void Bot::findValidNode () {
@ -1857,11 +1939,9 @@ void Bot::findValidNode () {
// if bot hasn't got a node we need a new one anyway or if time to get there expired get new one as well
if (m_currentNodeIndex == kInvalidNodeIndex) {
clearSearchNodes ();
findBestNearestNode ();
m_pathOrigin = m_path->origin;
findNextBestNode ();
}
else if (m_navTimeset + getReachTime () < game.time () && game.isNullEntity (m_enemy)) {
else if (m_navTimeset + getEstimatedNodeReachTime () < game.time () && game.isNullEntity (m_enemy)) {
// increase danager for both teams
for (int team = Team::Terrorist; team < kGameTeamNum; ++team) {
@ -1880,13 +1960,11 @@ void Bot::findValidNode () {
graph.setDangerDamage (m_team, m_currentNodeIndex, m_currentNodeIndex, damageValue);
}
clearSearchNodes ();
findBestNearestNode ();
m_pathOrigin = m_path->origin;
findNextBestNode ();
}
}
int Bot::changePointIndex (int index) {
int Bot::changeNodeIndex (int index) {
if (index == kInvalidNodeIndex) {
return 0;
}
@ -1896,11 +1974,14 @@ int Bot::changePointIndex (int index) {
m_previousNodes[0] = m_currentNodeIndex;
m_currentNodeIndex = index;
m_navTimeset = game.time ();
m_collideTime = game.time ();
m_path = &graph[index];
m_path = &graph[m_currentNodeIndex];
m_pathFlags = m_path->flags;
m_pathOrigin = m_path->origin;
return m_currentNodeIndex; // to satisfy static-code analyzers
}
@ -2004,7 +2085,7 @@ int Bot::findDefendNode (const Vector &origin) {
// provides enough cover point, and is far away from the defending position
if (m_currentNodeIndex == kInvalidNodeIndex) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
TraceResult tr {};
@ -2020,7 +2101,7 @@ int Bot::findDefendNode (const Vector &origin) {
int srcIndex = m_currentNodeIndex;
// max search distance
const int kMaxDistance = 128 * bots.getBotCount ();
const int kMaxDistance = cr::clamp (148 * bots.getBotCount (), 256, 1024);
// some of points not found, return random one
if (srcIndex == kInvalidNodeIndex || posIndex == kInvalidNodeIndex) {
@ -2177,7 +2258,7 @@ int Bot::findCoverNode (float maxDistance) {
}
// ensure we're on valid point
changePointIndex (srcIndex);
changeNodeIndex (srcIndex);
// find the best node now
for (int i = 0; i < graph.length (); ++i) {
@ -2207,35 +2288,35 @@ int Bot::findCoverNode (float maxDistance) {
continue;
}
if (distance > minDistance[0]) {
if (distance < minDistance[0]) {
nodeIndex[0] = i;
minDistance[0] = distance;
}
else if (distance > minDistance[1]) {
else if (distance < minDistance[1]) {
nodeIndex[1] = i;
minDistance[1] = distance;
}
else if (distance > minDistance[2]) {
else if (distance < minDistance[2]) {
nodeIndex[2] = i;
minDistance[2] = distance;
}
else if (distance > minDistance[3]) {
else if (distance < minDistance[3]) {
nodeIndex[3] = i;
minDistance[3] = distance;
}
else if (distance > minDistance[4]) {
else if (distance < minDistance[4]) {
nodeIndex[4] = i;
minDistance[4] = distance;
}
else if (distance > minDistance[5]) {
else if (distance < minDistance[5]) {
nodeIndex[5] = i;
minDistance[5] = distance;
}
else if (distance > minDistance[6]) {
else if (distance < minDistance[6]) {
nodeIndex[6] = i;
minDistance[6] = distance;
}
else if (distance > minDistance[7]) {
else if (distance < minDistance[7]) {
nodeIndex[7] = i;
minDistance[7] = distance;
}
@ -2411,7 +2492,7 @@ bool Bot::advanceMovement () {
}
// check if bot is going to jump
bool willJump = false;
float jumpDistance = 0.0f;
m_jumpDistance = 0.0f;
Vector src;
Vector dst;
@ -2420,15 +2501,15 @@ bool Bot::advanceMovement () {
if (m_pathWalk.hasNext ()) {
auto nextIndex = m_pathWalk.next ();
const Path &path = graph[destIndex];
const Path &next = graph[nextIndex];
const auto &path = graph[destIndex];
const auto &next = graph[nextIndex];
for (const auto &link : path.links) {
if (link.index == nextIndex && (link.flags & PathFlag::Jump)) {
src = path.origin;
dst = next.origin;
jumpDistance = path.origin.distance (next.origin);
m_jumpDistance = path.origin.distance (next.origin);
willJump = true;
break;
@ -2437,14 +2518,14 @@ bool Bot::advanceMovement () {
}
// is there a jump node right ahead and do we need to draw out the light weapon ?
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (jumpDistance > 200.0f || (dst.z - 32.0f > src.z && jumpDistance > 150.0f)) && !(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))) {
selectWeaponByName ("weapon_knife"); // draw out the knife if we needed
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (m_jumpDistance > 175.0f || (dst.z - 32.0f > src.z && m_jumpDistance > 135.0f)) && !(m_states & Sense::SeeingEnemy)) {
selectWeaponById (Weapon::Knife); // draw out the knife if we needed
}
// bot not already on ladder but will be soon?
if ((graph[destIndex].flags & NodeFlag::Ladder) && !isOnLadder ()) {
// get ladder nodes used by other (first moving) bots
// get ladder nodes used by other (first moving) bots
for (const auto &other : bots) {
// if another bot uses this ladder, wait 3 secs
if (other.get () != this && other->m_notKilled && other->m_currentNodeIndex == destIndex) {
@ -2454,10 +2535,9 @@ bool Bot::advanceMovement () {
}
}
}
changePointIndex (destIndex);
changeNodeIndex (destIndex);
}
}
m_pathOrigin = m_path->origin;
// if wayzone radius non zero vary origin a bit depending on the body angles
if (m_path->radius > 0.0f) {
@ -2484,7 +2564,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
// first do a trace from the bot's eyes forward...
auto src = getEyesPos ();
auto forward = src + normal * 24.0f;
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
bool traceResult = false;
@ -2531,7 +2611,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
}
// now check below waist
if (pev->flags & FL_DUCKING) {
if (isDucking ()) {
src = pev->origin + Vector (0.0f, 0.0f, -19.0f + 19.0f);
forward = src + Vector (0.0f, 0.0f, 10.0f) + normal * 24.0f;
@ -2646,7 +2726,7 @@ bool Bot::canJumpUp (const Vector &normal) {
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
return false;
}
auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
// check for normal jump height first...
auto src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
@ -2793,7 +2873,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
Vector baseHeight;
// use center of the body first...
if (pev->flags & FL_DUCKING) {
if (isDucking ()) {
baseHeight = pev->origin + Vector (0.0f, 0.0f, -17.0f);
}
else {
@ -2812,7 +2892,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
}
// convert current view angle to vectors for TraceLine math...
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
// now check same height to one side of the bot...
src = baseHeight + right * 16.0f;
@ -3008,16 +3088,17 @@ void Bot::changeYaw (float speed) {
pev->angles.y = pev->v_angle.y;
}
int Bot::findCampingDirection () {
int Bot::getRandomCampDir () {
// find a good node to look at when camping
if (m_currentNodeIndex == kInvalidNodeIndex) {
m_currentNodeIndex = changePointIndex (findNearestNode ());
changeNodeIndex (findNearestNode ());
}
constexpr int kMaxNodesToSearch = 5;
int count = 0, indices[3] {};
float distTab[3] {};
uint16 visibility[3] {};
int count = 0, indices[kMaxNodesToSearch] {};
float distTab[kMaxNodesToSearch] {};
uint16_t visibility[kMaxNodesToSearch] {};
int currentNode = m_currentNodeIndex;
@ -3025,9 +3106,9 @@ int Bot::findCampingDirection () {
if (currentNode == i || !graph.isVisible (currentNode, i)) {
continue;
}
const Path &path = graph[i];
const auto &path = graph[i];
if (count < 3) {
if (count < kMaxNodesToSearch) {
indices[count] = i;
distTab[count] = pev->origin.distanceSq (path.origin);
@ -3037,9 +3118,9 @@ int Bot::findCampingDirection () {
}
else {
float distance = pev->origin.distanceSq (path.origin);
uint16 visBits = path.vis.crouch + path.vis.stand;
uint16_t visBits = path.vis.crouch + path.vis.stand;
for (int j = 0; j < 3; ++j) {
for (int j = 0; j < kMaxNodesToSearch; ++j) {
if (visBits >= visibility[j] && distance > distTab[j]) {
indices[j] = i;
@ -3077,7 +3158,7 @@ void Bot::updateLookAngles () {
// adjust all body and view angles to face an absolute vector
Vector direction = (m_lookAt - getEyesPos ()).angles ();
direction.x *= -1.0f; // invert for engine
direction.x = -direction.x; // invert for engine
direction.clampAngles ();
@ -3089,18 +3170,14 @@ void Bot::updateLookAngles () {
return;
}
if (m_difficulty == Difficulty::Expert && (m_aimFlags & AimFlags::Enemy) && (m_wantsToFire || usesSniper ()) && m_kpdRatio < 1.0f && m_healthValue < 50.0f) {
pev->v_angle = direction;
updateBodyAngles ();
return;
}
float accelerate = 3000.0f;
float stiffness = 200.0f;
float damping = 25.0f;
if ((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) && m_difficulty > Difficulty::Hard) {
if (((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) || m_wantsToFire) && m_difficulty > Difficulty::Normal) {
if (m_difficulty == Difficulty::Expert) {
accelerate += 600.0f;
}
stiffness += 100.0f;
damping += 5.0f;
}
@ -3275,14 +3352,10 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) {
}
auto bot = bots[client.ent];
if (bot == nullptr || bot == this || !bot->m_notKilled || bot->getTask ()->data == index) {
if (bot == nullptr || bot == this || !bot->m_notKilled) {
continue;
}
auto occupyId = util.getShootingCone (bot->ent (), pev->origin) >= 0.7f ? bot->m_previousNodes[0] : bot->m_currentNodeIndex;
if (index == occupyId) {
return true;
}
return index == bot->m_currentNodeIndex || bot->getTask ()->data == index;
}
return false;
}
@ -3366,10 +3439,17 @@ bool Bot::isReachableNode (int index) {
}
bool Bot::isBannedNode (int index) {
if (graph.exists (cv_debug_goal.int_ ())) {
if (graph.exists (cv_debug_goal.int_ ()) || !graph.exists (index)) {
return false;
}
for (const auto &node : m_goalHistory) {
const auto &bucket = graph.getNodesInBucket (graph[index].origin);
// too few nodes in bucket near location, do not ban anything
if (bucket.length <int> () <= kMaxNodeLinks) {
return false;
}
for (const auto &node : m_nodeHistory) {
if (node == index) {
return true;
}