From 0eb6b394d186a33d1b49b91992bcdeb28d385396 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 15 Jul 2007 15:07:16 +0200 Subject: Add debug level for PBS --- src/debug.cpp | 2 ++ src/debug.h | 1 + 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/src/debug.cpp b/src/debug.cpp index fb1e28a..c4970aa 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -31,6 +31,7 @@ int _debug_freetype_level; int _debug_sl_level; int _debug_station_level; int _debug_gamelog_level; +int _debug_pbs_level; struct DebugLevel { @@ -56,6 +57,7 @@ struct DebugLevel { DEBUG_LEVEL(sl), DEBUG_LEVEL(station), DEBUG_LEVEL(gamelog), + DEBUG_LEVEL(pbs), }; #undef DEBUG_LEVEL diff --git a/src/debug.h b/src/debug.h index 4048c16..127021d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -48,6 +48,7 @@ extern int _debug_sl_level; extern int _debug_station_level; extern int _debug_gamelog_level; + extern int _debug_pbs_level; void CDECL debug(const char *dbg, ...); void CDECL debug_print(const char *dbg, const char *buf); -- 1.5.6 From d202d15948bba40e09c30e84bff2bfa96d782019 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 22 May 2008 18:49:30 +0200 Subject: Add map accessors for PBS reservations. --- docs/landscape.html | 38 +++++++++++++++++++++ docs/landscape_grid.html | 16 ++++---- src/rail_map.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++ src/road_map.h | 37 ++++++++++++++++++++ src/station_map.h | 36 +++++++++++++++++++ src/track_func.h | 15 ++++++++ src/tunnelbridge_map.h | 39 +++++++++++++++++++++ 7 files changed, 257 insertions(+), 8 deletions(-) diff --git a/docs/landscape.html b/docs/landscape.html index 9493f35..e4bbf58 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -407,6 +407,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 @@ -425,6 +458,7 @@
  • +
  • m5 bit 4: pbs reservation state
  • m5 bit 7 set, bit 6 set: railway depot @@ -452,6 +486,7 @@
  • +
  • m5 bit 4: pbs reservation state
  • m6 bits 7..6 : Possibility of a bridge above, in the direction specified
  • @@ -582,6 +617,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
  • @@ -898,6 +934,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
  • m7: animation frame
  • @@ -1369,6 +1406,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
      diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index f47b7f4..da1a86c 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -90,7 +90,7 @@ the array so you can quickly see what is used and what is not. rail XXXX XXXX ~~~X XXXX - OOOO OOOO OOOO OOOO + OOOO XXXX OOOO OOOO OOOO ~~XX OOOO XXXX XXXX XXXX @@ -101,7 +101,7 @@ the array so you can quickly see what is used and what is not. rail with signals -inherit- -inherit- - OOOO OOOO OXXX OXXX + OOOO XXXX OXXX OXXX XXXX ~~XX XXXX XXXX -inherit- @@ -115,7 +115,7 @@ the array so you can quickly see what is used and what is not. OOOO OOOO OOOO OOOO OOOO ~~XX OOOO XXXX - XXOO OOXX + XXOX OXXX XXOO OOXX OOOO OOOO @@ -126,7 +126,7 @@ the array so you can quickly see what is used and what is not. XXXX XXXX XXXX XXXX OOOO ~~XX OOOO XXXX - XXOO OOOX + XXOX OOOX XXOO OOXX OOOO OOOO @@ -149,7 +149,7 @@ the array so you can quickly see what is used and what is not. -inherit- XXXX ~~XX OXXX XXXX - XXOO XXXX + XXOX XXXX XXOO OOXX -inherit- @@ -197,7 +197,7 @@ the array so you can quickly see what is used and what is not. XXXX ~~XX XXXX XXXX XXXX XXXX - OOXX XOXX + OOXX XXXX XXXX XXXX @@ -310,7 +310,7 @@ the array so you can quickly see what is used and what is not. OOOO OOOO OOOO OOOO OOOO ~XXX XOOO OOOO - XOOO ~XXX + XOOX ~XXX XXOO OOXX OOOO OOOO @@ -321,7 +321,7 @@ the array so you can quickly see what is used and what is not. OOOO OOOO XXXX OOOO OOOO ~XXX XOOO OOOO - XOOO ~XXX + XOOX ~XXX XXOO OOXX OOOO OOOO diff --git a/src/rail_map.h b/src/rail_map.h index 8c436d0..050e9b1 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -224,6 +224,90 @@ static inline WaypointID GetWaypointIndex(TileIndex t) 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 + if (track_b == 0) return TRACK_BIT_NONE; + return (TrackBits)(TrackToTrackBits(track) | (HasBit(_m[t].m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)); +} + +/** + * 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)); + assert(b != INVALID_TRACK_BIT); + assert(!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)); +} + +/** + * 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); +} + +#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; +} + + static inline SignalType GetSignalType(TileIndex t, Track track) { assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); diff --git a/src/road_map.h b/src/road_map.h index 756e3fc..b8a7b2f 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -253,6 +253,43 @@ static inline TrackBits GetCrossingRailBits(TileIndex tile) return AxisToTrackBits(GetCrossingRailAxis(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(IsLevelCrossing(t)); diff --git a/src/station_map.h b/src/station_map.h index 0ec6797..6d95e96 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -204,6 +204,41 @@ static inline bool IsCompatibleTrainStationTile(TileIndex t1, TileIndex t2) !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) { @@ -277,6 +312,7 @@ static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, { MakeStation(t, o, sid, STATION_RAIL, section + a); SetRailType(t, rt); + SetRailwayStationReservation(t, false); } static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) diff --git a/src/track_func.h b/src/track_func.h index e659121..144ea03 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -198,6 +198,21 @@ static inline bool IsValidTrackdir(Trackdir trackdir) */ /** + * 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 diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index bdc7099..afcfb58 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -11,6 +11,7 @@ #include "bridge_map.h" #include "tunnel_map.h" #include "transport_type.h" +#include "track_func.h" /** @@ -80,4 +81,42 @@ static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t) 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)); + assert(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)); + assert(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) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE; +} + #endif /* TUNNELBRIDGE_MAP_H */ -- 1.5.6 From 2e3329c35c67866951033fabd822dc1edb50a98c Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 18:55:42 +0200 Subject: Draw reserved tracks darker if a patch setting is set. --- src/lang/english.txt | 1 + src/rail_cmd.cpp | 33 +++++++++++++++++++++++++++++++++ src/road_cmd.cpp | 6 ++++++ src/settings.cpp | 1 + src/settings_gui.cpp | 1 + src/settings_type.h | 1 + src/station_cmd.cpp | 6 ++++++ src/tunnelbridge_cmd.cpp | 16 ++++++++++++++++ 8 files changed, 65 insertions(+), 0 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 26b9273..bb6f777 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1134,6 +1134,7 @@ STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_MAGLEV :Maglev STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_FIRST :First available STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_LAST :Last available STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_MOST_USED :Most used +STR_CONFIG_PATCHES_SHOW_TRACK_RESERVATION :{LTBLUE}Show reserved tracks: {ORANGE}{STRING1} STR_CONFIG_PATCHES_ALWAYS_BUILD_INFRASTRUCTURE :{LTBLUE}Show building tools when no suitable vehicles are available: {ORANGE}{STRING1} STR_CONFIG_PATCHES_MAX_TRAINS :{LTBLUE}Max trains per player: {ORANGE}{STRING1} diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index f750a30..0d57181 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1688,6 +1688,29 @@ static void DrawTrackBits(TileInfo* ti, TrackBits track) if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE); } + /* PBS debugging, draw reserved tracks darker */ + if (_settings_client.gui.show_track_reservation) { + TrackBits pbs = GetTrackReservation(ti->tile); + if (pbs & TRACK_BIT_X) { + if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) { + DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH); + } else { + DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH); + } + } + if (pbs & TRACK_BIT_Y) { + if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) { + DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); + } else { + DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, 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)); @@ -1702,6 +1725,11 @@ static void DrawTrackBits(TileInfo* ti, TrackBits track) default: break; } DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner])); + + if (_settings_client.gui.show_track_reservation && IsSteepSlope(ti->tileh) && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) { + static const byte _corner_to_track_sprite[] = {3, 1, 2, 0}; + AddSortableSpriteToDraw(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + 16); + } } } @@ -1846,6 +1874,11 @@ default_waypoint: DrawGroundSprite(image, PAL_NONE); + /* PBS debugging, draw reserved tracks darker */ + if (_settings_client.gui.show_track_reservation && GetWaypointReservation(ti->tile)) { + DrawGroundSprite(GetWaypointAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH); + } + if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti); foreach_draw_tile_seq(dtss, dts->seq) { diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 28d4e76..33711c3 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -1171,6 +1171,12 @@ static void DrawTile_Road(TileInfo *ti) } DrawGroundSprite(image, pal); + + /* PBS debugging, draw reserved tracks darker */ + if (_settings_client.gui.show_track_reservation && 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 (HasTileRoadType(ti->tile, ROADTYPE_TRAM)) { DrawGroundSprite(SPR_TRAMWAY_OVERLAY + (GetCrossingRoadAxis(ti->tile) ^ 1), pal); DrawTramCatenary(ti, GetCrossingRoadBits(ti->tile)); diff --git a/src/settings.cpp b/src/settings.cpp index 2e34cdb..7e30a75 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1788,6 +1788,7 @@ const SettingDesc _patch_settings[] = { SDTC_BOOL(gui.bridge_pillars, S, 0, true, STR_NULL, NULL), SDTC_BOOL(gui.auto_euro, S, 0, true, STR_NULL, NULL), SDTC_VAR(gui.news_message_timeout, SLE_UINT8, S, 0, 2, 1, 255, 0, STR_NULL, NULL), + SDTC_BOOL(gui.show_track_reservation, S, 0, false, STR_CONFIG_PATCHES_SHOW_TRACK_RESERVATION, RedrawScreen), #ifdef ENABLE_NETWORK SDTC_VAR(network.sync_freq, SLE_UINT16,C|S,NO, 100, 0, 100, 0, STR_NULL, NULL), diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 8489af7..cf7dced 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -613,6 +613,7 @@ static const char *_patches_ui[] = { "gui.timetable_in_ticks", "gui.default_rail_type", "gui.always_build_infrastructure", + "gui.show_track_reservation", }; static const char *_patches_construction[] = { diff --git a/src/settings_type.h b/src/settings_type.h index 0926db6..97eb0a9 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -79,6 +79,7 @@ struct GUISettings { int16 autorenew_months; ///< how many months from EOL of vehicles should autorenew trigger for new companies? int32 autorenew_money; ///< how much money before autorenewing for new companies? byte news_message_timeout; ///< how much longer than the news message "age" should we keep the message in the history + bool show_track_reservation; ///< highlight reserved tracks. }; /** Settings related to currency/unit systems. */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 42ed629..441ffb1 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2211,6 +2211,12 @@ static void DrawTile_Station(TileInfo *ti) image += total_offset; } DrawGroundSprite(image, HasBit(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE); + + /* PBS debugging, draw reserved tracks darker */ + if (_settings_client.gui.show_track_reservation && 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 (IsRailwayStation(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 47d7fcb..612c5fb 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -888,6 +888,13 @@ static void DrawTile_TunnelBridge(TileInfo *ti) image += tunnelbridge_direction * 2; DrawGroundSprite(image, PAL_NONE); + + /* PBS debugging, draw reserved tracks darker */ + if (_settings_client.gui.show_track_reservation && (transport_type == TRANSPORT_RAIL && GetTunnelBridgeReservation(ti->tile))) { + const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); + DrawGroundSprite(DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH); + } + if (transport_type == TRANSPORT_ROAD) { RoadTypes rts = GetRoadTypes(ti->tile); @@ -964,6 +971,15 @@ static void DrawTile_TunnelBridge(TileInfo *ti) /* Bridge heads are drawn solid no matter how invisibility/transparency is set */ AddSortableSpriteToDraw(psid->sprite, psid->pal, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 0 : 8, ti->z); + if (_settings_client.gui.show_track_reservation && transport_type == TRANSPORT_RAIL && GetTunnelBridgeReservation(ti->tile)) { + const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); + if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) { + AddSortableSpriteToDraw(DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + 8); + } else { + AddSortableSpriteToDraw(rti->base_sprites.single_sloped + tunnelbridge_direction, PALETTE_CRASH, ti->x, ti->y, 16, 16, 8, ti->z); + } + } + if (transport_type == TRANSPORT_ROAD) { RoadTypes rts = GetRoadTypes(ti->tile); -- 1.5.6 From 48d7bb79bda28023741265a4fc4ead0692ec3428 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 18:56:40 +0200 Subject: Function for getting the PBS reservation state of any tile. --- projects/openttd_vs80.vcproj | 8 +++++++ projects/openttd_vs90.vcproj | 8 +++++++ source.list | 2 + src/pbs.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++ src/pbs.h | 13 ++++++++++++ 5 files changed, 74 insertions(+), 0 deletions(-) create mode 100644 src/pbs.cpp create mode 100644 src/pbs.h diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 2d4f9c0..b19198b 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -664,6 +664,10 @@ > + + @@ -1264,6 +1268,10 @@ > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 4b33a7f..3523d11 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -661,6 +661,10 @@ > + + @@ -1261,6 +1265,10 @@ > + + diff --git a/source.list b/source.list index 8624afe..a46e52a 100644 --- a/source.list +++ b/source.list @@ -57,6 +57,7 @@ os_timer.cpp ottdres.rc #end pathfind.cpp +pbs.cpp players.cpp queue.cpp rail.cpp @@ -241,6 +242,7 @@ order_base.h order_func.h order_type.h pathfind.h +pbs.h player_base.h player_face.h player_func.h diff --git a/src/pbs.cpp b/src/pbs.cpp new file mode 100644 index 0000000..c75eb8c --- /dev/null +++ b/src/pbs.cpp @@ -0,0 +1,43 @@ +/* $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" + +/** + * 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; +} diff --git a/src/pbs.h b/src/pbs.h new file mode 100644 index 0000000..51a9ba2 --- /dev/null +++ b/src/pbs.h @@ -0,0 +1,13 @@ +/* $Id$ */ + +/** @file pbs.h PBS support routines */ + +#ifndef PBS_H +#define PBS_H + +#include "tile_type.h" +#include "track_type.h" + +TrackBits GetReservedTrackbits(TileIndex t); + +#endif /* PBS_H */ -- 1.5.6 From c42927c07ae0dfb9721b23e69e78f1750c37ac73 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 18:57:46 +0200 Subject: Reserving and unreserving of single tiles is now possible. --- src/pbs.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/pbs.h | 18 +++++++++ src/rail_map.h | 33 ++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletions(-) diff --git a/src/pbs.cpp b/src/pbs.cpp index c75eb8c..3870a2b 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -1,7 +1,6 @@ /* $Id$ */ /** @file pbs.cpp */ - #include "stdafx.h" #include "openttd.h" #include "pbs.h" @@ -9,6 +8,10 @@ #include "road_map.h" #include "station_map.h" #include "tunnelbridge_map.h" +#include "functions.h" +#include "debug.h" +#include "direction_func.h" +#include "settings_type.h" /** * Get the reserved trackbits for any tile, regardless of type. @@ -41,3 +44,114 @@ TrackBits GetReservedTrackbits(TileIndex t) } 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)); + assert(GetRailStationAxis(start) == DiagDirToAxis(dir)); + + do { + SetRailwayStationReservation(tile, b); + if (_settings_client.gui.show_track_reservation) 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 (_settings_client.gui.show_track_reservation) { + 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 (_settings_client.gui.show_track_reservation) { + 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; + } +} diff --git a/src/pbs.h b/src/pbs.h index 51a9ba2..4103f1e 100644 --- a/src/pbs.h +++ b/src/pbs.h @@ -6,8 +6,26 @@ #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 HasReservedTracks(TileIndex tile, TrackBits tracks) +{ + return (GetReservedTrackbits(tile) & tracks) != TRACK_BIT_NONE; +} + #endif /* PBS_H */ diff --git a/src/rail_map.h b/src/rail_map.h index 050e9b1..b4a455c 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -257,6 +257,39 @@ static inline void SetTrackReservation(TileIndex t, TrackBits b) } /** + * 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) -- 1.5.6 From 5193e5741ec440c9b4f77cf703be08df67b98a10 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 22 May 2008 18:50:10 +0200 Subject: Retain the reservation state when building/removing waypoints or level crossings. --- src/road_cmd.cpp | 7 ++++++- src/waypoint.cpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 33711c3..799d8a9 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -344,7 +344,10 @@ static CommandCost RemoveRoad(TileIndex tile, uint32 flags, RoadBits pieces, Roa if (flags & DC_EXEC) { RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt)); if (rts == ROADTYPES_NONE) { - MakeRailNormal(tile, GetTileOwner(tile), GetCrossingRailBits(tile), GetRailType(tile)); + TrackBits tracks = GetCrossingRailBits(tile); + bool reserved = GetCrossingReservation(tile); + MakeRailNormal(tile, GetTileOwner(tile), tracks, GetRailType(tile)); + if (reserved) SetTrackReservation(tile, tracks); } else { SetRoadTypes(tile, rts); } @@ -562,7 +565,9 @@ CommandCost CmdBuildRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { YapfNotifyTrackLayoutChange(tile, FindFirstTrack(GetTrackBits(tile))); /* Always add road to the roadtypes (can't draw without it) */ + bool reserved = HasBit(GetTrackReservation(tile), AxisToTrack(OtherAxis(roaddir))); MakeRoadCrossing(tile, _current_player, _current_player, _current_player, GetTileOwner(tile), roaddir, GetRailType(tile), RoadTypeToRoadTypes(rt) | ROADTYPES_ROAD, p2); + SetCrossingReservation(tile, reserved); UpdateLevelCrossing(tile, false); MarkTileDirtyByTile(tile); } diff --git a/src/waypoint.cpp b/src/waypoint.cpp index 6dd30cf..397c4ae 100644 --- a/src/waypoint.cpp +++ b/src/waypoint.cpp @@ -249,7 +249,9 @@ CommandCost CmdBuildTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint3 const StationSpec* statspec; + bool reserved = HasBit(GetTrackReservation(tile), AxisToTrack(axis)); MakeRailWaypoint(tile, GetTileOwner(tile), axis, GetRailType(tile), wp->index); + SetWaypointReservation(tile, reserved); MarkTileDirtyByTile(tile); statspec = GetCustomStationSpec(STAT_CLASS_WAYP, p1); @@ -319,7 +321,10 @@ CommandCost RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove) RedrawWaypointSign(wp); if (justremove) { - MakeRailNormal(tile, GetTileOwner(tile), GetRailWaypointBits(tile), GetRailType(tile)); + TrackBits tracks = GetRailWaypointBits(tile); + bool reserved = GetWaypointReservation(tile); + MakeRailNormal(tile, GetTileOwner(tile), tracks, GetRailType(tile)); + if (reserved) SetTrackReservation(tile, tracks); MarkTileDirtyByTile(tile); } else { DoClearSquare(tile); -- 1.5.6 From 6d4364cdaad7917d2f8478b5718ea3cce80752b6 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 22 May 2008 18:53:21 +0200 Subject: PBS signals added to the map array. The signals are drawn using the old Patch-like PBS sprites. --- docs/landscape.html | 26 ++++++++++++++++++-------- docs/landscape_grid.html | 2 +- src/lang/english.txt | 11 +++++++++++ src/misc/dbg_helpers.cpp | 2 +- src/misc_gui.cpp | 10 +++++----- src/openttd.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/rail_cmd.cpp | 36 ++++++++++++++++++++++++++++++------ src/rail_map.h | 30 ++++++++++++++++++++++++------ src/signal_type.h | 12 ++++++++---- 9 files changed, 138 insertions(+), 31 deletions(-) diff --git a/docs/landscape.html b/docs/landscape.html index e4bbf58..979c3ce 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -376,30 +376,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
      • diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index da1a86c..2b2cda2 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -101,7 +101,7 @@ the array so you can quickly see what is used and what is not. rail with signals -inherit- -inherit- - OOOO XXXX OXXX OXXX + OOOO XXXX XXXX XXXX XXXX ~~XX XXXX XXXX -inherit- diff --git a/src/lang/english.txt b/src/lang/english.txt index bb6f777..438653c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1666,12 +1666,23 @@ STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS :Railway track w 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 one-way advanced 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 one-way advanced 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 one-way advanced 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 one-way advanced 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 one-way advanced signals +STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS :Railway track with advanced and one-way advanced signals STR_MUST_REMOVE_RAILWAY_STATION_FIRST :{WHITE}Must remove railway station first diff --git a/src/misc/dbg_helpers.cpp b/src/misc/dbg_helpers.cpp index 6db02e8..1c34e25 100644 --- a/src/misc/dbg_helpers.cpp +++ b/src/misc/dbg_helpers.cpp @@ -49,7 +49,7 @@ CStrA ValueStr(DiagDirection dd) /** SignalType short names. */ static const char* signal_type_names[] = { - "NORMAL", "ENTRY", "EXIT", "COMBO", + "NORMAL", "ENTRY", "EXIT", "COMBO", "PBS", "NOENTRY", }; /** Return name of given SignalType. */ diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 9665ccf..818e7e3 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -58,13 +58,13 @@ static bool _savegame_sort_dirty; static const Widget _land_info_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 14, 11, 279, 0, 13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_BOTTOM, 14, 0, 279, 14, 99, 0x0, STR_NULL}, +{ WWT_CAPTION, RESIZE_NONE, 14, 11, 299, 0, 13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, RESIZE_BOTTOM, 14, 0, 299, 14, 99, 0x0, STR_NULL}, { WIDGETS_END}, }; static const WindowDesc _land_info_desc = { - WDP_AUTO, WDP_AUTO, 280, 100, 280, 100, + WDP_AUTO, WDP_AUTO, 300, 100, 300, 100, WC_LAND_INFO, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _land_info_widgets, @@ -90,13 +90,13 @@ public: for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) { if (StrEmpty(this->landinfo_data[i])) break; - DoDrawStringCentered(140, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING); + DoDrawStringCentered(150, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING); y += i == 0 ? 16 : 12; } y += 6; - if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) DrawStringMultiCenter(140, y, BindCString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]), this->width - 4); + if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) DrawStringMultiCenter(150, y, BindCString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]), this->width - 4); } LandInfoWindow(TileIndex tile) : Window(&_land_info_desc) { diff --git a/src/openttd.cpp b/src/openttd.cpp index 3c33f57..8fb38d1 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -2452,6 +2452,46 @@ bool AfterLoadGame() } } + /* 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(99)) { + 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; + } + } + } + if (_debug_gamelog_level > 0) GamelogPrintDebug(); return InitializeWindowsAndCaches(); diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 0d57181..c7c2cc6 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1452,7 +1452,7 @@ static void DrawSingleSignal(TileIndex tile, Track track, byte condition, uint i sprite = SPR_ORIGINAL_SIGNALS_BASE + image + condition; } else { /* All other signals are picked from add on sprites. */ - sprite = SPR_SIGNALS_BASE + (type - 1) * 16 + variant * 64 + image + condition; + sprite = SPR_SIGNALS_BASE + (type - 1) * 16 + variant * 64 + image + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0); } AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track)); @@ -2230,30 +2230,54 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td) 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 } }; diff --git a/src/rail_map.h b/src/rail_map.h index b4a455c..6f77dcd 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -341,19 +341,24 @@ static inline TrackBits GetRailDepotReservation(TileIndex t) } +static inline bool IsPbsSignal(SignalType s) +{ + return s == SIGTYPE_PBS || s == SIGTYPE_PBS_ONEWAY; +} + 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) @@ -378,15 +383,15 @@ static inline void CycleSignalSide(TileIndex t, Track track) 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 @@ -513,6 +518,19 @@ static inline void SetSignalStateByTrackdir(TileIndex tile, Trackdir trackdir, S } } +/** + * 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. diff --git a/src/signal_type.h b/src/signal_type.h index 5dccff6..0bcd107 100644 --- a/src/signal_type.h +++ b/src/signal_type.h @@ -14,10 +14,14 @@ enum SignalVariant { /** 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_PBS_ONEWAY = 5, ///< no-entry signal + SIGTYPE_LAST = SIGTYPE_PBS_ONEWAY, + SIGTYPE_LAST_NOPBS = SIGTYPE_COMBO }; -- 1.5.6 From 52a3675b1baf617a308f1c35412db4e3586fd074 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 19:01:21 +0200 Subject: Handle through and PBS signals correctly in the signal code. --- src/rail_cmd.cpp | 13 +++++++------ src/rail_map.h | 6 ++++++ src/signal.cpp | 18 +++++++++++++----- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index c7c2cc6..4d70dd1 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -2180,12 +2180,13 @@ static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode, 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) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E); if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W); diff --git a/src/rail_map.h b/src/rail_map.h index 6f77dcd..e9fbc21 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -371,6 +371,12 @@ static inline bool IsPresignalExit(TileIndex t, Track track) 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_PBS; +} + static inline void CycleSignalSide(TileIndex t, Track track) { byte sig; diff --git a/src/signal.cpp b/src/signal.cpp index 5d71cd0..b7b9f3b 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -256,6 +256,7 @@ enum SigFlags { 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) @@ -323,13 +324,19 @@ static SigFlags ExploreSegment(Owner owner) 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 (IsPbsSignal(sig)) { + flags |= SF_PBS; + } else if (!_tbuset.Add(tile, reversedir)) { + return flags | SF_FULL; + } } + if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) flags |= SF_PBS; + /* 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 @@ -337,6 +344,7 @@ static SigFlags ExploreSegment(Owner owner) flags |= SF_GREEN; } } + continue; } } @@ -434,13 +442,13 @@ static void UpdateSignalsAroundSegment(SigFlags flags) 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 -- 1.5.6 From 5e3dec42267e3bf6377932fcc20207cee9664a29 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 02:24:14 +0100 Subject: Handle through signals in the pathfinders. --- src/npf.cpp | 6 +++--- src/pathfind.cpp | 2 +- src/yapf/yapf_costrail.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/npf.cpp b/src/npf.cpp index e4498bb..478b62b 100644 --- a/src/npf.cpp +++ b/src/npf.cpp @@ -719,10 +719,10 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current) 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 (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; } { diff --git a/src/pathfind.cpp b/src/pathfind.cpp index 9757b09..e6a27df 100644 --- a/src/pathfind.cpp +++ b/src/pathfind.cpp @@ -674,7 +674,7 @@ start_at: if (HasSignals(tile)) { 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; } diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index abd4210..ddfdca6 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -136,7 +136,7 @@ public: 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 (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) { -- 1.5.6 From 939dc0922ae8b563fb190beed6d5adde6ec59e8a Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 22 May 2008 18:54:27 +0200 Subject: PBS signals can now be built with the normal signal tools. --- src/lang/english.txt | 8 ++++++++ src/rail_cmd.cpp | 28 ++++++++++++++++++++-------- src/rail_gui.cpp | 16 ++++++++++++---- src/settings.cpp | 2 ++ src/settings_gui.cpp | 2 ++ src/settings_type.h | 2 ++ 6 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 438653c..cfef885 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1170,6 +1170,14 @@ STR_CONFIG_PATCHES_ALLOW_SHARES :{LTBLUE}Allow b 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_DEFAULT_SIGNAL_TYPE :{LTBLUE}Signal type to build by default: {ORANGE}{STRING1} +STR_CONFIG_PATCHES_DEFAULT_SIGNAL_NORMAL :Normal +STR_CONFIG_PATCHES_DEFAULT_SIGNAL_PBS :Advanced +STR_CONFIG_PATCHES_DEFAULT_SIGNAL_PBSOWAY :One-way advanced +STR_CONFIG_PATCHES_CYCLE_SIGNAL_TYPES :{LTBLUE}Cycle through signal types: {ORANGE}{STRING1} +STR_CONFIG_PATCHES_CYCLE_SIGNAL_NORMAL :Normal only +STR_CONFIG_PATCHES_CYCLE_SIGNAL_PBS :Advanced only +STR_CONFIG_PATCHES_CYCLE_SIGNAL_ALL :All 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} diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 4d70dd1..c48101c 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -793,8 +793,10 @@ CommandCost CmdBuildTrainDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p * - 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-11)- start cycle from this signal type + * - p1 = (bit 12-14)-wrap around after this signal type * @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. */ @@ -803,10 +805,14 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 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 + SignalType cycle_start = (SignalType)GB(p1, 9, 3); + SignalType cycle_stop = (SignalType)GB(p1, 12, 3); CommandCost cost; + if (sigtype > SIGTYPE_LAST) return CMD_ERROR; + if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoTrainOnTrack(tile, track)) return CMD_ERROR; @@ -868,7 +874,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 if (p2 == 0) { if (!HasSignalOnTrack(tile, track)) { /* build new signals */ - SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track)); + SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track))); SetSignalType(tile, track, sigtype); SetSignalVariant(tile, track, sigvar); } else { @@ -885,10 +891,12 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 } } else if (ctrl_pressed) { - /* cycle between normal -> pre -> exit -> combo -> ... */ - sigtype = GetSignalType(tile, track); + /* cycle between cycle_start and cycle_end */ + sigtype = (SignalType)(GetSignalType(tile, track) + 1); + + if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start; - SetSignalType(tile, track, sigtype == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(sigtype + 1)); + SetSignalType(tile, track, sigtype); } else { /* cycle the signal side: both -> left -> right -> both -> ... */ CycleSignalSide(tile, track); @@ -901,6 +909,10 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 SetSignalVariant(tile, track, sigvar); } + if (IsPbsSignal(sigtype)) { + 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); diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index fe053b4..c4f6548 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -51,6 +51,9 @@ static bool _convert_signal_button; ///< convert signal button in the s static SignalVariant _cur_signal_variant; ///< set the signal variant (for signal GUI) static SignalType _cur_signal_type; ///< set the signal type (for signal GUI) +/* Map _patches.default_signal_type to the corresponding signal type */ +static const SignalType _default_signal_type[] = {SIGTYPE_NORMAL, SIGTYPE_PBS, SIGTYPE_PBS_ONEWAY}; + static struct { byte orientation; ///< Currently selected rail station orientation byte numtracks; ///< Currently selected number of tracks in station (if not \c dragdrop ) @@ -216,6 +219,9 @@ static void GenericPlaceSignals(TileIndex tile) } else { const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0); + /* Map _patches.cycle_signal_types to the lower and upper allowed signal type. */ + static const uint cycle_bounds[] = {SIGTYPE_NORMAL | (SIGTYPE_LAST_NOPBS << 3), SIGTYPE_PBS | (SIGTYPE_LAST << 3), SIGTYPE_NORMAL | (SIGTYPE_LAST << 3)}; + /* various bitstuffed elements for CmdBuildSingleSignal() */ uint32 p1 = track; @@ -223,13 +229,15 @@ static void GenericPlaceSignals(TileIndex tile) /* signal GUI is used */ SB(p1, 3, 1, _ctrl_pressed); SB(p1, 4, 1, _cur_signal_variant); - SB(p1, 5, 2, _cur_signal_type); - SB(p1, 7, 1, _convert_signal_button); + SB(p1, 5, 3, _cur_signal_type); + SB(p1, 8, 1, _convert_signal_button); + SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]); } else { SB(p1, 3, 1, _ctrl_pressed); SB(p1, 4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC)); - SB(p1, 5, 2, SIGTYPE_NORMAL); - SB(p1, 7, 1, 0); + SB(p1, 5, 3, _default_signal_type[_settings_client.gui.default_signal_type]); + SB(p1, 8, 1, 0); + SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]); } DoCommandP(tile, p1, 0, CcPlaySound1E, CMD_BUILD_SIGNALS | diff --git a/src/settings.cpp b/src/settings.cpp index 7e30a75..47ccd76 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1789,6 +1789,8 @@ const SettingDesc _patch_settings[] = { SDTC_BOOL(gui.auto_euro, S, 0, true, STR_NULL, NULL), SDTC_VAR(gui.news_message_timeout, SLE_UINT8, S, 0, 2, 1, 255, 0, STR_NULL, NULL), SDTC_BOOL(gui.show_track_reservation, S, 0, false, STR_CONFIG_PATCHES_SHOW_TRACK_RESERVATION, RedrawScreen), + SDTC_VAR(gui.default_signal_type, SLE_UINT8, S, MS, 0, 0, 2, 1, STR_CONFIG_PATCHES_DEFAULT_SIGNAL_TYPE, NULL), + SDTC_VAR(gui.cycle_signal_types, SLE_UINT8, S, MS, 0, 0, 2, 1, STR_CONFIG_PATCHES_CYCLE_SIGNAL_TYPES, NULL), #ifdef ENABLE_NETWORK SDTC_VAR(network.sync_freq, SLE_UINT16,C|S,NO, 100, 0, 100, 0, STR_NULL, NULL), diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index cf7dced..6830506 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -627,6 +627,8 @@ static const char *_patches_construction[] = { "gui.drag_signals_density", "game_creation.oil_refinery_limit", "gui.semaphore_build_before", + "gui.default_signal_type", + "gui.cycle_signal_types", }; static const char *_patches_stations[] = { diff --git a/src/settings_type.h b/src/settings_type.h index 97eb0a9..098d56f 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -80,6 +80,8 @@ struct GUISettings { int32 autorenew_money; ///< how much money before autorenewing for new companies? byte news_message_timeout; ///< how much longer than the news message "age" should we keep the message in the history bool show_track_reservation; ///< highlight reserved tracks. + uint8 default_signal_type; ///< the signal type to build by default. + uint8 cycle_signal_types; ///< what signal types to cycle with the build signal tool. }; /** Settings related to currency/unit systems. */ -- 1.5.6 From 6aaa488ce7d8d2a6cf238ba6e5a4864a69b10262 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 28 Jan 2008 22:23:56 +0100 Subject: Enable the auto-signal tool to build PBS signals and honor the default signal type. --- src/rail_cmd.cpp | 12 +++++++++++- src/rail_gui.cpp | 2 ++ 2 files changed, 13 insertions(+), 1 deletions(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index c48101c..5d2365e 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -907,6 +907,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 * direction of the first signal given as parameter by CmdBuildManySignals */ SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track))); SetSignalVariant(tile, track, sigvar); + SetSignalType(tile, track, sigtype); } if (IsPbsSignal(sigtype)) { @@ -986,6 +987,7 @@ static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal * - p2 = (bit 4) - 0 = signals, 1 = semaphores * - p2 = (bit 5) - 0 = build, 1 = remove signals * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill + * - p2 = (bit 7- 9) - default signal type * - p2 = (bit 24-31) - user defined signals_density */ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) @@ -1023,6 +1025,9 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, /* Autofill must start on a valid track to be able to avoid loops */ if (autofill && !HasTrack(tile, track)) return CMD_ERROR; + SignalType sigtype = (SignalType)GB(p2, 7, 3); + if (sigtype > SIGTYPE_LAST) return CMD_ERROR; + /* copy the signal-style of the first rail-piece if existing */ if (HasSignals(tile)) { signals = GetPresentSignals(tile) & SignalOnTrack(track); @@ -1030,8 +1035,10 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, /* 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); + signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track); } byte signal_dir = 0; @@ -1053,6 +1060,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, 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; @@ -1102,6 +1110,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, * - p2 = (bit 4) - 0 = signals, 1 = semaphores * - p2 = (bit 5) - 0 = build, 1 = remove signals * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill + * - p2 = (bit 7- 9) - default signal type * - p2 = (bit 24-31) - user defined signals_density * @see CmdSignalTrackHelper */ @@ -1165,6 +1174,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint3 * - p2 = (bit 4) - 0 = signals, 1 = semaphores * - p2 = (bit 5) - 0 = build, 1 = remove signals * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill + * - p2 = (bit 7- 9) - default signal type * - p2 = (bit 24-31) - user defined signals_density * @see CmdSignalTrackHelper */ diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index c4f6548..e7b27cd 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -555,11 +555,13 @@ static void HandleAutoSignalPlacement() SB(p2, 3, 1, 0); SB(p2, 4, 1, _cur_signal_variant); SB(p2, 6, 1, _ctrl_pressed); + SB(p2, 7, 3, _cur_signal_type); SB(p2, 24, 8, _settings_client.gui.drag_signals_density); } else { SB(p2, 3, 1, 0); SB(p2, 4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC)); SB(p2, 6, 1, _ctrl_pressed); + SB(p2, 7, 3, _default_signal_type[_settings_client.gui.default_signal_type]); SB(p2, 24, 8, _settings_client.gui.drag_signals_density); } -- 1.5.6 From 1b0b0b91b9a4e3becc47aa1df5637e81d7f7e231 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 16:13:41 +0100 Subject: Don't cycle through two-sided PBS signals during build. --- src/rail_cmd.cpp | 6 ++++++ src/rail_map.h | 2 +- 2 files changed, 7 insertions(+), 1 deletions(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 5d2365e..5d66f7a 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -888,6 +888,9 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 /* convert the present signal to the chosen type and variant */ SetSignalType(tile, track, sigtype); SetSignalVariant(tile, track, sigvar); + if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) { + SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track))); + } } } else if (ctrl_pressed) { @@ -897,6 +900,9 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start; SetSignalType(tile, track, sigtype); + if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) { + SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track))); + } } else { /* cycle the signal side: both -> left -> right -> both -> ... */ CycleSignalSide(tile, track); diff --git a/src/rail_map.h b/src/rail_map.h index e9fbc21..014ffd5 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -383,7 +383,7 @@ static inline void CycleSignalSide(TileIndex t, Track track) byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 6; sig = GB(_m[t].m3, pos, 2); - if (--sig == 0) sig = 3; + if (--sig == 0) sig = IsPbsSignal(GetSignalType(t, track)) ? 2 : 3; SB(_m[t].m3, pos, 2, sig); } -- 1.5.6 From b9861f9bee6efdc57189949c25e6cd32a479dbb5 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 4 Feb 2008 23:31:34 +0100 Subject: Add the new signals to the build signal GUI. --- src/lang/english.txt | 4 ++++ src/rail_gui.cpp | 32 ++++++++++++++++++++++++-------- src/table/sprites.h | 22 +++++++++++++--------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index cfef885..5be2ffd 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3570,10 +3570,14 @@ STR_BUILD_SIGNAL_SEMAPHORE_NORM_TIP :{BLACK}Standard STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TIP :{BLACK}Entry-Signal (semaphore){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red. STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TIP :{BLACK}Exit-Signal (semaphore){}Behaves in the same way as a normal signal but is necessary to trigger the correct colour on entry & combo pre-signals. STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TIP :{BLACK}Combo-Signal (semaphore){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of presignals. +STR_BUILD_SIGNAL_SEMAPHORE_PBS_TIP :{BLACK}Advanced Signal (semaphore){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Advanced signals can be passed from the backside. +STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TIP :{BLACK}One-way Advanced Signal (semaphore){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way signals cannot be passed from the backside. STR_BUILD_SIGNAL_ELECTRIC_NORM_TIP :{BLACK}Standard Signal (electric){}Signals are necessary to keep trains from crashing on railway networks with more than one train. STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TIP :{BLACK}Entry-Signal (electric){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red. STR_BUILD_SIGNAL_ELECTRIC_EXIT_TIP :{BLACK}Exit-Signal (electric){}Behaves in the same way as a normal signal but is necessary to trigger the correct colour on entry & combo pre-signals. STR_BUILD_SIGNAL_ELECTRIC_COMBO_TIP :{BLACK}Combo-Signal (electric){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of presignals. +STR_BUILD_SIGNAL_ELECTRIC_PBS_TIP :{BLACK}Advanced Signal (electric){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Advanced signals can be passed from the backside. +STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TIP :{BLACK}One-way Advanced Signal (electric){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way signals cannot be passed from the backside. STR_SIGNAL_CONVERT_TIP :{BLACK}Signal Convert{}When selected, clicking an existing signal will convert it to the selected signal type and variant, CTRL-click will toggle the existing variant. STR_DRAG_SIGNALS_DENSITY_TIP :{BLACK}Dragging signal density STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP :{BLACK}Decrease dragging signal density diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index e7b27cd..2796aa5 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1360,10 +1360,14 @@ enum BuildSignalWidgets { BSW_SEMAPHORE_ENTRY, BSW_SEMAPHORE_EXIT, BSW_SEMAPHORE_COMBO, + BSW_SEMAPHORE_PBS, + BSW_SEMAPHORE_PBS_OWAY, BSW_ELECTRIC_NORM, BSW_ELECTRIC_ENTRY, BSW_ELECTRIC_EXIT, BSW_ELECTRIC_COMBO, + BSW_ELECTRIC_PBS, + BSW_ELECTRIC_PBS_OWAY, BSW_CONVERT, BSW_DRAG_SIGNALS_DENSITY, BSW_DRAG_SIGNALS_DENSITY_DECREASE, @@ -1410,10 +1414,14 @@ public: this->DrawSignalSprite(BSW_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, -1, 13); // xsize of sprite + 1 == 10 this->DrawSignalSprite(BSW_SEMAPHORE_EXIT, SPR_IMG_SIGNAL_SEMAPHORE_EXIT, 0, 12); // xsize of sprite + 1 == 9 this->DrawSignalSprite(BSW_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_COMBO, 0, 12); // xsize of sprite + 1 == 9 + this->DrawSignalSprite(BSW_SEMAPHORE_PBS, SPR_IMG_SIGNAL_SEMAPHORE_PBS, 0, 12); // xsize of sprite + 1 == 9 + this->DrawSignalSprite(BSW_SEMAPHORE_PBS_OWAY, SPR_IMG_SIGNAL_SEMAPHORE_PBSOWAY, -1, 13); // xsize of sprite + 1 == 10 this->DrawSignalSprite(BSW_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_NORM, -1, 4); this->DrawSignalSprite(BSW_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, -2, 6); this->DrawSignalSprite(BSW_ELECTRIC_EXIT, SPR_IMG_SIGNAL_ELECTRIC_EXIT, -2, 6); this->DrawSignalSprite(BSW_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_COMBO, -2, 6); + this->DrawSignalSprite(BSW_ELECTRIC_PBS, SPR_IMG_SIGNAL_ELECTRIC_PBS, -1, 4); + this->DrawSignalSprite(BSW_ELECTRIC_PBS_OWAY,SPR_IMG_SIGNAL_ELECTRIC_OBSOWAY,-2, 6); /* Draw dragging signal density value in the BSW_DRAG_SIGNALS_DENSITY widget */ SetDParam(0, _settings_client.gui.drag_signals_density); @@ -1429,13 +1437,17 @@ public: case BSW_SEMAPHORE_ENTRY: case BSW_SEMAPHORE_EXIT: case BSW_SEMAPHORE_COMBO: + case BSW_SEMAPHORE_PBS: + case BSW_SEMAPHORE_PBS_OWAY: case BSW_ELECTRIC_NORM: case BSW_ELECTRIC_ENTRY: case BSW_ELECTRIC_EXIT: case BSW_ELECTRIC_COMBO: + case BSW_ELECTRIC_PBS: + case BSW_ELECTRIC_PBS_OWAY: this->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type); - _cur_signal_type = (SignalType)((uint)((widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_COMBO + 1))); + _cur_signal_type = (SignalType)((uint)((widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_LAST + 1))); _cur_signal_variant = widget >= BSW_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE; break; @@ -1467,29 +1479,33 @@ public: /** Widget definition of the build signal window */ static const Widget _signal_builder_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // BSW_CLOSEBOX -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 109, 0, 13, STR_SIGNAL_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, // BSW_CAPTION +{ WWT_CAPTION, RESIZE_NONE, 7, 11, 152, 0, 13, STR_SIGNAL_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, // BSW_CAPTION { WWT_PANEL, RESIZE_NONE, 7, 0, 21, 14, 40, STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_NORM_TIP}, // BSW_SEMAPHORE_NORM { WWT_PANEL, RESIZE_NONE, 7, 22, 43, 14, 40, STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TIP}, // BSW_SEMAPHORE_ENTRY { WWT_PANEL, RESIZE_NONE, 7, 44, 65, 14, 40, STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TIP}, // BSW_SEMAPHORE_EXIT { WWT_PANEL, RESIZE_NONE, 7, 66, 87, 14, 40, STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TIP}, // BSW_SEMAPHORE_COMBO +{ WWT_PANEL, RESIZE_NONE, 7, 88, 109, 14, 40, STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_TIP}, // BSW_SEMAPHORE_PBS +{ WWT_PANEL, RESIZE_NONE, 7, 109, 130, 14, 40, STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TIP},// BSW_SEMAPHORE_PBS_OWAY { WWT_PANEL, RESIZE_NONE, 7, 0, 21, 41, 67, STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_NORM_TIP}, // BSW_ELECTRIC_NORM { WWT_PANEL, RESIZE_NONE, 7, 22, 43, 41, 67, STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TIP}, // BSW_ELECTRIC_ENTRY { WWT_PANEL, RESIZE_NONE, 7, 44, 65, 41, 67, STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_EXIT_TIP}, // BSW_ELECTRIC_EXIT { WWT_PANEL, RESIZE_NONE, 7, 66, 87, 41, 67, STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_COMBO_TIP}, // BSW_ELECTRIC_COMBO +{ WWT_PANEL, RESIZE_NONE, 7, 88, 109, 41, 67, STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_TIP}, // BSW_ELECTRIC_PBS +{ WWT_PANEL, RESIZE_NONE, 7, 109, 130, 41, 67, STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TIP},// BSW_ELECTRIC_PBS_OWAY -{ WWT_IMGBTN, RESIZE_NONE, 7, 88, 109, 14, 40, SPR_IMG_SIGNAL_CONVERT, STR_SIGNAL_CONVERT_TIP}, // BSW_CONVERT -{ WWT_PANEL, RESIZE_NONE, 7, 88, 109, 41, 67, STR_NULL, STR_DRAG_SIGNALS_DENSITY_TIP}, // BSW_DRAG_SIGNALS_DENSITY -{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 90, 98, 54, 65, SPR_ARROW_LEFT, STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_DECREASE -{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 99, 107, 54, 65, SPR_ARROW_RIGHT, STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_INCREASE +{ WWT_IMGBTN, RESIZE_NONE, 7, 131, 152, 14, 40, SPR_IMG_SIGNAL_CONVERT, STR_SIGNAL_CONVERT_TIP}, // BSW_CONVERT +{ WWT_PANEL, RESIZE_NONE, 7, 131, 152, 41, 67, STR_NULL, STR_DRAG_SIGNALS_DENSITY_TIP}, // BSW_DRAG_SIGNALS_DENSITY +{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 133, 141, 54, 65, SPR_ARROW_LEFT, STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_DECREASE +{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 142, 150, 54, 65, SPR_ARROW_RIGHT, STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_INCREASE { WIDGETS_END}, }; /** Signal selection window description */ static const WindowDesc _signal_builder_desc = { - WDP_AUTO, WDP_AUTO, 110, 68, 110, 68, + WDP_AUTO, WDP_AUTO, 153, 68, 153, 68, WC_BUILD_SIGNAL, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _signal_builder_widgets, @@ -1785,6 +1801,6 @@ void InitializeRailGUI() SetDefaultRailGui(); _convert_signal_button = false; - _cur_signal_type = SIGTYPE_NORMAL; + _cur_signal_type = _default_signal_type[_settings_client.gui.default_signal_type]; ResetSignalVariant(); } diff --git a/src/table/sprites.h b/src/table/sprites.h index 678831f..c23d73f 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -1297,15 +1297,19 @@ enum Sprites { SPR_IMG_RAIL_STATION = 1298, SPR_IMG_RAIL_SIGNALS = 1291, - SPR_IMG_SIGNAL_ELECTRIC_NORM = 1287, - SPR_IMG_SIGNAL_ELECTRIC_ENTRY = SPR_SIGNALS_BASE + 12, - SPR_IMG_SIGNAL_ELECTRIC_EXIT = SPR_SIGNALS_BASE + 28, - SPR_IMG_SIGNAL_ELECTRIC_COMBO = SPR_SIGNALS_BASE + 44, - SPR_IMG_SIGNAL_SEMAPHORE_NORM = SPR_SIGNALS_BASE + 60, - SPR_IMG_SIGNAL_SEMAPHORE_ENTRY = SPR_SIGNALS_BASE + 76, - SPR_IMG_SIGNAL_SEMAPHORE_EXIT = SPR_SIGNALS_BASE + 92, - SPR_IMG_SIGNAL_SEMAPHORE_COMBO = SPR_SIGNALS_BASE + 108, - SPR_IMG_SIGNAL_CONVERT = SPR_OPENTTD_BASE + 135, + SPR_IMG_SIGNAL_ELECTRIC_NORM = 1287, + SPR_IMG_SIGNAL_ELECTRIC_ENTRY = SPR_SIGNALS_BASE + 12, + SPR_IMG_SIGNAL_ELECTRIC_EXIT = SPR_SIGNALS_BASE + 28, + SPR_IMG_SIGNAL_ELECTRIC_COMBO = SPR_SIGNALS_BASE + 44, + SPR_IMG_SIGNAL_ELECTRIC_PBS = SPR_SIGNALS_BASE + 124, + SPR_IMG_SIGNAL_ELECTRIC_OBSOWAY = SPR_SIGNALS_BASE + 140, + SPR_IMG_SIGNAL_SEMAPHORE_NORM = SPR_SIGNALS_BASE + 60, + SPR_IMG_SIGNAL_SEMAPHORE_ENTRY = SPR_SIGNALS_BASE + 76, + SPR_IMG_SIGNAL_SEMAPHORE_EXIT = SPR_SIGNALS_BASE + 92, + SPR_IMG_SIGNAL_SEMAPHORE_COMBO = SPR_SIGNALS_BASE + 108, + SPR_IMG_SIGNAL_SEMAPHORE_PBS = SPR_SIGNALS_BASE + 188, + SPR_IMG_SIGNAL_SEMAPHORE_PBSOWAY = SPR_SIGNALS_BASE + 204, + SPR_IMG_SIGNAL_CONVERT = SPR_OPENTTD_BASE + 135, SPR_IMG_TUNNEL_RAIL = 2430, SPR_IMG_TUNNEL_MONO = 2431, -- 1.5.6 From 95d0ff0541bf23dc4b6eda21ca362cc769d5e1f9 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 19:04:27 +0200 Subject: Extend YAPF with the possibility to override the railtype info of the vehicle. --- src/yapf/follow_track.hpp | 12 +++++++----- src/yapf/yapf_costrail.hpp | 4 ++-- src/yapf/yapf_destrail.hpp | 8 +++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/yapf/follow_track.hpp b/src/yapf/follow_track.hpp index d3c9c45..035bc24 100644 --- a/src/yapf/follow_track.hpp +++ b/src/yapf/follow_track.hpp @@ -34,13 +34,14 @@ struct CFollowTrackT int m_tiles_skipped; ///< number of skipped tunnel or station tiles ErrorCode m_err; CPerformanceTimer *m_pPerf; + RailTypes m_railtypes; - FORCEINLINE CFollowTrackT(const Vehicle *v = NULL, CPerformanceTimer* pPerf = NULL) + FORCEINLINE CFollowTrackT(const Vehicle *v = NULL, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = NULL) { - Init(v, pPerf); + Init(v, railtype_override, pPerf); } - FORCEINLINE void Init(const Vehicle *v, CPerformanceTimer* pPerf) + FORCEINLINE void Init(const Vehicle *v, RailTypes railtype_override, CPerformanceTimer *pPerf) { assert(!IsRailTT() || (v != NULL && v->type == VEH_TRAIN)); m_veh = v; @@ -52,6 +53,7 @@ struct CFollowTrackT m_is_station = m_is_bridge = m_is_tunnel = false; m_tiles_skipped = 0; m_err = EC_NONE; + if (IsRailTT()) m_railtypes = railtype_override == INVALID_RAILTYPES ? v->u.rail.compatible_railtypes : railtype_override; } FORCEINLINE static TransportType TT() {return Ttr_type_;} @@ -79,7 +81,7 @@ struct CFollowTrackT /** main follower routine. Fills all members and return true on success. * Otherwise returns false if track can't be followed. */ - FORCEINLINE bool Follow(TileIndex old_tile, Trackdir old_td) + inline bool Follow(TileIndex old_tile, Trackdir old_td) { m_old_tile = old_tile; m_old_td = old_td; @@ -256,7 +258,7 @@ protected: // rail transport is possible only on compatible rail types if (IsRailTT()) { RailType rail_type = GetTileRailType(m_new_tile); - if (!HasBit(m_veh->u.rail.compatible_railtypes, rail_type)) { + if (!HasBit(m_railtypes, rail_type)) { // incompatible rail type m_err = EC_RAIL_TYPE; return false; diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index ddfdca6..509cba5 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -271,7 +271,7 @@ public: EndSegmentReasonBits end_segment_reason = ESRB_NONE; - TrackFollower tf_local(v, &Yapf().m_perf_ts_cost); + TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); if (!has_parent) { /* We will jump to the middle of the cost calculator assuming that segment cache is not used. */ @@ -373,7 +373,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th /* Move to the next tile/trackdir. */ tf = &tf_local; - tf_local.Init(v, &Yapf().m_perf_ts_cost); + tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); if (!tf_local.Follow(cur.tile, cur.td)) { assert(tf_local.m_err != TrackFollower::EC_NONE); diff --git a/src/yapf/yapf_destrail.hpp b/src/yapf/yapf_destrail.hpp index aba4094..7ea7388 100644 --- a/src/yapf/yapf_destrail.hpp +++ b/src/yapf/yapf_destrail.hpp @@ -11,15 +11,21 @@ protected: RailTypes m_compatible_railtypes; public: - void SetDestination(const Vehicle* v) + void SetDestination(const Vehicle *v, bool override_rail_type = false) { m_compatible_railtypes = v->u.rail.compatible_railtypes; + if (override_rail_type) m_compatible_railtypes |= GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes; } bool IsCompatibleRailType(RailType rt) { return HasBit(m_compatible_railtypes, rt); } + + RailTypes GetCompatibleRailTypes() const + { + return m_compatible_railtypes; + } }; template -- 1.5.6 From a32dba8d6eb0311f5ad48fb7766a078ffbb2c40a Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 19:07:11 +0200 Subject: YAPF is now able to reserve the found path. --- src/pbs.cpp | 119 ++++++++++++++++++++++++++++++++++ src/pbs.h | 15 +++++ src/rail_map.h | 15 +++++ src/yapf/yapf.h | 5 +- src/yapf/yapf_node_rail.hpp | 31 +++++++++ src/yapf/yapf_rail.cpp | 147 ++++++++++++++++++++++++++++++++++++++----- 6 files changed, 315 insertions(+), 17 deletions(-) diff --git a/src/pbs.cpp b/src/pbs.cpp index 3870a2b..50258fa 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -12,6 +12,9 @@ #include "debug.h" #include "direction_func.h" #include "settings_type.h" +#include "road_func.h" +#include "vehicle_base.h" +#include "yapf/follow_track.hpp" /** * Get the reserved trackbits for any tile, regardless of type. @@ -155,3 +158,119 @@ bool TryReserveRailTrack(TileIndex tile, Track t) break; } } + +/** + * Follow a train reservation to the last tile. + * + * @param v the vehicle + * @returns The last tile of the reservation or the current train tile if no reservation present. + */ +PBSTileInfo FollowTrainReservation(const Vehicle *v) +{ + assert(v->type == VEH_TRAIN); + + TileIndex tile = v->tile; + Trackdir trackdir = GetVehicleTrackdir(v); + + if (IsRailDepotTile(tile) && !GetRailDepotReservation(tile)) return PBSTileInfo(tile, trackdir, false); + + /* Do not disallow 90 deg turns as the setting might have changed between reserving and now. */ + CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes); + while (ft.Follow(tile, trackdir)) { + TrackdirBits reserved = (TrackdirBits)(ft.m_new_td_bits & (GetReservedTrackbits(ft.m_new_tile) * 0x101)); + + /* No reservation --> path end found */ + if (reserved == TRACKDIR_BIT_NONE) break; + + /* Can't have more than one reserved trackdir */ + Trackdir new_trackdir = FindFirstTrackdir(reserved); + + /* One-way signal against us. The reservation can't be ours as it is not + * a safe position from our direction and we can never pass the signal. */ + if (HasOnewaySignalBlockingTrackdir(ft.m_new_tile, new_trackdir)) break; + + tile = ft.m_new_tile; + trackdir = new_trackdir; + + /* Depot tile? Can't continue. */ + if (IsRailDepotTile(tile)) break; + /* Non-pbs signal? Reservation can't continue. */ + if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break; + } + + return PBSTileInfo(tile, trackdir, IsSafeWaitingPosition(v, tile, trackdir, true, _settings_game.pf.forbid_90_deg)); +} + +/** + * Determine whether a certain track on a tile is a safe position to end a path. + * + * @param v the vehicle to test for + * @param tile The tile + * @param trackdir The trackdir to test + * @param include_line_end Should end-of-line tiles be considered safe? + * @param forbid_90def Don't allow trains to make 90 degree turns + * @return True if it is a safe position + */ +bool IsSafeWaitingPosition(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg) +{ + if (IsRailDepotTile(tile)) return true; + + if (IsTileType(tile, MP_RAILWAY)) { + /* For non-pbs signals, stop on the signal tile. */ + if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true; + } + + /* Check next tile. For perfomance reasons, we check for 90 degree turns ourself. */ + CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes); + + /* End of track? */ + if (!ft.Follow(tile, trackdir)) { + /* Last tile of a terminus station is a safe position. */ + if (include_line_end) return true; + } + + /* Check for reachable tracks. */ + ft.m_new_td_bits &= DiagdirReachesTrackdirs(ft.m_exitdir); + if (ft.m_new_td_bits == TRACKDIR_BIT_NONE) return include_line_end; + if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir); + + if (ft.m_new_td_bits != TRACKDIR_BIT_NONE && KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) { + /* PBS signal on next trackdir? Safe position. */ + if (HasPbsSignalOnTrackdir(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) return true; + } + + return false; +} + +/** + * Check if a safe position is free. + * + * @param v the vehicle to test for + * @param tile The tile + * @param trackdir The trackdir to test + * @param forbid_90def Don't allow trains to make 90 degree turns + * @return True if the position is free + */ +bool IsWaitingPositionFree(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg) +{ + Track track = TrackdirToTrack(trackdir); + TrackBits reserved = GetReservedTrackbits(tile); + + /* Tile reserved? Can never be a free waiting position. */ + if (TrackOverlapsTracks(reserved, track)) return false; + + /* Not reserved and depot or not a pbs signal -> free. */ + if (IsRailDepotTile(tile)) return true; + if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) return true; + + /* Check the next tile, if it's a PBS signal, it has to be free as well. */ + CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes); + + if (!ft.Follow(tile, trackdir)) return true; + + /* Check for reachable tracks. */ + ft.m_new_td_bits &= DiagdirReachesTrackdirs(ft.m_exitdir); + if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir); + + return !HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits)); +} diff --git a/src/pbs.h b/src/pbs.h index 4103f1e..d4b87b8 100644 --- a/src/pbs.h +++ b/src/pbs.h @@ -8,6 +8,7 @@ #include "tile_type.h" #include "direction_type.h" #include "track_type.h" +#include "vehicle_type.h" TrackBits GetReservedTrackbits(TileIndex t); @@ -16,6 +17,20 @@ void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bo bool TryReserveRailTrack(TileIndex tile, Track t); void UnreserveRailTrack(TileIndex tile, Track t); +/** This struct contains information about the end of a reserved path. */ +struct PBSTileInfo { + TileIndex tile; ///< Tile the path ends, INVALID_TILE if no valid path was found. + Trackdir trackdir; ///< The reserved trackdir on the tile. + bool okay; ///< True if tile is a safe wairing position, false otherwise. + + PBSTileInfo() : tile(INVALID_TILE), trackdir(INVALID_TRACKDIR), okay(false) {} + PBSTileInfo(TileIndex _t, Trackdir _td, bool _okay) : tile(_t), trackdir(_td), okay(_okay) {} +}; + +PBSTileInfo FollowTrainReservation(const Vehicle *v); +bool IsSafeWaitingPosition(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg = false); +bool IsWaitingPositionFree(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg = false); + /** * Check whether some of tracks is reserved on a tile. * diff --git a/src/rail_map.h b/src/rail_map.h index 014ffd5..ff9fcbb 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -537,6 +537,21 @@ static inline bool HasPbsSignalOnTrackdir(TileIndex tile, Trackdir td) IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td))); } +/** + * Is a one-way signal blocking the trackdir? A one-way signal on the + * trackdir against will block, but signals on both trackdirs won't. + * @param tile the tile to check + * @param td the trackdir to check + */ +static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td) +{ + return + IsTileType(tile, MP_RAILWAY) && + HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && + !HasSignalOnTrackdir(tile, td) && + IsOnewaySignal(tile, TrackdirToTrack(td)); +} + /** * Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile. diff --git a/src/yapf/yapf.h b/src/yapf/yapf.h index ca400a7..fb39854 100644 --- a/src/yapf/yapf.h +++ b/src/yapf/yapf.h @@ -8,6 +8,7 @@ #include "../debug.h" #include "../depot_type.h" #include "../direction_type.h" +#include "../pbs.h" /** Finds the best path for given ship. * @param v the ship that needs to find a path @@ -32,9 +33,11 @@ Trackdir YapfChooseRoadTrack(const Vehicle *v, TileIndex tile, DiagDirection ent * @param enterdir diagonal direction which the RV will enter this new tile from * @param tracks available trackdirs on the new tile (to choose from) * @param path_not_found [out] true is returned if no path can be found (returned Trackdir is only a 'guess') + * @param reserve_track indicates whether YAPF should try to reserve the found path + * @param target [out] the target tile of the reservation, free is set to true if path was reserved * @return the best trackdir for next turn or INVALID_TRACKDIR if the path could not be found */ -Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found); +Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target); /** 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) diff --git a/src/yapf/yapf_node_rail.hpp b/src/yapf/yapf_node_rail.hpp index 9745ff3..6b42d30 100644 --- a/src/yapf/yapf_node_rail.hpp +++ b/src/yapf/yapf_node_rail.hpp @@ -177,6 +177,37 @@ struct CYapfRailNodeT 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, Tpf &yapf, Tbase &obj, bool (Tfunc::*func)(TileIndex, Trackdir)) const + { + typename Tbase::TrackFollower ft(v, yapf.GetCompatibleRailTypes()); + TileIndex cur = base::GetTile(); + Trackdir cur_td = base::GetTrackdir(); + + while (cur != GetLastTile() || cur_td != GetLastTrackdir()) { + 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); + + /* Call func for all tiles in between. */ + for (int i = 0; i < ft.m_tiles_skipped; ++i) { + if (!(obj.*func)(tile, cur_td)) return false; + tile = TILE_ADD(tile, diff); + } + } + } + + return (obj.*func)(cur, cur_td); + } + void Dump(DumpTarget &dmp) const { base::Dump(dmp); diff --git a/src/yapf/yapf_rail.cpp b/src/yapf/yapf_rail.cpp index 7d7d4ee..9e8ecac 100644 --- a/src/yapf/yapf_rail.cpp +++ b/src/yapf/yapf_rail.cpp @@ -9,6 +9,7 @@ #include "yapf_costrail.hpp" #include "yapf_destrail.hpp" #include "../vehicle_func.h" +#include "../pbs.h" #define DEBUG_YAPF_CACHE 0 @@ -16,7 +17,113 @@ int _total_pf_time_us = 0; +template +class CYapfReserveTrack +{ +public: + typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class) + typedef typename Types::TrackFollower TrackFollower; + typedef typename Types::NodeList::Titem Node; ///< this will be our node type + +protected: + /// to access inherited pathfinder + FORCEINLINE Tpf& Yapf() {return *static_cast(this);} + +private: + 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 + TileIndex m_res_fail_tile; ///< The tile where the reservation failed + + bool FindSafePositionProc(TileIndex tile, Trackdir td) + { + if (IsSafeWaitingPosition(Yapf().GetVehicle(), tile, td, true, !TrackFollower::Allow90degTurns())) { + m_res_dest = tile; + m_res_dest_td = td; + return false; // Stop iterating segment + } + return true; + } + + bool ReserveSingleTrack(TileIndex tile, Trackdir td) + { + if (!TryReserveRailTrack(tile, TrackdirToTrack(td))) { + /* Tile couldn't be reserved, undo. */ + m_res_fail_tile = tile; + return false; + } + /* YAPF can sometimes skip parts of a station, so make sure we + * always reserve the whole platform. */ + if (IsRailwayStationTile(tile)) SetRailwayStationPlatformReservation(tile, TrackdirToExitdir(ReverseTrackdir(td)), true); + 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; + } + +public: + /** Set the target to where the reservation should be extended. */ + inline void SetReservationTarget(Node *node, TileIndex tile, Trackdir td) + { + m_res_node = node; + m_res_dest = tile; + m_res_dest_td = td; + } + /** Check the node for a possible reservation target. */ + inline void FindSafePositionOnNode(Node *node) + { + assert(node->m_parent != NULL); + + /* We will never pass more than two signals, no need to check for a safe tile. */ + if (node->m_parent->m_num_signals_passed >= 2) return; + + if (!node->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack::FindSafePositionProc)) { + m_res_node = node; + } + } + + /** Try to reserve the path till the reservation target. */ + bool TryReservePath(PBSTileInfo *target) + { + m_res_fail_tile = INVALID_TILE; + + if (target != NULL) { + target->tile = m_res_dest; + target->trackdir = m_res_dest_td; + target->okay = false; + } + + /* Don't bother if the target is reserved. */ + if (!IsWaitingPositionFree(Yapf().GetVehicle(), m_res_dest, m_res_dest_td)) return false; + + for (Node *node = m_res_node; node->m_parent != NULL; node = node->m_parent) { + node->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack::ReserveSingleTrack); + if (m_res_fail_tile != INVALID_TILE) { + /* Reservation failed, undo. */ + Node *fail_node = m_res_node; + TileIndex stop_tile = m_res_fail_tile; + do { + /* If this is the node that failed, stop at the failed tile. */ + m_res_fail_tile = fail_node == node ? stop_tile : INVALID_TILE; + fail_node->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack::UnreserveSingleTrack); + } while (fail_node != node && (fail_node = fail_node->m_parent) != NULL); + + return false; + } + } + + if (target != NULL) target->okay = true; + + if (Yapf().CanUseGlobalCache(*m_res_node)) + YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK); + + return true; + } +}; template class CYapfFollowAnyDepotRailT @@ -95,7 +202,7 @@ public: }; template -class CYapfFollowRailT +class CYapfFollowRailT : protected CYapfReserveTrack { public: typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class) @@ -121,16 +228,18 @@ public: /// return debug report character to identify the transportation type FORCEINLINE char TransportTypeChar() const {return 't';} - static Trackdir stChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found) + static Trackdir stChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target) { // 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, target); -#if DEBUG_YAPF_CACHE +#else + Trackdir result1 = pf1.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found, false, NULL); 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, target); if (result1 != result2) { DEBUG(yapf, 0, "CACHE ERROR: ChooseRailTrack() = [%d, %d]", result1, result2); DumpTarget dmp1, dmp2; @@ -148,10 +257,13 @@ public: return result1; } - FORCEINLINE Trackdir ChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found) + FORCEINLINE Trackdir ChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target) { + if (target != NULL) target->tile = INVALID_TILE; + // set origin and destination nodes - Yapf().SetOrigin(v->tile, GetVehicleTrackdir(v), INVALID_TILE, INVALID_TRACKDIR, 1, true); + PBSTileInfo origin = FollowTrainReservation(v); + Yapf().SetOrigin(origin.tile, origin.trackdir, INVALID_TILE, INVALID_TRACKDIR, 1, true); Yapf().SetDestination(v); // find the best path @@ -166,17 +278,23 @@ public: Trackdir next_trackdir = INVALID_TRACKDIR; Node *pNode = Yapf().GetBestNode(); if (pNode != NULL) { + // reserve till end of path + this->SetReservationTarget(pNode, pNode->GetLastTile(), pNode->GetLastTrackdir()); + // 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; + + this->FindSafePositionOnNode(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 && path_found) this->TryReservePath(target); } return next_trackdir; } @@ -247,10 +365,10 @@ struct CYapfAnyDepotRail1 : CYapfT > {}; -Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found) +Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target) { // default is YAPF type 2 - typedef Trackdir (*PfnChooseRailTrack)(const Vehicle*, TileIndex, DiagDirection, TrackBits, bool*); + typedef Trackdir (*PfnChooseRailTrack)(const Vehicle*, TileIndex, DiagDirection, TrackBits, bool*, bool, PBSTileInfo*); PfnChooseRailTrack pfnChooseRailTrack = &CYapfRail1::stChooseRailTrack; // check if non-default YAPF type needed @@ -258,7 +376,7 @@ Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection ent 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, target); return td_ret; } @@ -330,11 +448,8 @@ bool YapfFindNearestRailDepotTwoWay(const Vehicle *v, int max_distance, int reve const Vehicle *last_veh = GetLastVehicleInChain(v); - TileIndex tile = v->tile; + PBSTileInfo origin = FollowTrainReservation(v); TileIndex last_tile = last_veh->tile; - - // their trackdirs - Trackdir td = GetVehicleTrackdir(v); Trackdir td_rev = ReverseTrackdir(GetVehicleTrackdir(last_veh)); typedef bool (*PfnFindNearestDepotTwoWay)(const Vehicle*, TileIndex, Trackdir, TileIndex, Trackdir, int, int, TileIndex*, bool*); @@ -345,7 +460,7 @@ bool YapfFindNearestRailDepotTwoWay(const Vehicle *v, int max_distance, int reve pfnFindNearestDepotTwoWay = &CYapfAnyDepotRail2::stFindNearestDepotTwoWay; // Trackdir, forbid 90-deg } - bool ret = pfnFindNearestDepotTwoWay(v, tile, td, last_tile, td_rev, max_distance, reverse_penalty, depot_tile, reversed); + bool ret = pfnFindNearestDepotTwoWay(v, origin.tile, origin.trackdir, last_tile, td_rev, max_distance, reverse_penalty, depot_tile, reversed); return ret; } -- 1.5.6 From 55b71a134cd1d51ad570f5576a988cce3f1b4f2d Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Wed, 4 Jun 2008 21:02:24 +0200 Subject: Added PBS related penalties to YAPF. --- src/settings.cpp | 2 + src/settings_type.h | 2 + src/track_func.h | 13 +++++ src/yapf/yapf_costrail.hpp | 107 +++++++++++++++++++++++++++----------------- 4 files changed, 83 insertions(+), 41 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 47ccd76..5b850b9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1724,6 +1724,8 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p0, SLE_INT, 28, SL_MAX_VERSION, 0, 0, 500, -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p1, SLE_INT, 28, SL_MAX_VERSION, 0, 0, -100, -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p2, SLE_INT, 28, SL_MAX_VERSION, 0, 0, 5, -1000000, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_cross_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_signal_back_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 15 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.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(GameSettings, pf.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(GameSettings, pf.yapf.rail_shorter_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 40 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), diff --git a/src/settings_type.h b/src/settings_type.h index 098d56f..80e5960 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -218,6 +218,8 @@ struct YAPFSettings { int32 rail_look_ahead_signal_p0; ///< constant in polynomial penalty function int32 rail_look_ahead_signal_p1; ///< constant in polynomial penalty function int32 rail_look_ahead_signal_p2; ///< constant in polynomial penalty function + uint32 rail_pbs_cross_penalty; ///< penalty for crossing a reserved tile + uint32 rail_pbs_signal_back_penalty; ///< penalty for passing a pbs signal from the backside uint32 rail_longer_platform_penalty; ///< penalty for longer station platform than train uint32 rail_longer_platform_per_tile_penalty; ///< penalty for longer station platform than train (per tile) diff --git a/src/track_func.h b/src/track_func.h index 144ea03..706de20 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -590,6 +590,19 @@ static inline bool TracksOverlap(TrackBits bits) } /** + * Check if a given track is contained within or overlaps some other tracks. + * + * @param tracks Tracks to be testet against + * @param track The track to test + * @return true if the track is already in the tracks or overlaps the tracks. + */ +static inline bool TrackOverlapsTracks(TrackBits tracks, Track track) +{ + if (HasBit(tracks, track)) return true; + return TracksOverlap(tracks | TrackToTrackBits(track)); +} + +/** * Checks whether the trackdir means that we are reversing. * @param dir the trackdir to check * @return true if it is a reversing road trackdir diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index 509cba5..7ff99bb 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -5,6 +5,7 @@ #ifndef YAPF_COSTRAIL_HPP #define YAPF_COSTRAIL_HPP +#include "../pbs.h" template class CYapfCostRailT @@ -128,6 +129,19 @@ public: 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() / 2) return 0; + + if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) { + int cost = Yapf().PfGetSettings().rail_pbs_cross_penalty; + if (!IsDiagonalTrackdir(trackdir)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH; + return cost * (skipped + 1); + } + return 0; + } + int SignalCost(Node& n, TileIndex tile, Trackdir trackdir) { int cost = 0; @@ -139,50 +153,58 @@ public: 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; - - // look-ahead signal penalty - if (look_ahead_cost > 0) { - // add the look ahead penalty only if it is positive - cost += look_ahead_cost; - } + } 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 { + SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir)); + // we have a red signal in our direction + // was it first signal which is two-way? + if (!IsPbsSignal(sig_type) && 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; + } + n.m_last_red_signal_type = sig_type; + n.flags_u.flags_s.m_last_signal_was_red = true; - // 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; - }; + // 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; + }; + } } + + 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; } - n.m_num_signals_passed++; - n.m_segment->m_last_signal_tile = tile; - n.m_segment->m_last_signal_td = trackdir; } } return cost; @@ -327,6 +349,9 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th /* 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; -- 1.5.6 From 0066989215930836c178a2d6aeb993dbc0d1519b Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 24 Mar 2008 16:54:59 +0100 Subject: Add a penalty for double slips to YAPF. --- src/settings.cpp | 1 + src/settings_type.h | 1 + src/yapf/yapf_costrail.hpp | 11 +++++++++++ 3 files changed, 13 insertions(+), 0 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 5b850b9..086e8d6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1726,6 +1726,7 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p2, SLE_INT, 28, SL_MAX_VERSION, 0, 0, 5, -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_cross_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_signal_back_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 15 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.yapf.rail_doubleslip_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.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(GameSettings, pf.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(GameSettings, pf.yapf.rail_shorter_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 40 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), diff --git a/src/settings_type.h b/src/settings_type.h index 80e5960..d2d3a18 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -220,6 +220,7 @@ struct YAPFSettings { int32 rail_look_ahead_signal_p2; ///< constant in polynomial penalty function uint32 rail_pbs_cross_penalty; ///< penalty for crossing a reserved tile uint32 rail_pbs_signal_back_penalty; ///< penalty for passing a pbs signal from the backside + uint32 rail_doubleslip_penalty; ///< penalty for passing a double slip switch uint32 rail_longer_platform_penalty; ///< penalty for longer station platform than train uint32 rail_longer_platform_per_tile_penalty; ///< penalty for longer station platform than train (per tile) diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index 7ff99bb..f959f1a 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -105,6 +105,16 @@ public: return cost; } + FORCEINLINE int SwitchCost(TileIndex tile1, TileIndex tile2, DiagDirection exitdir) + { + if (IsTileType(tile1, MP_RAILWAY) && IsTileType(tile2, MP_RAILWAY)) { + bool t1 = KillFirstBit(GetTrackBits(tile1) & DiagdirReachesTracks(ReverseDiagDir(exitdir))) != TRACK_BIT_NONE; + bool t2 = KillFirstBit(GetTrackBits(tile2) & DiagdirReachesTracks(exitdir)) != TRACK_BIT_NONE; + if (t1 && t2) return Yapf().PfGetSettings().rail_doubleslip_penalty; + } + return 0; + } + /** Return one tile cost (base cost + level crossing penalty). */ FORCEINLINE int OneTileCost(TileIndex& tile, Trackdir trackdir) { @@ -305,6 +315,7 @@ public: for (;;) { /* Transition cost (cost of the move from previous tile) */ transition_cost = Yapf().CurveCost(prev.td, cur.td); + transition_cost += Yapf().SwitchCost(prev.tile, cur.tile, TrackdirToExitdir(prev.td)); /* First transition cost counts against segment entry cost, other transitions * inside segment will come to segment cost (and will be cached) */ -- 1.5.6 From ca3dd0e5c9a771d314a799a03be71f3593777785 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 13 Apr 2008 16:48:48 +0200 Subject: Reserved station tiles can now be penalized more in YAPF. This should help with spread out stations. --- src/settings.cpp | 1 + src/settings_type.h | 1 + src/yapf/yapf_costrail.hpp | 2 +- 3 files changed, 3 insertions(+), 1 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 086e8d6..1b1c2b5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1725,6 +1725,7 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p1, SLE_INT, 28, SL_MAX_VERSION, 0, 0, -100, -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p2, SLE_INT, 28, SL_MAX_VERSION, 0, 0, 5, -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_cross_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_station_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 8 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_signal_back_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 15 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_doubleslip_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, 1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_longer_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 8 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), diff --git a/src/settings_type.h b/src/settings_type.h index d2d3a18..708d441 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -219,6 +219,7 @@ struct YAPFSettings { int32 rail_look_ahead_signal_p1; ///< constant in polynomial penalty function int32 rail_look_ahead_signal_p2; ///< constant in polynomial penalty function uint32 rail_pbs_cross_penalty; ///< penalty for crossing a reserved tile + uint32 rail_pbs_station_penalty; ///< penalty for crossing a reserved station tile uint32 rail_pbs_signal_back_penalty; ///< penalty for passing a pbs signal from the backside uint32 rail_doubleslip_penalty; ///< penalty for passing a double slip switch diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index f959f1a..60eaae3 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -145,7 +145,7 @@ public: if (n.m_num_signals_passed >= m_sig_look_ahead_costs.Size() / 2) return 0; if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) { - int cost = Yapf().PfGetSettings().rail_pbs_cross_penalty; + int cost = IsRailwayStationTile(tile) ? Yapf().PfGetSettings().rail_pbs_station_penalty : Yapf().PfGetSettings().rail_pbs_cross_penalty; if (!IsDiagonalTrackdir(trackdir)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH; return cost * (skipped + 1); } -- 1.5.6 From 3cd3791ac49dc8485891acee36b6213ec521f282 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 16 Mar 2008 01:41:32 +0100 Subject: Add YAPF provider to find a safe tile and reserve a path. --- src/yapf/follow_track.hpp | 24 ++++++++++- src/yapf/yapf.h | 11 +++++ src/yapf/yapf_costrail.hpp | 14 ++++++ src/yapf/yapf_destrail.hpp | 35 +++++++++++++++ src/yapf/yapf_node_rail.hpp | 6 ++- src/yapf/yapf_rail.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 184 insertions(+), 4 deletions(-) diff --git a/src/yapf/follow_track.hpp b/src/yapf/follow_track.hpp index 035bc24..a2f727d 100644 --- a/src/yapf/follow_track.hpp +++ b/src/yapf/follow_track.hpp @@ -10,8 +10,8 @@ /** 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 */ -template + * types w/ or w/o 90-deg turns allowed */ +template struct CFollowTrackT { enum ErrorCode { @@ -20,6 +20,7 @@ struct CFollowTrackT EC_RAIL_TYPE, EC_90DEG, EC_NO_WAY, + EC_RESERVED, }; const Vehicle *m_veh; ///< moving vehicle @@ -62,6 +63,7 @@ struct CFollowTrackT FORCEINLINE bool IsTram() {return IsRoadTT() && HasBit(m_veh->u.road.compatible_roadtypes, ROADTYPE_TRAM);} FORCEINLINE static bool IsRoadTT() {return TT() == TRANSPORT_ROAD;} FORCEINLINE static bool Allow90degTurns() {return T90deg_turns_allowed_;} + FORCEINLINE static bool MaskReservedTracks() {return Tmask_reserved_tracks;} /** Tests if a tile is a road tile with a single tramtrack (tram can reverse) */ FORCEINLINE DiagDirection GetSingleTramBit(TileIndex tile) @@ -106,6 +108,21 @@ struct CFollowTrackT return false; } } + if (MaskReservedTracks()) { + TrackBits reserved = GetReservedTrackbits(m_new_tile); + /* Mask already reserved trackdirs. */ + m_new_td_bits &= ~TrackBitsToTrackdirBits(reserved); + /* Mask out all trackdirs that conflict with the reservation. */ + uint bits = (uint)TrackdirBitsToTrackBits(m_new_td_bits); + int i; + FOR_EACH_SET_BIT(i, bits) { + if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) m_new_td_bits &= ~TrackToTrackdirBits((Track)i); + } + if (m_new_td_bits == TRACKDIR_BIT_NONE) { + m_err = EC_RESERVED; + return false; + } + } return true; } @@ -382,4 +399,7 @@ typedef CFollowTrackT CFollowTrackWaterNo90; typedef CFollowTrackT CFollowTrackRoadNo90; typedef CFollowTrackT CFollowTrackRailNo90; +typedef CFollowTrackT CFollowTrackFreeRail; +typedef CFollowTrackT CFollowTrackFreeRailNo90; + #endif /* FOLLOW_TRACK_HPP */ diff --git a/src/yapf/yapf.h b/src/yapf/yapf.h index fb39854..45ba94e 100644 --- a/src/yapf/yapf.h +++ b/src/yapf/yapf.h @@ -66,6 +66,17 @@ bool YapfFindNearestRailDepotTwoWay(const Vehicle *v, int max_distance, int reve /** Returns true if it is better to reverse the train before leaving station */ bool YapfCheckReverseTrain(const Vehicle* v); +/** + * Try to extend the reserved path of a train to the nearest safe tile. + * + * @param v The train that needs to find a safe tile. + * @param tile Last tile of the current reserved path. + * @param td Last trackdir of the current reserved path. + * @param override_railtype Should all physically compabtible railtypes be searched, even if the vehicle can't on them on it own? + * @return True if the path could be extended to a safe tile. + */ +bool YapfRailFindNearestSafeTile(const Vehicle *v, TileIndex tile, Trackdir td, bool override_railtype); + /** Use this function to notify YAPF that track layout (or signal configuration) has change */ void YapfNotifyTrackLayoutChange(TileIndex tile, Track track); diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index 60eaae3..c19353f 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -387,6 +387,11 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th } else if (cur.tile_type == MP_RAILWAY && IsRailWaypoint(cur.tile)) { /* Waypoint is also a good reason to finish. */ end_segment_reason |= ESRB_WAYPOINT; + } else if (TrackFollower::MaskReservedTracks() && cur.tile_type == MP_RAILWAY) { + /* Searching for a safe tile? */ + if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) { + end_segment_reason |= ESRB_SAFE_TILE; + } } /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise @@ -419,6 +424,10 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th } else { end_segment_reason |= ESRB_DEAD_END; } + + if (TrackFollower::MaskReservedTracks() && tf_local.m_err != TrackFollower::EC_90DEG) { + end_segment_reason |= ESRB_SAFE_TILE; + } break; } @@ -432,6 +441,11 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th /* Gather the next tile/trackdir/tile_type/rail_type. */ TILE next(tf_local.m_new_tile, (Trackdir)FindFirstBit2x64(tf_local.m_new_td_bits)); + if (TrackFollower::MaskReservedTracks() && HasPbsSignalOnTrackdir(next.tile, next.td)) { + /* Possible safe tile. */ + end_segment_reason |= ESRB_SAFE_TILE; + } + /* Check the next tile for the rail type. */ if (next.rail_type != cur.rail_type) { /* Segment must consist from the same rail_type tiles. */ diff --git a/src/yapf/yapf_destrail.hpp b/src/yapf/yapf_destrail.hpp index 7ea7388..9790758 100644 --- a/src/yapf/yapf_destrail.hpp +++ b/src/yapf/yapf_destrail.hpp @@ -63,6 +63,41 @@ public: }; template +class CYapfDestinationAnySafeTileRailT + : public CYapfDestinationRailBase +{ +public: + typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class) + typedef typename Types::NodeList::Titem Node; ///< this will be our node type + typedef typename Node::Key Key; ///< key to hash tables + + /// to access inherited path finder + Tpf& Yapf() {return *static_cast(this);} + + /// Called by YAPF to detect if node ends in the desired destination + FORCEINLINE bool PfDetectDestination(Node& n) + { + return PfDetectDestination(n.GetLastTile(), n.GetLastTrackdir()); + } + + /// Called by YAPF to detect if node ends in the desired destination + FORCEINLINE bool PfDetectDestination(TileIndex tile, Trackdir td) + { + return + IsSafeWaitingPosition(Yapf().GetVehicle(), tile, td, true, Types::TrackFollower::Allow90degTurns()) && + IsWaitingPositionFree(Yapf().GetVehicle(), tile, td, Types::TrackFollower::Allow90degTurns()); + } + + /** Called by YAPF to calculate cost estimate. Calculates distance to the destination + * adds it to the actual cost from origin and stores the sum to the Node::m_estimate. */ + FORCEINLINE bool PfCalcEstimate(Node& n) + { + n.m_estimate = n.m_cost; + return true; + } +}; + +template class CYapfDestinationTileOrStationRailT : public CYapfDestinationRailBase { diff --git a/src/yapf/yapf_node_rail.hpp b/src/yapf/yapf_node_rail.hpp index 6b42d30..192bce2 100644 --- a/src/yapf/yapf_node_rail.hpp +++ b/src/yapf/yapf_node_rail.hpp @@ -39,6 +39,7 @@ enum EndSegmentReason { ESR_DEPOT, ///< stop in the depot (could be a target next time) ESR_WAYPOINT, ///< waypoint encountered (could be a target next time) ESR_STATION, ///< station encountered (could be a target next time) + ESR_SAFE_TILE, ///< safe waiting position found (could be a target) /* The following reasons are used only internally by PfCalcCost(). * They should not be found in the cached segment. */ @@ -62,6 +63,7 @@ enum EndSegmentReasonBits { ESRB_DEPOT = 1 << ESR_DEPOT, ESRB_WAYPOINT = 1 << ESR_WAYPOINT, ESRB_STATION = 1 << ESR_STATION, + ESRB_SAFE_TILE = 1 << ESR_SAFE_TILE, ESRB_PATH_TOO_LONG = 1 << ESR_PATH_TOO_LONG, ESRB_FIRST_TWO_WAY_RED = 1 << ESR_FIRST_TWO_WAY_RED, @@ -71,10 +73,10 @@ enum EndSegmentReasonBits { /* Additional (composite) values. */ /* What reasons mean that the target can be found and needs to be detected. */ - ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION, + ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE, /* What reasons can be stored back into cached segment. */ - ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION, + ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE, /* Reasons to abort pathfinding in this direction. */ ESRB_ABORT_PF_MASK = ESRB_DEAD_END | ESRB_PATH_TOO_LONG | ESRB_INFINITE_LOOP | ESRB_FIRST_TWO_WAY_RED, diff --git a/src/yapf/yapf_rail.cpp b/src/yapf/yapf_rail.cpp index 9e8ecac..507b82b 100644 --- a/src/yapf/yapf_rail.cpp +++ b/src/yapf/yapf_rail.cpp @@ -202,6 +202,88 @@ public: }; template +class CYapfFollowAnySafeTileRailT : protected CYapfReserveTrack +{ +public: + typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class) + typedef typename Types::TrackFollower TrackFollower; + typedef typename Types::NodeList::Titem Node; ///< this will be our node type + typedef typename Node::Key Key; ///< key to hash tables + +protected: + /// to access inherited path finder + FORCEINLINE Tpf& Yapf() {return *static_cast(this);} + +public: + /** Called by YAPF to move from the given node to the next tile. For each + * reachable trackdir on the new tile creates new node, initializes it + * and adds it to the open list by calling Yapf().AddNewNode(n) */ + inline void PfFollowNode(Node& old_node) + { + TrackFollower F(Yapf().GetVehicle(), Yapf().GetCompatibleRailTypes()); + if (F.Follow(old_node.GetLastTile(), old_node.GetLastTrackdir())) + Yapf().AddMultipleNodes(&old_node, F); + } + + /** Return debug report character to identify the transportation type */ + FORCEINLINE char TransportTypeChar() const {return 't';} + + static bool stFindNearestSafeTile(const Vehicle *v, TileIndex t1, Trackdir td, bool override_railtype) + { + /* Create pathfinder instance */ + Tpf pf1; +#if !DEBUG_YAPF_CACHE + bool result1 = pf1.FindNearestSafeTile(v, t1, td, override_railtype, false); + +#else + bool result2 = pf1.FindNearestSafeTile(v, t1, td, override_railtype, true); + Tpf pf2; + pf2.DisableCache(true); + bool result1 = pf2.FindNearestSafeTile(v, t1, td, override_railtype, false); + if (result1 != result2) { + DEBUG(yapf, 0, "CACHE ERROR: FindSafeTile() = [%s, %s]", result2 ? "T" : "F", result1 ? "T" : "F"); + DumpTarget dmp1, dmp2; + pf1.DumpBase(dmp1); + pf2.DumpBase(dmp2); + FILE *f1 = fopen("C:\\yapf1.txt", "wt"); + FILE *f2 = fopen("C:\\yapf2.txt", "wt"); + fwrite(dmp1.m_out.Data(), 1, dmp1.m_out.Size(), f1); + fwrite(dmp2.m_out.Data(), 1, dmp2.m_out.Size(), f2); + fclose(f1); + fclose(f2); + } +#endif + + return result1; + } + + bool FindNearestSafeTile(const Vehicle *v, TileIndex t1, Trackdir td, bool override_railtype, bool dont_reserve) + { + /* Set origin and destination. */ + Yapf().SetOrigin(t1, td); + Yapf().SetDestination(v, override_railtype); + + bool bFound = Yapf().FindPath(v); + if (!bFound) return false; + + /* Found a destination, set as reservation target. */ + Node *pNode = Yapf().GetBestNode(); + this->SetReservationTarget(pNode, pNode->GetLastTile(), pNode->GetLastTrackdir()); + + /* Walk through the path back to the origin. */ + Node* pPrev = NULL; + while (pNode->m_parent != NULL) { + pPrev = pNode; + pNode = pNode->m_parent; + + this->FindSafePositionOnNode(pPrev); + } + + return dont_reserve || this->TryReservePath(NULL); + } +}; + +template class CYapfFollowRailT : protected CYapfReserveTrack { public: @@ -364,6 +446,9 @@ struct CYapfRail2 : CYapfT > {}; struct CYapfAnyDepotRail2 : CYapfT > {}; +struct CYapfAnySafeTileRail1 : CYapfT > {}; +struct CYapfAnySafeTileRail2 : CYapfT > {}; + Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target) { @@ -464,6 +549,19 @@ bool YapfFindNearestRailDepotTwoWay(const Vehicle *v, int max_distance, int reve return ret; } +bool YapfRailFindNearestSafeTile(const Vehicle *v, TileIndex tile, Trackdir td, bool override_railtype) +{ + typedef bool (*PfnFindNearestSafeTile)(const Vehicle*, TileIndex, Trackdir, bool); + PfnFindNearestSafeTile pfnFindNearestSafeTile = CYapfAnySafeTileRail1::stFindNearestSafeTile; + + /* check if non-default YAPF type needed */ + if (_settings_game.pf.forbid_90_deg) { + pfnFindNearestSafeTile = &CYapfAnySafeTileRail2::stFindNearestSafeTile; + } + + return pfnFindNearestSafeTile(v, tile, td, override_railtype); +} + /** if any track changes, this counter is incremented - that will invalidate segment cost cache */ int CSegmentCostCacheBase::s_rail_change_counter = 0; -- 1.5.6 From a52c391d8d9afc93ed7b598f89e04d84ba9ad047 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 22 Jun 2008 03:02:47 +0200 Subject: Extend CFollowTrackT to not require a Vehicle when following rail. --- src/yapf/follow_track.hpp | 24 +++++++++++++++++++----- 1 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/yapf/follow_track.hpp b/src/yapf/follow_track.hpp index a2f727d..c642b20 100644 --- a/src/yapf/follow_track.hpp +++ b/src/yapf/follow_track.hpp @@ -24,6 +24,7 @@ struct CFollowTrackT }; const Vehicle *m_veh; ///< moving vehicle + Owner m_veh_owner; ///< owner of the vehicle TileIndex m_old_tile; ///< the origin (vehicle moved from) before move Trackdir m_old_td; ///< the trackdir (the vehicle was on) before move TileIndex m_new_tile; ///< the new tile (the vehicle has entered) @@ -42,10 +43,23 @@ struct CFollowTrackT Init(v, railtype_override, pPerf); } + FORCEINLINE CFollowTrackT(Owner o, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = NULL) + { + m_veh = NULL; + Init(o, railtype_override, pPerf); + } + FORCEINLINE void Init(const Vehicle *v, RailTypes railtype_override, CPerformanceTimer *pPerf) { assert(!IsRailTT() || (v != NULL && v->type == VEH_TRAIN)); m_veh = v; + Init(v != NULL ? v->owner : INVALID_OWNER, railtype_override == INVALID_RAILTYPES ? v->u.rail.compatible_railtypes : railtype_override, pPerf); + } + + FORCEINLINE void Init(Owner o, RailTypes railtype_override, CPerformanceTimer *pPerf) + { + assert((!IsRoadTT() || m_veh != NULL) && (!IsRailTT() || railtype_override != INVALID_RAILTYPES)); + m_veh_owner = o; m_pPerf = pPerf; // don't worry, all is inlined so compiler should remove unnecessary initializations m_new_tile = INVALID_TILE; @@ -54,7 +68,7 @@ struct CFollowTrackT m_is_station = m_is_bridge = m_is_tunnel = false; m_tiles_skipped = 0; m_err = EC_NONE; - if (IsRailTT()) m_railtypes = railtype_override == INVALID_RAILTYPES ? v->u.rail.compatible_railtypes : railtype_override; + m_railtypes = railtype_override; } FORCEINLINE static TransportType TT() {return Ttr_type_;} @@ -88,7 +102,7 @@ struct CFollowTrackT m_old_tile = old_tile; m_old_td = old_td; m_err = EC_NONE; - assert(((TrackStatusToTrackdirBits(GetTileTrackStatus(m_old_tile, TT(), m_veh->u.road.compatible_roadtypes)) & TrackdirToTrackdirBits(m_old_td)) != 0) || + assert(((TrackStatusToTrackdirBits(GetTileTrackStatus(m_old_tile, TT(), m_veh ? m_veh->u.road.compatible_roadtypes : 0)) & TrackdirToTrackdirBits(m_old_td)) != 0) || (GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR)); // Disable the assertion for single tram bits m_exitdir = TrackdirToExitdir(m_old_td); if (ForcedReverse()) return true; @@ -172,7 +186,7 @@ protected: if (IsRailTT() && GetTileType(m_new_tile) == MP_RAILWAY && IsPlainRailTile(m_new_tile)) { m_new_td_bits = (TrackdirBits)(GetTrackBits(m_new_tile) * 0x101); } else { - m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), m_veh->u.road.compatible_roadtypes)); + m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), m_veh != NULL ? m_veh->u.road.compatible_roadtypes : 0)); if (m_new_td_bits == 0) { /* GetTileTrackStatus() returns 0 for single tram bits. @@ -252,7 +266,7 @@ protected: return false; } // don't try to enter other player's depots - if (GetTileOwner(m_new_tile) != m_veh->owner) { + if (GetTileOwner(m_new_tile) != m_veh_owner) { m_err = EC_OWNER; return false; } @@ -266,7 +280,7 @@ protected: } // rail transport is possible only on tiles with the same owner as vehicle - if (IsRailTT() && GetTileOwner(m_new_tile) != m_veh->owner) { + if (IsRailTT() && GetTileOwner(m_new_tile) != m_veh_owner) { // different owner m_err = EC_NO_WAY; return false; -- 1.5.6 From 4156f9b7d619804454ec8c9ddde3b05820646f54 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 19:12:22 +0200 Subject: Implement track reserving for NPF as well. --- src/npf.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/npf.h | 6 +++- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/npf.cpp b/src/npf.cpp index 478b62b..b8cb499 100644 --- a/src/npf.cpp +++ b/src/npf.cpp @@ -441,9 +441,48 @@ static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current) } } -/* 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* FindSafePosition(PathNode *path, const Vehicle *v) +{ + /* If there is no signal, reserve the whole path. */ + PathNode *sig = path; + + for(; path->parent != NULL; path = path->parent) { + if (IsSafeWaitingPosition(v, path->node.tile, (Trackdir)path->node.direction, true, _settings_game.pf.forbid_90_deg)) { + sig = path; + } + } + + 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 first_run = true; + for (; start != end; start = start->parent) { + if (IsRailwayStationTile(start->node.tile) && first_run) { + SetRailwayStationPlatformReservation(start->node.tile, TrackdirToExitdir((Trackdir)start->node.direction), false); + } else { + UnreserveRailTrack(start->node.tile, TrackdirToTrack((Trackdir)start->node.direction)); + } + first_run = false; + } +} + +/** + * 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) { @@ -452,6 +491,40 @@ static void NPFSaveTargetData(AyStar* as, OpenListNode* current) ftd->best_path_dist = current->g; ftd->best_bird_dist = 0; ftd->node = current->path.node; + ftd->res_okay = false; + + if (as->user_target != NULL && ((NPFFindStationOrTileData*)as->user_target)->reserve_path && as->user_data[NPF_TYPE] == TRANSPORT_RAIL) { + /* Path reservation is requested. */ + const Vehicle *v = ((NPFFindStationOrTileData*)as->user_target)->v; + + const PathNode *target = FindSafePosition(¤t->path, v); + ftd->node = target->node; + + /* If the target is a station skip to platform end. */ + if (IsRailwayStationTile(target->node.tile)) { + DiagDirection dir = TrackdirToExitdir((Trackdir)target->node.direction); + uint len = GetStationByTile(target->node.tile)->GetPlatformLength(target->node.tile, dir); + TileIndex end_tile = TILE_ADD(target->node.tile, (len - 1) * TileOffsByDiagDir(dir)); + + /* Update only end tile, trackdir of a station stays the same. */ + ftd->node.tile = end_tile; + if (!IsWaitingPositionFree(v, end_tile, (Trackdir)target->node.direction, _settings_game.pf.forbid_90_deg)) return; + SetRailwayStationPlatformReservation(target->node.tile, dir, true); + SetRailwayStationReservation(target->node.tile, false); + } else { + if (!IsWaitingPositionFree(v, target->node.tile, (Trackdir)target->node.direction, _settings_game.pf.forbid_90_deg)) return; + } + + for (const PathNode *cur = target; cur->parent != NULL; cur = cur->parent) { + if (!TryReserveRailTrack(cur->node.tile, TrackdirToTrack((Trackdir)cur->node.direction))) { + /* Reservation failed, undo. */ + ClearPathReservation(target, cur); + return; + } + } + + ftd->res_okay = true; + } } /** @@ -782,7 +855,9 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, bool ignore_start /* Initialize result */ result.best_bird_dist = (uint)-1; result.best_path_dist = (uint)-1; - result.best_trackdir = INVALID_TRACKDIR; + result.best_trackdir = INVALID_TRACKDIR; + result.node.tile = INVALID_TILE; + result.res_okay = false; _npf_aystar.user_path = &result; /* Initialize target */ @@ -868,7 +943,7 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, */ Queue depots; int r; - NPFFoundTargetData best_result = {(uint)-1, (uint)-1, INVALID_TRACKDIR, {INVALID_TILE, 0, {0, 0}}}; + NPFFoundTargetData best_result = {(uint)-1, (uint)-1, INVALID_TRACKDIR, {INVALID_TILE, 0, {0, 0}}, false}; NPFFoundTargetData result; NPFFindStationOrTileData target; AyStarNode start; @@ -973,7 +1048,7 @@ void InitializeNPF() _npf_aystar.max_search_nodes = _settings_game.pf.npf.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, @@ -989,4 +1064,6 @@ void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v) fstd->dest_coords = v->dest_tile; fstd->station_index = INVALID_STATION; } + fstd->reserve_path = reserve_path; + fstd->v = v; } diff --git a/src/npf.h b/src/npf.h index 5a70b6b..d1429b3 100644 --- a/src/npf.h +++ b/src/npf.h @@ -45,6 +45,8 @@ enum { 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 + const Vehicle* v; ///< The vehicle we are pathfinding for }; /* Indices into AyStar.userdata[] */ @@ -67,6 +69,7 @@ enum NPFNodeFlag { 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_IGNORE_START_TILE, ///< Used to mark that the start tile is invalid, and searching should start from the second tile on + NPF_FLAG_TARGET_RESERVED, ///< Used to mark that the possible reservation target is already reserved }; /* Meant to be stored in AyStar.userpath */ @@ -75,6 +78,7 @@ struct NPFFoundTargetData { uint best_path_dist; ///< The shortest path. Is (uint)-1 if no path is found Trackdir best_trackdir; ///< The trackdir that leads to the shortest path/closest birds dist AyStarNode node; ///< The node within the target the search led us to + bool res_okay; ///< True if a path reservation could be made }; /* These functions below are _not_ re-entrant, in favor of speed! */ @@ -105,7 +109,7 @@ NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir t * of choices and accurate heuristics, such as water. */ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, bool ignore_start_tile, 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); /* -- 1.5.6 From ee3145856d8a79cc64e3aeb0c6affbd1252b1e29 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Wed, 4 Jun 2008 21:04:25 +0200 Subject: Added PBS related penalties to NPF. --- src/npf.cpp | 76 +++++++++++++++++++++++++++++++++++++-------------- src/npf.h | 2 + src/settings.cpp | 2 + src/settings_type.h | 2 + 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/npf.cpp b/src/npf.cpp index b8cb499..4aa817b 100644 --- a/src/npf.cpp +++ b/src/npf.cpp @@ -24,6 +24,7 @@ #include "vehicle_base.h" #include "settings_type.h" #include "tunnelbridge.h" +#include "pbs.h" static AyStar _npf_aystar; @@ -221,6 +222,23 @@ static uint NPFSlopeCost(AyStarNode* current) * 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 _settings_game.pf.npf.npf_rail_pbs_cross_penalty * (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 1); + } + } + return _settings_game.pf.npf.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. @@ -354,30 +372,46 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare /* 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 */ - - /* 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 += _settings_game.pf.npf.npf_rail_firstred_exit_penalty; + 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 (!IsPbsSignal(sigtype)) { + if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) { + /* Penalise exit and combo signals differently (heavier) */ + cost += _settings_game.pf.npf.npf_rail_firstred_exit_penalty; + } else { + cost += _settings_game.pf.npf.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 += _settings_game.pf.npf.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 (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(trackdir)) && !NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL)) { + cost += _settings_game.pf.npf.npf_rail_pbs_signal_back_penalty; + } } /* Penalise the tile if it is a target tile and the last signal was @@ -406,7 +440,7 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare } /* 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); diff --git a/src/npf.h b/src/npf.h index d1429b3..9ab9a45 100644 --- a/src/npf.h +++ b/src/npf.h @@ -66,6 +66,8 @@ enum { /* Flags for AyStarNode.userdata[NPF_NODE_FLAGS]. Use NPFGetBit() and NPFGetBit() to use them. */ enum NPFNodeFlag { NPF_FLAG_SEEN_SIGNAL, ///< Used to mark that a signal was seen on the way, for rail only + 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_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_IGNORE_START_TILE, ///< Used to mark that the start tile is invalid, and searching should start from the second tile on diff --git a/src/settings.cpp b/src/settings.cpp index 1b1c2b5..b943226 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1700,6 +1700,8 @@ const SettingDesc _patch_settings[] = { SDT_VAR(GameSettings, pf.npf.npf_rail_slope_penalty, SLE_UINT, 0, 0, ( 1 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL), SDT_VAR(GameSettings, pf.npf.npf_rail_curve_penalty, SLE_UINT, 0, 0, 1, 0, 100000, 0, STR_NULL, NULL), SDT_VAR(GameSettings, pf.npf.npf_rail_depot_reverse_penalty, SLE_UINT, 0, 0, ( 50 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.npf.npf_rail_pbs_cross_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, ( 3 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.npf.npf_rail_pbs_signal_back_penalty, SLE_UINT, 99, SL_MAX_VERSION, 0, 0, ( 15 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL), SDT_VAR(GameSettings, pf.npf.npf_buoy_penalty, SLE_UINT, 0, 0, ( 2 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL), SDT_VAR(GameSettings, pf.npf.npf_water_curve_penalty, SLE_UINT, 0, 0, (NPF_TILE_LENGTH / 4), 0, 100000, 0, STR_NULL, NULL), SDT_VAR(GameSettings, pf.npf.npf_road_curve_penalty, SLE_UINT, 0, 0, 1, 0, 100000, 0, STR_NULL, NULL), diff --git a/src/settings_type.h b/src/settings_type.h index 708d441..493e601 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -185,6 +185,8 @@ struct NPFSettings { uint32 npf_rail_slope_penalty; ///< the penalty for sloping upwards uint32 npf_rail_curve_penalty; ///< the penalty for curves uint32 npf_rail_depot_reverse_penalty; ///< the penalty for reversing in depots + 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 uint32 npf_buoy_penalty; ///< the penalty for going over (through) a buoy uint32 npf_water_curve_penalty; ///< the penalty for curves uint32 npf_road_curve_penalty; ///< the penalty for curves -- 1.5.6 From b6413b5fdfeacbac8d435de362733ec21869eeac Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 16 Mar 2008 01:42:31 +0100 Subject: Extend NPF with a function to find a safe tile and reserve a path. --- src/npf.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/npf.h | 7 +++++++ 2 files changed, 54 insertions(+), 0 deletions(-) diff --git a/src/npf.cpp b/src/npf.cpp index 4aa817b..d5f431b 100644 --- a/src/npf.cpp +++ b/src/npf.cpp @@ -456,6 +456,17 @@ static int32 NPFFindDepot(AyStar* as, OpenListNode *current) AYSTAR_FOUND_END_NODE : AYSTAR_DONE; } +/** Find any safe and free tile. */ +static int32 NPFFindSafeTile(AyStar *as, OpenListNode *current) +{ + const Vehicle *v = ((NPFFindStationOrTileData*)as->user_target)->v; + + return + IsSafeWaitingPosition(v, current->path.node.tile, (Trackdir)current->path.node.direction, true, _settings_game.pf.forbid_90_deg) && + IsWaitingPositionFree(v, current->path.node.tile, (Trackdir)current->path.node.direction, _settings_game.pf.forbid_90_deg) ? + AYSTAR_FOUND_END_NODE : AYSTAR_DONE; +} + /* Will find a station identified using the NPFFindStationOrTileData */ static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current) { @@ -820,6 +831,18 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current) } } + if (NPFGetFlag(¤t->path.node, NPF_FLAG_IGNORE_RESERVED)) { + /* Mask out any reserved tracks. */ + TrackBits reserved = GetReservedTrackbits(dst_tile); + trackdirbits &= ~TrackBitsToTrackdirBits(reserved); + + uint bits = TrackdirBitsToTrackBits(trackdirbits); + int i; + FOR_EACH_SET_BIT(i, bits) { + if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) trackdirbits &= ~TrackToTrackdirBits((Track)i); + } + } + /* Enumerate possible track */ uint i = 0; while (trackdirbits != 0) { @@ -1065,6 +1088,30 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, return best_result; } +NPFFoundTargetData NPFRouteToSafeTile(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool override_railtype) +{ + assert(v->type == VEH_TRAIN); + + NPFFindStationOrTileData fstd; + fstd.v = v; + fstd.reserve_path = true; + + AyStarNode start1; + start1.tile = tile; + /* We set this in case the target is also the start tile, we will just + * return a not found then */ + start1.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR; + start1.direction = trackdir; + NPFSetFlag(&start1, NPF_FLAG_IGNORE_RESERVED, true); + + RailTypes railtypes = v->u.rail.compatible_railtypes; + if (override_railtype) railtypes |= GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes; + + /* perform a breadth first search. Target is NULL, + * since we are just looking for any safe tile...*/ + return NPFRouteInternal(&start1, true, NULL, false, &fstd, NPFFindSafeTile, NPFCalcZero, TRANSPORT_RAIL, 0, v->owner, railtypes, 0); +} + void InitializeNPF() { static bool first_init = true; diff --git a/src/npf.h b/src/npf.h index 9ab9a45..bf19b62 100644 --- a/src/npf.h +++ b/src/npf.h @@ -72,6 +72,7 @@ enum NPFNodeFlag { NPF_FLAG_LAST_SIGNAL_RED, ///< Used to mark that the last signal on this path was red NPF_FLAG_IGNORE_START_TILE, ///< Used to mark that the start tile is invalid, and searching should start from the second tile on NPF_FLAG_TARGET_RESERVED, ///< Used to mark that the possible reservation target is already reserved + NPF_FLAG_IGNORE_RESERVED, ///< Used to mark that reserved tiles should be considered impassable }; /* Meant to be stored in AyStar.userpath */ @@ -111,6 +112,12 @@ NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir t * of choices and accurate heuristics, such as water. */ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, bool ignore_start_tile, TransportType type, uint sub_type, Owner owner, RailTypes railtypes); +/** + * Search for any safe tile using a breadth first search and try to reserve a path. + */ +NPFFoundTargetData NPFRouteToSafeTile(const Vehicle *v, TileIndex tile, Trackdir trackdir,bool override_railtype); + + void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, Vehicle *v, bool reserve_path = false); -- 1.5.6 From d402dfd579dabb79a2b3a09471669f6c11f08e31 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jun 2008 19:16:13 +0200 Subject: Declare the functions for processing conditional orders as non-static. --- src/order_cmd.cpp | 4 ++-- src/order_func.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index b8bc5dd..1320bf0 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1627,7 +1627,7 @@ static bool OrderConditionCompare(OrderConditionComparator occ, int variable, in * @param v the vehicle to update * @return index of next order to jump to, or INVALID_VEH_ORDER_ID to use the next order */ -static VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v) +VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v) { if (order->GetType() != OT_CONDITIONAL) return INVALID_VEH_ORDER_ID; @@ -1653,7 +1653,7 @@ static VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle * @param order the order the vehicle currently has * @param v the vehicle to update */ -static bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0) +bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth) { switch (order->GetType()) { case OT_GOTO_STATION: diff --git a/src/order_func.h b/src/order_func.h index 8d145da..157bd30 100644 --- a/src/order_func.h +++ b/src/order_func.h @@ -37,6 +37,8 @@ bool VehicleHasDepotOrders(const Vehicle *v); void CheckOrders(const Vehicle*); void DeleteVehicleOrders(Vehicle *v); bool ProcessOrders(Vehicle *v); +bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0); +VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v); void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable); -- 1.5.6 From 36e12626b79ac8e926bc30521956d29b58a3c405 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Wed, 4 Jun 2008 21:05:11 +0200 Subject: Trains can now also be in a 'stuck' state when waiting on a path reservation. --- src/lang/english.txt | 1 + src/train_cmd.cpp | 33 ++++++++++++++++++++++++++++----- src/vehicle.cpp | 3 ++- src/vehicle_base.h | 5 ++++- src/vehicle_gui.cpp | 2 ++ 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 5be2ffd..efc8f9a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2890,6 +2890,7 @@ STR_TRAIN_STOPPING_VEL :{RED}Stopping, STR_INCOMPATIBLE_RAIL_TYPES :Incompatible rail types STR_TRAIN_NO_POWER :{RED}No power STR_TRAIN_START_NO_CATENARY :This track lacks catenary, so the train can't start +STR_TRAIN_STUCK :{ORANGE}Waiting for free path STR_NEW_VEHICLE_NOW_AVAILABLE :{BLACK}{BIGFONT}New {STRING} now available! STR_NEW_VEHICLE_TYPE :{BLACK}{BIGFONT}{ENGINE} diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 88fa3d1..444169b 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1550,10 +1550,27 @@ static inline void SetLastSpeed(Vehicle *v, int spd) } } -static void SwapTrainFlags(byte *swap_flag1, byte *swap_flag2) +/** Mark a train as stuck and stop it if it isn't stopped right now. */ +static void MarkTrainAsStuck(Vehicle *v) { - byte flag1 = *swap_flag1; - byte flag2 = *swap_flag2; + if (!HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) { + /* It is the first time the problem occured, set the "train stuck" flag. */ + SetBit(v->u.rail.flags, VRF_TRAIN_STUCK); + v->load_unload_time_rem = 0; + + /* Stop train */ + v->cur_speed = 0; + v->subspeed = 0; + SetLastSpeed(v, 0); + + InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH); + } +} + +static void SwapTrainFlags(uint16 *swap_flag1, uint16 *swap_flag2) +{ + uint16 flag1 = *swap_flag1; + uint16 flag2 = *swap_flag2; /* Clear the flags */ ClrBit(*swap_flag1, VRF_GOINGUP); @@ -2606,7 +2623,7 @@ static int UpdateTrainSpeed(Vehicle *v) { uint accel; - if (v->vehstatus & VS_STOPPED || HasBit(v->u.rail.flags, VRF_REVERSING)) { + if (v->vehstatus & VS_STOPPED || HasBit(v->u.rail.flags, VRF_REVERSING) || HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) { if (_settings_game.vehicle.realistic_acceleration) { accel = GetTrainAcceleration(v, AM_BRAKE) * 2; } else { @@ -2995,6 +3012,9 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) /* In front of a red signal */ Trackdir i = FindFirstTrackdir(trackdirbits); + /* Don't handle stuck trains here. */ + if (HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) return; + if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) { v->cur_speed = 0; v->subspeed = 0; @@ -3433,7 +3453,10 @@ static void TrainLocoHandler(Vehicle *v, bool mode) return; } - if (v->u.rail.force_proceed != 0) v->u.rail.force_proceed--; + if (v->u.rail.force_proceed != 0) { + v->u.rail.force_proceed--; + ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK); + } /* train is broken down? */ if (v->breakdown_ctr != 0) { diff --git a/src/vehicle.cpp b/src/vehicle.cpp index b0ad681..1ad304b 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2076,7 +2076,8 @@ static const SaveLoad _train_desc[] = { SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, railtype), SLE_UINT8), SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, track), SLE_UINT8), - SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_UINT8, 2, SL_MAX_VERSION), + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_FILE_U8 | SLE_VAR_U16, 2, 98), + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_UINT16, 99, SL_MAX_VERSION), SLE_CONDNULL(2, 2, 59), SLE_CONDNULL(2, 2, 19), diff --git a/src/vehicle_base.h b/src/vehicle_base.h index c7b09b8..6ffab32 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -115,7 +115,7 @@ struct VehicleRail { * 0xffff == not in train */ EngineID first_engine; - byte flags; + uint16 flags; TrackBitsByte track; byte force_proceed; RailTypeByte railtype; @@ -143,6 +143,9 @@ enum VehicleRailFlags { /* used for vehicle var 0xFE bit 8 (toggled each time the train is reversed, accurate for first vehicle only) */ VRF_TOGGLE_REVERSE = 7, + + /* used to mark a train that can't get a path reservation */ + VRF_TRAIN_STUCK = 8, }; struct VehicleAir { diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 404e824..e47287e 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1893,6 +1893,8 @@ struct VehicleViewWindow : Window { } else { // no train str = STR_8861_STOPPED; } + } else if (v->type == VEH_TRAIN && HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) { + str = STR_TRAIN_STUCK; } else { // vehicle is in a "normal" state, show current order switch (v->current_order.GetType()) { case OT_GOTO_STATION: { -- 1.5.6 From c84a27334f558975d092a16a523aaf3e593287aa Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 24 May 2008 00:28:29 +0200 Subject: A train leaving a tile should unreserve the track it came from if it was reserved. --- src/train_cmd.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 46 insertions(+), 0 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 444169b..c87e3b9 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -51,6 +51,7 @@ #include "order_func.h" #include "newgrf_station.h" #include "effectvehicle_func.h" +#include "pbs.h" #include "table/strings.h" #include "table/train_cmd.h" @@ -2305,6 +2306,37 @@ static bool CheckTrainStayInDepot(Vehicle *v) 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 (_settings_client.gui.show_track_reservation) { + 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 (!IsCompatibleTrainStationTile(new_tile, tile)) { + SetRailwayStationPlatformReservation(tile, ReverseDiagDir(dir), false); + } + } else { + /* Any other tile */ + UnreserveRailTrack(tile, TrackdirToTrack(track_dir)); + } +} + /** Check for station tiles */ struct TrainTrackFollowerData { TileIndex dest_coords; @@ -2965,6 +2997,10 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET); return; } + if (v->Next() == NULL && IsRailDepotTile(v->tile) && HasBit(r, VETS_ENTERED_WORMHOLE)) { + SetDepotReservation(v->tile, false); + if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile); + } if (v->current_order.IsType(OT_LEAVESTATION)) { v->current_order.Free(); @@ -3067,6 +3103,16 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) 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)) { -- 1.5.6 From c5fbf0d0baa8f36b04e8d9348522230d04746b3e Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Wed, 4 Jun 2008 21:06:09 +0200 Subject: Trains now reserve paths trough PBS signals. Bump savegame version. --- src/saveload.cpp | 2 +- src/settings.cpp | 4 + src/settings_type.h | 4 + src/train.h | 2 + src/train_cmd.cpp | 216 ++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 200 insertions(+), 28 deletions(-) diff --git a/src/saveload.cpp b/src/saveload.cpp index dd08245..640ebcc 100644 --- a/src/saveload.cpp +++ b/src/saveload.cpp @@ -36,7 +36,7 @@ #include "table/strings.h" -extern const uint16 SAVEGAME_VERSION = 98; +extern const uint16 SAVEGAME_VERSION = 99; SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/settings.cpp b/src/settings.cpp index b943226..4030a3e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1689,6 +1689,10 @@ const SettingDesc _patch_settings[] = { SDT_VAR(GameSettings, pf.wait_twoway_signal, SLE_UINT8, 0, 0, 41, 2, 100, 0, STR_NULL, NULL), SDT_CONDLISTO(GameSettings, economy.town_noise_population, 3, SLE_UINT16, 96, SL_MAX_VERSION, 0,D0, "800,2000,4000", STR_NULL, NULL, CheckNoiseToleranceLevel), + SDT_CONDVAR(GameSettings, pf.wait_for_pbs_path, SLE_UINT8, 99, SL_MAX_VERSION, 0, 0, 30, 2, 255, 0, STR_NULL, NULL), + SDT_CONDBOOL(GameSettings, pf.reserve_paths, 99, SL_MAX_VERSION, 0, 0, false, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.path_backoff_interval, SLE_UINT8, 99, SL_MAX_VERSION, 0, 0, 20, 1, 255, 0, STR_NULL, NULL), + SDT_VAR(GameSettings, pf.opf.pf_maxlength, SLE_UINT16, 0, 0, 4096, 64, 65535, 0, STR_NULL, NULL), SDT_VAR(GameSettings, pf.opf.pf_maxdepth, SLE_UINT8, 0, 0, 48, 4, 255, 0, STR_NULL, NULL), diff --git a/src/settings_type.h b/src/settings_type.h index 493e601..c7341cd 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -244,6 +244,10 @@ struct PathfinderSettings { byte wait_oneway_signal; ///< waitingtime in days before a oneway signal byte wait_twoway_signal; ///< waitingtime in days before a twoway signal + bool reserve_paths; ///< always reserve paths regardless of signal type. + byte wait_for_pbs_path; ///< how long to wait for a path reservation. + byte path_backoff_interval; ///< ticks between checks for a free path. + OPFSettings opf; ///< pathfinder settings for the old pathfinder NPFSettings npf; ///< pathfinder settings for the new pathfinder YAPFSettings yapf; ///< pathfinder settings for the yet another pathfinder diff --git a/src/train.h b/src/train.h index af7bb94..da6ae35 100644 --- a/src/train.h +++ b/src/train.h @@ -273,6 +273,8 @@ int CheckTrainInDepot(const Vehicle *v, bool needs_to_be_stopped); int CheckTrainStoppedInDepot(const Vehicle *v); void UpdateTrainAcceleration(Vehicle* v); +void FreeTrainTrackReservation(const Vehicle *v, TileIndex origin = INVALID_TILE, Trackdir orig_td = INVALID_TRACKDIR); + /** * This class 'wraps' Vehicle; you do not actually instantiate this class. * You create a Vehicle using AllocateVehicle, so it is added to the pool diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index c87e3b9..8609f4f 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -33,6 +33,7 @@ #include "newgrf_text.h" #include "direction_func.h" #include "yapf/yapf.h" +#include "yapf/follow_track.hpp" #include "cargotype.h" #include "group.h" #include "table/sprites.h" @@ -56,6 +57,7 @@ #include "table/strings.h" #include "table/train_cmd.h" +static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck); static bool TrainCheckIfLineEnds(Vehicle *v); static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image); static TileIndex TrainApproachingCrossingTile(const Vehicle *v); @@ -2069,14 +2071,17 @@ static TrainFindDepotData FindClosestTrainDepot(Vehicle *v, int max_distance) tfdd.best_length = UINT_MAX; tfdd.reverse = false; - TileIndex tile = v->tile; - if (IsRailDepotTile(tile)) { - tfdd.tile = tile; + PBSTileInfo origin = FollowTrainReservation(v); + if (IsRailDepotTile(origin.tile)) { + tfdd.tile = origin.tile; tfdd.best_length = 0; return tfdd; } - switch (_settings_game.pf.pathfinder_for_trains) { + uint8 pathfinder = _settings_game.pf.pathfinder_for_trains; + if ((_settings_game.pf.reserve_paths || HasReservedTracks(v->tile, v->u.rail.track)) && pathfinder == VPF_NTP) pathfinder = VPF_NPF; + + switch (pathfinder) { case VPF_YAPF: { /* YAPF */ bool found = YapfFindNearestRailDepotTwoWay(v, max_distance, NPF_INFINITE_PENALTY, &tfdd.tile, &tfdd.reverse); tfdd.best_length = found ? max_distance / 2 : UINT_MAX; // some fake distance or NOT_FOUND @@ -2105,12 +2110,12 @@ static TrainFindDepotData FindClosestTrainDepot(Vehicle *v, int max_distance) case VPF_NTP: { /* NTP */ /* search in the forward direction first. */ DiagDirection i = TrainExitDir(v->direction, v->u.rail.track); - NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); + NewTrainPathfind(v->tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); if (tfdd.best_length == UINT_MAX){ tfdd.reverse = true; /* search in backwards direction */ i = TrainExitDir(ReverseDir(v->direction), v->u.rail.track); - NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); + NewTrainPathfind(v->tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); } } break; } @@ -2337,6 +2342,54 @@ static void ClearPathReservation(TileIndex tile, Trackdir track_dir) } } +/** Free the reserved path in front of a vehicle. */ +void FreeTrainTrackReservation(const Vehicle *v, TileIndex origin, Trackdir orig_td) +{ + assert(IsFrontEngine(v)); + + TileIndex tile = origin != INVALID_TILE ? origin : v->tile; + Trackdir td = orig_td != INVALID_TRACKDIR ? orig_td : GetVehicleTrackdir(v); + bool free_tile = tile != v->tile || !(IsRailwayStationTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)); + + /* Don't free reservation if it's not ours. */ + if (TracksOverlap(GetReservedTrackbits(tile) | TrackToTrackBits(TrackdirToTrack(td)))) return; + + CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes); + while (ft.Follow(tile, td)) { + 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 (IsTileType(tile, MP_RAILWAY)) { + if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) { + /* Conventional signal along trackdir: remove reservation and stop. */ + UnreserveRailTrack(tile, TrackdirToTrack(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); + } + } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) { + break; + } + } + + /* Don't free first station/bridge/tunnel if we are on it. */ + if (free_tile || (!ft.m_is_station && !ft.m_is_tunnel && !ft.m_is_bridge)) ClearPathReservation(tile, td); + + free_tile = true; + } +} + /** Check for station tiles */ struct TrainTrackFollowerData { TileIndex dest_coords; @@ -2402,25 +2455,34 @@ static const byte _search_directions[6][4] = { 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) +/** + * Perform pathfinding for a train. + * + * @param v The train + * @param tile The tile the train is about to enter + * @param enterdir Diagonal direction the train is coming from + * @param tracks Usable tracks on the new tile + * @param path_not_found [out] Set to false if the pathfinder couldn't find a way to the destination + * @param do_track_reservation + * @param dest [out] + * @return The best track the train should follow + */ +static Track DoTrainPathfind(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool do_track_reservation, PBSTileInfo *dest) { 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); + uint8 pathfinder = _settings_game.pf.pathfinder_for_trains; + if (do_track_reservation && pathfinder == VPF_NTP) pathfinder = VPF_NPF; - switch (_settings_game.pf.pathfinder_for_trains) { + switch (pathfinder) { case VPF_YAPF: { /* YAPF */ - Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, tracks, &path_not_found); + Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, tracks, path_not_found, do_track_reservation, dest); if (trackdir != INVALID_TRACKDIR) { best_track = TrackdirToTrack(trackdir); } else { @@ -2432,12 +2494,18 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir void *perf = NpfBeginInterval(); NPFFindStationOrTileData fstd; - NPFFillWithOrderData(&fstd, v); - /* The enterdir for the new tile, is the exitdir for the old tile */ - Trackdir trackdir = GetVehicleTrackdir(v); - assert(trackdir != INVALID_TRACKDIR); + NPFFillWithOrderData(&fstd, v, do_track_reservation); - NPFFoundTargetData ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_RAIL, 0, v->owner, v->u.rail.compatible_railtypes); + PBSTileInfo origin = FollowTrainReservation(v); + assert(IsValidTrackdir(origin.trackdir)); + + NPFFoundTargetData ftd = NPFRouteToStationOrTile(origin.tile, origin.trackdir, true, &fstd, TRANSPORT_RAIL, 0, v->owner, v->u.rail.compatible_railtypes); + + if (dest != NULL) { + dest->tile = ftd.node.tile; + dest->trackdir = (Trackdir)ftd.node.direction; + dest->okay = ftd.res_okay; + } if (ftd.best_trackdir == INVALID_TRACKDIR) { /* We are already at our target. Just do something @@ -2449,7 +2517,7 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir * 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); } @@ -2474,7 +2542,7 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir 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 == INVALID_TRACKDIR) { /* blaha */ @@ -2488,6 +2556,71 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir } break; } +#ifdef PF_BENCHMARK + TOC("PF time = ", 1) +#endif + + return best_track; +} + +/** + * 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 search should start from. + * @param td The trackdir the search should start from. + * @param override_tailtype Whether all physically compatible railtypes should be followed. + * @return True if a path to a safe stopping tile could be reserved. + */ +static bool TryReserveSafeTrack(const Vehicle* v, TileIndex tile, Trackdir td, bool override_tailtype) +{ + if (_settings_game.pf.pathfinder_for_trains == VPF_YAPF) { + return YapfRailFindNearestSafeTile(v, tile, td, override_tailtype); + } else { + return NPFRouteToSafeTile(v, tile, td, override_tailtype).res_okay; + } +} + +/* choose a track */ +static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck) +{ + Track best_track = INVALID_TRACK; + /* pathfinders are able to tell that route was only 'guessed' */ + bool path_not_found = false; + bool do_track_reservation = _settings_game.pf.reserve_paths || force_res; + bool changed_signal = false; + + assert((tracks & ~TRACK_BIT_MASK) == 0); + + if (got_reservation != NULL) *got_reservation = false; + + /* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */ + TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir)); + /* 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; + changed_signal = true; + SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir), SIGNAL_STATE_GREEN); + } else if (!do_track_reservation) { + return track; + } + best_track = track; + } + + if (do_track_reservation) { + if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL)) ProcessOrders(v); + } + + PBSTileInfo res_dest; + best_track = DoTrainPathfind(v, tile, enterdir, tracks, &path_not_found, do_track_reservation, &res_dest); + /* handle "path not found" state */ if (path_not_found) { /* PF didn't find the route */ @@ -2513,9 +2646,38 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir } } -#ifdef PF_BENCHMARK - TOC("PF time = ", 1) -#endif + /* No track reservation requested -> finished. */ + if (!do_track_reservation) return best_track; + + /* A path was found, but could not be reserved. */ + if (res_dest.tile != INVALID_TILE && !res_dest.okay) { + if (mark_stuck) MarkTrainAsStuck(v); + FreeTrainTrackReservation(v); + return best_track; + } + + /* No possible reservation target found, we are probably lost. */ + if (res_dest.tile == INVALID_TILE) { + /* Try to find any safe destination. */ + PBSTileInfo origin = FollowTrainReservation(v); + if (TryReserveSafeTrack(v, origin.tile, origin.trackdir, false)) { + TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir); + best_track = FindFirstTrack(res); + TryReserveRailTrack(v->tile, TrackdirToTrack(GetVehicleTrackdir(v))); + if (got_reservation != NULL) *got_reservation = true; + if (changed_signal) MarkTileDirtyByTile(tile); + } else { + FreeTrainTrackReservation(v); + if (mark_stuck) MarkTrainAsStuck(v); + } + return best_track; + } + + if (got_reservation != NULL) *got_reservation = true; + + TryReserveRailTrack(v->tile, TrackdirToTrack(GetVehicleTrackdir(v))); + + if (changed_signal) MarkTileDirtyByTile(tile); return best_track; } @@ -3040,8 +3202,8 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) if (prev == NULL) { /* Currently the locomotive is active. Determine which one of the * available tracks to choose */ - chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits)); - assert(chosen_track & bits); + chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, false, NULL, true)); + assert(chosen_track & (bits | GetReservedTrackbits(gp.new_tile))); /* Check if it's a red signal and that force proceed is not clicked. */ if (red_signals & chosen_track && v->u.rail.force_proceed == 0) { -- 1.5.6 From 7d06bd9dd8b46634c3b3a76e72db408c1a8f867a Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 24 May 2008 00:42:22 +0200 Subject: On reserving a path that ends at the destination, the path could end at a non-safe tile. In this case, extend the reservation based on the next vehicle orders. --- src/train_cmd.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 87 insertions(+), 0 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 8609f4f..5e3f710 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2582,6 +2582,50 @@ static bool TryReserveSafeTrack(const Vehicle* v, TileIndex tile, Trackdir td, b } } +/** + * Query the next order after a certain order index. + * + * @param v The vehicle + * @param order_index [in/out] Index of the current order, returns index of the chosen order + * @return Pointer to the order or NULL if the order chain was completely followed + * without finding a suitable order. + */ +static const Order* GetNextTrainOrder(const Vehicle *v, VehicleOrderID *order_index) +{ + ++(*order_index); + + do { + /* Wrap around. */ + if (*order_index >= v->num_orders) *order_index = 0; + + Order *order = GetVehicleOrder(v, *order_index); + assert(order != NULL); + + switch (order->GetType()) { + case OT_GOTO_DEPOT: + /* Skip service in depot orders when the train doesn't need service. */ + if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) break; + case OT_GOTO_STATION: + case OT_GOTO_WAYPOINT: + return order; + case OT_CONDITIONAL: { + VehicleOrderID next = ProcessConditionalOrder(order, v); + if (next != INVALID_VEH_ORDER_ID) { + *order_index = next; + continue; + } + break; + } + default: + break; + } + + ++(*order_index); + } while (*order_index != v->cur_order_index); + + return NULL; +} + /* choose a track */ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck) { @@ -2675,6 +2719,49 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir if (got_reservation != NULL) *got_reservation = true; + /* Reservation target found and free, check if it is safe. */ + Order cur_order = v->current_order; + TileIndex cur_dest_tile = v->dest_tile; + VehicleOrderID order_index = v->cur_order_index; + while (!IsSafeWaitingPosition(v, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) { + /* Extend reservation until we have found a safe position. */ + DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir); + TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir); + TrackBits reachable = TrackdirBitsToTrackBits((TrackdirBits)(GetTileTrackStatus(next_tile, TRANSPORT_RAIL, 0))) & DiagdirReachesTracks(exitdir); + if (_settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg) { + reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir)); + } + + /* Get next order with destination. */ + const Order *order = GetNextTrainOrder(v, &order_index); + if (order != NULL) { + v->current_order = *order; + UpdateOrderDest(v, order); + PBSTileInfo cur_dest; + DoTrainPathfind(v, next_tile, exitdir, reachable, NULL, true, &cur_dest); + if (cur_dest.tile != INVALID_TILE) { + res_dest = cur_dest; + if (res_dest.okay) continue; + /* Path found, but could not be reserved. */ + FreeTrainTrackReservation(v); + if (mark_stuck) MarkTrainAsStuck(v); + if (got_reservation != NULL) *got_reservation = false; + changed_signal = false; + break; + } + } + /* No order or no safe position found, try any position. */ + if (!TryReserveSafeTrack(v, res_dest.tile, res_dest.trackdir, true)) { + FreeTrainTrackReservation(v); + if (mark_stuck) MarkTrainAsStuck(v); + if (got_reservation != NULL) *got_reservation = false; + changed_signal = false; + } + break; + } + v->current_order = cur_order; + v->dest_tile = cur_dest_tile; + TryReserveRailTrack(v->tile, TrackdirToTrack(GetVehicleTrackdir(v))); if (changed_signal) MarkTileDirtyByTile(tile); -- 1.5.6 From 7bc23f2bf33a16db0c11d5a73d810898bc83fc36 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 23 Jun 2008 23:52:40 +0200 Subject: Try to extend the path of a stuck train so it is able to continue. --- src/lang/english.txt | 1 + src/train.h | 1 + src/train_cmd.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 0 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index efc8f9a..cd88f23 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -986,6 +986,7 @@ STR_CANT_SHARE_ORDER_LIST :{WHITE}Can't sh STR_CANT_COPY_ORDER_LIST :{WHITE}Can't copy order list... STR_END_OF_SHARED_ORDERS :{SETX 10}- - End of Shared Orders - - +STR_TRAIN_IS_STUCK :{WHITE}Train {COMMA} can't find a path to continue. STR_TRAIN_IS_LOST :{WHITE}Train {COMMA} is lost. STR_TRAIN_IS_UNPROFITABLE :{WHITE}Train {COMMA}'s profit last year was {CURRENCY} STR_EURO_INTRODUCE :{BLACK}{BIGFONT}European Monetary Union!{}{}The Euro is introduced as the sole currency for everyday transactions in your country! diff --git a/src/train.h b/src/train.h index da6ae35..7a26d76 100644 --- a/src/train.h +++ b/src/train.h @@ -274,6 +274,7 @@ int CheckTrainStoppedInDepot(const Vehicle *v); void UpdateTrainAcceleration(Vehicle* v); void FreeTrainTrackReservation(const Vehicle *v, TileIndex origin = INVALID_TILE, Trackdir orig_td = INVALID_TRACKDIR); +bool TryPathReserve(Vehicle *v, bool mark_as_stuck = false, bool first_tile_okay = false); /** * This class 'wraps' Vehicle; you do not actually instantiate this class. diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 5e3f710..8b3901e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2769,6 +2769,77 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir return best_track; } +/** + * Try to reserve a path to a safe position. + * + * @param v The vehicle + * @return True if a path could be reserved + */ +bool TryPathReserve(Vehicle *v, bool first_tile_okay, bool mark_as_stuck) +{ + assert(v->type == VEH_TRAIN && IsFrontEngine(v)); + + /* We have to handle depots specially as the track follower won't look + * at the depot tile itself but starts from the next tile. If we are still + * inside the depot, a depot reservation can never be ours. */ + if (v->u.rail.track & TRACK_BIT_DEPOT) { + if (GetDepotReservation(v->tile)) { + if (mark_as_stuck) MarkTrainAsStuck(v); + return false; + } else { + /* Depot not reserved, but the next tile might be. */ + TileIndex next_tile = TileAddByDiagDir(v->tile, GetRailDepotDirection(v->tile)); + if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(v->tile)))) return false; + } + } + + /* Special check if we are in front of a two-sided conventional signal. */ + DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track); + TileIndex next_tile = TileAddByDiagDir(v->tile, dir); + if (IsTileType(next_tile, MP_RAILWAY) && HasReservedTracks(next_tile, DiagdirReachesTracks(dir))) { + /* Can have only one reserved trackdir. */ + Trackdir td = FindFirstTrackdir((TrackdirBits)(GetReservedTrackbits(next_tile) * 0x101 & DiagdirReachesTrackdirs(dir))); + if (HasSignalOnTrackdir(next_tile, td) && HasSignalOnTrackdir(next_tile, ReverseTrackdir(td)) && + !IsPbsSignal(GetSignalType(next_tile, TrackdirToTrack(td)))) { + /* Signal already reserved, is not ours. */ + if (mark_as_stuck) MarkTrainAsStuck(v); + return false; + } + } + + PBSTileInfo origin = FollowTrainReservation(v); + /* If we have a reserved path and the path ends at a safe tile, we are finished already. */ + if (origin.okay && (v->tile != origin.tile || first_tile_okay)) { + /* Can't be stuck then. */ + ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK); + return true; + } + + /* If we are in a depot, tentativly reserve the depot. */ + if (v->u.rail.track & TRACK_BIT_DEPOT) { + SetDepotReservation(v->tile, true); + if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile); + } + + DiagDirection exitdir = TrackdirToExitdir(origin.trackdir); + TileIndex new_tile = TileAddByDiagDir(origin.tile, exitdir); + TrackBits reachable = TrackdirBitsToTrackBits((TrackdirBits)GetTileTrackStatus(new_tile, TRANSPORT_RAIL, 0) & DiagdirReachesTrackdirs(exitdir)); + + if (_settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir)); + + bool res_made = false; + ChooseTrainTrack(v, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck); + + if (!res_made) { + /* Free the depot reservation as well. */ + if (v->u.rail.track & TRACK_BIT_DEPOT) SetDepotReservation(v->tile, false); + return false; + } + + ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK); + return true; +} + static bool CheckReverseTrain(Vehicle *v) { @@ -3785,6 +3856,37 @@ static void TrainLocoHandler(Vehicle *v, bool mode) if (!mode) HandleLocomotiveSmokeCloud(v); + /* Handle stuck trains. */ + if (!mode && HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) { + ++v->load_unload_time_rem; + + /* Should we try reversing this tick if still stuck? */ + bool turn_around = v->load_unload_time_rem % (_settings_game.pf.wait_for_pbs_path * DAY_TICKS) == 0 && _settings_game.pf.wait_for_pbs_path < 255; + + if (!turn_around && v->u.rail.force_proceed == 0) return; + if (!TryPathReserve(v)) { + /* Still stuck. */ + if (turn_around) ReverseTrainDirection(v); + + if (HasBit(v->u.rail.flags, VRF_TRAIN_STUCK) && v->load_unload_time_rem > 2 * _settings_game.pf.wait_for_pbs_path * DAY_TICKS) { + /* Show message to player. */ + if (_settings_client.gui.lost_train_warn && v->owner == _local_player) { + SetDParam(0, v->unitnumber); + AddNewsItem( + STR_TRAIN_IS_STUCK, + NS_ADVICE, + v->index, + 0); + } + v->load_unload_time_rem = 0; + } + /* Exit if force proceed not pressed, else reset stuck flag anyway. */ + if (v->u.rail.force_proceed == 0) return; + ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK); + v->load_unload_time_rem = 0; + } + } + int j = UpdateTrainSpeed(v); /* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */ -- 1.5.6 From 7326dd02f797ac9609bc44195444b6c89df7a508 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 23 Jun 2008 22:28:40 +0200 Subject: Function for getting the train for a path. --- src/pbs.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++++++---------- src/pbs.h | 2 + 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/pbs.cpp b/src/pbs.cpp index 50258fa..4b0f838 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -14,6 +14,7 @@ #include "settings_type.h" #include "road_func.h" #include "vehicle_base.h" +#include "vehicle_func.h" #include "yapf/follow_track.hpp" /** @@ -159,23 +160,12 @@ bool TryReserveRailTrack(TileIndex tile, Track t) } } -/** - * Follow a train reservation to the last tile. - * - * @param v the vehicle - * @returns The last tile of the reservation or the current train tile if no reservation present. - */ -PBSTileInfo FollowTrainReservation(const Vehicle *v) -{ - assert(v->type == VEH_TRAIN); - - TileIndex tile = v->tile; - Trackdir trackdir = GetVehicleTrackdir(v); - - if (IsRailDepotTile(tile) && !GetRailDepotReservation(tile)) return PBSTileInfo(tile, trackdir, false); +/** Follow a reservation starting from a specific tile to the end. */ +static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Trackdir trackdir, bool ignore_oneway = false) +{ /* Do not disallow 90 deg turns as the setting might have changed between reserving and now. */ - CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes); + CFollowTrackRail ft(o, rts); while (ft.Follow(tile, trackdir)) { TrackdirBits reserved = (TrackdirBits)(ft.m_new_td_bits & (GetReservedTrackbits(ft.m_new_tile) * 0x101)); @@ -187,7 +177,7 @@ PBSTileInfo FollowTrainReservation(const Vehicle *v) /* One-way signal against us. The reservation can't be ours as it is not * a safe position from our direction and we can never pass the signal. */ - if (HasOnewaySignalBlockingTrackdir(ft.m_new_tile, new_trackdir)) break; + if (!ignore_oneway && HasOnewaySignalBlockingTrackdir(ft.m_new_tile, new_trackdir)) break; tile = ft.m_new_tile; trackdir = new_trackdir; @@ -198,7 +188,79 @@ PBSTileInfo FollowTrainReservation(const Vehicle *v) if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break; } - return PBSTileInfo(tile, trackdir, IsSafeWaitingPosition(v, tile, trackdir, true, _settings_game.pf.forbid_90_deg)); + return PBSTileInfo(tile, trackdir, false); +} + +/** + * Follow a train reservation to the last tile. + * + * @param v the vehicle + * @returns The last tile of the reservation or the current train tile if no reservation present. + */ +PBSTileInfo FollowTrainReservation(const Vehicle *v) +{ + assert(v->type == VEH_TRAIN); + + TileIndex tile = v->tile; + Trackdir trackdir = GetVehicleTrackdir(v); + + if (IsRailDepotTile(tile) && !GetRailDepotReservation(tile)) return PBSTileInfo(tile, trackdir, false); + + PBSTileInfo res = FollowReservation(v->owner, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes, tile, trackdir); + res.okay = IsSafeWaitingPosition(v, res.tile, res.trackdir, true, _settings_game.pf.forbid_90_deg); + return res; +} + +/** Callback for VehicleFromPos to find a train on a specific track. */ +static void *FindTrainOnTrackEnum(Vehicle *v, void *data) +{ + PBSTileInfo info = *(PBSTileInfo *)data; + + if (v->type == VEH_TRAIN && HasBit((TrackBits)v->u.rail.track, TrackdirToTrack(info.trackdir))) return v; + + return NULL; +} + +/** + * Find the train which has reserved a specific path. + * + * @param tile A tile on the path. + * @param track A reserved track on the tile. + * @return The vehicle holding the reservation or NULL if the path is stray. + */ +Vehicle *GetTrainForReservation(TileIndex tile, Track track) +{ + assert(HasReservedTracks(tile, TrackToTrackBits(track))); + Trackdir trackdir = TrackToTrackdir(track); + + RailTypes rts = GetRailTypeInfo(GetTileRailType(tile))->compatible_railtypes; + + /* Follow the path from tile to both ends, one of the end tiles should + * have a train on it. We need FollowReservation to ignore one-way signals + * here, as one of the two search directions will be the "wrong" way. */ + for (int i = 0; i < 2; ++i, trackdir = ReverseTrackdir(trackdir)) { + PBSTileInfo dest = FollowReservation(GetTileOwner(tile), rts, tile, trackdir, true); + + Vehicle *v = (Vehicle *)VehicleFromPos(dest.tile, &dest, FindTrainOnTrackEnum); + if (v != NULL) return v->First(); + + /* Special case for stations: check the whole platform for a vehicle. */ + if (IsRailwayStationTile(dest.tile)) { + TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(dest.trackdir))); + for (TileIndex st_tile = dest.tile + diff; IsCompatibleTrainStationTile(st_tile, dest.tile); st_tile += diff) { + v = (Vehicle *)VehicleFromPos(st_tile, &dest, FindTrainOnTrackEnum); + if (v != NULL) return v->First(); + } + } + + /* Special case for bridges/tunnels: check the other end as well. */ + if (IsTileType(dest.tile, MP_TUNNELBRIDGE)) { + v = (Vehicle *)VehicleFromPos(GetOtherTunnelBridgeEnd(dest.tile), &dest, FindTrainOnTrackEnum); + if (v != NULL) return v->First(); + } + } + + return NULL; } /** diff --git a/src/pbs.h b/src/pbs.h index d4b87b8..8a6a553 100644 --- a/src/pbs.h +++ b/src/pbs.h @@ -31,6 +31,8 @@ PBSTileInfo FollowTrainReservation(const Vehicle *v); bool IsSafeWaitingPosition(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg = false); bool IsWaitingPositionFree(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg = false); +Vehicle *GetTrainForReservation(TileIndex tile, Track track); + /** * Check whether some of tracks is reserved on a tile. * -- 1.5.6 From e3c23da084d931b7cb511b2fe59f06ed0bf7ac62 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 23 Jun 2008 22:30:09 +0200 Subject: Free the old path reservation on removing some tracks and reroute trains afterwards. --- src/rail_cmd.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++- src/station_cmd.cpp | 28 ++++++++++++++++++++++++++ src/tunnelbridge_cmd.cpp | 18 ++++++++++++++++- src/waypoint.cpp | 8 +++++++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 5d66f7a..e77698b 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -46,6 +46,8 @@ #include "functions.h" #include "elrail_func.h" #include "oldpool_func.h" +#include "pbs.h" +#include "core/smallvec_type.hpp" #include "table/sprites.h" #include "table/strings.h" @@ -458,6 +460,8 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 * so do not call GetTileOwner(tile) in any case here */ Owner owner = INVALID_OWNER; + Vehicle *v = NULL; + switch (GetTileType(tile)) { case MP_ROAD: { if (!IsLevelCrossing(tile) || @@ -468,6 +472,10 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 } if (flags & DC_EXEC) { + if (HasReservedTracks(tile, trackbit)) { + v = GetTrainForReservation(tile, track); + if (v != NULL) FreeTrainTrackReservation(v); + } owner = GetTileOwner(tile); MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypes(tile), GetTownIndex(tile), GetRoadOwner(tile, ROADTYPE_ROAD), GetRoadOwner(tile, ROADTYPE_TRAM), GetRoadOwner(tile, ROADTYPE_HWAY)); } @@ -492,6 +500,10 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 cost.AddCost(DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS)); if (flags & DC_EXEC) { + if (HasReservedTracks(tile, trackbit)) { + v = GetTrainForReservation(tile, track); + if (v != NULL) FreeTrainTrackReservation(v); + } owner = GetTileOwner(tile); present ^= trackbit; if (present == 0) { @@ -530,6 +542,8 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 AddTrackToSignalBuffer(tile, track, owner); YapfNotifyTrackLayoutChange(tile, track); } + + if (v != NULL) TryPathReserve(v, true); } return cost; @@ -1258,11 +1272,24 @@ CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) /* Trying to convert other's rail */ if (!CheckTileOwnership(tile)) continue; + SmallVector vehicles_affected; + /* Vehicle on the tile when not converting Rail <-> ElRail * Tunnels and bridges have special check later */ if (tt != MP_TUNNELBRIDGE) { if (!IsCompatibleRail(type, totype) && !EnsureNoVehicleOnGround(tile)) continue; if (flags & DC_EXEC) { // we can safely convert, too + TrackBits reserved = GetReservedTrackbits(tile); + Track track; + while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) { + Vehicle *v = GetTrainForReservation(tile, track); + if (v != NULL && !HasPowerOnRail(v->u.rail.railtype, totype)) { + /* No power on new rail type, reroute. */ + FreeTrainTrackReservation(v); + *vehicles_affected.Append() = v; + } + } + SetRailType(tile, totype); MarkTileDirtyByTile(tile); /* update power of train engines on this tile */ @@ -1319,14 +1346,20 @@ CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) GetVehicleTunnelBridge(tile, endtile) != NULL) continue; if (flags & DC_EXEC) { + Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile)); + if (GetTunnelBridgeReservation(tile)) { + Vehicle *v = GetTrainForReservation(tile, track); + if (v != NULL) { + FreeTrainTrackReservation(v); + *vehicles_affected.Append() = v; + } + } SetRailType(tile, totype); SetRailType(endtile, totype); VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); VehicleFromPos(endtile, NULL, &UpdateTrainPowerProc); - Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile)); - YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); @@ -1352,6 +1385,10 @@ CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) cost.AddCost(RailConvertCost(type, totype)); break; } + + for (uint i = 0; i < vehicles_affected.Length(); ++i) { + TryPathReserve(vehicles_affected[i], true); + } } } @@ -1370,11 +1407,18 @@ static CommandCost RemoveTrainDepot(TileIndex tile, uint32 flags) /* read variables before the depot is removed */ DiagDirection dir = GetRailDepotDirection(tile); Owner owner = GetTileOwner(tile); + Vehicle *v = NULL; + + if (GetDepotReservation(tile)) { + v = GetTrainForReservation(tile, DiagDirToDiagTrack(dir)); + if (v != NULL) FreeTrainTrackReservation(v); + } DoClearSquare(tile); delete GetDepotByTile(tile); AddSideToSignalBuffer(tile, dir, owner); YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); + if (v != NULL) TryPathReserve(v, true); } return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_train_depot); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 441ffb1..5b6dfa4 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -43,6 +43,7 @@ #include "oldpool_func.h" #include "animated_tile_func.h" #include "elrail_func.h" +#include "core/smallvec_type.hpp" #include "table/sprites.h" #include "table/strings.h" @@ -1018,11 +1019,20 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, numtracks_orig = numtracks; + SmallVector affected_vehicles; do { TileIndex tile = tile_org; int w = plat_len; do { byte layout = *layout_ptr++; + if (IsRailwayStationTile(tile) && GetRailwayStationReservation(tile)) { + Vehicle *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile))); + if (v != NULL) { + FreeTrainTrackReservation(v); + *affected_vehicles.Append() = v; + } + } + MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, (RailType)GB(p2, 0, 4)); SetCustomStationSpecIndex(tile, specindex); SetStationTileRandomBits(tile, GB(Random(), 0, 4)); @@ -1047,6 +1057,10 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, tile_org += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta } while (--numtracks); + for (uint i = 0; i < affected_vehicles.Length(); ++i) { + TryPathReserve(affected_vehicles[i], true); + } + st->MarkTilesDirty(false); UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); @@ -1172,6 +1186,12 @@ CommandCost CmdRemoveFromRailroadStation(TileIndex tile, uint32 flags, uint32 p1 uint specindex = GetCustomStationSpecIndex(tile2); Track track = GetRailStationTrack(tile2); Owner owner = GetTileOwner(tile2); + Vehicle *v = NULL; + + if (GetRailwayStationReservation(tile2)) { + v = GetTrainForReservation(tile2, track); + if (v != NULL) FreeTrainTrackReservation(v); + } DoClearSquare(tile2); st->rect.AfterRemoveTile(st, tile2); @@ -1187,6 +1207,8 @@ CommandCost CmdRemoveFromRailroadStation(TileIndex tile, uint32 flags, uint32 p1 st->MarkTilesDirty(false); UpdateStationSignCoord(st); + if (v != NULL) TryPathReserve(v, true); + /* if we deleted the whole station, delete the train facility. */ if (st->train_tile == 0) { st->facilities &= ~FACIL_TRAIN; @@ -1235,9 +1257,15 @@ static CommandCost RemoveRailroadStation(Station *st, TileIndex tile, uint32 fla /* read variables before the station tile is removed */ Track track = GetRailStationTrack(tile); Owner owner = GetTileOwner(tile); // _current_player can be OWNER_WATER + Vehicle *v = NULL; + if (GetRailwayStationReservation(tile)) { + v = GetTrainForReservation(tile, track); + if (v != NULL) FreeTrainTrackReservation(v); + } DoClearSquare(tile); AddTrackToSignalBuffer(tile, track, owner); YapfNotifyTrackLayoutChange(tile, track); + if (v != NULL) TryPathReserve(v, true); } } tile += TileDiffXY(1, 0); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 612c5fb..d1a0427 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -631,8 +631,15 @@ static CommandCost DoClearTunnel(TileIndex tile, uint32 flags) if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) { /* We first need to request values before calling DoClearSquare */ DiagDirection dir = GetTunnelBridgeDirection(tile); + Track track = DiagDirToDiagTrack(dir); Owner owner = GetTileOwner(tile); + Vehicle *v = NULL; + if (GetTunnelBridgeReservation(tile)) { + v = GetTrainForReservation(tile, track); + if (v != NULL) FreeTrainTrackReservation(v); + } + DoClearSquare(tile); DoClearSquare(endtile); @@ -640,9 +647,10 @@ static CommandCost DoClearTunnel(TileIndex tile, uint32 flags) AddSideToSignalBuffer(tile, ReverseDiagDir(dir), owner); AddSideToSignalBuffer(endtile, dir, owner); - Track track = DiagDirToDiagTrack(dir); YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); + + if (v != NULL) TryPathReserve(v); } else { DoClearSquare(tile); DoClearSquare(endtile); @@ -690,6 +698,12 @@ static CommandCost DoClearBridge(TileIndex tile, uint32 flags) bool rail = GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL; Owner owner = GetTileOwner(tile); uint height = GetBridgeHeight(tile); + Vehicle *v = NULL; + + if (rail && GetTunnelBridgeReservation(tile)) { + v = GetTrainForReservation(tile, DiagDirToDiagTrack(direction)); + if (v != NULL) FreeTrainTrackReservation(v); + } DoClearSquare(tile); DoClearSquare(endtile); @@ -711,6 +725,8 @@ static CommandCost DoClearBridge(TileIndex tile, uint32 flags) Track track = DiagDirToDiagTrack(direction); YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); + + if (v != NULL) TryPathReserve(v, true); } } diff --git a/src/waypoint.cpp b/src/waypoint.cpp index 397c4ae..5aefc91 100644 --- a/src/waypoint.cpp +++ b/src/waypoint.cpp @@ -33,6 +33,8 @@ #include "newgrf_station.h" #include "oldpool_func.h" #include "viewport_func.h" +#include "pbs.h" +#include "train.h" #include "table/strings.h" @@ -320,6 +322,7 @@ CommandCost RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove) wp->deleted = 30; // let it live for this many days before we do the actual deletion. RedrawWaypointSign(wp); + Vehicle *v = NULL; if (justremove) { TrackBits tracks = GetRailWaypointBits(tile); bool reserved = GetWaypointReservation(tile); @@ -327,10 +330,15 @@ CommandCost RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove) if (reserved) SetTrackReservation(tile, tracks); MarkTileDirtyByTile(tile); } else { + if (GetWaypointReservation(tile)) { + v = GetTrainForReservation(tile, track); + if (v != NULL) FreeTrainTrackReservation(v); + } DoClearSquare(tile); AddTrackToSignalBuffer(tile, track, owner); } YapfNotifyTrackLayoutChange(tile, track); + if (v != NULL) TryPathReserve(v, true); } return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_train_depot); -- 1.5.6 From f0e9cac6ff84f02d227b5cf6a59201056a3fa04f Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 2 Jun 2008 16:50:47 +0200 Subject: Implement look-ahead for trains so they extend their reservation before reaching the end. --- src/train_cmd.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 8b3901e..c29bbc9 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2263,6 +2263,44 @@ void Train::PlayLeaveStationSound() const SndPlayVehicleFx(sfx[RailVehInfo(engtype)->engclass], this); } +/** Check if the train is on the last reserved tile and try to extend the path then. */ +static void CheckNextTrainTile(Vehicle *v) +{ + /* Don't do any look-ahead if path_backoff_interval is 255. */ + if (_settings_game.pf.path_backoff_interval == 255) return; + + /* Exit if we reached our destination or are inside a depot. */ + if ((v->tile == v->dest_tile && !v->current_order.IsType(OT_GOTO_STATION)) || v->u.rail.track & TRACK_BIT_DEPOT) return; + /* Exit if we are on a station tile and are going to stop. */ + if (IsRailwayStationTile(v->tile) && v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile))) return; + /* Exit if the current order doesn't have a destination, but the train has orders. */ + if ((v->current_order.IsType(OT_NOTHING) || v->current_order.IsType(OT_LEAVESTATION)) && v->num_orders > 0) return; + + Trackdir td = GetVehicleTrackdir(v); + + /* On a tile with a red non-pbs signal, don't look ahead. */ + if (IsTileType(v->tile, MP_RAILWAY) && HasSignalOnTrackdir(v->tile, td) && + !IsPbsSignal(GetSignalType(v->tile, TrackdirToTrack(td))) && + GetSignalStateByTrackdir(v->tile, td) == SIGNAL_STATE_RED) return; + + CFollowTrackRail ft(v); + if (!ft.Follow(v->tile, td)) return; + + if (!HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits))) { + /* Next tile is not reserved. */ + if (KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) { + if (HasPbsSignalOnTrackdir(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) { + /* If the next tile is a PBS signal, try to make a reservation. */ + TrackBits tracks = TrackdirBitsToTrackBits(ft.m_new_td_bits); + if (_settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg) { + tracks &= ~TrackCrossesTracks(TrackdirToTrack(ft.m_old_td)); + } + ChooseTrainTrack(v, ft.m_new_tile, ft.m_exitdir, tracks, false, NULL, false); + } + } + } +} + static bool CheckTrainStayInDepot(Vehicle *v) { /* bail out if not all wagons are in the same depot or not in a depot at all */ @@ -3420,8 +3458,6 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) goto invalid_rail; } - 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); @@ -3450,6 +3486,13 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir); v->direction = chosen_dir; + + if (IsFrontEngine(v)) { + v->load_unload_time_rem = 0; + + /* Always try to extend the reservation when entering a tile. */ + CheckNextTrainTile(v); + } } } else { /* In a tunnel or on a bridge @@ -3460,7 +3503,10 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) min(v->cur_speed, GetBridgeSpec(GetBridgeType(v->tile))->speed); } - if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { + if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { + /* Perform look-ahead on tunnel exit. */ + CheckNextTrainTile(v); + } else { v->x_pos = gp.x; v->y_pos = gp.y; VehiclePositionChanged(v); @@ -3494,6 +3540,9 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) if (IsLevelCrossingTile(gp.old_tile)) UpdateLevelCrossing(gp.old_tile); } } + + /* Do not check on every tick to save some computing time. */ + if (IsFrontEngine(v) && v->tick_counter % _settings_game.pf.path_backoff_interval == 0) CheckNextTrainTile(v); } return; @@ -3840,6 +3889,7 @@ static void TrainLocoHandler(Vehicle *v, bool mode) /* exit if train is stopped */ if (v->vehstatus & VS_STOPPED && v->cur_speed == 0) return; + bool valid_order = v->current_order.IsValid() && v->current_order.GetType() != OT_CONDITIONAL; if (ProcessOrders(v) && CheckReverseTrain(v)) { v->load_unload_time_rem = 0; v->cur_speed = 0; @@ -3856,6 +3906,11 @@ static void TrainLocoHandler(Vehicle *v, bool mode) if (!mode) HandleLocomotiveSmokeCloud(v); + /* We had no order but have an order now, do look ahead. */ + if (!valid_order && v->current_order.IsValid()) { + CheckNextTrainTile(v); + } + /* Handle stuck trains. */ if (!mode && HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) { ++v->load_unload_time_rem; @@ -3863,7 +3918,7 @@ static void TrainLocoHandler(Vehicle *v, bool mode) /* Should we try reversing this tick if still stuck? */ bool turn_around = v->load_unload_time_rem % (_settings_game.pf.wait_for_pbs_path * DAY_TICKS) == 0 && _settings_game.pf.wait_for_pbs_path < 255; - if (!turn_around && v->u.rail.force_proceed == 0) return; + if (!turn_around && v->load_unload_time_rem % _settings_game.pf.path_backoff_interval != 0 && v->u.rail.force_proceed == 0) return; if (!TryPathReserve(v)) { /* Still stuck. */ if (turn_around) ReverseTrainDirection(v); -- 1.5.6 From 9cca820c4ff1a66697de2f2e6e5965f682192a55 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 25 Mar 2008 01:12:35 +0100 Subject: Try to extend a train reservation as far as possible and only call into the pathfinder on track choices. --- src/train_cmd.cpp | 169 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 143 insertions(+), 26 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index c29bbc9..38ac245 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2602,6 +2602,109 @@ static Track DoTrainPathfind(Vehicle* v, TileIndex tile, DiagDirection enterdir, } /** + * Extend a train path as far as possible. Stops on encountering a safe tile, + * another reservation or a track choice. + * @return INVALID_TILE indicates that the reservation failed. + */ +static PBSTileInfo ExtendTrainReservation(const Vehicle *v, TrackBits *new_tracks, DiagDirection *enterdir) +{ + bool no_90deg_turns = _settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg; + PBSTileInfo origin = FollowTrainReservation(v); + + CFollowTrackRail ft(v); + + TileIndex tile = origin.tile; + Trackdir cur_td = origin.trackdir; + while (ft.Follow(tile, cur_td)) { + if (KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) { + /* Possible signal tile. */ + if (HasOnewaySignalBlockingTrackdir(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) break; + } + + if (no_90deg_turns) { + ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(ft.m_old_td); + if (ft.m_new_td_bits == TRACKDIR_BIT_NONE) break; + } + + /* Station, depot or waypoint are a possible target. */ + bool target_seen = ft.m_is_station || (IsTileType(ft.m_new_tile, MP_RAILWAY) && !IsPlainRailTile(ft.m_new_tile)); + if (target_seen || KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) { + /* Choice found or possible target encountered. + * On finding a possible target, we need to stop and let the pathfinder handle the + * remaining path. This is because we don't know if this target is in one of our + * orders, so we might cause pathfinding to fail later on if we find a choice. + * This failure would cause a bogous call to TryReserveSafePath which might reserve + * a wrong path not leading to our next destination. */ + if (HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(TrackdirReachesTrackdirs(ft.m_old_td)))) break; + + /* Choice found, path valid but not okay. Save info about the choice tile as well. */ + if (new_tracks) *new_tracks = TrackdirBitsToTrackBits(ft.m_new_td_bits); + if (enterdir) *enterdir = ft.m_exitdir; + return PBSTileInfo(ft.m_new_tile, ft.m_old_td, false); + } + + tile = ft.m_new_tile; + cur_td = FindFirstTrackdir(ft.m_new_td_bits); + + /* CFollowTrackRail skips station tiles, reserve those skipped tiles as well. */ + if (ft.m_is_station && ft.m_tiles_skipped > 0) { + TileIndexDiff diff = TileOffsByDiagDir(ft.m_exitdir); + TileIndex st = TILE_ADD(ft.m_new_tile, -ft.m_tiles_skipped * diff); + for (int i = 0; i < ft.m_tiles_skipped; ++i, st += diff) { + if (GetRailwayStationReservation(st)) { + ft.m_new_tile = st; + break; + } + SetRailwayStationReservation(st, true); + } + } + + if (IsSafeWaitingPosition(v, tile, cur_td, true, no_90deg_turns)) { + bool wp_free = IsWaitingPositionFree(v, tile, cur_td, no_90deg_turns); + if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break; + /* Safe position is all good, path valid and okay. */ + return PBSTileInfo(tile, cur_td, true); + } + + if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break; + } + + if (ft.m_err == CFollowTrackRail::EC_OWNER && ft.m_err == CFollowTrackRail::EC_NO_WAY) { + /* End of line, path valid and okay. */ + return PBSTileInfo(ft.m_old_tile, ft.m_old_td, true); + } + + /* Sorry, can't reserve path, back out. */ + tile = ft.m_new_tile; + ft.m_new_tile = origin.tile; + ft.m_new_td_bits = TrackdirToTrackdirBits(origin.trackdir); + while (ft.Follow(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) { + /* Check for skipped station tiles. */ + if (ft.m_is_station && ft.m_tiles_skipped > 0) { + TileIndexDiff diff = TileOffsByDiagDir(ft.m_exitdir); + TileIndex st = TILE_ADD(ft.m_new_tile, -ft.m_tiles_skipped * diff); + for (int i = 0; i < ft.m_tiles_skipped; ++i, st += diff) { + if (st == tile) break; + SetRailwayStationReservation(st, false); + } + } + + /* Break on reaching the previous failure. */ + if (ft.m_new_tile == tile) break; + + if (no_90deg_turns) { + ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(ft.m_old_td); + assert(ft.m_new_td_bits != TRACKDIR_BIT_NONE); + } + + assert(KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE); + UnreserveRailTrack(ft.m_new_tile, TrackdirToTrack(FindFirstTrackdir(ft.m_new_td_bits))); + } + /* Path invalid. */ + return PBSTileInfo(); +} + +/** * 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. * @@ -2668,8 +2771,6 @@ static const Order* GetNextTrainOrder(const Vehicle *v, VehicleOrderID *order_in static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck) { Track best_track = INVALID_TRACK; - /* pathfinders are able to tell that route was only 'guessed' */ - bool path_not_found = false; bool do_track_reservation = _settings_game.pf.reserve_paths || force_res; bool changed_signal = false; @@ -2696,35 +2797,51 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir best_track = track; } + PBSTileInfo res_dest(tile, INVALID_TRACKDIR, false); + DiagDirection dest_enterdir = enterdir; if (do_track_reservation) { if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL)) ProcessOrders(v); - } - PBSTileInfo res_dest; - best_track = DoTrainPathfind(v, tile, enterdir, tracks, &path_not_found, do_track_reservation, &res_dest); + res_dest = ExtendTrainReservation(v, &tracks, &dest_enterdir); + if (res_dest.tile == INVALID_TILE) { + /* Reservation failed? */ + if (mark_stuck) MarkTrainAsStuck(v); + if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED); + return FindFirstTrack(tracks); + } + } - /* handle "path not found" state */ - if (path_not_found) { - /* PF didn't find the route */ - if (!HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) { - /* it is first time the problem occurred, set the "path not found" flag */ - SetBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION); - /* and notify user about the event */ - if (_settings_client.gui.lost_train_warn && v->owner == _local_player) { - SetDParam(0, v->unitnumber); - AddNewsItem( - STR_TRAIN_IS_LOST, - NS_ADVICE, - v->index, - 0); + if (res_dest.tile != INVALID_TILE && !res_dest.okay) { + /* Pathfinders are able to tell that route was only 'guessed'. */ + bool path_not_found = false; + TileIndex new_tile = res_dest.tile; + + Track next_track = DoTrainPathfind(v, new_tile, dest_enterdir, tracks, &path_not_found, do_track_reservation, &res_dest); + if (new_tile == tile) best_track = next_track; + + /* handle "path not found" state */ + if (path_not_found) { + /* PF didn't find the route */ + if (!HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) { + /* it is first time the problem occurred, set the "path not found" flag */ + SetBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION); + /* and notify user about the event */ + if (_settings_client.gui.lost_train_warn && v->owner == _local_player) { + SetDParam(0, v->unitnumber); + AddNewsItem( + STR_TRAIN_IS_LOST, + NS_ADVICE, + v->index, + 0); + } + } + } else { + /* route found, is the train marked with "path not found" flag? */ + if (HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) { + /* clear the flag as the PF's problem was solved */ + ClrBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION); + /* can we also delete the "News" item somehow? */ } - } - } else { - /* route found, is the train marked with "path not found" flag? */ - if (HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) { - /* clear the flag as the PF's problem was solved */ - ClrBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION); - /* can we also delete the "News" item somehow? */ } } -- 1.5.6 From 3552e97d82e5001e19274516de1b10dc686dc5d4 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 03:31:46 +0100 Subject: Implement proper handling of crashed trains. --- src/map_func.h | 17 +++++++++++++++++ src/train_cmd.cpp | 33 +++++++++++++++++++++++++++++++++ src/water_cmd.cpp | 5 ++++- 3 files changed, 54 insertions(+), 1 deletions(-) diff --git a/src/map_func.h b/src/map_func.h index ff539b8..96d722f 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -268,6 +268,23 @@ static inline TileIndexDiffC TileIndexDiffCByDir(Direction dir) 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. * diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 38ac245..b8b35a6 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3317,6 +3317,14 @@ static void SetVehicleCrashed(Vehicle *v) { if (v->u.rail.crash_anim_pos != 0) return; + /* Free a possible path reservation and try to mark all tiles occupied by the train reserved. */ + if (IsFrontEngine(v)) { + FreeTrainTrackReservation(v); + for (const Vehicle *u = v; u != NULL; u = u->Next()) { + TryReserveRailTrack(u->tile, TrackdirToTrack(GetVehicleTrackdir(u))); + } + } + /* we may need to update crossing we were approaching */ TileIndex crossing = TrainApproachingCrossingTile(v); @@ -3710,6 +3718,31 @@ static void DeleteLastWagon(Vehicle *v) 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)) { + SetDepotReservation(v->tile, false); + } else if (HasReservedTracks(v->tile, v->u.rail.track)) { + Trackdir td = TrackToTrackdir(TrackBitsToTrack(v->u.rail.track)); + ClearPathReservation(v->tile, td); + if (HasPbsSignalOnTrackdir(v->tile, td)) SetSignalStateByTrackdir(v->tile, td, SIGNAL_STATE_RED); + if (HasPbsSignalOnTrackdir(v->tile, ReverseTrackdir(td))) SetSignalStateByTrackdir(v->tile, ReverseTrackdir(td), SIGNAL_STATE_RED); + } + } else if ((v->u.rail.track & ~TRACK_BIT_MASK) == TRACK_BIT_NONE && u->tile != v->tile && HasReservedTracks(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; diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index b72b0a9..54c116d 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -841,7 +841,10 @@ static void FloodVehicle(Vehicle *v) switch (v->type) { default: NOT_REACHED(); case VEH_TRAIN: - if (IsFrontEngine(v)) pass += 4; // driver + if (IsFrontEngine(v)) { + pass += 4; // driver + FreeTrainTrackReservation(v); + } v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast InvalidateWindowClassesData(WC_TRAINS_LIST, 0); break; -- 1.5.6 From c21573b1071ab207ea731c1c02bd9809d3ed9ee2 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 16:26:08 +0100 Subject: Reserve a path when exiting a depot into a PBS block. --- src/signal.cpp | 6 ++++-- src/signal_func.h | 1 + src/train_cmd.cpp | 20 +++++++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/signal.cpp b/src/signal.cpp index b7b9f3b..096b223 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -541,8 +541,10 @@ static SigSegState UpdateSignalsInBuffer(Owner owner) if (first) { first = false; - if ((flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL)) { - /* SIGSEG_FREE is set by default */ + /* SIGSEG_FREE is set by default */ + if (flags & SF_PBS) { + state = SIGSEG_PBS; + } else if (flags & SF_TRAIN || (flags & SF_EXIT && !(flags & SF_GREEN)) || flags & SF_FULL) { state = SIGSEG_FULL; } } diff --git a/src/signal_func.h b/src/signal_func.h index 7dcf3fd..7e35168 100644 --- a/src/signal_func.h +++ b/src/signal_func.h @@ -45,6 +45,7 @@ static inline byte SignalOnTrack(Track track) 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); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index b8b35a6..3d74ef6 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2315,7 +2315,10 @@ static bool CheckTrainStayInDepot(Vehicle *v) return true; } + SigSegState seg_state; + 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; @@ -2323,12 +2326,27 @@ static bool CheckTrainStayInDepot(Vehicle *v) v->load_unload_time_rem = 0; - if (UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner) == SIGSEG_FULL) { + seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner); + if (seg_state == SIGSEG_FULL || GetDepotReservation(v->tile)) { + /* Full and no PBS signal in block or depot reserved, can't exit. */ InvalidateWindowClasses(WC_TRAINS_LIST); return true; } + } else { + seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner); + } + + /* Only leave when we can reserve a path to our destination. */ + if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->u.rail.force_proceed == 0) { + /* No path and no force proceed. */ + InvalidateWindowClasses(WC_TRAINS_LIST); + MarkTrainAsStuck(v); + return true; } + SetDepotReservation(v->tile, true); + if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile); + VehicleServiceInDepot(v); InvalidateWindowClasses(WC_TRAINS_LIST); v->PlayLeaveStationSound(); -- 1.5.6 From 8fc3fbc81a2776db162868dd6551826a39c2a95f Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 03:37:56 +0100 Subject: Handle train reversing. --- src/train_cmd.cpp | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 3d74ef6..631f9f3 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -61,6 +61,7 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir static bool TrainCheckIfLineEnds(Vehicle *v); static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image); static TileIndex TrainApproachingCrossingTile(const Vehicle *v); +static void CheckNextTrainTile(Vehicle *v); static const byte _vehicle_initial_x_fract[4] = {10, 8, 4, 8}; static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10}; @@ -1828,6 +1829,9 @@ static void ReverseTrainDirection(Vehicle *v) InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); } + /* Clear path reservation in front. */ + FreeTrainTrackReservation(v); + /* Check if we were approaching a rail/road-crossing */ TileIndex crossing = TrainApproachingCrossingTile(v); @@ -1864,6 +1868,27 @@ static void ReverseTrainDirection(Vehicle *v) /* maybe we are approaching crossing now, after reversal */ crossing = TrainApproachingCrossingTile(v); if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing); + + /* If we are inside a depot after reversing, don't bother with path reserving. */ + if (v->u.rail.track & TRACK_BIT_DEPOT) return; + + /* TrainExitDir does not always produce the desired dir for depots and + * tunnels/bridges that is needed for UpdateSignalsOnSegment. */ + DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track); + if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR; + + if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) { + /* If we are currently on a tile with conventional signals, we can't treat the + * current tile as a safe tile or we would enter a PBS block without a reservation. */ + bool first_tile_okay = !(IsTileType(v->tile, MP_RAILWAY) + && HasSignalOnTrackdir(v->tile, GetVehicleTrackdir(v)) + && !IsPbsSignal(GetSignalType(v->tile, FindFirstTrack(v->u.rail.track)))); + + if (TryPathReserve(v, true, first_tile_okay)) { + /* Do a look-ahead now in case our current tile was already a safe tile. */ + CheckNextTrainTile(v); + } + } } /** Reverse train. -- 1.5.6 From 97781088ca4b60f0cf0333df8473375d056fa178 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 24 Feb 2008 00:44:21 +0100 Subject: Do not reverse in front of red signals when inside a PBS block and reversing of stuck trains is disabled. --- src/train_cmd.cpp | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 631f9f3..42417ff 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3594,6 +3594,13 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) if (VehicleFromPos(o_tile, &rdir, &CheckVehicleAtSignal) == NULL) return; } } + + /* If we would reverse but are currently in a PBS block and reversing of + * stuck trains is disabled, don't reverse and mark stuck instead. */ + if (_settings_game.pf.wait_for_pbs_path == 255 && UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) { + MarkTrainAsStuck(v); + return; + } goto reverse_train_direction; } } else { -- 1.5.6 From 210aeec8233c028172542282b50f1d5d9dd09fa3 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 01:17:01 +0100 Subject: Reserve a track when entering a PBS block through a conventional signal. --- src/train_cmd.cpp | 24 +++++++++++++++++++++--- 1 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 42417ff..3039f57 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3345,14 +3345,18 @@ static inline void AffectSpeedByZChange(Vehicle *v, byte old_z) } } -static void TrainMovedChangeSignals(TileIndex tile, DiagDirection dir) +static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir) { if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_SIGNALS) { TrackdirBits tracks = TrackBitsToTrackdirBits(GetTrackBits(tile)) & DiagdirReachesTrackdirs(dir); Trackdir trackdir = FindFirstTrackdir(tracks); - UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)); + if (UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SIGSEG_PBS && HasSignalOnTrackdir(tile, trackdir)) { + /* A PBS block with a non-PBS signal facing us? */ + if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true; + } } + return false; } @@ -3706,7 +3710,21 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) } if (update_signals_crossing) { - if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir); + if (IsFrontEngine(v)) { + if (TrainMovedChangeSignals(gp.new_tile, enterdir)) { + /* We are entering a block with PBS signals right now, but + * not through a PBS signal. This means we don't have a + * reservation right now. As a conventional signal will only + * ever be green if no other train is in the block, getting + * a path should always be possible. If the player built + * such a strange network that it is not possible, the train + * will be marked as stuck and the player has to deal with + * the problem. */ + if (!(HasReservedTracks(gp.new_tile, v->u.rail.track) || + TryReserveRailTrack(gp.new_tile, FindFirstTrack(v->u.rail.track))) || + !TryPathReserve(v)) MarkTrainAsStuck(v); + } + } /* Signals can only change when the first * (above) or the last vehicle moves. */ -- 1.5.6 From cff336b807f197c0fc0502c391346199ed888009 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 24 Feb 2008 14:15:51 +0100 Subject: Check if a train needs servicing when reserving the next path segment. --- src/train_cmd.cpp | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 3039f57..c51c079 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -61,6 +61,7 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir static bool TrainCheckIfLineEnds(Vehicle *v); static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image); static TileIndex TrainApproachingCrossingTile(const Vehicle *v); +static void CheckIfTrainNeedsService(Vehicle *v); static void CheckNextTrainTile(Vehicle *v); static const byte _vehicle_initial_x_fract[4] = {10, 8, 4, 8}; @@ -2843,7 +2844,11 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir PBSTileInfo res_dest(tile, INVALID_TRACKDIR, false); DiagDirection dest_enterdir = enterdir; if (do_track_reservation) { - if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL)) ProcessOrders(v); + /* Check if the train needs service here, so it has a chance to always find a depot. + * Also check if the current order is a service order so we don't reserve a path to + * the destination but instead to the next one if service isn't needed. */ + CheckIfTrainNeedsService(v); + if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL) || v->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(v); res_dest = ExtendTrainReservation(v, &tracks, &dest_enterdir); if (res_dest.tile == INVALID_TILE) { -- 1.5.6 From df50e57abe013c9a33b37112300d01c4d4ef4996 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 16 Feb 2008 03:15:25 +0100 Subject: Implement newgrf var 0x44 for stations (PBS reservation state). --- src/newgrf_station.cpp | 8 +++++++- src/pbs.cpp | 14 +++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 4f57699..5414e11 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -25,6 +25,7 @@ #include "player_func.h" #include "animated_tile_func.h" #include "functions.h" +#include "rail_map.h" #include "table/sprites.h" #include "table/strings.h" @@ -397,7 +398,12 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by case 0x42: return GetTerrainType(tile) | (GetRailType(tile) << 8); case 0x43: return st->owner; // Station owner - case 0x44: return 2; // PBS status + case 0x44: + if (IsTileType(tile, MP_RAILWAY) && IsRailWaypoint(tile)) { + return GetWaypointReservation(tile) ? 7 : 4; + } else { + return GetRailwayStationReservation(tile) ? 7 : 4; // PBS status + } case 0x45: if (!HasBit(_svc.valid, 2)) { _svc.v45 = GetRailContinuationInfo(tile); SetBit(_svc.valid, 2); } return _svc.v45; diff --git a/src/pbs.cpp b/src/pbs.cpp index 4b0f838..6d385b2 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -66,7 +66,7 @@ void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bo do { SetRailwayStationReservation(tile, b); - if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile); tile = TILE_ADD(tile, diff); } while (IsCompatibleTrainStationTile(tile, start)); } @@ -92,6 +92,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t) if (IsRailWaypoint(tile) || IsRailDepot(tile)) { if (!GetWaypointReservation(tile)) { SetWaypointReservation(tile, true); + MarkTileDirtyByTile(tile); return true; } } @@ -107,6 +108,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t) case MP_STATION: if (IsRailwayStation(tile) && !GetRailwayStationReservation(tile)) { SetRailwayStationReservation(tile, true); + MarkTileDirtyByTile(tile); return true; } break; @@ -139,7 +141,10 @@ bool TryReserveRailTrack(TileIndex tile, Track t) switch (GetTileType(tile)) { case MP_RAILWAY: - if (IsRailWaypoint(tile) || IsRailDepot(tile)) SetWaypointReservation(tile, false); + if (IsRailWaypoint(tile) || IsRailDepot(tile)) { + SetWaypointReservation(tile, false); + MarkTileDirtyByTile(tile); + } if (IsPlainRailTile(tile)) UnreserveTrack(tile, t); break; @@ -148,7 +153,10 @@ bool TryReserveRailTrack(TileIndex tile, Track t) break; case MP_STATION: - if (IsRailwayStation(tile)) SetRailwayStationReservation(tile, false); + if (IsRailwayStation(tile)) { + SetRailwayStationReservation(tile, false); + MarkTileDirtyByTile(tile); + } break; case MP_TUNNELBRIDGE: -- 1.5.6 From cc0a0f6c6cab16ac47c42c6bacd077d6105892cc Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 30 Mar 2008 23:53:40 +0200 Subject: Bar level crossings upon path reservation. --- src/pbs.cpp | 7 ++++++- src/train_cmd.cpp | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/pbs.cpp b/src/pbs.cpp index 6d385b2..939786f 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -101,6 +101,8 @@ bool TryReserveRailTrack(TileIndex tile, Track t) case MP_ROAD: if (IsLevelCrossing(tile) && !GetCrossingReservation(tile)) { SetCrossingReservation(tile, true); + BarCrossing(tile); + MarkTileDirtyByTile(tile); return true; } break; @@ -149,7 +151,10 @@ bool TryReserveRailTrack(TileIndex tile, Track t) break; case MP_ROAD: - if (IsLevelCrossing(tile)) SetCrossingReservation(tile, false); + if (IsLevelCrossing(tile)) { + SetCrossingReservation(tile, false); + UpdateLevelCrossing(tile); + } break; case MP_STATION: diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index c51c079..7c4199e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1709,8 +1709,8 @@ void UpdateLevelCrossing(TileIndex tile, bool sound) { assert(IsLevelCrossingTile(tile)); - /* train on crossing || train approaching crossing */ - bool new_state = VehicleFromPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile); + /* train on crossing || train approaching crossing || reserved */ + bool new_state = VehicleFromPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile) || GetCrossingReservation(tile); if (new_state != IsCrossingBarred(tile)) { if (new_state && sound) { @@ -3674,6 +3674,10 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) if (IsFrontEngine(v)) { v->load_unload_time_rem = 0; + /* If we are approching a crossing that is reserved, play the sound now. */ + TileIndex crossing = TrainApproachingCrossingTile(v); + if (crossing != INVALID_TILE && GetCrossingReservation(crossing)) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing); + /* Always try to extend the reservation when entering a tile. */ CheckNextTrainTile(v); } -- 1.5.6 From 3d265529acac3f4d8021b6681bfc60d3413226cb Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 01:21:09 +0100 Subject: Tweak pathfinder penalties a bit. --- src/settings.cpp | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 4030a3e..f36afa8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1720,9 +1720,9 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR(GameSettings, pf.yapf.rail_firstred_exit_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 100 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_lastred_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 10 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_lastred_exit_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 100 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), - SDT_CONDVAR(GameSettings, pf.yapf.rail_station_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 30 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.yapf.rail_station_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 10 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_slope_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 2 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), - SDT_CONDVAR(GameSettings, pf.yapf.rail_curve45_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, pf.yapf.rail_curve45_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_curve90_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 6 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_depot_reverse_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 50 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR(GameSettings, pf.yapf.rail_crossing_penalty, SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), -- 1.5.6 From 39a2d922c790f0fe17931554b245229799c4f462 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Fri, 20 Jun 2008 21:34:54 +0200 Subject: Debug: Asserts added to all calls to FreeTrainTrackReservation to help track down a bug. --- src/train_cmd.cpp | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 7c4199e..afa020d 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1831,6 +1831,7 @@ static void ReverseTrainDirection(Vehicle *v) } /* Clear path reservation in front. */ + assert(IsFrontEngine(v)); FreeTrainTrackReservation(v); /* Check if we were approaching a rail/road-crossing */ @@ -2899,6 +2900,7 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir /* A path was found, but could not be reserved. */ if (res_dest.tile != INVALID_TILE && !res_dest.okay) { if (mark_stuck) MarkTrainAsStuck(v); + assert(IsFrontEngine(v)); FreeTrainTrackReservation(v); return best_track; } @@ -2914,6 +2916,7 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir if (got_reservation != NULL) *got_reservation = true; if (changed_signal) MarkTileDirtyByTile(tile); } else { + assert(IsFrontEngine(v)); FreeTrainTrackReservation(v); if (mark_stuck) MarkTrainAsStuck(v); } @@ -2946,6 +2949,7 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir res_dest = cur_dest; if (res_dest.okay) continue; /* Path found, but could not be reserved. */ + assert(IsFrontEngine(v)); FreeTrainTrackReservation(v); if (mark_stuck) MarkTrainAsStuck(v); if (got_reservation != NULL) *got_reservation = false; @@ -2955,6 +2959,7 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir } /* No order or no safe position found, try any position. */ if (!TryReserveSafeTrack(v, res_dest.tile, res_dest.trackdir, true)) { + assert(IsFrontEngine(v)); FreeTrainTrackReservation(v); if (mark_stuck) MarkTrainAsStuck(v); if (got_reservation != NULL) *got_reservation = false; -- 1.5.6