X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fbot%2Fdefault%2Fnavigation.qc;h=b21670a06357527f6a44d9ccb4d0bdd62695ec89;hb=84af4c8fa898e60fc3b083828cb4308ee0efe50c;hp=ebf2f6e16595e32be4670810e66739846700fccf;hpb=ea9b5cb05be578ab46ca4847e89b62c160edddd1;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index ebf2f6e16..b21670a06 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -1,5 +1,7 @@ #include "navigation.qh" +#include +#include #include "cvars.qh" #include "bot.qh" @@ -15,6 +17,24 @@ .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; @@ -181,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; @@ -209,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 @@ -216,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; @@ -228,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; } @@ -303,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); @@ -343,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; @@ -358,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; @@ -515,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 @@ -544,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; @@ -711,7 +738,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)) { @@ -720,12 +747,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; } } @@ -736,6 +763,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; @@ -743,22 +773,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(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 && !ent.navigation_dynamicgoal) + if(!autocvar_g_waypointeditor && walkfromwp && !ent.navigation_dynamicgoal) { waypoint_clearlinks(ent); // initialize wpXXmincost fields IL_EACH(g_waypoints, it != ent, @@ -766,8 +805,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(it, org, v2, v2_height); + if(vdist(v2 - org, <, 1050)) + if(tracewalk(ent, org, PL_MIN_CONST, PL_MAX_CONST, v2, v2_height, bot_navigation_movemode)) navigation_item_addlink(it, ent); }); } @@ -777,8 +817,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; @@ -801,10 +845,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); @@ -1158,29 +1202,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)); @@ -1227,19 +1250,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; @@ -1251,9 +1280,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; @@ -1264,6 +1293,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... @@ -1273,21 +1304,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 (;;) @@ -1305,8 +1330,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 @@ -1321,9 +1354,10 @@ void navigation_poptouchedgoals(entity this) this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; } navigation_poproute(this); + ++removed_goals; } else - return; + return removed_goals; } // If for some reason the bot is closer to the next goal, pop the current one @@ -1331,13 +1365,16 @@ void navigation_poptouchedgoals(entity this) if(vlen2(this.goalcurrent.origin - this.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 @@ -1366,6 +1403,9 @@ void navigation_poptouchedgoals(entity this) } navigation_poproute(this); + ++removed_goals; + if(this.goalcurrent && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) + return removed_goals; } } } @@ -1391,7 +1431,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) @@ -1402,9 +1481,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) @@ -1484,9 +1564,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) @@ -1503,7 +1583,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