2004-08-22 15:56:56 +00:00
|
|
|
#include "stdafx.h"
|
2005-06-02 19:30:21 +00:00
|
|
|
#include "openttd.h"
|
2005-02-05 15:58:59 +00:00
|
|
|
#include "debug.h"
|
2004-12-15 22:18:54 +00:00
|
|
|
#include "map.h"
|
2005-01-29 12:19:05 +00:00
|
|
|
#include "tile.h"
|
2004-08-22 15:56:56 +00:00
|
|
|
#include "command.h"
|
2005-07-17 15:34:10 +00:00
|
|
|
#include "ai_new.h"
|
2005-02-06 22:36:08 +00:00
|
|
|
#include "depot.h"
|
2005-07-21 18:44:27 +00:00
|
|
|
#include "variables.h"
|
2004-08-22 15:56:56 +00:00
|
|
|
|
|
|
|
#define TEST_STATION_NO_DIR 0xFF
|
|
|
|
|
|
|
|
// Tests if a station can be build on the given spot
|
|
|
|
// TODO: make it train compatible
|
2005-06-24 12:38:35 +00:00
|
|
|
static bool TestCanBuildStationHere(TileIndex tile, byte dir)
|
2005-01-22 22:47:58 +00:00
|
|
|
{
|
2005-06-21 16:28:17 +00:00
|
|
|
Player *p = GetPlayer(_current_player);
|
2005-04-20 22:30:40 +00:00
|
|
|
|
|
|
|
if (dir == TEST_STATION_NO_DIR) {
|
|
|
|
int32 ret;
|
|
|
|
// TODO: currently we only allow spots that can be access from al 4 directions...
|
|
|
|
// should be fixed!!!
|
2005-04-25 14:29:58 +00:00
|
|
|
for (dir = 0; dir < 4; dir++) {
|
2005-04-20 22:30:40 +00:00
|
|
|
ret = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST);
|
|
|
|
if (!CmdFailed(ret)) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return true if command succeeded, so the inverse of CmdFailed()
|
|
|
|
return !CmdFailed(AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST));
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
|
2005-01-17 09:16:43 +00:00
|
|
|
|
|
|
|
static bool IsRoad(TileIndex tile)
|
|
|
|
{
|
|
|
|
return
|
|
|
|
// MP_STREET, but not a road depot?
|
2005-04-25 14:29:58 +00:00
|
|
|
(IsTileType(tile, MP_STREET) && !IsTileDepotType(tile, TRANSPORT_ROAD)) ||
|
2005-01-17 09:16:43 +00:00
|
|
|
(IsTileType(tile, MP_TUNNELBRIDGE) && (
|
|
|
|
// road tunnel?
|
2005-07-13 18:04:01 +00:00
|
|
|
((_m[tile].m5 & 0x80) == 0 && (_m[tile].m5 & 0x4) == 0x4) ||
|
2005-01-17 09:16:43 +00:00
|
|
|
// road bridge?
|
2005-07-13 18:04:01 +00:00
|
|
|
((_m[tile].m5 & 0x80) != 0 && (_m[tile].m5 & 0x2) == 0x2)
|
2005-01-17 09:16:43 +00:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// Checks if a tile 'a' is between the tiles 'b' and 'c'
|
2005-01-07 17:02:43 +00:00
|
|
|
#define TILES_BETWEEN(a, b, c) (TileX(a) >= TileX(b) && TileX(a) <= TileX(c) && TileY(a) >= TileY(b) && TileY(a) <= TileY(c))
|
2004-08-22 15:56:56 +00:00
|
|
|
|
|
|
|
// Check if the current tile is in our end-area
|
2005-04-02 10:38:31 +00:00
|
|
|
static int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current)
|
2005-01-22 22:47:58 +00:00
|
|
|
{
|
2004-08-22 15:56:56 +00:00
|
|
|
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
|
|
|
// It is not allowed to have a station on the end of a bridge or tunnel ;)
|
2005-04-02 10:38:31 +00:00
|
|
|
if (current->path.node.user_data[0] != 0) return AYSTAR_DONE;
|
|
|
|
if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br))
|
|
|
|
if (IsTileType(current->path.node.tile, MP_CLEAR) || IsTileType(current->path.node.tile, MP_TREES))
|
2005-04-25 14:29:58 +00:00
|
|
|
if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile, AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile)))
|
|
|
|
return AYSTAR_FOUND_END_NODE;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
return AYSTAR_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculates the hash
|
|
|
|
// Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64)
|
2005-01-22 22:47:58 +00:00
|
|
|
static uint AiPathFinder_Hash(uint key1, uint key2)
|
|
|
|
{
|
2005-01-07 17:02:43 +00:00
|
|
|
return (TileX(key1) & 0x1F) + ((TileY(key1) & 0x1F) << 5);
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the memory of all the things
|
2005-01-22 22:47:58 +00:00
|
|
|
static void AyStar_AiPathFinder_Free(AyStar *aystar)
|
|
|
|
{
|
2004-08-22 15:56:56 +00:00
|
|
|
AyStarMain_Free(aystar);
|
|
|
|
free(aystar);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
|
|
|
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
|
|
|
|
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current);
|
|
|
|
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current);
|
|
|
|
|
|
|
|
// This creates the AiPathFinder
|
2005-04-25 14:29:58 +00:00
|
|
|
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo)
|
|
|
|
{
|
2004-08-22 15:56:56 +00:00
|
|
|
PathNode start_node;
|
2005-04-25 14:29:58 +00:00
|
|
|
uint x;
|
|
|
|
uint y;
|
2004-08-22 15:56:56 +00:00
|
|
|
// Create AyStar
|
|
|
|
AyStar *result = malloc(sizeof(AyStar));
|
|
|
|
init_AyStar(result, AiPathFinder_Hash, 1 << 10);
|
|
|
|
// Set the function pointers
|
|
|
|
result->CalculateG = AyStar_AiPathFinder_CalculateG;
|
|
|
|
result->CalculateH = AyStar_AiPathFinder_CalculateH;
|
|
|
|
result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck;
|
|
|
|
result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
|
|
|
|
result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
|
|
|
|
|
2005-07-04 14:58:55 +00:00
|
|
|
result->BeforeExit = NULL;
|
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
result->free = AyStar_AiPathFinder_Free;
|
|
|
|
|
|
|
|
// Set some information
|
|
|
|
result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK;
|
|
|
|
result->max_path_cost = 0;
|
|
|
|
result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES;
|
|
|
|
|
|
|
|
// Set the user_data to the PathFinderInfo
|
|
|
|
result->user_target = PathFinderInfo;
|
|
|
|
|
|
|
|
// Set the start node
|
|
|
|
start_node.parent = NULL;
|
|
|
|
start_node.node.direction = 0;
|
|
|
|
start_node.node.user_data[0] = 0;
|
|
|
|
|
|
|
|
// Now we add all the starting tiles
|
2005-01-07 17:02:43 +00:00
|
|
|
for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
|
|
|
|
for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
|
2005-06-25 16:44:57 +00:00
|
|
|
start_node.node.tile = TileXY(x, y);
|
2005-05-07 22:00:36 +00:00
|
|
|
result->addstart(result, &start_node.node, 0);
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// To reuse AyStar we sometimes have to clean all the memory
|
2005-04-25 14:29:58 +00:00
|
|
|
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo)
|
|
|
|
{
|
2004-08-22 15:56:56 +00:00
|
|
|
PathNode start_node;
|
2005-04-25 14:29:58 +00:00
|
|
|
uint x;
|
|
|
|
uint y;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
aystar->clear(aystar);
|
|
|
|
|
|
|
|
// Set the user_data to the PathFinderInfo
|
|
|
|
aystar->user_target = PathFinderInfo;
|
|
|
|
|
|
|
|
// Set the start node
|
|
|
|
start_node.parent = NULL;
|
|
|
|
start_node.node.direction = 0;
|
|
|
|
start_node.node.user_data[0] = 0;
|
|
|
|
start_node.node.tile = PathFinderInfo->start_tile_tl;
|
|
|
|
|
|
|
|
// Now we add all the starting tiles
|
2005-01-07 17:02:43 +00:00
|
|
|
for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
|
|
|
|
for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
|
2005-06-25 16:44:57 +00:00
|
|
|
if (!(IsTileType(TileXY(x, y), MP_CLEAR) || IsTileType(TileXY(x, y), MP_TREES))) continue;
|
|
|
|
if (!TestCanBuildStationHere(TileXY(x, y), TEST_STATION_NO_DIR)) continue;
|
|
|
|
start_node.node.tile = TileXY(x, y);
|
2005-05-07 22:00:36 +00:00
|
|
|
aystar->addstart(aystar, &start_node.node, 0);
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The h-value, simple calculation
|
2005-04-25 14:29:58 +00:00
|
|
|
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
|
|
|
|
{
|
2004-08-22 15:56:56 +00:00
|
|
|
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
|
|
|
int r, r2;
|
|
|
|
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) {
|
|
|
|
// The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate
|
2005-01-31 07:23:15 +00:00
|
|
|
r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDir(PathFinderInfo->end_direction));
|
|
|
|
r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDir(PathFinderInfo->end_direction));
|
2004-08-22 15:56:56 +00:00
|
|
|
} else {
|
|
|
|
// No direction, so just get the fastest route to the station
|
2005-01-31 07:23:15 +00:00
|
|
|
r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl);
|
|
|
|
r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br);
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
2004-12-29 13:13:29 +00:00
|
|
|
// See if the bottomright is faster than the topleft..
|
2004-08-22 15:56:56 +00:00
|
|
|
if (r2 < r) r = r2;
|
|
|
|
return r * AI_PATHFINDER_H_MULTIPLER;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We found the end.. let's get the route back and put it in an array
|
2005-04-25 14:29:58 +00:00
|
|
|
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current)
|
|
|
|
{
|
2004-08-22 15:56:56 +00:00
|
|
|
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
2005-01-09 21:25:44 +00:00
|
|
|
uint i = 0;
|
2004-08-22 15:56:56 +00:00
|
|
|
PathNode *parent = ¤t->path;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
do {
|
2005-04-21 05:51:47 +00:00
|
|
|
PathFinderInfo->route_extra[i] = parent->node.user_data[0];
|
2004-08-22 15:56:56 +00:00
|
|
|
PathFinderInfo->route[i++] = parent->node.tile;
|
|
|
|
if (i > lengthof(PathFinderInfo->route)) {
|
|
|
|
// We ran out of space for the PathFinder
|
2005-04-25 14:29:58 +00:00
|
|
|
DEBUG(ai, 0)("[AiPathFinder] Ran out of space in the route[] array!!!");
|
2004-08-22 15:56:56 +00:00
|
|
|
PathFinderInfo->route_length = -1; // -1 indicates out of space
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
parent = parent->parent;
|
|
|
|
} while (parent != NULL);
|
|
|
|
PathFinderInfo->route_length = i;
|
2005-04-25 14:29:58 +00:00
|
|
|
DEBUG(ai, 1)("[Ai-PathFinding] Found route of %d nodes long in %d nodes of searching", i, Hash_Size(&aystar->ClosedListHash));
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// What tiles are around us.
|
2005-04-25 14:29:58 +00:00
|
|
|
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current)
|
|
|
|
{
|
2005-04-21 05:51:47 +00:00
|
|
|
uint i;
|
|
|
|
int ret;
|
|
|
|
int dir;
|
|
|
|
|
|
|
|
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
|
|
|
|
|
|
|
aystar->num_neighbours = 0;
|
|
|
|
|
|
|
|
// Go through all surrounding tiles and check if they are within the limits
|
|
|
|
for (i = 0; i < 4; i++) {
|
2005-04-21 16:13:52 +00:00
|
|
|
TileIndex ctile = current->path.node.tile; // Current tile
|
|
|
|
TileIndex atile = ctile + TileOffsByDir(i); // Adjacent tile
|
|
|
|
|
2005-04-25 14:29:58 +00:00
|
|
|
if (TileX(atile) > 1 && TileX(atile) < MapMaxX() - 1 &&
|
|
|
|
TileY(atile) > 1 && TileY(atile) < MapMaxY() - 1) {
|
2005-04-21 05:51:47 +00:00
|
|
|
// We also directly test if the current tile can connect to this tile..
|
|
|
|
// We do this simply by just building the tile!
|
|
|
|
|
|
|
|
// If the next step is a bridge, we have to enter it the right way
|
2005-04-21 16:13:52 +00:00
|
|
|
if (!PathFinderInfo->rail_or_road && IsRoad(atile)) {
|
|
|
|
if (IsTileType(atile, MP_TUNNELBRIDGE)) {
|
2005-04-21 05:51:47 +00:00
|
|
|
// An existing bridge... let's test the direction ;)
|
2005-07-13 18:04:01 +00:00
|
|
|
if ((_m[atile].m5 & 1U) != (i & 1)) continue;
|
2005-04-21 05:51:47 +00:00
|
|
|
// This problem only is valid for tunnels:
|
|
|
|
// When the last tile was not yet a tunnel, check if we enter from the right side..
|
2005-07-13 18:04:01 +00:00
|
|
|
if ((_m[atile].m5 & 0x80) == 0) {
|
|
|
|
if (i != (_m[atile].m5 & 3U)) continue;
|
2005-04-21 05:51:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// But also if we are on a bridge, we can only move a certain direction
|
2005-04-21 16:13:52 +00:00
|
|
|
if (!PathFinderInfo->rail_or_road && IsRoad(ctile)) {
|
|
|
|
if (IsTileType(ctile, MP_TUNNELBRIDGE)) {
|
2005-04-21 05:51:47 +00:00
|
|
|
// An existing bridge/tunnel... let's test the direction ;)
|
2005-07-13 18:04:01 +00:00
|
|
|
if ((_m[ctile].m5 & 1U) != (i & 1)) continue;
|
2005-04-21 05:51:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 ||
|
|
|
|
(AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) {
|
|
|
|
// We are a bridge/tunnel, how cool!!
|
|
|
|
// This means we can only point forward.. get the direction from the user_data
|
|
|
|
if (i != (current->path.node.user_data[0] >> 8)) continue;
|
|
|
|
}
|
|
|
|
dir = 0;
|
|
|
|
|
|
|
|
// First, check if we have a parent
|
|
|
|
if (current->path.parent == NULL && current->path.node.user_data[0] == 0) {
|
|
|
|
// If not, this means we are at the starting station
|
|
|
|
if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) {
|
|
|
|
// We do need a direction?
|
2005-04-21 16:13:52 +00:00
|
|
|
if (AiNew_GetDirection(ctile, atile) != PathFinderInfo->start_direction) {
|
2005-04-21 05:51:47 +00:00
|
|
|
// We are not pointing the right way, invalid tile
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (current->path.node.user_data[0] == 0) {
|
|
|
|
if (PathFinderInfo->rail_or_road) {
|
|
|
|
// Rail check
|
2005-04-21 16:13:52 +00:00
|
|
|
dir = AiNew_GetRailDirection(current->path.parent->node.tile, ctile, atile);
|
|
|
|
ret = DoCommandByTile(ctile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
|
2005-04-21 05:51:47 +00:00
|
|
|
if (CmdFailed(ret)) continue;
|
2004-08-22 15:56:56 +00:00
|
|
|
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
|
2005-04-21 05:51:47 +00:00
|
|
|
if (current->path.parent->parent != NULL) {
|
|
|
|
// Check if we don't make a 90degree curve
|
2005-04-21 16:13:52 +00:00
|
|
|
int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, ctile);
|
2005-04-21 05:51:47 +00:00
|
|
|
if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2004-08-22 15:56:56 +00:00
|
|
|
#endif
|
2005-04-21 05:51:47 +00:00
|
|
|
} else {
|
|
|
|
// Road check
|
2005-04-21 16:13:52 +00:00
|
|
|
dir = AiNew_GetRoadDirection(current->path.parent->node.tile, ctile, atile);
|
|
|
|
if (IsRoad(ctile)) {
|
|
|
|
if (IsTileType(ctile, MP_TUNNELBRIDGE)) {
|
2005-04-21 05:51:47 +00:00
|
|
|
// We have a bridge, how nicely! We should mark it...
|
|
|
|
dir = 0;
|
|
|
|
} else {
|
|
|
|
// It already has road.. check if we miss any bits!
|
2005-07-13 18:04:01 +00:00
|
|
|
if ((_m[ctile].m5 & dir) != dir) {
|
2005-04-21 05:51:47 +00:00
|
|
|
// We do miss some pieces :(
|
2005-07-13 18:04:01 +00:00
|
|
|
dir &= ~_m[ctile].m5;
|
2005-04-21 05:51:47 +00:00
|
|
|
} else {
|
|
|
|
dir = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Only destruct things if it is MP_CLEAR of MP_TREES
|
|
|
|
if (dir != 0) {
|
2005-04-21 16:13:52 +00:00
|
|
|
ret = DoCommandByTile(ctile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
|
2005-04-21 05:51:47 +00:00
|
|
|
if (CmdFailed(ret)) continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// The tile can be connected
|
2005-04-21 16:13:52 +00:00
|
|
|
aystar->neighbours[aystar->num_neighbours].tile = atile;
|
2005-04-21 05:51:47 +00:00
|
|
|
aystar->neighbours[aystar->num_neighbours].user_data[0] = 0;
|
|
|
|
aystar->neighbours[aystar->num_neighbours++].direction = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next step, check for bridges and tunnels
|
|
|
|
if (current->path.parent != NULL && current->path.node.user_data[0] == 0) {
|
|
|
|
TileInfo ti;
|
|
|
|
// First we get the dir from this tile and his parent
|
|
|
|
int dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
|
|
|
|
// It means we can only walk with the track, so the bridge has to be in the same direction
|
|
|
|
TileIndex tile = current->path.node.tile;
|
|
|
|
TileIndex new_tile = tile;
|
|
|
|
|
|
|
|
FindLandscapeHeightByTile(&ti, tile);
|
|
|
|
|
|
|
|
// Bridges can only be build on land that is not flat
|
|
|
|
// And if there is a road or rail blocking
|
|
|
|
if (ti.tileh != 0 ||
|
|
|
|
(PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDir(dir), MP_STREET)) ||
|
|
|
|
(!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDir(dir), MP_RAILWAY))) {
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
new_tile += TileOffsByDir(dir);
|
|
|
|
|
|
|
|
// Precheck, is the length allowed?
|
2005-04-25 14:29:58 +00:00
|
|
|
if (!CheckBridge_Stuff(0, GetBridgeLength(tile, new_tile))) break;
|
2005-04-21 05:51:47 +00:00
|
|
|
|
|
|
|
// Check if we hit the station-tile.. we don't like that!
|
2005-04-25 14:29:58 +00:00
|
|
|
if (TILES_BETWEEN(new_tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) break;
|
2005-04-21 05:51:47 +00:00
|
|
|
|
|
|
|
// Try building the bridge..
|
2005-04-25 14:29:58 +00:00
|
|
|
ret = DoCommandByTile(tile, new_tile, (0 << 8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE);
|
2005-04-21 05:51:47 +00:00
|
|
|
if (CmdFailed(ret)) continue;
|
|
|
|
// We can build a bridge here.. add him to the neighbours
|
|
|
|
aystar->neighbours[aystar->num_neighbours].tile = new_tile;
|
|
|
|
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8);
|
|
|
|
aystar->neighbours[aystar->num_neighbours++].direction = 0;
|
2004-08-22 15:56:56 +00:00
|
|
|
// We can only have 12 neighbours, and we need 1 left for tunnels
|
|
|
|
if (aystar->num_neighbours == 11) break;
|
|
|
|
}
|
2005-04-21 05:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Next, check for tunnels!
|
|
|
|
// Tunnels can only be build with tileh of 3, 6, 9 or 12, depending on the direction
|
|
|
|
// For now, we check both sides for this tile.. terraforming gives fuzzy result
|
|
|
|
if ((dir == 0 && ti.tileh == 12) ||
|
|
|
|
(dir == 1 && ti.tileh == 6) ||
|
|
|
|
(dir == 2 && ti.tileh == 3) ||
|
|
|
|
(dir == 3 && ti.tileh == 9)) {
|
|
|
|
// Now simply check if a tunnel can be build
|
|
|
|
ret = DoCommandByTile(tile, (PathFinderInfo->rail_or_road?0:0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL);
|
|
|
|
FindLandscapeHeightByTile(&ti, _build_tunnel_endtile);
|
|
|
|
if (!CmdFailed(ret) && (ti.tileh == 3 || ti.tileh == 6 || ti.tileh == 9 || ti.tileh == 12)) {
|
|
|
|
aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile;
|
|
|
|
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8);
|
|
|
|
aystar->neighbours[aystar->num_neighbours++].direction = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern uint GetRailFoundation(uint tileh, uint bits);
|
|
|
|
extern uint GetRoadFoundation(uint tileh, uint bits);
|
|
|
|
extern uint GetBridgeFoundation(uint tileh, byte direction);
|
|
|
|
enum {
|
2005-04-21 05:51:47 +00:00
|
|
|
BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12,
|
2004-08-22 15:56:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// The most important function: it calculates the g-value
|
2005-04-25 14:29:58 +00:00
|
|
|
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
|
|
|
|
{
|
2004-08-22 15:56:56 +00:00
|
|
|
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
|
|
|
|
int r, res = 0;
|
|
|
|
TileInfo ti, parent_ti;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// Gather some information about the tile..
|
|
|
|
FindLandscapeHeightByTile(&ti, current->tile);
|
|
|
|
FindLandscapeHeightByTile(&parent_ti, parent->path.node.tile);
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// Check if we hit the end-tile
|
2005-04-25 14:29:58 +00:00
|
|
|
if (TILES_BETWEEN(current->tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) {
|
2004-08-22 15:56:56 +00:00
|
|
|
// We are at the end-tile, check if we had a direction or something...
|
|
|
|
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction)
|
|
|
|
// We are not pointing the right way, invalid tile
|
|
|
|
return AYSTAR_INVALID_NODE;
|
|
|
|
// If it was valid, drop out.. we don't build on the endtile
|
|
|
|
return 0;
|
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// Give everything a small penalty
|
|
|
|
res += AI_PATHFINDER_PENALTY;
|
|
|
|
|
|
|
|
if (!PathFinderInfo->rail_or_road) {
|
|
|
|
// Road has the lovely advantage it can use other road... check if
|
|
|
|
// the current tile is road, and if so, give a good bonus
|
2005-01-17 09:16:43 +00:00
|
|
|
if (IsRoad(current->tile)) {
|
2004-08-22 15:56:56 +00:00
|
|
|
res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS;
|
|
|
|
}
|
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// We should give a penalty when the tile is going up or down.. this is one way to do so!
|
2005-03-12 08:52:40 +00:00
|
|
|
// Too bad we have to count it from the parent.. but that is not so bad.
|
|
|
|
// We also dislike long routes on slopes, since they do not look too realistic
|
|
|
|
// when there is a flat land all around, they are more expensive to build, and
|
|
|
|
// especially they essentially block the ability to connect or cross the road
|
|
|
|
// from one side.
|
2004-08-22 15:56:56 +00:00
|
|
|
if (parent_ti.tileh != 0 && parent->path.parent != NULL) {
|
|
|
|
// Skip if the tile was from a bridge or tunnel
|
|
|
|
if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) {
|
|
|
|
if (PathFinderInfo->rail_or_road) {
|
|
|
|
r = GetRailFoundation(parent_ti.tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
|
|
|
|
// Maybe is BRIDGE_NO_FOUNDATION a bit strange here, but it contains just the right information..
|
|
|
|
if (r >= 15 || (r == 0 && (BRIDGE_NO_FOUNDATION & (1 << ti.tileh)))) {
|
|
|
|
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
|
2005-03-12 08:52:40 +00:00
|
|
|
} else {
|
|
|
|
res += AI_PATHFINDER_FOUNDATION_PENALTY;
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
} else {
|
2005-01-17 09:16:43 +00:00
|
|
|
if (!(IsRoad(parent->path.node.tile) && IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE))) {
|
2004-08-22 15:56:56 +00:00
|
|
|
r = GetRoadFoundation(parent_ti.tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
|
|
|
|
if (r >= 15 || r == 0)
|
|
|
|
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
|
2005-03-12 08:52:40 +00:00
|
|
|
else
|
|
|
|
res += AI_PATHFINDER_FOUNDATION_PENALTY;
|
2004-08-22 15:56:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// Are we part of a tunnel?
|
|
|
|
if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) {
|
|
|
|
// Tunnels are very expensive when build on long routes..
|
|
|
|
// Ironicly, we are using BridgeCode here ;)
|
|
|
|
r = AI_PATHFINDER_TUNNEL_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
|
|
|
|
res += r + (r >> 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Are we part of a bridge?
|
|
|
|
if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) {
|
|
|
|
// That means for every length a penalty
|
|
|
|
res += AI_PATHFINDER_BRIDGE_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
|
|
|
|
// Check if we are going up or down, first for the starting point
|
|
|
|
// In user_data[0] is at the 8th bit the direction
|
|
|
|
if (!(BRIDGE_NO_FOUNDATION & (1 << parent_ti.tileh))) {
|
|
|
|
if (GetBridgeFoundation(parent_ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
|
|
|
|
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
|
|
|
}
|
|
|
|
// Second for the end point
|
|
|
|
if (!(BRIDGE_NO_FOUNDATION & (1 << ti.tileh))) {
|
|
|
|
if (GetBridgeFoundation(ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
|
|
|
|
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
|
|
|
}
|
|
|
|
if (parent_ti.tileh == 0)
|
|
|
|
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
|
|
|
if (ti.tileh == 0)
|
|
|
|
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
|
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// To prevent the AI from taking the fastest way in tiles, but not the fastest way
|
|
|
|
// in speed, we have to give a good penalty to direction changing
|
|
|
|
// This way, we get almost the fastest way in tiles, and a very good speed on the track
|
|
|
|
if (!PathFinderInfo->rail_or_road) {
|
|
|
|
if (parent->path.parent != NULL &&
|
|
|
|
AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) {
|
|
|
|
// When road exists, we don't like turning, but its free, so don't be to piggy about it
|
2005-01-17 09:16:43 +00:00
|
|
|
if (IsRoad(parent->path.node.tile))
|
2004-08-22 15:56:56 +00:00
|
|
|
res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY;
|
|
|
|
else
|
|
|
|
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// For rail we have 1 exeption: diagonal rail..
|
|
|
|
// So we fetch 2 raildirection. That of the current one, and of the one before that
|
|
|
|
if (parent->path.parent != NULL && parent->path.parent->parent != NULL) {
|
|
|
|
int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile);
|
|
|
|
int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile);
|
2004-12-29 13:13:29 +00:00
|
|
|
// First, see if we are on diagonal path, that is better than straight path
|
2004-08-22 15:56:56 +00:00
|
|
|
if (dir1 > 1) { res -= AI_PATHFINDER_DIAGONAL_BONUS; }
|
|
|
|
|
|
|
|
// First see if they are different
|
|
|
|
if (dir1 != dir2) {
|
|
|
|
// dir 2 and 3 are 1 diagonal track, and 4 and 5.
|
|
|
|
if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) {
|
|
|
|
// It is not, so we changed of direction
|
|
|
|
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
|
|
|
|
}
|
|
|
|
if (parent->path.parent->parent->parent != NULL) {
|
|
|
|
int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile);
|
|
|
|
// Check if we changed 3 tiles of direction in 3 tiles.. bad!!!
|
|
|
|
if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) {
|
|
|
|
res += AI_PATHFINDER_CURVE_PENALTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-08-22 15:56:56 +00:00
|
|
|
// Res should never be below zero.. if so, make it zero!
|
|
|
|
if (res < 0) { res = 0; }
|
|
|
|
|
|
|
|
// Return our value
|
|
|
|
return res;
|
|
|
|
}
|