]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/default/navigation.qc
Don't respawn frozen non-players when touching the void
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / navigation.qc
index 7ece113e6a858b6ff4d7263b74b6da2ba33ba13e..250b7cb09c639e45680e699514958d769b97a686 100644 (file)
@@ -13,7 +13,8 @@
 
 #include <common/constants.qh>
 #include <common/net_linked.qh>
-#include <common/triggers/trigger/jumppads.qh>
+#include <common/mapobjects/func/ladder.qh>
+#include <common/mapobjects/trigger/jumppads.qh>
 
 .float speed;
 
@@ -49,12 +50,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;
@@ -75,9 +79,11 @@ void navigation_dynamicgoal_init(entity this, bool initially_static)
                this.nearestwaypointtimeout = time;
 }
 
-void navigation_dynamicgoal_set(entity this)
+void navigation_dynamicgoal_set(entity this, entity dropper)
 {
        this.nearestwaypointtimeout = time;
+       if (dropper && dropper.nearestwaypointtimeout && dropper.nearestwaypointtimeout < time + 2)
+               this.nearestwaypoint = dropper.nearestwaypoint;
        if (this.nearestwaypoint)
                this.nearestwaypointtimeout += 2;
 }
@@ -123,8 +129,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 +262,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)
@@ -899,7 +916,7 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom
        vector pm2 = ent.origin + ent.maxs;
 
        // do two scans, because box test is cheaper
-       IL_EACH(g_waypoints, it != ent && it != except,
+       IL_EACH(g_waypoints, it != ent && it != except && !(it.wpflags & WAYPOINTFLAG_TELEPORT),
        {
                if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
                {
@@ -1201,11 +1218,7 @@ void navigation_markroutes_inverted(entity fixed_source_waypoint)
 // updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item
 void navigation_routerating(entity this, entity e, float f, float rangebias)
 {
-       if (!e)
-               return;
-
-       if(e.blacklisted)
-               return;
+       if (!e || e.blacklisted) { return; }
 
        rangebias = waypoint_getlinearcost(rangebias);
        f = waypoint_getlinearcost(f);
@@ -1213,8 +1226,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 +1249,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;
@@ -1314,10 +1331,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 || (this.items & IT_UNLIMITED_WEAPON_AMMO))
+                       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
@@ -1685,8 +1702,16 @@ int navigation_poptouchedgoals(entity this)
                        gc_min = this.goalcurrent.origin - '1 1 1' * 12;
                        gc_max = this.goalcurrent.origin + '1 1 1' * 12;
                }
-               if(!boxesoverlap(this.absmin, this.absmax, gc_min, gc_max))
-                       break;
+               if (time < this.ladder_time)
+               {
+                       if (!boxesoverlap(this.absmin, this.absmax - eZ * STAT(PL_MAX, this).z, gc_min, gc_max))
+                               break;
+               }
+               else
+               {
+                       if (!boxesoverlap(this.absmin, this.absmax, gc_min, gc_max))
+                               break;
+               }
 
                // Detect personal waypoints
                if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
@@ -1851,7 +1876,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)
@@ -1860,6 +1885,17 @@ void navigation_unstuck(entity this)
                                bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal;
                        }
                }
+
+               // move to a random waypoint while bot is searching for a walkable path;
+               // this is usually sufficient to unstuck bots from bad spots or when other
+               // bots of the same team block all their ways
+               if (!bot_waypoint_queue_bestgoal && (!this.goalentity || random() < 0.1))
+               {
+                       navigation_clearroute(this);
+                       navigation_routetogoal(this, bot_waypoint_queue_goal, this.origin);
+                       navigation_goalrating_timeout_expire(this, 1 + random() * 2);
+               }
+
                bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal;
 
                if (!bot_waypoint_queue_goal)
@@ -1867,6 +1903,7 @@ void navigation_unstuck(entity this)
                        if (bot_waypoint_queue_bestgoal)
                        {
                                LOG_DEBUG(this.netname, " stuck, reachable waypoint found, heading to it");
+                               navigation_clearroute(this);
                                navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin);
                                navigation_goalrating_timeout_set(this);
                                this.aistatus &= ~AI_STATUS_STUCK;