This patch introduces the (currently unused) function bool
fpathCheck(Vector2i, Vector2i) that checks whether a path is
theoretically possible in O(1) time. It does this by doing lookups a
pre-calculated continents table that is created on map load using a
non-recursive flood fill algorithm. It is intended to be used in the
AI scripts to avoid attempting to send units where they cannot
possibly go, like sending tracked droids over water to another
continent. I am not sure how to add new script functions, so I'll need
some help with that from someone else.

Please give feedback. Sending it directly to the list since the
trac->list gateway is apparently down.

  - Per
Index: src/display.c
===================================================================
--- src/display.c	(revision 6596)
+++ src/display.c	(working copy)
@@ -2192,14 +2192,14 @@
 				audio_PlayTrack( ID_SOUND_SELECT );
 			}
 
-			if(godMode && (mouseTileX >= 0) && (mouseTileX < (SDWORD)mapWidth) &&
-				(mouseTileY >= 0) && (mouseTileY < (SDWORD)mapHeight))
+			if (getDebugMappingStatus() && tileOnMap(mouseTileX, mouseTileY))
 			{
-				DBCONPRINTF(ConsoleString,(ConsoleString,"Tile Coords : %d,%d (%d,%d)", mouseTileX,mouseTileY,
-					mouseTileX*TILE_UNITS + TILE_UNITS/2, mouseTileY*TILE_UNITS + TILE_UNITS/2));
+				MAPTILE *psTile = mapTile(mouseTileX, mouseTileY);
+
+				DBCONPRINTF(ConsoleString, (ConsoleString, "Tile Coords : %d, %d [%d, %d] continent(l%d, h%d)", 
+				            mouseTileX, mouseTileY, world_coord(mouseTileX), world_coord(mouseTileY),
+				            (int)psTile->limitedContinent, (int)psTile->hoverContinent));
 			}
-
-			//addConsoleMessage("Droid ordered to new location",DEFAULT_JUSTIFY,SYSTEM_MESSAGE);
 		}
 
 		driveDisableTactical();
Index: src/map.c
===================================================================
--- src/map.c	(revision 6596)
+++ src/map.c	(working copy)
@@ -50,6 +50,12 @@
 #include "fpath.h"
 #include "levels.h"
 
+struct ffnode
+{
+	struct ffnode *next;
+	int x, y;
+};
+
 //scroll min and max values
 SDWORD		scrollMinX, scrollMaxX, scrollMinY, scrollMaxY;
 
@@ -301,6 +307,9 @@
 	scrollMaxX = mapWidth;
 	scrollMaxY = mapHeight;
 
+	/* Set continents. This should ideally be done in advance by the map editor. */
+	mapFloodFillContinents();
+
 	return true;
 }
 
@@ -1546,3 +1555,125 @@
 
 	fprintf(stdout, "\tMap self-test: PASSED\n");
 }
+
+// Convert a direction into an offset.
+// dir 0 => x = 0, y = -1
+#define NUM_DIR		8
+static const Vector2i aDirOffset[NUM_DIR] =
+{
+        { 0, 1},
+        {-1, 1},
+        {-1, 0},
+        {-1,-1},
+        { 0,-1},
+        { 1,-1},
+        { 1, 0},
+        { 1, 1},
+};
+
+// Flood fill a "continent". Note that we reuse x, y inside this function.
+static void mapFloodFill(int x, int y, int continent, PROPULSION_TYPE propulsion)
+{
+	struct ffnode *open = NULL;
+	int i;
+
+	do
+	{
+		MAPTILE *currTile = mapTile(x, y);
+
+		// Add accessible neighbouring tiles to the open list
+		for (i = 0; i < NUM_DIR; i++)
+		{
+			// rely on the fact that all border tiles are inaccessible to avoid checking explicitly
+			Vector2i npos = { x + aDirOffset[i].x, y + aDirOffset[i].y };
+			MAPTILE *psTile;
+			bool limitedTile = (propulsion == PROPULSION_TYPE_PROPELLOR || propulsion == PROPULSION_TYPE_WHEELED);
+
+			if (!tileOnMap(npos.x, npos.y))
+			{
+				continue;
+			}
+			psTile = mapTile(npos.x, npos.y);
+
+			if (!fpathBlockingTile(x, y, propulsion) && ((limitedTile && psTile->limitedContinent == 0) || (!limitedTile && psTile->hoverContinent == 0)))
+			{
+				struct ffnode *node = malloc(sizeof(*node));
+
+				node->next = open;	// add to beginning of open list
+				node->x = npos.x;
+				node->y = npos.y;
+				open = node;
+			}
+		}
+
+		// Set continent value
+		if (propulsion == PROPULSION_TYPE_PROPELLOR || propulsion == PROPULSION_TYPE_WHEELED)
+		{
+			currTile->limitedContinent = continent;
+		}
+		else
+		{
+			currTile->hoverContinent = continent;
+		}
+
+		// Pop the first open node off the list for the next iteration
+		if (open)
+		{
+			struct ffnode *tmp = open;
+
+			x = open->x;
+			y = open->y;
+			open = open->next;
+			free(tmp);
+		}
+	} while (open);
+}
+
+void mapFloodFillContinents()
+{
+	int x, y, limitedContinents = 0, hoverContinents = 0;
+
+	/* Clear continents */
+	for (x = 0; x < mapWidth; x++)
+	{
+		for (y = 0; y < mapHeight; y++)
+		{
+			MAPTILE *psTile = mapTile(x, y);
+
+			psTile->limitedContinent = 0;
+			psTile->hoverContinent = 0;
+		}
+	}
+
+	/* Iterate over the whole map, looking for unset continents */
+	for (x = 0; x < mapWidth; x++)
+	{
+		for (y = 0; y < mapHeight; y++)
+		{
+			MAPTILE *psTile = mapTile(x, y);
+
+			if (psTile->limitedContinent == 0 && !fpathBlockingTile(x, y, PROPULSION_TYPE_WHEELED))
+			{
+				mapFloodFill(x, y, limitedContinents++, PROPULSION_TYPE_WHEELED);
+			}
+			else if (psTile->limitedContinent == 0 && !fpathBlockingTile(x, y, PROPULSION_TYPE_PROPELLOR))
+			{
+				mapFloodFill(x, y, limitedContinents++, PROPULSION_TYPE_PROPELLOR);
+			}
+		}
+	}
+	debug(LOG_MAP, "Found %d limited continents", (int)limitedContinents);
+	for (x = 0; x < mapWidth; x++)
+	{
+		for (y = 0; y < mapHeight; y++)
+		{
+			MAPTILE *psTile = mapTile(x, y);
+
+			if (psTile->hoverContinent == 0 && !fpathBlockingTile(x, y, PROPULSION_TYPE_HOVER))
+			{
+				mapFloodFill(x, y, hoverContinents++, PROPULSION_TYPE_HOVER);
+			}
+		}
+	}
+	debug(LOG_MAP, "Found %d hover continents", (int)hoverContinents);
+}
Index: src/map.h
===================================================================
--- src/map.h	(revision 6596)
+++ src/map.h	(working copy)
@@ -90,6 +90,8 @@
 	float			level;
 	BASE_OBJECT		*psObject;		// Any object sitting on the location (e.g. building)
 	PIELIGHT		colour;
+	char			limitedContinent;	/** For land or sea limited propulsion types */
+	char			hoverContinent;		/** For hover type propulsions */
 
 //	TYPE_OF_TERRAIN	type;			// The terrain type for the tile
 } MAPTILE;
@@ -347,6 +349,8 @@
 //scroll min and max values
 extern SDWORD		scrollMinX, scrollMaxX, scrollMinY, scrollMaxY;
 
+void mapFloodFillContinents(void);
+
 extern void mapTest(void);
 
 #endif // __INCLUDED_SRC_MAP_H__
Index: src/fpath.c
===================================================================
--- src/fpath.c	(revision 6596)
+++ src/fpath.c	(working copy)
@@ -679,3 +679,31 @@
 	assert(firstJob == NULL);
 	assert(firstResult == NULL);
 }
+
+bool fpathCheck(Vector2i orig, Vector2i dest, PROPULSION_TYPE propulsion)
+{
+	MAPTILE *origTile = mapTile(orig.x, orig.y);
+	MAPTILE *destTile = mapTile(dest.x, dest.y);
+
+	ASSERT(propulsion != PROPULSION_TYPE_NUM, "Bad propulsion type");
+	ASSERT(origTile != NULL && destTile != NULL, "Bad tile parameter");
+
+	switch (propulsion)
+	{
+	case PROPULSION_TYPE_PROPELLOR:
+	case PROPULSION_TYPE_WHEELED:
+	case PROPULSION_TYPE_TRACKED:
+	case PROPULSION_TYPE_LEGGED:
+	case PROPULSION_TYPE_SKI: 	// ?!
+	case PROPULSION_TYPE_HALF_TRACKED:
+		return origTile->limitedContinent == destTile->limitedContinent;
+	case PROPULSION_TYPE_HOVER:
+		return origTile->hoverContinent == destTile->hoverContinent;
+	case PROPULSION_TYPE_JUMP:
+	case PROPULSION_TYPE_LIFT:
+		return true;	// FIXME: This is not entirely correct for all possible maps. - Per
+	case PROPULSION_TYPE_NUM:
+		break;
+	}
+	return true;	// should never get here
+}
Index: src/fpath.h
===================================================================
--- src/fpath.h	(revision 6596)
+++ src/fpath.h	(working copy)
@@ -73,6 +73,10 @@
 /** Clean up path jobs and results for a droid. Function is thread-safe. */
 extern void fpathRemoveDroidData(int id);
 
+/** Quick O(1) test of whether it is theoretically possible to go from origin to destination
+ *  using the given propulsion type. */
+bool fpathCheck(Vector2i orig, Vector2i dest, PROPULSION_TYPE propulsion);
+
 /** Unit testing. */
 void fpathTest(int x, int y, int x2, int y2);
 
_______________________________________________
Warzone-dev mailing list
Warzone-dev@gna.org
https://mail.gna.org/listinfo/warzone-dev

Reply via email to