]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/navigation.qc
Remove a redundant check
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / navigation.qc
index cf7181dc505e54ffa90a640f050dbb73a8459b2f..5de03e8afd3e8b6886d9047cc6d3227c9dba04c1 100644 (file)
@@ -1,5 +1,7 @@
 #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;
@@ -37,13 +57,33 @@ void navigation_dynamicgoal_unset(entity this)
        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);
@@ -161,7 +201,15 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                        {
                                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;
@@ -189,6 +237,8 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                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
@@ -196,8 +246,6 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                        //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;
@@ -208,8 +256,6 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                }
                else // horiz. direction
                {
-                       if (stepdist > flatdist)
-                               stepdist = flatdist;
                        flatdist -= stepdist;
                        move = org + flatdir * stepdist;
                }
@@ -226,12 +272,13 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
 
                                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");
@@ -239,7 +286,7 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                                        }
 
                                        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");
@@ -282,14 +329,14 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                        {
                                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);
@@ -322,7 +369,7 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float 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;
 
@@ -337,14 +384,14 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                                                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;
@@ -494,12 +541,6 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                        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
@@ -523,6 +564,13 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                                }
                        }
 
+                       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;
@@ -556,7 +604,8 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
 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;
@@ -603,7 +652,8 @@ void navigation_clearroute(entity this)
 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)
@@ -648,7 +698,8 @@ void navigation_pushroute(entity this, entity e)
 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)
@@ -690,7 +741,7 @@ void navigation_poproute(entity this)
 // 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))
        {
@@ -699,12 +750,12 @@ float navigation_waypoint_will_link(vector v, vector org, entity ent, vector v2,
                {
                        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;
                        }
                }
@@ -715,6 +766,9 @@ float navigation_waypoint_will_link(vector v, vector org, entity ent, vector v2,
 // 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;
 
@@ -722,22 +776,31 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom
        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,
@@ -745,8 +808,9 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom
                        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);
                });
        }
@@ -756,8 +820,12 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom
        {
                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;
@@ -780,10 +848,10 @@ entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
 // 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);
@@ -796,7 +864,7 @@ float navigation_markroutes_nearestwaypoints(entity this, float maxdist)
                        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;
@@ -825,7 +893,7 @@ void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost, vector
        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;
@@ -1137,29 +1205,8 @@ void navigation_routerating(entity this, entity e, float f, float rangebias)
                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));
 
@@ -1186,7 +1233,7 @@ void navigation_routerating(entity this, entity e, float f, float rangebias)
        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), ")");
@@ -1206,19 +1253,25 @@ bool navigation_routetogoal(entity this, entity e, vector startposition)
        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;
@@ -1230,9 +1283,9 @@ bool navigation_routetogoal(entity this, entity e, vector startposition)
                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;
 
@@ -1243,6 +1296,8 @@ bool navigation_routetogoal(entity this, entity e, vector startposition)
                e = e.nearestwaypoint;
                nearest_wp = e;
        }
+       else if(teleport_goal)
+               e = teleport_goal;
        else
                e = e.enemy; // we already have added it, so...
 
@@ -1252,21 +1307,15 @@ bool navigation_routetogoal(entity this, entity e, vector startposition)
        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 (;;)
@@ -1284,8 +1333,16 @@ bool navigation_routetogoal(entity this, entity e, vector startposition)
 
 // 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
@@ -1300,23 +1357,28 @@ void navigation_poptouchedgoals(entity this)
                                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
@@ -1345,6 +1407,9 @@ void navigation_poptouchedgoals(entity this)
                                }
 
                                navigation_poproute(this);
+                               ++removed_goals;
+                               if(this.goalcurrent && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+                                       return removed_goals;
                        }
                }
        }
@@ -1370,7 +1435,46 @@ void navigation_poptouchedgoals(entity this)
                }
 
                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)
@@ -1381,9 +1485,10 @@ void navigation_goalrating_start(entity this)
 
        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)
@@ -1411,11 +1516,13 @@ void botframe_updatedangerousobjects(float maxupdate)
        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;
@@ -1423,7 +1530,7 @@ void botframe_updatedangerousobjects(float maxupdate)
                        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);
@@ -1461,9 +1568,9 @@ void navigation_unstuck(entity this)
                // 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)
@@ -1480,7 +1587,7 @@ void navigation_unstuck(entity this)
                        {
                                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