#include "navigation.qh"
+#include <server/defs.qh>
+#include <server/miscfunctions.qh>
#include "cvars.qh"
#include "bot.qh"
.float speed;
+void navigation_goalrating_timeout_set(entity this)
+{
+ if(IS_MOVABLE(this.goalentity))
+ this.bot_strategytime = time + autocvar_bot_ai_strategyinterval_movingtarget;
+ else
+ this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+}
+
+void navigation_goalrating_timeout_force(entity this)
+{
+ this.bot_strategytime = 0;
+}
+
+bool navigation_goalrating_timeout(entity this)
+{
+ return this.bot_strategytime < time;
+}
+
void navigation_dynamicgoal_init(entity this, bool initially_static)
{
this.navigation_dynamicgoal = true;
this.nearestwaypointtimeout = -1;
}
+bool navigation_check_submerged_state(entity ent, vector pos)
+{
+ bool submerged;
+ if(IS_PLAYER(ent))
+ submerged = (ent.waterlevel == WATERLEVEL_SUBMERGED);
+ else if(ent.nav_submerged_state != SUBMERGED_UNDEFINED)
+ submerged = (ent.nav_submerged_state == SUBMERGED_YES);
+ else
+ {
+ submerged = SUBMERGED(pos);
+ // NOTE: SUBMERGED check of box waypoint origin may fail even if origin
+ // is actually submerged because often they are inside some solid.
+ // That's why submerged state is saved now that we know current pos is
+ // not stuck in solid (previous tracewalk call to this pos was successfully)
+ if(!ent.navigation_dynamicgoal)
+ ent.nav_submerged_state = (submerged) ? SUBMERGED_YES : SUBMERGED_NO;
+ }
+ return submerged;
+}
+
bool navigation_checkladders(entity e, vector org, vector m1, vector m2, vector end, vector end2, int movemode)
{
IL_EACH(g_ladders, it.classname == "func_ladder",
{
if(it.bot_pickup)
if(boxesoverlap(org + m1 + '-1 -1 -1', org + m2 + '1 1 1', it.absmin, it.absmax))
- if(boxesoverlap(end, end2, it.absmin + (m1 - eZ * m1.z - '1 1 0'), it.absmax + (m2 - eZ * m2.z + '1 1 0')))
+ if(boxesoverlap(end, end2, it.absmin + vec2(m1) + '-1 -1 0', it.absmax + vec2(m2) + '1 1 0'))
{
vector top = org;
top.z = it.absmax.z + (PL_MAX_CONST.z - PL_MIN_CONST.z);
{
tracebox(org, m1, m2, org - jumpheight_vec, movemode, e);
if (SUBMERGED(trace_endpos))
- RESURFACE_LIMITED(trace_endpos, end.z);
+ {
+ vector v = trace_endpos;
+ tracebox(v, m1, m2, end, movemode, e);
+ if(trace_endpos.z >= end.z - 1)
+ {
+ RESURFACE_LIMITED(v, trace_endpos.z);
+ trace_endpos = v;
+ }
+ }
else if (trace_endpos.z > org.z - jumpheight_vec.z)
tracebox(trace_endpos, m1, m2, trace_endpos + jumpheight_vec, movemode, e);
org = trace_endpos;
if (flatdist <= 0)
break;
+ if (stepdist > flatdist)
+ stepdist = flatdist;
if(nav_action == NAV_SWIM_UNDERWATER || (nav_action == NAV_SWIM_ONWATER && org.z > end2.z))
{
// can't use movement direction here to calculate move because of
//water_dir = normalize(water_end - org);
//move = org + water_dir * stepdist;
fixed_end.z = bound(end.z, org.z, end2.z);
- if (stepdist > flatdist)
- stepdist = flatdist;
if (stepdist == flatdist) {
move = fixed_end;
flatdist = 0;
}
else // horiz. direction
{
- if (stepdist > flatdist)
- stepdist = flatdist;
flatdist -= stepdist;
move = org + flatdir * stepdist;
}
if (trace_fraction < 1 || trace_startsolid) // can't jump obstacle out of water
{
- if(navigation_checkladders(e, trace_endpos, m1, m2, end, end2, movemode))
+ org = trace_endpos;
+ if(navigation_checkladders(e, org, m1, m2, end, end2, movemode))
{
if(autocvar_bot_debug_tracewalk)
{
- debugnode(e, trace_endpos);
- debugnodestatus(trace_endpos, DEBUG_NODE_SUCCESS);
+ debugnode(e, org);
+ debugnodestatus(org, DEBUG_NODE_SUCCESS);
}
//print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
}
if(autocvar_bot_debug_tracewalk)
- debugnodestatus(trace_endpos, DEBUG_NODE_FAIL);
+ debugnodestatus(org, DEBUG_NODE_FAIL);
return false;
//print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
{
tracebox(org, m1, m2, move, movemode, e); // swim
- bool stepswimmed = false;
+ bool stepswum = false;
// hit something
if (trace_fraction < 1)
{
// stepswim
vector stepswim_move = move + stepheightvec;
- if (flatdist > 0 && stepswim_move.z > end2.z) // don't allow stepswim to go higher than destination
+ if (flatdist > 0 && stepswim_move.z > end2.z + stepheightvec.z) // don't allow stepswim to go higher than destination
stepswim_move.z = end2.z;
tracebox(org + stepheightvec, m1, m2, stepswim_move, movemode, e);
nav_action = NAV_SWIM_ONWATER;
// we didn't advance horiz. in this step, flatdist decrease should be reverted
- // but we can do it properly right now... apply this workaround instead
+ // but we can't do it properly right now... apply this workaround instead
if (flatdist <= 0)
flatdist = 1;
continue;
}
- stepswimmed = true;
+ stepswum = true;
}
if (!WETFEET(trace_endpos))
{
tracebox(trace_endpos, m1, m2, trace_endpos - eZ * (stepdist + (m2.z - m1.z)), movemode, e);
- // if stepswimmed we'll land on the obstacle, avoid the SUBMERGED check
- if (!stepswimmed && SUBMERGED(trace_endpos))
+ // if stepswum we'll land on the obstacle, avoid the SUBMERGED check
+ if (!stepswum && SUBMERGED(trace_endpos))
{
RESURFACE_LIMITED(trace_endpos, end2.z);
org = trace_endpos;
else
move = trace_endpos;
- if (flatdist <= 0)
- {
- org = move;
- continue;
- }
-
// trace down from stepheight as far as possible and move there,
// if this starts in solid we try again without the stepup, and
// if that also fails we assume it is a wall
}
}
+ if (flatdist <= 0)
+ {
+ if(move.z >= end2.z && org.z < end2.z)
+ org.z = end2.z;
+ continue;
+ }
+
if(org.z > move.z - 1 || !SUBMERGED(org))
{
nav_action = NAV_WALK;
void navigation_clearroute(entity this)
{
this.goalcurrent_prev = this.goalcurrent;
- this.goalcurrent_distance = 10000000;
+ this.goalcurrent_distance_2d = FLOAT_MAX;
+ this.goalcurrent_distance_z = FLOAT_MAX;
this.goalcurrent_distance_time = 0;
//print("bot ", etos(this), " clear\n");
this.goalentity = NULL;
void navigation_pushroute(entity this, entity e)
{
this.goalcurrent_prev = this.goalcurrent;
- this.goalcurrent_distance = 10000000;
+ this.goalcurrent_distance_2d = FLOAT_MAX;
+ this.goalcurrent_distance_z = FLOAT_MAX;
this.goalcurrent_distance_time = 0;
//print("bot ", etos(this), " push ", etos(e), "\n");
if(this.goalstack31 == this.goalentity)
void navigation_poproute(entity this)
{
this.goalcurrent_prev = this.goalcurrent;
- this.goalcurrent_distance = 10000000;
+ this.goalcurrent_distance_2d = FLOAT_MAX;
+ this.goalcurrent_distance_z = FLOAT_MAX;
this.goalcurrent_distance_time = 0;
//print("bot ", etos(this), " pop\n");
if(this.goalcurrent == this.goalentity)
// walking to wp (walkfromwp == false) v2 and v2_height will be used as
// waypoint destination coordinates instead of v (only useful for box waypoints)
// for normal waypoints v2 == v and v2_height == 0
-float navigation_waypoint_will_link(vector v, vector org, entity ent, vector v2, float v2_height, float walkfromwp, float bestdist)
+float navigation_waypoint_will_link(vector v, vector org, entity ent, vector v2, float v2_height, vector o2, float o2_height, float walkfromwp, float bestdist)
{
if (vdist(v - org, <, bestdist))
{
{
if (walkfromwp)
{
- if (tracewalk(ent, v, PL_MIN_CONST, PL_MAX_CONST, org, 0, bot_navigation_movemode))
+ if (tracewalk(ent, v, PL_MIN_CONST, PL_MAX_CONST, v2, v2_height, bot_navigation_movemode))
return true;
}
else
{
- if (tracewalk(ent, org, PL_MIN_CONST, PL_MAX_CONST, v2, v2_height, bot_navigation_movemode))
+ if (tracewalk(ent, org, PL_MIN_CONST, PL_MAX_CONST, o2, o2_height, bot_navigation_movemode))
return true;
}
}
// find the spawnfunc_waypoint near a dynamic goal such as a dropped weapon
entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfromwp, float bestdist, entity except)
{
+ if(ent.tag_entity)
+ ent = ent.tag_entity;
+
vector pm1 = ent.origin + ent.mins;
vector pm2 = ent.origin + ent.maxs;
IL_EACH(g_waypoints, it != ent && it != except,
{
if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
+ {
+ if(!autocvar_g_waypointeditor && walkfromwp && !ent.navigation_dynamicgoal)
+ {
+ waypoint_clearlinks(ent); // initialize wpXXmincost fields
+ navigation_item_addlink(it, ent);
+ }
return it;
+ }
});
- vector org = ent.origin + 0.5 * (ent.mins + ent.maxs);
- org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height
- // TODO possibly make other code have the same support for bboxes
- if(ent.tag_entity)
- org = org + ent.tag_entity.origin;
+ vector org = ent.origin;
if (navigation_testtracewalk)
te_plasmaburn(org);
entity best = NULL;
- vector v, v2;
- float v2_height;
+ vector v = '0 0 0', v2 = '0 0 0';
+ float v2_height = 0;
- if(!autocvar_g_waypointeditor && !ent.navigation_dynamicgoal)
+ if(ent.size && !IS_PLAYER(ent))
+ {
+ org += 0.5 * (ent.mins + ent.maxs);
+ org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height
+ }
+
+ if(!autocvar_g_waypointeditor && walkfromwp && !ent.navigation_dynamicgoal)
{
waypoint_clearlinks(ent); // initialize wpXXmincost fields
IL_EACH(g_waypoints, it != ent,
if(walkfromwp && (it.wpflags & WAYPOINTFLAG_NORELINK))
continue;
- SET_TRACEWALK_DESTCOORDS_2(it, org, v, v2, v2_height);
- if(navigation_waypoint_will_link(v, org, ent, v2, v2_height, walkfromwp, 1050))
+ SET_TRACEWALK_DESTCOORDS(ent, it.origin, v2, v2_height);
+ if(vdist(v2 - it.origin, <, 1050))
+ if(tracewalk(ent, it.origin, PL_MIN_CONST, PL_MAX_CONST, v2, v2_height, bot_navigation_movemode))
navigation_item_addlink(it, ent);
});
}
{
if(walkfromwp && (it.wpflags & WAYPOINTFLAG_NORELINK))
continue;
- SET_TRACEWALK_DESTCOORDS_2(it, org, v, v2, v2_height);
- if(navigation_waypoint_will_link(v, org, ent, v2, v2_height, walkfromwp, bestdist))
+ v = it.origin;
+ if(walkfromwp)
+ SET_TRACEWALK_DESTCOORDS(ent, v, v2, v2_height);
+ else
+ SET_TRACEWALK_DESTCOORDS(it, org, v2, v2_height);
+ if(navigation_waypoint_will_link(v, org, ent, v2, v2_height, v2, v2_height, walkfromwp, bestdist))
{
bestdist = vlen(v - org);
best = it;
// finds the waypoints near the bot initiating a navigation query
float navigation_markroutes_nearestwaypoints(entity this, float maxdist)
{
- vector v;
+ vector v = '0 0 0';
//navigation_testtracewalk = true;
int c = 0;
- float v_height;
+ float v_height = 0;
IL_EACH(g_waypoints, !it.wpconsidered,
{
SET_TRACEWALK_DESTCOORDS(it, this.origin, v, v_height);
if (tracewalk(this, this.origin, this.mins, this.maxs, v, v_height, bot_navigation_movemode))
{
it.wpnearestpoint = v;
- it.wpcost = waypoint_gettravelcost(this.origin, v, SUBMERGED(this.origin), SUBMERGED(v)) + it.dmg;
+ it.wpcost = waypoint_gettravelcost(this.origin, v, this, it) + it.dmg;
it.wpfire = 1;
it.enemy = NULL;
c = c + 1;
if (w.wpflags & WAYPOINTFLAG_TELEPORT)
cost += w.wp00mincost; // assuming teleport has exactly one destination
else
- cost += waypoint_gettravelcost(p, v, SUBMERGED(p), SUBMERGED(v));
+ cost += waypoint_gettravelcost(p, v, w, wp);
if (wp.wpcost > cost)
{
wp.wpcost = cost;
if ((!e.nearestwaypoint || e.navigation_dynamicgoal)
&& e.nearestwaypointtimeout >= 0 && time > e.nearestwaypointtimeout)
{
- nwp = navigation_findnearestwaypoint(e, true);
- if(nwp)
- {
- e.nearestwaypoint = nwp;
-
- vector m1 = nwp.absmin, m2 = nwp.absmax;
- m1.x = nwp.origin.x; m1.y = nwp.origin.y;
- m2.x = nwp.origin.x; m2.y = nwp.origin.y;
- vector ve = (e.absmin - e.absmax) * 0.5;
- ve.x = bound(m1.x, ve.x, m2.x);
- ve.y = bound(m1.y, ve.y, m2.y);
- ve.z = bound(m1.z, ve.z, m2.z);
-
- m1 = e.absmin; m2 = e.absmax;
- m1.x = e.origin.x; m1.y = e.origin.y;
- m2.x = e.origin.x; m2.y = e.origin.y;
- vector vnwp = nwp.origin;
- vnwp.x = bound(m1.x, vnwp.x, m2.x);
- vnwp.y = bound(m1.y, vnwp.y, m2.y);
- vnwp.z = bound(m1.z, vnwp.z, m2.z);
- e.nearestwaypoint_dist = vlen(ve - vnwp);
- }
- else
+ e.nearestwaypoint = nwp = navigation_findnearestwaypoint(e, true);
+ if(!nwp)
{
LOG_DEBUG("FAILED to find a nearest waypoint to '", e.classname, "' #", etos(e));
if (nwp.wpcost < 10000000)
{
//te_wizspike(nwp.wpnearestpoint);
- float cost = nwp.wpcost + waypoint_gettravelcost(nwp.wpnearestpoint, goal_org, SUBMERGED(nwp.wpnearestpoint), SUBMERGED(goal_org));
+ float cost = nwp.wpcost + waypoint_gettravelcost(nwp.wpnearestpoint, goal_org, nwp, e);
LOG_DEBUG(e.classname, " ", ftos(f), "/(1+", ftos(cost), "/", ftos(rangebias), ") = ");
f = f * rangebias / (rangebias + cost);
LOG_DEBUG("considering ", e.classname, " (with rating ", ftos(f), ")");
if (!e)
return false;
+ entity teleport_goal = NULL;
+
+ this.goalentity = e;
+
if(e.wpflags & WAYPOINTFLAG_TELEPORT)
{
// force teleport destination as route destination
- e.wp00.enemy = e;
- e = e.wp00;
+ teleport_goal = e;
+ navigation_pushroute(this, e.wp00);
+ this.goalentity = e.wp00;
}
- this.goalentity = e;
-
// put the entity on the goal stack
//print("routetogoal ", etos(e), "\n");
navigation_pushroute(this, e);
+ if(teleport_goal)
+ e = this.goalentity;
+
if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL))
{
this.wp_goal_prev1 = this.wp_goal_prev0;
return true;
// if it can reach the goal there is nothing more to do
- vector dest = (e.absmin + e.absmax) * 0.5;
- dest.z = e.absmin.z;
- float dest_height = e.absmax.z - e.absmin.z;
+ vector dest = '0 0 0';
+ float dest_height = 0;
+ SET_TRACEWALK_DESTCOORDS(e, startposition, dest, dest_height);
if (tracewalk(this, startposition, STAT(PL_MIN, this), STAT(PL_MAX, this), dest, dest_height, bot_navigation_movemode))
return true;
e = e.nearestwaypoint;
nearest_wp = e;
}
+ else if(teleport_goal)
+ e = teleport_goal;
else
e = e.enemy; // we already have added it, so...
if(nearest_wp && nearest_wp.enemy)
{
// often path can be optimized by not adding the nearest waypoint
- if (this.goalentity.nearestwaypoint_dist < 8)
- e = nearest_wp.enemy;
- else
+ if (this.goalentity.navigation_dynamicgoal || autocvar_g_waypointeditor)
{
- if (this.goalentity.navigation_dynamicgoal)
- {
- vector dest = (this.goalentity.absmin + this.goalentity.absmax) * 0.5;
- dest.z = this.goalentity.absmin.z;
- float dest_height = this.goalentity.absmax.z - this.goalentity.absmin.z;
- if(tracewalk(this, nearest_wp.enemy.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), dest, dest_height, bot_navigation_movemode))
- e = nearest_wp.enemy;
- }
- else if(navigation_item_islinked(nearest_wp.enemy, this.goalentity))
+ SET_TRACEWALK_DESTCOORDS(this.goalentity, nearest_wp.enemy.origin, dest, dest_height);
+ if(vdist(dest - nearest_wp.enemy.origin, <, 1050))
+ if(tracewalk(this, nearest_wp.enemy.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), dest, dest_height, bot_navigation_movemode))
e = nearest_wp.enemy;
}
+ else if(navigation_item_islinked(nearest_wp.enemy, this.goalentity))
+ e = nearest_wp.enemy;
}
for (;;)
// removes any currently touching waypoints from the goal stack
// (this is how bots detect if they reached a goal)
-void navigation_poptouchedgoals(entity this)
+int navigation_poptouchedgoals(entity this)
{
+ int removed_goals = 0;
+
+ if(IS_PLAYER(this.goalcurrent) && IS_DEAD(this.goalcurrent) && checkpvs(this.origin + this.view_ofs, this.goalcurrent))
+ {
+ navigation_poproute(this);
+ ++removed_goals;
+ }
+
if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
{
// make sure jumppad is really hit, don't rely on distance based checks
this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
}
navigation_poproute(this);
+ this.lastteleporttime = 0;
+ ++removed_goals;
}
else
- return;
+ return removed_goals;
}
// If for some reason the bot is closer to the next goal, pop the current one
if(this.goalstack01 && !wasfreed(this.goalstack01))
- if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin))
+ if(vlen2(this.goalcurrent.origin - this.goalstack01.origin) > vlen2(this.goalstack01.origin - this.origin))
if(checkpvs(this.origin + this.view_ofs, this.goalstack01))
{
- vector dest = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5;
- dest.z = this.goalstack01.absmin.z;
- float dest_height = this.goalstack01.absmax.z - this.goalstack01.absmin.z;
+ vector dest = '0 0 0';
+ float dest_height = 0;
+ SET_TRACEWALK_DESTCOORDS(this.goalstack01, this.origin, dest, dest_height);
if(tracewalk(this, this.origin, this.mins, this.maxs, dest, dest_height, bot_navigation_movemode))
{
LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue");
navigation_poproute(this);
+ ++removed_goals;
+ if(this.goalcurrent && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+ return removed_goals;
// TODO this may also be a nice idea to do "early" (e.g. by
// manipulating the vlen() comparisons) to shorten paths in
// general - this would make bots walk more "on rails" than
}
navigation_poproute(this);
+ ++removed_goals;
+ if(this.goalcurrent && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+ return removed_goals;
}
}
}
}
navigation_poproute(this);
+ ++removed_goals;
+ if(this.goalcurrent && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+ return removed_goals;
}
+ return removed_goals;
+}
+
+entity navigation_get_really_close_waypoint(entity this)
+{
+ entity wp = this.goalcurrent;
+ if(!wp || vdist(wp.origin - this.origin, >, 50))
+ wp = this.goalcurrent_prev;
+ if(!wp)
+ return NULL;
+ if(wp.classname != "waypoint")
+ {
+ wp = wp.nearestwaypoint;
+ if(!wp)
+ return NULL;
+ }
+ if(vdist(wp.origin - this.origin, >, 50))
+ {
+ IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_TELEPORT),
+ {
+ if(vdist(it.origin - this.origin, <, 50))
+ {
+ wp = it;
+ break;
+ }
+ });
+ }
+ if(wp.wpflags & WAYPOINTFLAG_TELEPORT)
+ return NULL;
+
+ vector dest = '0 0 0';
+ float dest_height = 0;
+ SET_TRACEWALK_DESTCOORDS(wp, this.origin, dest, dest_height);
+ if (!tracewalk(this, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), dest, dest_height, bot_navigation_movemode))
+ return NULL;
+ return wp;
}
// begin a goal selection session (queries spawnfunc_waypoint network)
this.navigation_jetpack_goal = NULL;
navigation_bestrating = -1;
+ entity wp = navigation_get_really_close_waypoint(this);
navigation_clearroute(this);
navigation_bestgoal = NULL;
- navigation_markroutes(this, NULL);
+ navigation_markroutes(this, wp);
}
// ends a goal selection session (updates goal stack to the best goal)
vector m1, m2, v, o;
float c, d, danger;
c = 0;
+ entity wp_cur;
IL_EACH(g_waypoints, true,
{
danger = 0;
m1 = it.absmin;
m2 = it.absmax;
+ wp_cur = it;
IL_EACH(g_bot_dodge, it.bot_dodge,
{
v = it.origin;
v.y = bound(m1_y, v.y, m2_y);
v.z = bound(m1_z, v.z, m2_z);
o = (it.absmin + it.absmax) * 0.5;
- d = waypoint_getlinearcost(it.bot_dodgerating) - waypoint_gettravelcost(o, v, SUBMERGED(o), SUBMERGED(v));
+ d = waypoint_getlinearcost(it.bot_dodgerating) - waypoint_gettravelcost(o, v, it, wp_cur);
if (d > 0)
{
traceline(o, v, true, NULL);
// evaluate the next goal on the queue
float d = vlen2(this.origin - bot_waypoint_queue_goal.origin);
LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d));
- vector dest = (bot_waypoint_queue_goal.absmin + bot_waypoint_queue_goal.absmax) * 0.5;
- dest.z = bot_waypoint_queue_goal.absmin.z;
- float dest_height = bot_waypoint_queue_goal.absmax.z - bot_waypoint_queue_goal.absmin.z;
+ vector dest = '0 0 0';
+ float dest_height = 0;
+ SET_TRACEWALK_DESTCOORDS(bot_waypoint_queue_goal, this.origin, dest, dest_height);
if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), dest, dest_height, bot_navigation_movemode))
{
if( d > bot_waypoint_queue_bestgoalrating)
{
LOG_DEBUG(this.netname, " stuck, reachable waypoint found, heading to it");
navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin);
- this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ navigation_goalrating_timeout_set(this);
this.aistatus &= ~AI_STATUS_STUCK;
}
else