X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fserver%2Fbot%2Fdefault%2Fnavigation.qc;h=ac027bd22915496fe921d884d79b2358d6e6a0ea;hp=847077d695a63b07ec78d41091e49cbace328784;hb=5aab6120acfc624751d20a695d1b911b3e919831;hpb=3c141835d207df3bb5cd8b9d6d2f65b8959bcc7a diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index 847077d695..ac027bd229 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -13,7 +13,7 @@ #include #include -#include +#include .float speed; @@ -246,6 +246,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) @@ -264,8 +265,7 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e int nav_action; // Analyze starting point - traceline(start, start, MOVE_NORMAL, e); - if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) + if (IN_LAVA(start)) ignorehazards = true; tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e); @@ -731,6 +731,7 @@ void navigation_clearroute(entity this) this.goalcurrent_distance_z = FLOAT_MAX; this.goalcurrent_distance_time = 0; this.goalentity_lock_timeout = 0; + this.goalentity_shouldbefrozen = false; this.goalentity = NULL; this.goalcurrent = NULL; this.goalstack01 = NULL; @@ -1256,7 +1257,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)) @@ -1318,7 +1318,7 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel)); // enough fuel ? - if(this.ammo_fuel>fuel) + if(this.ammo_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 @@ -1387,7 +1387,6 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) nwp = e.nearestwaypoint; } - LOG_DEBUG("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")"); if (nwp && nwp.wpcost < 10000000) { //te_wizspike(nwp.wpnearestpoint); @@ -1397,12 +1396,12 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) else nwptoitem_cost = waypoint_gettravelcost(nwp.wpnearestpoint, goal_org, nwp, e); float cost = nwp.wpcost + nwptoitem_cost; - LOG_DEBUG(e.classname, " ", ftos(f), "/(1+", ftos(cost), "/", ftos(rangebias), ") = "); + LOG_DEBUG("checking ^5", e.classname, "^7 with base rating ^xf04", ftos(f), "^7 and rangebias ^xf40", ftos(rangebias)); f = f * rangebias / (rangebias + cost); - LOG_DEBUG("considering ", e.classname, " (with rating ", ftos(f), ")"); + LOG_DEBUG(" ^5", e.classname, "^7 with cost ^6", ftos(cost), "^7 and final rating ^2", ftos(f)); if (navigation_bestrating < f) { - LOG_DEBUG("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")"); + LOG_DEBUG(" ground path: ^3added goal ^5", e.classname); navigation_bestrating = f; navigation_bestgoal = e; } @@ -1474,16 +1473,20 @@ 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) { - if (nearest_wp.enemy.wpcost < autocvar_bot_ai_strategyinterval_movingtarget - && vdist(vec2(this.goalentity.origin - nearest_wp.origin), >, 16)) + if (nearest_wp.enemy.wpcost < autocvar_bot_ai_strategyinterval_movingtarget) { - set_tracewalk_dest(this.goalentity, nearest_wp.enemy.origin, true); - if (trace_ent == this || (vdist(tracewalk_dest - nearest_wp.enemy.origin, <, 1050) - && vlen2(tracewalk_dest - nearest_wp.enemy.origin) < vlen2(nearest_wp.origin - nearest_wp.enemy.origin) - && tracewalk(this, nearest_wp.enemy.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), - tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode))) - { + if (vdist(vec2(this.goalentity.origin - nearest_wp.origin), <, 32)) e = nearest_wp.enemy; + else + { + set_tracewalk_dest(this.goalentity, nearest_wp.enemy.origin, true); + if (trace_ent == this || (vdist(tracewalk_dest - nearest_wp.enemy.origin, <, 1050) + && vlen2(tracewalk_dest - nearest_wp.enemy.origin) < vlen2(nearest_wp.origin - nearest_wp.enemy.origin) + && 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; + } } } } @@ -1505,12 +1508,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; @@ -1549,8 +1552,9 @@ void navigation_shortenpath(entity this) navigation_poproute(this); } while (this.goalcurrent != next); + return true; } - return; + return false; } } @@ -1569,10 +1573,12 @@ void navigation_shortenpath(entity this) if (trace_ent == this || 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"); + 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 @@ -1588,8 +1594,7 @@ int navigation_poptouchedgoals(entity this) { // make sure jumppad is really hit, don't rely on distance based checks // as they may report a touch even if it didn't really happen - if(this.lastteleporttime > 0 - && time - this.lastteleporttime < ((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) ? 2 : 0.15)) + if(this.lastteleporttime > 0 && TELEPORT_USED(this, this.goalcurrent)) { if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this) @@ -1597,6 +1602,16 @@ int navigation_poptouchedgoals(entity this) this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; } + if(this.jumppadcount) + { + // remove jumppad waypoint after a random delay to prevent bots getting + // stuck on certain jumppads that require an extra initial horizontal speed + float max_delay = 0.1; + if (vdist(vec2(this.velocity), >, 2 * autocvar_sv_maxspeed)) + max_delay = 0.05; + if (time - this.lastteleporttime < random() * max_delay) + return removed_goals; + } navigation_poproute(this); this.lastteleporttime = 0; ++removed_goals; @@ -1604,6 +1619,37 @@ int navigation_poptouchedgoals(entity this) else return removed_goals; } + else if (this.lastteleporttime > 0) + { + // sometimes bot is pushed so hard (by a jumppad or a shot) that ends up touching the next + // teleport / jumppad / warpzone present in its path skipping check of one or more goals + // if so immediately fix bot path by removing skipped goals + entity tele_ent = NULL; + if (this.goalstack01 && (this.goalstack01.wpflags & WAYPOINTFLAG_TELEPORT)) + tele_ent = this.goalstack01; + else if (this.goalstack02 && (this.goalstack02.wpflags & WAYPOINTFLAG_TELEPORT)) + tele_ent = this.goalstack02; + else if (this.goalstack03 && (this.goalstack03.wpflags & WAYPOINTFLAG_TELEPORT)) + tele_ent = this.goalstack03; + if (tele_ent && TELEPORT_USED(this, tele_ent)) + { + if (this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) + if (tele_ent.wpflags & WAYPOINTFLAG_PERSONAL && tele_ent.owner == this) + { + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; + this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; + } + while (this.goalcurrent != tele_ent) + { + navigation_poproute(this); + ++removed_goals; + } + navigation_poproute(this); + this.lastteleporttime = 0; + ++removed_goals; + return removed_goals; + } + } // Loose goal touching check when running if(this.aistatus & AI_STATUS_RUNNING) @@ -1662,10 +1708,16 @@ int navigation_poptouchedgoals(entity this) entity navigation_get_really_close_waypoint(entity this) { entity wp = this.goalcurrent; - if(!wp || vdist(wp.origin - this.origin, >, 50)) + if(!wp) wp = this.goalcurrent_prev; if(!wp) return NULL; + if(wp != this.goalcurrent_prev && vdist(wp.origin - this.origin, >, 50)) + { + wp = this.goalcurrent_prev; + if(!wp) + return NULL; + } if(wp.classname != "waypoint") { wp = wp.nearestwaypoint; @@ -1730,6 +1782,7 @@ void navigation_goalrating_end(entity this) this.aistatus |= AI_STATUS_STUCK; } } + this.goalentity_shouldbefrozen = boolean(STAT(FROZEN, this.goalentity)); } void botframe_updatedangerousobjects(float maxupdate) @@ -1768,11 +1821,20 @@ void botframe_updatedangerousobjects(float maxupdate) void navigation_unstuck(entity this) { - float search_radius = 1000; - if (!autocvar_bot_wander_enable) return; + bool has_user_waypoints = false; + IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_GENERATED), + { + has_user_waypoints = true; + break; + }); + if (!has_user_waypoints) + return; + + float search_radius = 1000; + if (!bot_waypoint_queue_owner) { LOG_DEBUG(this.netname, " stuck, taking over the waypoints queue"); @@ -1790,7 +1852,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)