From: mand1nga <mand1nga@xonotic.org>
Date: Wed, 20 Apr 2011 07:34:23 +0000 (-0300)
Subject: Added a better handling of stuck bots. Walkability is checked for every potential... 
X-Git-Tag: xonotic-v0.5.0~264^2~28^2~1^2~9
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=9c00831f15821273adee4870b692443feddfb09f;p=xonotic%2Fxonotic-data.pk3dir.git

Added a better handling of stuck bots. Walkability is checked for every potential goal, and this check is spread among many frames. Also bots get unstuck one at a time, preventing server lag.
---

diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc
index 581e3f0c7..5c70af58c 100644
--- a/qcsrc/server/bot/bot.qc
+++ b/qcsrc/server/bot/bot.qc
@@ -99,6 +99,10 @@ void bot_think()
 		}
 	}
 
+	//
+	if(self.aistatus & AI_STATUS_STUCK)
+		navigation_unstuck();
+
 	// now call the current bot AI (havocbot for example)
 	self.bot_ai();
 };
@@ -374,6 +378,8 @@ void bot_clientdisconnect()
 	self.playermodel_freeme = string_null;
 	self.playerskin_freeme = string_null;
 	remove(self.bot_cmd_current);
+	if(navigation_wander_owner==self)
+		navigation_wander_owner = world;
 }
 
 void bot_clientconnect()
diff --git a/qcsrc/server/bot/bot.qh b/qcsrc/server/bot/bot.qh
index 9e6d447e6..b4556bb76 100644
--- a/qcsrc/server/bot/bot.qh
+++ b/qcsrc/server/bot/bot.qh
@@ -13,6 +13,7 @@ float AI_STATUS_WAYPOINT_PERSONAL_GOING		= 128;	// Going to a personal waypoint
 float AI_STATUS_WAYPOINT_PERSONAL_REACHED	= 256;	// Personal waypoint reached
 float AI_STATUS_JETPACK_FLYING			= 512;
 float AI_STATUS_JETPACK_LANDING			= 1024;
+float AI_STATUS_STUCK			= 2048; // bot cannot reach any useful goal
 
 .float isbot; // true if this client is actually a bot
 .float aistatus;
diff --git a/qcsrc/server/bot/navigation.qc b/qcsrc/server/bot/navigation.qc
index e460bdc57..e39c5408d 100644
--- a/qcsrc/server/bot/navigation.qc
+++ b/qcsrc/server/bot/navigation.qc
@@ -890,6 +890,9 @@ void navigation_poptouchedgoals()
 // 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;
@@ -901,31 +904,21 @@ void navigation_goalrating_start()
 // 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)
 	{
-		if not(autocvar_g_campaign)
-			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
 	}
@@ -968,6 +961,90 @@ void botframe_updatedangerousobjects(float maxupdate)
 	}
 };
 
+void navigation_unstuck()
+{
+	if not(navigation_wander_owner)
+	{
+		dprint(self.netname, " taking over the waypoints queue\n");
+		navigation_wander_owner = self;
+		navigation_wander_bestgoal = world;
+		navigation_wander_bestgoalrating = 0;
+	}
+
+	if(navigation_wander_owner!=self)
+		return;
+
+	if (navigation_wander_goal)
+	{
+		// evaluate the next goal on the queue
+		float d = vlen(self.origin - navigation_wander_goal.origin);
+		dprint(self.netname, " evaluating ", navigation_wander_goal.classname, " with distance ", ftos(d), "\n");
+		if(tracewalk(navigation_wander_goal, self.origin, PL_MIN, PL_MAX, navigation_wander_goal.origin, bot_navigation_movemode))
+		{
+			if( d > navigation_wander_bestgoalrating)
+			{
+				navigation_wander_bestgoalrating = d;
+				navigation_wander_bestgoal = navigation_wander_goal;
+			}
+		}
+		navigation_wander_goal = navigation_wander_goal.navigation_wander_nextgoal;
+
+		if not(navigation_wander_goal)
+		{
+			if (navigation_wander_bestgoal)
+			{
+				navigation_routetogoal(navigation_wander_bestgoal, self.origin);
+				self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+				self.aistatus &~= AI_STATUS_STUCK;
+			}
+			else
+			{
+				dprint(self.netname, " got BADLY stuck, giving up.\n");
+			}
+
+			navigation_wander_owner = world;
+		}
+	}
+	else
+	{
+		// build a new queue
+		dprint(self.netname, " building a new wayoints queue\n");
+
+		entity first, head, prev;
+		head = findradius(self.origin, 1000);
+
+		while(head)
+		{
+			if(head.classname=="waypoint")
+			if(!(head.wpflags & WAYPOINTFLAG_GENERATED))
+			if(vlen(self.origin-head.origin)>100)
+			{
+				if (first==world)
+				{
+					first = head;
+					navigation_wander_goal = head;
+				}
+				else
+				{
+					prev = navigation_wander_goal;
+					navigation_wander_goal = head;
+					prev.navigation_wander_nextgoal = head;
+					navigation_wander_goal.navigation_wander_nextgoal = world;
+				}
+			}
+
+			head = head.chain;
+		}
+
+		if (first)
+			navigation_wander_goal = first;
+		else
+		{
+			dprint(self.netname, " got BADLY stuck, giving up.\n");
+		}
+	}
+}
+
 // Support for debugging tracewalk visually
 
 void debugresetnodes()
diff --git a/qcsrc/server/bot/navigation.qh b/qcsrc/server/bot/navigation.qh
index 06bbb85c3..6355ef700 100644
--- a/qcsrc/server/bot/navigation.qh
+++ b/qcsrc/server/bot/navigation.qh
@@ -35,6 +35,12 @@ float DEBUG_NODE_WARNING        = 2;
 float DEBUG_NODE_FAIL           = 3;
 vector debuglastnode;
 
+entity navigation_wander_owner; 	// Owner of the temporary list of goals
+entity navigation_wander_goal;		// Head of the temporary list of goals
+.entity navigation_wander_nextgoal;
+entity navigation_wander_bestgoal;
+float navigation_wander_bestgoalrating;
+
 /*
  * Functions
  */
@@ -60,6 +66,7 @@ void navigation_routerating(entity e, float f, float rangebias);
 void navigation_poptouchedgoals();
 void navigation_goalrating_start();
 void navigation_goalrating_end();
+void navigation_unstuck();
 
 void botframe_updatedangerousobjects(float maxupdate);