Index: docs/landscape.html
===================================================================
--- docs/landscape.html (revision 12052)
+++ docs/landscape.html (working copy)
@@ -371,30 +371,40 @@
- - m2 bit 6: Signal 0 and 1: set = semaphore signals, clear = light signals
- - m2 bit 2: Signal 2 and 3: set = semaphore signals, clear = light signals
- - m2 bits 5..4: type of signal 0 and 1 (same values as m2 bits 1..0)
- - m2 bits 1..0: type of signal 2 and 3
+
- m2 bit 7: Signal 0 and 1: set = semaphore signals, clear = light signals
+ - m2 bit 3: Signal 2 and 3: set = semaphore signals, clear = light signals
+ - m2 bits 6..4: type of signal 0 and 1 (same values as m2 bits 2..0)
+ - m2 bits 2..0: type of signal 2 and 3
- 00: |
+ 000: |
normal signals |
- 01: |
+ 001: |
pre-signals |
- 10: |
+ 010: |
exit-signals |
- 11: |
+ 011: |
combo-signals |
+
+
+ 100: |
+ pbs signals |
+
+
+
+ 101: |
+ no-entry signals |
+
@@ -402,6 +412,39 @@
- m4 bits 7..4: bit clear = signal 3..0 shows red
+ m2 bits 8..10: track reserved for pbs
+
+
+ 0 |
+ not reserved |
+
+
+ 1 |
+ X direction |
+
+
+ 2 |
+ Y direction |
+
+
+ 3 |
+ north corner (W-E) |
+
+
+ 4 |
+ south corner (W-E) |
+
+
+ 5 |
+ west corner (N-S) |
+
+
+ 6 |
+ east corner (N-S) |
+
+
+
+ m2 bit 11: opposite track is reserved, too
m5 bit 7 set, bit 6 clear: checkpoint
@@ -420,6 +463,7 @@
+ m5 bit 4: pbs reservation state
m5 bit 7 set, bit 6 set: railway depot
@@ -447,6 +491,7 @@
+ m5 bit 4: pbs reservation state
m6 bits 7..6 : Possibility of a bridge above, in the direction specified
@@ -577,6 +622,7 @@
m4 bit 5: set if crossing lights are on
m4 bits 4..0: owner of the road type 0 (normal road)
+ m5 bit 4: pbs reservation state
@@ -893,6 +939,7 @@
m6 bits 5..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy)
m6 bit 2: 1 when a drive through road stop is built over a town owned road, otherwise 0
+ m6 bit 2: pbs reservation state for railway stations
m6 bits 1..0 : Tropic zone definition
@@ -1364,6 +1411,7 @@
m3 bits 3..0: track type for railway
m3 bits 2..0: present road types for road
m4 bit 7 set = on snow or desert
+ m5 bit 4: pbs reservation state for railway
m5 bits 7 clear: tunnel entrance/exit
m5 bit 7 set: bridge ramp
Index: docs/landscape_grid.html
===================================================================
--- docs/landscape_grid.html (revision 12052)
+++ docs/landscape_grid.html (working copy)
@@ -101,7 +101,7 @@
rail with signals |
-inherit- |
-inherit- |
- OOOO OOOO OXXX OXXX |
+ OOOO XXXX XXXX XXXX |
XXXX ~~XX |
XXXX XXXX |
-inherit- |
@@ -115,8 +115,8 @@
OOOO OOOO OOOO OOOO |
OOOO ~~XX |
OOOO XXXX |
+ XXOX OXXX |
XXOO OOXX |
- XXOO OOXX |
OOOO OOOO |
@@ -126,7 +126,7 @@
XXXX XXXX XXXX XXXX |
OOOO ~~XX |
OOOO XXXX |
- XXOO OOOX |
+ XXOX OOOX |
XXOO OOXX |
OOOO OOOO |
@@ -149,7 +149,7 @@
-inherit- |
XXXX ~~XX |
OXXX XXXX |
- XXOO XXXX |
+ XXOX XXXX |
XXOO OOXX |
-inherit- |
@@ -197,7 +197,7 @@
XXXX ~~XX |
XXXX XXXX |
XXXX XXXX |
- OOXX XOXX |
+ OOXX XXXX |
OOOO OOOO |
@@ -310,7 +310,7 @@
OOOO OOOO OOOO OOOO |
OOOO ~XXX |
XOOO OOOO |
- XOOO ~XXX |
+ XOOX ~XXX |
XXOO OOXX |
OOOO OOOO |
@@ -321,7 +321,7 @@
OOOO OOOO XXXX OOOO |
OOOO ~XXX |
XOOO OOOO |
- XOOO ~XXX |
+ XOOX ~XXX |
XXOO OOXX |
OOOO OOOO |
Index: projects/openttd_vs80.vcproj
===================================================================
--- projects/openttd_vs80.vcproj (revision 12052)
+++ projects/openttd_vs80.vcproj (working copy)
@@ -656,6 +656,10 @@
>
+
+
@@ -1060,6 +1064,10 @@
>
+
+
Index: projects/openttd_vs90.vcproj
===================================================================
--- projects/openttd_vs90.vcproj (revision 12052)
+++ projects/openttd_vs90.vcproj (working copy)
@@ -653,6 +653,10 @@
>
+
+
@@ -1057,6 +1061,10 @@
>
+
+
Index: source.list
===================================================================
--- source.list (revision 12052)
+++ source.list (working copy)
@@ -55,6 +55,7 @@
ottdres.rc
#end
pathfind.cpp
+pbs.cpp
players.cpp
queue.cpp
rail.cpp
@@ -173,6 +174,7 @@
oldpool.h
openttd.h
pathfind.h
+pbs.h
player.h
player_face.h
queue.h
Index: src/debug.cpp
===================================================================
--- src/debug.cpp (revision 12052)
+++ src/debug.cpp (working copy)
@@ -30,6 +30,7 @@
int _debug_freetype_level;
int _debug_sl_level;
int _debug_station_level;
+int _debug_pbs_level;
struct DebugLevel {
@@ -54,6 +55,7 @@
DEBUG_LEVEL(freetype),
DEBUG_LEVEL(sl),
DEBUG_LEVEL(station),
+ DEBUG_LEVEL(pbs),
};
#undef DEBUG_LEVEL
Index: src/debug.h
===================================================================
--- src/debug.h (revision 12052)
+++ src/debug.h (working copy)
@@ -47,6 +47,7 @@
extern int _debug_freetype_level;
extern int _debug_sl_level;
extern int _debug_station_level;
+ extern int _debug_pbs_level;
void CDECL debug(const char *dbg, ...);
#endif /* NO_DEBUG_MESSAGES */
Index: src/lang/english.txt
===================================================================
--- src/lang/english.txt (revision 12052)
+++ src/lang/english.txt (working copy)
@@ -1165,6 +1165,8 @@
STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY :{LTBLUE}When dragging, place signals every: {ORANGE}{STRING1} tile(s)
STR_CONFIG_PATCHES_SEMAPHORE_BUILD_BEFORE_DATE :{LTBLUE}Automatically build semaphores before: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_ENABLE_SIGNAL_GUI :{LTBLUE}Enable the signal GUI: {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_DO_PATH_RESERVATION :{LTBLUE}Make path reservations for trains (forces NPF or YAPF): {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_BUILD_PBS_SIG :{LTBLUE}Build advanced signals: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_TOWN_LAYOUT_INVALID :{WHITE}The town layout "no more roads" isn't valid in the scenario editor
STR_CONFIG_PATCHES_TOWN_LAYOUT :{LTBLUE}Select town-road layout: {ORANGE}{STRING1}
@@ -1634,12 +1636,23 @@
STR_RAILROAD_TRACK_WITH_PRESIGNALS :Railway track with pre-signals
STR_RAILROAD_TRACK_WITH_EXITSIGNALS :Railway track with exit-signals
STR_RAILROAD_TRACK_WITH_COMBOSIGNALS :Railway track with combo-signals
+STR_RAILROAD_TRACK_WITH_PBSSIGNALS :Railway track with advanced signals
+STR_RAILROAD_TRACK_WITH_NOENTRYSIGNALS :Railway track with no-entry signals
STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS :Railway track with normal and pre-signals
STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS :Railway track with normal and exit-signals
STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS :Railway track with normal and combo-signals
+STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS :Railway track with normal and advanced signals
+STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS :Railway track with normal and no-entry signals
STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS :Railway track with pre- and exit-signals
STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS :Railway track with pre- and combo-signals
+STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS :Railway track with pre- and advanced signals
+STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS :Railway track with pre- and no-entry signals
STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS :Railway track with exit- and combo-signals
+STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS :Railway track with exit- and advanced signals
+STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS :Railway track with exit- and no-entry signals
+STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS :Railway track with combo- and advanced signals
+STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS :Railway track with combo- and no-entry signals
+STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS :Railway track with advanced and no-entry signals
STR_MUST_REMOVE_RAILWAY_STATION_FIRST :{WHITE}Must remove railway station first
Index: src/map_func.h
===================================================================
--- src/map_func.h (revision 12052)
+++ src/map_func.h (working copy)
@@ -268,6 +268,23 @@
return _tileoffs_by_dir[dir];
}
+/*
+ * Returns the DiagDirection from a TileIndexDiffC.
+ *
+ * @param diff The tile offset
+ * @return The diagonal direction
+ */
+static inline DiagDirection DiagDirByTileIndexDiff(const TileIndexDiffC& diff)
+{
+ assert(diff.x == 0 || diff.y == 0);
+
+ if (diff.x == 0) {
+ return diff.y > 0 ? DIAGDIR_SE : DIAGDIR_NW;
+ } else {
+ return diff.x > 0 ? DIAGDIR_SW : DIAGDIR_NE;
+ }
+}
+
/**
* Add a TileIndexDiffC to a TileIndex and returns the new one.
*
Index: src/misc/dbg_helpers.cpp
===================================================================
--- src/misc/dbg_helpers.cpp (revision 12052)
+++ src/misc/dbg_helpers.cpp (working copy)
@@ -47,7 +47,7 @@
/** SignalType short names. */
static const char* signal_type_names[] = {
- "NORMAL", "ENTRY", "EXIT", "COMBO",
+ "NORMAL", "ENTRY", "EXIT", "COMBO", "PBS", "NOENTRY",
};
/** Return name of given SignalType. */
Index: src/npf.cpp
===================================================================
--- src/npf.cpp (revision 12052)
+++ src/npf.cpp (working copy)
@@ -23,6 +23,7 @@
#include "vehicle_base.h"
#include "settings_type.h"
#include "tunnelbridge.h"
+#include "pbs.h"
static AyStar _npf_aystar;
@@ -220,6 +221,23 @@
* there is only one level of steepness... */
}
+static uint NPFReservedTrackCost(AyStarNode* current)
+{
+ TileIndex tile = current->tile;
+ TrackBits track = TrackToTrackBits(TrackdirToTrack((Trackdir)current->direction));
+ TrackBits res = GetReservedTrackbits(tile);
+
+ if (NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL) || (res & track) == TRACK_BIT_NONE && !TracksOverlap(res | track)) return 0;
+
+ if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+ DiagDirection exitdir = TrackdirToExitdir((Trackdir)current->direction);
+ if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(exitdir)) {
+ return _patches.npf_rail_pbs_cross_penalty * (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 1);
+ }
+ }
+ return _patches.npf_rail_pbs_cross_penalty;
+}
+
/**
* Mark tiles by mowing the grass when npf debug level >= 1.
* Will not work for multiplayer games, since it can (will) cause desyncs.
@@ -353,30 +371,48 @@
/* Determine extra costs */
/* Check for signals */
- if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir)) {
- /* Ordinary track with signals */
- if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) {
- /* Signal facing us is red */
- if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
- /* Penalize the first signal we
- * encounter, if it is red */
+ if (IsTileType(tile, MP_RAILWAY)) {
+ if (HasSignalOnTrackdir(tile, trackdir)) {
+ /* Ordinary track with signals */
+ if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) {
+ /* Signal facing us is red */
+ if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
+ /* Penalize the first signal we
+ * encounter, if it is red */
- /* Is this a presignal exit or combo? */
- SignalType sigtype = GetSignalType(tile, TrackdirToTrack(trackdir));
- if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) {
- /* Penalise exit and combo signals differently (heavier) */
- cost += _patches.npf_rail_firstred_exit_penalty;
+ /* Is this a presignal exit or combo? */
+ SignalType sigtype = GetSignalType(tile, TrackdirToTrack(trackdir));
+ if (!IsPbsSignal(sigtype)) {
+ if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) {
+ /* Penalise exit and combo signals differently (heavier) */
+ cost += _patches.npf_rail_firstred_exit_penalty;
+ } else {
+ cost += _patches.npf_rail_firstred_penalty;
+ }
+ }
+ }
+ /* Record the state of this signal */
+ NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
+ } else {
+ /* Record the state of this signal */
+ NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
+ }
+ if (NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
+ if (NPFGetFlag(current, NPF_FLAG_2ND_SIGNAL)) {
+ NPFSetFlag(current, NPF_FLAG_3RD_SIGNAL, true);
} else {
- cost += _patches.npf_rail_firstred_penalty;
+ NPFSetFlag(current, NPF_FLAG_2ND_SIGNAL, true);
}
+ } else {
+ NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
}
- /* Record the state of this signal */
- NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
- } else {
- /* Record the state of this signal */
- NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
}
- NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
+
+ if (HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
+ if (!NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL) && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) {
+ cost += _patches.npf_rail_pbs_signal_back_penalty;
+ }
+ }
}
/* Penalise the tile if it is a target tile and the last signal was
@@ -405,7 +441,7 @@
}
/* Check for occupied track */
- //TODO
+ cost += NPFReservedTrackCost(current);
NPFMarkTile(tile);
DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
@@ -440,9 +476,109 @@
}
}
-/* To be called when current contains the (shortest route to) the target node.
+/**
+ * Find the node containing the first signal on the path.
+ *
+ * If the first signal is on the very first two tiles of the path,
+ * the second signal is returnd. If no suitable signal is present, the
+ * last node of the path is returned.
+ */
+static const PathNode* FindFirstSignal(PathNode *path)
+{
+ /* If there is no signal, reserve the whole path. */
+ PathNode *sig = path;
+
+ if (HasReservedTrack(path->node.tile, TrackToTrackBits(TrackdirToTrack((Trackdir)path->node.direction)))) {
+ NPFSetFlag(&sig->node, NPF_FLAG_TARGET_RESERVED, true);
+ }
+
+ while (path) {
+ if (IsTileType(path->node.tile, MP_RAILWAY) && path->parent != NULL) {
+ /* Update node if the tile contains a signal which isn't on the very first two
+ * tiles of the path. We reserve till the tile in front of the current tile. */
+ if (HasSignalOnTrackdir(path->node.tile, (Trackdir)path->node.direction) && path->parent->parent != NULL) {
+ sig = path->parent;
+ /* Note whether the signal tile is reserved as we can't use it
+ * then to avoid a possible crash. */
+ if (HasReservedTrack(path->node.tile, TrackToTrackBits(TrackdirToTrack((Trackdir)path->node.direction)))) {
+ NPFSetFlag(&sig->node, NPF_FLAG_TARGET_RESERVED, true);
+ }
+ }
+ /* Depots can also be a target. */
+ if (IsRailDepot(path->node.tile)) {
+ sig = path;
+ if (GetRailDepotReservation(path->node.tile)) NPFSetFlag(&sig->node, NPF_FLAG_TARGET_RESERVED, true);
+ }
+ }
+ path = path->parent;
+ }
+ return sig;
+}
+
+/**
+ * Lift the reservation of the tiles from @p start till @p end, excluding @p end itself.
+ */
+static void ClearPathReservation(const PathNode *start, const PathNode *end, bool start_is_target)
+{
+ while (start != end) {
+ if (IsRailwayStationTile(start->node.tile) && start_is_target) {
+ SetRailwayStationPlatformReservation(start->node.tile, TrackdirToExitdir((Trackdir)start->node.direction), false);
+ } else {
+ UnreserveRailTrack(start->node.tile, TrackdirToTrack((Trackdir)start->node.direction));
+ }
+ start_is_target = false;
+ start = start->parent;
+ }
+}
+
+/**
+ * Try to reserve all compatible remaining station tiles starting from @p target.
+ */
+static bool ReserveTargetStation(const PathNode *target)
+{
+ TileIndex tile = target->node.tile;
+ DiagDirection dir = TrackdirToExitdir((Trackdir)target->node.direction);
+ TileIndexDiff diff = TileOffsByDiagDir(dir);
+ TileIndex next = tile;
+
+ do {
+ if (GetRailwayStationReservation(next)) {
+ /* Sorry, already reserved, aborting... */
+ while (next != tile) {
+ next = TILE_ADD(next, -diff);
+ SetRailwayStationReservation(next, false);
+ }
+ return false;
+ }
+ SetRailwayStationReservation(next, true);
+ if (_debug_pbs_level >= 2)
+ MarkTileDirtyByTile(next);
+
+ next = TILE_ADD(next, diff);
+ } while (IsCompatibleTrainStationTile(next, tile));
+
+ /* If the tile right after the station is reserved, abort because of the danger of a crash. */
+ if ((GetTileTrackStatus(next, TRANSPORT_RAIL, 0) & TRACKDIR_BIT_MASK) != TRACKDIR_BIT_NONE &&
+ !(IsTileType(next, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(next) != dir) &&
+ !(IsTileDepotType(next, TRANSPORT_RAIL) && GetRailDepotDirection(next) == dir) &&
+ GetTileOwner(tile) == GetTileOwner(next)) {
+ if (HasReservedTrack(next, DiagdirReachesTracks(dir))) {
+ while (next != tile) {
+ next = TILE_ADD(next, -diff);
+ SetRailwayStationReservation(next, false);
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * To be called when @p current contains the (shortest route to) the target node.
* Will fill the contents of the NPFFoundTargetData using
- * AyStarNode[NPF_TRACKDIR_CHOICE].
+ * AyStarNode[NPF_TRACKDIR_CHOICE]. If requested, path reservation
+ * is done here.
*/
static void NPFSaveTargetData(AyStar* as, OpenListNode* current)
{
@@ -451,6 +587,30 @@
ftd->best_path_dist = current->g;
ftd->best_bird_dist = 0;
ftd->node = current->path.node;
+
+ if (as->user_target != NULL && ((NPFFindStationOrTileData*)as->user_target)->reserve_path && as->user_data[NPF_TYPE] == TRANSPORT_RAIL) {
+ /* We are supposed to reserve a path here */
+ const PathNode *cur, *target = FindFirstSignal(¤t->path);
+ cur = target;
+
+ /* Don't bother if the target is already reserved. */
+ if (NPFGetFlag(&target->node, NPF_FLAG_TARGET_RESERVED)) return;
+
+ if (cur == ¤t->path && IsRailwayStationTile(cur->node.tile)) {
+ /* Reservation target is the destination tile and the destination is a station. */
+ if (!ReserveTargetStation(cur)) return;
+ if ((cur = cur->parent) == NULL) return;
+ }
+
+ while (cur->parent != NULL) {
+ if (!TryReserveRailTrack(cur->node.tile, TrackdirToTrack((Trackdir)cur->node.direction))) {
+ /* Reservation failed, undo */
+ ClearPathReservation(target, cur, target == ¤t->path);
+ break;
+ }
+ cur = cur->parent;
+ }
+ }
}
/**
@@ -637,10 +797,11 @@
Trackdir dst_trackdir = RemoveFirstTrackdir(&trackdirbits);
DEBUG(npf, 5, "Expanded into trackdir: %d, remaining trackdirs: 0x%X", dst_trackdir, trackdirbits);
- /* Check for oneway signal against us */
+ /* Tile with signals? */
if (IsTileType(dst_tile, MP_RAILWAY) && GetRailTileType(dst_tile) == RAIL_TILE_SIGNALS) {
- if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(dst_trackdir)) && !HasSignalOnTrackdir(dst_tile, dst_trackdir))
- /* if one way signal not pointing towards us, stop going in this direction. */
+ if (HasNoEntrySignalOnTrackdir(dst_tile, dst_trackdir)) break;
+ if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(dst_trackdir)) && !HasSignalOnTrackdir(dst_tile, dst_trackdir) && IsOnewaySignal(dst_tile, TrackdirToTrack(dst_trackdir)))
+ /* If there's a one-way signal not pointing towards us, stop going in this direction. */
break;
}
{
@@ -888,7 +1049,7 @@
_npf_aystar.max_search_nodes = _patches.npf_max_search_nodes;
}
-void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v)
+void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v, bool reserve_path)
{
/* Ships don't really reach their stations, but the tile in front. So don't
* save the station id for ships. For roadvehs we don't store it either,
@@ -904,4 +1065,5 @@
fstd->dest_coords = v->dest_tile;
fstd->station_index = INVALID_STATION;
}
+ fstd->reserve_path = reserve_path;
}
Index: src/npf.h
===================================================================
--- src/npf.h (revision 12052)
+++ src/npf.h (working copy)
@@ -42,6 +42,7 @@
struct NPFFindStationOrTileData {
TileIndex dest_coords; ///< An indication of where the station is, for heuristic purposes, or the target tile
StationID station_index; ///< station index we're heading for, or INVALID_STATION when we're heading for a tile
+ bool reserve_path; ///< Indicates whether the found path should be reserved
};
/* Indices into AyStar.userdata[] */
@@ -63,6 +64,9 @@
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_2ND_SIGNAL, ///< Used to mark that two signals were seen, rail only
+ NPF_FLAG_3RD_SIGNAL, ///< Used to mark that three signals were seen, rail only
+ NPF_FLAG_TARGET_RESERVED ///< Used to mark that the possible reservation target is already reserved
};
/* Meant to be stored in AyStar.userpath */
@@ -101,7 +105,7 @@
* of choices and accurate heuristics, such as water. */
NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, TransportType type, uint sub_type, Owner owner, RailTypes railtypes);
-void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v);
+void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v, bool reserve_path = false);
/*
Index: src/openttd.cpp
===================================================================
--- src/openttd.cpp (revision 12052)
+++ src/openttd.cpp (working copy)
@@ -2352,6 +2352,45 @@
}
}
+ /* Move the signal variant back up one bit for PBS. We don't convert the old PBS
+ * format here, as an old layout wouldn't work properly anyway. To be safe, we
+ * clear any possible PBS reservations as well. */
+ if (CheckSavegameVersion(87)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ switch (GetTileType(t)) {
+ case MP_RAILWAY:
+ if (HasSignals(t)) {
+ /* move the signal variant */
+ SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+ SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+ ClrBit(_m[t].m2, 2);
+ ClrBit(_m[t].m2, 6);
+ }
+
+ /* Clear PBS reservation on track */
+ if (IsRailDepot(t) ||IsRailWaypoint(t))
+ SetWaypointReservation(t, false);
+ else
+ SetTrackReservation(t, TRACK_BIT_NONE);
+ break;
+
+ case MP_ROAD: /* Clear PBS reservation on crossing */
+ if (IsLevelCrossing(t)) SetCrossingReservation(t, false);
+ break;
+
+ case MP_STATION: /* Clear PBS reservation on station */
+ if (IsRailwayStation(t)) SetRailwayStationReservation(t, false);
+ break;
+
+ case MP_TUNNELBRIDGE: /* Clear PBS reservation on tunnels/birdges */
+ if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) SetTunnelBridgeReservation(t, false);
+ break;
+
+ default: break;
+ }
+ }
+ }
+
return InitializeWindowsAndCaches();
}
Index: src/pathfind.cpp
===================================================================
--- src/pathfind.cpp (revision 12052)
+++ src/pathfind.cpp (working copy)
@@ -745,9 +745,13 @@
/* railway tile with signals..? */
if (HasSignals(tile)) {
- if (!HasSignalOnTrackdir(tile, track)) {
+ if (HasNoEntrySignalOnTrackdir(tile, track)) {
+ /* no-entry signal pointing to us, can't pass it. */
+ bits = TRACK_BIT_NONE;
+ break;
+ } else if (!HasSignalOnTrackdir(tile, track)) {
/* if one way signal not pointing towards us, stop going in this direction => End of rail segment. */
- if (HasSignalOnTrackdir(tile, ReverseTrackdir(track))) {
+ if (HasSignalOnTrackdir(tile, ReverseTrackdir(track)) && IsOnewaySignal(tile, TrackdirToTrack(track))) {
bits = TRACK_BIT_NONE;
break;
}
Index: src/pbs.cpp
===================================================================
--- src/pbs.cpp (revision 0)
+++ src/pbs.cpp (revision 0)
@@ -0,0 +1,155 @@
+/* $Id$ */
+
+/** @file pbs.cpp */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "pbs.h"
+#include "rail_map.h"
+#include "road_map.h"
+#include "station_map.h"
+#include "tunnelbridge_map.h"
+#include "functions.h"
+#include "debug.h"
+#include "direction_func.h"
+#include "rail.h"
+
+/**
+ * Get the reserved trackbits for any tile, regardless of type.
+ * @param t the tile
+ * @return the reserved trackbits. TRACK_BIT_NONE on nothing reserved or
+ * a tile without rail.
+ */
+TrackBits GetReservedTrackbits(TileIndex t)
+{
+ switch (GetTileType(t)) {
+ case MP_RAILWAY:
+ if (IsRailWaypoint(t) || IsRailDepot(t)) return GetRailWaypointReservation(t);
+ if (IsPlainRailTile(t)) return GetTrackReservation(t);
+ break;
+
+ case MP_ROAD:
+ if (IsLevelCrossing(t)) return GetRailCrossingReservation(t);
+ break;
+
+ case MP_STATION:
+ if (IsRailwayStation(t)) return GetRailStationReservation(t);
+ break;
+
+ case MP_TUNNELBRIDGE:
+ if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) return GetRailTunnelBridgeReservation(t);
+ break;
+
+ default:
+ break;
+ }
+ return TRACK_BIT_NONE;
+}
+
+/**
+ * Set the reservation for a complete station platform.
+ * @pre IsRailwayStationTile(start)
+ * @param start starting tile of the platform
+ * @param dir the direction in which to follow the platform
+ * @param b the state the reservation should be set to
+ */
+void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
+{
+ TileIndex tile = start;
+ TileIndexDiff diff = TileOffsByDiagDir(dir);
+
+ assert(IsRailwayStationTile(start) && GetRailStationAxis(start) == DiagDirToAxis(dir));
+
+ do {
+ SetRailwayStationReservation(tile, b);
+ if (_debug_pbs_level >= 2) MarkTileDirtyByTile(tile);
+ tile = TILE_ADD(tile, diff);
+ } while (IsCompatibleTrainStationTile(tile, start));
+}
+
+/**
+ * Try to reserve a specific track on a tile
+ * @param tile the tile
+ * @param t the track
+ * @return true if reservation was successfull, i.e. the track was
+ * free and didn't cross any other reserved tracks.
+ */
+bool TryReserveRailTrack(TileIndex tile, Track t)
+{
+ assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
+
+ if (_debug_pbs_level >= 2)
+ MarkTileDirtyByTile(tile);
+
+ switch (GetTileType(tile)) {
+ case MP_RAILWAY:
+ if (IsPlainRailTile(tile)) return TryReserveTrack(tile, t);
+ if (IsRailWaypoint(tile) || IsRailDepot(tile)) {
+ if (!GetWaypointReservation(tile)) {
+ SetWaypointReservation(tile, true);
+ return true;
+ }
+ }
+ break;
+
+ case MP_ROAD:
+ if (IsLevelCrossing(tile) && !GetCrossingReservation(tile)) {
+ SetCrossingReservation(tile, true);
+ return true;
+ }
+ break;
+
+ case MP_STATION:
+ if (IsRailwayStation(tile) && !GetRailwayStationReservation(tile)) {
+ SetRailwayStationReservation(tile, true);
+ return true;
+ }
+ break;
+
+ case MP_TUNNELBRIDGE:
+ if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && !GetRailTunnelBridgeReservation(tile)) {
+ SetTunnelBridgeReservation(tile, true);
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/**
+ * Lift the reservation of a specific track on a tile
+ * @param tile the tile
+ * @param t the track
+ */
+ void UnreserveRailTrack(TileIndex tile, Track t)
+{
+ assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
+
+ if (_debug_pbs_level >= 2)
+ MarkTileDirtyByTile(tile);
+
+ switch (GetTileType(tile)) {
+ case MP_RAILWAY:
+ if (IsRailWaypoint(tile) || IsRailDepot(tile)) SetWaypointReservation(tile, false);
+ if (IsPlainRailTile(tile)) UnreserveTrack(tile, t);
+ break;
+
+ case MP_ROAD:
+ if (IsLevelCrossing(tile)) SetCrossingReservation(tile, false);
+ break;
+
+ case MP_STATION:
+ if (IsRailwayStation(tile)) SetRailwayStationReservation(tile, false);
+ break;
+
+ case MP_TUNNELBRIDGE:
+ if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) SetTunnelBridgeReservation(tile, false);
+ break;
+
+ default:
+ break;
+ }
+}
Property changes on: src\pbs.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Index: src/pbs.h
===================================================================
--- src/pbs.h (revision 0)
+++ src/pbs.h (revision 0)
@@ -0,0 +1,31 @@
+/* $Id$ */
+
+/** @file pbs.h PBS support routines */
+
+#ifndef PBS_H
+#define PBS_H
+
+#include "tile_type.h"
+#include "direction_type.h"
+#include "track_type.h"
+
+TrackBits GetReservedTrackbits(TileIndex t);
+
+void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bool b);
+
+bool TryReserveRailTrack(TileIndex tile, Track t);
+void UnreserveRailTrack(TileIndex tile, Track t);
+
+/**
+ * Check whether some of tracks is reserved on a tile
+ *
+ * @param tile the tile
+ * @param tracks the tracks to test
+ * @return true if at least on of tracks is reserved
+ */
+static inline bool HasReservedTrack(TileIndex tile, TrackBits tracks)
+{
+ return (GetReservedTrackbits(tile) & tracks) != TRACK_BIT_NONE;
+}
+
+#endif /* PBS_H */
Property changes on: src\pbs.h
___________________________________________________________________
Name: svn:eol-style
+ native
Index: src/rail_cmd.cpp
===================================================================
--- src/rail_cmd.cpp (revision 12052)
+++ src/rail_cmd.cpp (working copy)
@@ -41,6 +41,7 @@
#include "sound_func.h"
#include "signal_func.h"
#include "tunnelbridge.h"
+#include "pbs.h"
#include "table/sprites.h"
#include "table/strings.h"
@@ -431,7 +432,7 @@
/** Remove a single piece of track
* @param tile tile to remove track from
* @param flags operation to perform
- * @param p1 unused
+ * @param p1 force remove even if track is reserved
* @param p2 rail orientation
*/
CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
@@ -444,6 +445,8 @@
if (!ValParamTrackOrientation((Track)p2)) return CMD_ERROR;
trackbit = TrackToTrackBits(track);
+ if (p1 == 0 && HasReservedTrack(tile, trackbit)) return CMD_ERROR;
+
/* Need to read tile owner now because it may change when the rail is removed
* Also, in case of floods, _current_player != owner
* There may be invalid tiletype even in exec run (when removing long track),
@@ -666,7 +669,7 @@
if (flags & DC_EXEC) SndPlayTileFx(SND_20_SPLAT_2, tile);
for (;;) {
- ret = DoCommand(tile, railtype, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL);
+ ret = DoCommand(tile, (mode == 0) ? railtype : 0, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL);
if (CmdFailed(ret)) {
if ((_error_message != STR_1007_ALREADY_BUILT) && (mode == 0)) break;
@@ -788,8 +791,9 @@
* - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum)
* - p1 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal or (for bit 7) toggle variant (CTRL-toggle)
* - p1 = (bit 4) - 0 = signals, 1 = semaphores
- * - p1 = (bit 5-6) - type of the signal, for valid values see enum SignalType in rail_map.h
- * - p1 = (bit 7) - convert the present signal type and variant
+ * - p1 = (bit 5-7) - type of the signal, for valid values see enum SignalType in rail_map.h
+ * - p1 = (bit 8) - convert the present signal type and variant
+ * - p1 = (bit 9) - build advanced signals instead of normal signals
* @param p2 used for CmdBuildManySignals() to copy direction of first signal
* TODO: p2 should be replaced by two bits for "along" and "against" the track.
*/
@@ -798,8 +802,9 @@
Track track = (Track)GB(p1, 0, 3);
bool ctrl_pressed = HasBit(p1, 3); // was the CTRL button pressed
SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal
- SignalType sigtype = (SignalType)GB(p1, 5, 2); // the signal type of the new signal
- bool convert_signal = HasBit(p1, 7); // convert button pressed
+ SignalType sigtype = (SignalType)GB(p1, 5, 3); // the signal type of the new signal
+ bool convert_signal = HasBit(p1, 8); // convert button pressed
+ bool build_pbs_sig = HasBit(p1, 9);
CommandCost cost;
if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoTrainOnTrack(tile, track))
@@ -863,7 +868,7 @@
if (p2 == 0) {
if (!HasSignalOnTrack(tile, track)) {
/* build new signals */
- SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track));
+ SetPresentSignals(tile, GetPresentSignals(tile) | (build_pbs_sig ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
SetSignalType(tile, track, sigtype);
SetSignalVariant(tile, track, sigvar);
} else {
@@ -881,9 +886,14 @@
} else if (ctrl_pressed) {
/* cycle between normal -> pre -> exit -> combo -> ... */
- sigtype = GetSignalType(tile, track);
+ sigtype = (SignalType)(GetSignalType(tile, track) + 1);
- SetSignalType(tile, track, sigtype == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(sigtype + 1));
+ if (build_pbs_sig)
+ sigtype = sigtype > SIGTYPE_LAST ? SIGTYPE_PBS : (sigtype < SIGTYPE_PBS ? SIGTYPE_PBS : sigtype);
+ else
+ sigtype = sigtype > SIGTYPE_LAST_NOPBS ? SIGTYPE_NORMAL : sigtype;
+
+ SetSignalType(tile, track, sigtype);
} else {
/* cycle the signal side: both -> left -> right -> both -> ... */
CycleSignalSide(tile, track);
@@ -896,6 +906,10 @@
SetSignalVariant(tile, track, sigvar);
}
+ if (sigtype == SIGTYPE_PBS) {
+ uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
+ SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetTrackReservation(tile), track) ? (uint)-1 : 0) & mask));
+ }
MarkTileDirtyByTile(tile);
AddTrackToSignalBuffer(tile, track, _current_player);
YapfNotifyTrackLayoutChange(tile, track);
@@ -1007,6 +1021,8 @@
/* Autofill must start on a valid track to be able to avoid loops */
if (autofill && !HasTrack(tile, track)) return CMD_ERROR;
+ SignalType sigtype = SIGTYPE_NORMAL;
+
/* copy the signal-style of the first rail-piece if existing */
if (HasSignals(tile)) {
signals = GetPresentSignals(tile) & SignalOnTrack(track);
@@ -1014,6 +1030,8 @@
/* copy signal/semaphores style (independent of CTRL) */
semaphores = GetSignalVariant(tile, track) != SIG_ELECTRIC;
+
+ sigtype = GetSignalType(tile, track);
} else { // no signals exist, drag a two-way signal stretch
signals = SignalOnTrack(track);
}
@@ -1037,6 +1055,7 @@
uint32 p1 = GB(TrackdirToTrack(trackdir), 0, 3);
SB(p1, 3, 1, mode);
SB(p1, 4, 1, semaphores);
+ SB(p1, 5, 3, sigtype);
/* Pick the correct orientation for the track direction */
signals = 0;
@@ -1373,7 +1392,8 @@
TrackBits tracks = GetTrackBits(tile);
while (tracks != TRACK_BIT_NONE) {
Track track = RemoveFirstTrack(&tracks);
- ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
+ /* NOTE: Force removal of reserved tracks, maybe remove if no bugs found. */
+ ret = DoCommand(tile, 1, track, flags, CMD_REMOVE_SINGLE_RAIL);
if (CmdFailed(ret)) return CMD_ERROR;
cost.AddCost(ret);
}
@@ -1453,7 +1473,7 @@
if (GetSignalType(tile, track) == SIGTYPE_NORMAL && GetSignalVariant(tile, track) == SIG_ELECTRIC) {
sprite = SignalBase[side][GetSignalVariant(tile, track)][GetSignalType(tile, track)] + image + condition;
} else {
- sprite = SPR_SIGNALS_BASE + (GetSignalType(tile, track) - 1) * 16 + GetSignalVariant(tile, track) * 64 + image + condition;
+ sprite = SPR_SIGNALS_BASE + (GetSignalType(tile, track) - 1) * 16 + GetSignalVariant(tile, track) * 64 + image + condition + (GetSignalType(tile, track) > SIGTYPE_LAST_NOPBS ? 64 : 0);
}
AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track));
@@ -1689,6 +1709,17 @@
if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
}
+ /* PBS debugging, draw reserved tracks darker */
+ if (_debug_pbs_level >= 2) {
+ TrackBits pbs = GetTrackReservation(ti->tile);
+ if (pbs & TRACK_BIT_X && (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED)) DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH);
+ if (pbs & TRACK_BIT_Y && (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED)) DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH);
+ if (pbs & TRACK_BIT_UPPER) AddSortableSpriteToDraw(rti->base_sprites.single_n, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_N ? 8 : 0));
+ if (pbs & TRACK_BIT_LOWER) AddSortableSpriteToDraw(rti->base_sprites.single_s, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_S ? 8 : 0));
+ if (pbs & TRACK_BIT_LEFT) AddSortableSpriteToDraw(rti->base_sprites.single_w, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_W ? 8 : 0));
+ if (pbs & TRACK_BIT_RIGHT) AddSortableSpriteToDraw(rti->base_sprites.single_e, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_E ? 8 : 0));
+ }
+
if (IsValidCorner(halftile_corner)) {
DrawFoundation(ti, HalftileFoundation(halftile_corner));
@@ -1825,6 +1856,10 @@
DrawGroundSprite(image, PAL_NONE);
+ /* PBS debugging, draw reserved tracks darker */
+ if (_debug_pbs_level >= 2 && GetWaypointReservation(ti->tile))
+ DrawGroundSprite(GetWaypointAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
+
if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
foreach_draw_tile_seq(dtss, dts->seq) {
@@ -2059,17 +2094,18 @@
b &= a;
- /* When signals are not present (in neither
- * direction), we pretend them to be green. (So if
- * signals are only one way, the other way will
- * implicitely become `red' */
- if ((a & 0xC) == 0) b |= 0xC;
- if ((a & 0x3) == 0) b |= 0x3;
+ /* When signals are not present (in neither direction),
+ * we pretend them to be green. Otherwise, it depends on
+ * the signal type. For signals that are only active from
+ * one side, we set the missing signals explicitely to
+ * `green'. Otherwise, they implicitely become `red'. */
+ if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
+ if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
- if ((b & 0x8) == 0) ret |= 0x10070000;
- if ((b & 0x4) == 0) ret |= 0x07100000;
- if ((b & 0x2) == 0) ret |= 0x20080000;
- if ((b & 0x1) == 0) ret |= 0x08200000;
+ if ((b & 0x8) == 0) ret |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E) << 16;
+ if ((b & 0x4) == 0) ret |= (TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_LEFT_S) << 16;
+ if ((b & 0x2) == 0) ret |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E) << 16;
+ if ((b & 0x1) == 0) ret |= (TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_S) << 16;
return ret;
}
@@ -2097,30 +2133,54 @@
break;
case RAIL_TILE_SIGNALS: {
- const StringID signal_type[4][4] = {
+ const StringID signal_type[6][6] = {
{
STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS,
STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS,
STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS,
- STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS
+ STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS,
+ STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS
},
{
STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS,
STR_RAILROAD_TRACK_WITH_PRESIGNALS,
STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS,
- STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS
+ STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS,
+ STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS
},
{
STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS,
STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS,
STR_RAILROAD_TRACK_WITH_EXITSIGNALS,
- STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS
+ STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS,
+ STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS
},
{
STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS,
STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS,
STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS,
- STR_RAILROAD_TRACK_WITH_COMBOSIGNALS
+ STR_RAILROAD_TRACK_WITH_COMBOSIGNALS,
+ STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS
+ },
+ {
+ STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_PBSSIGNALS,
+ STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS
+ },
+ {
+ STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
+ STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS,
+ STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS,
+ STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS,
+ STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS,
+ STR_RAILROAD_TRACK_WITH_NOENTRYSIGNALS
}
};
Index: src/rail_gui.cpp
===================================================================
--- src/rail_gui.cpp (revision 12052)
+++ src/rail_gui.cpp (working copy)
@@ -210,8 +210,9 @@
uint32 p1 = track;
SB(p1, 3, 1, _ctrl_pressed);
SB(p1, 4, 1, _cur_signal_variant);
- SB(p1, 5, 2, _patches.enable_signal_gui ? _cur_signal_type : SIGTYPE_NORMAL);
- SB(p1, 7, 1, _convert_signal_button);
+ SB(p1, 5, 3, _patches.enable_signal_gui ? _cur_signal_type : (_patches.build_pbs_signals ? SIGTYPE_PBS : SIGTYPE_NORMAL));
+ SB(p1, 8, 1, _convert_signal_button);
+ SB(p1, 9, 1, _patches.build_pbs_signals);
DoCommandP(tile, p1, 0, CcPlaySound1E, CMD_BUILD_SIGNALS |
CMD_MSG(_convert_signal_button ? STR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE : STR_1010_CAN_T_BUILD_SIGNALS_HERE));
Index: src/rail_map.h
===================================================================
--- src/rail_map.h (revision 12052)
+++ src/rail_map.h (working copy)
@@ -156,7 +156,18 @@
return (DiagDirection)GB(_m[t].m5, 0, 2);
}
+/**
+ * Returns the track of a depot, ignoring direction
+ * @pre IsRailDepotTile(t)
+ * @param t the tile to get the depot track from
+ * @return the track of the depot
+ */
+static inline Track GetRailDepotTrack(TileIndex t)
+{
+ return AxisToTrack(DiagDirToAxis(GetRailDepotDirection(t)));
+}
+
/**
* Returns the axis of the waypoint
* @param t the tile to get the waypoint axis from
@@ -201,27 +212,150 @@
return (WaypointID)_m[t].m2;
}
+
+/**
+ * Returns the reserved track bits of the tile
+ * @pre IsPlainRailTile(t)
+ * @param t the tile to query
+ * @return the track bits
+ */
+static inline TrackBits GetTrackReservation(TileIndex t)
+{
+ assert(IsPlainRailTile(t));
+ byte track_b = GB(_m[t].m2, 8, 3);
+ Track track = (Track)(track_b - 1); // map array saves Track+1
+ return track_b ? (TrackBits)(TrackToTrackBits(track) | (HasBit(_m[t].m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)) : TRACK_BIT_NONE;
+}
+
+/**
+ * Sets the reserved track bits of the tile
+ * @pre IsPlainRailTile(t) && !TracksOverlap(b)
+ * @param t the tile to change
+ * @param b the track bits
+ */
+static inline void SetTrackReservation(TileIndex t, TrackBits b)
+{
+ assert(IsPlainRailTile(t) && b != INVALID_TRACK_BIT && !TracksOverlap(b));
+ Track track = RemoveFirstTrack(&b);
+ SB(_m[t].m2, 8, 3, track == INVALID_TRACK ? 0 : track+1);
+ SB(_m[t].m2, 11, 1, ((byte)(b != TRACK_BIT_NONE)) & 1);
+}
+
+/**
+ * Try to reserve a specific track on a tile
+ * @pre IsPlainRailTile(t) && HasTrack(tile, t)
+ * @param tile the tile
+ * @param t the rack to reserve
+ * @return true if successful
+ */
+static inline bool TryReserveTrack(TileIndex tile, Track t)
+{
+ assert(HasTrack(tile, t));
+ TrackBits bits = TrackToTrackBits(t);
+ TrackBits res = GetTrackReservation(tile);
+ if ((res & bits) != TRACK_BIT_NONE) return false; // already reserved
+ res |= bits;
+ if (TracksOverlap(res)) return false; // crossing reservation present
+ SetTrackReservation(tile, res);
+ return true;
+}
+
+/**
+ * Lift the reservation of a specific track on a tile
+ * @pre IsPlainRailTile(t) && HasTrack(tile, t)
+ * @param tile the tile
+ * @param t the track to free
+ */
+static inline void UnreserveTrack(TileIndex tile, Track t)
+{
+ assert(HasTrack(tile, t));
+ TrackBits res = GetTrackReservation(tile);
+ res &= ~TrackToTrackBits(t);
+ SetTrackReservation(tile, res);
+}
+
+/**
+ * Get the reservation state of the waypoint or depot
+ * @note Works for both waypoints and rail depots
+ * @pre IsRailWaypoint(t) || IsRailDepot(t)
+ * @param t the waypoint/depot tile
+ * @return reservation state
+ */
+static inline bool GetWaypointReservation(TileIndex t)
+{
+ assert(IsRailWaypoint(t) || IsRailDepot(t));
+ return HasBit(_m[t].m5, 4);
+}
+
+/**
+ * Set the reservation state of the waypoint or depot
+ * @note Works for both waypoints and rail depots
+ * @pre IsRailWaypoint(t) || IsRailDepot(t)
+ * @param t the waypoint/depot tile
+ * @param b the reservation state
+ */
+static inline void SetWaypointReservation(TileIndex t, bool b)
+{
+ assert(IsRailWaypoint(t) || IsRailDepot(t));
+ SB(_m[t].m5, 4, 1, (byte)b & 1);
+}
+
+#define GetDepotReservation GetWaypointReservation
+#define SetDepotReservation SetWaypointReservation
+
+/**
+ * Get the reserved track bits for a waypoint
+ * @pre IsRailWaypoint(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailWaypointReservation(TileIndex t)
+{
+ return GetWaypointReservation(t) ? GetRailWaypointBits(t) : TRACK_BIT_NONE;
+}
+
+/**
+ * Get the reserved track bits for a depot
+ * @pre IsRailDepot(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailDepotReservation(TileIndex t)
+{
+ return GetWaypointReservation(t) ? TrackToTrackBits(GetRailDepotTrack(t)) : TRACK_BIT_NONE;
+}
+
+
/** Type of signal, i.e. how does the signal behave? */
enum SignalType {
- SIGTYPE_NORMAL = 0, ///< normal signal
- SIGTYPE_ENTRY = 1, ///< presignal block entry
- SIGTYPE_EXIT = 2, ///< presignal block exit
- SIGTYPE_COMBO = 3 ///< presignal inter-block
+ SIGTYPE_NORMAL = 0, ///< normal signal
+ SIGTYPE_ENTRY = 1, ///< presignal block entry
+ SIGTYPE_EXIT = 2, ///< presignal block exit
+ SIGTYPE_COMBO = 3, ///< presignal inter-block
+ SIGTYPE_PBS = 4, ///< normal pbs signal
+ SIGTYPE_NOENTRY = 5, ///< no-entry signal
+ SIGTYPE_LAST = SIGTYPE_NOENTRY,
+ SIGTYPE_LAST_NOPBS = SIGTYPE_COMBO
};
+static inline bool IsPbsSignal(SignalType s)
+{
+ return s == SIGTYPE_PBS;
+}
+
static inline SignalType GetSignalType(TileIndex t, Track track)
{
assert(GetRailTileType(t) == RAIL_TILE_SIGNALS);
byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0;
- return (SignalType)GB(_m[t].m2, pos, 2);
+ return (SignalType)GB(_m[t].m2, pos, 3);
}
static inline void SetSignalType(TileIndex t, Track track, SignalType s)
{
assert(GetRailTileType(t) == RAIL_TILE_SIGNALS);
byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0;
- SB(_m[t].m2, pos, 2, s);
- if (track == INVALID_TRACK) SB(_m[t].m2, 4, 2, s);
+ SB(_m[t].m2, pos, 3, s);
+ if (track == INVALID_TRACK) SB(_m[t].m2, 4, 3, s);
}
static inline bool IsPresignalEntry(TileIndex t, Track track)
@@ -234,6 +368,12 @@
return GetSignalType(t, track) == SIGTYPE_EXIT || GetSignalType(t, track) == SIGTYPE_COMBO;
}
+/** One-way signals can't be passed the 'wrong' way. */
+static inline bool IsOnewaySignal(TileIndex t, Track track)
+{
+ return GetSignalType(t, track) <= SIGTYPE_LAST_NOPBS;
+}
+
static inline void CycleSignalSide(TileIndex t, Track track)
{
byte sig;
@@ -252,15 +392,15 @@
static inline SignalVariant GetSignalVariant(TileIndex t, Track track)
{
- byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 6 : 2;
+ byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3;
return (SignalVariant)GB(_m[t].m2, pos, 1);
}
static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v)
{
- byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 6 : 2;
+ byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3;
SB(_m[t].m2, pos, 1, v);
- if (track == INVALID_TRACK) SB(_m[t].m2, 6, 1, v);
+ if (track == INVALID_TRACK) SB(_m[t].m2, 7, 1, v);
}
/** These are states in which a signal can be. Currently these are only two, so
@@ -387,8 +527,35 @@
}
}
+/**
+ * Does the tile have a no-entry signal facing us?
+ * @param tile the tile to check
+ * @param td the trackdir to check
+ * @return true if a no-entry signals points at us
+ */
+static inline bool HasNoEntrySignalOnTrackdir(TileIndex tile, Trackdir td)
+{
+ return
+ IsTileType(tile, MP_RAILWAY) &&
+ HasSignalOnTrackdir(tile, td) &&
+ GetSignalType(tile, TrackdirToTrack(td)) == SIGTYPE_NOENTRY;
+}
/**
+ * Is a pbs signal present along the trackdir?
+ * @param tile the tile to check
+ * @param td the trackdir to check
+ */
+static inline bool HasPbsSignalOnTrackdir(TileIndex tile, Trackdir td)
+{
+ return
+ IsTileType(tile, MP_RAILWAY) &&
+ HasSignalOnTrackdir(tile, td) &&
+ IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)));
+}
+
+
+/**
* Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile.
*/
RailType GetTileRailType(TileIndex tile);
Index: src/road_cmd.cpp
===================================================================
--- src/road_cmd.cpp (revision 12052)
+++ src/road_cmd.cpp (working copy)
@@ -1089,6 +1089,11 @@
}
DrawGroundSprite(image, pal);
+
+ /* PBS debugging, draw reserved tracks darker */
+ if (_debug_pbs_level >= 2 && GetCrossingReservation(ti->tile))
+ DrawGroundSprite(GetCrossingRoadAxis(ti->tile) == AXIS_Y ? GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_y : GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_x, PALETTE_CRASH);
+
if (HasBit(GetRoadTypes(ti->tile), ROADTYPE_TRAM)) {
DrawGroundSprite(SPR_TRAMWAY_OVERLAY + (GetCrossingRoadAxis(ti->tile) ^ 1), pal);
DrawTramCatenary(ti, GetCrossingRoadBits(ti->tile));
Index: src/road_map.h
===================================================================
--- src/road_map.h (revision 12052)
+++ src/road_map.h (working copy)
@@ -191,6 +191,43 @@
return AxisToTrackBits(OtherAxis(GetCrossingRoadAxis(tile)));
}
+
+/**
+ * Get the reservation state of the rail crossing
+ * @pre IsLevelCrossingTile(t)
+ * @param t the crossing tile
+ * @return reservation state
+ */
+static inline bool GetCrossingReservation(TileIndex t)
+{
+ assert(IsLevelCrossingTile(t));
+ return HasBit(_m[t].m5, 4);
+}
+
+/**
+ * Set the reservation state of the rail crossing
+ * @note Works for both waypoints and rail depots
+ * @pre IsLevelCrossingTile(t)
+ * @param t the crossing tile
+ * @param b the reservation state
+ */
+static inline void SetCrossingReservation(TileIndex t, bool b)
+{
+ assert(IsLevelCrossingTile(t));
+ SB(_m[t].m5, 4, 1, (byte)b & 1);
+}
+
+/**
+ * Get the reserved track bits for a rail crossing
+ * @pre IsLevelCrossingTile(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailCrossingReservation(TileIndex t)
+{
+ return GetCrossingReservation(t) ? GetCrossingRailBits(t) : TRACK_BIT_NONE;
+}
+
static inline bool IsCrossingBarred(TileIndex t)
{
assert(GetRoadTileType(t) == ROAD_TILE_CROSSING);
Index: src/saveload.cpp
===================================================================
--- src/saveload.cpp (revision 12052)
+++ src/saveload.cpp (working copy)
@@ -34,7 +34,7 @@
#include "table/strings.h"
-extern const uint16 SAVEGAME_VERSION = 86;
+extern const uint16 SAVEGAME_VERSION = 87;
uint16 _sl_version; ///< the major savegame version identifier
byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
Index: src/settings.cpp
===================================================================
--- src/settings.cpp (revision 12052)
+++ src/settings.cpp (working copy)
@@ -1391,6 +1391,8 @@
SDT_VAR(Patches, drag_signals_density,SLE_UINT8,S, 0, 4, 1, 20, 0, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY,NULL),
SDT_VAR(Patches, semaphore_build_before,SLE_INT32, S, NC, 1975, MIN_YEAR, MAX_YEAR, 1, STR_CONFIG_PATCHES_SEMAPHORE_BUILD_BEFORE_DATE, NULL),
SDT_CONDVAR(Patches, town_layout, SLE_UINT8, 59, SL_MAX_VERSION, 0, MS, TL_ORIGINAL, TL_NO_ROADS, NUM_TLS - 1, 1, STR_CONFIG_PATCHES_TOWN_LAYOUT, CheckTownLayout),
+ SDT_CONDBOOL(Patches, reserve_paths, 87, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_PATCHES_DO_PATH_RESERVATION, NULL),
+ SDT_CONDBOOL(Patches, build_pbs_signals, 87, SL_MAX_VERSION, N, 0, false, STR_CONFIG_PATCHES_BUILD_PBS_SIG, NULL),
/***************************************************************************/
/* Vehicle section of the GUI-configure patches window */
@@ -1541,6 +1543,10 @@
SDT_VAR(Patches, npf_crossing_penalty, SLE_UINT, 0, 0, (3 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL),
/* This is the penalty for drive-through road, stops. */
SDT_CONDVAR (Patches, npf_road_drive_through_penalty, SLE_UINT, 47, SL_MAX_VERSION, 0, 0, 8 * NPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
+ /* Penalty for crossing a reserved tile */
+ SDT_CONDVAR(Patches, npf_rail_pbs_cross_penalty, SLE_UINT, 87, SL_MAX_VERSION, 0, 0, 3 * NPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
+ /* Penalty for passing a pbs signal from the backside */
+ SDT_CONDVAR(Patches, npf_rail_pbs_signal_back_penalty,SLE_UINT, 87, SL_MAX_VERSION, 0, 0, 50 * NPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
/* The maximum number of nodes to search */
@@ -1565,10 +1571,12 @@
SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p0 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, 500 , -1000000, 1000000, 0, STR_NULL, NULL),
SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p1 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, -100 , -1000000, 1000000, 0, STR_NULL, NULL),
SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p2 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, 5 , -1000000, 1000000, 0, STR_NULL, NULL),
+ SDT_CONDVAR (Patches, yapf.rail_pbs_cross_penalty , SLE_UINT, 87, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
+ SDT_CONDVAR (Patches, yapf.rail_pbs_signal_back_penalty,SLE_UINT, 87, SL_MAX_VERSION, 0, 0, 25 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
/* penalties for too long or too short station platforms */
SDT_CONDVAR (Patches, yapf.rail_longer_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 8 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL),
SDT_CONDVAR (Patches, yapf.rail_longer_platform_per_tile_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 0 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL),
- SDT_CONDVAR (Patches, yapf.rail_shorter_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 40 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL),
+ SDT_CONDVAR (Patches, yapf.rail_shorter_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 80 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL),
SDT_CONDVAR (Patches, yapf.rail_shorter_platform_per_tile_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 0 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL),
/* road vehicles - penalties */
SDT_CONDVAR (Patches, yapf.road_slope_penalty , SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 2 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
Index: src/settings_gui.cpp
===================================================================
--- src/settings_gui.cpp (revision 12052)
+++ src/settings_gui.cpp (working copy)
@@ -742,6 +742,7 @@
"drag_signals_density",
"oil_refinery_limit",
"semaphore_build_before",
+ "build_pbs_signals",
};
static const char *_patches_stations[] = {
@@ -797,6 +798,7 @@
"yapf.ship_use_yapf",
"yapf.road_use_yapf",
"yapf.rail_use_yapf",
+ "reserve_paths",
"train_income_warn",
"order_review_system",
"never_expire_vehicles",
@@ -1070,7 +1072,7 @@
{ WWT_CLOSEBOX, RESIZE_NONE, 10, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 10, 11, 369, 0, 13, STR_CONFIG_PATCHES_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 10, 0, 369, 14, 41, 0x0, STR_NULL},
-{ WWT_PANEL, RESIZE_NONE, 10, 0, 369, 42, 380, 0x0, STR_NULL},
+{ WWT_PANEL, RESIZE_NONE, 10, 0, 369, 42, 390, 0x0, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 3, 10, 96, 16, 27, STR_CONFIG_PATCHES_GUI, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 3, 97, 183, 16, 27, STR_CONFIG_PATCHES_CONSTRUCTION, STR_NULL},
@@ -1082,7 +1084,7 @@
};
static const WindowDesc _patches_selection_desc = {
- WDP_CENTER, WDP_CENTER, 370, 381, 370, 381,
+ WDP_CENTER, WDP_CENTER, 370, 391, 370, 391,
WC_GAME_OPTIONS, WC_NONE,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
_patches_selection_widgets,
Index: src/settings_type.h
===================================================================
--- src/settings_type.h (revision 12052)
+++ src/settings_type.h (working copy)
@@ -194,6 +194,8 @@
uint32 npf_road_curve_penalty; ///< The penalty for curves
uint32 npf_crossing_penalty; ///< The penalty for level crossings
uint32 npf_road_drive_through_penalty; ///< The penalty for going through a drive-through road stop
+ uint32 npf_rail_pbs_cross_penalty; ///< The penalty for crossing a reserved rail track
+ uint32 npf_rail_pbs_signal_back_penalty; ///< The penalty for passing a pbs signal from the backside
bool population_in_label; ///< Show the population of a town in his label?
@@ -226,6 +228,9 @@
bool give_money; ///< allow giving other players money
bool enable_signal_gui; ///< Show the signal GUI when the signal button is pressed
+
+ bool reserve_paths; ///< Always reserve paths regardless of signal type
+ bool build_pbs_signals; ///< Build advanced PBS signals
};
extern Patches _patches;
Index: src/signal.cpp
===================================================================
--- src/signal.cpp (revision 12052)
+++ src/signal.cpp (working copy)
@@ -256,6 +256,7 @@
SF_GREEN = 1 << 3, ///< green exitsignal found
SF_GREEN2 = 1 << 4, ///< two or more green exits found
SF_FULL = 1 << 5, ///< some of buffers was full, do not continue
+ SF_PBS = 1 << 6, ///< pbs signal found
};
DECLARE_ENUM_AS_BIT_SET(SigFlags)
@@ -322,13 +323,17 @@
Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]);
Trackdir reversedir = ReverseTrackdir(trackdir);
/* add (tile, reversetrackdir) to 'to-be-updated' set when there is
- * ANY signal in REVERSE direction
+ * ANY conventional signal in REVERSE direction
* (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
if (HasSignalOnTrackdir(tile, reversedir)) {
- if (!_tbuset.Add(tile, reversedir)) return flags | SF_FULL;
+ if (sig == SIGTYPE_PBS) {
+ flags |= SF_PBS;
+ } else {
+ if (!_tbuset.Add(tile, reversedir)) return flags | SF_FULL;
+ }
}
/* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */
- if (!(flags & SF_GREEN2) && (sig & SIGTYPE_EXIT) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
+ if (!(flags & SF_GREEN2) && IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
if (flags & SF_EXIT) flags |= SF_EXIT2; // found two (or more) exits
flags |= SF_EXIT; // found at least one exit - allow for compiler optimizations
if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
@@ -336,6 +341,7 @@
flags |= SF_GREEN;
}
}
+
continue;
}
}
@@ -418,7 +424,7 @@
SignalState newstate = SIGNAL_STATE_GREEN;
/* determine whether the new state is red */
- if (flags & SF_TRAIN) {
+ if ((flags & SF_TRAIN) || sig == SIGTYPE_NOENTRY) {
/* train in the segment */
newstate = SIGNAL_STATE_RED;
} else {
@@ -433,13 +439,13 @@
newstate = SIGNAL_STATE_RED;
}
} else { // entry, at least one exit, no green exit
- if (sig & SIGTYPE_ENTRY && (flags & SF_EXIT && !(flags & SF_GREEN))) newstate = SIGNAL_STATE_RED;
+ if (IsPresignalEntry(tile, TrackdirToTrack(trackdir)) && (flags & SF_EXIT && !(flags & SF_GREEN))) newstate = SIGNAL_STATE_RED;
}
}
/* only when the state changes */
if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
- if (sig & SIGTYPE_EXIT) {
+ if (IsPresignalExit(tile, TrackdirToTrack(trackdir))) {
/* for pre-signal exits, add block to the global set */
DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
_globset.Add(tile, exitdir); // do not check for full global set, first update all signals
@@ -465,15 +471,15 @@
* Updates blocks in _globset buffer
*
* @param owner player whose signals we are updating
- * @return false iff presignal entry would be green (needed for trains leaving depot)
+ * @return state of the first block from _globset
* @pre IsValidPlayer(owner)
*/
-static bool UpdateSignalsInBuffer(Owner owner)
+static SigSegState UpdateSignalsInBuffer(Owner owner)
{
assert(IsValidPlayer(owner));
bool first = true; // first block?
- bool state = false; // value to return
+ SigSegState state = SIGSEG_FREE; // value to return
TileIndex tile;
DiagDirection dir;
@@ -532,7 +538,11 @@
if (first) {
first = false;
- state = (flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL); // true iff train CAN'T leave the depot
+ if (flags & SF_PBS) {
+ state = SIGSEG_PBS;
+ } else if ((flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN) || (flags & SF_FULL))) {
+ state = SIGSEG_FULL;
+ }
}
/* do not do anything when some buffer was full */
@@ -627,9 +637,9 @@
* @param tile tile where we start
* @param side side of tile
* @param owner owner whose signals we will update
- * @return false iff train can leave depot
+ * @return the state of the signal segment
*/
-bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
+SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
{
assert(_globset.IsEmpty());
_globset.Add(tile, side);
Index: src/signal_func.h
===================================================================
--- src/signal_func.h (revision 12052)
+++ src/signal_func.h (working copy)
@@ -41,7 +41,14 @@
return _signal_on_track[track];
}
-bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner);
+/** State of the signal segment */
+enum SigSegState {
+ SIGSEG_FREE, ///< Free and has no pre-signal exits or at least one green exit
+ SIGSEG_FULL, ///< Occupied by a train
+ SIGSEG_PBS ///< Segment is a PBS segment
+};
+
+SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner);
void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner);
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner);
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner);
Index: src/station_cmd.cpp
===================================================================
--- src/station_cmd.cpp (revision 12052)
+++ src/station_cmd.cpp (working copy)
@@ -1183,6 +1183,9 @@
continue;
}
+ /* Don't remove tile if it is reserved. */
+ if (GetRailwayStationReservation(tile2)) continue;
+
/* If there is a vehicle on ground, do not allow to remove (flood) the tile */
if (!EnsureNoVehicleOnGround(tile2)) {
continue;
@@ -2152,6 +2155,12 @@
* but this is something else. If AI builds station with 114 it looks all weird */
DrawGroundSprite(image, HasBit(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE);
+ /* PBS debugging, draw reserved tracks darker */
+ if (_debug_pbs_level >= 2 && IsRailwayStation(ti->tile) && GetRailwayStationReservation(ti->tile)) {
+ const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+ DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
+ }
+
if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
if (HasBit(roadtypes, ROADTYPE_TRAM)) {
Index: src/station_map.h
===================================================================
--- src/station_map.h (revision 12052)
+++ src/station_map.h (working copy)
@@ -185,7 +185,42 @@
!IsStationTileBlocked(t1);
}
+/**
+ * Get the reservation state of the rail station
+ * @pre IsRailwayStationTile(t)
+ * @param t the station tile
+ * @return reservation state
+ */
+static inline bool GetRailwayStationReservation(TileIndex t)
+{
+ assert(IsRailwayStationTile(t));
+ return HasBit(_m[t].m6, 2);
+}
+/**
+ * Set the reservation state of the rail station
+ * @pre IsRailwayStationTile(t)
+ * @param t the station tile
+ * @param b the reservation state
+ */
+static inline void SetRailwayStationReservation(TileIndex t, bool b)
+{
+ assert(IsRailwayStationTile(t));
+ SB(_m[t].m6, 2, 1, (byte)b & 1);
+}
+
+/**
+ * Get the reserved track bits for a waypoint
+ * @pre IsRailwayStationTile(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailStationReservation(TileIndex t)
+{
+ return GetRailwayStationReservation(t) ? AxisToTrackBits(GetRailStationAxis(t)) : TRACK_BIT_NONE;
+}
+
+
static inline DiagDirection GetDockDirection(TileIndex t)
{
StationGfx gfx = GetStationGfx(t);
@@ -251,6 +286,7 @@
_m[t].m3 = 0;
_m[t].m4 = 0;
_m[t].m5 = section;
+ _m[t].m6 = 0;
SB(_m[t].m6, 3, 3, st);
}
Index: src/track_func.h
===================================================================
--- src/track_func.h (revision 12052)
+++ src/track_func.h (working copy)
@@ -200,6 +200,21 @@
*/
/**
+ * Find the opposite track to a given track.
+ *
+ * TRACK_LOWER -> TRACK_UPPER and vice versa, likewise for left/right.
+ * TRACK_X is mapped to TRACK_Y and reversed.
+ *
+ * @param t the track to convert
+ * @return the opposite track
+ */
+static inline Track TrackToOppositeTrack(Track t)
+{
+ assert(t != INVALID_TRACK);
+ return (Track)(t ^ 1);
+}
+
+/**
* Maps a trackdir to the reverse trackdir.
*
* Returns the reverse trackdir of a Trackdir value. The reverse trackdir
Index: src/train.h
===================================================================
--- src/train.h (revision 12052)
+++ src/train.h (working copy)
@@ -275,6 +275,8 @@
int CheckTrainInDepot(const Vehicle *v, bool needs_to_be_stopped);
int CheckTrainStoppedInDepot(const Vehicle *v);
+void FreeTrainTrackReservation(const Vehicle *v);
+
/**
* This class 'wraps' Vehicle; you do not actually instantiate this class.
* You create a Vehicle using AllocateVehicle, so it is added to the pool
Index: src/train_cmd.cpp
===================================================================
--- src/train_cmd.cpp (revision 12052)
+++ src/train_cmd.cpp (working copy)
@@ -47,13 +47,19 @@
#include "autoreplace_gui.h"
#include "gfx_func.h"
#include "settings_type.h"
+#include "pbs.h"
+#include "yapf/follow_track.hpp"
+#include
#include "table/strings.h"
#include "table/train_cmd.h"
+static Track DoTrainPathfind(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool do_track_reservation);
+static bool TryReserveSafeTrack(const Vehicle* v, TileIndex tile, Trackdir td);
static bool TrainCheckIfLineEnds(Vehicle *v);
static void TrainController(Vehicle *v, bool update_image);
static TileIndex TrainApproachingCrossingTile(const Vehicle *v);
+static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile);
static const byte _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
@@ -1764,6 +1770,9 @@
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
}
+ /* Check for path reservation */
+ FreeTrainTrackReservation(v);
+
/* Check if we were approaching a rail/road-crossing */
TileIndex crossing = TrainApproachingCrossingTile(v);
@@ -1796,6 +1805,38 @@
/* maybe we are approaching crossing now, after reversal */
crossing = TrainApproachingCrossingTile(v);
if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
+
+ DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
+ DiagDirection sig_dir = IsTileType(v->tile, MP_TUNNELBRIDGE) || IsTileDepotType(v->tile, TRANSPORT_RAIL) ? INVALID_DIAGDIR : dir;
+ if (UpdateSignalsOnSegment(v->tile, sig_dir, v->owner) == SIGSEG_PBS) {
+ /* Try to reserve a path if the train is in a pbs block. */
+ TileIndex tile = TileAddByDiagDir(v->tile, dir);
+ TrackBits bits = TrackdirBitsToTrackBits((TrackdirBits)GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & DiagdirReachesTrackdirs(dir));
+
+ /* TODO: Don't reverse if no reservation possible or simply continue anyway
+ * and maybe crash? Simple variant implemented for now. */
+ if (!HasReservedTrack(tile, bits)) {
+ /* We might find an alternative path to the target, try it. */
+ bool path_not_found = true;
+ DoTrainPathfind(v, tile, dir, bits, &path_not_found, true);
+ if (path_not_found) FreeTrainTrackReservation(v);
+
+ if (!HasReservedTrack(tile, bits)) {
+ /* Still no reservation, try for any possible track. */
+ TryReserveSafeTrack(v, tile, GetVehicleTrackdir(v));
+ }
+
+ /* If we have a reservation, check for a signal to turn green. */
+ TrackBits new_bits = GetReservedTrackbits(tile) & bits;
+ if (new_bits != TRACK_BIT_NONE) {
+ Trackdir trackdir = TrackEnterdirToTrackdir(TrackBitsToTrack(new_bits), dir);
+ if (HasPbsSignalOnTrackdir(tile, trackdir)) {
+ SetSignalStateByTrackdir(tile, trackdir, SIGNAL_STATE_GREEN);
+ MarkTileDirtyByTile(tile);
+ }
+ }
+ }
+ }
}
/** Reverse train.
@@ -2223,6 +2264,44 @@
TrainPlayLeaveStationSound(this);
}
+static void CheckNextTrainTile(Vehicle *v)
+{
+ DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
+ TileIndex next_tile = TileAddByDiagDir(v->tile, dir);
+
+ if (v->tile == v->dest_tile) return;
+
+ /* Exit if the current order doesn't have a destination. */
+ if (v->current_order.type == OT_NOTHING || v->current_order.type == OT_LEAVESTATION) return;
+
+ /* Check if we reached our destination and it is not a TTDPatch non-stop destination */
+ if (v->current_order.type == OT_GOTO_STATION && IsRailwayStationTile(v->tile)) {
+ if (v->current_order.dest == GetStationIndex(v->tile) && !(_patches.new_nonstop && v->current_order.flags & OFB_NON_STOP)) return;
+ }
+
+ /* Tunnel/bridge entrance? */
+ if (IsTileType(v->tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(v->tile) == dir) return;
+ if (IsDepotTile(v->tile) && GetRailDepotDirection(v->tile) == ReverseDiagDir(dir)) return;
+
+ if (IsTileType(next_tile, MP_RAILWAY) && HasSignals(next_tile)) {
+ TrackBits bits = GetTrackBits(next_tile) & DiagdirReachesTracks(dir);
+ if (bits == TRACK_BIT_NONE || HasReservedTrack(next_tile, bits)) return;
+ /* A signal tile can't be a junction tile */
+ assert(KillFirstBit(bits) == TRACK_BIT_NONE);
+ Track track = TrackBitsToTrack(bits);
+ Trackdir tdir = TrackEnterdirToTrackdir(track, dir);
+ if (HasPbsSignalOnTrackdir(next_tile, tdir)) {
+ DoTrainPathfind(v, next_tile, dir, bits, NULL, true);
+
+ /* Did we get our reservation? */
+ if (HasReservedTrack(next_tile, bits)) {
+ SetSignalStateByTrackdir(next_tile, tdir, SIGNAL_STATE_GREEN);
+ MarkTileDirtyByTile(next_tile);
+ }
+ }
+ }
+}
+
static bool CheckTrainStayInDepot(Vehicle *v)
{
/* bail out if not all wagons are in the same depot or not in a depot at all */
@@ -2237,7 +2316,10 @@
return true;
}
+ SigSegState sig_state = UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
+
if (v->u.rail.force_proceed == 0) {
+ /* force proceed was not pressed */
if (++v->load_unload_time_rem < 37) {
InvalidateWindowClasses(WC_TRAINS_LIST);
return true;
@@ -2245,12 +2327,37 @@
v->load_unload_time_rem = 0;
- if (UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner)) {
+ if (sig_state == SIGSEG_FULL || GetDepotReservation(v->tile)) {
InvalidateWindowClasses(WC_TRAINS_LIST);
return true;
}
}
+ if (sig_state == SIGSEG_PBS) {
+ /* Try to reserve a path */
+ DiagDirection dir = GetRailDepotDirection(v->tile);
+ TileIndex next_tile = TileAddByDiagDir(v->tile, dir);
+
+ uint32 ts = GetTileTrackStatus(next_tile, TRANSPORT_RAIL, 0) & DiagdirReachesTrackdirs(dir);
+ TrackBits bits = TrackdirBitsToTrackBits((TrackdirBits)ts);
+
+ if ((GetDepotReservation(v->tile) || HasReservedTrack(next_tile, bits)) && v->u.rail.force_proceed == 0) {
+ /* tile is already reserved */
+ InvalidateWindowClasses(WC_TRAINS_LIST);
+ return true;
+ }
+
+ if (bits != TRACK_BIT_NONE) DoTrainPathfind(v, next_tile, dir, bits, NULL, true);
+
+ /* Did we get our reservation? */
+ if (!HasReservedTrack(next_tile, bits) && v->u.rail.force_proceed == 0) {
+ InvalidateWindowClasses(WC_TRAINS_LIST);
+ return true;
+ }
+ SetDepotReservation(v->tile, true);
+ if (_debug_pbs_level >= 2) MarkTileDirtyByTile(v->tile);
+ }
+
VehicleServiceInDepot(v);
InvalidateWindowClasses(WC_TRAINS_LIST);
TrainPlayLeaveStationSound(v);
@@ -2271,6 +2378,80 @@
return false;
}
+/** Clear the reservation of a tile that was just left by a wagon on track_dir. */
+static void ClearPathReservation(TileIndex tile, Trackdir track_dir)
+{
+ DiagDirection dir = TrackdirToExitdir(track_dir);
+
+ if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+ /* Are we just leaving a tunnel/bridge? */
+ if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
+ TileIndex end = GetOtherTunnelBridgeEnd(tile);
+
+ SetTunnelBridgeReservation(tile, false);
+ SetTunnelBridgeReservation(end, false);
+
+ if (_debug_pbs_level >= 2) {
+ MarkTileDirtyByTile(tile);
+ MarkTileDirtyByTile(end);
+ }
+ }
+ } else if (IsRailwayStationTile(tile)) {
+ TileIndex new_tile = TileAddByDiagDir(tile, dir);
+ /* If the new tile is not a further tile of the same station, we
+ * clear the reservation for the whole platform. */
+ if (!IsRailwayStationTile(new_tile) || GetStationIndex(tile) != GetStationIndex(new_tile)) {
+ SetRailwayStationPlatformReservation(tile, ReverseDiagDir(dir), false);
+ }
+ } else {
+ /* Any other tile */
+ UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
+ }
+}
+
+/** Free the reserved path in front of a vehicle. */
+void FreeTrainTrackReservation(const Vehicle *v)
+{
+ assert(IsFrontEngine(v));
+
+ CFollowTrackRail ft(v);
+
+ bool dont_free_tile = IsRailwayStationTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE);
+ TileIndex tile = v->tile;
+ Trackdir td = GetVehicleTrackdir(v);
+
+ /* Don't free reservation if it's not ours. */
+ if (TracksOverlap(GetReservedTrackbits(tile) | TrackToTrackBits(TrackdirToTrack(td)))) return;
+
+ do {
+ if (!ft.Follow(tile, td)) break;
+
+ tile = ft.m_new_tile;
+ TrackdirBits bits = (TrackdirBits)(ft.m_new_td_bits & (GetReservedTrackbits(tile) * 0x101));
+ td = RemoveFirstTrackdir(&bits);
+ assert(bits == TRACKDIR_BIT_NONE);
+
+ if (!IsValidTrackdir(td)) break;
+
+ if (HasNoEntrySignalOnTrackdir(tile, td)) break;
+ if (HasPbsSignalOnTrackdir(tile, td)) {
+ if (GetSignalStateByTrackdir(tile, td) == SIGNAL_STATE_RED) {
+ /* Red PBS signal? Can't be our reservation, would be green then. */
+ break;
+ } else {
+ /* Turn the signal back to red. */
+ SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_RED);
+ MarkTileDirtyByTile(tile);
+ }
+ }
+
+ /* Don't free first station/bridge/tunnel if we are on it. */
+ if (!(dont_free_tile && (ft.m_is_station || ft.m_is_tunnel || ft.m_is_bridge))) ClearPathReservation(tile, td);
+
+ dont_free_tile = false;
+ } while (true);
+}
+
/* Check for station tiles */
struct TrainTrackFollowerData {
TileIndex dest_coords;
@@ -2347,34 +2528,28 @@
static const byte _pick_track_table[6] = {1, 3, 2, 2, 0, 0};
-/* choose a track */
-static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
+static Track DoTrainPathfind(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool do_track_reservation)
{
Track best_track;
- /* pathfinders are able to tell that route was only 'guessed' */
- bool path_not_found = false;
#ifdef PF_BENCHMARK
TIC()
#endif
- assert((tracks & ~TRACK_BIT_MASK) == 0);
+ if (path_not_found) *path_not_found = false;
- /* quick return in case only one possible track is available */
- if (KillFirstBit(tracks) == TRACK_BIT_NONE) return FindFirstTrack(tracks);
-
if (_patches.yapf.rail_use_yapf) {
- Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, tracks, &path_not_found);
+ Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, tracks, path_not_found, do_track_reservation);
if (trackdir != INVALID_TRACKDIR) {
best_track = TrackdirToTrack(trackdir);
} else {
best_track = FindFirstTrack(tracks);
}
- } else if (_patches.new_pathfinding_all) { /* Use a new pathfinding for everything */
+ } else if (_patches.new_pathfinding_all || do_track_reservation) { /* Use a new pathfinding for everything */
void* perf = NpfBeginInterval();
NPFFindStationOrTileData fstd;
- NPFFillWithOrderData(&fstd, v);
+ NPFFillWithOrderData(&fstd, v, do_track_reservation);
/* The enterdir for the new tile, is the exitdir for the old tile */
Trackdir trackdir = GetVehicleTrackdir(v);
assert(trackdir != 0xff);
@@ -2391,7 +2566,7 @@
the direction we need to take to get there, if ftd.best_bird_dist is not 0,
we did not find our target, but ftd.best_trackdir contains the direction leading
to the tile closest to our target. */
- if (ftd.best_bird_dist != 0) path_not_found = true;
+ if (ftd.best_bird_dist != 0 && path_not_found != NULL) *path_not_found = true;
/* Discard enterdir information, making it a normal track */
best_track = TrackdirToTrack(ftd.best_trackdir);
}
@@ -2413,7 +2588,7 @@
v->u.rail.compatible_railtypes, enterdir, (NTPEnumProc*)NtpCallbFindStation, &fd);
/* check whether the path was found or only 'guessed' */
- if (fd.best_bird_dist != 0) path_not_found = true;
+ if (fd.best_bird_dist != 0 && path_not_found != NULL) *path_not_found = true;
if (fd.best_track == 0xff) {
/* blaha */
@@ -2425,6 +2600,147 @@
int time = NpfEndInterval(perf);
DEBUG(yapf, 4, "[NTPT] %d us - %d rounds - %d open - %d closed -- ", time, 0, 0, 0);
}
+
+#ifdef PF_BENCHMARK
+ TOC("PF time = ", 1)
+#endif
+
+ return best_track;
+}
+
+struct TileTracks {
+ TileIndex tile;
+ TrackdirBits tracks;
+ Track track;
+
+ TileTracks(const Vehicle *v, TileIndex _tile, Trackdir td) : tile(_tile), track(INVALID_TRACK)
+ {
+ DiagDirection enterdir = TrackdirToExitdir(td);
+ uint32 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0);
+ /* no tracks || not compatible || wrong owner || depot facing backwards ||
+ * tunnel/bridge from wrong side --> no usable trackdirs */
+ if (GB(ts, 0, 16) == 0 || !CheckCompatibleRail(v, tile) || GetTileOwner(tile) != v->owner ||
+ (IsTileDepotType(tile, TRANSPORT_RAIL) && GetRailDepotDirection(tile) == enterdir) ||
+ (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(tile) != enterdir)) {
+ tracks = TRACKDIR_BIT_NONE;
+ } else {
+ tracks = (TrackdirBits)ts & DiagdirReachesTrackdirs(enterdir);
+ if ((_patches.new_pathfinding_all || _patches.yapf.rail_use_yapf) && _patches.forbid_90_deg) {
+ tracks &= ~TrackdirCrossesTrackdirs(td);
+ }
+ }
+ }
+};
+
+/**
+ * Try to reserve any path to a safe tile, ignoring the vehicle's destination.
+ * Safe tiles are tiles in front of a signal, depots and station tiles at end of line.
+ *
+ * @param v the vehicle
+ * @param tile the tile the vehicle is about to enter
+ * @param td the trackdir the vehicle currently is on
+ * @return true if a path to a safe stopping tile could be reserved.
+ */
+static bool TryReserveSafeTrack(const Vehicle* v, TileIndex tile, Trackdir td)
+{
+ std::stack visited;
+
+ visited.push(TileTracks(v, tile, td));
+
+ while (!visited.empty()) {
+ TileTracks &top = visited.top();
+
+ /* Is this a safe stop tile? Junction tiles can't be safe stops. */
+ if (top.tracks != TRACKDIR_BIT_NONE && KillFirstBit(top.tracks) == TRACKDIR_BIT_NONE) {
+ Trackdir track = FindFirstTrackdir(top.tracks);
+
+ /* Front of a signal is safe. */
+ if (IsTileType(top.tile, MP_RAILWAY) && HasSignalOnTrackdir(top.tile, track)) break;
+ /* A depot is safe. */
+ if (IsTileDepotType(top.tile, TRANSPORT_RAIL) && !GetDepotReservation(top.tile)) {
+ SetDepotReservation(top.tile, true);
+ break;
+ }
+
+ /* A station tile which is at the end of line is safe. */
+ if (IsRailwayStationTile(top.tile)) {
+ TileTracks tt(v, TileAddByDiagDir(top.tile, TrackdirToExitdir(track)), track);
+ if (tt.tracks == TRACKDIR_BIT_NONE && !GetRailwayStationReservation(top.tile)) {
+ SetRailwayStationReservation(top.tile, true);
+ break;
+ }
+ }
+ }
+
+ /* If we revisit a tile, free the old track reservation. */
+ if (top.track != INVALID_TRACK) {
+ UnreserveRailTrack(top.tile, top.track);
+ top.track = INVALID_TRACK;
+ }
+
+ /* No more possible tracks? Backtrack and try next tile. */
+ if (top.tracks == TRACKDIR_BIT_NONE) {
+ visited.pop();
+ continue;
+ }
+
+ /* Grab a trackdir and try to reserve it. */
+ Trackdir td = RemoveFirstTrackdir(&top.tracks);
+ if (!TryReserveRailTrack(top.tile, TrackdirToTrack(td))) continue;
+ top.track = TrackdirToTrack(td);
+
+ /* Reservation successful, then add the tile reached by following the trackdir. */
+ visited.push(TileTracks(v, TileAddByDiagDir(top.tile, TrackdirToExitdir(td)), td));
+ }
+
+ bool found = !visited.empty();
+
+ if (_debug_pbs_level >= 2) {
+ /* Mark path dirty */
+ while (!visited.empty()) {
+ MarkTileDirtyByTile(visited.top().tile);
+ visited.pop();
+ }
+ }
+
+ return found;
+}
+
+/* choose a track */
+static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
+{
+ Track best_track;
+ /* pathfinders are able to tell that route was only 'guessed' */
+ bool path_not_found = false;
+ bool do_track_reservation = _patches.reserve_paths;
+ bool turn_signal_green = false;
+
+ assert((tracks & ~TRACK_BIT_MASK) == 0);
+
+ TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & tracks);
+ assert(KillFirstBit(res_tracks) == TRACK_BIT_NONE);
+ /* do we have a suitable reserved track? */
+ if (res_tracks != TRACK_BIT_NONE) return FindFirstTrack(res_tracks);
+
+ /* quick return in case only one possible track is available */
+ if (KillFirstBit(tracks) == TRACK_BIT_NONE) {
+ Track track = FindFirstTrack(tracks);
+ /* We need to check for signals only here, as a junction tile can't have signals. */
+ if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
+ do_track_reservation = true;
+ turn_signal_green = true;
+ } else {
+ return track;
+ }
+ }
+
+ best_track = DoTrainPathfind(v, tile, enterdir, tracks, &path_not_found, do_track_reservation);
+
+ if (turn_signal_green && HasBit(GetReservedTrackbits(tile), best_track)) {
+ SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_GREEN);
+ MarkTileDirtyByTile(tile);
+ }
+
/* handle "path not found" state */
if (path_not_found) {
/* PF didn't find the route */
@@ -2450,10 +2766,6 @@
}
}
-#ifdef PF_BENCHMARK
- TOC("PF time = ", 1)
-#endif
-
return best_track;
}
@@ -2911,12 +3223,14 @@
if (!(tcc->v->vehstatus & VS_CRASHED)) {
/* two drivers + passengers killed in train tcc->v (if it was not crashed already) */
tcc->num += 2 + CountPassengersInTrain(tcc->v);
+ if (IsFrontEngine(tcc->v)) FreeTrainTrackReservation(tcc->v);
SetVehicleCrashed(tcc->v);
}
if (!(coll->vehstatus & VS_CRASHED)) {
/* two drivers + passengers killed in train coll (if it was not crashed already) */
tcc->num += 2 + CountPassengersInTrain(coll);
+ FreeTrainTrackReservation(coll);
SetVehicleCrashed(coll);
}
}
@@ -3007,6 +3321,10 @@
TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET);
return;
}
+ if (v->Next() == NULL && IsTileDepotType(v->tile, TRANSPORT_RAIL) && HasBit(r, VETS_ENTERED_WORMHOLE)) {
+ SetDepotReservation(v->tile, false);
+ if (_debug_pbs_level >= 2) MarkTileDirtyByTile(v->tile);
+ }
if (v->current_order.type == OT_LEAVESTATION) {
v->current_order.Free();
@@ -3073,6 +3391,10 @@
if (VehicleFromPos(o_tile, &rdir, &CheckVehicleAtSignal) == NULL) return;
}
}
+
+ /* Don't reverse in front of a PBS signal, it might cause crashes. */
+ if (IsPbsSignal(GetSignalType(gp.new_tile, TrackdirToTrack(i)))) return;
+
goto reverse_train_direction;
}
} else {
@@ -3108,6 +3430,16 @@
if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
+ Track track = FindFirstTrack(chosen_track);
+ Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
+ if (IsFrontEngine(v) && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
+ SetSignalStateByTrackdir(gp.new_tile, tdir, SIGNAL_STATE_RED);
+ MarkTileDirtyByTile(gp.new_tile);
+ }
+
+ /* Clear any track reservation when the last vehicle leaves the tile */
+ if (v->Next() == NULL) ClearPathReservation(v->tile, GetVehicleTrackdir(v));
+
v->tile = gp.new_tile;
if (GetTileRailType(gp.new_tile) != GetTileRailType(gp.old_tile)) {
@@ -3169,6 +3501,8 @@
if (IsLevelCrossingTile(gp.old_tile)) UpdateLevelCrossing(gp.old_tile);
}
}
+
+ if (IsFrontEngine(v)) CheckNextTrainTile(v);
}
return;
@@ -3220,6 +3554,26 @@
MarkSingleVehicleDirty(v);
+ /* Clear a possible path reservation */
+ if (IsFrontEngine(v)) {
+ if (IsTileType(v->tile, MP_TUNNELBRIDGE) && GetTunnelBridgeReservation(v->tile)) {
+ DiagDirection dir = ReverseDiagDir(GetTunnelBridgeDirection(v->tile));
+ ClearPathReservation(v->tile, DiagdirToDiagTrackdir(dir));
+ } else if (IsRailwayStationTile(v->tile) && GetRailwayStationReservation(v->tile)) {
+ DiagDirection dir = AxisToDiagDir(GetRailStationAxis(v->tile));
+ SetRailwayStationPlatformReservation(v->tile, dir, false);
+ SetRailwayStationPlatformReservation(v->tile, ReverseDiagDir(dir), false);
+ } else if ((v->u.rail.track == TRACK_BIT_DEPOT && GetDepotReservation(v->tile)) || HasReservedTrack(v->tile, v->u.rail.track)) {
+ ClearPathReservation(v->tile, TrackToTrackdir(TrackBitsToTrack(v->u.rail.track)));
+ }
+ } else if ((v->u.rail.track & ~TRACK_BIT_MASK) == TRACK_BIT_NONE && u->tile != v->tile && HasReservedTrack(v->tile, v->u.rail.track)) {
+ DiagDirection dir = DiagDirByTileIndexDiff(TileIndexToTileIndexDiffC(u->tile, v->tile));
+ Trackdir td = TrackExitdirToTrackdir(TrackBitsToTrack(v->u.rail.track), dir);
+ assert(IsValidTrackdir(td));
+
+ ClearPathReservation(v->tile, td);
+ }
+
/* 'v' shouldn't be accessed after it has been deleted */
TrackBits track = v->u.rail.track;
TileIndex tile = v->tile;
Index: src/tunnelbridge_cmd.cpp
===================================================================
--- src/tunnelbridge_cmd.cpp (revision 12052)
+++ src/tunnelbridge_cmd.cpp (working copy)
@@ -878,6 +878,13 @@
image += GetTunnelBridgeDirection(ti->tile) * 2;
DrawGroundSprite(image, PAL_NONE);
+
+ /* PBS debugging, draw reserved tracks darker */
+ if (_debug_pbs_level >= 2 && GetTunnelBridgeTransportType(ti->tile) == TRANSPORT_RAIL && GetTunnelBridgeReservation(ti->tile)) {
+ const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+ DrawGroundSprite(DiagDirToAxis(GetTunnelBridgeDirection(ti->tile)) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
+ }
+
if (GetTunnelBridgeTransportType(ti->tile) == TRANSPORT_ROAD) {
DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
RoadTypes rts = GetRoadTypes(ti->tile);
Index: src/tunnelbridge_map.h
===================================================================
--- src/tunnelbridge_map.h (revision 12052)
+++ src/tunnelbridge_map.h (working copy)
@@ -10,6 +10,7 @@
#include "tile_map.h"
#include "bridge_map.h"
#include "tunnel_map.h"
+#include "track_func.h"
/**
@@ -77,4 +78,40 @@
return IsTunnel(t) ? GetOtherTunnelEnd(t) : GetOtherBridgeEnd(t);
}
+
+/**
+ * Get the reservation state of the rail tunnel/bridge
+ * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @return reservation state
+ */
+static inline bool GetTunnelBridgeReservation(TileIndex t)
+{
+ assert(IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL);
+ return HasBit(_m[t].m5, 4);
+}
+
+/**
+ * Set the reservation state of the rail tunnel/bridge
+ * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @param b the reservation state
+ */
+static inline void SetTunnelBridgeReservation(TileIndex t, bool b)
+{
+ assert(IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL);
+ SB(_m[t].m5, 4, 1, (byte)b & 1);
+}
+
+/**
+ * Get the reserved track bits for a rail tunnel/bridge
+ * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailTunnelBridgeReservation(TileIndex t)
+{
+ return GetTunnelBridgeReservation(t) ? AxisToTrackBits(DiagDirToAxis(GetTunnelBridgeDirection(t))) : TRACK_BIT_NONE;
+}
+
#endif /* TUNNELBRIDGE_MAP_H */
Index: src/yapf/follow_track.hpp
===================================================================
--- src/yapf/follow_track.hpp (revision 12052)
+++ src/yapf/follow_track.hpp (working copy)
@@ -9,7 +9,7 @@
/** Track follower helper template class (can serve pathfinders and vehicle
* controllers). See 6 different typedefs below for 3 different transport
- * types w/ of w/o 90-deg turns allowed */
+ * types w/ or w/o 90-deg turns allowed */
template
struct CFollowTrackT : public FollowTrack_t
{
Index: src/yapf/yapf.h
===================================================================
--- src/yapf/yapf.h (revision 12052)
+++ src/yapf/yapf.h (working copy)
@@ -32,7 +32,7 @@
* @param path_not_found [out] true is returned if no path can be found (returned Trackdir is only a 'guess')
* @return the best trackdir for next turn or INVALID_TRACKDIR if the path could not be found
*/
-Trackdir YapfChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found);
+Trackdir YapfChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track = false);
/** Used by RV multistop feature to find the nearest road stop that has a free slot.
* @param v RV (its current tile will be the origin)
Index: src/yapf/yapf_costrail.hpp
===================================================================
--- src/yapf/yapf_costrail.hpp (revision 12052)
+++ src/yapf/yapf_costrail.hpp (working copy)
@@ -5,6 +5,7 @@
#ifndef YAPF_COSTRAIL_HPP
#define YAPF_COSTRAIL_HPP
+#include "../pbs.h"
template
class CYapfCostRailT
@@ -128,6 +129,22 @@
return cost;
}
+ /** The cost for reserved tiles, including skipped ones. */
+ FORCEINLINE int ReservationCost(Node& n, TileIndex& tile, Trackdir trackdir, int skipped)
+ {
+ if (n.m_num_signals_passed >= m_sig_look_ahead_costs.Size() / 3) return 0;
+
+ TrackBits tdb = TrackToTrackBits(TrackdirToTrack(trackdir));
+ TrackBits res = GetReservedTrackbits(tile);
+ if ((res & tdb) != TRACK_BIT_NONE) {
+ return Yapf().PfGetSettings().rail_pbs_cross_penalty * (skipped + 1);
+ } else {
+ res |= tdb;
+ if (TracksOverlap(res)) return Yapf().PfGetSettings().rail_pbs_cross_penalty * (skipped + 1);
+ }
+ return 0;
+ }
+
int SignalCost(Node& n, TileIndex tile, Trackdir trackdir)
{
int cost = 0;
@@ -136,53 +153,64 @@
if (IsTileType(tile, MP_RAILWAY)) {
bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir));
bool has_signal_along = HasSignalOnTrackdir(tile, trackdir);
- if (has_signal_against && !has_signal_along) {
+ if (HasNoEntrySignalOnTrackdir(tile, trackdir)) {
+ // no-entry signal ahead
+ n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
+ } else if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) {
// one-way signal in opposite direction
n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
- } else if (has_signal_along) {
- SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
- // cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is
- int look_ahead_cost = (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) ? m_sig_look_ahead_costs.Data()[n.m_num_signals_passed] : 0;
- if (sig_state != SIGNAL_STATE_RED) {
- // green signal
- n.flags_u.flags_s.m_last_signal_was_red = false;
- // negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal
- if (look_ahead_cost < 0) {
- // add its negation to the cost
- cost -= look_ahead_cost;
- }
- } else {
- // we have a red signal in our direction
- // was it first signal which is two-way?
- if (Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) {
- // yes, the first signal is two-way red signal => DEAD END
- n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
- Yapf().m_stopped_on_first_two_way_signal = true;
- return -1;
- }
- SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir));
- n.m_last_red_signal_type = sig_type;
- n.flags_u.flags_s.m_last_signal_was_red = true;
+ } else {
+ if (has_signal_along) {
+ SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
+ // cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is
+ int look_ahead_cost = (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) ? m_sig_look_ahead_costs.Data()[n.m_num_signals_passed] : 0;
+ if (sig_state != SIGNAL_STATE_RED) {
+ // green signal
+ n.flags_u.flags_s.m_last_signal_was_red = false;
+ // negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal
+ if (look_ahead_cost < 0) {
+ // add its negation to the cost
+ cost -= look_ahead_cost;
+ }
+ } else {
+ // we have a red signal in our direction
+ // was it first signal which is two-way?
+ if (Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) {
+ // yes, the first signal is two-way red signal => DEAD END
+ n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
+ Yapf().m_stopped_on_first_two_way_signal = true;
+ return -1;
+ }
+ SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir));
+ n.m_last_red_signal_type = sig_type;
+ n.flags_u.flags_s.m_last_signal_was_red = true;
- // look-ahead signal penalty
- if (look_ahead_cost > 0) {
- // add the look ahead penalty only if it is positive
- cost += look_ahead_cost;
+ // look-ahead signal penalty
+ if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) {
+ // add the look ahead penalty only if it is positive
+ cost += look_ahead_cost;
+ }
+
+ // special signal penalties
+ if (n.m_num_signals_passed == 0) {
+ switch (sig_type) {
+ case SIGTYPE_COMBO:
+ case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
+ case SIGTYPE_NORMAL:
+ case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
+ default: break;
+ };
+ }
}
- // special signal penalties
- if (n.m_num_signals_passed == 0) {
- switch (sig_type) {
- case SIGTYPE_COMBO:
- case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
- case SIGTYPE_NORMAL:
- case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
- };
- }
+ n.m_num_signals_passed++;
+ n.m_segment->m_last_signal_tile = tile;
+ n.m_segment->m_last_signal_td = trackdir;
}
- n.m_num_signals_passed++;
- n.m_segment->m_last_signal_tile = tile;
- n.m_segment->m_last_signal_td = trackdir;
+
+ if (has_signal_against && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) {
+ cost += n.m_num_signals_passed < Yapf().PfGetSettings().rail_look_ahead_max_signals ? Yapf().PfGetSettings().rail_pbs_signal_back_penalty : 0;
+ }
}
}
return cost;
@@ -327,6 +355,9 @@
/* Slope cost. */
segment_cost += Yapf().SlopeCost(cur.tile, cur.td);
+ /* Reserved tiles. */
+ segment_cost += Yapf().ReservationCost(n, cur.tile, cur.td, tf->m_tiles_skipped);
+
/* Signal cost (routine can modify segment data). */
segment_cost += Yapf().SignalCost(n, cur.tile, cur.td);
end_segment_reason = segment.m_end_segment_reason;
Index: src/yapf/yapf_node_rail.hpp
===================================================================
--- src/yapf/yapf_node_rail.hpp (revision 12052)
+++ src/yapf/yapf_node_rail.hpp (working copy)
@@ -177,6 +177,37 @@
FORCEINLINE Trackdir GetLastTrackdir() const {assert(m_segment != NULL); return m_segment->m_last_td;}
FORCEINLINE void SetLastTileTrackdir(TileIndex tile, Trackdir td) {assert(m_segment != NULL); m_segment->m_last_tile = tile; m_segment->m_last_td = td;}
+ template
+ bool IterateTiles(const Vehicle* v, Tbase &obj, bool (Tfunc::*func)(TileIndex, Trackdir)) const
+ {
+ typename Tbase::TrackFollower ft(v);
+ TileIndex cur = base::GetTile();
+ Trackdir cur_td = base::GetTrackdir();
+
+ while (cur != GetLastTile()) {
+ if (!((obj.*func)(cur, cur_td))) return false;
+
+ ft.Follow(cur, cur_td);
+ cur = ft.m_new_tile;
+ assert(KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE);
+ cur_td = FindFirstTrackdir(ft.m_new_td_bits);
+
+ /* Did we skip tiles because of a station? */
+ if (ft.m_is_station && ft.m_tiles_skipped > 0) {
+ TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(cur_td));
+ TileIndex tile = TILE_ADD(cur, -diff * ft.m_tiles_skipped);
+
+ for (int i = 0; i < ft.m_tiles_skipped; ++i) {
+ if (!(obj.*func)(tile, cur_td)) return false;
+ tile = TILE_ADD(tile, diff);
+ }
+ }
+ }
+ assert(TrackdirToTrack(cur_td) == TrackdirToTrack(GetLastTrackdir()));
+
+ return (obj.*func)(cur, cur_td);
+ }
+
void Dump(DumpTarget &dmp) const
{
base::Dump(dmp);
Index: src/yapf/yapf_rail.cpp
===================================================================
--- src/yapf/yapf_rail.cpp (revision 12052)
+++ src/yapf/yapf_rail.cpp (working copy)
@@ -9,6 +9,7 @@
#include "yapf_costrail.hpp"
#include "yapf_destrail.hpp"
#include "../vehicle_func.h"
+#include "../pbs.h"
#define DEBUG_YAPF_CACHE 0
@@ -104,6 +105,13 @@
typedef typename Node::Key Key; ///< key to hash tables
protected:
+ TileIndex m_res_dest; ///< The reservation target tile
+ Trackdir m_res_dest_td; ///< The reservation target trackdir
+ Node *m_res_node; ///< The reservation target node
+ bool m_res_dest_res; ///< Is the target already reserved?
+ bool m_res_skip_tile; ///< Flag that the first tile of a node should be skipped when determining reservation target
+ TileIndex m_res_fail_tile; ///< The tile where the reservation failed
+
/// to access inherited path finder
FORCEINLINE Tpf& Yapf() {return *static_cast(this);}
@@ -121,16 +129,18 @@
/// return debug report character to identify the transportation type
FORCEINLINE char TransportTypeChar() const {return 't';}
- static Trackdir stChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found)
+ static Trackdir stChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track)
{
// create pathfinder instance
Tpf pf1;
- Trackdir result1 = pf1.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found);
+#if !DEBUG_YAPF_CACHE
+ Trackdir result1 = pf1.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found, reserve_track);
-#if DEBUG_YAPF_CACHE
+#else
+ Trackdir result1 = pf1.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found, false);
Tpf pf2;
pf2.DisableCache(true);
- Trackdir result2 = pf2.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found);
+ Trackdir result2 = pf2.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found, reserve_track);
if (result1 != result2) {
DEBUG(yapf, 0, "CACHE ERROR: ChooseRailTrack() = [%d, %d]", result1, result2);
DumpTarget dmp1, dmp2;
@@ -148,7 +158,7 @@
return result1;
}
- FORCEINLINE Trackdir ChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found)
+ FORCEINLINE Trackdir ChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track)
{
// set origin and destination nodes
Yapf().SetOrigin(v->tile, GetVehicleTrackdir(v), INVALID_TILE, INVALID_TRACKDIR, 1, true);
@@ -166,21 +176,117 @@
Trackdir next_trackdir = INVALID_TRACKDIR;
Node *pNode = Yapf().GetBestNode();
if (pNode != NULL) {
+ // reserve till end of path
+ m_res_dest = pNode->GetLastTile();
+ m_res_dest_td = pNode->GetLastTrackdir();
+ m_res_node = pNode;
+ m_res_dest_res = HasReservedTrack(m_res_dest, TrackToTrackBits(TrackdirToTrack(m_res_dest_td)));
+ if (!m_res_dest_res && IsRailwayStationTile(m_res_dest)) {
+ // Check the tile after a station, if it is reserved we can't
+ // use it as it might lead to a crash.
+ TrackFollower ft(v);
+ if (ft.Follow(m_res_dest, m_res_dest_td)) m_res_dest_res = HasReservedTrack(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits));
+ }
+
// path was found or at least suggested
// walk through the path back to the origin
Node* pPrev = NULL;
while (pNode->m_parent != NULL) {
pPrev = pNode;
pNode = pNode->m_parent;
+
+ // a reservation can only stop at the first or second signal, so no
+ // need to search nodes with more passed signals.
+ if (pPrev->m_num_signals_passed > 0 && pNode->m_num_signals_passed <= 1) Yapf().FindSignalOnNode(v, pPrev);
}
// return trackdir from the best origin node (one of start nodes)
Node& best_next_node = *pPrev;
- assert(best_next_node.GetTile() == tile);
next_trackdir = best_next_node.GetTrackdir();
+
+ if (reserve_track) Yapf().TryReserveTrack(v);
}
return next_trackdir;
}
+ bool FindSignalProc(TileIndex tile, Trackdir td)
+ {
+ if (IsTileType(tile, MP_RAILWAY) && (HasSignalOnTrackdir(tile, td) || IsRailDepot(tile)) && !m_res_skip_tile) {
+ m_res_dest = tile;
+ m_res_dest_td = td;
+ m_res_dest_res = HasReservedTrack(tile, TrackToTrackBits(TrackdirToTrack(td)));
+ return false; /* Don't continue search. */
+ }
+ m_res_skip_tile = false;
+ return true;
+ }
+
+ void FindSignalOnNode(const Vehicle *v, Node *node)
+ {
+ assert(node->m_parent != NULL);
+
+ /* Skip the first tile of the second segment, otherwise we could never
+ * pass a signal right in front of us. */
+ m_res_skip_tile = node->m_parent->m_parent == NULL;
+
+ if (!node->IterateTiles(v, *this, &Tpf::FindSignalProc)) {
+ if (IsTileDepotType(m_res_dest, TRANSPORT_RAIL)) {
+ m_res_node = node;
+ } else {
+ /* Reserve tracks only to the tile in front of the signal. */
+ TrackFollower ft(v);
+ ft.Follow(m_res_dest, ReverseTrackdir(m_res_dest_td));
+ /* Did we skip a station? */
+ if (ft.m_is_station) ft.m_new_tile = TILE_ADD(ft.m_new_tile, -ft.m_tiles_skipped * TileOffsByDiagDir(ft.m_exitdir));
+
+ /* Update node if the current tile is the first on the old node. */
+ m_res_node = m_res_dest == node->GetTile() ? node->m_parent : node;
+ m_res_dest = ft.m_new_tile;
+ m_res_dest_td = ReverseTrackdir(FindFirstTrackdir(ft.m_new_td_bits));
+ }
+ }
+ }
+
+ bool ReserveSingleTrack(TileIndex tile, Trackdir td)
+ {
+ if (!TryReserveRailTrack(tile, TrackdirToTrack(td))) {
+ /* Tile couldn't be reserved, undo. */
+ m_res_fail_tile = tile;
+ return false;
+ }
+ return tile != m_res_dest;
+ }
+
+ bool UnreserveSingleTrack(TileIndex tile, Trackdir td)
+ {
+ if (tile != m_res_fail_tile) UnreserveRailTrack(tile, TrackdirToTrack(td));
+ return tile != m_res_dest && tile != m_res_fail_tile;
+ }
+
+ void TryReserveTrack(const Vehicle *v)
+ {
+ m_res_fail_tile = INVALID_TILE;
+
+ /* Don't bother if the target is reserved. */
+ if (m_res_dest_res) return;
+
+ Node *node = m_res_node;
+ while (node->m_parent != NULL) {
+ node->IterateTiles(v, *this, &Tpf::ReserveSingleTrack);
+ if (m_res_fail_tile != INVALID_TILE) {
+ /* Reservation failed, undo. */
+ Node* fail_node = m_res_node;
+ do {
+ fail_node->IterateTiles(v, *this, &Tpf::UnreserveSingleTrack);
+ } while (fail_node != node && (fail_node = fail_node->m_parent) != NULL);
+ return;
+ }
+ node = node->m_parent;
+ }
+
+ if (Yapf().CanUseGlobalCache(*m_res_node))
+ YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+ }
+
static bool stCheckReverseTrain(Vehicle* v, TileIndex t1, Trackdir td1, TileIndex t2, Trackdir td2)
{
Tpf pf1;
@@ -247,10 +353,10 @@
struct CYapfAnyDepotRail2 : CYapfT > {};
-Trackdir YapfChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found)
+Trackdir YapfChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track)
{
// default is YAPF type 2
- typedef Trackdir (*PfnChooseRailTrack)(Vehicle*, TileIndex, DiagDirection, TrackBits, bool*);
+ typedef Trackdir (*PfnChooseRailTrack)(Vehicle*, TileIndex, DiagDirection, TrackBits, bool*, bool);
PfnChooseRailTrack pfnChooseRailTrack = &CYapfRail1::stChooseRailTrack;
// check if non-default YAPF type needed
@@ -258,7 +364,7 @@
pfnChooseRailTrack = &CYapfRail2::stChooseRailTrack; // Trackdir, forbid 90-deg
}
- Trackdir td_ret = pfnChooseRailTrack(v, tile, enterdir, tracks, path_not_found);
+ Trackdir td_ret = pfnChooseRailTrack(v, tile, enterdir, tracks, path_not_found, reserve_track);
return td_ret;
}
Index: src/yapf/yapf_settings.h
===================================================================
--- src/yapf/yapf_settings.h (revision 12052)
+++ src/yapf/yapf_settings.h (working copy)
@@ -58,6 +58,8 @@
YS_DEF(int32 , rail_look_ahead_signal_p0) ///< constant in polynomial penalty function
YS_DEF(int32 , rail_look_ahead_signal_p1) ///< constant in polynomial penalty function
YS_DEF(int32 , rail_look_ahead_signal_p2) ///< constant in polynomial penalty function
+ YS_DEF(uint32, rail_pbs_cross_penalty) ///< penalty for crossing a reserved tile
+ YS_DEF(uint32, rail_pbs_signal_back_penalty) ///< penalty for passing a pbs signal from the backside
YS_DEF(uint32, rail_longer_platform_penalty) ///< penalty for longer station platform than train
YS_DEF(uint32, rail_longer_platform_per_tile_penalty) ///< penalty for longer station platform than train (per tile)