From 60ddaf95f0b52a09fad18ea05b2b9db4509d0511 Mon Sep 17 00:00:00 2001 From: hackykid Date: Mon, 4 Jul 2005 14:58:55 +0000 Subject: [PATCH] (svn r2516) - Feature: [pbs] Implement path-based-signalling. This allows multiple trains within the same signal block, provided their paths dont intersect. For this the block must have all exit and entry signals be pbs signals. Place these by ctrl-clicking 4 times on a normal signal. - Feature: [pbs] Implement autoplacement of pbs blocks, when a block has an entry and an exit pbs signal, covert the entire block to pbs. Can be turned off in the patch settings. - Feature: [pbs] Allow showing of reserved status by making the tracks darker, when the pbs debug level is at least 1. --- Makefile | 1 + ai_pathfinder.c | 2 + aystar.c | 4 + aystar.h | 6 + data/nsignalsw.grf | Bin 0 -> 50012 bytes data/signalsw.grf | Bin 22939 -> 0 bytes debug.c | 2 + debug.h | 1 + docs/landscape.html | 22 +++- lang/english.txt | 1 + npf.c | 234 ++++++++++++++++++++++++++++++++--- npf.h | 14 ++- pbs.c | 291 ++++++++++++++++++++++++++++++++++++++++++++ pbs.h | 82 +++++++++++++ rail.c | 9 ++ rail.h | 25 +++- rail_cmd.c | 133 ++++++++++++++------ road_cmd.c | 20 +++ roadveh_cmd.c | 4 +- settings.c | 1 + settings_gui.c | 1 + ship_cmd.c | 2 +- station_cmd.c | 22 +++- table/files.h | 4 +- table/sprites.h | 2 +- train_cmd.c | 218 +++++++++++++++++++++++++++++++-- tunnelbridge_cmd.c | 19 +++ variables.h | 1 + vehicle.c | 5 +- vehicle.h | 2 + 30 files changed, 1051 insertions(+), 77 deletions(-) create mode 100644 data/nsignalsw.grf delete mode 100644 data/signalsw.grf create mode 100644 pbs.c create mode 100644 pbs.h diff --git a/Makefile b/Makefile index be76e3d741..c1e20799cd 100644 --- a/Makefile +++ b/Makefile @@ -651,6 +651,7 @@ C_SOURCES += order_cmd.c C_SOURCES += order_gui.c C_SOURCES += openttd.c C_SOURCES += pathfind.c +C_SOURCES += pbs.c C_SOURCES += player_gui.c C_SOURCES += players.c C_SOURCES += pool.c diff --git a/ai_pathfinder.c b/ai_pathfinder.c index a3ddda9829..f26cead738 100644 --- a/ai_pathfinder.c +++ b/ai_pathfinder.c @@ -97,6 +97,8 @@ AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFin result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode; result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours; + result->BeforeExit = NULL; + result->free = AyStar_AiPathFinder_Free; // Set some information diff --git a/aystar.c b/aystar.c index e2f9109fed..785746185b 100644 --- a/aystar.c +++ b/aystar.c @@ -230,6 +230,10 @@ int AyStarMain_Main(AyStar *aystar) { else if (r == AYSTAR_LIMIT_REACHED) printf("[AyStar] Exceeded search_nodes, no path found\n"); #endif + + if (aystar->BeforeExit != NULL) + aystar->BeforeExit(aystar); + if (r != AYSTAR_STILL_BUSY) /* We're done, clean up */ aystar->clear(aystar); diff --git a/aystar.h b/aystar.h index adda33abab..52c93e9119 100644 --- a/aystar.h +++ b/aystar.h @@ -96,6 +96,11 @@ typedef void AyStar_GetNeighbours(AyStar *aystar, OpenListNode *current); */ typedef void AyStar_FoundEndNode(AyStar *aystar, OpenListNode *current); +/* + * Is called when aystar ends it pathfinding, but before cleanup. + */ +typedef void AyStar_BeforeExit(AyStar *aystar); + // For internal use, see aystar.c typedef void AyStar_AddStartNode(AyStar *aystar, AyStarNode* start_node, uint g); typedef int AyStar_Main(AyStar *aystar); @@ -115,6 +120,7 @@ struct AyStar { AyStar_GetNeighbours* GetNeighbours; AyStar_EndNodeCheck* EndNodeCheck; AyStar_FoundEndNode* FoundEndNode; + AyStar_BeforeExit* BeforeExit; /* These are completely untouched by AyStar, they can be accesed by * the application specific routines to input and output data. diff --git a/data/nsignalsw.grf b/data/nsignalsw.grf new file mode 100644 index 0000000000000000000000000000000000000000..03dee78637caea405c4ddc47652b30bca757a758 GIT binary patch literal 50012 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 LjzC-HJyHG-DXS0G literal 0 HcmV?d00001 diff --git a/data/signalsw.grf b/data/signalsw.grf deleted file mode 100644 index ca78876d09756ac8fb2dc87f631df0e59a1b6044..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22939 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 bits 7..4: bit clear = signal shows red; same bits as in map3_lo
  • OpenTTD bits in map3_hi: - - - - - + + + + + +
    bits 1..0: type of signal:
    00: normal signals
    01: pre-signals
    10: exit-signals
    11: combo-signals
    bits 2..0: type of signal:
    000: normal signals
    001: pre-signals
    010: exit-signals
    011: combo-signals
    100: PBS signals
    bit 3: set = semaphore signals, clear = light signals
  • @@ -120,7 +121,11 @@ map5 bit 7 clear: railway track C  on snow or desert
  • map3_lo bits 0..3 = track type: 0 - conventional railway, 1 - monorail, 2 - maglev -
  • +
  • map3_lo bits 4..7 = Pbs reserved status: + + + +
    bits 4..6  'Track'number of reserved track + 1, if this is zero it means nothing is reserved on this tile
    bit 7  If this is set, then the opposite track ('Track'number^1) is also reserved
  • map5 bits 7 and 6 set: railway depot / checkpoints
      @@ -133,6 +138,7 @@ map5 bits 7 and 6 set: railway depot / checkpoints
    • map_owner: owner of the depot / checkpoint
    • map3_lo bits 0..3 = track type
    • map3_lo bit 4 = use custom sprite (valid only for the checkpoint)
    • +
    • map3_lo bit 6 = track on this tile is reserved by pbs
    • map3_hi = custom station id
    @@ -157,6 +163,7 @@ map5 bit 4 set, bits 7..5 clear: level crossing
    • map5 bit 3: clear - road in the X direction, set - road in the Y direction (railway track always perpendicular)
    • map5 bit 2: set if crossing lights are on
    • +
    • map5 bit 0: set if rail track is reserved by pbs
    • map_owner: owner of the railway track
    • map2: Index into the array of towns, 0 for non-town roads
    • map3_lo bits 0..7: owner of the road
    • @@ -372,6 +379,7 @@ exit towards: 47 - NE, 48 - SE, 49 - SW, 4A
    • map2: index into the array of stations
    • map3_lo bits 0..3: track type for railway stations, must be 0 for all the other stations
    • map3_lo bit 4 = use custom sprite (valid only railway stations FOR NOW)
    • +
    • map3lo bit 6 set = track is reserved by pbs (railway stations only)
    • map3_hi = custom station id
    @@ -542,6 +550,8 @@ map5 bits 7..4 clear: tunnel entrance/exit
  • map_owner: owner of the tunnel
  • map3_lo bits 3..0 = track type for railway tunnel, must be 0 for road tunnel
  • map3_hi bit 7 set = on snow or desert
  • +
  • map3hi bit 0 set = track with 'Track'number 0 is reserved by pbs
  • +
  • map3hi bit 1 set = track with 'Track'number 1 is reserved by pbs
  • map5 bit 7 set: bridge
    • diff --git a/lang/english.txt b/lang/english.txt index b97214009b..81cb29d35e 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -1073,6 +1073,7 @@ STR_CONFIG_PATCHES_ENDING_DATE :{LTBLUE}End gam STR_CONFIG_PATCHES_SMOOTH_ECONOMY :{LTBLUE}Enable smooth economy (more, smaller changes) STR_CONFIG_PATCHES_ALLOW_SHARES :{LTBLUE}Allow buying shares from other companies STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY :{LTBLUE}When dragging, place signals every: {ORANGE}{STRING} tile(s) +STR_CONFIG_AUTO_PBS_PLACEMENT :{LTBLUE}Allow automatic placement of pbs signals: {ORANGE}{STRING} STR_CONFIG_PATCHES_TOOLBAR_POS :{LTBLUE}Position of main toolbar: {ORANGE}{STRING} STR_CONFIG_PATCHES_TOOLBAR_POS_LEFT :Left STR_CONFIG_PATCHES_TOOLBAR_POS_CENTER :Centre diff --git a/npf.c b/npf.c index bbc992fede..bdb26af52a 100644 --- a/npf.c +++ b/npf.c @@ -21,6 +21,57 @@ static const uint _trackdir_length[TRACKDIR_END] = { NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH }; +/** + * Check if a rail track is the end of the line. Will also consider 1-way signals to be the end of a line. + * @param tile The tile on which the current track is. + * @param trackdir The (track)direction in which you want to look + */ +bool IsEndOfLine(TileIndex tile, Trackdir trackdir) +{ + byte exitdir = TrackdirToExitdir(trackdir); + TileIndex dst_tile; + uint32 ts; + + // tunnel entrance? + if (IsTileType(tile, MP_TUNNELBRIDGE) && (_map5[tile] & 0xF0)==0 && (_map5[tile] & 3) == exitdir) + return false; + + // depot + if (IsTileDepotType(tile, TRANSPORT_RAIL)) + return false; + + /* Calculate next tile */ + dst_tile = tile + TileOffsByDir(exitdir); + // determine the track status on the next tile. + ts = GetTileTrackStatus(dst_tile, TRANSPORT_RAIL) & TrackdirReachesTrackdirs(trackdir); + + // when none of the trackdir bits are set, we cant enter the new tile + if ( (ts & TRACKDIR_BIT_MASK) == 0) + return true; + + { + byte src_type = GetTileRailType(tile, trackdir); + byte dst_type = GetTileRailType(dst_tile, TrackdirToExitdir(trackdir)); + if (src_type != dst_type) { + return true; + } + if (GetTileOwner(tile) != GetTileOwner(dst_tile)) + return true; + + if (IsTileDepotType(dst_tile, TRANSPORT_RAIL) && (TrackdirToExitdir(trackdir) != ReverseDiagdir(GetDepotDirection(dst_tile, TRANSPORT_RAIL)))) + return true; + + /* Check for oneway signal against us */ + if (IsTileType(dst_tile, MP_RAILWAY) && GetRailTileType(dst_tile) == RAIL_TYPE_SIGNALS) { + if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(FindFirstBit2x64(ts))) && !HasSignalOnTrackdir(dst_tile, FindFirstBit2x64(ts))) + // if one way signal not pointing towards us, stop going in this direction. + return true; + } + + return false; + } +}; + static uint NTPHash(uint key1, uint key2) { /* This function uses the old hash, which is fixed on 10 bits (1024 buckets) */ @@ -76,6 +127,82 @@ static TileIndex CalcClosestStationTile(StationID station, TileIndex tile) return TileXY(x, y); }; +/* On PBS pathfinding runs, this is called before pathfinding ends (BeforeExit aystar callback), and will + * reserve the appropriate tracks, if needed. */ +void NPFReservePBSPath(AyStar *as) +{ + NPFFoundTargetData* ftd = (NPFFoundTargetData*)as->user_path; + bool eol_end = false; + + if (ftd->best_trackdir == 0xFF) + return; + + if (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_EXIT) && IsEndOfLine(ftd->node.tile, ftd->node.direction) && !NPFGetFlag(&ftd->node, NPF_FLAG_SEEN_SIGNAL)) { + /* The path ends in an end of line, we'll need to reserve a path. + * We treat and end of line as a red exit signal */ + eol_end = true; + NPFSetFlag(&ftd->node, NPF_FLAG_PBS_EXIT, true); + if (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_TARGET_SEEN)) + NPFSetFlag(&ftd->node, NPF_FLAG_PBS_RED, true); + } + + if (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_CHOICE)) { + /* there have been no choices to make on our path, we dont care if our end signal is red */ + NPFSetFlag(&ftd->node, NPF_FLAG_PBS_RED, false); + } + + if (NPFGetFlag(&ftd->node, NPF_FLAG_PBS_EXIT) && // we passed an exit signal + !NPFGetFlag(&ftd->node, NPF_FLAG_PBS_BLOCKED) && // we didnt encounter reserver tracks + ((as->user_data[NPF_PBS_MODE] != PBS_MODE_GREEN) || (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_RED))) ) { // our mode permits having a red exit signal, or the signal is green + PathNode parent; + PathNode *curr; + PathNode *prev; + TileIndex start = INVALID_TILE; + byte trackdir = 0; + + parent.node = ftd->node; + parent.parent = &ftd->path; + curr = &parent; + prev = NULL; + + do { + if (!NPFGetFlag(&curr->node, NPF_FLAG_PBS_EXIT) || eol_end) { + /* check for already reserved track on this path, if they clash with what we + currently trying to reserve, we have a self-crossing path :-( */ + if ((PBSTileUnavail(curr->node.tile) & (1 << curr->node.direction)) + && !(PBSTileReserved(curr->node.tile) & (1 << (curr->node.direction & 7))) + && (start != INVALID_TILE)) { + /* It's actually quite bad if this happens, it means the pathfinder + * found a path that is intersecting with itself, which is a very bad + * thing in a pbs block. Also there is not much we can do about it at + * this point.... + * BUT, you have to have a pretty fucked up junction layout for this to happen, + * so we'll just stop this train, the user will eventually notice, so he can fix it. + */ + PBSClearPath(start, trackdir); + NPFSetFlag(&ftd->node, NPF_FLAG_PBS_BLOCKED, true); + DEBUG(pbs, 1) ("PBS: Self-crossing path!!!"); + return; + }; + + PBSReserveTrack(curr->node.tile, (curr->node.direction & 7) ); + + /* we want to reserve the last tile (with the signal) on the path too */ + if (prev != NULL && start == INVALID_TILE) { + PBSReserveTrack(prev->node.tile, (prev->node.direction & 7) ); + start = prev->node.tile; + trackdir = ReverseTrackdir(prev->node.direction); + } + } + + prev = curr; + curr = curr->parent; + } while (curr != NULL); + } + +} + + /* Calcs the heuristic to the target station or tile. For train stations, it * takes into account the direction of approach. */ @@ -98,15 +225,27 @@ static int32 NPFCalcStationOrTileHeuristic(AyStar* as, AyStarNode* current, Open /* Ships and trains can also go diagonal, so the minimum distance is shorter */ dist = DistanceTrack(from, to) * NPF_TILE_LENGTH; - if (dist < ftd->best_bird_dist) { + DEBUG(npf, 4)("Calculating H for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), dist); + + /* for pbs runs, we ignore tiles inside the pbs block for the tracking + of the 'closest' tile */ + if ((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE) + && (!NPFGetFlag(current , NPF_FLAG_SEEN_SIGNAL)) + && (!IsEndOfLine(current->tile, current->direction))) + return dist; + + if ((dist < ftd->best_bird_dist) || + /* for pbs runs, prefer tiles that pass a green exit signal to the pbs blocks */ + ((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE) && !NPFGetFlag(current, NPF_FLAG_PBS_RED) && NPFGetFlag(&ftd->node, NPF_FLAG_PBS_RED)) +) { ftd->best_bird_dist = dist; ftd->best_trackdir = current->user_data[NPF_TRACKDIR_CHOICE]; + ftd->path = parent->path; + ftd->node = *current; } - DEBUG(npf, 4)("Calculating H for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), dist); return dist; } - /* Fills AyStarNode.user_data[NPF_TRACKDIRCHOICE] with the chosen direction to * get here, either getting it from the current choice or from the parent's * choice */ @@ -301,6 +440,11 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare /* Determine extra costs */ + /* Check for reserved tracks (PBS) */ + if ((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE) && !(NPFGetFlag(current, NPF_FLAG_PBS_EXIT)) && !(NPFGetFlag(current, NPF_FLAG_PBS_BLOCKED)) && (PBSTileUnavail(tile) & (1<EndNodeCheck(as, &new_node)==AYSTAR_FOUND_END_NODE) + + /* Check for depots */ + if (IsTileDepotType(tile, TRANSPORT_RAIL)) { /* Penalise any depot tile that is not the last tile in the path. This * _should_ penalise every occurence of reversing in a depot (and only * that) */ - cost += _patches.npf_rail_depot_reverse_penalty; + if (as->EndNodeCheck(as, &new_node) != AYSTAR_FOUND_END_NODE) + cost += _patches.npf_rail_depot_reverse_penalty; + + /* Do we treat this depot as a pbs signal? */ + if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) { + if (NPFGetFlag(current, NPF_FLAG_PBS_BLOCKED)) { + cost += 1000; + } + if (PBSIsPbsDepot(tile)) { + NPFSetFlag(current, NPF_FLAG_PBS_EXIT, true); + NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true); + } + } + NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false); + } /* Check for occupied track */ //TODO @@ -379,12 +551,22 @@ static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current) AyStarNode *node = ¤t->path.node; TileIndex tile = node->tile; + if (tile == 0x4611c) { + tile++; + tile--; + } + /* If GetNeighbours said we could get here, we assume the station type * is correct */ if ( (fstd->station_index == -1 && tile == fstd->dest_coords) || /* We've found the tile, or */ - (IsTileType(tile, MP_STATION) && _map2[tile] == fstd->station_index) /* the station */ + (IsTileType(tile, MP_STATION) && _map2[tile] == fstd->station_index) || /* the station */ + (NPFGetFlag(node, NPF_FLAG_PBS_TARGET_SEEN)) /* or, we've passed it already (for pbs) */ ) { + NPFSetFlag(¤t->path.node, NPF_FLAG_PBS_TARGET_SEEN, true); + /* for pbs runs, only accept we've found the target if we've also found a way out of the block */ + if ((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE) && !NPFGetFlag(node, NPF_FLAG_SEEN_SIGNAL) && !IsEndOfLine(node->tile, node->direction)) + return AYSTAR_DONE; return AYSTAR_FOUND_END_NODE; } else { return AYSTAR_DONE; @@ -402,6 +584,7 @@ static void NPFSaveTargetData(AyStar* as, OpenListNode* current) ftd->best_path_dist = current->g; ftd->best_bird_dist = 0; ftd->node = current->path.node; + ftd->path = current->path; } /** @@ -478,6 +661,8 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current) aystar->num_neighbours = 0; DEBUG(npf, 4)("Expanding: (%d, %d, %d) [%d]", TileX(src_tile), TileY(src_tile), src_trackdir, src_tile); + aystar->EndNodeCheck(aystar, current); + /* Find dest tile */ if (IsTileType(src_tile, MP_TUNNELBRIDGE) && (_map5[src_tile] & 0xF0)==0 && (DiagDirection)(_map5[src_tile] & 3) == src_exitdir) { /* This is a tunnel. We know this tunnel is our type, @@ -555,13 +740,23 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current) } else { ts = GetTileTrackStatus(dst_tile, type); } - trackdirbits = ts & 0x3F3F; /* Filter out signal status and the unused bits */ + trackdirbits = ts & TRACKDIR_BIT_MASK; /* Filter out signal status and the unused bits */ DEBUG(npf, 4)("Next node: (%d, %d) [%d], possible trackdirs: %#x", TileX(dst_tile), TileY(dst_tile), dst_tile, trackdirbits); /* Select only trackdirs we can reach from our current trackdir */ trackdirbits &= TrackdirReachesTrackdirs(src_trackdir); if (_patches.forbid_90_deg && (type == TRANSPORT_RAIL || type == TRANSPORT_WATER)) /* Filter out trackdirs that would make 90 deg turns for trains */ - trackdirbits &= ~TrackdirCrossesTrackdirs(src_trackdir); + + trackdirbits &= ~TrackdirCrossesTrackdirs(src_trackdir); + + if (KillFirstBit2x64(trackdirbits) != 0) + NPFSetFlag(¤t->path.node, NPF_FLAG_PBS_CHOICE, true); + + /* When looking for 'any' route, ie when already inside a pbs block, discard all tracks that would cross + other reserved tracks, so we *always* will find a valid route if there is one */ + if (!(NPFGetFlag(¤t->path.node, NPF_FLAG_PBS_EXIT)) && (aystar->user_data[NPF_PBS_MODE] == PBS_MODE_ANY)) + trackdirbits &= ~PBSTileUnavail(dst_tile); + DEBUG(npf,6)("After filtering: (%d, %d), possible trackdirs: %#x", TileX(dst_tile), TileY(dst_tile), trackdirbits); i = 0; @@ -602,7 +797,7 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current) * multiple targets that are spread around, we should perform a breadth first * search by specifiying CalcZero as our heuristic. */ -static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailType railtype, uint reverse_penalty) +static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailType railtype, uint reverse_penalty, byte pbs_mode) { int r; NPFFoundTargetData result; @@ -621,6 +816,11 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start else assert(0); + if (pbs_mode != PBS_MODE_NONE) + _npf_aystar.BeforeExit = NPFReservePBSPath; + else + _npf_aystar.BeforeExit = NULL; + /* Initialize Start Node(s) */ start1->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR; start1->user_data[NPF_NODE_FLAGS] = 0; @@ -645,6 +845,7 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start _npf_aystar.user_data[NPF_TYPE] = type; _npf_aystar.user_data[NPF_OWNER] = owner; _npf_aystar.user_data[NPF_RAILTYPE] = railtype; + _npf_aystar.user_data[NPF_PBS_MODE] = pbs_mode; /* GO! */ r = AyStarMain_Main(&_npf_aystar); @@ -662,7 +863,7 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start return result; } -NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype) +NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode) { AyStarNode start1; AyStarNode start2; @@ -676,12 +877,12 @@ NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir track start2.direction = trackdir2; start2.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR; - return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, owner, railtype, 0); + return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, owner, railtype, 0, pbs_mode); } -NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype) +NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode) { - return NPFRouteToStationOrTileTwoWay(tile, trackdir, INVALID_TILE, 0, target, type, owner, railtype); + return NPFRouteToStationOrTileTwoWay(tile, trackdir, INVALID_TILE, 0, target, type, owner, railtype, pbs_mode); } NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, TransportType type, Owner owner, RailType railtype, uint reverse_penalty) @@ -700,7 +901,7 @@ NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir t /* perform a breadth first search. Target is NULL, * since we are just looking for any depot...*/ - return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), NULL, NPFFindDepot, NPFCalcZero, type, owner, railtype, reverse_penalty); + return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), NULL, NPFFindDepot, NPFCalcZero, type, owner, railtype, reverse_penalty, PBS_MODE_NONE); } NPFFoundTargetData NPFRouteToDepotBreadthFirst(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailType railtype) @@ -753,6 +954,8 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, else assert(0); + _npf_aystar.BeforeExit = NULL; + /* Initialize target */ target.station_index = -1; /* We will initialize dest_coords inside the loop below */ _npf_aystar.user_target = ⌖ @@ -760,6 +963,7 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, /* Initialize user_data */ _npf_aystar.user_data[NPF_TYPE] = type; _npf_aystar.user_data[NPF_OWNER] = owner; + _npf_aystar.user_data[NPF_PBS_MODE] = PBS_MODE_NONE; /* Initialize Start Node */ start.tile = tile; diff --git a/npf.h b/npf.h index 847a36b4ec..0ded7fcfa3 100644 --- a/npf.h +++ b/npf.h @@ -4,6 +4,7 @@ #include "openttd.h" #include "aystar.h" #include "vehicle.h" +#include "pbs.h" #include "tile.h" #include "rail.h" @@ -36,16 +37,23 @@ enum { /* Indices into AyStar.userdata[] */ NPF_TYPE = 0, /* Contains a TransportTypes value */ NPF_OWNER, /* Contains an Owner value */ NPF_RAILTYPE, /* Contains the RailType value of the engine when NPF_TYPE == TRANSPORT_RAIL. Unused otherwise. */ + NPF_PBS_MODE, /* Contains the pbs mode, see pbs.h */ }; enum { /* Indices into AyStarNode.userdata[] */ NPF_TRACKDIR_CHOICE = 0, /* The trackdir chosen to get here */ NPF_NODE_FLAGS, }; + typedef enum { /* Flags for AyStarNode.userdata[NPF_NODE_FLAGS]. Use NPFGetBit() and NPFGetBit() to use them. */ NPF_FLAG_SEEN_SIGNAL, /* Used to mark that a signal was seen on the way, for rail only */ NPF_FLAG_REVERSE, /* Used to mark that this node was reached from the second start node, if applicable */ NPF_FLAG_LAST_SIGNAL_RED, /* Used to mark that the last signal on this path was red */ + NPF_FLAG_PBS_EXIT, /* Used to mark tracks inside a pbs block, for rail only, for the end node, this is set when the path found goes through a pbs block */ + NPF_FLAG_PBS_BLOCKED, /* Used to mark that this path crosses another pbs path */ + NPF_FLAG_PBS_RED, /* Used to mark that this path goes through a red exit-pbs signal */ + NPF_FLAG_PBS_CHOICE, /* Used to mark that the train has had a choice on this path */ + NPF_FLAG_PBS_TARGET_SEEN, /* Used to mark that a target tile has been passed on this path */ } NPFNodeFlag; typedef struct NPFFoundTargetData { /* Meant to be stored in AyStar.userpath */ @@ -53,6 +61,7 @@ typedef struct NPFFoundTargetData { /* Meant to be stored in AyStar.userpath */ uint best_path_dist; /* The shortest path. Is (uint)-1 if no path is found */ Trackdir best_trackdir; /* The trackdir that leads to the shortest path/closest birds dist */ AyStarNode node; /* The node within the target the search led us to */ + PathNode path; } NPFFoundTargetData; /* These functions below are _not_ re-entrant, in favor of speed! */ @@ -60,11 +69,12 @@ typedef struct NPFFoundTargetData { /* Meant to be stored in AyStar.userpath */ /* Will search from the given tile and direction, for a route to the given * station for the given transport type. See the declaration of * NPFFoundTargetData above for the meaning of the result. */ -NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype); +NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode); + /* Will search as above, but with two start nodes, the second being the * reverse. Look at the NPF_FLAG_REVERSE flag in the result node to see which * direction was taken (NPFGetBit(result.node, NPF_FLAG_REVERSE)) */ -NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype); +NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode); /* Will search a route to the closest depot. */ diff --git a/pbs.c b/pbs.c new file mode 100644 index 0000000000..fdb47854dd --- /dev/null +++ b/pbs.c @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "openttd.h" +#include "pbs.h" +#include "functions.h" +#include "debug.h" +#include "map.h" +#include "tile.h" +#include "npf.h" +#include "pathfind.h" +#include "depot.h" + +/** @file pbs.c Path-Based-Signalling implementation file + * @see pbs.h */ + +/* reserved track encoding: + normal railway tracks: + map3lo bits 4..6 = 'Track'number of reserved track + 1, if this is zero it means nothing is reserved on this tile + map3lo bit 7 = if this is set, then the opposite track ('Track'number^1) is also reserved + waypoints/stations: + map3lo bit 6 set = track is reserved + tunnels/bridges: + map3hi bit 0 set = track with 'Track'number 0 is reserved + map3hi bit 1 set = track with 'Track'number 1 is reserved + level crossings: + map5 bit 0 set = the rail track is reserved +*/ + +/** + * maps an encoded reserved track (from map3lo bits 4..7) + * to the tracks that are reserved. + * 0xFF are invalid entries and should never be accessed. + */ +static const byte encrt_to_reserved[16] = { + 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0C, 0x0C, 0x30, 0x30, 0xFF +}; + +/** + * maps an encoded reserved track (from map3lo bits 4..7) + * to the track(dir)s that are unavailable due to reservations. + * 0xFFFF are invalid entries and should never be accessed. + */ +static const int16 encrt_to_unavail[16] = { + 0x0000, 0x3F3F, 0x3F3F, 0x3737, 0x3B3B, 0x1F1F, 0x2F2F, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0x3F3F, 0x3F3F, 0x3F3F, 0x3F3F, 0xFFFF +}; + +void PBSReserveTrack(TileIndex tile, Track track) { + assert(IsValidTile(tile)); + assert(track <= 5); + switch (GetTileType(tile)) { + case MP_RAILWAY: + if ((_map5[tile] & ~1) == 0xC4) { + // waypoint + SETBIT(_map3_lo[tile], 6); + } else { + // normal rail track + byte encrt = (_map3_hi[tile] & 0xF0) >> 4; // get current encoded info (see comments at top of file) + + if (encrt == 0) // nothing reserved before + encrt = track + 1; + else if (encrt == (track^1) + 1) // opposite track reserved before + encrt |= 8; + + _map3_hi[tile] &= ~0xF0; + _map3_hi[tile] |= encrt << 4; + } + break; + case MP_TUNNELBRIDGE: + _map3_hi[tile] |= (1 << track) & 3; + break; + case MP_STATION: + SETBIT(_map3_lo[tile], 6); + break; + case MP_STREET: + // make sure it is a railroad crossing + if (!IsLevelCrossing(tile)) return; + SETBIT(_map5[tile], 0); + break; + default: + return; + }; + // if debugging, mark tile dirty to show reserved status + if (_debug_pbs_level >= 1) + MarkTileDirtyByTile(tile); +} + +byte PBSTileReserved(TileIndex tile) { + assert(IsValidTile(tile)); + switch (GetTileType(tile)) { + case MP_RAILWAY: + if ((_map5[tile] & ~1) == 0xC4) { + // waypoint + // check if its reserved + if (!HASBIT(_map3_lo[tile], 6)) return 0; + // return the track for the correct direction + return HASBIT(_map5[tile], 0) ? 2 : 1; + } else { + // normal track + byte res = encrt_to_reserved[(_map3_hi[tile] & 0xF0) >> 4]; + assert(res != 0xFF); + return res; + }; + case MP_TUNNELBRIDGE: + return (_map3_hi[tile] & 3); + case MP_STATION: + // check if its reserved + if (!HASBIT(_map3_lo[tile], 6)) return 0; + // return the track for the correct direction + return HASBIT(_map5[tile], 0) ? 2 : 1; + case MP_STREET: + // make sure its a railroad crossing + if (!IsLevelCrossing(tile)) return 0; + // check if its reserved + if (!HASBIT(_map5[tile], 0)) return 0; + // return the track for the correct direction + return HASBIT(_map5[tile], 3) ? 1 : 2; + default: + return 0; + }; +}; + +uint16 PBSTileUnavail(TileIndex tile) { + assert(IsValidTile(tile)); + switch (GetTileType(tile)) { + case MP_RAILWAY: + if ((_map5[tile] & ~1) == 0xC4) { + // waypoint + return HASBIT(_map3_lo[tile], 6) ? TRACKDIR_BIT_MASK : 0; + } else { + // normal track + uint16 res = encrt_to_unavail[(_map3_hi[tile] & 0xF0) >> 4]; + assert(res != 0xFFFF); + return res; + }; + case MP_TUNNELBRIDGE: + return (_map3_hi[tile] & 3) | ((_map3_hi[tile] & 3) << 8); + case MP_STATION: + return HASBIT(_map3_lo[tile], 6) ? TRACKDIR_BIT_MASK : 0; + case MP_STREET: + // make sure its a railroad crossing + if (!IsLevelCrossing(tile)) return 0; + // check if its reserved + return (HASBIT(_map5[tile], 0)) ? TRACKDIR_BIT_MASK : 0; + default: + return 0; + }; +}; + +void PBSClearTrack(TileIndex tile, Track track) { + assert(IsValidTile(tile)); + assert(track <= 5); + switch (GetTileType(tile)) { + case MP_RAILWAY: + if ((_map5[tile] & ~1) == 0xC4) { + // waypoint + CLRBIT(_map3_lo[tile], 6); + } else { + // normal rail track + byte encrt = (_map3_hi[tile] & 0xF0) >> 4; + + if (encrt == track + 1) + encrt = 0; + else if (encrt == track + 1 + 8) + encrt = (track^1) + 1; + else if (encrt == (track^1) + 1 + 8) + encrt &= 7; + + _map3_hi[tile] &= ~0xF0; + _map3_hi[tile] |= encrt << 4; + } + break; + case MP_TUNNELBRIDGE: + _map3_hi[tile] &= ~((1 << track) & 3); + break; + case MP_STATION: + CLRBIT(_map3_lo[tile], 6); + break; + case MP_STREET: + // make sure it is a railroad crossing + if (!IsLevelCrossing(tile)) return; + CLRBIT(_map5[tile], 0); + break; + default: + return; + }; + // if debugging, mark tile dirty to show reserved status + if (_debug_pbs_level >= 1) + MarkTileDirtyByTile(tile); +}; + +void PBSClearPath(TileIndex tile, Trackdir trackdir) { + uint16 res; + FindLengthOfTunnelResult flotr; + assert(IsValidTile(tile)); + assert((trackdir & ~8) <= 5); + do { + PBSClearTrack(tile, trackdir & 7); + + if (IsTileType(tile, MP_TUNNELBRIDGE) && (_map5[tile] & 0xF0)==0 && (unsigned)(_map5[tile] & 3) == TrackdirToExitdir(trackdir)) { + // this is a tunnel + flotr = FindLengthOfTunnel(tile, TrackdirToExitdir(trackdir)); + + tile = flotr.tile; + } else { + byte exitdir = TrackdirToExitdir(trackdir); + if (IsTileDepotType(tile, TRANSPORT_RAIL) && (exitdir != GetDepotDirection(tile, TRANSPORT_RAIL))) + return; + tile = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(exitdir)); + if (IsTileDepotType(tile, TRANSPORT_RAIL) && (exitdir != ReverseDiagdir(GetDepotDirection(tile, TRANSPORT_RAIL)))) + return; + }; + + res = PBSTileReserved(tile); + res |= res << 8; + res &= TrackdirReachesTrackdirs(trackdir); + trackdir = FindFirstBit2x64(res); + + } while (res != 0); +}; + +bool PBSIsPbsSignal(TileIndex tile, Trackdir trackdir) +{ + assert(IsValidTile(tile)); + assert(IsValidTrackdir(trackdir)); + + if (!_patches.new_pathfinding_all) + return false; + + if (!IsTileType(tile, MP_RAILWAY)) + return false; + + if (GetRailTileType(tile) != RAIL_TYPE_SIGNALS) + return false; + + if (!HasSignalOnTrackdir(tile, trackdir)) + return false; + + if (GetSignalType(tile, TrackdirToTrack(trackdir)) == 4) + return true; + else + return false; +}; + +typedef struct SetSignalsDataPbs { + int cur; + + // these are used to keep track of the signals. + byte bit[NUM_SSD_ENTRY]; + TileIndex tile[NUM_SSD_ENTRY]; +} SetSignalsDataPbs; + +// This function stores the signals inside the SetSignalsDataPbs struct, passed as callback to FollowTrack() in the PBSIsPbsSegment() function below +static bool SetSignalsEnumProcPBS(uint tile, SetSignalsDataPbs *ssd, int trackdir, uint length, byte *state) +{ + // the tile has signals? + if (IsTileType(tile, MP_RAILWAY)) { + if (HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) { + + if (ssd->cur != NUM_SSD_ENTRY) { + ssd->tile[ssd->cur] = tile; // remember the tile index + ssd->bit[ssd->cur] = TrackdirToTrack(trackdir); // and the controlling bit number + ssd->cur++; + } + return true; + } else if (IsTileDepotType(tile, TRANSPORT_RAIL)) + return true; // don't look further if the tile is a depot + } + return false; +} + +bool PBSIsPbsDepot(uint tile) +{ + SetSignalsDataPbs ssd; + bool result = false; + DiagDirection direction = GetDepotDirection(tile,TRANSPORT_RAIL); + int i; + + ssd.cur = 0; + + FollowTrack(tile, 0xC000 | TRANSPORT_RAIL, direction, (TPFEnumProc*)SetSignalsEnumProcPBS, NULL, &ssd); + for(i=0; i!=ssd.cur; i++) { + uint tile = ssd.tile[i]; + byte bit = ssd.bit[i]; + if (!PBSIsPbsSignal(tile, bit) && !PBSIsPbsSignal(tile, bit | 8)) + return false; + result = true; + }; + + return result; +} diff --git a/pbs.h b/pbs.h new file mode 100644 index 0000000000..398bd73d9e --- /dev/null +++ b/pbs.h @@ -0,0 +1,82 @@ +#ifndef PBS_H +#define PBS_H + +/** @file pbs.h Path-Based-Signalling header file + * @see pbs.c */ + +#include "vehicle.h" +#include "tile.h" +#include "map.h" +#include "rail.h" + +/** + * constants used for pbs_mode argument of npf-functions + */ +enum pbs_modes { + PBS_MODE_NONE = 0, // no pbs + PBS_MODE_GREEN = 1, // look for green exit signal from pbs block + PBS_MODE_ANY = 2, // look for any exit signal from block +}; + +/** + * constants used for v->u.rail.pbs_status + */ +enum PBSStatus { + PBS_STAT_NONE = 0, + PBS_STAT_HAS_PATH = 1, + PBS_STAT_NEED_PATH = 2, +}; + + +void PBSReserveTrack(TileIndex tile, Track track); +/**< + * Marks a track as reserved. + * @param tile The tile of the track. + * @param track The track to reserve, valid values 0-5. + */ + +byte PBSTileReserved(TileIndex tile); +/**< + * Check which tracks are reserved on a tile. + * @param tile The tile which you want to check. + * @return The tracks reserved on that tile, each of the bits 0-5 is set when the corresponding track is reserved. + */ + +uint16 PBSTileUnavail(TileIndex tile); +/**< + * Check which trackdirs are unavailable due to reserved tracks on a tile. + * @param tile The tile which you want to check. + * @return The tracks reserved on that tile, each of the bits 0-5,8-13 is set when the corresponding trackdir is unavailable. + */ + +void PBSClearTrack(TileIndex tile, Track track); +/**< + * Unreserves a track. + * @param tile The tile of the track. + * @param track The track to unreserve, valid values 0-5. + */ + +void PBSClearPath(TileIndex tile, Trackdir trackdir); +/**< + * Follows a planned(reserved) path, and unreserves the tracks. + * @param tile The tile on which the path starts + * @param trackdir The trackdirection in which the path starts + */ + +bool PBSIsPbsSignal(TileIndex tile, Trackdir trackdir); +/**< + * Checks if there are pbs signals on a track. + * @param tile The tile you want to check + * @param trackdir The trackdir you want to check + * @return True when there are pbs signals on that tile + */ + +bool PBSIsPbsDepot(uint tile); +/**< + * Checks if a depot is inside a pbs block. + * Tis means that the block it is in needs to have at least 1 signal, and that all signals in it need to be pbs signals. + * @param tile The depot tile to check + * @return True when the depot is inside a pbs block. + */ + +#endif diff --git a/rail.c b/rail.c index 670b8345b5..ecd6be9375 100644 --- a/rail.c +++ b/rail.c @@ -75,6 +75,15 @@ const Trackdir _track_exitdir_to_trackdir[][DIAGDIR_END] = { {TRACKDIR_RIGHT_N, TRACKDIR_RIGHT_S, INVALID_TRACKDIR, INVALID_TRACKDIR} }; +const Trackdir _track_enterdir_to_trackdir[][DIAGDIR_END] = { // TODO: replace magic with enums + {TRACKDIR_DIAG1_NE, INVALID_TRACKDIR, TRACKDIR_DIAG1_SW, INVALID_TRACKDIR}, + {INVALID_TRACKDIR, TRACKDIR_DIAG2_SE, INVALID_TRACKDIR, TRACKDIR_DIAG2_NW}, + {INVALID_TRACKDIR, TRACKDIR_UPPER_E, TRACKDIR_UPPER_W, INVALID_TRACKDIR}, + {TRACKDIR_LOWER_E, INVALID_TRACKDIR, INVALID_TRACKDIR, TRACKDIR_LOWER_W}, + {TRACKDIR_LEFT_N, TRACKDIR_LEFT_S, INVALID_TRACKDIR, INVALID_TRACKDIR}, + {INVALID_TRACKDIR, INVALID_TRACKDIR, TRACKDIR_RIGHT_S, TRACKDIR_RIGHT_N} +}; + const Trackdir _track_direction_to_trackdir[][DIR_END] = { {INVALID_TRACKDIR, TRACKDIR_DIAG1_NE, INVALID_TRACKDIR, INVALID_TRACKDIR, INVALID_TRACKDIR, TRACKDIR_DIAG1_SW, INVALID_TRACKDIR, INVALID_TRACKDIR}, {INVALID_TRACKDIR, INVALID_TRACKDIR, INVALID_TRACKDIR, TRACKDIR_DIAG2_SE, INVALID_TRACKDIR, INVALID_TRACKDIR, INVALID_TRACKDIR, TRACKDIR_DIAG2_NW}, diff --git a/rail.h b/rail.h index 323e0f9832..d8611787ec 100644 --- a/rail.h +++ b/rail.h @@ -43,8 +43,9 @@ typedef enum SignalTypes { SIGTYPE_ENTRY = 1, // presignal block entry SIGTYPE_EXIT = 2, // presignal block exit SIGTYPE_COMBO = 3, // presignal inter-block + SIGTYPE_PBS = 4, // pbs signal SIGTYPE_END, - SIGTYPE_MASK = 3, + SIGTYPE_MASK = 7, } SignalType; typedef enum RailTypes { @@ -134,6 +135,11 @@ typedef enum SignalStates { SIGNAL_STATE_GREEN = 1, } SignalState; +// these are the maximums used for updating signal blocks, and checking if a depot is in a pbs block +enum { + NUM_SSD_ENTRY = 256, // max amount of blocks + NUM_SSD_STACK = 32 ,// max amount of blocks to check recursively +}; /** * Maps a Trackdir to the corresponding TrackdirBits value @@ -316,6 +322,15 @@ static inline Trackdir TrackExitdirToTrackdir(Track track, DiagDirection diagdir return _track_exitdir_to_trackdir[track][diagdir]; } +/** + * Maps a track and an (4-way) dir to the trackdir that represents the track + * with the exit in the given direction. + */ +static inline Trackdir TrackEnterdirToTrackdir(Track track, DiagDirection diagdir) { + extern const Trackdir _track_enterdir_to_trackdir[TRACK_END][DIAGDIR_END]; + return _track_enterdir_to_trackdir[track][diagdir]; +} + /** * Maps a track and a full (8-way) direction to the trackdir that represents * the track running in the given direction. @@ -359,6 +374,14 @@ static inline DiagDirection ReverseDiagdir(DiagDirection diagdir) { return _reverse_diagdir[diagdir]; } +/** + * Maps a (8-way) direction to a (4-way) DiagDirection + */ +static inline DiagDirection DirToDiagdir(Direction dir) { + assert(dir < DIR_END); + return (DiagDirection)(dir >> 1); +} + /* Checks if a given Track is diagonal */ static inline bool IsDiagonalTrack(Track track) { return (track == TRACK_DIAG1) || (track == TRACK_DIAG2); } diff --git a/rail_cmd.c b/rail_cmd.c index a1340428b9..796ba88fdd 100644 --- a/rail_cmd.c +++ b/rail_cmd.c @@ -15,7 +15,9 @@ #include "station.h" #include "sprite.h" #include "depot.h" +#include "pbs.h" #include "waypoint.h" +#include "npf.h" #include "rail.h" extern uint16 _custom_sprites_base; @@ -757,10 +759,10 @@ int32 CmdBuildSingleSignal(int x, int y, uint32 flags, uint32 p1, uint32 p2) _map3_lo[tile] |= SignalOnTrack(track); } else { if (pre_signal) { - // cycle between normal -> pre -> exit -> combo -> ... - byte type = (GetSignalType(tile, track) + 1) & 0x03; - _map3_hi[tile] &= ~0x03; - _map3_hi[tile] |= type; + // cycle between normal -> pre -> exit -> combo -> pbs ->... + byte type = ((GetSignalType(tile, track) + 1) % 5); + _map3_hi[tile] &= ~0x07; + _map3_hi[tile] |= type ; } else { // cycle between two-way -> one-way -> one-way -> ... /* TODO: Rewrite switch into something more general */ @@ -1123,21 +1125,24 @@ static const SpriteID _signal_base_sprites[32] = { 0x1333, 0x1343, - 0x0, //PBS place, light signal - 0x0, //reserved for future use - 0x0, //reserved for future use - 0x0, //reserved for future use + // pbs signals + 0x1393, + 0x13A3, // not used (yet?) + 0x13B3, // not used (yet?) + 0x13C3, // not used (yet?) - // use semaphores instead of signals? + // semaphores 0x1353, 0x1363, 0x1373, 0x1383, - 0x0, //PBS place, semaphore - 0x0, //reserved for future use - 0x0, //reserved for future use - 0x0, //reserved for future use + // pbs semaphores + 0x13D3, + 0x13E3, // not used (yet?) + 0x13F3, // not used (yet?) + 0x1403, // not used (yet?) + // mirrored versions 0x4FB, @@ -1145,20 +1150,23 @@ static const SpriteID _signal_base_sprites[32] = { 0x1333, 0x1343, - 0x0, //PBS place, semaphore - 0x0, //reserved for future use - 0x0, //reserved for future use - 0x0, //reserved for future use + // pbs signals + 0x1393, + 0x13A3, // not used (yet?) + 0x13B3, // not used (yet?) + 0x13C3, // not used (yet?) - 0x13C6, - 0x13D6, - 0x13E6, - 0x13F6, + // semaphores + 0x1446, + 0x1456, + 0x1466, + 0x1476, - 0x0, //PBS place, semaphore - 0x0, //reserved for future use - 0x0, //reserved for future use - 0x0, //reserved for future use + // pbs semaphores + 0x14C6, + 0x14D6, // not used (yet?) + 0x14E6, // not used (yet?) + 0x14F6, // not used (yet?) }; // used to determine the side of the road for the signal @@ -1466,6 +1474,16 @@ static void DrawTile_Track(TileInfo *ti) if (m5 & TRACK_BIT_RIGHT) DrawGroundSprite(TrackSet[SINGLE_EAST]); } + if (_debug_pbs_level >= 1) { + byte pbs = PBSTileReserved(ti->tile); + if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite(TrackSet[SINGLE_Y] | PALETTE_CRASH); + if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite(TrackSet[SINGLE_X] | PALETTE_CRASH); + if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(TrackSet[SINGLE_NORTH] | PALETTE_CRASH); + if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(TrackSet[SINGLE_SOUTH] | PALETTE_CRASH); + if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(TrackSet[SINGLE_WEST] | PALETTE_CRASH); + if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(TrackSet[SINGLE_EAST] | PALETTE_CRASH); + } + if (_display_opt & DO_FULL_DETAIL) { _detailed_track_proc[_map2[ti->tile] & RAIL_MAP2LO_GROUND_MASK](ti); } @@ -1575,6 +1593,16 @@ static void DrawTile_Track(TileInfo *ti) DrawGroundSprite(image); + if (_debug_pbs_level >= 1) { + byte pbs = PBSTileReserved(ti->tile); + if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite((0x3ED + tracktype_offs) | PALETTE_CRASH); + if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite((0x3EE + tracktype_offs) | PALETTE_CRASH); + if (pbs & TRACK_BIT_UPPER) DrawGroundSprite((0x3EF + tracktype_offs) | PALETTE_CRASH); + if (pbs & TRACK_BIT_LOWER) DrawGroundSprite((0x3F0 + tracktype_offs) | PALETTE_CRASH); + if (pbs & TRACK_BIT_LEFT) DrawGroundSprite((0x3F2 + tracktype_offs) | PALETTE_CRASH); + if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite((0x3F1 + tracktype_offs) | PALETTE_CRASH); + } + while ((image=drss->image) != 0) { DrawSpecialBuilding(image, type < 4 ? tracktype_offs : 0, ti, drss->subcoord_x, drss->subcoord_y, 0, @@ -1611,15 +1639,17 @@ void DrawTrainDepotSprite(int x, int y, int image, int railtype) } } -#define NUM_SSD_ENTRY 256 -#define NUM_SSD_STACK 32 - typedef struct SetSignalsData { int cur; int cur_stack; bool stop; bool has_presignal; + bool has_pbssignal; + // lowest 2 bits = amount of pbs signals in the block, clamped at 2 + // bit 2 = there is a pbs entry signal in this block + // bit 3 = there is a pbs exit signal in this block + // presignal info int presignal_exits; int presignal_exits_free; @@ -1628,6 +1658,10 @@ typedef struct SetSignalsData { byte bit[NUM_SSD_ENTRY]; TileIndex tile[NUM_SSD_ENTRY]; + int pbs_cur; + // these are used to keep track of all signals in the block + TileIndex pbs_tile[NUM_SSD_ENTRY]; + // these are used to keep track of the stack that modifies presignals recursively TileIndex next_tile[NUM_SSD_STACK]; byte next_dir[NUM_SSD_STACK]; @@ -1647,15 +1681,34 @@ static bool SetSignalsEnumProc(TileIndex tile, SetSignalsData *ssd, int track, u ssd->cur++; } + if (PBSIsPbsSignal(tile, ReverseTrackdir(track))) + SETBIT(ssd->has_pbssignal, 2); + // remember if this block has a presignal. ssd->has_presignal |= (_map3_hi[tile]&1); } - // is this an exit signal that points out from the segment? - if ((_map3_hi[tile]&2) && _map3_lo[tile]&_signals_table_other[track]) { - ssd->presignal_exits++; - if ((_map2[tile]&_signals_table_other[track]) != 0) - ssd->presignal_exits_free++; + if (PBSIsPbsSignal(tile, ReverseTrackdir(track)) || PBSIsPbsSignal(tile, track)) { + byte num = ssd->has_pbssignal & 3; + num = clamp(num + 1, 0, 2); + ssd->has_pbssignal &= ~3; + ssd->has_pbssignal |= num; + } + + if ((_map3_lo[tile] & _signals_table_both[track]) != 0) { + ssd->pbs_tile[ssd->pbs_cur] = tile; // remember the tile index + ssd->pbs_cur++; + } + + if (_map3_lo[tile]&_signals_table_other[track]) { + if (_map3_hi[tile]&2) { + // this is an exit signal that points out from the segment + ssd->presignal_exits++; + if ((_map2[tile]&_signals_table_other[track]) != 0) + ssd->presignal_exits_free++; + } + if (PBSIsPbsSignal(tile, track)) + SETBIT(ssd->has_pbssignal, 3); } return true; @@ -1792,6 +1845,15 @@ static void ChangeSignalStates(SetSignalsData *ssd) // there is at least one green exit signal OR // there are no exit signals in the segment + // convert the block to pbs, if needed + if (_patches.auto_pbs_placement && !(ssd->stop) && (ssd->has_pbssignal == 0xE) && !ssd->has_presignal && (ssd->presignal_exits == 0)) // 0xE means at least 2 pbs signals, and at least 1 entry and 1 exit, see comments ssd->has_pbssignal + for(i=0; i!=ssd->pbs_cur; i++) { + TileIndex tile = ssd->pbs_tile[i]; + _map3_hi[tile] &= ~0x07; + _map3_hi[tile] |= 0x04; + MarkTileDirtyByTile(tile); + }; + // then mark the signals in the segment accordingly for(i=0; i!=ssd->cur; i++) { TileIndex tile = ssd->tile[i]; @@ -1852,8 +1914,9 @@ bool UpdateSignalsOnSegment(TileIndex tile, byte direction) for(;;) { // go through one segment and update all signals pointing into that segment. - ssd.cur = ssd.presignal_exits = ssd.presignal_exits_free = 0; + ssd.cur = ssd.pbs_cur = ssd.presignal_exits = ssd.presignal_exits_free = 0; ssd.has_presignal = false; + ssd.has_pbssignal = false; FollowTrack(tile, 0xC000 | TRANSPORT_RAIL, direction, (TPFEnumProc*)SetSignalsEnumProc, SetSignalsAfterProc, &ssd); ChangeSignalStates(&ssd); @@ -2162,6 +2225,8 @@ static uint32 VehicleEnter_Track(Vehicle *v, TileIndex tile, int x, int y) } else if (_fractcoords_enter[dir] == fract_coord) { if (_enter_directions[dir] == v->direction) { /* enter the depot */ + if (v->next == NULL) + PBSClearTrack(v->tile, FIND_FIRST_BIT(v->u.rail.track)); v->u.rail.track = 0x80, v->vehstatus |= VS_HIDDEN; /* hide it */ v->direction ^= 4; diff --git a/road_cmd.c b/road_cmd.c index 2b954ed232..c44b38d8ac 100644 --- a/road_cmd.c +++ b/road_cmd.c @@ -1,5 +1,6 @@ #include "stdafx.h" #include "openttd.h" +#include "table/sprites.h" #include "table/strings.h" #include "map.h" #include "tile.h" @@ -11,6 +12,8 @@ #include "gfx.h" #include "sound.h" #include "depot.h" +#include "pbs.h" +#include "debug.h" /* When true, GetTrackStatus for roads will treat roads under reconstruction * as normal roads instead of impassable. This is used when detecting whether @@ -246,6 +249,7 @@ int32 CmdRemoveRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2) cost = _price.remove_road * 2; if (flags & DC_EXEC) { + byte pbs_track = PBSTileReserved(tile); ChangeTownRating(t, -road_remove_cost[(byte)edge_road], RATING_ROAD_MINIMUM); ModifyTile(tile, @@ -254,6 +258,8 @@ int32 CmdRemoveRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2) _map3_hi[tile] & 0xF, /* map3_lo */ c /* map5 */ ); + if (pbs_track != 0) + PBSReserveTrack(tile, FIND_FIRST_BIT(pbs_track)); } return cost; } else @@ -396,6 +402,7 @@ int32 CmdBuildRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2) goto do_clear; if (flags & DC_EXEC) { + byte pbs_track = PBSTileReserved(tile); ModifyTile(tile, MP_SETTYPE(MP_STREET) | MP_MAP2 | MP_MAP3LO | MP_MAP3HI | MP_MAP5, @@ -404,6 +411,8 @@ int32 CmdBuildRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2) _map3_lo[tile] & 0xF, /* map3_hi */ m5 /* map5 */ ); + if (pbs_track != 0) + PBSReserveTrack(tile, FIND_FIRST_BIT(pbs_track)); } return _price.build_road * 2; } else if (ti.type == MP_TUNNELBRIDGE) { @@ -826,6 +835,17 @@ static void DrawTile_Road(TileInfo *ti) } DrawGroundSprite(image + (_map3_hi[ti->tile] & 0xF) * 12); + + if (_debug_pbs_level >= 1) { + byte pbs = PBSTileReserved(ti->tile); + if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite(0x3ED | PALETTE_CRASH); + if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite(0x3EE | PALETTE_CRASH); + if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(0x3EF | PALETTE_CRASH); + if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(0x3F0 | PALETTE_CRASH); + if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(0x3F2 | PALETTE_CRASH); + if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(0x3F1 | PALETTE_CRASH); + } + } else { uint32 ormod; int player; diff --git a/roadveh_cmd.c b/roadveh_cmd.c index 1796d1a32d..22f3cf0a47 100644 --- a/roadveh_cmd.c +++ b/roadveh_cmd.c @@ -1086,7 +1086,7 @@ static int RoadFindPathToDest(Vehicle *v, TileIndex tile, int enterdir) trackdir = DiagdirToDiagTrackdir(enterdir); //debug("Finding path. Enterdir: %d, Trackdir: %d", enterdir, trackdir); - ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE); + ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE, PBS_MODE_NONE); if (ftd.best_trackdir == 0xff) { /* We are already at our target. Just do something */ //TODO: maybe display error? @@ -1163,7 +1163,7 @@ static uint RoadFindPathToStation(const Vehicle *v, TileIndex tile) fstd.dest_coords = tile; fstd.station_index = -1; // indicates that the destination is a tile, not a station - return NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE).best_path_dist; + return NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE, PBS_MODE_NONE).best_path_dist; } typedef struct RoadDriveEntry { diff --git a/settings.c b/settings.c index 03d0abbe47..f6dfdf3df5 100644 --- a/settings.c +++ b/settings.c @@ -845,6 +845,7 @@ static const SettingDesc patch_player_settings[] = { // Non-static, needed in network_server.c const SettingDesc patch_settings[] = { {"build_on_slopes", SDT_BOOL, (void*)true, &_patches.build_on_slopes, NULL}, + {"auto_pbs_placement", SDT_BOOL, (void*)true, &_patches.auto_pbs_placement, NULL}, {"mammoth_trains", SDT_BOOL, (void*)true, &_patches.mammoth_trains, NULL}, {"join_stations", SDT_BOOL, (void*)true, &_patches.join_stations, NULL}, {"station_spread", SDT_UINT8, (void*)12, &_patches.station_spread, NULL}, diff --git a/settings_gui.c b/settings_gui.c index 4583e6e069..14458be3d3 100644 --- a/settings_gui.c +++ b/settings_gui.c @@ -669,6 +669,7 @@ static const PatchEntry _patches_construction[] = { {PE_BOOL, 0, STR_CONFIG_PATCHES_SMALL_AIRPORTS, "always_small_airport", &_patches.always_small_airport, 0, 0, 0, NULL}, {PE_UINT8, PF_PLAYERBASED, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY, "drag_signals_density", &_patches.drag_signals_density, 1, 20, 1, NULL}, + {PE_BOOL, 0, STR_CONFIG_AUTO_PBS_PLACEMENT, "auto_pbs_placement", &_patches.auto_pbs_placement, 1, 20, 1, NULL}, }; diff --git a/ship_cmd.c b/ship_cmd.c index 08c6528516..78f7959b09 100644 --- a/ship_cmd.c +++ b/ship_cmd.c @@ -565,7 +565,7 @@ static int ChooseShipTrack(Vehicle *v, TileIndex tile, int enterdir, uint tracks NPFFillWithOrderData(&fstd, v); - ftd = NPFRouteToStationOrTile(src_tile, trackdir, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE); + ftd = NPFRouteToStationOrTile(src_tile, trackdir, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE, PBS_MODE_NONE); if (ftd.best_trackdir != 0xff) /* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains diff --git a/station_cmd.c b/station_cmd.c index 9e5e6ffc42..edc9ccdfa8 100644 --- a/station_cmd.c +++ b/station_cmd.c @@ -19,6 +19,7 @@ #include "airport.h" #include "sprite.h" #include "depot.h" +#include "pbs.h" enum { /* Max stations: 64000 (64 * 1000) */ @@ -2120,6 +2121,7 @@ static void DrawTile_Station(TileInfo *ti) const DrawTileSeqStruct *dtss; const DrawTileSprites *t = NULL; byte railtype = _map3_lo[ti->tile] & 0xF; + int type_offset; uint32 relocation = 0; { @@ -2154,15 +2156,27 @@ static void DrawTile_Station(TileInfo *ti) if (image & 0x8000) image |= image_or_modificator; + // For custom sprites, there's no railtype-based pitching. + type_offset = railtype * ((image & 0x3FFF) < _custom_sprites_base ? TRACKTYPE_SPRITE_PITCH : 1); + // station_land array has been increased from 82 elements to 114 // but this is something else. If AI builds station with 114 it looks all weird - image += railtype * ((image & 0x3FFF) < _custom_sprites_base ? TRACKTYPE_SPRITE_PITCH : 1); + image += type_offset; DrawGroundSprite(image); + if (_debug_pbs_level >= 1) { + byte pbs = PBSTileReserved(ti->tile); + if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite((0x3ED + type_offset) | PALETTE_CRASH); + if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite((0x3EE + type_offset) | PALETTE_CRASH); + if (pbs & TRACK_BIT_UPPER) DrawGroundSprite((0x3EF + type_offset) | PALETTE_CRASH); + if (pbs & TRACK_BIT_LOWER) DrawGroundSprite((0x3F0 + type_offset) | PALETTE_CRASH); + if (pbs & TRACK_BIT_LEFT) DrawGroundSprite((0x3F2 + type_offset) | PALETTE_CRASH); + if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite((0x3F1 + type_offset) | PALETTE_CRASH); + } + foreach_draw_tile_seq(dtss, t->seq) { - image = dtss->image + relocation; - // For custom sprites, there's no railtype-based pitching. - image += railtype * ((image & 0x3FFF) < _custom_sprites_base ? TRACKTYPE_SPRITE_PITCH : 0); + image = dtss->image + relocation; + image += type_offset; if (_display_opt & DO_TRANS_BUILDINGS) { image = (image & 0x3FFF) | 0x03224000; } else { diff --git a/table/files.h b/table/files.h index 7b3b91c894..7b284295a4 100644 --- a/table/files.h +++ b/table/files.h @@ -26,7 +26,7 @@ FileList files_dos = { { "TRG1.GRF", {0x93,0x11,0x67,0x62,0x80,0xe5,0xb1,0x40,0x77,0xa8,0xee,0x41,0xc1,0xb4,0x21,0x92} }, // 0 - 4792 inclusive { "TRGI.GRF", {0xda,0x6a,0x6c,0x9d,0xcc,0x45,0x1e,0xec,0x88,0xd7,0x92,0x11,0x43,0x7b,0x76,0xa8} }, // 4793 - 4889 inclusive { "dosdummy.grf", {0x07,0x01,0xe6,0xc4,0x07,0x6a,0x5b,0xc3,0xf4,0x9f,0x01,0xad,0x21,0x6c,0xa0,0xc2} }, // 4890 - 4895 inclusive - { "signalsw.grf", {0x76,0x1b,0x42,0x25,0x44,0x0d,0x21,0xc7,0xe0,0xb4,0x25,0xd8,0x2f,0xc8,0x52,0x38} }, // 4896 - 5125 inclusive + { "nsignalsw.grf", {0x65,0xb9,0xd7,0x30,0x56,0x06,0xcc,0x9e,0x27,0x57,0xc8,0xe4,0x9b,0xb3,0x66,0x81} }, // 4896 - 5381 inclusive { NULL, { 0 } } }, { { "TRGC.GRF", {0xed,0x44,0x66,0x37,0xe0,0x34,0x10,0x4c,0x55,0x59,0xb3,0x2c,0x18,0xaf,0xe7,0x8d} }, @@ -39,7 +39,7 @@ FileList files_win = { { { "TRG1R.GRF", {0xb0,0x4c,0xe5,0x93,0xd8,0xc5,0x01,0x6e,0x07,0x47,0x3a,0x74,0x3d,0x7d,0x33,0x58} }, // 0 - 4792 inclusive { "TRGIR.GRF", {0x0c,0x24,0x84,0xff,0x6b,0xe4,0x9f,0xc6,0x3a,0x83,0xbe,0x6a,0xb5,0xc3,0x8f,0x32} }, // 4793 - 4895 inclusive - { "signalsw.grf", {0x76,0x1b,0x42,0x25,0x44,0x0d,0x21,0xc7,0xe0,0xb4,0x25,0xd8,0x2f,0xc8,0x52,0x38} }, // 4896 - 5125 inclusive + { "nsignalsw.grf", {0x65,0xb9,0xd7,0x30,0x56,0x06,0xcc,0x9e,0x27,0x57,0xc8,0xe4,0x9b,0xb3,0x66,0x81} }, // 4896 - 5381 inclusive { NULL, { 0 } }, { NULL, { 0 } } }, diff --git a/table/sprites.h b/table/sprites.h index 91b350de6a..0c46dcdcc3 100644 --- a/table/sprites.h +++ b/table/sprites.h @@ -42,7 +42,7 @@ enum Sprites { SPR_ASCII_SPACE_BIG = 450, /* Extra graphic spritenumbers */ - SPR_CANALS_BASE = 5126, + SPR_CANALS_BASE = 5382, SPR_SLOPES_BASE = SPR_CANALS_BASE + 70, SPR_AUTORAIL_BASE = SPR_SLOPES_BASE + 78, SPR_OPENTTD_BASE = SPR_AUTORAIL_BASE + 55, // can be lowered once autorail.grf is finalized diff --git a/train_cmd.c b/train_cmd.c index d62e8251ba..d8ac7a051d 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -15,6 +15,7 @@ #include "player.h" #include "sound.h" #include "depot.h" +#include "debug.h" #include "waypoint.h" #include "vehicle_gui.h" @@ -1296,14 +1297,86 @@ static void AdvanceWagons(Vehicle *v, bool before) } } +TileIndex GetVehicleTileOutOfTunnel(const Vehicle *v, bool reverse) +{ + TileIndex tile; + byte direction = (!reverse) ? DirToDiagdir(v->direction) : ReverseDiagdir(v->direction >> 1); + TileIndexDiff delta = TileOffsByDir(direction); + + if (v->u.rail.track != 0x40) + return v->tile; + + for (tile = v->tile;; tile += delta) { + if (IsTileType(tile, MP_TUNNELBRIDGE) && + (_map5[tile] & 0xF3) != (direction) && + GetTileZ(tile) == v->z_pos) + break; + } + return tile; + +}; + static void ReverseTrainDirection(Vehicle *v) { int l = 0, r = -1; Vehicle *u; + TileIndex tile; + byte trackdir; + + u = GetLastVehicleInChain(v); + tile = GetVehicleTileOutOfTunnel(u, false); + trackdir = ReverseTrackdir(GetVehicleTrackdir(u)); + + if (PBSTileReserved(tile) & (1 << (trackdir&7))) { + NPFFindStationOrTileData fstd; + NPFFoundTargetData ftd; + + NPFFillWithOrderData(&fstd, v); + + tile = GetVehicleTileOutOfTunnel(u, true); + + DEBUG(pbs, 2) ("pbs: (%i) choose reverse (RV), tile:%x, trackdir:%i",v->unitnumber, u->tile, trackdir); + ftd = NPFRouteToStationOrTile(tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_ANY); + + if (ftd.best_trackdir == 0xFF) { + DEBUG(pbs, 0) ("pbs: (%i) no nodes encountered (RV)", v->unitnumber); + CLRBIT(v->u.rail.flags, VRF_REVERSING); + return; + } + + // we found a way out of the pbs block + if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) { + if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED)) { + CLRBIT(v->u.rail.flags, VRF_REVERSING); + return; + } + } + } + + tile = GetVehicleTileOutOfTunnel(v, false); + trackdir = GetVehicleTrackdir(v); + + if (v->u.rail.pbs_status == PBS_STAT_HAS_PATH) { + byte trackdir = GetVehicleTrackdir(v); + TileIndex tile = AddTileIndexDiffCWrap(v->tile, TileIndexDiffCByDir(TrackdirToExitdir(trackdir))); + uint32 ts; + assert(tile != INVALID_TILE); + ts = GetTileTrackStatus(tile, TRANSPORT_RAIL); + ts &= TrackdirReachesTrackdirs(trackdir); + assert(ts != 0 && KillFirstBit2x64(ts) == 0); + trackdir = FindFirstBit2x64(ts); + PBSClearPath(tile, trackdir); + v->u.rail.pbs_status = PBS_STAT_NONE; + } else if (PBSTileReserved(tile) & (1 << (trackdir&7))) { + PBSClearPath(tile, trackdir); + if (v->u.rail.track != 0x40) + PBSReserveTrack(tile, trackdir & 7); + }; if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); + /* Check if we were approaching a rail/road-crossing */ { TileIndex tile = v->tile; @@ -1748,12 +1821,35 @@ static bool CheckTrainStayInDepot(Vehicle *v) v->load_unload_time_rem = 0; + if (PBSIsPbsDepot(v->tile)) { + byte trackdir = GetVehicleTrackdir(v); + NPFFindStationOrTileData fstd; + NPFFoundTargetData ftd; + + if (PBSTileUnavail(v->tile) & (1 << trackdir)) + return true; + + NPFFillWithOrderData(&fstd, v); + + DEBUG(pbs, 2) ("pbs: (%i) choose depot (DP), tile:%x, trackdir:%i",v->unitnumber, v->tile, trackdir); + ftd = NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_GREEN); + + // we found a way out of the pbs block + if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) { + if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED) || NPFGetFlag(&ftd.node, NPF_FLAG_PBS_RED)) + return true; + else + goto green; + } + } + + if (UpdateSignalsOnSegment(v->tile, v->direction)) { InvalidateWindowClasses(WC_TRAINS_LIST); return true; } } - +green: VehicleServiceInDepot(v); InvalidateWindowClasses(WC_TRAINS_LIST); TrainPlayLeaveStationSound(v); @@ -1904,13 +2000,26 @@ static byte ChooseTrainTrack(Vehicle *v, TileIndex tile, int enterdir, TrackdirB NPFFindStationOrTileData fstd; NPFFoundTargetData ftd; Trackdir trackdir; + uint16 pbs_tracks; NPFFillWithOrderData(&fstd, v); /* The enterdir for the new tile, is the exitdir for the old tile */ trackdir = GetVehicleTrackdir(v); assert(trackdir != 0xff); - ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype); + pbs_tracks = PBSTileReserved(tile); + pbs_tracks |= pbs_tracks << 8; + pbs_tracks &= TrackdirReachesTrackdirs(trackdir); + if (pbs_tracks || (v->u.rail.pbs_status == PBS_STAT_NEED_PATH)) { + DEBUG(pbs, 2) ("pbs: (%i) choosefromblock, tile_org:%x tile_dst:%x trackdir:%i pbs_tracks:%i",v->unitnumber, tile,tile - TileOffsByDir(enterdir), trackdir, pbs_tracks); + // clear the currently planned path + if (v->u.rail.pbs_status != PBS_STAT_NEED_PATH) PBSClearPath(tile, FindFirstBit2x64(pbs_tracks)); + + // try to find a route to a green exit signal + ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_ANY); + + } else + ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_NONE); if (ftd.best_trackdir == 0xff) { /* We are already at our target. Just do something */ @@ -1923,7 +2032,7 @@ static byte ChooseTrainTrack(Vehicle *v, TileIndex tile, int enterdir, TrackdirB we did not find our target, but ftd.best_trackdir contains the direction leading to the tile closest to our target. */ /* Discard enterdir information, making it a normal track */ - best_track = ftd.best_trackdir & 7; /* TODO: Wrapper function? */ + best_track = TrackdirToTrack(ftd.best_trackdir); } } else { @@ -2048,7 +2157,8 @@ static bool CheckReverseTrain(Vehicle *v) assert(trackdir != 0xff); assert(trackdir_rev != 0xff); - ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype); + ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_NONE); + if (ftd.best_bird_dist != 0) { /* We didn't find anything, just keep on going straight ahead */ reverse_best = false; @@ -2644,7 +2754,7 @@ static void TrainController(Vehicle *v) } else { /* is not inside depot */ - if (!TrainCheckIfLineEnds(v)) + if ((prev == NULL) && (!TrainCheckIfLineEnds(v))) return; r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); @@ -2699,11 +2809,54 @@ static void TrainController(Vehicle *v) } if (prev == NULL) { + byte trackdir; /* Currently the locomotive is active. Determine which one of the * available tracks to choose */ chosen_track = 1 << ChooseTrainTrack(v, gp.new_tile, enterdir, bits); assert(chosen_track & tracks); + trackdir = TrackEnterdirToTrackdir(FIND_FIRST_BIT(chosen_track), enterdir); + assert(trackdir != 0xff); + + if (PBSIsPbsSignal(gp.new_tile,trackdir)) { + // encountered a pbs signal, and possible a pbs block + DEBUG(pbs, 3) ("pbs: (%i) arrive AT signal, tile:%x pbs_stat:%i",v->unitnumber, gp.new_tile, v->u.rail.pbs_status); + + if (v->u.rail.pbs_status == PBS_STAT_NONE) { + // we havent planned a path already, so try to find one now + NPFFindStationOrTileData fstd; + NPFFoundTargetData ftd; + + NPFFillWithOrderData(&fstd, v); + + DEBUG(pbs, 2) ("pbs: (%i) choose signal (TC), tile:%x, trackdir:%i",v->unitnumber, gp.new_tile, trackdir); + ftd = NPFRouteToStationOrTile(gp.new_tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_GREEN); + + if (v->u.rail.force_proceed != 0) + goto green_light; + + if (ftd.best_trackdir == 0xFF) + goto red_light; + + // we found a way out of the pbs block + if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) { + if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED) || NPFGetFlag(&ftd.node, NPF_FLAG_PBS_RED)) + goto red_light; + else { + goto green_light; + } + + }; + + } else { + // we have already planned a path through this pbs block + // on entering the block, we reset our status + v->u.rail.pbs_status = PBS_STAT_NONE; + goto green_light; + }; + DEBUG(pbs, 3) ("pbs: (%i) no green light found, or was no pbs-block",v->unitnumber); + }; + /* Check if it's a red signal and that force proceed is not clicked. */ if ( (tracks>>16)&chosen_track && v->u.rail.force_proceed == 0) goto red_light; } else { @@ -2712,6 +2865,9 @@ static void TrainController(Vehicle *v) /* The wagon is active, simply follow the prev vehicle. */ chosen_track = (byte)(_matching_tracks[GetDirectionToVehicle(prev, gp.x, gp.y)] & bits); } +green_light: + if (v->next == NULL) + PBSClearTrack(gp.old_tile, FIND_FIRST_BIT(v->u.rail.track)); /* make sure chosen track is a valid track */ assert(chosen_track==1 || chosen_track==2 || chosen_track==4 || chosen_track==8 || chosen_track==16 || chosen_track==32); @@ -2740,12 +2896,12 @@ static void TrainController(Vehicle *v) } if (v->subtype == TS_Front_Engine) - TrainMovedChangeSignals(gp.new_tile, enterdir); + TrainMovedChangeSignals(gp.new_tile, enterdir); /* Signals can only change when the first * (above) or the last vehicle moves. */ if (v->next == NULL) - TrainMovedChangeSignals(gp.old_tile, (enterdir) ^ 2); + TrainMovedChangeSignals(gp.old_tile, (enterdir) ^ 2); if (prev == NULL) { AffectSpeedByDirChange(v, chosen_dir); @@ -2860,6 +3016,17 @@ static void DeleteLastWagon(Vehicle *v) EndVehicleMove(v); DeleteVehicle(v); + // clear up reserved pbs tracks + if (PBSTileReserved(v->tile) & v->u.rail.track) { + if (v == u) { + PBSClearPath(v->tile, FIND_FIRST_BIT(v->u.rail.track)); + PBSClearPath(v->tile, FIND_FIRST_BIT(v->u.rail.track) + 8); + }; + if (v->tile != u->tile) { + PBSClearTrack(v->tile, FIND_FIRST_BIT(v->u.rail.track)); + }; + } + if (!(v->u.rail.track & 0xC0)) SetSignalsOnBothDir(v->tile, FIND_FIRST_BIT(v->u.rail.track)); @@ -2987,6 +3154,7 @@ static bool TrainCheckIfLineEnds(Vehicle *v) uint x,y; int t; uint32 ts; + byte trackdir; if ((uint)(t=v->breakdown_ctr) > 1) { v->vehstatus |= VS_TRAIN_SLOWING; @@ -3002,6 +3170,10 @@ static bool TrainCheckIfLineEnds(Vehicle *v) if (v->u.rail.track & 0x40) return true; + // exit if inside a depot + if (v->u.rail.track & 0x80) + return true; + tile = v->tile; // tunnel entrance? @@ -3025,6 +3197,12 @@ static bool TrainCheckIfLineEnds(Vehicle *v) // determine the track status on the next tile. ts = GetTileTrackStatus(tile, TRANSPORT_RAIL) & _reachable_tracks[t]; + // if there are tracks on the new tile, pick one (trackdir will only be used when its a signal tile, in which case only 1 trackdir is accessible for us) + if (ts & TRACKDIR_BIT_MASK) + trackdir = FindFirstBit2x64(ts & TRACKDIR_BIT_MASK); + else + trackdir = INVALID_TRACKDIR; + /* Calc position within the current tile ?? */ x = v->x_pos & 0xF; y = v->y_pos & 0xF; @@ -3077,6 +3255,26 @@ static bool TrainCheckIfLineEnds(Vehicle *v) return false; } + if (v->u.rail.pbs_status == PBS_STAT_HAS_PATH) + return true; + + if ((trackdir != INVALID_TRACKDIR) && (PBSIsPbsSignal(tile,trackdir)) && !(IsTileType(v->tile, MP_STATION) && (v->current_order.station == _map2[v->tile]))) { + NPFFindStationOrTileData fstd; + NPFFoundTargetData ftd; + + NPFFillWithOrderData(&fstd, v); + + DEBUG(pbs, 2) ("pbs: (%i) choose signal (CEOL), tile:%x trackdir:%i", v->unitnumber, tile, trackdir); + ftd = NPFRouteToStationOrTile(tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_GREEN); + + if (ftd.best_trackdir != 0xFF && NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) { + if (!(NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED) || NPFGetFlag(&ftd.node, NPF_FLAG_PBS_RED))) { + v->u.rail.pbs_status = PBS_STAT_HAS_PATH; + return true; + } + }; + }; + // slow down v->vehstatus |= VS_TRAIN_SLOWING; t = _breakdown_speeds[x & 0xF]; @@ -3237,6 +3435,12 @@ static void CheckIfTrainNeedsService(Vehicle *v) Depot *depot; TrainFindDepotData tfdd; + if (PBSTileReserved(v->tile) & v->u.rail.track) + return; + + if (v->u.rail.pbs_status == PBS_STAT_HAS_PATH) + return; + if (_patches.servint_trains == 0) return; diff --git a/tunnelbridge_cmd.c b/tunnelbridge_cmd.c index 36924686c5..0ae9bb5c65 100644 --- a/tunnelbridge_cmd.c +++ b/tunnelbridge_cmd.c @@ -10,6 +10,8 @@ #include "player.h" #include "town.h" #include "sound.h" +#include "pbs.h" +#include "debug.h" extern void DrawCanalWater(TileIndex tile); @@ -770,6 +772,7 @@ static int32 DoClearBridge(TileIndex tile, uint32 flags) byte m5; uint c = tile; uint16 new_data; + byte pbs; //checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until // you have a "Poor" (0) town rating @@ -778,6 +781,7 @@ static int32 DoClearBridge(TileIndex tile, uint32 flags) do { m5 = _map5[c]; + pbs = PBSTileReserved(c); if (m5 & 0x40) { if (m5 & 0x20) { @@ -791,6 +795,9 @@ static int32 DoClearBridge(TileIndex tile, uint32 flags) SetTileType(c, new_data >> 12); _map5[c] = (byte)new_data; _map2[c] = 0; + _map3_hi[c] &= 0x0F; + if (direction ? HASBIT(pbs,0) : HASBIT(pbs,1)) + PBSReserveTrack(c, direction ? 0 : 1); MarkTileDirtyByTile(c); @@ -1144,6 +1151,16 @@ static void DrawTile_TunnelBridge(TileInfo *ti) } } } + + if (_debug_pbs_level >= 1) { + byte pbs = PBSTileReserved(ti->tile); + if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite(0x3ED | PALETTE_CRASH); + if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite(0x3EE | PALETTE_CRASH); + if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(0x3EF | PALETTE_CRASH); + if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(0x3F0 | PALETTE_CRASH); + if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(0x3F2 | PALETTE_CRASH); + if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(0x3F1 | PALETTE_CRASH); + } } static uint GetSlopeZ_TunnelBridge(TileInfo *ti) { @@ -1426,6 +1443,8 @@ static uint32 VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y return 0; } if (fc == _tunnel_fractcoord_2[dir]) { + if (v->next == NULL) + PBSClearTrack(v->tile, FIND_FIRST_BIT(v->u.rail.track)); v->tile = tile; v->u.rail.track = 0x40; v->vehstatus |= VS_HIDDEN; diff --git a/variables.h b/variables.h index 4b6d382456..5d505af416 100644 --- a/variables.h +++ b/variables.h @@ -106,6 +106,7 @@ typedef struct Patches { bool modified_catchment; //different-size catchment areas bool vehicle_speed; // show vehicle speed bool build_on_slopes; // allow building on slopes + bool auto_pbs_placement;// automatic pbs signal placement bool mammoth_trains; // allow very long trains bool join_stations; // allow joining of train stations bool full_load_any; // new full load calculation, any cargo must be full diff --git a/vehicle.c b/vehicle.c index 5e5d4de8c7..6a526f577f 100644 --- a/vehicle.c +++ b/vehicle.c @@ -1949,8 +1949,9 @@ static const SaveLoad _train_desc[] = { SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,flags), SLE_UINT8, 2, 255), SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,days_since_order_progr), SLE_UINT16, 2, 255), - // reserve extra space in savegame here. (currently 13 bytes) - SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 13, 2, 255), + SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_status), SLE_UINT8, 2, 255), + // reserve extra space in savegame here. (currently 12 bytes) + SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 12, 2, 255), SLE_END() }; diff --git a/vehicle.h b/vehicle.h index 37aaf6de28..a413426afe 100644 --- a/vehicle.h +++ b/vehicle.h @@ -68,6 +68,8 @@ typedef struct VehicleRail { byte railtype; byte flags; + + byte pbs_status; } VehicleRail; enum {