Add a danger map to the AIs. This will keep them from wandering into the line of fire of enemies while going
from one place to another, and can also be used for quick lookups to see if a particular tile is threatened by potential enemy fire. Patch review by SafetyOff and with help from Cyp.master
parent
860d70715d
commit
0c0bcd5c35
|
@ -113,6 +113,7 @@ struct PathBlockingMap
|
|||
|
||||
PathBlockingType type;
|
||||
std::vector<bool> map;
|
||||
std::vector<bool> dangerMap; // using threatBits
|
||||
};
|
||||
|
||||
// Data structures used for pathfinding, can contain cached results.
|
||||
|
@ -124,6 +125,10 @@ struct PathfindContext
|
|||
// Not sure whether the out-of-bounds check is needed, can only happen if pathfinding is started on a blocking tile (or off the map).
|
||||
return x < 0 || y < 0 || x >= mapWidth || y >= mapWidth || blockingMap->map[x + y*mapWidth];
|
||||
}
|
||||
bool isDangerous(int x, int y) const
|
||||
{
|
||||
return !blockingMap->dangerMap.empty() && blockingMap->dangerMap[x + y*mapWidth];
|
||||
}
|
||||
bool matches(PathBlockingMap const *blockingMap_, PathCoord tileS_) const
|
||||
{
|
||||
// Must check myGameTime == blockingMap_->type.gameTime, otherwise blockingMap be a deleted pointer which coincidentally compares equal to the valid pointer blockingMap_.
|
||||
|
@ -225,8 +230,9 @@ static inline void fpathNewNode(PathfindContext &context, PathCoord dest, PathCo
|
|||
|
||||
// Create the node.
|
||||
PathNode node;
|
||||
unsigned costFactor = context.isDangerous(pos.x, pos.y) ? 5 : 1;
|
||||
node.p = pos;
|
||||
node.dist = prevDist + fpathEstimate(prevPos, pos);
|
||||
node.dist = prevDist + fpathEstimate(prevPos, pos)*costFactor;
|
||||
node.est = node.dist + fpathEstimate(pos, dest);
|
||||
|
||||
PathExploredTile &expl = context.map[pos.x + pos.y*mapWidth];
|
||||
|
@ -543,6 +549,16 @@ void fpathSetBlockingMap(PATHJOB *psJob)
|
|||
{
|
||||
map[x + y*mapWidth] = fpathBaseBlockingTile(x, y, type.propulsion, type.owner, type.moveType);
|
||||
}
|
||||
if (!isHumanPlayer(type.owner) && type.moveType == FMT_MOVE)
|
||||
{
|
||||
std::vector<bool> &dangerMap = i->dangerMap;
|
||||
dangerMap.resize(mapWidth*mapHeight);
|
||||
for (int y = 0; y < mapHeight; ++y)
|
||||
for (int x = 0; x < mapWidth; ++x)
|
||||
{
|
||||
dangerMap[x + y*mapWidth] = mapTile(x, y)->threatBits & (1 << type.owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// i now points to the correct map. Make psJob->blockingMap point to it.
|
||||
|
|
|
@ -2210,10 +2210,11 @@ void dealWithLMB( void )
|
|||
{
|
||||
MAPTILE *psTile = mapTile(mouseTileX, mouseTileY);
|
||||
|
||||
CONPRINTF(ConsoleString, (ConsoleString, "%s tile %d, %d [%d, %d] continent(l%d, h%d) level %g illum %d",
|
||||
CONPRINTF(ConsoleString, (ConsoleString, "%s tile %d, %d [%d, %d] continent(l%d, h%d) level %g illum %d %s %s",
|
||||
tileIsExplored(psTile) ? "Explored" : "Unexplored",
|
||||
mouseTileX, mouseTileY, world_coord(mouseTileX), world_coord(mouseTileY),
|
||||
(int)psTile->limitedContinent, (int)psTile->hoverContinent, psTile->level, (int)psTile->illumination));
|
||||
(int)psTile->limitedContinent, (int)psTile->hoverContinent, psTile->level, (int)psTile->illumination,
|
||||
psTile->dangerBits & (1 << selectedPlayer) ? "danger" : "", psTile->threatBits & (1 << selectedPlayer) ? "threat" : ""));
|
||||
}
|
||||
|
||||
driveDisableTactical();
|
||||
|
|
|
@ -1063,8 +1063,8 @@ BOOL stageThreeInitialise(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
mapInit();
|
||||
clustInitialise();
|
||||
|
||||
gridReset();
|
||||
|
||||
//if mission screen is up, close it.
|
||||
|
|
120
src/map.c
120
src/map.c
|
@ -2210,9 +2210,127 @@ bool fireOnLocation(unsigned int x, unsigned int y)
|
|||
return psTile != NULL && TileIsBurning(psTile);
|
||||
}
|
||||
|
||||
static void dangerFloodFill(int player)
|
||||
{
|
||||
struct ffnode *open = NULL;
|
||||
int i;
|
||||
Vector2i pos = getPlayerStartPosition(player);
|
||||
|
||||
pos.x = map_coord(pos.x);
|
||||
pos.y = map_coord(pos.y);
|
||||
|
||||
do
|
||||
{
|
||||
MAPTILE *currTile = mapTile(pos.x, pos.y);
|
||||
|
||||
// Add accessible neighbouring tiles to the open list
|
||||
for (i = 0; i < NUM_DIR; i++)
|
||||
{
|
||||
Vector2i npos = { pos.x + aDirOffset[i].x, pos.y + aDirOffset[i].y };
|
||||
MAPTILE *psTile;
|
||||
|
||||
if (!tileOnMap(npos.x, npos.y) || (npos.x == pos.x && npos.y == pos.y))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
psTile = mapTile(npos.x, npos.y);
|
||||
|
||||
if (!(psTile->threatBits & (1 << player)) && (psTile->dangerBits & (1 <<player)) && !fpathBlockingTile(npos.x, npos.y, PROPULSION_TYPE_WHEELED))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear danger
|
||||
currTile->dangerBits &= ~(1 << player);
|
||||
|
||||
// Pop the first open node off the list for the next iteration
|
||||
if (open)
|
||||
{
|
||||
struct ffnode *tmp = open;
|
||||
|
||||
pos.x = open->x;
|
||||
pos.y = open->y;
|
||||
open = open->next;
|
||||
free(tmp);
|
||||
}
|
||||
} while (open);
|
||||
}
|
||||
|
||||
static inline void threatUpdate(int player, BASE_OBJECT *psObj)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((psObj->visible[player] || psObj->born == 2) && !aiCheckAlliances(player, psObj->player))
|
||||
{
|
||||
for (i = 0; i < psObj->numWatchedTiles; i++)
|
||||
{
|
||||
const TILEPOS pos = psObj->watchedTiles[i];
|
||||
MAPTILE *psTile = mapTile(pos.x, pos.y);
|
||||
psTile->threatBits |= (1 << player); // set threat for this tile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dangerUpdate(int player)
|
||||
{
|
||||
MAPTILE *psTile = psMapTiles;
|
||||
int i;
|
||||
|
||||
// Step 1: Clear our threat bits, set our danger bits
|
||||
for (i = 0; i < mapWidth * mapHeight; i++, psTile++)
|
||||
{
|
||||
psTile->threatBits &= ~(1 << player);
|
||||
psTile->dangerBits |= (1 << player);
|
||||
}
|
||||
|
||||
// Step 2: Set threat bits
|
||||
for (i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
DROID *psDroid;
|
||||
STRUCTURE *psStruct;
|
||||
|
||||
for (psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
|
||||
{
|
||||
if (psDroid->droidType != DROID_CONSTRUCT && psDroid->droidType != DROID_CYBORG_CONSTRUCT
|
||||
&& psDroid->droidType != DROID_REPAIR && psDroid->droidType != DROID_CYBORG_REPAIR)
|
||||
{
|
||||
threatUpdate(player, (BASE_OBJECT *)psDroid);
|
||||
}
|
||||
}
|
||||
|
||||
for (psStruct = apsStructLists[i]; psStruct; psStruct = psStruct->psNext)
|
||||
{
|
||||
if (psStruct->pStructureType->pSensor->location == LOC_TURRET || psStruct->numWeaps > 0)
|
||||
{
|
||||
threatUpdate(player, (BASE_OBJECT *)psStruct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Clear danger bits using a flood fill from start position
|
||||
dangerFloodFill(player);
|
||||
}
|
||||
|
||||
void mapInit()
|
||||
{
|
||||
int player;
|
||||
|
||||
for (player = 0; player < MAX_PLAYERS; player++)
|
||||
{
|
||||
dangerUpdate(player);
|
||||
}
|
||||
}
|
||||
|
||||
void mapUpdate()
|
||||
{
|
||||
uint16_t currentTime = gameTime / GAME_TICKS_PER_UPDATE;
|
||||
int updatedPlayer = currentTime % game.maxPlayers;
|
||||
|
||||
int posX, posY;
|
||||
for (posY = 0; posY < mapHeight; ++posY)
|
||||
|
@ -2229,5 +2347,5 @@ void mapUpdate()
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Make waves in the water?
|
||||
dangerUpdate(updatedPlayer);
|
||||
}
|
||||
|
|
|
@ -103,6 +103,8 @@ typedef struct _maptile
|
|||
uint8_t tileInfoBits;
|
||||
uint8_t tileExploredBits;
|
||||
uint8_t sensorBits; // bit per player, who can see tile with sensor
|
||||
uint8_t dangerBits; // bit per player, does AI sense danger going there? not always up to date
|
||||
uint8_t threatBits; // bit per player, can hostile player shoot here? not always up to date
|
||||
float height; // The height at the top left of the tile
|
||||
uint8_t illumination; // How bright is this tile?
|
||||
uint16_t texture; // Which graphics texture is on this tile
|
||||
|
@ -448,6 +450,7 @@ static inline bool hasSensorOnTile(MAPTILE *psTile, unsigned player)
|
|||
return ((player == selectedPlayer && godMode) || (alliancebits[selectedPlayer] & (satuplinkbits | psTile->sensorBits)));
|
||||
}
|
||||
|
||||
void mapInit(void);
|
||||
void mapUpdate(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -153,6 +153,11 @@ BOOL scrGetPlayer()
|
|||
return true;
|
||||
}
|
||||
|
||||
Vector2i getPlayerStartPosition(int player)
|
||||
{
|
||||
return positions[player];
|
||||
}
|
||||
|
||||
BOOL scrGetPlayerStartPosition(void)
|
||||
{
|
||||
SDWORD *x, *y, player;
|
||||
|
@ -1455,7 +1460,7 @@ BOOL scrBuildDroid(void)
|
|||
|
||||
if (productionRun != 1)
|
||||
{
|
||||
debug(LOG_WARNING, "A script is trying to build a different number (%d) than 1 droid.", productionRun);
|
||||
debug(LOG_ERROR, "A script is trying to build a different number (%d) than 1 droid.", productionRun);
|
||||
}
|
||||
structSetManufacture(psFactory, psTemplate);
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ extern BOOL scrGetPlayer(void);
|
|||
extern BOOL scrScavengersActive(void);
|
||||
extern BOOL scrGetPlayerStartPosition(void);
|
||||
|
||||
extern Vector2i getPlayerStartPosition(int player);
|
||||
|
||||
// not used in scripts, but used in code.
|
||||
extern BOOL objectInRange(struct BASE_OBJECT *psList, SDWORD x, SDWORD y, SDWORD range);
|
||||
|
||||
|
|
Loading…
Reference in New Issue