View Issue Details

IDProjectCategoryView StatusLast Update
0001131FSSCPgameplaypublic2006-11-06 03:34
ReporterGoober5000 Assigned ToGoober5000  
PrioritynormalSeveritymajorReproducibilityalways
Status resolvedResolutionfixed 
Fixed in Version3.6.9 
Summary0001131: "No-dynamic" flag behaves differently from retail
DescriptionI discovered this in a ST:R mission last night. Apparently, FSO builds take "no dynamic goals" a little too literally; goals don't switch at all.

I have two wings, Iota and Kappa, with the following goals and flags:

$AI Goals: ( goals ( ai-chase-wing "Rama" 89 ) ( ai-chase-wing "Durga" 80 ) ( ai-chase-wing "Bheema" 70 ) ( ai-chase-wing "Asura" 60 ) ( ai-guard "GTD Repulse" 50 ) )
+Flags:( "no-dynamic" )

The Repulse arrives first, so Iota and Kappa start guarding it as they're supposed to. When Rama &co. arrive, in retail, Iota and Kappa break off and attack. However, in FSO, they fail to do so -- they keep flying stupidly around the Repulse.

I'm guessing something was changed in the goal prioritization between retail and FSO. I can provide the mission upon request, as it's a bit secret. ;)
TagsNo tags attached.

Activities

taylor

2006-10-31 00:16

administrator   ~0007048

Send it my way, I'll take a look in the next day or two.

Goober5000

2006-11-02 01:46

administrator   ~0007058

Okay, I think the bug was introduced between 2/20 and 2/21.

taylor

2006-11-02 02:22

administrator   ~0007059

In that time frame there were starfield and OpenGL updates by me, some SEXPs by karajorma, a very minor thing or two from you (that looked fine to me), and also "fixed several more things in the new ignore code" from you. I assume that, if it really did break during that time, it was the ignore code changes which did it.

Goober5000

2006-11-02 06:16

administrator   ~0007060

Me too, though I can't imagine why. I'll see if I can look at it in the next day or two.

2006-11-02 12:52

 

09.diff (11,648 bytes)   
? blah.txt
Index: code/ai/aicode.cpp
===================================================================
RCS file: /home/fs2source/cvsroot/fs2_open/code/ai/aicode.cpp,v
retrieving revision 1.61
retrieving revision 1.62
diff -u -r1.61 -r1.62
--- code/ai/aicode.cpp	20 Feb 2006 02:13:07 -0000	1.61
+++ code/ai/aicode.cpp	20 Feb 2006 07:59:26 -0000	1.62
@@ -9,13 +9,17 @@
 
 /*
  * $Logfile: /Freespace2/code/Ship/AiCode.cpp $
- * $Revision: 1.61 $
- * $Date: 2006/02/20 02:13:07 $
+ * $Revision: 1.62 $
+ * $Date: 2006/02/20 07:59:26 $
  * $Author: Goober5000 $
  * 
  * AI code that does interesting stuff
  *
  * $Log: aicode.cpp,v $
+ * Revision 1.62  2006/02/20 07:59:26  Goober5000
+ * fixed several more things in the new ignore code
+ * --Goober5000
+ *
  * Revision 1.61  2006/02/20 02:13:07  Goober5000
  * added ai-ignore-new which hopefully should fix the ignore bug
  * --Goober5000
@@ -2692,14 +2696,14 @@
 
 */
 
+// Goober5000 - fixed up a bit
 //	Determine if object objnum is supposed to be ignored by object with ai_info *aip.
 //	Return:
 //		TRUE	if objnum is aip->ignore_objnum (and signatures match)
 //				or objnum is in ignore wing
 //		FALSE	otherwise
-int is_ignore_object(ai_info *aip, int objnum)
+int is_ignore_object_sub(int *ignore_objnum, int *ignore_signature, int objnum)
 {
-
 /*	// First, scan all objects in global array of objects to be ignored.
 	for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
 		if (Ignore_objects[i].objnum != -1)
@@ -2711,31 +2715,27 @@
 	// Didn't find in global list.  Now check 
 
 	// Not ignoring anything.
-	if (aip->ignore_objnum == UNUSED_OBJNUM)
+	if (*ignore_objnum == UNUSED_OBJNUM)
 	{
 		return 0;									
 	}
 	// This means it's ignoring an object, not a wing.
-	else if (aip->ignore_objnum >= 0)
+	else if (*ignore_objnum >= 0)
 	{
-		if (aip->ignore_objnum == objnum)
+		// see if this object became invalid
+		if (Objects[*ignore_objnum].signature != *ignore_signature)
 		{
-			// numbers and signatures match
-			if (Objects[aip->ignore_objnum].signature == aip->ignore_signature)
-			{
-				return 1;
-			}
-			// object is no longer valid; reset
-			else
-			{
-				aip->ignore_objnum = UNUSED_OBJNUM;
-				return 0;
-			}
+			// reset
+			*ignore_objnum = UNUSED_OBJNUM;
 		}
-		else
+		// objects and signatures match
+		else if (*ignore_objnum == objnum)
 		{
-			return 0;
+			// found it
+			return 1;
 		}
+
+		return 0;
 	}
 	// Ignoring a wing.
 	else
@@ -2744,10 +2744,9 @@
 		return 0;
 
 		/*
-		int	ignore_wingnum = -(aip->ignore_objnum + 1);
+		int	ignore_wingnum = -(*ignore_objnum + 1);
 
 		Assert(ignore_wingnum < MAX_WINGS);
-		Assert(aip->shipnum >= 0);
 		return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
 		*/
 	}
@@ -2760,57 +2759,28 @@
 
 	for (i = 0; i < MAX_IGNORE_NEW_OBJECTS; i++)
 	{
-		// Not ignoring anything.
-		if (aip->ignore_new_objnums[i] == UNUSED_OBJNUM)
-		{
-			continue;
-		}
-		// This means it's ignoring an object, not a wing.
-		else if (aip->ignore_new_objnums[i] >= 0)
-		{
-			if (aip->ignore_new_objnums[i] == objnum)
-			{
-				// numbers and signatures match
-				if (Objects[aip->ignore_new_objnums[i]].signature == aip->ignore_new_signatures[i])
-				{
-					return i;
-				}
-				// object is no longer valid; reset
-				else
-				{
-					aip->ignore_new_objnums[i] = UNUSED_OBJNUM;
-					continue;
-				}
-			}
-			else
-			{
-				continue;
-			}
-		}
-		// Ignoring a wing.
-		else
-		{
-			Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
-			continue;
-
-			/*
-			int	ignore_wingnum = -(aip->ignore_objnums[i] + 1);
-	
-			Assert(ignore_wingnum < MAX_WINGS);
-			Assert(aip->shipnum >= 0);
-			return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
-			*/
-		}
+		if (is_ignore_object_sub(&aip->ignore_new_objnums[i], &aip->ignore_new_signatures[i], objnum))
+			return i;
 	}
 
 	return -1;
 }
 
 // Goober5000
-// Like above, but using ignore-new.
-int is_ignore_new_object(ai_info *aip, int objnum)
+int is_ignore_object(ai_info *aip, int objnum, int just_the_original = 0)
 {
-	return (find_ignore_new_object_index(aip, objnum) >= 0);
+	// check original (retail) ignore
+	if (is_ignore_object_sub(&aip->ignore_objnum, &aip->ignore_signature, objnum))
+		return 1;
+
+	// check new ignore
+	if (!just_the_original)
+	{
+		if (find_ignore_new_object_index(aip, objnum) >= 0)
+			return 1;
+	}
+
+	return 0;
 }
 
 // -----------------------------------------------------------------------------
@@ -2874,7 +2844,7 @@
 			if (shipp->flags & SF_DYING)
 				return;
 
-			if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
+			if (is_ignore_object(aip, OBJ_INDEX(eno->trial_objp)))
 				return;
 
 			if (eno->trial_objp->flags & OF_PROTECTED)
@@ -3231,13 +3201,15 @@
 			aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);	//	No dynamic targeting for 7 seconds.
 	}
 
-	if (is_ignore_object(aip, aip->target_objnum))
-		aip->ignore_objnum = UNUSED_OBJNUM;
-
 	// Goober5000
 	if ((temp = find_ignore_new_object_index(aip, aip->target_objnum)) >= 0)
+	{
 		aip->ignore_new_objnums[temp] = UNUSED_OBJNUM;
-
+	}
+	else if (is_ignore_object(aip, aip->target_objnum, 1))
+	{
+		aip->ignore_objnum = UNUSED_OBJNUM;
+	}
 
 	aip->mode = AIM_CHASE;
 	aip->submode = SM_ATTACK;				// AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
@@ -8102,7 +8074,13 @@
 
 	// Goober5000
 	if ((temp = find_ignore_new_object_index(aip, aip->target_objnum)) >= 0)
+	{
 		aip->ignore_new_objnums[temp] = UNUSED_OBJNUM;
+	}
+	else if (is_ignore_object(aip, aip->target_objnum, 1))
+	{
+		aip->ignore_objnum = UNUSED_OBJNUM;
+	}
 
 	// -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
 
@@ -10088,7 +10066,7 @@
 
 	if ( hitter_objp->type == OBJ_SHIP ) {
 		//	If the hitter object is the ignore object, don't attack it.
-		if (is_ignore_object(aip, hitter_objp-Objects))
+		if (is_ignore_object(aip, OBJ_INDEX(hitter_objp)))
 			return;
 
 		//	If hitter is on same team as me, don't attack him.
@@ -12663,23 +12641,6 @@
 		aip->target_objnum = -1;
 }
 
-// Goober5000
-void ai_preprocess_ignore_new_objnums(object *objp, ai_info *aip)
-{
-	if (is_ignore_new_object(aip, aip->goal_objnum))
-	{
-		aip->goal_objnum = -1;
-
-		// AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
-		//              set to -1
-		if (aip->mode == AIM_STRAFE)
-			aip->target_objnum = -1;
-	}
-
-	if (is_ignore_new_object(aip, aip->target_objnum))
-		aip->target_objnum = -1;
-}
-
 /*
 void ai_safety_circle_spot()
 {
Index: code/ai/aigoals.cpp
===================================================================
RCS file: /home/fs2source/cvsroot/fs2_open/code/ai/aigoals.cpp,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- code/ai/aigoals.cpp	20 Feb 2006 02:13:07 -0000	1.20
+++ code/ai/aigoals.cpp	20 Feb 2006 07:59:26 -0000	1.21
@@ -9,13 +9,17 @@
 
 /*
  * $Logfile: /Freespace2/code/Ship/AiGoals.cpp $
- * $Revision: 1.20 $
- * $Date: 2006/02/20 02:13:07 $
+ * $Revision: 1.21 $
+ * $Date: 2006/02/20 07:59:26 $
  * $Author: Goober5000 $
  *
  * File to deal with manipulating AI goals, etc.
  *
  * $Log: aigoals.cpp,v $
+ * Revision 1.21  2006/02/20 07:59:26  Goober5000
+ * fixed several more things in the new ignore code
+ * --Goober5000
+ *
  * Revision 1.20  2006/02/20 02:13:07  Goober5000
  * added ai-ignore-new which hopefully should fix the ignore bug
  * --Goober5000
@@ -676,7 +680,11 @@
 #define PLAYER_PRIORITY_SUPPORT_LOW		10
 
 // define for which goals cause other goals to get purged
-#define PURGE_GOALS		(AI_GOAL_IGNORE | AI_GOAL_IGNORE_NEW | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP)
+// Goober5000 - okay, this seems really stupid.  If any ship in the mission is assigned a goal
+// in PURGE_GOALS_ALL_SHIPS, *every* other ship will have certain goals purged.  So I added
+// PURGE_GOALS_ONE_SHIP for goals which should only purge other goals in the one ship.
+#define PURGE_GOALS_ALL_SHIPS		(AI_GOAL_IGNORE | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP)
+#define PURGE_GOALS_ONE_SHIP		(AI_GOAL_IGNORE_NEW)
 
 // goals given from the player to other ships in the game are also handled in this
 // code
@@ -1095,46 +1103,46 @@
 		} else if ( purge_wing != wingnum )
 			continue;
 
-		switch( mode ) {
-		// ignore goals should get rid of any kind of attack goal
-		case AI_GOAL_IGNORE:
-		case AI_GOAL_IGNORE_NEW:
-			if ( purge_ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_DESTROY_SUBSYSTEM) )
-				purge_goal->flags |= AIGF_PURGE;
-			break;
+		switch (mode)
+		{
+			// ignore goals should get rid of any kind of attack goal
+			case AI_GOAL_IGNORE:
+			case AI_GOAL_IGNORE_NEW:
+				if ( purge_ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP | AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_DESTROY_SUBSYSTEM) )
+					purge_goal->flags |= AIGF_PURGE;
+				break;
 
-		// disarm/disable goals should remove any general attack
-		case AI_GOAL_DISABLE_SHIP:
-		case AI_GOAL_DISARM_SHIP:
-			if ( purge_ai_mode & (AI_GOAL_CHASE | AI_GOAL_CHASE_WING) )
-				purge_goal->flags |= AIGF_PURGE;
-			break;
+			// disarm/disable goals should remove any general attack
+			case AI_GOAL_DISABLE_SHIP:
+			case AI_GOAL_DISARM_SHIP:
+				if ( purge_ai_mode & (AI_GOAL_CHASE | AI_GOAL_CHASE_WING) )
+					purge_goal->flags |= AIGF_PURGE;
+				break;
 		}
 	}
 }
 
-// function to purge the goals of all ships in the game based on the incoming goal structure
-void ai_goal_purge_all_invalid_goals( ai_goal *aigp )
+// function to purge the goals of *all* ships in the game based on the incoming goal structure
+void ai_goal_purge_all_invalid_goals(ai_goal *aigp)
 {
-	int mode, i;
+	int i;
 	ship_obj *sop;
 
-	mode = aigp->ai_mode;
-
 	// only purge goals if a new goal is one of the types in next statement
-	if ( !(mode & PURGE_GOALS) )
+	if (!(aigp->ai_mode & PURGE_GOALS_ALL_SHIPS))
 		return;
 
-	for ( sop = GET_FIRST(&Ship_obj_list); sop != END_OF_LIST(&Ship_obj_list); sop = GET_NEXT(sop) ) {
-		ship *shipp;
-
-		shipp = &Ships[Objects[sop->objnum].instance];
-		ai_goal_purge_invalid_goals( aigp, Ai_info[shipp->ai_index].goals );
+	for (sop = GET_FIRST(&Ship_obj_list); sop != END_OF_LIST(&Ship_obj_list); sop = GET_NEXT(sop))
+	{
+		ship *shipp = &Ships[Objects[sop->objnum].instance];
+		ai_goal_purge_invalid_goals(aigp, Ai_info[shipp->ai_index].goals);
 	}
 
 	// we must do the same for the wing goals
-	for (i = 0; i < Num_wings; i++ )
-		ai_goal_purge_invalid_goals( aigp, Wings[i].ai_goals );
+	for (i = 0; i < Num_wings; i++)
+	{
+		ai_goal_purge_invalid_goals(aigp, Wings[i].ai_goals);
+	}
 }
 
 // Goober5000
@@ -1959,11 +1967,20 @@
 	}
 
 	// if the goal is an ignore/disable/disarm goal, then 
-	if ( (status == SHIP_STATUS_ARRIVED) && (aigp->ai_mode & PURGE_GOALS) && !(aigp->flags & AIGF_GOALS_PURGED) ) {
-		ai_goal_purge_all_invalid_goals( aigp );
-		aigp->flags |= AIGF_GOALS_PURGED;
-	}
-		
+	// Goober5000 - see note at PURGE_GOALS_ALL_SHIPS... this is bizarre
+	if ((status == SHIP_STATUS_ARRIVED) && !(aigp->flags & AIGF_GOALS_PURGED))
+	{
+		if (aigp->ai_mode & PURGE_GOALS_ALL_SHIPS)
+		{
+			ai_goal_purge_all_invalid_goals(aigp);
+			aigp->flags |= AIGF_GOALS_PURGED;
+		}
+		else if (aigp->ai_mode & PURGE_GOALS_ONE_SHIP)
+		{
+			ai_goal_purge_invalid_goals(aigp, aip->goals);
+			aigp->flags |= AIGF_GOALS_PURGED;
+		}
+	}	
 
 	// if we are docking, validate the docking indices on both ships.  We might have to change names to indices.
 	// only enter this calculation if the ship we are docking with has arrived.  If the ship is gone, then
09.diff (11,648 bytes)   

taylor

2006-11-02 12:53

administrator   ~0007061

Here is a diff of the changes in question (minus the OpenGL/SEXP/starfield stuff), may make identifying the problem a bit easier.

Goober5000

2006-11-05 21:44

administrator   ~0007076

Interesting. It's not the ignore code stuff. I'm still checking...

Goober5000

2006-11-05 22:05

administrator   ~0007077

Ugh. Guess those builds didn't reflect their dates after all. :(

Goober5000

2006-11-06 02:24

administrator   ~0007079

BAHAHAHAHA!

Found it. It's taylor's fault, somehow. ;) The newly attached diff caused it. Now I just need to figure out what happened.

2006-11-06 02:24

 

bug.diff (19,018 bytes)   
===================================================================
RCS file: /home/fs2source/cvsroot/fs2_open/code/ai/aigoals.cpp,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -p -r1.21 -r1.22
--- fs2_open/code/ai/aigoals.cpp	2006/02/20 07:59:26	1.21
+++ fs2_open/code/ai/aigoals.cpp	2006/03/25 10:38:44	1.22
@@ -9,13 +9,18 @@
 
 /*
  * $Logfile: /Freespace2/code/Ship/AiGoals.cpp $
- * $Revision: 1.21 $
- * $Date: 2006/02/20 07:59:26 $
- * $Author: Goober5000 $
+ * $Revision: 1.22 $
+ * $Date: 2006/03/25 10:38:44 $
+ * $Author: taylor $
  *
  * File to deal with manipulating AI goals, etc.
  *
  * $Log: aigoals.cpp,v $
+ * Revision 1.22  2006/03/25 10:38:44  taylor
+ * minor cleanup
+ * address numerous out-of-bounds issues
+ * add some better error checking and Assert()'s around
+ *
  * Revision 1.21  2006/02/20 07:59:26  Goober5000
  * fixed several more things in the new ignore code
  * --Goober5000
@@ -1747,6 +1752,13 @@ int ai_mission_goal_achievable( int objn
 	int return_val;
 	object *objp;
 	ai_info *aip;
+	int index = -1, sindex = -1;
+	int modelnum = -1;
+
+	objp = &Objects[objnum];
+	Assert( objp->instance != -1 );
+	ai_shipname = Ships[objp->instance].ship_name;
+	aip = &Ai_info[Ships[objp->instance].ai_index];
 
 	//  these orders are always achievable.
 	if ( (aigp->ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE)
@@ -1757,7 +1769,7 @@ int ai_mission_goal_achievable( int objn
 	// warp (depart) only achievable if there's somewhere to depart to
 	if (aigp->ai_mode == AI_GOAL_WARP)
 	{
-		ship *shipp = &Ships[Objects[objnum].instance];
+		ship *shipp = &Ships[objp->instance];
 
 		// always valid if has subspace drive
 		if (!(shipp->flags2 & SF2_NO_SUBSPACE_DRIVE))
@@ -1804,116 +1816,122 @@ int ai_mission_goal_achievable( int objn
 		return AI_GOAL_ACHIEVABLE;
 	}
 
-	objp = &Objects[objnum];
-	Assert( objp->instance != -1 );
-	ai_shipname = Ships[objp->instance].ship_name;
-	aip = &Ai_info[Ships[objp->instance].ai_index];
 
 	return_val = AI_GOAL_SATISFIED;
 
 	// next, determine if the goal has been completed successfully
-	switch ( aigp->ai_mode ) {
-
-	case AI_GOAL_DOCK:
-	case AI_GOAL_CHASE_WING:
-	case AI_GOAL_UNDOCK:
-		//status = mission_log_get_time( LOG_SHIP_DOCK, ai_shipname, aigp->ship_name, NULL);
-		//status = mission_log_get_time( LOG_SHIP_UNDOCK, ai_shipname, aigp->ship_name, NULL );
-		//MWA 3/20/97 -- cannot short circuit a dock or undock goal already succeeded -- we must
-		// rely on the goal removal code to just remove this goal.  This is because docking/undock
-		// can happen > 1 time per mission per pair of ships.  The above checks will find only
-		// if the ships docked or undocked at all, which is not what we want.
-		status = 0;
-		break;
-	case AI_GOAL_DESTROY_SUBSYSTEM: {
-		int shipnum;
-		ship_subsys *ssp;
-
-		// shipnum could be -1 depending on if the ship hasn't arrived or died.  only look for subsystem
-		// destroyed when shipnum is valid
-		shipnum = ship_name_lookup( aigp->ship_name );
-
-		// can't determine the status of this goal if ship not valid
-		// or we haven't found a valid subsystem index yet
-		if ( (shipnum == -1) || (aigp->flags & AIGF_SUBSYS_NEEDS_FIXUP) ) {
+	switch ( aigp->ai_mode )
+	{
+		case AI_GOAL_DOCK:
+		case AI_GOAL_CHASE_WING:
+		case AI_GOAL_UNDOCK:
+			//status = mission_log_get_time( LOG_SHIP_DOCK, ai_shipname, aigp->ship_name, NULL);
+			//status = mission_log_get_time( LOG_SHIP_UNDOCK, ai_shipname, aigp->ship_name, NULL );
+			//MWA 3/20/97 -- cannot short circuit a dock or undock goal already succeeded -- we must
+			// rely on the goal removal code to just remove this goal.  This is because docking/undock
+			// can happen > 1 time per mission per pair of ships.  The above checks will find only
+			// if the ships docked or undocked at all, which is not what we want.
 			status = 0;
 			break;
+
+		case AI_GOAL_DESTROY_SUBSYSTEM:
+		{
+			ship_subsys *ssp;
+
+			// shipnum could be -1 depending on if the ship hasn't arrived or died.  only look for subsystem
+			// destroyed when shipnum is valid
+			sindex = ship_name_lookup( aigp->ship_name );
+
+			// can't determine the status of this goal if ship not valid
+			// or we haven't found a valid subsystem index yet
+			if ( (sindex == -1) || (aigp->flags & AIGF_SUBSYS_NEEDS_FIXUP) ) {
+				status = 0;
+				break;
+			}
+
+			// if the ship is not in the mission or the subsystem name is still being stored, mark the status
+			// as 0 so we can continue.  (The subsystem name must be turned into an index into the ship's subsystems
+			// for this goal to be valid).
+			Assert ( aigp->ai_submode >= 0 );
+			ssp = ship_get_indexed_subsys( &Ships[sindex], aigp->ai_submode );
+			status = mission_log_get_time( LOG_SHIP_SUBSYS_DESTROYED, aigp->ship_name, ssp->system_info->name, NULL );
+
+			break;
 		}
 
-		// if the ship is not in the mission or the subsystem name is still being stored, mark the status
-		// as 0 so we can continue.  (The subsystem name must be turned into an index into the ship's subsystems
-		// for this goal to be valid).
-		Assert ( aigp->ai_submode >= 0 );
-		ssp = ship_get_indexed_subsys( &Ships[shipnum], aigp->ai_submode );
-		status = mission_log_get_time( LOG_SHIP_SUBSYS_DESTROYED, aigp->ship_name, ssp->system_info->name, NULL );
+		case AI_GOAL_DISABLE_SHIP:
+			status = mission_log_get_time( LOG_SHIP_DISABLED, aigp->ship_name, NULL, NULL );
+			break;
 
-		break;
-	}
-	case AI_GOAL_DISABLE_SHIP:
-		status = mission_log_get_time( LOG_SHIP_DISABLED, aigp->ship_name, NULL, NULL );
-		break;
-	case AI_GOAL_DISARM_SHIP:
-		status = mission_log_get_time( LOG_SHIP_DISARMED, aigp->ship_name, NULL, NULL );
-		break;
+		case AI_GOAL_DISARM_SHIP:
+			status = mission_log_get_time( LOG_SHIP_DISARMED, aigp->ship_name, NULL, NULL );
+			break;
 
 		// to guard or ignore a ship, the goal cannot continue if the ship being guarded is either destroyed
 		// or has departed.
-	case AI_GOAL_GUARD:
-	case AI_GOAL_IGNORE:
-	case AI_GOAL_IGNORE_NEW:
-	case AI_GOAL_EVADE_SHIP:
-	case AI_GOAL_CHASE:
-	case AI_GOAL_STAY_NEAR_SHIP:
-	case AI_GOAL_REARM_REPAIR:
-	case AI_GOAL_FLY_TO_SHIP: {
-		int shipnum;
+		case AI_GOAL_GUARD:
+		case AI_GOAL_IGNORE:
+		case AI_GOAL_IGNORE_NEW:
+		case AI_GOAL_EVADE_SHIP:
+		case AI_GOAL_CHASE:
+		case AI_GOAL_STAY_NEAR_SHIP:
+		case AI_GOAL_REARM_REPAIR:
+		case AI_GOAL_FLY_TO_SHIP:
+		{
+			// MWA -- 4/22/98.  Check for the ship actually being in the mission before
+			// checking departure and destroyed.  In multiplayer, since ships can respawn,
+			// they get log entries for being destroyed even though they have respawned.
+			sindex = ship_name_lookup( aigp->ship_name );
+			if ( sindex == -1 ) {
+				status = mission_log_get_time( LOG_SHIP_DEPART, aigp->ship_name, NULL, NULL);
+				if ( !status ) {
+					status = mission_log_get_time( LOG_SHIP_DESTROYED, aigp->ship_name, NULL, NULL);
+					if ( status )
+						return_val = AI_GOAL_NOT_ACHIEVABLE;
+				}
+			} else {
+				status = 0;
+			}
 
-		// MWA -- 4/22/98.  Check for the ship actually being in the mission before
-		// checking departure and destroyed.  In multiplayer, since ships can respawn,
-		// they get log entries for being destroyed even though they have respawned.
-		shipnum = ship_name_lookup( aigp->ship_name );
-		if ( shipnum == -1 ) {
-			status = mission_log_get_time( LOG_SHIP_DEPART, aigp->ship_name, NULL, NULL);
+			break;
+		}
+
+		case AI_GOAL_GUARD_WING:
+		{
+			status = mission_log_get_time( LOG_WING_DEPART, aigp->ship_name, NULL, NULL );
 			if ( !status ) {
-				status = mission_log_get_time( LOG_SHIP_DESTROYED, aigp->ship_name, NULL, NULL);
+				status = mission_log_get_time( LOG_WING_DESTROYED, aigp->ship_name, NULL, NULL);
 				if ( status )
 					return_val = AI_GOAL_NOT_ACHIEVABLE;
 			}
-		} else {
-			status = 0;
-		}
-		break;
-	}
 
-	case AI_GOAL_GUARD_WING:
-		status = mission_log_get_time( LOG_WING_DEPART, aigp->ship_name, NULL, NULL );
-		if ( !status ) {
-			status = mission_log_get_time( LOG_WING_DESTROYED, aigp->ship_name, NULL, NULL);
-			if ( status )
-				return_val = AI_GOAL_NOT_ACHIEVABLE;
+			break;
 		}
-		break;
 
 		// the following case statement returns control to caller on all paths!!!!
-	case AI_GOAL_CHASE_WEAPON:
-		// for chase weapon, we simply need to look at the weapon instance that we are trying to
-		// attack and see if the object still exists, and has the same signature that we expect.
-		if ( Weapons[aigp->wp_index].objnum == -1 )
-			return AI_GOAL_NOT_ACHIEVABLE;
+		case AI_GOAL_CHASE_WEAPON:
+		{
+			// for chase weapon, we simply need to look at the weapon instance that we are trying to
+			// attack and see if the object still exists, and has the same signature that we expect.
+			Assert( aigp->wp_index >= 0 );
 
-		// if the signatures don't match, then goal isn't achievable.
-		if ( Objects[Weapons[aigp->wp_index].objnum].signature != aigp->weapon_signature )
-			return AI_GOAL_NOT_ACHIEVABLE;
+			if ( Weapons[aigp->wp_index].objnum == -1 )
+				return AI_GOAL_NOT_ACHIEVABLE;
 
-		// otherwise, we should be good to go
-		return AI_GOAL_ACHIEVABLE;
+			// if the signatures don't match, then goal isn't achievable.
+			if ( Objects[Weapons[aigp->wp_index].objnum].signature != aigp->weapon_signature )
+				return AI_GOAL_NOT_ACHIEVABLE;
 
-		break;
+			// otherwise, we should be good to go
+			return AI_GOAL_ACHIEVABLE;
 
-	default:
-		Int3();
-		status = 0;
-		break;
+			break;
+		}
+
+		default:
+			Int3();
+			status = 0;
+			break;
 	}
 
 	// if status is true, then the mission log event was found and the goal was satisfied.  return
@@ -1926,8 +1944,12 @@ int ai_mission_goal_achievable( int objn
 	// the if statement.
 	if ( (aigp->ai_mode == AI_GOAL_CHASE_WING) || (aigp->ai_mode == AI_GOAL_GUARD_WING) )
 	{
-		int num = wing_name_lookup( aigp->ship_name );
-		wing *wingp = &Wings[num];
+		sindex = wing_name_lookup( aigp->ship_name );
+
+		if (sindex < 0)
+			return AI_GOAL_NOT_ACHIEVABLE;
+
+		wing *wingp = &Wings[sindex];
 
 		if ( wingp->flags & WF_WING_GONE )
 			return AI_GOAL_NOT_ACHIEVABLE;
@@ -1986,24 +2008,25 @@ int ai_mission_goal_achievable( int objn
 	// only enter this calculation if the ship we are docking with has arrived.  If the ship is gone, then
 	// this goal will get removed.
 	if ( (aigp->ai_mode == AI_GOAL_DOCK) && (status == SHIP_STATUS_ARRIVED) ) {
-		int index, modelnum, shipnum;
-
 		if (!(aigp->flags & AIGF_DOCKER_INDEX_VALID)) {
 			modelnum = Ships[objp->instance].modelnum;
+			Assert( modelnum >= 0 );
 			index = model_find_dock_name_index(modelnum, aigp->docker.name);
 			aigp->docker.index = index;
 			aigp->flags |= AIGF_DOCKER_INDEX_VALID;
 		}
+
 		if (!(aigp->flags & AIGF_DOCKEE_INDEX_VALID)) {
-			shipnum = ship_name_lookup(aigp->ship_name);
-			if ( shipnum != -1 ) {
-				modelnum = Ships[shipnum].modelnum;
+			sindex = ship_name_lookup(aigp->ship_name);
+			if ( sindex != -1 ) {
+				modelnum = Ships[sindex].modelnum;
 				index = model_find_dock_name_index(modelnum, aigp->dockee.name);
 				aigp->dockee.index = index;
 				aigp->flags |= AIGF_DOCKEE_INDEX_VALID;
 			} else
 				aigp->dockee.index = -1;		// this will force code into if statement below making goal not achievable.
 		}
+
 		if ( (aigp->dockee.index == -1) || (aigp->docker.index == -1) ) {
 			Int3();			// for now, allender wants to know about these things!!!!
 			return AI_GOAL_NOT_ACHIEVABLE;
@@ -2014,9 +2037,9 @@ int ai_mission_goal_achievable( int objn
 			return AI_GOAL_NOT_KNOWN;
 
 		// we must also determine if we're prevented from docking for any reason
-		shipnum = ship_name_lookup(aigp->ship_name);
-		Assert( shipnum != -1 );
-		object *goal_objp = &Objects[Ships[shipnum].objnum];
+		sindex = ship_name_lookup(aigp->ship_name);
+		Assert( sindex >= 0 );
+		object *goal_objp = &Objects[Ships[sindex].objnum];
 
 		// if the ship that I am supposed to dock with is docked with something else, then I need to put my goal on hold
 		//	[MK, 4/23/98: With Mark, we believe this fixes the problem of Comet refusing to warp out after docking with Omega.
@@ -2075,11 +2098,10 @@ int ai_mission_goal_achievable( int objn
 		// have fixed up the subsystem name (of the subsystem to destroy) into an index into
 		// the ship's subsystem list
 		if ( aigp->flags & AIGF_SUBSYS_NEEDS_FIXUP ) {
-			int shipnum;			
+			sindex = ship_name_lookup( aigp->ship_name );
 
-			shipnum = ship_name_lookup( aigp->ship_name );
-			if ( shipnum != -1 ) {
-				aigp->ai_submode = ship_get_subsys_index( &Ships[shipnum], aigp->docker.name );
+			if ( sindex != -1 ) {
+				aigp->ai_submode = ship_get_subsys_index( &Ships[sindex], aigp->docker.name );
 				aigp->flags &= ~AIGF_SUBSYS_NEEDS_FIXUP;
 			} else {
 				Int3();
@@ -2087,97 +2109,103 @@ int ai_mission_goal_achievable( int objn
 			}
 		}
 	} else if ( ((aigp->ai_mode == AI_GOAL_IGNORE) || (aigp->ai_mode == AI_GOAL_IGNORE_NEW)) && (status == SHIP_STATUS_ARRIVED) ) {
-		int shipnum;
 		object *ignored;
 
 		// for ignoring a ship, call the ai_ignore object function, then declare the goal satisfied
-		shipnum = ship_name_lookup( aigp->ship_name );
-		Assert( shipnum != -1 );		// should be true because of above status
-		ignored = &Objects[Ships[shipnum].objnum];
+		sindex = ship_name_lookup( aigp->ship_name );
+		Assert( sindex != -1 );		// should be true because of above status
+		ignored = &Objects[Ships[sindex].objnum];
 
 		ai_ignore_object(objp, ignored, 100, (aigp->ai_mode == AI_GOAL_IGNORE_NEW));
+
 		return AI_GOAL_SATISFIED;
 	}
 
-	switch ( aigp->ai_mode ) {
+	switch ( aigp->ai_mode )
+	{
+		case AI_GOAL_CHASE:
+		case AI_GOAL_DOCK:
+		case AI_GOAL_DESTROY_SUBSYSTEM:
+		case AI_GOAL_UNDOCK:
+		case AI_GOAL_GUARD:
+		case AI_GOAL_GUARD_WING:
+		case AI_GOAL_DISABLE_SHIP:
+		case AI_GOAL_DISARM_SHIP:
+		case AI_GOAL_IGNORE:
+		case AI_GOAL_IGNORE_NEW:
+		case AI_GOAL_EVADE_SHIP:
+		case AI_GOAL_STAY_NEAR_SHIP:
+		case AI_GOAL_FLY_TO_SHIP:
+		{
+			if ( status == SHIP_STATUS_ARRIVED )
+				return AI_GOAL_ACHIEVABLE;
+			else if ( status == SHIP_STATUS_NOT_ARRIVED )
+				return AI_GOAL_NOT_KNOWN;
+			else if ( status == SHIP_STATUS_GONE )
+				return AI_GOAL_NOT_ACHIEVABLE;
+			else if ( status == SHIP_STATUS_UNKNOWN )
+				return AI_GOAL_NOT_KNOWN;
 
-	case AI_GOAL_CHASE:
-	case AI_GOAL_DOCK:
-	case AI_GOAL_DESTROY_SUBSYSTEM:
-	case AI_GOAL_UNDOCK:
-	case AI_GOAL_GUARD:
-	case AI_GOAL_GUARD_WING:
-	case AI_GOAL_DISABLE_SHIP:
-	case AI_GOAL_DISARM_SHIP:
-	case AI_GOAL_IGNORE:
-	case AI_GOAL_IGNORE_NEW:
-	case AI_GOAL_EVADE_SHIP:
-	case AI_GOAL_STAY_NEAR_SHIP:
-	case AI_GOAL_FLY_TO_SHIP:
-		if ( status == SHIP_STATUS_ARRIVED )
-			return AI_GOAL_ACHIEVABLE;
-		else if ( status == SHIP_STATUS_NOT_ARRIVED )
-			return AI_GOAL_NOT_KNOWN;
-		else if ( status == SHIP_STATUS_GONE )
-			return AI_GOAL_NOT_ACHIEVABLE;
-		else if ( status == SHIP_STATUS_UNKNOWN )
-			return AI_GOAL_NOT_KNOWN;
 			Int3();		// get allender -- bad logic
-		break;
-
-	// for rearm repair ships, a goal is only achievable if the support ship isn't repairing anything
-	// else at the time, or is set to repair the ship for this goal.  All other goals should be placed
-	// on hold by returning GOAL_NOT_KNOWN.
-	case AI_GOAL_REARM_REPAIR: {
-		int shipnum;
-
-		// short circuit a couple of cases.  Ship not arrived shouldn't happen.  Ship gone means
-		// we mark the goal as not achievable.
-		if ( status == SHIP_STATUS_NOT_ARRIVED ) {
-			Int3();										// get Allender.  this shouldn't happen!!!
-			return AI_GOAL_NOT_ACHIEVABLE;
+			break;
 		}
 
-		if ( status == SHIP_STATUS_GONE )
-			return AI_GOAL_NOT_ACHIEVABLE;
+		// for rearm repair ships, a goal is only achievable if the support ship isn't repairing anything
+		// else at the time, or is set to repair the ship for this goal.  All other goals should be placed
+		// on hold by returning GOAL_NOT_KNOWN.
+		case AI_GOAL_REARM_REPAIR:
+		{
+			// short circuit a couple of cases.  Ship not arrived shouldn't happen.  Ship gone means
+			// we mark the goal as not achievable.
+			if ( status == SHIP_STATUS_NOT_ARRIVED ) {
+				Int3();										// get Allender.  this shouldn't happen!!!
+				return AI_GOAL_NOT_ACHIEVABLE;
+			}
 
-		Assert( aigp->ship_name );
-		shipnum = ship_name_lookup( aigp->ship_name );
+			if ( status == SHIP_STATUS_GONE )
+				return AI_GOAL_NOT_ACHIEVABLE;
 
-		// if desitnation currently being repaired, then goal is stil active
-		if ( Ai_info[Ships[shipnum].ai_index].ai_flags & AIF_BEING_REPAIRED )
-			return AI_GOAL_ACHIEVABLE;
+			sindex = ship_name_lookup( aigp->ship_name );
 
-		// if the destination ship is dying or departing (but not completed yet), the mark goal as
-		// not achievable.
-		if ( Ships[shipnum].flags & (SF_DYING | SF_DEPARTING) )
-			return AI_GOAL_NOT_ACHIEVABLE;
+			if ( sindex < 0 ) {
+				Int3();
+				return AI_GOAL_NOT_ACHIEVABLE;
+			}
 
-		// if the destination object is no longer awaiting repair, then remove the item
-		if ( !(Ai_info[Ships[shipnum].ai_index].ai_flags & AIF_AWAITING_REPAIR) )
-			return AI_GOAL_NOT_ACHIEVABLE;
+			// if desitnation currently being repaired, then goal is stil active
+			if ( Ai_info[Ships[sindex].ai_index].ai_flags & AIF_BEING_REPAIRED )
+				return AI_GOAL_ACHIEVABLE;
+
+			// if the destination ship is dying or departing (but not completed yet), the mark goal as
+			// not achievable.
+			if ( Ships[sindex].flags & (SF_DYING | SF_DEPARTING) )
+				return AI_GOAL_NOT_ACHIEVABLE;
 
-		// not repairing anything means that he can do this goal!!!
-		if ( !(aip->ai_flags  & AIF_REPAIRING) )
-			return AI_GOAL_ACHIEVABLE;
+			// if the destination object is no longer awaiting repair, then remove the item
+			if ( !(Ai_info[Ships[sindex].ai_index].ai_flags & AIF_AWAITING_REPAIR) )
+				return AI_GOAL_NOT_ACHIEVABLE;
 
-		// test code!!!
-		if ( aip->goal_objnum == -1 ) {
-			// -- MK, 11/9/97 -- I was always hitting this: Int3();
-			return AI_GOAL_ACHIEVABLE;
-		}
+			// not repairing anything means that he can do this goal!!!
+			if ( !(aip->ai_flags  & AIF_REPAIRING) )
+				return AI_GOAL_ACHIEVABLE;
+
+			// test code!!!
+			if ( aip->goal_objnum == -1 ) {
+				// -- MK, 11/9/97 -- I was always hitting this: Int3();
+				return AI_GOAL_ACHIEVABLE;
+			}
 
-		// if he is repairing something, he can satisfy his repair goal (his goal_objnum)
-		// return GOAL_NOT_KNOWN which is kind of a hack which puts the goal on hold until it can be
-		// satisfied.  
-		if ( aip->goal_objnum != Ships[shipnum].objnum )
-			return AI_GOAL_NOT_KNOWN;
+			// if he is repairing something, he can satisfy his repair goal (his goal_objnum)
+			// return GOAL_NOT_KNOWN which is kind of a hack which puts the goal on hold until it can be
+			// satisfied.  
+			if ( aip->goal_objnum != Ships[sindex].objnum )
+				return AI_GOAL_NOT_KNOWN;
 
-		return AI_GOAL_ACHIEVABLE;
-	}
+			return AI_GOAL_ACHIEVABLE;
+		}
 
-	default:
-		Int3();			// invalid case in switch:
+		default:
+			Int3();			// invalid case in switch:
 	}
 
 	return AI_GOAL_NOT_KNOWN;
bug.diff (19,018 bytes)   

taylor

2006-11-06 02:49

administrator   ~0007080

Crap. That diff is mostly cosmetic too, which makes it more difficult to find the problem.

The only real difference that I see, is that AI_GOAL_CHASE_WING and AI_GOAL_GUARD_WING have an out-of-bounds check now, when they didn't before. This can make it return AI_GOAL_NOT_ACHIEVABLE when it might not have done so previously. That might be the place to start, but from the code it actually looks like it's doing the proper thing now since it would have validated the goal improperly before, or just crashed.

I think that it should probably return AI_GOAL_NOT_KNOWN if the wing number can't be found. That would hold the goal rather than deleting it like it does now. I can't test that right now though.

taylor

2006-11-06 02:54

administrator   ~0007081

I should also add that it points out another bug, since the goal would never be removed with that fix. So in the switch() statement directly above that one the AI_GOAL_CHASE_WING probably needs to be moved to the AI_GOAL_GUARD_WING case so that when the hostile wing either departs or gets destroyed the goal can be properly completed/removed. I may be wrong (probably am) about that though, so better double-check that before you make that particular change.

Goober5000

2006-11-06 03:04

administrator   ~0007082

Okay, I'm not quite sure what you meant there, so I'll probably have to investigate more closely, but you're right about the wing lookup. This is the culprit:

+ sindex = wing_name_lookup( aigp->ship_name );
+
+ if (sindex < 0)
+ return AI_GOAL_NOT_ACHIEVABLE;
+

The sneaky part about this is that wing_name_lookup doesn't just resolve the wing index, it *also* checks to see if the wing is present in-mission. So AI_GOAL_NOT_KNOWN would be the proper thing to return there.

Goober5000

2006-11-06 03:32

administrator   ~0007083

I'm pretty sure you're right about the other thing too. I'm committing both fixes.

Issue History

Date Modified Username Field Change
2006-10-30 23:41 Goober5000 New Issue
2006-10-31 00:16 taylor Note Added: 0007048
2006-11-02 01:46 Goober5000 Note Added: 0007058
2006-11-02 02:22 taylor Note Added: 0007059
2006-11-02 06:16 Goober5000 Note Added: 0007060
2006-11-02 12:52 taylor File Added: 09.diff
2006-11-02 12:53 taylor Note Added: 0007061
2006-11-05 21:44 Goober5000 Note Added: 0007076
2006-11-05 22:05 Goober5000 Note Added: 0007077
2006-11-06 02:24 Goober5000 Note Added: 0007079
2006-11-06 02:24 Goober5000 File Added: bug.diff
2006-11-06 02:49 taylor Note Added: 0007080
2006-11-06 02:54 taylor Note Added: 0007081
2006-11-06 03:04 Goober5000 Note Added: 0007082
2006-11-06 03:32 Goober5000 Note Added: 0007083
2006-11-06 03:34 Goober5000 Assigned To => Goober5000
2006-11-06 03:34 Goober5000 Status new => resolved
2006-11-06 03:34 Goober5000 Resolution open => fixed
2006-11-06 03:34 Goober5000 Fixed in Version => 3.6.9