Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
navigation.qc File Reference
Include dependency graph for navigation.qc:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define MAX_CHASE_DISTANCE   700
#define NAV_SWIM_ONWATER   1
#define NAV_SWIM_UNDERWATER   2
#define NAV_WALK   0
#define RESURFACE_LIMITED(org, lim)

Functions

void botframe_updatedangerousobjects (float maxupdate)
void debuggoalstack (entity this)
void debugnode (entity this, vector node)
void debugnodestatus (vector position, float status)
void debugresetnodes ()
vector get_closer_dest (entity ent, vector org)
bool navigation_check_submerged_state (entity ent, vector pos)
bool navigation_checkladders (entity e, vector org, vector m1, vector m2, vector end, vector end2, int movemode)
void navigation_clearroute (entity this)
void navigation_dynamicgoal_init (entity this, bool initially_static)
void navigation_dynamicgoal_set (entity this, entity dropper)
void navigation_dynamicgoal_unset (entity this)
entity navigation_findnearestwaypoint (entity ent, float walkfromwp)
entity navigation_findnearestwaypoint_withdist_except (entity ent, float walkfromwp, float bestdist, entity except)
entity navigation_get_really_close_waypoint (entity this)
void navigation_goalrating_end (entity this)
void navigation_goalrating_start (entity this)
bool navigation_goalrating_timeout (entity this)
bool navigation_goalrating_timeout_can_be_anticipated (entity this)
void navigation_goalrating_timeout_expire (entity this, float seconds)
ERASEABLE void navigation_goalrating_timeout_extend_if_needed (entity this, float seconds)
void navigation_goalrating_timeout_force (entity this)
void navigation_goalrating_timeout_set (entity this)
void navigation_markroutes (entity this, entity fixed_source_waypoint)
void navigation_markroutes_checkwaypoint (entity w, entity wp, float cost, vector p)
void navigation_markroutes_inverted (entity fixed_source_waypoint)
float navigation_markroutes_nearestwaypoints (entity this, float maxdist)
void navigation_poproute (entity this)
int navigation_poptouchedgoals (entity this)
void navigation_pushroute (entity this, entity e)
void navigation_routerating (entity this, entity e, float f, float rangebias)
bool navigation_routetogoal (entity this, entity e, vector startposition)
bool navigation_shortenpath (entity this)
void navigation_unstuck (entity this)
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)
vector resurface_limited (vector org, float lim, vector m1)
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)

Variables

float goalcounter
vector lastposition
float speed

Macro Definition Documentation

◆ MAX_CHASE_DISTANCE

#define MAX_CHASE_DISTANCE   700

Definition at line 55 of file navigation.qc.

Referenced by navigation_routetogoal(), and navigation_shortenpath().

◆ NAV_SWIM_ONWATER

#define NAV_SWIM_ONWATER   1

Definition at line 267 of file navigation.qc.

Referenced by tracewalk().

◆ NAV_SWIM_UNDERWATER

#define NAV_SWIM_UNDERWATER   2

Definition at line 268 of file navigation.qc.

Referenced by tracewalk().

◆ NAV_WALK

#define NAV_WALK   0

Definition at line 266 of file navigation.qc.

Referenced by tracewalk().

◆ RESURFACE_LIMITED

#define RESURFACE_LIMITED ( org,
lim )
Value:
vector resurface_limited(vector org, float lim, vector m1)
vector org
Definition self.qh:92

Definition at line 264 of file navigation.qc.

Referenced by tracewalk().

Function Documentation

◆ botframe_updatedangerousobjects()

void botframe_updatedangerousobjects ( float maxupdate)

Definition at line 1874 of file navigation.qc.

1875{
1876 vector m1, m2, v, o;
1877 float c, d, danger;
1878 c = 0;
1879 entity wp_cur;
1880 IL_EACH(g_waypoints, true,
1881 {
1882 danger = 0;
1883 m1 = it.absmin;
1884 m2 = it.absmax;
1885 wp_cur = it;
1886 IL_EACH(g_bot_dodge, it.bot_dodge,
1887 {
1888 v = it.origin;
1889 v.x = bound(m1_x, v.x, m2_x);
1890 v.y = bound(m1_y, v.y, m2_y);
1891 v.z = bound(m1_z, v.z, m2_z);
1892 o = (it.absmin + it.absmax) * 0.5;
1893 d = waypoint_getlinearcost(it.bot_dodgerating) - waypoint_gettravelcost(o, v, it, wp_cur);
1894 if (d > 0)
1895 {
1896 traceline(o, v, true, NULL);
1897 if (trace_fraction == 1)
1898 danger = danger + d;
1899 }
1900 });
1901 it.dmg = danger;
1902 c = c + 1;
1903 if (c >= maxupdate)
1904 break;
1905 });
1906}
IntrusiveList g_bot_dodge
Definition api.qh:150
IntrusiveList g_waypoints
Definition api.qh:148
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define IL_EACH(this, cond, body)
vector
Definition self.qh:92

References entity(), g_bot_dodge, g_waypoints, IL_EACH, and vector.

Referenced by bot_serverframe().

◆ debuggoalstack()

void debuggoalstack ( entity this)

Definition at line 2062 of file navigation.qc.

2063{
2064 entity goal;
2065 vector org, go;
2066
2067 if(this.goalcounter==0)goal=this.goalcurrent;
2068 else if(this.goalcounter==1)goal=this.goalstack01;
2069 else if(this.goalcounter==2)goal=this.goalstack02;
2070 else if(this.goalcounter==3)goal=this.goalstack03;
2071 else if(this.goalcounter==4)goal=this.goalstack04;
2072 else if(this.goalcounter==5)goal=this.goalstack05;
2073 else if(this.goalcounter==6)goal=this.goalstack06;
2074 else if(this.goalcounter==7)goal=this.goalstack07;
2075 else if(this.goalcounter==8)goal=this.goalstack08;
2076 else if(this.goalcounter==9)goal=this.goalstack09;
2077 else if(this.goalcounter==10)goal=this.goalstack10;
2078 else if(this.goalcounter==11)goal=this.goalstack11;
2079 else if(this.goalcounter==12)goal=this.goalstack12;
2080 else if(this.goalcounter==13)goal=this.goalstack13;
2081 else if(this.goalcounter==14)goal=this.goalstack14;
2082 else if(this.goalcounter==15)goal=this.goalstack15;
2083 else if(this.goalcounter==16)goal=this.goalstack16;
2084 else if(this.goalcounter==17)goal=this.goalstack17;
2085 else if(this.goalcounter==18)goal=this.goalstack18;
2086 else if(this.goalcounter==19)goal=this.goalstack19;
2087 else if(this.goalcounter==20)goal=this.goalstack20;
2088 else if(this.goalcounter==21)goal=this.goalstack21;
2089 else if(this.goalcounter==22)goal=this.goalstack22;
2090 else if(this.goalcounter==23)goal=this.goalstack23;
2091 else if(this.goalcounter==24)goal=this.goalstack24;
2092 else if(this.goalcounter==25)goal=this.goalstack25;
2093 else if(this.goalcounter==26)goal=this.goalstack26;
2094 else if(this.goalcounter==27)goal=this.goalstack27;
2095 else if(this.goalcounter==28)goal=this.goalstack28;
2096 else if(this.goalcounter==29)goal=this.goalstack29;
2097 else if(this.goalcounter==30)goal=this.goalstack30;
2098 else if(this.goalcounter==31)goal=this.goalstack31;
2099 else goal=NULL;
2100
2101 if(goal==NULL)
2102 {
2103 this.goalcounter = 0;
2104 this.lastposition='0 0 0';
2105 return;
2106 }
2107
2108 if(this.lastposition=='0 0 0')
2109 org = this.origin;
2110 else
2111 org = this.lastposition;
2112
2113
2114 go = ( goal.absmin + goal.absmax ) * 0.5;
2115 te_lightning2(NULL, org, go);
2116 this.lastposition = go;
2117
2118 this.goalcounter++;
2119}
vector origin
float goalcounter
vector lastposition
entity goalstack06
Definition navigation.qh:20
entity goalstack15
Definition navigation.qh:22
entity goalstack17
Definition navigation.qh:23
entity goalstack03
Definition navigation.qh:19
entity goalstack23
Definition navigation.qh:24
entity goalstack07
Definition navigation.qh:20
entity goalstack19
Definition navigation.qh:23
entity goalstack27
Definition navigation.qh:25
entity goalstack25
Definition navigation.qh:25
entity goalstack22
Definition navigation.qh:24
entity goalcurrent
Definition navigation.qh:19
entity goalstack12
Definition navigation.qh:22
entity goalstack08
Definition navigation.qh:21
entity goalstack11
Definition navigation.qh:21
entity goalstack30
Definition navigation.qh:26
entity goalstack29
Definition navigation.qh:26
entity goalstack24
Definition navigation.qh:25
entity goalstack04
Definition navigation.qh:20
entity goalstack05
Definition navigation.qh:20
entity goalstack16
Definition navigation.qh:23
entity goalstack14
Definition navigation.qh:22
entity goalstack18
Definition navigation.qh:23
entity goalstack20
Definition navigation.qh:24
entity goalstack13
Definition navigation.qh:22
entity goalstack09
Definition navigation.qh:21
entity goalstack31
Definition navigation.qh:26
entity goalstack10
Definition navigation.qh:21
entity goalstack02
Definition navigation.qh:19
entity goalstack21
Definition navigation.qh:24
entity goalstack28
Definition navigation.qh:26
entity goalstack01
Definition navigation.qh:19
entity goalstack26
Definition navigation.qh:25
#define NULL
Definition post.qh:14

References entity(), goalcounter, goalcurrent, goalstack01, goalstack02, goalstack03, goalstack04, goalstack05, goalstack06, goalstack07, goalstack08, goalstack09, goalstack10, goalstack11, goalstack12, goalstack13, goalstack14, goalstack15, goalstack16, goalstack17, goalstack18, goalstack19, goalstack20, goalstack21, goalstack22, goalstack23, goalstack24, goalstack25, goalstack26, goalstack27, goalstack28, goalstack29, goalstack30, goalstack31, lastposition, NULL, org, origin, and vector.

Referenced by havocbot_moveto(), and havocbot_movetogoal().

◆ debugnode()

void debugnode ( entity this,
vector node )

Definition at line 2019 of file navigation.qc.

2020{
2021 if (!IS_PLAYER(this))
2022 return;
2023
2024 if(debuglastnode=='0 0 0')
2025 {
2026 debuglastnode = node;
2027 return;
2028 }
2029
2030 te_lightning2(NULL, node, debuglastnode);
2031 debuglastnode = node;
2032}
#define IS_PLAYER(s)
Definition player.qh:243
vector debuglastnode
Definition navigation.qh:83

References debuglastnode, entity(), IS_PLAYER, NULL, and vector.

Referenced by tracewalk().

◆ debugnodestatus()

void debugnodestatus ( vector position,
float status )

Definition at line 2034 of file navigation.qc.

2035{
2036 vector c;
2037
2038 switch (status)
2039 {
2040 case DEBUG_NODE_SUCCESS:
2041 c = '0 15 0';
2042 break;
2043 case DEBUG_NODE_WARNING:
2044 c = '15 15 0';
2045 break;
2046 case DEBUG_NODE_FAIL:
2047 c = '15 0 0';
2048 break;
2049 default:
2050 c = '15 15 15';
2051 }
2052
2053 te_customflash(position, 40, 2, c);
2054}
const float DEBUG_NODE_SUCCESS
Definition navigation.qh:80
const float DEBUG_NODE_WARNING
Definition navigation.qh:81
const float DEBUG_NODE_FAIL
Definition navigation.qh:82

References DEBUG_NODE_FAIL, DEBUG_NODE_SUCCESS, DEBUG_NODE_WARNING, and vector.

Referenced by tracewalk().

◆ debugresetnodes()

void debugresetnodes ( )

Definition at line 2014 of file navigation.qc.

2015{
2016 debuglastnode = '0 0 0';
2017}

References debuglastnode.

Referenced by tracewalk().

◆ get_closer_dest()

vector get_closer_dest ( entity ent,
vector org )

Definition at line 104 of file navigation.qc.

105{
106 vector dest = '0 0 0';
107 if ((ent.classname != "waypoint") || ent.wpisbox)
108 {
109 vector wm1 = ent.origin + ent.mins;
110 vector wm2 = ent.origin + ent.maxs;
111 dest.x = bound(wm1.x, org.x, wm2.x);
112 dest.y = bound(wm1.y, org.y, wm2.y);
113 dest.z = bound(wm1.z, org.z, wm2.z);
114 }
115 else
116 dest = ent.origin;
117 return dest;
118}
vector dest
Definition jumppads.qh:54
float bound(float min, float value, float max)

References bound(), dest, entity(), org, and vector.

Referenced by havocbot_ai(), havocbot_bunnyhop(), havocbot_moveto(), and havocbot_movetogoal().

◆ navigation_check_submerged_state()

bool navigation_check_submerged_state ( entity ent,
vector pos )

Definition at line 204 of file navigation.qc.

205{
206 bool submerged;
207 if(IS_PLAYER(ent))
208 submerged = (ent.waterlevel == WATERLEVEL_SUBMERGED);
209 else if(ent.nav_submerged_state != SUBMERGED_UNDEFINED)
210 submerged = (ent.nav_submerged_state == SUBMERGED_YES);
211 else
212 {
213 submerged = SUBMERGED(pos);
214 // NOTE: SUBMERGED check of box waypoint origin may fail even if origin
215 // is actually submerged because often they are inside some solid.
216 // That's why submerged state is saved now that we know current pos is
217 // not stuck in solid (previous tracewalk call to this pos was successfully)
218 if(!ent.navigation_dynamicgoal)
219 ent.nav_submerged_state = (submerged) ? SUBMERGED_YES : SUBMERGED_NO;
220 }
221 return submerged;
222}
#define SUBMERGED(pos)
Definition bot.qh:86
const int WATERLEVEL_SUBMERGED
Definition movetypes.qh:14
#define SUBMERGED_UNDEFINED
#define SUBMERGED_YES
#define SUBMERGED_NO

References entity(), IS_PLAYER, SUBMERGED, SUBMERGED_NO, SUBMERGED_UNDEFINED, SUBMERGED_YES, vector, and WATERLEVEL_SUBMERGED.

Referenced by waypoint_gettravelcost().

◆ navigation_checkladders()

bool navigation_checkladders ( entity e,
vector org,
vector m1,
vector m2,
vector end,
vector end2,
int movemode )

Definition at line 224 of file navigation.qc.

225{
226 IL_EACH(g_ladders, true,
227 {
228 if(it.bot_pickup)
229 if(boxesoverlap(org + m1, org + m2, it.absmin, it.absmax))
230 if(boxesoverlap(end, end2, it.absmin + vec2(m1), it.absmax + vec2(m2)))
231 {
232 vector top = org;
233 top.z = it.absmax.z + (PL_MAX_CONST.z - PL_MIN_CONST.z);
234 tracebox(org, m1, m2, top, movemode, e);
235 if(trace_fraction == 1)
236 return true;
237 }
238 });
239 return false;
240}
const vector PL_MIN_CONST
Definition constants.qh:56
const vector PL_MAX_CONST
Definition constants.qh:55
float trace_fraction
IntrusiveList g_ladders
Definition ladder.qh:7
ERASEABLE float boxesoverlap(vector m1, vector m2, vector m3, vector m4)
requires that m2>m1 in all coordinates, and that m4>m3
Definition vector.qh:73
#define vec2(...)
Definition vector.qh:90

References boxesoverlap(), entity(), g_ladders, IL_EACH, org, PL_MAX_CONST, PL_MIN_CONST, trace_fraction, vec2, and vector.

Referenced by tracewalk().

◆ navigation_clearroute()

void navigation_clearroute ( entity this)

Definition at line 751 of file navigation.qc.

752{
753 this.lastteleporttime = 0;
754 this.goalcurrent_prev = this.goalcurrent;
759 this.goalentity_shouldbefrozen = false;
760 this.goalentity = NULL;
761 this.goalcurrent = NULL;
762 this.goalstack01 = NULL;
763 this.goalstack02 = NULL;
764 this.goalstack03 = NULL;
765 this.goalstack04 = NULL;
766 this.goalstack05 = NULL;
767 this.goalstack06 = NULL;
768 this.goalstack07 = NULL;
769 this.goalstack08 = NULL;
770 this.goalstack09 = NULL;
771 this.goalstack10 = NULL;
772 this.goalstack11 = NULL;
773 this.goalstack12 = NULL;
774 this.goalstack13 = NULL;
775 this.goalstack14 = NULL;
776 this.goalstack15 = NULL;
777 this.goalstack16 = NULL;
778 this.goalstack17 = NULL;
779 this.goalstack18 = NULL;
780 this.goalstack19 = NULL;
781 this.goalstack20 = NULL;
782 this.goalstack21 = NULL;
783 this.goalstack22 = NULL;
784 this.goalstack23 = NULL;
785 this.goalstack24 = NULL;
786 this.goalstack25 = NULL;
787 this.goalstack26 = NULL;
788 this.goalstack27 = NULL;
789 this.goalstack28 = NULL;
790 this.goalstack29 = NULL;
791 this.goalstack30 = NULL;
792 this.goalstack31 = NULL;
793}
float lastteleporttime
Definition api.qh:50
float goalentity_lock_timeout
Definition api.qh:97
const float FLOAT_MAX
Definition float.qh:3
entity goalentity
Definition viewloc.qh:16
bool goalentity_shouldbefrozen
Definition navigation.qh:36
float goalcurrent_distance_2d
Definition navigation.qh:30
float goalcurrent_distance_time
Definition navigation.qh:31
entity goalcurrent_prev
Definition navigation.qh:28
float goalcurrent_distance_z
Definition navigation.qh:29

References entity(), FLOAT_MAX, goalcurrent, goalcurrent_distance_2d, goalcurrent_distance_time, goalcurrent_distance_z, goalcurrent_prev, goalentity, goalentity_lock_timeout, goalentity_shouldbefrozen, goalstack01, goalstack02, goalstack03, goalstack04, goalstack05, goalstack06, goalstack07, goalstack08, goalstack09, goalstack10, goalstack11, goalstack12, goalstack13, goalstack14, goalstack15, goalstack16, goalstack17, goalstack18, goalstack19, goalstack20, goalstack21, goalstack22, goalstack23, goalstack24, goalstack25, goalstack26, goalstack27, goalstack28, goalstack29, goalstack30, goalstack31, lastteleporttime, and NULL.

Referenced by havocbot_ai(), havocbot_movetogoal(), havocbot_resetgoal(), navigation_goalrating_start(), and navigation_unstuck().

◆ navigation_dynamicgoal_init()

void navigation_dynamicgoal_init ( entity this,
bool initially_static )

Definition at line 77 of file navigation.qc.

78{
79 this.navigation_dynamicgoal = true;
81 if(initially_static)
82 this.nearestwaypointtimeout = -1;
83 else
85}
entity bot_basewaypoint
Definition api.qh:106
float nearestwaypointtimeout
Definition api.qh:53
entity nearestwaypoint
Definition api.qh:54
bool navigation_dynamicgoal
Definition api.qh:107
float time

References bot_basewaypoint, entity(), navigation_dynamicgoal, nearestwaypoint, nearestwaypointtimeout, and time.

Referenced by ctf_DelayedFlagSetup(), ka_SpawnBalls(), kh_Key_Spawn(), PutPlayerInServer(), tka_SpawnBalls(), and W_ThrowNewWeapon().

◆ navigation_dynamicgoal_set()

void navigation_dynamicgoal_set ( entity this,
entity dropper )

Definition at line 87 of file navigation.qc.

88{
90 if (dropper && dropper.nearestwaypointtimeout && dropper.nearestwaypointtimeout < time + 2)
91 this.nearestwaypoint = dropper.nearestwaypoint;
92 if (this.nearestwaypoint)
93 this.nearestwaypointtimeout += 2;
94}

References entity(), nearestwaypoint, nearestwaypointtimeout, and time.

Referenced by ctf_Handle_Throw(), ka_DropEvent(), ka_RespawnBall(), kh_Key_Detach(), tka_DropEvent(), and tka_RespawnBall().

◆ navigation_dynamicgoal_unset()

void navigation_dynamicgoal_unset ( entity this)

◆ navigation_findnearestwaypoint()

entity navigation_findnearestwaypoint ( entity ent,
float walkfromwp )

Definition at line 1012 of file navigation.qc.

1013{
1014 entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, NULL);
1016 {
1017 entity wp2 = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, wp);
1018 if (wp && !wp2)
1019 wp.wpflags |= WAYPOINTFLAG_PROTECTED;
1020 }
1021 return wp;
1022}
const int WAYPOINTFLAG_PROTECTED
Definition api.qh:16
int autocvar_g_waypointeditor_auto
Definition cvars.qh:60
entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfromwp, float bestdist, entity except)

References autocvar_g_waypointeditor_auto, entity(), navigation_findnearestwaypoint_withdist_except(), NULL, and WAYPOINTFLAG_PROTECTED.

Referenced by waypoint_remove_fromeditor(), and waypoint_unreachable().

◆ navigation_findnearestwaypoint_withdist_except()

entity navigation_findnearestwaypoint_withdist_except ( entity ent,
float walkfromwp,
float bestdist,
entity except )

Definition at line 919 of file navigation.qc.

920{
921 if(ent.tag_entity)
922 ent = ent.tag_entity;
923
924 vector pm1 = ent.origin + ent.mins;
925 vector pm2 = ent.origin + ent.maxs;
926
928 {
929 // this code allows removing waypoints in the air and seeing jumppad/telepport waypoint links
930 // FIXME it causes a bug where a waypoint spawned really close to another one (max 16 qu)
931 // isn't detected as the nearest waypoint
932 IL_EACH(g_waypoints, it != ent && it != except,
933 {
934 if (boxesoverlap(pm1, pm2, it.absmin, it.absmax))
935 return it;
936 });
937 }
938 else
939 {
940 // do two scans, because box test is cheaper
941 IL_EACH(g_waypoints, it != ent && it != except && !(it.wpflags & (WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_JUMP)),
942 {
943 if(boxesoverlap(pm1, pm2, it.absmin, it.absmax))
944 {
945 if(walkfromwp && !ent.navigation_dynamicgoal)
946 waypoint_clearlinks(ent); // initialize wpXXmincost fields
947 return it;
948 }
949 });
950 }
951
952 vector org = ent.origin;
954 te_plasmaburn(org);
955
956 entity best = NULL;
957 vector v = '0 0 0';
958
959 if(ent.size && !IS_PLAYER(ent))
960 {
961 org += 0.5 * (ent.mins + ent.maxs);
962 org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height
963 }
964
965 // box check failed, try walk
966 IL_EACH(g_waypoints, it != ent,
967 {
968 if (walkfromwp && (it.wpflags & WPFLAGMASK_NORELINK))
969 continue;
970 v = it.origin;
971
972 if (walkfromwp)
973 {
974 set_tracewalk_dest(ent, v, true);
975 if (trace_ent == ent)
976 {
977 bestdist = 0;
978 best = it;
979 break;
980 }
981 }
982 else
983 set_tracewalk_dest(it, org, false);
984
987 tracewalk_dest, tracewalk_dest_height, walkfromwp, bestdist))
988 {
989 if (walkfromwp)
990 bestdist = vlen(tracewalk_dest - v);
991 else
992 bestdist = vlen(v - org);
993 best = it;
994 }
995 });
996 if(!best && !ent.navigation_dynamicgoal)
997 {
998 int solid_save = ent.solid;
999 ent.solid = SOLID_BSP;
1000 IL_EACH(g_jumppads, true,
1001 {
1002 if(trigger_push_test(it, ent))
1003 {
1004 best = it.nearestwaypoint;
1005 break;
1006 }
1007 });
1008 ent.solid = solid_save;
1009 }
1010 return best;
1011}
const int WAYPOINTFLAG_TELEPORT
Definition api.qh:13
const int WPFLAGMASK_NORELINK
Definition api.qh:29
const int WAYPOINTFLAG_JUMP
Definition api.qh:20
entity trace_ent
const float SOLID_BSP
best
Definition all.qh:82
bool trigger_push_test(entity this, entity item)
Definition jumppads.qc:579
IntrusiveList g_jumppads
Definition jumppads.qh:18
float vlen(vector v)
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)
void set_tracewalk_dest(entity ent, vector org, bool fix_player_dest)
vector tracewalk_dest
Definition navigation.qh:66
float tracewalk_dest_height
Definition navigation.qh:67
float navigation_testtracewalk
Definition navigation.qh:8
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
bool waypointeditor_enabled
Definition waypoints.qh:3

References boxesoverlap(), entity(), g_waypoints, IL_EACH, IS_BOT_CLIENT, vector, waypointeditor_enabled, WAYPOINTFLAG_JUMP, and WAYPOINTFLAG_TELEPORT.

Referenced by navigation_findnearestwaypoint().

◆ navigation_get_really_close_waypoint()

entity navigation_get_really_close_waypoint ( entity this)

Definition at line 1784 of file navigation.qc.

1785{
1786 entity wp = this.goalcurrent;
1787 if(!wp)
1788 wp = this.goalcurrent_prev;
1789 if(!wp)
1790 return NULL;
1791 float min_dist = ((this.aistatus & AI_STATUS_RUNNING) ? BOT_BUNNYHOP_WP_DETECTION_RANGE : 50);
1792 if(wp != this.goalcurrent_prev && vdist(wp.origin - this.origin, >, min_dist))
1793 {
1794 wp = this.goalcurrent_prev;
1795 if(!wp)
1796 return NULL;
1797 }
1798 if(wp.classname != "waypoint")
1799 {
1800 wp = wp.nearestwaypoint;
1801 if(!wp)
1802 return NULL;
1803 }
1804 if(vdist(wp.origin - this.origin, >, min_dist))
1805 {
1806 wp = NULL;
1808 {
1809 if(vdist(it.origin - this.origin, <, min_dist))
1810 {
1811 wp = it;
1812 break;
1813 }
1814 });
1815 if(!wp)
1816 return NULL;
1817 }
1818 if(wp.wpflags & WAYPOINTFLAG_TELEPORT)
1819 return NULL;
1820
1821 set_tracewalk_dest(wp, this.origin, false);
1822 if (!tracewalk(this, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this),
1824 {
1825 return NULL;
1826 }
1827 return wp;
1828}
int aistatus
Definition bot.qh:20
const int AI_STATUS_RUNNING
Definition bot.qh:8
#define STAT(...)
Definition stats.qh:82
bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float end_height, float movemode)
float bot_navigation_movemode
Definition navigation.qh:7
const float BOT_BUNNYHOP_WP_DETECTION_RANGE
Definition navigation.qh:91
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8

References AI_STATUS_RUNNING, aistatus, BOT_BUNNYHOP_WP_DETECTION_RANGE, entity(), g_waypoints, goalcurrent, goalcurrent_prev, IL_EACH, NULL, vdist, WAYPOINTFLAG_JUMP, and WAYPOINTFLAG_TELEPORT.

Referenced by navigation_goalrating_start().

◆ navigation_goalrating_end()

void navigation_goalrating_end ( entity this)

Definition at line 1846 of file navigation.qc.

1847{
1848 if(this.aistatus & AI_STATUS_STUCK)
1849 return;
1850
1851 entity wp = this.goalstack31; // save to wp as this.goalstack31 is set by navigation_routetogoal
1852 this.goalstack31 = NULL;
1853
1855 LOG_DEBUG("best goal ", navigation_bestgoal.classname);
1856
1857 if (wp && this.goalcurrent == wp)
1858 navigation_poproute(this);
1859
1860 // If the bot got stuck then try to reach the farthest waypoint
1861 if (!this.goalentity)
1862 {
1864 {
1865 LOG_DEBUG(this.netname, " cannot walk to any goal");
1866 this.aistatus |= AI_STATUS_STUCK;
1867 }
1868 this.goalentity_shouldbefrozen = false;
1869 }
1870 else
1871 this.goalentity_shouldbefrozen = boolean(STAT(FROZEN, this.goalentity));
1872}
#define boolean(value)
Definition bool.qh:9
const int AI_STATUS_STUCK
Definition bot.qh:17
string netname
Definition powerups.qc:20
bool autocvar_bot_wander_enable
Definition cvars.qh:57
#define LOG_DEBUG(...)
Definition log.qh:80
bool navigation_routetogoal(entity this, entity e, vector startposition)
void navigation_poproute(entity this)
entity navigation_bestgoal
Definition navigation.qh:15

References AI_STATUS_STUCK, aistatus, autocvar_bot_wander_enable, boolean, entity(), goalcurrent, goalentity, goalentity_shouldbefrozen, goalstack31, LOG_DEBUG, navigation_bestgoal, navigation_poproute(), navigation_routetogoal(), netname, NULL, origin, and STAT.

Referenced by havocbot_moveto_refresh_route(), havocbot_role_ast_defense(), havocbot_role_ast_offense(), havocbot_role_ctf_carrier(), havocbot_role_ctf_defense(), havocbot_role_ctf_escort(), havocbot_role_ctf_middle(), havocbot_role_ctf_offense(), havocbot_role_ctf_retriever(), havocbot_role_cts(), havocbot_role_dom(), havocbot_role_ft_freeing(), havocbot_role_ft_offense(), havocbot_role_generic(), havocbot_role_ka_carrier(), havocbot_role_ka_collector(), havocbot_role_kh_carrier(), havocbot_role_kh_defense(), havocbot_role_kh_freelancer(), havocbot_role_kh_offense(), havocbot_role_ons_offense(), havocbot_role_race(), havocbot_role_tka_carrier(), and havocbot_role_tka_collector().

◆ navigation_goalrating_start()

void navigation_goalrating_start ( entity this)

Definition at line 1831 of file navigation.qc.

1832{
1833 if(this.aistatus & AI_STATUS_STUCK)
1834 return;
1835
1841 navigation_markroutes(this, wp);
1842 this.goalstack31 = wp; // temporarly save the really close waypoint
1843}
entity navigation_get_really_close_waypoint(entity this)
void navigation_markroutes(entity this, entity fixed_source_waypoint)
void navigation_clearroute(entity this)
entity navigation_jetpack_goal
Definition navigation.qh:77
float navigation_bestrating
Definition navigation.qh:6

References AI_STATUS_STUCK, aistatus, entity(), goalstack31, navigation_bestgoal, navigation_bestrating, navigation_clearroute(), navigation_get_really_close_waypoint(), navigation_jetpack_goal, navigation_markroutes(), and NULL.

Referenced by havocbot_moveto_refresh_route(), havocbot_role_ast_defense(), havocbot_role_ast_offense(), havocbot_role_ctf_carrier(), havocbot_role_ctf_defense(), havocbot_role_ctf_escort(), havocbot_role_ctf_middle(), havocbot_role_ctf_offense(), havocbot_role_ctf_retriever(), havocbot_role_cts(), havocbot_role_dom(), havocbot_role_ft_freeing(), havocbot_role_ft_offense(), havocbot_role_generic(), havocbot_role_ka_carrier(), havocbot_role_ka_collector(), havocbot_role_kh_carrier(), havocbot_role_kh_defense(), havocbot_role_kh_freelancer(), havocbot_role_kh_offense(), havocbot_role_ons_offense(), havocbot_role_race(), havocbot_role_tka_carrier(), and havocbot_role_tka_collector().

◆ navigation_goalrating_timeout()

◆ navigation_goalrating_timeout_can_be_anticipated()

bool navigation_goalrating_timeout_can_be_anticipated ( entity this)

Definition at line 56 of file navigation.qc.

57{
58 vector gco = (this.goalentity.absmin + this.goalentity.absmax) * 0.5;
59 if (vdist(gco - this.origin, >, autocvar_sv_maxspeed * 1.5)
60 && time > this.bot_strategytime - (IS_MOVABLE(this.goalentity) ? 3 : 2))
61 {
62 return true;
63 }
64
65 if (this.goalentity.bot_pickup && time > this.bot_strategytime - 5)
66 {
68 {
69 this.ignoregoal = this.goalentity;
71 return true;
72 }
73 }
74 return false;
75}
bool havocbot_goalrating_item_pickable_check_players(entity this, vector org, entity item, vector item_org)
Definition roles.qc:60
entity ignoregoal
Definition api.qh:99
float ignoregoaltime
Definition api.qh:98
float autocvar_sv_maxspeed
Definition player.qh:53
float autocvar_bot_ai_ignoregoal_timeout
Definition cvars.qh:36
#define IS_MOVABLE(v)
Definition utils.qh:25

References autocvar_bot_ai_ignoregoal_timeout, autocvar_sv_maxspeed, bot_strategytime, entity(), goalentity, havocbot_goalrating_item_pickable_check_players(), ignoregoal, ignoregoaltime, IS_MOVABLE, origin, time, vdist, and vector.

Referenced by havocbot_movetogoal().

◆ navigation_goalrating_timeout_expire()

void navigation_goalrating_timeout_expire ( entity this,
float seconds )

◆ navigation_goalrating_timeout_extend_if_needed()

ERASEABLE void navigation_goalrating_timeout_extend_if_needed ( entity this,
float seconds )

Definition at line 50 of file navigation.qc.

51{
52 this.bot_strategytime = max(this.bot_strategytime, time + seconds);
53}
float max(float f,...)

References bot_strategytime, entity(), max(), and time.

◆ navigation_goalrating_timeout_force()

void navigation_goalrating_timeout_force ( entity this)

Definition at line 29 of file navigation.qc.

30{
32}
void navigation_goalrating_timeout_expire(entity this, float seconds)
Definition navigation.qc:36

References entity(), and navigation_goalrating_timeout_expire().

Referenced by bot_think(), havocbot_ai(), havocbot_chooserole(), havocbot_movetogoal(), havocbot_role_ctf_retriever(), and havocbot_role_ctf_setrole().

◆ navigation_goalrating_timeout_set()

◆ navigation_markroutes()

void navigation_markroutes ( entity this,
entity fixed_source_waypoint )

Definition at line 1082 of file navigation.qc.

1083{
1084 float cost, cost2;
1085 vector p;
1086
1087 IL_EACH(g_waypoints, true,
1088 {
1089 it.wpconsidered = false;
1090 it.wpnearestpoint = '0 0 0';
1091 it.wpcost = 10000000;
1092 it.wpfire = 0;
1093 it.enemy = NULL;
1094 });
1095
1096 if(fixed_source_waypoint)
1097 {
1098 fixed_source_waypoint.wpconsidered = true;
1099 fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
1100 fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg;
1101 fixed_source_waypoint.wpfire = 1;
1102 fixed_source_waypoint.enemy = NULL;
1103 }
1104 else
1105 {
1106 // try a short range search for the nearest waypoints, and expand the search repeatedly if none are found
1107 // as this search is expensive we will use lower values if the bot is on the air
1108 float increment, maxdistance;
1109 if(IS_ONGROUND(this))
1110 {
1111 increment = 750;
1112 maxdistance = 50000;
1113 }
1114 else
1115 {
1116 increment = 500;
1117 maxdistance = 1500;
1118 }
1119
1120 for(int j = increment; !navigation_markroutes_nearestwaypoints(this, j) && j < maxdistance; j += increment);
1121 }
1122
1123 bool searching = true;
1124 while (searching)
1125 {
1126 searching = false;
1127 IL_EACH(g_waypoints, it.wpfire,
1128 {
1129 searching = true;
1130 it.wpfire = 0;
1131 cost = it.wpcost;
1132 p = it.wpnearestpoint;
1133 entity wp;
1134 wp = it.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp00mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1135 wp = it.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp01mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1136 wp = it.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp02mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1137 wp = it.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp03mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1138 wp = it.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp04mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1139 wp = it.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp05mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1140 wp = it.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp06mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1141 wp = it.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp07mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1142 wp = it.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp08mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1143 wp = it.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp09mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1144 wp = it.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp10mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1145 wp = it.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp11mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1146 wp = it.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp12mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1147 wp = it.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp13mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1148 wp = it.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp14mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1149 wp = it.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp15mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1150 wp = it.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp16mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1151 wp = it.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp17mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1152 wp = it.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp18mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1153 wp = it.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp19mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1154 wp = it.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp20mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1155 wp = it.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp21mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1156 wp = it.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp22mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1157 wp = it.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp23mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1158 wp = it.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp24mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1159 wp = it.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp25mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1160 wp = it.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp26mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1161 wp = it.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp27mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1162 wp = it.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp28mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1163 wp = it.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp29mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1164 wp = it.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp30mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1165 wp = it.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp31mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p);
1166 }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
1167 });
1168 }
1169}
#define IS_ONGROUND(s)
Definition movetypes.qh:16
float navigation_markroutes_nearestwaypoints(entity this, float maxdist)

References entity(), g_waypoints, IL_EACH, IS_ONGROUND, navigation_markroutes_nearestwaypoints(), NULL, and vector.

Referenced by navigation_goalrating_start(), and waypoint_unreachable().

◆ navigation_markroutes_checkwaypoint()

void navigation_markroutes_checkwaypoint ( entity w,
entity wp,
float cost,
vector p )

Definition at line 1054 of file navigation.qc.

1055{
1056 vector m1, m2;
1057 vector v;
1058 if (wp.wpisbox)
1059 {
1060 m1 = wp.origin + wp.mins;
1061 m2 = wp.origin + wp.maxs;
1062 v.x = bound(m1_x, p.x, m2_x);
1063 v.y = bound(m1_y, p.y, m2_y);
1064 v.z = bound(m1_z, p.z, m2_z);
1065 }
1066 else
1067 v = wp.origin;
1068 if (w.wpflags & WAYPOINTFLAG_TELEPORT)
1069 cost += w.wp00mincost; // assuming teleport has exactly one destination
1070 else
1071 cost += waypoint_gettravelcost(p, v, w, wp);
1072 if (wp.wpcost > cost)
1073 {
1074 wp.wpcost = cost;
1075 wp.enemy = w;
1076 wp.wpfire = 1;
1077 wp.wpnearestpoint = v;
1078 }
1079}
float waypoint_gettravelcost(vector from, vector to, entity from_ent, entity to_ent)

References bound(), entity(), vector, waypoint_gettravelcost(), and WAYPOINTFLAG_TELEPORT.

◆ navigation_markroutes_inverted()

void navigation_markroutes_inverted ( entity fixed_source_waypoint)

Definition at line 1172 of file navigation.qc.

1173{
1174 float cost, cost2;
1175 vector p;
1176 IL_EACH(g_waypoints, true,
1177 {
1178 it.wpconsidered = false;
1179 it.wpnearestpoint = '0 0 0';
1180 it.wpcost = 10000000;
1181 it.wpfire = 0;
1182 it.enemy = NULL;
1183 });
1184
1185 if(fixed_source_waypoint)
1186 {
1187 fixed_source_waypoint.wpconsidered = true;
1188 fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs);
1189 fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; // the cost to get from X to fixed_source_waypoint
1190 fixed_source_waypoint.wpfire = 1;
1191 fixed_source_waypoint.enemy = NULL;
1192 }
1193 else
1194 {
1195 error("need to start with a waypoint\n");
1196 }
1197
1198 bool searching = true;
1199 while (searching)
1200 {
1201 searching = false;
1202 IL_EACH(g_waypoints, it.wpfire,
1203 {
1204 searching = true;
1205 it.wpfire = 0;
1206 cost = it.wpcost; // cost to walk from it to home
1207 p = it.wpnearestpoint;
1208 entity wp = it;
1209 IL_EACH(g_waypoints, it != wp,
1210 {
1211 if(!waypoint_islinked(it, wp))
1212 continue;
1213 cost2 = cost + it.dmg;
1214 navigation_markroutes_checkwaypoint(wp, it, cost2, p);
1215 });
1216 });
1217 }
1218}
#define error
Definition pre.qh:6

References entity(), error, g_waypoints, IL_EACH, NULL, and vector.

Referenced by waypoint_unreachable().

◆ navigation_markroutes_nearestwaypoints()

float navigation_markroutes_nearestwaypoints ( entity this,
float maxdist )

Definition at line 1025 of file navigation.qc.

1026{
1027 //navigation_testtracewalk = true;
1028 int c = 0;
1029 IL_EACH(g_waypoints, !it.wpconsidered,
1030 {
1031 set_tracewalk_dest(it, this.origin, false);
1032
1033 vector diff = tracewalk_dest - this.origin;
1034 diff.z = max(0, diff.z);
1035 if(vdist(diff, <, maxdist))
1036 {
1037 it.wpconsidered = true;
1038 if (tracewalk(this, this.origin, this.mins, this.maxs,
1039 tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode))
1040 {
1041 it.wpnearestpoint = tracewalk_dest;
1042 it.wpcost = waypoint_gettravelcost(this.origin, tracewalk_dest, this, it) + it.dmg;
1043 it.wpfire = 1;
1044 it.enemy = NULL;
1045 c = c + 1;
1046 }
1047 }
1048 });
1049 //navigation_testtracewalk = false;
1050 return c;
1051}

References entity(), g_waypoints, and IL_EACH.

Referenced by navigation_markroutes().

◆ navigation_poproute()

void navigation_poproute ( entity this)

Definition at line 847 of file navigation.qc.

848{
849 this.goalcurrent_prev = this.goalcurrent;
853 //print("bot ", etos(this), " pop\n");
854 if(this.goalcurrent == this.goalentity)
855 {
856 this.goalentity = NULL;
858 }
859 this.goalcurrent = this.goalstack01;
860 this.goalstack01 = this.goalstack02;
861 this.goalstack02 = this.goalstack03;
862 this.goalstack03 = this.goalstack04;
863 this.goalstack04 = this.goalstack05;
864 this.goalstack05 = this.goalstack06;
865 this.goalstack06 = this.goalstack07;
866 this.goalstack07 = this.goalstack08;
867 this.goalstack08 = this.goalstack09;
868 this.goalstack09 = this.goalstack10;
869 this.goalstack10 = this.goalstack11;
870 this.goalstack11 = this.goalstack12;
871 this.goalstack12 = this.goalstack13;
872 this.goalstack13 = this.goalstack14;
873 this.goalstack14 = this.goalstack15;
874 this.goalstack15 = this.goalstack16;
875 this.goalstack16 = this.goalstack17;
876 this.goalstack17 = this.goalstack18;
877 this.goalstack18 = this.goalstack19;
878 this.goalstack19 = this.goalstack20;
879 this.goalstack20 = this.goalstack21;
880 this.goalstack21 = this.goalstack22;
881 this.goalstack22 = this.goalstack23;
882 this.goalstack23 = this.goalstack24;
883 this.goalstack24 = this.goalstack25;
884 this.goalstack25 = this.goalstack26;
885 this.goalstack26 = this.goalstack27;
886 this.goalstack27 = this.goalstack28;
887 this.goalstack28 = this.goalstack29;
888 this.goalstack29 = this.goalstack30;
889 this.goalstack30 = this.goalstack31;
890 this.goalstack31 = NULL;
891}

References entity(), FLOAT_MAX, goalcurrent, goalcurrent_distance_2d, goalcurrent_distance_time, goalcurrent_distance_z, goalcurrent_prev, goalentity, goalentity_lock_timeout, goalstack01, goalstack02, goalstack03, goalstack04, goalstack05, goalstack06, goalstack07, goalstack08, goalstack09, goalstack10, goalstack11, goalstack12, goalstack13, goalstack14, goalstack15, goalstack16, goalstack17, goalstack18, goalstack19, goalstack20, goalstack21, goalstack22, goalstack23, goalstack24, goalstack25, goalstack26, goalstack27, goalstack28, goalstack29, goalstack30, goalstack31, and NULL.

Referenced by navigation_goalrating_end(), navigation_poptouchedgoals(), and navigation_shortenpath().

◆ navigation_poptouchedgoals()

int navigation_poptouchedgoals ( entity this)

Definition at line 1630 of file navigation.qc.

1631{
1632 int removed_goals = 0;
1633
1634 if(!this.goalcurrent)
1635 return removed_goals;
1636
1637 if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
1638 {
1639 if (!this.goalcurrent.wpisbox // warpzone
1640 && vlen2(this.origin - this.goalstack01.origin) < vlen2(this.origin - this.goalcurrent.origin))
1641 {
1642 // immediately remove origin and destination waypoints
1643 navigation_poproute(this);
1644 ++removed_goals;
1645 navigation_poproute(this);
1646 ++removed_goals;
1647 this.lastteleporttime = 0;
1648 }
1649
1650 // make sure jumppad is really hit, don't rely on distance based checks
1651 // as they may report a touch even if it didn't really happen
1652 if(this.lastteleporttime > 0 && TELEPORT_USED(this, this.goalcurrent))
1653 {
1655 if((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) && this.goalcurrent.owner==this)
1656 {
1659 }
1660 if(this.jumppadcount)
1661 {
1662 // remove jumppad waypoint after a random delay to prevent bots getting
1663 // stuck on certain jumppads that require an extra initial horizontal speed
1664 float max_delay = 0.1;
1665 if (vdist(vec2(this.velocity), >, 2 * autocvar_sv_maxspeed))
1666 max_delay = 0.05;
1667 if (time - this.lastteleporttime < random() * max_delay)
1668 return removed_goals;
1669 }
1670 else if (this.goalcurrent.wpisbox) // teleport
1671 {
1672 // immediately remove origin and destination waypoints
1673 navigation_poproute(this);
1674 ++removed_goals;
1675 }
1676 navigation_poproute(this);
1677 this.lastteleporttime = 0;
1678 ++removed_goals;
1679 }
1680 return removed_goals;
1681 }
1682 else if (this.lastteleporttime > 0)
1683 {
1684 // sometimes bot is pushed so hard (by a jumppad or a shot) that ends up touching the next
1685 // teleport / jumppad / warpzone present in its path skipping check of one or more goals
1686 // if so immediately fix bot path by removing skipped goals
1687 entity tele_ent = NULL;
1688 if (this.goalstack01 && (this.goalstack01.wpflags & WAYPOINTFLAG_TELEPORT))
1689 tele_ent = this.goalstack01;
1690 else if (this.goalstack02 && (this.goalstack02.wpflags & WAYPOINTFLAG_TELEPORT))
1691 tele_ent = this.goalstack02;
1692 else if (this.goalstack03 && (this.goalstack03.wpflags & WAYPOINTFLAG_TELEPORT))
1693 tele_ent = this.goalstack03;
1694 if (tele_ent && TELEPORT_USED(this, tele_ent))
1695 {
1697 if ((tele_ent.wpflags & WAYPOINTFLAG_PERSONAL) && tele_ent.owner == this)
1698 {
1701 }
1702 while (this.goalcurrent != tele_ent)
1703 {
1704 navigation_poproute(this);
1705 ++removed_goals;
1706 }
1707 navigation_poproute(this);
1708 this.lastteleporttime = 0;
1709 ++removed_goals;
1710 return removed_goals;
1711 }
1712 // reset of lastteleporttime can be overriden by a jumppad when it's set
1713 // in more than one frame: make sure it's reset
1714 this.lastteleporttime = 0;
1715 }
1716
1717 // Loose goal touching check when running
1718 // check goalstack01 to make sure waypoint isn't the final goal
1719 if((this.aistatus & AI_STATUS_RUNNING) && this.goalcurrent.classname == "waypoint" && !(this.goalcurrent.wpflags & WAYPOINTFLAG_JUMP)
1720 && this.goalstack01 && !wasfreed(this.goalstack01) && vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed))
1721 {
1722 vector gco = this.goalcurrent.origin;
1723 float min_dist = BOT_BUNNYHOP_WP_DETECTION_RANGE;
1724 // also detect waypoints when bot is way above them but with a narrower horizontal range
1725 // so to increase chances bot ends up in the standard range (optimizes nearest waypoint finding)
1726 if(vdist(this.origin - gco, <, min_dist)
1727 || (vdist(vec2(this.origin - gco), <, min_dist * 0.5) && vdist(this.origin - eZ * 1.5 * min_dist - gco, <, min_dist)))
1728 {
1729 traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL);
1730 if(trace_fraction==1)
1731 {
1732 // Detect personal waypoints
1734 if((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) && this.goalcurrent.owner==this)
1735 {
1738 }
1739
1740 navigation_poproute(this);
1741 ++removed_goals;
1742 if(this.goalcurrent && (this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT))
1743 return removed_goals;
1744 }
1745 }
1746 }
1747
1748 while (this.goalcurrent && !IS_PLAYER(this.goalcurrent))
1749 {
1750 vector gc_min = this.goalcurrent.absmin;
1751 vector gc_max = this.goalcurrent.absmax;
1752 if(this.goalcurrent.classname == "waypoint" && !this.goalcurrent.wpisbox)
1753 {
1754 gc_min = this.goalcurrent.origin - '1 1 1' * 12;
1755 gc_max = this.goalcurrent.origin + '1 1 1' * 12 + eZ * (jumpheight_vec.z + STAT(PL_MIN, this).z);
1756 }
1757 if (this.ladder_entity)
1758 {
1759 if (!boxesoverlap(this.absmin, this.absmax - eZ * STAT(PL_MAX, this).z, gc_min, gc_max))
1760 break;
1761 }
1762 else
1763 {
1764 if (!boxesoverlap(this.absmin, this.absmax, gc_min, gc_max))
1765 break;
1766 }
1767
1768 // Detect personal waypoints
1770 if((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) && this.goalcurrent.owner==this)
1771 {
1774 }
1775
1776 navigation_poproute(this);
1777 ++removed_goals;
1778 if(this.goalcurrent && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)
1779 return removed_goals;
1780 }
1781 return removed_goals;
1782}
const int WAYPOINTFLAG_PERSONAL
Definition api.qh:15
const int AI_STATUS_WAYPOINT_PERSONAL_REACHED
Definition bot.qh:14
const int AI_STATUS_WAYPOINT_PERSONAL_GOING
Definition bot.qh:13
vector velocity
vector absmax
vector absmin
float jumppadcount
Definition jumppads.qh:28
entity ladder_entity
Definition ladder.qh:11
float random(void)
vector jumpheight_vec
Definition navigation.qh:12
#define TELEPORT_USED(pl, tele_wp)
Definition navigation.qh:63
vector view_ofs
Definition progsdefs.qc:151
#define vlen2(v)
Definition vector.qh:4
const vector eZ
Definition vector.qh:46

References absmax, absmin, AI_STATUS_RUNNING, AI_STATUS_WAYPOINT_PERSONAL_GOING, AI_STATUS_WAYPOINT_PERSONAL_REACHED, aistatus, autocvar_sv_maxspeed, BOT_BUNNYHOP_WP_DETECTION_RANGE, boxesoverlap(), entity(), eZ, goalcurrent, goalstack01, goalstack02, goalstack03, IS_PLAYER, jumpheight_vec, jumppadcount, ladder_entity, lastteleporttime, navigation_poproute(), NULL, origin, random(), STAT, TELEPORT_USED, time, trace_fraction, vdist, vec2, vector, velocity, view_ofs, vlen2, WAYPOINTFLAG_JUMP, WAYPOINTFLAG_PERSONAL, and WAYPOINTFLAG_TELEPORT.

Referenced by havocbot_movetogoal().

◆ navigation_pushroute()

void navigation_pushroute ( entity this,
entity e )

Definition at line 801 of file navigation.qc.

802{
803 this.goalcurrent_prev = this.goalcurrent;
807 //print("bot ", etos(this), " push ", etos(e), "\n");
808 if(this.goalstack31 == this.goalentity)
809 this.goalentity = NULL;
810 this.goalstack31 = this.goalstack30;
811 this.goalstack30 = this.goalstack29;
812 this.goalstack29 = this.goalstack28;
813 this.goalstack28 = this.goalstack27;
814 this.goalstack27 = this.goalstack26;
815 this.goalstack26 = this.goalstack25;
816 this.goalstack25 = this.goalstack24;
817 this.goalstack24 = this.goalstack23;
818 this.goalstack23 = this.goalstack22;
819 this.goalstack22 = this.goalstack21;
820 this.goalstack21 = this.goalstack20;
821 this.goalstack20 = this.goalstack19;
822 this.goalstack19 = this.goalstack18;
823 this.goalstack18 = this.goalstack17;
824 this.goalstack17 = this.goalstack16;
825 this.goalstack16 = this.goalstack15;
826 this.goalstack15 = this.goalstack14;
827 this.goalstack14 = this.goalstack13;
828 this.goalstack13 = this.goalstack12;
829 this.goalstack12 = this.goalstack11;
830 this.goalstack11 = this.goalstack10;
831 this.goalstack10 = this.goalstack09;
832 this.goalstack09 = this.goalstack08;
833 this.goalstack08 = this.goalstack07;
834 this.goalstack07 = this.goalstack06;
835 this.goalstack06 = this.goalstack05;
836 this.goalstack05 = this.goalstack04;
837 this.goalstack04 = this.goalstack03;
838 this.goalstack03 = this.goalstack02;
839 this.goalstack02 = this.goalstack01;
840 this.goalstack01 = this.goalcurrent;
841 this.goalcurrent = e;
842}

References entity(), FLOAT_MAX, goalcurrent, goalcurrent_distance_2d, goalcurrent_distance_time, goalcurrent_distance_z, goalcurrent_prev, goalentity, goalstack01, goalstack02, goalstack03, goalstack04, goalstack05, goalstack06, goalstack07, goalstack08, goalstack09, goalstack10, goalstack11, goalstack12, goalstack13, goalstack14, goalstack15, goalstack16, goalstack17, goalstack18, goalstack19, goalstack20, goalstack21, goalstack22, goalstack23, goalstack24, goalstack25, goalstack26, goalstack27, goalstack28, goalstack29, goalstack30, goalstack31, and NULL.

Referenced by havocbot_ai(), havocbot_movetogoal(), and navigation_routetogoal().

◆ navigation_routerating()

void navigation_routerating ( entity this,
entity e,
float f,
float rangebias )

Definition at line 1221 of file navigation.qc.

1222{
1223 if (!e || e.blacklisted) { return; }
1224
1225 rangebias = waypoint_getlinearcost(rangebias);
1227
1228 if (IS_PLAYER(e))
1229 {
1230 bool rate_wps = false;
1231 if (e.watertype < CONTENT_WATER || (e.waterlevel > WATERLEVEL_WETFEET && !STAT(FROZEN, e))
1232 || (e.flags & FL_PARTIALGROUND))
1233 {
1234 rate_wps = true;
1235 }
1236
1237 if(!IS_ONGROUND(e))
1238 {
1239 traceline(e.origin, e.origin + '0 0 -1500', true, NULL);
1240 int t = pointcontents(trace_endpos + '0 0 1');
1241 if(t != CONTENT_SOLID )
1242 {
1243 if(t == CONTENT_WATER || t == CONTENT_SLIME || t == CONTENT_LAVA)
1244 rate_wps = true;
1245 else if(tracebox_hits_trigger_hurt(e.origin, e.mins, e.maxs, trace_endpos))
1246 return;
1247 }
1248 }
1249
1250 if(rate_wps)
1251 {
1252 entity theEnemy = e;
1253 entity best_wp = NULL;
1254 float best_dist = FLOAT_MAX;
1256 && vdist(it.origin - theEnemy.origin, <, 500)
1257 && vdist(it.origin - this.origin, >, 100)
1258 && vdist(it.origin - this.origin, <, 10000),
1259 {
1260 float dist = vlen2(it.origin - theEnemy.origin);
1261 if (dist < best_dist)
1262 {
1263 best_wp = it;
1264 best_dist = dist;
1265 }
1266 });
1267 if (!best_wp)
1268 return;
1269 e = best_wp;
1270 }
1271 }
1272
1273 vector goal_org = (e.absmin + e.absmax) * 0.5;
1274
1275 //print("routerating ", etos(e), " = ", ftos(f), " - ", ftos(rangebias), "\n");
1276
1277 // Evaluate path using jetpack
1278 if(this.items & IT_JETPACK)
1281 {
1282 vector pointa, pointb;
1283
1284 LOG_DEBUG("jetpack ai: evaluating path for ", e.classname);
1285
1286 // Point A
1287 traceline(this.origin, this.origin + '0 0 65535', MOVE_NORMAL, this);
1288 pointa = trace_endpos - '0 0 1';
1289
1290 // Point B
1291 traceline(goal_org, goal_org + '0 0 65535', MOVE_NORMAL, e);
1292 pointb = trace_endpos - '0 0 1';
1293
1294 // Can I see these two points from the sky?
1295 traceline(pointa, pointb, MOVE_NORMAL, this);
1296
1297 if(trace_fraction==1)
1298 {
1299 LOG_DEBUG("jetpack ai: can bridge these two points");
1300
1301 // Lower the altitude of these points as much as possible
1302 float zdistance, xydistance, cost, t, fuel;
1303 vector down, npa, npb;
1304
1305 down = '0 0 -1' * (STAT(PL_MAX, this).z - STAT(PL_MIN, this).z) * 10;
1306
1307 do{
1308 npa = pointa + down;
1309 npb = pointb + down;
1310
1311 if(npa.z<=this.absmax.z)
1312 break;
1313
1314 if(npb.z<=e.absmax.z)
1315 break;
1316
1317 traceline(npa, npb, MOVE_NORMAL, this);
1318 if(trace_fraction==1)
1319 {
1320 pointa = npa;
1321 pointb = npb;
1322 }
1323 }
1324 while(trace_fraction == 1);
1325
1326
1327 // Rough estimation of fuel consumption
1328 // (ignores acceleration and current xyz velocity)
1329 xydistance = vlen(pointa - pointb);
1330 zdistance = fabs(pointa.z - this.origin.z);
1331
1332 t = zdistance / autocvar_g_jetpack_maxspeed_up;
1333 t += xydistance / autocvar_g_jetpack_maxspeed_side;
1334 fuel = t * autocvar_g_jetpack_fuel * 0.8;
1335
1336 LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), ", have ", ftos(GetResource(this, RES_FUEL)));
1337
1338 // enough fuel ?
1339 if(GetResource(this, RES_FUEL) > fuel || (this.items & IT_UNLIMITED_AMMO))
1340 {
1341 // Estimate cost
1342 // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship
1343 // - between air and ground speeds)
1344
1347 cost *= 1.5;
1348
1349 // Compare against other goals
1350 f = f * rangebias / (rangebias + cost);
1351
1352 if (navigation_bestrating < f)
1353 {
1354 LOG_DEBUG("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")");
1357 this.navigation_jetpack_goal = e;
1358 this.navigation_jetpack_point = pointb;
1359 }
1360 return;
1361 }
1362 }
1363 }
1364
1365 entity nwp;
1366 //te_wizspike(e.origin);
1367 //bprint(etos(e));
1368 //bprint("\n");
1369 // update the cached spawnfunc_waypoint link on a dynamic item entity
1370 if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL))
1371 {
1372 nwp = e;
1373 }
1374 else
1375 {
1376 if(waypointeditor_enabled && e.nearestwaypointtimeout >= 0 && time > e.nearestwaypointtimeout)
1377 e.nearestwaypoint = NULL;
1378
1379 if ((!e.nearestwaypoint || e.navigation_dynamicgoal)
1380 && e.nearestwaypointtimeout >= 0 && time > e.nearestwaypointtimeout)
1381 {
1382 if(IS_BOT_CLIENT(e) && e.goalcurrent && e.goalcurrent.classname == "waypoint")
1383 e.nearestwaypoint = nwp = e.goalcurrent;
1384 else
1385 e.nearestwaypoint = nwp = navigation_findnearestwaypoint(e, true);
1386 if(!nwp)
1387 {
1388 LOG_DEBUG("FAILED to find a nearest waypoint to '", e.classname, "' #", etos(e));
1389
1390 if(!e.navigation_dynamicgoal)
1391 e.blacklisted = true;
1392
1393 if(e.blacklisted)
1394 {
1395 LOG_DEBUG("The entity '", e.classname, "' is going to be excluded from path finding during this match");
1396 return;
1397 }
1398 }
1399
1400 if(e.navigation_dynamicgoal)
1401 e.nearestwaypointtimeout = time + 2;
1402 else if(waypointeditor_enabled)
1403 e.nearestwaypointtimeout = time + 3 + random() * 2;
1404 }
1405 nwp = e.nearestwaypoint;
1406 }
1407
1408 if (nwp && nwp.wpcost < 10000000)
1409 {
1410 //te_wizspike(nwp.wpnearestpoint);
1411 float nwptoitem_cost = 0;
1412 if(nwp.wpflags & WAYPOINTFLAG_TELEPORT)
1413 nwptoitem_cost = nwp.wp00mincost;
1414 else
1415 nwptoitem_cost = waypoint_gettravelcost(nwp.wpnearestpoint, goal_org, nwp, e);
1416 float cost = nwp.wpcost + nwptoitem_cost;
1417 LOG_DEBUG("checking ^5", e.classname, "^7 with base rating ^xf04", ftos(f), "^7 and rangebias ^xf40", ftos(rangebias));
1418 f = f * rangebias / (rangebias + cost);
1419 LOG_DEBUG(" ^5", e.classname, "^7 with cost ^6", ftos(cost), "^7 and final rating ^2", ftos(f));
1420 if (navigation_bestrating < f)
1421 {
1422 LOG_DEBUG(" ground path: ^3added goal ^5", e.classname);
1425 }
1426 }
1427}
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
const int IT_JETPACK
Definition item.qh:26
const int IT_UNLIMITED_AMMO
Definition item.qh:23
int items
Definition player.qh:227
int autocvar_g_jetpack_fuel
Definition stats.qh:298
float autocvar_g_jetpack_maxspeed_up
Definition stats.qh:300
float autocvar_g_jetpack_maxspeed_side
Definition stats.qh:299
const int FL_PARTIALGROUND
Definition constants.qh:79
const float CONTENT_SOLID
const float MOVE_NORMAL
const float CONTENT_WATER
vector trace_endpos
const float CONTENT_LAVA
const float CONTENT_SLIME
float autocvar_bot_ai_navigation_jetpack_mindistance
Definition cvars.qh:40
float autocvar_bot_ai_navigation_jetpack
Definition cvars.qh:39
bool tracebox_hits_trigger_hurt(vector start, vector e_min, vector e_max, vector end)
Definition hurt.qc:79
string ftos(float f)
float fabs(float f)
string etos(entity e)
const int WATERLEVEL_WETFEET
Definition movetypes.qh:12
entity navigation_findnearestwaypoint(entity ent, float walkfromwp)
vector navigation_jetpack_point
Definition navigation.qh:78
float waypoint_getlinearcost(float dist)

References CONTENT_LAVA, CONTENT_SLIME, CONTENT_SOLID, CONTENT_WATER, entity(), FL_PARTIALGROUND, FLOAT_MAX, g_waypoints, IL_EACH, IS_ONGROUND, IS_PLAYER, NULL, STAT, trace_endpos, tracebox_hits_trigger_hurt(), vdist, WATERLEVEL_WETFEET, waypoint_getlinearcost(), and WAYPOINTFLAG_TELEPORT.

Referenced by havocbot_goalrating_ctf_droppedflags(), havocbot_goalrating_ctf_enemybase(), havocbot_goalrating_ctf_enemyflag(), havocbot_goalrating_ctf_ourbase(), havocbot_goalrating_ctf_ourstolenflag(), havocbot_goalrating_enemyplayers(), havocbot_goalrating_ft_freeplayers(), havocbot_goalrating_items(), havocbot_goalrating_kh(), havocbot_goalrating_ons_controlpoints_attack(), havocbot_goalrating_ons_generator_attack(), havocbot_moveto_refresh_route(), havocbot_role_cts(), and havocbot_role_race().

◆ navigation_routetogoal()

bool navigation_routetogoal ( entity this,
entity e,
vector startposition )

Definition at line 1430 of file navigation.qc.

1431{
1432 // if there is no goal, just exit
1433 if (!e)
1434 return false;
1435
1436 entity teleport_goal = NULL;
1437
1438 this.goalentity = e;
1439
1440 if(e.wpflags & WAYPOINTFLAG_TELEPORT)
1441 {
1442 // force teleport destination as route destination
1443 teleport_goal = e;
1444 navigation_pushroute(this, e.wp00);
1445 this.goalentity = e.wp00;
1446 }
1447
1448 // put the entity on the goal stack
1449 //print("routetogoal ", etos(e), "\n");
1450 navigation_pushroute(this, e);
1451
1452 if(teleport_goal)
1453 e = this.goalentity;
1454
1455 if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL))
1456 {
1457 this.wp_goal_prev1 = this.wp_goal_prev0;
1458 this.wp_goal_prev0 = e;
1459 }
1460
1461 if((this.items & IT_JETPACK) && e == this.navigation_jetpack_goal)
1462 return true;
1463
1464 // if it can reach the goal there is nothing more to do
1465 set_tracewalk_dest(e, startposition, true);
1467 && (trace_ent == this || tracewalk(this, startposition, STAT(PL_MIN, this), STAT(PL_MAX, this),
1469 {
1470 return true;
1471 }
1472
1473 entity nearest_wp = NULL;
1474 // see if there are waypoints describing a path to the item
1475 if(e.classname != "waypoint" || (e.wpflags & WAYPOINTFLAG_PERSONAL))
1476 {
1477 e = e.nearestwaypoint;
1478 nearest_wp = e;
1479 }
1480 else if(teleport_goal)
1481 e = teleport_goal;
1482 else
1483 e = e.enemy; // we already have added it, so...
1484
1485 if(e == NULL)
1486 return false;
1487
1488 if(nearest_wp && nearest_wp.enemy && !(nearest_wp.enemy.wpflags & WPFLAGMASK_NORELINK))
1489 {
1490 // often path can be optimized by not adding the nearest waypoint
1491 if (this.goalentity.navigation_dynamicgoal || waypointeditor_enabled)
1492 {
1493 if (nearest_wp.enemy.wpcost < autocvar_bot_ai_strategyinterval_movingtarget)
1494 {
1495 if (vdist(vec2(this.goalentity.origin - nearest_wp.origin), <, 32))
1496 e = nearest_wp.enemy;
1497 else
1498 {
1499 set_tracewalk_dest(this.goalentity, nearest_wp.enemy.origin, true);
1500 if (trace_ent == this || (vdist(tracewalk_dest - nearest_wp.enemy.origin, <, 1050)
1501 && vlen2(tracewalk_dest - nearest_wp.enemy.origin) < vlen2(nearest_wp.origin - nearest_wp.enemy.origin)
1502 && tracewalk(this, nearest_wp.enemy.origin, STAT(PL_MIN, this), STAT(PL_MAX, this),
1504 {
1505 e = nearest_wp.enemy;
1506 }
1507 }
1508 }
1509 }
1510 else
1511 {
1512 // NOTE unlike waypoints, items hold incoming links
1514 int link_num = navigation_item_getlinknum(this.goalentity, nearest_wp.enemy);
1515 if (link_num >= 0)
1516 {
1517 if (navigation_item_iswalkablelink(this.goalentity, link_num))
1518 e = nearest_wp.enemy;
1519 }
1520 else // untested link
1521 {
1522 entity wp = nearest_wp.enemy;
1523 entity goal = this.goalentity;
1524 bool walkable = false;
1525 if (checkpvs(wp.origin, goal))
1526 {
1527 set_tracewalk_dest(goal, wp.origin, false);
1528 if (vdist(tracewalk_dest - wp.origin, <, 1050)
1529 && tracewalk(goal, wp.origin, PL_MIN_CONST, PL_MAX_CONST,
1531 {
1532 walkable = true;
1533 e = nearest_wp.enemy;
1534 }
1535 }
1536 navigation_item_add_link(wp, goal, walkable);
1537 }
1538 }
1539 }
1540
1541 for (;;)
1542 {
1543 // add the spawnfunc_waypoint to the path
1544 navigation_pushroute(this, e);
1545 e = e.enemy;
1546
1547 if(e==NULL)
1548 break;
1549 }
1550
1551 return false;
1552}
bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float end_height, float movemode)
float checkpvs(vector viewpos, entity viewee)
#define MAX_CHASE_DISTANCE
Definition navigation.qc:55
void navigation_pushroute(entity this, entity e)
entity wp_goal_prev1
Definition navigation.qh:70
#define navigation_item_getlinknum(to_item, from_wp)
Definition navigation.qh:57
#define navigation_item_iswalkablelink(to_item, from_wp)
Definition navigation.qh:58
#define navigation_item_initlinks_ifneeded(e)
Definition navigation.qh:56
entity wp_goal_prev0
Definition navigation.qh:69
#define navigation_item_add_link(from_wp, to_item, walkable)
Definition navigation.qh:60

References autocvar_bot_ai_strategyinterval_movingtarget, bot_navigation_movemode, checkpvs(), entity(), goalcurrent, goalentity, IS_MOVABLE, IT_JETPACK, items, MAX_CHASE_DISTANCE, navigation_item_add_link, navigation_item_getlinknum, navigation_item_initlinks_ifneeded, navigation_item_iswalkablelink, navigation_jetpack_goal, navigation_pushroute(), NULL, origin, PL_MAX_CONST, PL_MIN_CONST, set_tracewalk_dest(), STAT, trace_ent, tracewalk(), tracewalk_dest, tracewalk_dest_height, vdist, vec2, vector, vlen2, waypointeditor_enabled, WAYPOINTFLAG_PERSONAL, WAYPOINTFLAG_TELEPORT, wp_goal_prev0, wp_goal_prev1, and WPFLAGMASK_NORELINK.

Referenced by havocbot_movetogoal(), navigation_goalrating_end(), and navigation_unstuck().

◆ navigation_shortenpath()

bool navigation_shortenpath ( entity this)

Definition at line 1555 of file navigation.qc.

1556{
1557 if (!this.goalstack01 || wasfreed(this.goalstack01))
1558 return false;
1559 if (this.bot_tracewalk_time > time)
1560 return false;
1561 this.bot_tracewalk_time = max(time, this.bot_tracewalk_time) + 0.25;
1562
1563 bool cut_allowed = false;
1564 entity next = this.goalentity;
1565 // evaluate whether bot can discard current route and chase directly a player, trying to
1566 // keep waypoint route as long as possible, as it is safer and faster (bot can bunnyhop)
1567 if (IS_MOVABLE(next))
1568 {
1569 set_tracewalk_dest(next, this.origin, true);
1570 if (vdist(this.origin - tracewalk_dest, <, 200))
1571 cut_allowed = true;
1572 else if (vdist(tracewalk_dest - this.origin, <, MAX_CHASE_DISTANCE)
1573 && vdist(tracewalk_dest - this.goalcurrent.origin, >, 200)
1574 && vdist(this.origin - this.goalcurrent.origin, >, 100)
1575 && checkpvs(this.origin + this.view_ofs, next))
1576 {
1577 if (vlen2(next.origin - this.origin) < vlen2(this.goalcurrent.origin - this.origin))
1578 cut_allowed = true;
1579 else
1580 {
1581 vector deviation = vectoangles(this.goalcurrent.origin - this.origin) - vectoangles(next.origin - this.origin);
1582 while (deviation.y < -180) deviation.y += 360;
1583 while (deviation.y > 180) deviation.y -= 360;
1584 if (fabs(deviation.y) > 25)
1585 cut_allowed = true;
1586 }
1587 }
1588 if (cut_allowed)
1589 {
1590 if (trace_ent == this || tracewalk(this, this.origin, this.mins, this.maxs,
1592 {
1593 LOG_DEBUG("path optimized for ", this.netname, ", route cleared");
1594 do
1595 {
1596 navigation_poproute(this);
1597 }
1598 while (this.goalcurrent != next);
1599 return true;
1600 }
1601 return false;
1602 }
1603 }
1604
1605 next = this.goalstack01;
1606 // if for some reason the bot is closer to the next goal, pop the current one
1608 && vlen2(this.goalcurrent.origin - next.origin) > vlen2(next.origin - this.origin)
1609 && checkpvs(this.origin + this.view_ofs, next))
1610 {
1611 set_tracewalk_dest(next, this.origin, true);
1612 cut_allowed = true;
1613 }
1614
1615 if (cut_allowed)
1616 {
1617 if (trace_ent == this || tracewalk(this, this.origin, this.mins, this.maxs,
1619 {
1620 LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue");
1621 navigation_poproute(this);
1622 return true;
1623 }
1624 }
1625 return false;
1626}
float bot_tracewalk_time
Definition api.qh:37
vector mins
vector maxs
next
Definition all.qh:93
vector vectoangles(vector v)

References bot_navigation_movemode, bot_tracewalk_time, checkpvs(), entity(), fabs(), goalcurrent, goalentity, goalstack01, IS_MOVABLE, LOG_DEBUG, max(), MAX_CHASE_DISTANCE, maxs, mins, navigation_poproute(), netname, next, origin, set_tracewalk_dest(), time, trace_ent, tracewalk(), tracewalk_dest, tracewalk_dest_height, vdist, vectoangles(), vector, view_ofs, vlen2, WAYPOINTFLAG_JUMP, and WAYPOINTFLAG_TELEPORT.

Referenced by havocbot_movetogoal().

◆ navigation_unstuck()

void navigation_unstuck ( entity this)

Definition at line 1908 of file navigation.qc.

1909{
1911 return;
1912
1913 bool has_user_waypoints = false;
1915 {
1916 has_user_waypoints = true;
1917 break;
1918 });
1919 if (!has_user_waypoints)
1920 return;
1921
1922 float search_radius = 1000;
1923
1925 {
1926 LOG_DEBUG(this.netname, " stuck, taking over the waypoints queue");
1930 }
1931
1932 if(bot_waypoint_queue_owner!=this)
1933 return;
1934
1936 {
1937 // evaluate the next goal on the queue
1938 float d = vlen2(this.origin - bot_waypoint_queue_goal.origin);
1939 LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with squared distance ", ftos(d));
1941 if (tracewalk(this, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this),
1943 {
1945 {
1948 }
1949 }
1950
1951 // move to a random waypoint while bot is searching for a walkable path;
1952 // this is usually sufficient to unstuck bots from bad spots or when other
1953 // bots of the same team block all their ways
1954 if (!bot_waypoint_queue_bestgoal && (!this.goalentity || random() < 0.1))
1955 {
1959 }
1960
1961 bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal;
1962
1964 {
1966 {
1967 LOG_DEBUG(this.netname, " stuck, reachable waypoint found, heading to it");
1971 this.aistatus &= ~AI_STATUS_STUCK;
1972 }
1973 else
1974 {
1975 LOG_DEBUG(this.netname, " stuck, cannot walk to any waypoint at all");
1976 }
1977
1979 }
1980 }
1981 else
1982 {
1983 if(bot_strategytoken!=this)
1984 return;
1985
1986 // build a new queue
1987 LOG_DEBUG(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu");
1988
1989 entity first = NULL;
1990
1991 FOREACH_ENTITY_RADIUS(this.origin, search_radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
1992 {
1993 if(bot_waypoint_queue_goal)
1994 bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = it;
1995 else
1996 first = it;
1997
1998 bot_waypoint_queue_goal = it;
1999 bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL;
2000 });
2001
2002 if (first)
2004 else
2005 {
2006 LOG_DEBUG(this.netname, " stuck, cannot walk to any waypoint at all");
2008 }
2009 }
2010}
const int WAYPOINTFLAG_GENERATED
Definition api.qh:11
entity bot_strategytoken
Definition bot.qh:77
#define FOREACH_ENTITY_RADIUS(org, dist, cond, body)
Definition iter.qh:160
void navigation_goalrating_timeout_set(entity this)
Definition navigation.qc:20
entity bot_waypoint_queue_bestgoal
Definition navigation.qh:88
float bot_waypoint_queue_bestgoalrating
Definition navigation.qh:89
entity bot_waypoint_queue_owner
Definition navigation.qh:85
entity bot_waypoint_queue_goal
Definition navigation.qh:86

References AI_STATUS_STUCK, aistatus, autocvar_bot_wander_enable, bot_navigation_movemode, bot_strategytoken, bot_waypoint_queue_bestgoal, bot_waypoint_queue_bestgoalrating, bot_waypoint_queue_goal, bot_waypoint_queue_owner, entity(), FOREACH_ENTITY_RADIUS, ftos(), g_waypoints, goalentity, IL_EACH, LOG_DEBUG, navigation_clearroute(), navigation_goalrating_timeout_expire(), navigation_goalrating_timeout_set(), navigation_routetogoal(), netname, NULL, origin, random(), set_tracewalk_dest(), STAT, tracewalk(), tracewalk_dest, tracewalk_dest_height, vlen2, and WAYPOINTFLAG_GENERATED.

Referenced by bot_think().

◆ navigation_waypoint_will_link()

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 )

Definition at line 896 of file navigation.qc.

897{
898 if (vdist(v - org, <, bestdist))
899 {
900 traceline(v, org, true, ent);
901 if (trace_fraction == 1)
902 {
903 if (walkfromwp)
904 {
905 if (tracewalk(ent, v, PL_MIN_CONST, PL_MAX_CONST, v2, v2_height, bot_navigation_movemode))
906 return true;
907 }
908 else
909 {
911 return true;
912 }
913 }
914 }
915 return false;
916}

References bot_navigation_movemode, entity(), org, PL_MAX_CONST, PL_MIN_CONST, trace_fraction, tracewalk(), vdist, and vector.

Referenced by botframe_autowaypoints_fix_from().

◆ resurface_limited()

vector resurface_limited ( vector org,
float lim,
vector m1 )

Definition at line 245 of file navigation.qc.

246{
247 if (WETFEET(org + eZ * (lim - org.z)))
248 org.z = lim;
249 else
250 {
251 float RES_min_h = org.z;
252 float RES_max_h = lim;
253 do {
254 org.z = 0.5 * (RES_min_h + RES_max_h);
255 if(WETFEET(org))
256 RES_min_h = org.z;
257 else
258 RES_max_h = org.z;
259 } while (RES_max_h - RES_min_h >= 1);
260 org.z = RES_min_h;
261 }
262 return org;
263}
#define WETFEET(pos)
Definition bot.qh:87

References eZ, org, vector, and WETFEET.

◆ set_tracewalk_dest()

void set_tracewalk_dest ( entity ent,
vector org,
bool fix_player_dest )

Definition at line 120 of file navigation.qc.

121{
122 if ((ent.classname != "waypoint") || ent.wpisbox)
123 {
124 vector wm1 = ent.origin + ent.mins;
125 vector wm2 = ent.origin + ent.maxs;
126 if (IS_PLAYER(ent) || IS_MONSTER(ent))
127 {
128 // move destination point out of player bbox otherwise tracebox always fails
129 // (if bot_navigation_ignoreplayers is false)
130 wm1 += vec2(PL_MIN_CONST) + '-1 -1 0';
131 wm2 += vec2(PL_MAX_CONST) + '1 1 0';
132 }
133 // set destination point to x and y coords of ent that are closer to org
134 // z coord is set to ent's min height
135 tracewalk_dest.x = bound(wm1.x, org.x, wm2.x);
136 tracewalk_dest.y = bound(wm1.y, org.y, wm2.y);
137 if ((IS_PLAYER(ent) || IS_MONSTER(ent))
138 && org.x == tracewalk_dest.x && org.y == tracewalk_dest.y && org.z > tracewalk_dest.z)
139 {
140 tracewalk_dest.z = wm2.z - PL_MIN_CONST.z;
142 fix_player_dest = false;
143 }
144 else
145 {
146 tracewalk_dest.z = wm1.z;
147 tracewalk_dest_height = wm2.z - wm1.z;
148 }
149 }
150 else
151 {
152 tracewalk_dest = ent.origin;
154 }
155 if (fix_player_dest && IS_PLAYER(ent) && !IS_ONGROUND(ent))
156 {
157 // snap player to the ground
158 if (org.x == tracewalk_dest.x && org.y == tracewalk_dest.y)
159 {
160 // bot is right under the player
161 tracebox(ent.origin, ent.mins, ent.maxs, ent.origin - '0 0 700', MOVE_NORMAL, ent);
164 }
165 else
166 {
167 tracebox(tracewalk_dest, ent.mins, ent.maxs, tracewalk_dest - '0 0 700', MOVE_NORMAL, ent);
169 {
172 }
173 }
174 }
175}
float trace_startsolid
#define IS_MONSTER(v)
Definition utils.qh:21

References bound(), entity(), IS_MONSTER, IS_ONGROUND, IS_PLAYER, MOVE_NORMAL, org, PL_MAX_CONST, PL_MIN_CONST, trace_endpos, trace_startsolid, tracewalk_dest, tracewalk_dest_height, vec2, and vector.

Referenced by havocbot_movetogoal(), navigation_routetogoal(), navigation_shortenpath(), and navigation_unstuck().

◆ set_tracewalk_dest_2()

vector set_tracewalk_dest_2 ( entity ent,
vector org )

Definition at line 178 of file navigation.qc.

179{
180 vector closer_dest = '0 0 0';
181 if ((ent.classname != "waypoint") || ent.wpisbox)
182 {
183 vector wm1 = ent.origin + ent.mins;
184 vector wm2 = ent.origin + ent.maxs;
185 closer_dest.x = bound(wm1.x, org.x, wm2.x);
186 closer_dest.y = bound(wm1.y, org.y, wm2.y);
187 closer_dest.z = bound(wm1.z, org.z, wm2.z);
188 // set destination point to x and y coords of ent that are closer to org
189 // z coord is set to ent's min height
190 tracewalk_dest.x = closer_dest.x;
191 tracewalk_dest.y = closer_dest.y;
192 tracewalk_dest.z = wm1.z;
193 tracewalk_dest_height = wm2.z - wm1.z; // destination height
194 }
195 else
196 {
197 closer_dest = ent.origin;
198 tracewalk_dest = closer_dest;
200 }
201 return closer_dest;
202}

References bound(), entity(), org, tracewalk_dest, tracewalk_dest_height, and vector.

Referenced by waypoint_think().

◆ tracewalk()

bool tracewalk ( entity e,
vector start,
vector m1,
vector m2,
vector end,
float end_height,
float movemode )

Definition at line 274 of file navigation.qc.

275{
277 {
279 debugnode(e, start);
280 }
281
282 vector org = start;
283 vector flatdir = end - start;
284 flatdir.z = 0;
285 float flatdist = vlen(flatdir);
286 flatdir = normalize(flatdir);
287 float stepdist = 32;
288 bool ignorehazards = false;
289 int nav_action;
290
291 // Analyze starting point
292 if (IN_LAVA(start))
293 ignorehazards = true;
294
295 tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e);
297 {
298 // Bad start
301
302 //print("tracewalk: ", vtos(start), " is a bad start\n");
303 return false;
304 }
305
306 vector end2 = end;
307 if(end_height)
308 end2.z += end_height;
309
310 vector fixed_end = end;
311 vector move;
312
313 if (flatdist > 0 && WETFEET(org))
314 {
315 if (SUBMERGED(org))
316 nav_action = NAV_SWIM_UNDERWATER;
317 else
318 {
319 // tracebox down by player's height
320 // useful to know if water level is so low that bot can still walk
321 tracebox(org, m1, m2, org - eZ * (m2.z - m1.z), movemode, e);
323 {
325 nav_action = NAV_SWIM_UNDERWATER;
326 }
327 else
328 nav_action = NAV_WALK;
329 }
330 }
331 else
332 nav_action = NAV_WALK;
333
334 // Movement loop
335 while (true)
336 {
337 if (flatdist <= 0)
338 {
339 bool success = true;
340 if (org.z > end2.z + 1)
341 {
342 tracebox(org, m1, m2, end2, movemode, e);
344 if (org.z > end2.z + 1)
345 success = false;
346 }
347 else if (org.z < end.z - 1)
348 {
349 tracebox(org, m1, m2, org - jumpheight_vec, movemode, e);
351 {
353 tracebox(v, m1, m2, end, movemode, e);
354 if(trace_endpos.z >= end.z - 1)
355 {
357 trace_endpos = v;
358 }
359 }
360 else if (trace_endpos.z > org.z - jumpheight_vec.z)
361 tracebox(trace_endpos, m1, m2, trace_endpos + jumpheight_vec, movemode, e);
363 if (org.z < end.z - 1)
364 success = false;
365 }
366
367 if (success)
368 {
369 // Succeeded
371 {
372 debugnode(e, org);
374 }
375
376 //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
377 return true;
378 }
379 }
380
382 debugnode(e, org);
383
384 if (flatdist <= 0)
385 break;
386
387 if (stepdist > flatdist)
388 stepdist = flatdist;
389 if(nav_action == NAV_SWIM_UNDERWATER || (nav_action == NAV_SWIM_ONWATER && org.z > end2.z))
390 {
391 // can't use movement direction here to calculate move because of
392 // precision errors especially when direction has a high enough z value
393 //water_dir = normalize(water_end - org);
394 //move = org + water_dir * stepdist;
395 fixed_end.z = bound(end.z, org.z, end2.z);
396 if (stepdist == flatdist) {
397 move = fixed_end;
398 flatdist = 0;
399 } else {
400 move = org + (fixed_end - org) * (stepdist / flatdist);
401 flatdist = vlen(vec2(fixed_end - move));
402 }
403 }
404 else // horiz. direction
405 {
406 flatdist -= stepdist;
407 move = org + flatdir * stepdist;
408 }
409
410 if(nav_action == NAV_SWIM_ONWATER)
411 {
412 tracebox(org, m1, m2, move, movemode, e); // swim
413
414 // hit something
415 if (trace_fraction < 1)
416 {
417 // stepswim
418 tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
419
420 if (trace_fraction < 1 || trace_startsolid) // can't jump obstacle out of water
421 {
423 if(navigation_checkladders(e, org, m1, m2, end, end2, movemode))
424 {
426 {
427 debugnode(e, org);
429 }
430
431 //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
432 return true;
433 }
434
437
438 return false;
439 //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
440 }
441
442 //succesful stepswim
443
444 if (flatdist <= 0)
445 {
447 continue;
448 }
449
450 if (org.z <= move.z) // going horiz.
451 {
452 tracebox(trace_endpos, m1, m2, move, movemode, e);
454 nav_action = NAV_WALK;
455 continue;
456 }
457 }
458
459 if (org.z <= move.z) // going horiz.
460 {
462 nav_action = NAV_SWIM_ONWATER;
463 }
464 else // going down
465 {
467 if (SUBMERGED(org))
468 nav_action = NAV_SWIM_UNDERWATER;
469 else
470 nav_action = NAV_SWIM_ONWATER;
471 }
472 }
473 else if(nav_action == NAV_SWIM_UNDERWATER)
474 {
475 if (move.z >= org.z) // swimming upwards or horiz.
476 {
477 tracebox(org, m1, m2, move, movemode, e); // swim
478
479 bool stepswum = false;
480
481 // hit something
482 if (trace_fraction < 1)
483 {
484 // stepswim
485 vector stepswim_move = move + stepheightvec;
486 if (flatdist > 0 && stepswim_move.z > end2.z + stepheightvec.z) // don't allow stepswim to go higher than destination
487 stepswim_move.z = end2.z;
488
489 tracebox(org + stepheightvec, m1, m2, stepswim_move, movemode, e);
490
491 // hit something
493 {
496
497 //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
498 return false;
499 }
500
501 if (trace_fraction < 1)
502 {
503 float org_z_prev = org.z;
504 RESURFACE_LIMITED(org, end2.z);
505 if(org.z == org_z_prev)
506 {
509
510 //print("tracewalk: ", vtos(start), " can't reach ", vtos(end), "\n");
511 return false;
512 }
513 if(SUBMERGED(org))
514 nav_action = NAV_SWIM_UNDERWATER;
515 else
516 nav_action = NAV_SWIM_ONWATER;
517
518 // we didn't advance horiz. in this step, flatdist decrease should be reverted
519 // but we can't do it properly right now... apply this workaround instead
520 if (flatdist <= 0)
521 flatdist = 1;
522
523 continue;
524 }
525
526 //succesful stepswim
527
528 if (flatdist <= 0)
529 {
531 continue;
532 }
533
534 stepswum = true;
535 }
536
537 if (!WETFEET(trace_endpos))
538 {
539 tracebox(trace_endpos, m1, m2, trace_endpos - eZ * (stepdist + (m2.z - m1.z)), movemode, e);
540 // if stepswum we'll land on the obstacle, avoid the SUBMERGED check
541 if (!stepswum && SUBMERGED(trace_endpos))
542 {
545 nav_action = NAV_SWIM_ONWATER;
546 continue;
547 }
548
549 // not submerged
551 nav_action = NAV_WALK;
552 continue;
553 }
554
555 // wetfeet
557 nav_action = NAV_SWIM_UNDERWATER;
558 continue;
559 }
560 else //if (move.z < org.z) // swimming downwards
561 {
562 tracebox(org, m1, m2, move, movemode, e); // swim
563
564 // hit something
565 if (trace_fraction < 1)
566 {
567 // stepswim
568 tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
569
570 // hit something
571 if (trace_fraction < 1 || trace_startsolid) // can't jump obstacle out of water
572 {
575
576 //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
577 return false;
578 }
579
580 //succesful stepswim
581
582 if (flatdist <= 0)
583 {
585 continue;
586 }
587
588 if (trace_endpos.z > org.z && !SUBMERGED(trace_endpos))
589 {
590 // stepswim caused upwards direction
591 tracebox(trace_endpos, m1, m2, trace_endpos - stepheightvec, movemode, e);
593 {
595 nav_action = NAV_WALK;
596 continue;
597 }
598 }
599 }
600
602 nav_action = NAV_SWIM_UNDERWATER;
603 continue;
604 }
605 }
606 else if(nav_action == NAV_WALK)
607 {
608 // walk
609 tracebox(org, m1, m2, move, movemode, e);
610
613
614 // hit something
615 if (trace_fraction < 1)
616 {
617 // check if we can walk over this obstacle, possibly by jumpstepping
618 tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
620 {
621 if (trace_startsolid) // hit ceiling above org
622 {
623 // reduce stepwalk height
624 tracebox(org, m1, m2, org + stepheightvec, movemode, e);
625 tracebox(trace_endpos, m1, m2, move + eZ * (trace_endpos.z - move.z), movemode, e);
626 }
627 else //if (trace_fraction < 1)
628 {
629 tracebox(org + jumpstepheightvec, m1, m2, move + jumpstepheightvec, movemode, e);
630 if (trace_startsolid) // hit ceiling above org
631 {
632 // reduce jumpstepwalk height
633 tracebox(org, m1, m2, org + jumpstepheightvec, movemode, e);
634 tracebox(trace_endpos, m1, m2, move + eZ * (trace_endpos.z - move.z), movemode, e);
635 }
636 }
637
638 if (trace_fraction < 1)
639 {
641 v.z = org.z + jumpheight_vec.z;
642 if(navigation_checkladders(e, v, m1, m2, end, end2, movemode))
643 {
645 {
646 debugnode(e, v);
648 }
649
650 //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
651 return true;
652 }
653
656
657 traceline( org, move, movemode, e);
658
659 if ( trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
660 {
661 vector nextmove;
662 move = trace_endpos;
663 while(trace_ent.classname == "door_rotating" || trace_ent.classname == "door")
664 {
665 nextmove = move + (flatdir * stepdist);
666 traceline( move, nextmove, movemode, e);
667 move = nextmove;
668 }
669 flatdist = vlen(vec2(end - move));
670 }
671 else
672 {
675
676 //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
677 //te_explosion(trace_endpos);
678 //print(ftos(e.dphitcontentsmask), "\n");
679 return false; // failed
680 }
681 }
682 else
683 move = trace_endpos;
684 }
685 else
686 move = trace_endpos;
687 }
688 else
689 move = trace_endpos;
690
691 // trace down from stepheight as far as possible and move there,
692 // if this starts in solid we try again without the stepup, and
693 // if that also fails we assume it is a wall
694 // (this is the same logic as the Quake walkmove function used)
695 tracebox(move, m1, m2, move + '0 0 -65536', movemode, e);
696
698
699 if (!ignorehazards)
700 {
701 if (IN_LAVA(org))
702 {
704 {
707 }
708
709 //print("tracewalk: ", vtos(start), " hits a hazard when trying to reach ", vtos(end), "\n");
710 return false;
711 }
712 }
713
714 if (flatdist <= 0)
715 {
716 if(move.z >= end2.z && org.z < end2.z)
717 org.z = end2.z;
718 continue;
719 }
720
721 if(org.z > move.z - 1 || !SUBMERGED(org))
722 {
723 nav_action = NAV_WALK;
724 continue;
725 }
726
727 // ended up submerged while walking
729 debugnode(e, org);
730
731 RESURFACE_LIMITED(org, move.z);
732 nav_action = NAV_SWIM_ONWATER;
733 continue;
734 }
735 }
736
737 //print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n");
738
739 // moved but didn't arrive at the intended destination
742
743 return false;
744}
#define IN_LAVA(pos)
Definition bot.qh:84
const float MOVE_NOMONSTERS
bool autocvar_bot_debug_tracewalk
Definition cvars.qh:55
vector normalize(vector v)
#define NAV_SWIM_ONWATER
void debugnodestatus(vector position, float status)
bool navigation_checkladders(entity e, vector org, vector m1, vector m2, vector end, vector end2, int movemode)
void debugnode(entity this, vector node)
#define NAV_SWIM_UNDERWATER
#define NAV_WALK
#define RESURFACE_LIMITED(org, lim)
void debugresetnodes()
vector jumpstepheightvec
Definition navigation.qh:10
vector stepheightvec
Definition navigation.qh:11

References autocvar_bot_debug_tracewalk, bound(), DEBUG_NODE_FAIL, DEBUG_NODE_SUCCESS, DEBUG_NODE_WARNING, debugnode(), debugnodestatus(), debugresetnodes(), entity(), eZ, IN_LAVA, jumpheight_vec, jumpstepheightvec, MOVE_NOMONSTERS, NAV_SWIM_ONWATER, NAV_SWIM_UNDERWATER, NAV_WALK, navigation_checkladders(), normalize(), org, RESURFACE_LIMITED, stepheightvec, SUBMERGED, trace_endpos, trace_ent, trace_fraction, trace_startsolid, vec2, vector, vlen(), and WETFEET.

Referenced by GameCommand_trace(), havocbot_movetogoal(), navigation_routetogoal(), navigation_shortenpath(), navigation_unstuck(), navigation_waypoint_will_link(), and waypoint_think().

Variable Documentation

◆ goalcounter

float goalcounter

Definition at line 2058 of file navigation.qc.

Referenced by debuggoalstack().

◆ lastposition

vector lastposition

Definition at line 2059 of file navigation.qc.

Referenced by debuggoalstack().

◆ speed

float speed

Definition at line 18 of file navigation.qc.