#include "navigation.qh"
-#include <server/defs.qh>
-#include <server/miscfunctions.qh>
-#include "cvars.qh"
-
-#include "bot.qh"
-#include "waypoints.qh"
-
-#include <common/t_items.qh>
-
-#include <common/items/_mod.qh>
-
#include <common/constants.qh>
-#include <common/net_linked.qh>
+#include <common/items/_mod.qh>
#include <common/mapobjects/func/ladder.qh>
+#include <common/mapobjects/trigger/hurt.qh>
#include <common/mapobjects/trigger/jumppads.qh>
+#include <common/net_linked.qh>
+#include <common/stats.qh>
+#include <common/weapons/_all.qh>
+#include <server/bot/api.qh>
+#include <server/bot/default/bot.qh>
+#include <server/bot/default/cvars.qh>
+#include <server/bot/default/waypoints.qh>
+#include <server/items/items.qh>
.float speed;
return this.bot_strategytime < time;
}
+ERASEABLE
void navigation_goalrating_timeout_extend_if_needed(entity this, float seconds)
{
this.bot_strategytime = max(this.bot_strategytime, time + seconds);
bool navigation_checkladders(entity e, vector org, vector m1, vector m2, vector end, vector end2, int movemode)
{
- IL_EACH(g_ladders, it.classname == "func_ladder",
+ IL_EACH(g_ladders, true,
{
if(it.bot_pickup)
- if(boxesoverlap(org + m1 + '-1 -1 -1', org + m2 + '1 1 1', it.absmin, it.absmax))
- if(boxesoverlap(end, end2, it.absmin + vec2(m1) + '-1 -1 0', it.absmax + vec2(m2) + '1 1 0'))
+ if(boxesoverlap(org + m1, org + m2, it.absmin, it.absmax))
+ if(boxesoverlap(end, end2, it.absmin + vec2(m1), it.absmax + vec2(m2)))
{
vector top = org;
top.z = it.absmax.z + (PL_MAX_CONST.z - PL_MIN_CONST.z);
vector pm1 = ent.origin + ent.mins;
vector pm2 = ent.origin + ent.maxs;
- if (autocvar_g_waypointeditor && !IS_BOT_CLIENT(ent))
+ if (waypointeditor_enabled && !IS_BOT_CLIENT(ent))
{
// this code allows removing waypoints in the air and seeing jumppad/telepport waypoint links
// FIXME it causes a bug where a waypoint spawned really close to another one (max 16 qu)
if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
{
if(walkfromwp && !ent.navigation_dynamicgoal)
- {
waypoint_clearlinks(ent); // initialize wpXXmincost fields
- navigation_item_addlink(it, ent);
- }
return it;
}
});
org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height
}
- if(!autocvar_g_waypointeditor && walkfromwp && !ent.navigation_dynamicgoal)
- {
- waypoint_clearlinks(ent); // initialize wpXXmincost fields
- IL_EACH(g_waypoints, it != ent,
- {
- if (walkfromwp && (it.wpflags & WPFLAGMASK_NORELINK))
- continue;
-
- 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);
- }
- });
- }
-
// box check failed, try walk
IL_EACH(g_waypoints, it != ent,
{
}
else
{
- if(autocvar_g_waypointeditor && e.nearestwaypointtimeout >= 0 && time > e.nearestwaypointtimeout)
+ if(waypointeditor_enabled && e.nearestwaypointtimeout >= 0 && time > e.nearestwaypointtimeout)
e.nearestwaypoint = NULL;
if ((!e.nearestwaypoint || e.navigation_dynamicgoal)
if(e.navigation_dynamicgoal)
e.nearestwaypointtimeout = time + 2;
- else if(autocvar_g_waypointeditor)
+ else if(waypointeditor_enabled)
e.nearestwaypointtimeout = time + 3 + random() * 2;
}
nwp = e.nearestwaypoint;
this.wp_goal_prev0 = e;
}
- if(g_jetpack)
- if(e==this.navigation_jetpack_goal)
+ if((this.items & IT_JETPACK) && e == this.navigation_jetpack_goal)
return true;
// if it can reach the goal there is nothing more to do
if(e == NULL)
return false;
- if(nearest_wp && nearest_wp.enemy)
+ if(nearest_wp && nearest_wp.enemy && !(nearest_wp.enemy.wpflags & WPFLAGMASK_NORELINK))
{
// often path can be optimized by not adding the nearest waypoint
- if (this.goalentity.navigation_dynamicgoal || autocvar_g_waypointeditor)
+ if (this.goalentity.navigation_dynamicgoal || waypointeditor_enabled)
{
if (nearest_wp.enemy.wpcost < autocvar_bot_ai_strategyinterval_movingtarget)
{
}
}
}
- else if(navigation_item_islinked(nearest_wp.enemy, this.goalentity))
- e = nearest_wp.enemy;
+ else
+ {
+ // NOTE unlike waypoints, items hold incoming links
+ navigation_item_initlinks_ifneeded(this.goalentity);
+ int link_num = navigation_item_getlinknum(this.goalentity, nearest_wp.enemy);
+ if (link_num >= 0)
+ {
+ if (navigation_item_iswalkablelink(this.goalentity, link_num))
+ e = nearest_wp.enemy;
+ }
+ else // untested link
+ {
+ entity wp = nearest_wp.enemy;
+ entity goal = this.goalentity;
+ bool walkable = false;
+ if (checkpvs(wp.origin, goal))
+ {
+ set_tracewalk_dest(goal, wp.origin, false);
+ if (vdist(tracewalk_dest - wp.origin, <, 1050)
+ && tracewalk(goal, wp.origin, PL_MIN_CONST, PL_MAX_CONST,
+ tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode))
+ {
+ walkable = true;
+ e = nearest_wp.enemy;
+ }
+ }
+ navigation_item_add_link(wp, goal, walkable);
+ }
+ }
}
for (;;)
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)
+ if((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) && this.goalcurrent.owner==this)
{
this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
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)
+ if ((tele_ent.wpflags & WAYPOINTFLAG_PERSONAL) && tele_ent.owner == this)
{
this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
}
// Loose goal touching check when running
- if(this.aistatus & AI_STATUS_RUNNING)
- if(this.goalcurrent.classname=="waypoint")
- if(vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed)) // if -really- running
- {
- if(vdist(this.origin - this.goalcurrent.origin, <, 150))
+ // check goalstack01 to make sure waypoint isn't the final goal
+ if((this.aistatus & AI_STATUS_RUNNING) && this.goalcurrent.classname == "waypoint" && !(this.goalcurrent.wpflags & WAYPOINTFLAG_JUMP)
+ && this.goalstack01 && !wasfreed(this.goalstack01) && vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed))
+ {
+ vector gco = this.goalcurrent.origin;
+ float min_dist = BOT_BUNNYHOP_WP_DETECTION_RANGE;
+ // also detect waypoints when bot is way above them but with a narrower horizontal range
+ // so to increase chances bot ends up in the standard range (optimizes nearest waypoint finding)
+ if(vdist(this.origin - gco, <, min_dist)
+ || (vdist(vec2(this.origin - gco), <, min_dist * 0.5) && vdist(this.origin - eZ * 1.5 * min_dist - gco, <, min_dist)))
{
traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL);
if(trace_fraction==1)
{
// Detect personal waypoints
if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
- if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+ if((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) && this.goalcurrent.owner==this)
{
this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
navigation_poproute(this);
++removed_goals;
- if(this.goalcurrent && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
+ if(this.goalcurrent && (this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
return removed_goals;
}
}
if(this.goalcurrent.classname == "waypoint" && !this.goalcurrent.wpisbox)
{
gc_min = this.goalcurrent.origin - '1 1 1' * 12;
- gc_max = this.goalcurrent.origin + '1 1 1' * 12;
+ gc_max = this.goalcurrent.origin + '1 1 1' * 12 + eZ * (jumpheight_vec.z + STAT(PL_MIN, this).z);
}
- if (time < this.ladder_time)
+ if (this.ladder_entity)
{
if (!boxesoverlap(this.absmin, this.absmax - eZ * STAT(PL_MAX, this).z, gc_min, gc_max))
break;
// Detect personal waypoints
if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING)
- if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this)
+ if((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) && this.goalcurrent.owner==this)
{
this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING;
this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED;
wp = this.goalcurrent_prev;
if(!wp)
return NULL;
- if(wp != this.goalcurrent_prev && vdist(wp.origin - this.origin, >, 50))
+ float min_dist = ((this.aistatus & AI_STATUS_RUNNING) ? BOT_BUNNYHOP_WP_DETECTION_RANGE : 50);
+ if(wp != this.goalcurrent_prev && vdist(wp.origin - this.origin, >, min_dist))
{
wp = this.goalcurrent_prev;
if(!wp)
if(!wp)
return NULL;
}
- if(vdist(wp.origin - this.origin, >, 50))
+ if(vdist(wp.origin - this.origin, >, min_dist))
{
wp = NULL;
IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_JUMP)),
{
- if(vdist(it.origin - this.origin, <, 50))
+ if(vdist(it.origin - this.origin, <, min_dist))
{
wp = it;
break;