From 26280ae0d91f80b27f03ee645c76d1234a4215b7 Mon Sep 17 00:00:00 2001 From: terencehill Date: Thu, 9 Nov 2017 09:32:57 +0100 Subject: [PATCH] Bot AI: allow bots to chase players in the air by adjusting tracewalk destination point; while at it turn 3 long macros into functions --- qcsrc/server/bot/api.qh | 4 + qcsrc/server/bot/default/havocbot/havocbot.qc | 22 +-- qcsrc/server/bot/default/navigation.qc | 155 ++++++++++++++---- qcsrc/server/bot/default/navigation.qh | 57 +------ qcsrc/server/bot/default/waypoints.qc | 8 +- 5 files changed, 142 insertions(+), 104 deletions(-) diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh index 12f05c752..39285f77e 100644 --- a/qcsrc/server/bot/api.qh +++ b/qcsrc/server/bot/api.qh @@ -94,6 +94,10 @@ void navigation_markroutes(entity this, entity fixed_source_waypoint); void navigation_markroutes_inverted(entity fixed_source_waypoint); void navigation_routerating(entity this, entity e, float f, float rangebias); +vector get_closer_dest(entity ent, vector org); + +void set_tracewalk_dest(entity ent, vector org, bool fix_player_dest); +vector set_tracewalk_dest_2(entity ent, vector org); bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float end_height, float movemode); void waypoint_remove_fromeditor(entity pl); diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index 7ca929859..a383dd87c 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -141,10 +141,10 @@ void havocbot_ai(entity this) this.aistatus |= AI_STATUS_ROAMING; this.aistatus &= ~AI_STATUS_ATTACKING; - vector v = '0 0 0', now, next; + vector now, next; float aimdistance,skillblend,distanceblend,blend; - SET_DESTCOORDS(this.goalcurrent, this.origin, v); + vector v = get_closer_dest(this.goalcurrent, this.origin); if(this.goalcurrent.wpisbox) { // avoid a glitch when bot is teleported but teleport waypoint isn't removed yet @@ -289,7 +289,6 @@ void havocbot_bunnyhop(entity this, vector dir) float bunnyhopdistance; vector deviation; float maxspeed; - vector gco = '0 0 0', gno; // Don't jump when attacking if(this.aistatus & AI_STATUS_ATTACKING) @@ -322,7 +321,7 @@ void havocbot_bunnyhop(entity this, vector dir) this.bot_timelastseengoal = 0; } - SET_DESTCOORDS(this.goalcurrent, this.origin, gco); + vector gco = get_closer_dest(this.goalcurrent, this.origin); bunnyhopdistance = vlen(this.origin - gco); // Run only to visible goals @@ -359,7 +358,7 @@ void havocbot_bunnyhop(entity this, vector dir) if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z) if(this.goalstack01 && !wasfreed(this.goalstack01)) { - gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5; + vector gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5; deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.origin); while (deviation.y < -180) deviation.y = deviation.y + 360; while (deviation.y > 180) deviation.y = deviation.y - 360; @@ -456,7 +455,6 @@ bool havocbot_checkgoaldistance(entity this, vector gco) void havocbot_movetogoal(entity this) { - vector destorg = '0 0 0'; vector diff; vector dir; vector flatdir; @@ -794,7 +792,7 @@ void havocbot_movetogoal(entity this) debuggoalstack(this); bool bunnyhop_forbidden = false; - SET_DESTCOORDS(this.goalcurrent, this.origin, destorg); + vector destorg = get_closer_dest(this.goalcurrent, this.origin); // in case bot ends up inside the teleport waypoint without touching // the teleport itself, head to the teleport origin @@ -887,10 +885,9 @@ void havocbot_movetogoal(entity this) return; } - vector dest = '0 0 0'; - float dest_height = 0; - SET_TRACEWALK_DESTCOORDS(this.goalcurrent, this.origin, dest, dest_height); - if (!tracewalk(this, this.origin, this.mins, this.maxs, dest, dest_height, bot_navigation_movemode)) + set_tracewalk_dest(this.goalcurrent, this.origin, false); + if (!tracewalk(this, this.origin, this.mins, this.maxs, + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) { navigation_clearroute(this); navigation_goalrating_timeout_force(this); @@ -1345,8 +1342,7 @@ float havocbot_moveto(entity this, vector pos) debuggoalstack(this); // Heading - vector dir = '0 0 0'; - SET_DESTCOORDS(this.goalcurrent, this.origin, dir); + vector dir = get_closer_dest(this.goalcurrent, this.origin); dir = dir - (this.origin + this.view_ofs); dir.z = 0; bot_aimdir(this, dir, -1); diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index 3ac966196..c26694994 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -88,6 +88,87 @@ void navigation_dynamicgoal_unset(entity this) this.nearestwaypointtimeout = -1; } +// returns point of ent closer to org +vector get_closer_dest(entity ent, vector org) +{ + vector dest = '0 0 0'; + if ((ent.classname != "waypoint") || ent.wpisbox) + { + vector wm1 = ent.origin + ent.mins - eZ * (PL_MAX_CONST.z - 1); + vector wm2 = ent.origin + ent.maxs - eZ * (PL_MIN_CONST.z + 1); + dest.x = bound(wm1.x, org.x, wm2.x); + dest.y = bound(wm1.y, org.y, wm2.y); + dest.z = bound(wm1.z, org.z, wm2.z); + } + else + dest = ent.origin; + return dest; +} + +void set_tracewalk_dest(entity ent, vector org, bool fix_player_dest) +{ + if ((ent.classname != "waypoint") || ent.wpisbox) + { + vector wm1 = ent.origin + ent.mins - eZ * (PL_MAX_CONST.z - 1); + vector wm2 = ent.origin + ent.maxs - eZ * (PL_MIN_CONST.z + 1); + if (IS_PLAYER(ent) || IS_MONSTER(ent)) + { + // move destination point out of player bbox otherwise tracebox always fails + // (if bot_navigation_ignoreplayers is false) + wm1 += vec2(PL_MIN_CONST) + '-1 -1 0'; + wm2 += vec2(PL_MAX_CONST) + '1 1 0'; + } + // set destination point to x and y coords of ent that are closer to org + // 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 + } + else + { + tracewalk_dest = ent.origin; + tracewalk_dest_height = 0; + } + if (fix_player_dest && IS_PLAYER(ent) && !IS_ONGROUND(ent)) + { + // snap player to the ground + tracebox(tracewalk_dest, ent.mins, ent.maxs, tracewalk_dest - '0 0 700', MOVE_NORMAL, ent); + if (!trace_startsolid && tracewalk_dest.z - trace_endpos.z > 0) + { + tracewalk_dest_height = tracewalk_dest.z - trace_endpos.z; + tracewalk_dest.z = trace_endpos.z; + } + } +} + +// returns point of ent closer to org +vector set_tracewalk_dest_2(entity ent, vector org) +{ + vector closer_dest = '0 0 0'; + if ((ent.classname != "waypoint") || ent.wpisbox) + { + vector wm1 = ent.origin + ent.mins - eZ * (PL_MAX_CONST.z - 1); + vector wm2 = ent.origin + ent.maxs - eZ * (PL_MIN_CONST.z + 1); + closer_dest.x = bound(wm1.x, org.x, wm2.x); + closer_dest.y = bound(wm1.y, org.y, wm2.y); + closer_dest.z = bound(wm1.z, org.z, wm2.z); + // set destination point to x and y coords of ent that are closer to org + // z coord is set to ent's min height + tracewalk_dest.x = closer_dest.x; + tracewalk_dest.y = closer_dest.y; + tracewalk_dest.z = wm1.z; + tracewalk_dest_height = wm2.z - wm1.z; // destination height + } + else + { + closer_dest = ent.origin; + tracewalk_dest = closer_dest; + tracewalk_dest_height = 0; + } + return closer_dest; +} + bool navigation_check_submerged_state(entity ent, vector pos) { bool submerged; @@ -825,8 +906,7 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom te_plasmaburn(org); entity best = NULL; - vector v = '0 0 0', v2 = '0 0 0'; - float v2_height = 0; + vector v = '0 0 0'; if(ent.size && !IS_PLAYER(ent)) { @@ -842,10 +922,13 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom if(walkfromwp && (it.wpflags & WAYPOINTFLAG_NORELINK)) continue; - 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)) + set_tracewalk_dest(ent, it.origin, false); + if (vdist(tracewalk_dest - it.origin, <, 1050) + && tracewalk(ent, it.origin, PL_MIN_CONST, PL_MAX_CONST, + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) + { navigation_item_addlink(it, ent); + } }); } @@ -856,10 +939,12 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom continue; v = it.origin; if(walkfromwp) - SET_TRACEWALK_DESTCOORDS(ent, v, v2, v2_height); + set_tracewalk_dest(ent, v, false); 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)) + set_tracewalk_dest(it, org, false); + if (navigation_waypoint_will_link(v, org, ent, + tracewalk_dest, tracewalk_dest_height, + tracewalk_dest, tracewalk_dest_height, walkfromwp, bestdist)) { bestdist = vlen(v - org); best = it; @@ -896,23 +981,22 @@ 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 = '0 0 0'; //navigation_testtracewalk = true; int c = 0; - float v_height = 0; IL_EACH(g_waypoints, !it.wpconsidered, { - SET_TRACEWALK_DESTCOORDS(it, this.origin, v, v_height); + set_tracewalk_dest(it, this.origin, false); - vector diff = v - this.origin; + vector diff = tracewalk_dest - this.origin; diff.z = max(0, diff.z); if(vdist(diff, <, maxdist)) { it.wpconsidered = true; - if (tracewalk(this, this.origin, this.mins, this.maxs, v, v_height, bot_navigation_movemode)) + if (tracewalk(this, this.origin, this.mins, this.maxs, + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) { - it.wpnearestpoint = v; - it.wpcost = waypoint_gettravelcost(this.origin, v, this, it) + it.dmg; + it.wpnearestpoint = tracewalk_dest; + it.wpcost = waypoint_gettravelcost(this.origin, tracewalk_dest, this, it) + it.dmg; it.wpfire = 1; it.enemy = NULL; c = c + 1; @@ -1338,11 +1422,12 @@ 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 = '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)) + set_tracewalk_dest(e, startposition, true); + if (tracewalk(this, startposition, STAT(PL_MIN, this), STAT(PL_MAX, this), + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) + { return true; + } entity nearest_wp = NULL; // see if there are waypoints describing a path to the item @@ -1364,10 +1449,13 @@ bool navigation_routetogoal(entity this, entity e, vector startposition) // often path can be optimized by not adding the nearest waypoint if (this.goalentity.navigation_dynamicgoal || autocvar_g_waypointeditor) { - 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)) + set_tracewalk_dest(this.goalentity, nearest_wp.enemy.origin, true); + if (vdist(tracewalk_dest - nearest_wp.enemy.origin, <, 1050) + && tracewalk(this, nearest_wp.enemy.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) + { e = nearest_wp.enemy; + } } else if(navigation_item_islinked(nearest_wp.enemy, this.goalentity)) e = nearest_wp.enemy; @@ -1425,10 +1513,9 @@ int navigation_poptouchedgoals(entity this) 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 = '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)) + set_tracewalk_dest(this.goalstack01, this.origin, false); + if(tracewalk(this, this.origin, this.mins, this.maxs, + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) { LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue"); navigation_poproute(this); @@ -1525,11 +1612,12 @@ entity navigation_get_really_close_waypoint(entity this) 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)) + set_tracewalk_dest(wp, this.origin, false); + if (!tracewalk(this, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) + { return NULL; + } return wp; } @@ -1624,10 +1712,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 = '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)) + 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), + tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) { if( d > bot_waypoint_queue_bestgoalrating) { diff --git a/qcsrc/server/bot/default/navigation.qh b/qcsrc/server/bot/default/navigation.qh index 63d066b51..90e959a26 100644 --- a/qcsrc/server/bot/default/navigation.qh +++ b/qcsrc/server/bot/default/navigation.qh @@ -50,61 +50,8 @@ entity navigation_bestgoal; #define navigation_item_addlink(from_wp, to_item) \ waypoint_addlink_customcost(to_item, from_wp, waypoint_getlinkcost(from_wp, to_item)) -// if ent is a box waypoint or an item v is set to coords of ent that are closer to org -#define SET_DESTCOORDS(ent, org, v) MACRO_BEGIN { \ - if ((ent.classname != "waypoint") || ent.wpisbox) { \ - vector wm1 = ent.origin + ent.mins - eZ * (PL_MAX_CONST.z - 1); \ - vector wm2 = ent.origin + ent.maxs - eZ * (PL_MIN_CONST.z + 1); \ - v.x = bound(wm1.x, org.x, wm2.x); \ - v.y = bound(wm1.y, org.y, wm2.y); \ - v.z = bound(wm1.z, org.z, wm2.z); \ - } else { \ - v = ent.origin; \ - } \ -} MACRO_END - -// if ent is a box waypoint or an item v is set to coords of ent that are closer to org -// (but v.z is set to the lowest coord of ent), v_height is set to ent's height -// if destination ent is a player make so that destination point doesn't overlap with -// player bbox, otherwise tracebox always fails (if bot_navigation_ignoreplayers is false) -#define SET_TRACEWALK_DESTCOORDS(ent, org, v, v_height) MACRO_BEGIN { \ - if ((ent.classname != "waypoint") || ent.wpisbox) { \ - vector wm1 = ent.origin + ent.mins - eZ * (PL_MAX_CONST.z - 1); \ - vector wm2 = ent.origin + ent.maxs - eZ * (PL_MIN_CONST.z + 1); \ - if (IS_PLAYER(ent) || IS_MONSTER(ent)) \ - { \ - wm1 += vec2(PL_MIN_CONST) + '-1 -1 0'; \ - wm2 += vec2(PL_MAX_CONST) + '1 1 0'; \ - } \ - v.x = bound(wm1.x, org.x, wm2.x); \ - v.y = bound(wm1.y, org.y, wm2.y); \ - v.z = wm1.z; \ - v_height = wm2.z - wm1.z; \ - } else { \ - v = ent.origin; \ - v_height = 0; \ - } \ -} MACRO_END - -// if ent is a box waypoint or an item v and v2 are set to coords of ent that are closer to org -// (but v2.z is set to the lowest coord of ent), v2_height is set to ent's height -#define SET_TRACEWALK_DESTCOORDS_2(ent, org, v, v2, v2_height) MACRO_BEGIN { \ - if ((ent.classname != "waypoint") || ent.wpisbox) { \ - vector wm1 = ent.origin + ent.mins - eZ * (PL_MAX_CONST.z - 1); \ - vector wm2 = ent.origin + ent.maxs - eZ * (PL_MIN_CONST.z + 1); \ - v.x = bound(wm1.x, org.x, wm2.x); \ - v.y = bound(wm1.y, org.y, wm2.y); \ - v.z = bound(wm1.z, org.z, wm2.z); \ - v2.x = v.x; \ - v2.y = v.y; \ - v2.z = wm1.z; \ - v2_height = wm2.z - wm1.z; \ - } else { \ - v = ent.origin; \ - v2 = v; \ - v2_height = 0; \ - } \ -} MACRO_END +vector tracewalk_dest; +float tracewalk_dest_height; .entity wp_goal_prev0; .entity wp_goal_prev1; diff --git a/qcsrc/server/bot/default/waypoints.qc b/qcsrc/server/bot/default/waypoints.qc index c3b67a378..fed491b68 100644 --- a/qcsrc/server/bot/default/waypoints.qc +++ b/qcsrc/server/bot/default/waypoints.qc @@ -583,8 +583,12 @@ void waypoint_think(entity this) continue; } - SET_TRACEWALK_DESTCOORDS_2(this, it.origin, sv, sv2, sv2_height); - SET_TRACEWALK_DESTCOORDS_2(it, this.origin, ev, ev2, ev2_height); + sv = set_tracewalk_dest_2(this, it.origin); + sv2 = tracewalk_dest; + sv2_height = tracewalk_dest_height; + ev = set_tracewalk_dest_2(it, this.origin); + ev2 = tracewalk_dest; + ev2_height = tracewalk_dest_height; dv = ev - sv; dv.z = 0; -- 2.39.2