]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Bot AI: fix and greatly improve underwater navigation; normal navigation: allow reach...
authorterencehill <piuntn@gmail.com>
Fri, 21 Jul 2017 18:57:18 +0000 (20:57 +0200)
committerterencehill <piuntn@gmail.com>
Sun, 23 Jul 2017 15:42:06 +0000 (17:42 +0200)
qcsrc/server/bot/default/navigation.qc

index 48c36a03574ee088221316d1927359b088897995..8fa2fd82c64f2f7da1af35adb2e3741443054498 100644 (file)
@@ -57,15 +57,34 @@ bool navigation_checkladders(entity e, vector org, vector m1, vector m2, vector
 
 #define IN_WATER(pos) (cont = pointcontents(pos), (cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME))
 #define SUBMERGED(pos) IN_WATER(pos + autocvar_sv_player_viewoffset)
-#define WATERFEET(pos) IN_WATER(pos + eZ * (m1.z + 1))
-
-#define RESURFACE(org) MACRO_BEGIN { \
-       while(org.z + 4 < end2.z) { \
-               if(!WATERFEET(org + eZ * 4)) \
-                       break; \
-               org.z += 4; \
-       } \
-} MACRO_END
+#define WETFEET(pos) IN_WATER(pos + eZ * (m1.z + 1))
+
+vector resurface_limited(vector org, float lim, vector m1)
+{
+       int cont;
+       if (WETFEET(org + eZ * (lim - org.z)))
+               org.z = lim;
+       else
+       {
+               float RES_min_h = org.z;
+               float RES_max_h = lim;
+               do {
+                       org.z = 0.5 * (RES_min_h + RES_max_h);
+                       if(WETFEET(org))
+                               RES_min_h = org.z;
+                       else
+                               RES_max_h = org.z;
+               } while (RES_max_h - RES_min_h >= 1);
+               org.z = RES_min_h;
+       }
+       return org;
+}
+#define RESURFACE_LIMITED(org, lim) org = resurface_limited(org, lim, m1)
+
+#define NAV_WALK 0
+#define NAV_SWIM_ONWATER 1
+#define NAV_SWIM_UNDERWATER 2
+
 // 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]
@@ -84,9 +103,8 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
        dir = normalize(dir);
        float stepdist = 32;
        bool ignorehazards = false;
-       bool swimming = false;
-       bool swimming_outwater = false;
        int cont;
+       int nav_action;
 
        // Analyze starting point
        traceline(start, start, MOVE_NORMAL, e);
@@ -112,18 +130,70 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
        vector end2 = end;
        if(end_height)
                end2.z += end_height;
+
+       vector fixed_end = end;
+       vector move;
+
+       if (dist > 0 && WETFEET(org))
+       {
+               if (SUBMERGED(org))
+                       nav_action = NAV_SWIM_UNDERWATER;
+               else
+               {
+                       // tracebox down by player's height
+                       // useful to know if water level is so low that bot can still walk
+                       tracebox(org, m1, m2, org - eZ * (m2.z - m1.z), movemode, e);
+                       if (SUBMERGED(trace_endpos))
+                       {
+                               org = trace_endpos;
+                               nav_action = NAV_SWIM_UNDERWATER;
+                       }
+                       else
+                               nav_action = NAV_WALK;
+               }
+       }
+       else
+               nav_action =  NAV_WALK;
+
        // Movement loop
-       for (;;)
+       while (true)
        {
-               if (boxesoverlap(end, end2, org + '-1 -1 -1', org + '1 1 1'))
+               if (dist <= 0)
                {
-                       // Succeeded
-                       if(autocvar_bot_debug_tracewalk)
-                               debugnodestatus(org, DEBUG_NODE_SUCCESS);
+                       bool success = true;
+                       if (org.z > end2.z + 1)
+                       {
+                               tracebox(org, m1, m2, end2, movemode, e);
+                               org = trace_endpos;
+                               if (org.z > end2.z + 1)
+                                       success = false;
+                       }
+                       else if (org.z < end.z - 1)
+                       {
+                               tracebox(org, m1, m2, org - jumpheight_vec, movemode, e);
+                               if (SUBMERGED(trace_endpos))
+                                       RESURFACE_LIMITED(trace_endpos, end.z);
+                               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 (org.z < end.z - 1)
+                                       success = false;
+                       }
 
-                       //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
-                       return true;
+                       if (success)
+                       {
+                               // Succeeded
+                               if(autocvar_bot_debug_tracewalk)
+                               {
+                                       debugnode(e, org);
+                                       debugnodestatus(org, DEBUG_NODE_SUCCESS);
+                               }
+
+                               //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
+                               return true;
+                       }
                }
+
                if(autocvar_bot_debug_tracewalk)
                        debugnode(e, org);
 
@@ -144,126 +214,230 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                                return false;
                        }
                }
-               if ((trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK) || swimming)
+
+               if(nav_action == NAV_SWIM_UNDERWATER || (nav_action == NAV_SWIM_ONWATER && org.z > end2.z))
                {
-                       vector water_end = end;
-                       water_end.z = bound(end.z, org.z, end2.z);
-                       vector move;
-                       if(swimming_outwater)
-                       {
-                               if (stepdist > dist)
-                                       stepdist = dist;
-                               dist -= stepdist;
-                               move = org + dir * stepdist;
-                       }
-                       else
-                       {
-                               // can't use movement direction here to calculate move because of precision errors
-                               // especially when direction has a high enough z value
-                               //water_dir = normalize(water_end - org);
-                               //move = org + water_dir * stepdist;
-
-                               if (stepdist > dist)
-                                       stepdist = dist;
-                               move = org + (water_end - org) * (stepdist / dist);
-                               dist = vlen(vec2(water_end - move));
+                       // can't use movement direction here to calculate move because of
+                       // precision errors especially when direction has a high enough z value
+                       //water_dir = normalize(water_end - org);
+                       //move = org + water_dir * stepdist;
+                       fixed_end.z = bound(end.z, org.z, end2.z);
+                       if (stepdist > dist)
+                               stepdist = dist;
+                       if (stepdist == dist) {
+                               move = fixed_end;
+                               dist = 0;
+                       } else {
+                               move = org + (fixed_end - org) * (stepdist / dist);
+                               dist = vlen(vec2(fixed_end - move));
                        }
+               }
+               else // horiz. direction
+               {
+                       if (stepdist > dist)
+                               stepdist = dist;
+                       dist -= stepdist;
+                       move = org + dir * stepdist;
+               }
 
-                       tracebox(org, m1, m2, move, movemode, e);
-                       if (trace_fraction < 1) // cant swim in the current direction
+               if(nav_action == NAV_SWIM_ONWATER)
+               {
+                       tracebox(org, m1, m2, move, movemode, e); // swim
+
+                       // hit something
+                       if (trace_fraction < 1)
                        {
-                               if(dist <= 0)
-                                       tracebox(org + stepheightvec, m1, m2, move, movemode, e);
-                               else
-                                       tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
-                               if (trace_startsolid)
+                               // stepswim
+                               tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, 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))
+                                       {
+                                               if(autocvar_bot_debug_tracewalk)
+                                               {
+                                                       debugnode(e, trace_endpos);
+                                                       debugnodestatus(trace_endpos, DEBUG_NODE_SUCCESS);
+                                               }
+
+                                               //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
+                                               return true;
+                                       }
+
                                        if(autocvar_bot_debug_tracewalk)
-                                               debugnodestatus(org, DEBUG_NODE_FAIL);
+                                               debugnodestatus(trace_endpos, DEBUG_NODE_FAIL);
 
                                        return false;
-                                       //print("tracewalk: ", vtos(start), " failed under water\n");
+                                       //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
                                }
 
-                               if (trace_fraction < 1) // cant step-swim in the current direction
+                               //succesful stepswim
+
+                               if (dist <= 0)
                                {
-                                       if(autocvar_bot_debug_tracewalk)
-                                               debugnodestatus(org, DEBUG_NODE_WARNING);
+                                       org = trace_endpos;
+                                       continue;
+                               }
+
+                               if (org.z <= move.z) // going horiz.
+                               {
+                                       tracebox(trace_endpos, m1, m2, move, movemode, e);
+                                       org = trace_endpos;
+                                       nav_action = NAV_WALK;
+                                       continue;
+                               }
+                       }
 
-                                       if(WATERFEET(org))
+                       if (org.z <= move.z) // going horiz.
+                       {
+                               org = trace_endpos;
+                               nav_action = NAV_SWIM_ONWATER;
+                       }
+                       else // going down
+                       {
+                               org = trace_endpos;
+                               if (SUBMERGED(org))
+                                       nav_action = NAV_SWIM_UNDERWATER;
+                               else
+                                       nav_action = NAV_SWIM_ONWATER;
+                       }
+               }
+               else if(nav_action == NAV_SWIM_UNDERWATER)
+               {
+                       if (move.z >= org.z) // swimming upwards or horiz.
+                       {
+                               tracebox(org, m1, m2, move, movemode, e); // swim
+
+                               bool stepswimmed = false;
+
+                               // hit something
+                               if (trace_fraction < 1)
+                               {
+                                       // stepswim
+                                       vector stepswim_move = move + stepheightvec;
+                                       if (dist > 0 && stepswim_move.z > end2.z) // don't allow stepswim to go higher than destination
+                                               stepswim_move.z = end2.z;
+
+                                       tracebox(org + stepheightvec, m1, m2, stepswim_move, movemode, e);
+
+                                       // hit something
+                                       if (trace_startsolid)
                                        {
-                                               RESURFACE(org);
                                                if(autocvar_bot_debug_tracewalk)
-                                                       debugnode(e, org);
-
-                                               move = org + dir * stepdist;
+                                                       debugnodestatus(org, DEBUG_NODE_FAIL);
 
-                                               tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
+                                               //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
+                                               return false;
                                        }
 
-                                       if (trace_fraction < 1 || trace_startsolid) // can't jump obstacle out of water
+                                       if (trace_fraction < 1)
                                        {
-                                               vector v = trace_endpos - stepheightvec + jumpheight_vec;
-                                               if(navigation_checkladders(e, v, m1, m2, end, end2, movemode))
+                                               float org_z_prev = org.z;
+                                               RESURFACE_LIMITED(org, end2.z);
+                                               if(org.z == org_z_prev)
                                                {
                                                        if(autocvar_bot_debug_tracewalk)
-                                                       {
-                                                               debugnode(e, v);
-                                                               debugnodestatus(v, DEBUG_NODE_SUCCESS);
-                                                       }
+                                                               debugnodestatus(org, DEBUG_NODE_FAIL);
 
-                                                       //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
-                                                       return true;
+                                                       //print("tracewalk: ", vtos(start), " can't reach ", vtos(end), "\n");
+                                                       return false;
                                                }
+                                               if(SUBMERGED(org))
+                                                       nav_action = NAV_SWIM_UNDERWATER;
+                                               else
+                                                       nav_action = NAV_SWIM_ONWATER;
 
-                                               if(autocvar_bot_debug_tracewalk)
-                                                       debugnodestatus(org, DEBUG_NODE_FAIL);
+                                               // we didn't advance horiz. in this step, dist decrease should be reverted
+                                               // but we can do it properly right now... apply this workaround instead
+                                               if (dist <= 0)
+                                                       dist = 1;
 
-                                               return false;
-                                               //print("tracewalk: ", vtos(start), " failed under water\n");
+                                               continue;
                                        }
 
-                                       // successfully jumped obstacle up out of water
-                                       move = trace_endpos;
-                                       tracebox(move, m1, m2, move + '0 0 -65536', movemode, e);
-                                       if(autocvar_bot_debug_tracewalk && trace_endpos != move)
-                                               debugnode(e, move);
-                                       org = trace_endpos;
+                                       //succesful stepswim
 
-                                       swimming = false;
-                                       swimming_outwater = false;
+                                       if (dist <= 0)
+                                       {
+                                               org = trace_endpos;
+                                               continue;
+                                       }
 
-                                       continue;
+                                       stepswimmed = true;
                                }
-                       }
 
-                       float height = trace_endpos.z - org.z;
+                               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))
+                                       {
+                                               RESURFACE_LIMITED(trace_endpos, end2.z);
+                                               org = trace_endpos;
+                                               nav_action = NAV_SWIM_ONWATER;
+                                               continue;
+                                       }
 
-                       // successfully advanced by swimming or step-swimming
-                       org = trace_endpos;
+                                       // not submerged
+                                       org = trace_endpos;
+                                       nav_action = NAV_WALK;
+                                       continue;
+                               }
 
-                       if(height > 0 && (swimming || !SUBMERGED(org)))
+                               // wetfeet
+                               org = trace_endpos;
+                               nav_action = NAV_SWIM_UNDERWATER;
+                               continue;
+                       }
+                       else //if (move.z < org.z) // swimming downwards
                        {
-                               swimming = true;
-                               if(!swimming_outwater && !WATERFEET(org))
+                               tracebox(org, m1, m2, move, movemode, e); // swim
+
+                               // hit something
+                               if (trace_fraction < 1)
                                {
-                                       // put it back in the water if it gets out of water
-                                       do {
-                                               org.z -= 4;
-                                               if(WATERFEET(org))
-                                                       break;
-                                       } while(org.z > height);
-                                       swimming_outwater = true;
+                                       // stepswim
+                                       tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
+
+                                       // hit something
+                                       if (trace_fraction < 1 || trace_startsolid) // can't jump obstacle out of water
+                                       {
+                                               if(autocvar_bot_debug_tracewalk)
+                                                       debugnodestatus(move, DEBUG_NODE_FAIL);
+
+                                               //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
+                                               return false;
+                                       }
+
+                                       //succesful stepswim
+
+                                       if (dist <= 0)
+                                       {
+                                               org = trace_endpos;
+                                               continue;
+                                       }
+
+                                       if (trace_endpos.z > org.z && !SUBMERGED(trace_endpos))
+                                       {
+                                               // stepswim caused upwards direction
+                                               tracebox(trace_endpos, m1, m2, trace_endpos - stepheightvec, movemode, e);
+                                               if (!SUBMERGED(trace_endpos))
+                                               {
+                                                       org = trace_endpos;
+                                                       nav_action = NAV_WALK;
+                                                       continue;
+                                               }
+                                       }
                                }
+
+                               org = trace_endpos;
+                               nav_action = NAV_SWIM_UNDERWATER;
+                               continue;
                        }
                }
-               else // if (!((trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK) || swimming))
+               else if(nav_action == NAV_WALK)
                {
-                       if (stepdist > dist)
-                               stepdist = dist;
-                       dist -= stepdist;
-
-                       vector move = org + dir * stepdist;
+                       // walk
                        tracebox(org, m1, m2, move, movemode, e);
 
                        if(autocvar_bot_debug_tracewalk)
@@ -329,6 +503,12 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
                        else
                                move = trace_endpos;
 
+                       if (dist <= 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
@@ -337,15 +517,19 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e
 
                        org = trace_endpos;
 
-                       if(org.z < move.z && SUBMERGED(org))
+                       if(org.z > move.z - 1 || !SUBMERGED(org))
                        {
-                               // ended up underwater while walking, resurface
-                               if(autocvar_bot_debug_tracewalk)
-                                       debugnode(e, org);
-                               RESURFACE(org);
-                               swimming = true;
-                               swimming_outwater = true;
+                               nav_action = NAV_WALK;
+                               continue;
                        }
+
+                       // ended up submerged while walking
+                       if(autocvar_bot_debug_tracewalk)
+                               debugnode(e, org);
+
+                       RESURFACE_LIMITED(org, move.z);
+                       nav_action = NAV_SWIM_ONWATER;
+                       continue;
                }
        }