]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/navigation.qc
Bot AI: fix tracewalk failing to reach a player when it starts from above them
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / navigation.qc
index 22e6b0d1982f21f19d03f5746210b1ce0509666c..6ae5feba09eb0486f52300aa5b134f5cba96796d 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <common/constants.qh>
 #include <common/net_linked.qh>
-#include <common/triggers/trigger/jumppads.qh>
+#include <common/mapobjects/trigger/jumppads.qh>
 
 .float speed;
 
@@ -49,12 +49,15 @@ bool navigation_goalrating_timeout(entity this)
 #define MAX_CHASE_DISTANCE 700
 bool navigation_goalrating_timeout_can_be_anticipated(entity this)
 {
-       if(time > this.bot_strategytime - (IS_MOVABLE(this.goalentity) ? 3 : 2))
+       vector gco = (this.goalentity.absmin + this.goalentity.absmax) * 0.5;
+       if (vdist(gco - this.origin, >, autocvar_sv_maxspeed * 1.5)
+               && time > this.bot_strategytime - (IS_MOVABLE(this.goalentity) ? 3 : 2))
+       {
                return true;
+       }
 
        if (this.goalentity.bot_pickup && time > this.bot_strategytime - 5)
        {
-               vector gco = (this.goalentity.absmin + this.goalentity.absmax) * 0.5;
                if(!havocbot_goalrating_item_pickable_check_players(this, this.origin, this.goalentity, gco))
                {
                        this.ignoregoal = this.goalentity;
@@ -123,8 +126,18 @@ void set_tracewalk_dest(entity ent, vector org, bool fix_player_dest)
                // z coord is set to ent's min height
                tracewalk_dest.x = bound(wm1.x, org.x, wm2.x);
                tracewalk_dest.y = bound(wm1.y, org.y, wm2.y);
-               tracewalk_dest.z = wm1.z;
-               tracewalk_dest_height = wm2.z - wm1.z; // destination height
+               if ((IS_PLAYER(ent) || IS_MONSTER(ent))
+                       && org.x == tracewalk_dest.x && org.y == tracewalk_dest.y && org.z > tracewalk_dest.z)
+               {
+                       tracewalk_dest.z = wm2.z - PL_MIN_CONST.z;
+                       tracewalk_dest_height = 0;
+                       fix_player_dest = false;
+               }
+               else
+               {
+                       tracewalk_dest.z = wm1.z;
+                       tracewalk_dest_height = wm2.z - wm1.z;
+               }
        }
        else
        {
@@ -246,6 +259,7 @@ vector resurface_limited(vector org, float lim, vector m1)
 // rough simulation of walking from one point to another to test if a path
 // can be traveled, used for waypoint linking and havocbot
 // if end_height is > 0 destination is any point in the vertical segment [end, end + end_height * eZ]
+// INFO: the command sv_cmd trace walk is useful to test this function in game
 bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float end_height, float movemode)
 {
        if(autocvar_bot_debug_tracewalk)
@@ -1213,8 +1227,11 @@ void navigation_routerating(entity this, entity e, float f, float rangebias)
        if (IS_PLAYER(e))
        {
                bool rate_wps = false;
-               if((e.flags & FL_INWATER) || (e.flags & FL_PARTIALGROUND))
+               if (e.watertype < CONTENT_WATER || (e.waterlevel > WATERLEVEL_WETFEET && !STAT(FROZEN, e))
+                       || (e.flags & FL_PARTIALGROUND))
+               {
                        rate_wps = true;
+               }
 
                if(!IS_ONGROUND(e))
                {
@@ -1233,12 +1250,13 @@ void navigation_routerating(entity this, entity e, float f, float rangebias)
                {
                        entity theEnemy = e;
                        entity best_wp = NULL;
-                       float best_dist = 10000;
-                       IL_EACH(g_waypoints, vdist(it.origin - theEnemy.origin, <, 500)
+                       float best_dist = FLOAT_MAX;
+                       IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_TELEPORT)
+                               && vdist(it.origin - theEnemy.origin, <, 500)
                                && vdist(it.origin - this.origin, >, 100)
-                               && !(it.wpflags & WAYPOINTFLAG_TELEPORT),
+                               && vdist(it.origin - this.origin, <, 10000),
                        {
-                               float dist = vlen(it.origin - theEnemy.origin);
+                               float dist = vlen2(it.origin - theEnemy.origin);
                                if (dist < best_dist)
                                {
                                        best_wp = it;
@@ -1256,7 +1274,6 @@ void navigation_routerating(entity this, entity e, float f, float rangebias)
        //print("routerating ", etos(e), " = ", ftos(f), " - ", ftos(rangebias), "\n");
 
        // Evaluate path using jetpack
-       if(g_jetpack)
        if(this.items & IT_JETPACK)
        if(autocvar_bot_ai_navigation_jetpack)
        if(vdist(this.origin - goal_org, >, autocvar_bot_ai_navigation_jetpack_mindistance))
@@ -1315,10 +1332,10 @@ void navigation_routerating(entity this, entity e, float f, float rangebias)
                        t += xydistance / autocvar_g_jetpack_maxspeed_side;
                        fuel = t * autocvar_g_jetpack_fuel * 0.8;
 
-                       LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel));
+                       LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), ", have ", ftos(GetResourceAmount(this, RESOURCE_FUEL)));
 
                        // enough fuel ?
-                       if(this.ammo_fuel>fuel)
+                       if(GetResourceAmount(this, RESOURCE_FUEL) > fuel || (this.items & IT_UNLIMITED_WEAPON_AMMO))
                        {
                                // Estimate cost
                                // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship
@@ -1508,12 +1525,12 @@ bool navigation_routetogoal(entity this, entity e, vector startposition)
 }
 
 // shorten path by removing intermediate goals
-void navigation_shortenpath(entity this)
+bool navigation_shortenpath(entity this)
 {
        if (!this.goalstack01 || wasfreed(this.goalstack01))
-               return;
+               return false;
        if (this.bot_tracewalk_time > time)
-               return;
+               return false;
        this.bot_tracewalk_time = max(time, this.bot_tracewalk_time) + 0.25;
 
        bool cut_allowed = false;
@@ -1552,8 +1569,9 @@ void navigation_shortenpath(entity this)
                                        navigation_poproute(this);
                                }
                                while (this.goalcurrent != next);
+                               return true;
                        }
-                       return;
+                       return false;
                }
        }
 
@@ -1574,8 +1592,10 @@ void navigation_shortenpath(entity this)
                {
                        LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue");
                        navigation_poproute(this);
+                       return true;
                }
        }
+       return false;
 }
 
 // removes any currently touching waypoints from the goal stack
@@ -1849,7 +1869,7 @@ void navigation_unstuck(entity this)
                float d = vlen2(this.origin - bot_waypoint_queue_goal.origin);
                LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d));
                set_tracewalk_dest(bot_waypoint_queue_goal, this.origin, false);
-               if (tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this),
+               if (tracewalk(this, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this),
                        tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode))
                {
                        if( d > bot_waypoint_queue_bestgoalrating)