local float ignorehazards;
local float swimming;
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
+ {
debugresetnodes();
debugnode(start);
- #endif
+ }
move = end - start;
move_z = 0;
if (trace_startsolid)
{
// Bad start
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnodestatus(start, DEBUG_NODE_FAIL);
- #endif
+
//print("tracewalk: ", vtos(start), " is a bad start\n");
return FALSE;
}
if (boxesoverlap(end, end, org + m1 + '-1 -1 -1', org + m2 + '1 1 1'))
{
// Succeeded
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnodestatus(org, DEBUG_NODE_SUCCESS);
- #endif
+
//print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n");
return TRUE;
}
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnode(org);
- #endif
if (dist <= 0)
break;
if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA))
{
// hazards blocking path
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnodestatus(org, DEBUG_NODE_FAIL);
- #endif
+
//print("tracewalk: ", vtos(start), " hits a hazard when trying to reach ", vtos(end), "\n");
return FALSE;
}
move = normalize(end - org);
tracebox(org, m1, m2, org + move * stepdist, movemode, e);
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnode(trace_endpos);
- #endif
if (trace_fraction < 1)
{
org = trace_endpos - normalize(org - trace_endpos) * stepdist;
for(; org_z < end_z + self.maxs_z; org_z += stepdist)
{
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnode(org);
- #endif
+
if(pointcontents(org) == CONTENT_EMPTY)
break;
}
if not (pointcontents(org + '0 0 1') == CONTENT_EMPTY)
{
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnodestatus(org, DEBUG_NODE_FAIL);
- #endif
+
return FALSE;
//print("tracewalk: ", vtos(start), " failed under water\n");
}
move = dir * stepdist + org;
tracebox(org, m1, m2, move, movemode, e);
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnode(trace_endpos);
- #endif
// hit something
if (trace_fraction < 1)
tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e);
if (trace_fraction < 1 || trace_startsolid)
{
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnodestatus(trace_endpos, DEBUG_NODE_WARNING);
- #endif
// check for doors
traceline( org, move, movemode, e);
}
else
{
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnodestatus(trace_endpos, DEBUG_NODE_FAIL);
- #endif
+
//print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n");
//te_explosion(trace_endpos);
//print(ftos(e.dphitcontentsmask), "\n");
//print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n");
// moved but didn't arrive at the intended destination
- #ifdef DEBUG_TRACEWALK
+ if(autocvar_bot_debug_tracewalk)
debugnodestatus(org, DEBUG_NODE_FAIL);
- #endif
return FALSE;
};
void navigation_routerating(entity e, float f, float rangebias)
{
entity nwp;
+ vector o;
if (!e)
return;
+ o = (e.absmin + e.absmax) * 0.5;
//print("routerating ", etos(e), " = ", ftos(f), " - ", ftos(rangebias), "\n");
// Evaluate path using jetpack
if(g_jetpack)
if(self.items & IT_JETPACK)
if(autocvar_bot_ai_navigation_jetpack)
- if(vlen(self.origin - e.origin) > autocvar_bot_ai_navigation_jetpack_mindistance)
+ if(vlen(self.origin - o) > autocvar_bot_ai_navigation_jetpack_mindistance)
{
vector pointa, pointb;
pointa = trace_endpos - '0 0 1';
// Point B
- traceline(e.origin, e.origin + '0 0 65535', MOVE_NORMAL, e);
+ traceline(o, o + '0 0 65535', MOVE_NORMAL, e);
pointb = trace_endpos - '0 0 1';
// Can I see these two points from the sky?
{
//te_wizspike(nwp.wpnearestpoint);
// dprint(e.classname, " ", ftos(f), "/(1+", ftos((nwp.wpcost + vlen(e.origin - nwp.wpnearestpoint))), "/", ftos(rangebias), ") = ");
- f = f * rangebias / (rangebias + (nwp.wpcost + vlen(e.origin - nwp.wpnearestpoint)));
+ f = f * rangebias / (rangebias + (nwp.wpcost + vlen(o - nwp.wpnearestpoint)));
//dprint("considering ", e.classname, " (with rating ", ftos(f), ")\n");
//dprint(ftos(f));
if (navigation_bestrating < f)
return TRUE;
// if it can reach the goal there is nothing more to do
- if (tracewalk(self, startposition, PL_MIN, PL_MAX, e.origin, bot_navigation_movemode))
+ if (tracewalk(self, startposition, PL_MIN, PL_MAX, (e.absmin + e.absmax) * 0.5, bot_navigation_movemode))
return TRUE;
// see if there are waypoints describing a path to the item
// begin a goal selection session (queries spawnfunc_waypoint network)
void navigation_goalrating_start()
{
+ if(self.aistatus & AI_STATUS_STUCK)
+ return;
+
self.navigation_jetpack_goal = world;
navigation_bestrating = -1;
self.navigation_hasgoals = FALSE;
+ navigation_clearroute();
navigation_bestgoal = world;
navigation_markroutes(world);
};
// ends a goal selection session (updates goal stack to the best goal)
void navigation_goalrating_end()
{
+ if(self.aistatus & AI_STATUS_STUCK)
+ return;
+
navigation_routetogoal(navigation_bestgoal, self.origin);
// dprint("best goal ", self.goalcurrent.classname , "\n");
- // Hack: if it can't walk to any goal just move blindly to the first visible waypoint
+ // If the bot got stuck then try to reach the farthest waypoint
if not (self.navigation_hasgoals)
+ if (autocvar_bot_wander_enable)
{
- dprint(self.netname, " can't walk to any goal, going to a near waypoint\n");
-
- entity head;
-
- RandomSelection_Init();
- head = findradius(self.origin,1000);
- while(head)
+ if not(self.aistatus & AI_STATUS_STUCK)
{
- if(head.classname=="waypoint")
- if(!(head.wpflags & WAYPOINTFLAG_GENERATED))
- if(vlen(self.origin-head.origin)>100)
- if(checkpvs(self.view_ofs,head))
- RandomSelection_Add(head, 0, string_null, 1 + (vlen(self.origin-head.origin)<500), 0);
- head = head.chain;
+ dprint(self.netname, " cannot walk to any goal\n");
+ self.aistatus |= AI_STATUS_STUCK;
}
- if(RandomSelection_chosen_ent)
- navigation_routetogoal(RandomSelection_chosen_ent, self.origin);
self.navigation_hasgoals = FALSE; // Reset this value
}
void botframe_updatedangerousobjects(float maxupdate)
{
local entity head, bot_dodgelist;
- local vector m1, m2, v;
+ local vector m1, m2, v, o;
local float c, d, danger;
c = 0;
bot_dodgelist = findchainfloat(bot_dodge, TRUE);
v_x = bound(m1_x, v_x, m2_x);
v_y = bound(m1_y, v_y, m2_y);
v_z = bound(m1_z, v_z, m2_z);
- d = head.bot_dodgerating - vlen(head.origin - v);
+ o = (head.absmin + head.absmax) * 0.5;
+ d = head.bot_dodgerating - vlen(o - v);
if (d > 0)
{
- traceline(head.origin, v, TRUE, world);
+ traceline(o, v, TRUE, world);
if (trace_fraction == 1)
danger = danger + d;
}
}
};
-#ifdef DEBUG_TRACEWALK
+void navigation_unstuck()
+{
+ float search_radius = 1000;
+
+ if not(autocvar_bot_wander_enable)
+ return;
+
+ if not(bot_waypoint_queue_owner)
+ {
+ // dprint(self.netname, " sutck, taking over the waypoints queue\n");
+ bot_waypoint_queue_owner = self;
+ bot_waypoint_queue_bestgoal = world;
+ bot_waypoint_queue_bestgoalrating = 0;
+ }
+
+ if(bot_waypoint_queue_owner!=self)
+ return;
+
+ if (bot_waypoint_queue_goal)
+ {
+ // evaluate the next goal on the queue
+ float d = vlen(self.origin - bot_waypoint_queue_goal.origin);
+ // dprint(self.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d), "\n");
+ if(tracewalk(bot_waypoint_queue_goal, self.origin, PL_MIN, PL_MAX, bot_waypoint_queue_goal.origin, bot_navigation_movemode))
+ {
+ if( d > bot_waypoint_queue_bestgoalrating)
+ {
+ bot_waypoint_queue_bestgoalrating = d;
+ bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal;
+ }
+ }
+ bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal;
+
+ if not(bot_waypoint_queue_goal)
+ {
+ if (bot_waypoint_queue_bestgoal)
+ {
+ dprint(self.netname, " stuck, reachable waypoint found, heading to it\n");
+ navigation_routetogoal(bot_waypoint_queue_bestgoal, self.origin);
+ self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ self.aistatus &~= AI_STATUS_STUCK;
+ }
+ else
+ {
+ dprint(self.netname, " stuck, cannot walk to any waypoint at all\n");
+ }
+
+ bot_waypoint_queue_owner = world;
+ }
+ }
+ else
+ {
+ if(bot_strategytoken!=self)
+ return;
+
+ // build a new queue
+ dprint(self.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu\n");
+
+ entity head, first;
+
+ first = world;
+ head = findradius(self.origin, search_radius);
+
+ while(head)
+ {
+ if(head.classname=="waypoint")
+ // if(!(head.wpflags & WAYPOINTFLAG_GENERATED))
+ {
+ if(bot_waypoint_queue_goal)
+ bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = head;
+ else
+ first = head;
+
+ bot_waypoint_queue_goal = head;
+ bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = world;
+ }
+
+ head = head.chain;
+ }
+
+ if (first)
+ bot_waypoint_queue_goal = first;
+ else
+ {
+ dprint(self.netname, " stuck, cannot walk to any waypoint at all\n");
+ bot_waypoint_queue_owner = world;
+ }
+ }
+}
+
+// Support for debugging tracewalk visually
void debugresetnodes()
{
void debugnodestatus(vector position, float status)
{
- vector color;
+ vector c;
switch (status)
{
case DEBUG_NODE_SUCCESS:
- color = '0 15 0';
+ c = '0 15 0';
break;
case DEBUG_NODE_WARNING:
- color = '15 15 0';
+ c = '15 15 0';
break;
case DEBUG_NODE_FAIL:
- color = '15 0 0';
+ c = '15 0 0';
break;
default:
- color = '15 15 15';
+ c = '15 15 15';
}
- te_customflash(position, 40, 2, color);
+ te_customflash(position, 40, 2, c);
}
-#endif
-
-#ifdef DEBUG_BOT_GOALSTACK
+// Support for debugging the goal stack visually
.float goalcounter;
.vector lastposition;
// Debug the goal stack visually
void debuggoalstack()
{
- local entity target;
- local vector org;
-
- if(self.goalcounter==0)target=self.goalcurrent;
- else if(self.goalcounter==1)target=self.goalstack01;
- else if(self.goalcounter==2)target=self.goalstack02;
- else if(self.goalcounter==3)target=self.goalstack03;
- else if(self.goalcounter==4)target=self.goalstack04;
- else if(self.goalcounter==5)target=self.goalstack05;
- else if(self.goalcounter==6)target=self.goalstack06;
- else if(self.goalcounter==7)target=self.goalstack07;
- else if(self.goalcounter==8)target=self.goalstack08;
- else if(self.goalcounter==9)target=self.goalstack09;
- else if(self.goalcounter==10)target=self.goalstack10;
- else if(self.goalcounter==11)target=self.goalstack11;
- else if(self.goalcounter==12)target=self.goalstack12;
- else if(self.goalcounter==13)target=self.goalstack13;
- else if(self.goalcounter==14)target=self.goalstack14;
- else if(self.goalcounter==15)target=self.goalstack15;
- else if(self.goalcounter==16)target=self.goalstack16;
- else if(self.goalcounter==17)target=self.goalstack17;
- else if(self.goalcounter==18)target=self.goalstack18;
- else if(self.goalcounter==19)target=self.goalstack19;
- else if(self.goalcounter==20)target=self.goalstack20;
- else if(self.goalcounter==21)target=self.goalstack21;
- else if(self.goalcounter==22)target=self.goalstack22;
- else if(self.goalcounter==23)target=self.goalstack23;
- else if(self.goalcounter==24)target=self.goalstack24;
- else if(self.goalcounter==25)target=self.goalstack25;
- else if(self.goalcounter==26)target=self.goalstack26;
- else if(self.goalcounter==27)target=self.goalstack27;
- else if(self.goalcounter==28)target=self.goalstack28;
- else if(self.goalcounter==29)target=self.goalstack29;
- else if(self.goalcounter==30)target=self.goalstack30;
- else if(self.goalcounter==31)target=self.goalstack31;
-
- if(target==world)
+ local entity goal;
+ local vector org, go;
+
+ if(self.goalcounter==0)goal=self.goalcurrent;
+ else if(self.goalcounter==1)goal=self.goalstack01;
+ else if(self.goalcounter==2)goal=self.goalstack02;
+ else if(self.goalcounter==3)goal=self.goalstack03;
+ else if(self.goalcounter==4)goal=self.goalstack04;
+ else if(self.goalcounter==5)goal=self.goalstack05;
+ else if(self.goalcounter==6)goal=self.goalstack06;
+ else if(self.goalcounter==7)goal=self.goalstack07;
+ else if(self.goalcounter==8)goal=self.goalstack08;
+ else if(self.goalcounter==9)goal=self.goalstack09;
+ else if(self.goalcounter==10)goal=self.goalstack10;
+ else if(self.goalcounter==11)goal=self.goalstack11;
+ else if(self.goalcounter==12)goal=self.goalstack12;
+ else if(self.goalcounter==13)goal=self.goalstack13;
+ else if(self.goalcounter==14)goal=self.goalstack14;
+ else if(self.goalcounter==15)goal=self.goalstack15;
+ else if(self.goalcounter==16)goal=self.goalstack16;
+ else if(self.goalcounter==17)goal=self.goalstack17;
+ else if(self.goalcounter==18)goal=self.goalstack18;
+ else if(self.goalcounter==19)goal=self.goalstack19;
+ else if(self.goalcounter==20)goal=self.goalstack20;
+ else if(self.goalcounter==21)goal=self.goalstack21;
+ else if(self.goalcounter==22)goal=self.goalstack22;
+ else if(self.goalcounter==23)goal=self.goalstack23;
+ else if(self.goalcounter==24)goal=self.goalstack24;
+ else if(self.goalcounter==25)goal=self.goalstack25;
+ else if(self.goalcounter==26)goal=self.goalstack26;
+ else if(self.goalcounter==27)goal=self.goalstack27;
+ else if(self.goalcounter==28)goal=self.goalstack28;
+ else if(self.goalcounter==29)goal=self.goalstack29;
+ else if(self.goalcounter==30)goal=self.goalstack30;
+ else if(self.goalcounter==31)goal=self.goalstack31;
+
+ if(goal==world)
{
self.goalcounter = 0;
self.lastposition='0 0 0';
org = self.lastposition;
- te_lightning2(world, org, target.origin);
- self.lastposition = target.origin;
+ go = ( goal.absmin + goal.absmax ) * 0.5;
+ te_lightning2(world, org, go);
+ self.lastposition = go;
self.goalcounter++;
}
-
-#endif