From 7f6abd1c61760aecc665535fbbbeadfbd54705b7 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 aefa0a6..50a3832 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -30,6 +30,7 @@ int _debug_yapf_level; int _debug_freetype_level; int _debug_sl_level; int _debug_station_level; +int _debug_pbs_level; struct DebugLevel { @@ -54,6 +55,7 @@ struct DebugLevel { DEBUG_LEVEL(freetype), DEBUG_LEVEL(sl), DEBUG_LEVEL(station), + DEBUG_LEVEL(pbs), }; #undef DEBUG_LEVEL diff --git a/src/debug.h b/src/debug.h index 15f9f8c..bcd868b 100644 --- a/src/debug.h +++ b/src/debug.h @@ -47,6 +47,7 @@ extern int _debug_freetype_level; extern int _debug_sl_level; extern int _debug_station_level; + extern int _debug_pbs_level; void CDECL debug(const char *dbg, ...); #endif /* NO_DEBUG_MESSAGES */ -- 1.5.5 From 012a5537d56872c33801cae2080428f96d1e5e65 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 01:59:24 +0100 Subject: Add map accessors for PBS reservations. --- docs/landscape.html | 38 ++++++++++++++++++ docs/landscape_grid.html | 16 ++++---- src/rail_map.h | 95 ++++++++++++++++++++++++++++++++++++++++++++++ src/road_cmd.cpp | 7 +++- src/road_map.h | 37 ++++++++++++++++++ src/station_map.h | 36 +++++++++++++++++ src/track_func.h | 15 +++++++ src/tunnelbridge_map.h | 39 +++++++++++++++++++ src/waypoint.cpp | 7 +++- 9 files changed, 280 insertions(+), 10 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 2789b41..4205d3a 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -168,6 +168,17 @@ static inline DiagDirection GetRailDepotDirection(TileIndex t) return (DiagDirection)GB(_m[t].m5, 0, 2); } +/** + * Returns the track of a depot, ignoring direction + * @pre IsRailDepotTile(t) + * @param t the tile to get the depot track from + * @return the track of the depot + */ +static inline Track GetRailDepotTrack(TileIndex t) +{ + return AxisToTrack(DiagDirToAxis(GetRailDepotDirection(t))); +} + /** * Returns the axis of the waypoint @@ -213,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_cmd.cpp b/src/road_cmd.cpp index 0db3392..0d8027b 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -299,7 +299,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); } @@ -517,7 +520,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/road_map.h b/src/road_map.h index c742338..2d64d0e 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -247,6 +247,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 0c255a9..72014cb 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -198,6 +198,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) { @@ -271,6 +306,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 88d8d42..676928e 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -200,6 +200,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 e5c94a6..5dc331b 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -10,6 +10,7 @@ #include "tile_map.h" #include "bridge_map.h" #include "tunnel_map.h" +#include "track_func.h" /** @@ -79,4 +80,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) ? AxisToTrackBits(DiagDirToAxis(GetTunnelBridgeDirection(t))) : TRACK_BIT_NONE; +} + #endif /* TUNNELBRIDGE_MAP_H */ diff --git a/src/waypoint.cpp b/src/waypoint.cpp index 781c673..f4cfc6c 100644 --- a/src/waypoint.cpp +++ b/src/waypoint.cpp @@ -250,7 +250,9 @@ CommandCost CmdBuildTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint3 if (flags & DC_EXEC) { 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); @@ -321,7 +323,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.5 From ad460ba98d8c7f75ed8b0e4f1fbec67834fac8f3 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Feb 2008 02:03:25 +0100 Subject: Draw reserved tracks darker if a patch setting is set. --- src/lang/english.txt | 1 + src/rail_cmd.cpp | 15 +++++++++++++++ src/road_cmd.cpp | 6 ++++++ src/settings.cpp | 1 + src/settings_gui.cpp | 1 + src/settings_type.h | 2 ++ src/station_cmd.cpp | 6 ++++++ src/tunnelbridge_cmd.cpp | 7 +++++++ 8 files changed, 39 insertions(+), 0 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index e298044..e5b5f85 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1130,6 +1130,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 2870e4f..05f14a2 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1684,6 +1684,17 @@ 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 (_patches.show_track_reservation) { + TrackBits pbs = GetTrackReservation(ti->tile); + if (pbs & TRACK_BIT_X && (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED)) DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH); + if (pbs & TRACK_BIT_Y && (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED)) DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); + if (pbs & TRACK_BIT_UPPER) AddSortableSpriteToDraw(rti->base_sprites.single_n, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_N ? 8 : 0)); + if (pbs & TRACK_BIT_LOWER) AddSortableSpriteToDraw(rti->base_sprites.single_s, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_S ? 8 : 0)); + if (pbs & TRACK_BIT_LEFT) AddSortableSpriteToDraw(rti->base_sprites.single_w, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_W ? 8 : 0)); + if (pbs & TRACK_BIT_RIGHT) AddSortableSpriteToDraw(rti->base_sprites.single_e, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_E ? 8 : 0)); + } + if (IsValidCorner(halftile_corner)) { DrawFoundation(ti, HalftileFoundation(halftile_corner)); @@ -1842,6 +1853,10 @@ default_waypoint: DrawGroundSprite(image, PAL_NONE); + /* PBS debugging, draw reserved tracks darker */ + if (_patches.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 (HasCatenary(GetRailType(ti->tile))) DrawCatenary(ti); /* End now if buildings are invisible */ diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 0d8027b..cae8222 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -1130,6 +1130,12 @@ static void DrawTile_Road(TileInfo *ti) } DrawGroundSprite(image, pal); + + /* PBS debugging, draw reserved tracks darker */ + if (_patches.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 322b401..f201ccb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1405,6 +1405,7 @@ const SettingDesc _patch_settings[] = { SDT_BOOL(Patches, timetable_in_ticks, S, 0, false, STR_CONFIG_PATCHES_TIMETABLE_IN_TICKS, NULL), SDT_VAR(Patches, loading_indicators, SLE_UINT8, S,MS, 1, 0, 2, 0, STR_CONFIG_PATCHES_LOADING_INDICATORS, RedrawScreen), SDT_VAR(Patches, default_rail_type, SLE_UINT8, S,MS, 4, 0, 6, 0, STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE, NULL), + SDT_BOOL(Patches, show_track_reservation, S, 0, false, STR_CONFIG_PATCHES_SHOW_TRACK_RESERVATION,RedrawScreen), /***************************************************************************/ /* Construction section of the GUI-configure patches window */ diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index c699443..2d70115 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -731,6 +731,7 @@ static const char *_patches_ui[] = { "timetable_in_ticks", "default_rail_type", "always_build_infrastructure", + "show_track_reservation", }; static const char *_patches_construction[] = { diff --git a/src/settings_type.h b/src/settings_type.h index 54903d4..5b445e0 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -232,6 +232,8 @@ struct Patches { bool give_money; ///< allow giving other players money bool enable_signal_gui; ///< Show the signal GUI when the signal button is pressed + + bool show_track_reservation; ///< Highlite reserved tracks. }; extern Patches _patches; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index d99a5fc..79278eb 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2179,6 +2179,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 (_patches.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 (HasCatenary(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 109f86b..ddb01e4 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -870,6 +870,13 @@ static void DrawTile_TunnelBridge(TileInfo *ti) image += tunnelbridge_direction * 2; DrawGroundSprite(image, PAL_NONE); + + /* PBS debugging, draw reserved tracks darker */ + if (_patches.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); -- 1.5.5 From 9e57c177ee1a9a267f6c51663cdd773bef7a6c88 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 3 Jan 2008 18:19:14 +0100 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 100755 src/pbs.cpp create mode 100755 src/pbs.h diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 60c7d84..f1f509e 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -668,6 +668,10 @@ > + + @@ -1256,6 +1260,10 @@ > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 92d2a4e..0cd1ad4 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -665,6 +665,10 @@ > + + @@ -1253,6 +1257,10 @@ > + + diff --git a/source.list b/source.list index ece9534..b4246ce 100644 --- a/source.list +++ b/source.list @@ -58,6 +58,7 @@ os_timer.cpp ottdres.rc #end pathfind.cpp +pbs.cpp players.cpp queue.cpp rail.cpp @@ -235,6 +236,7 @@ order_func.h order_type.h core/overflowsafe_type.hpp 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 100755 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 100755 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.5 From 4ef97330cbd288469268534c4badd49a9823ffa8 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 25 Mar 2008 15:46:04 +0100 Subject: PBS signals added to the map array. Basic support for building and drawing present, but using the old sprites. --- docs/landscape.html | 26 +++++++++++++------ docs/landscape_grid.html | 2 +- src/lang/english.txt | 19 +++++++++++++ src/misc/dbg_helpers.cpp | 2 +- src/misc_gui.cpp | 20 +++++++------- src/openttd.cpp | 40 ++++++++++++++++++++++++++++ src/rail_cmd.cpp | 64 ++++++++++++++++++++++++++++++++++++---------- src/rail_gui.cpp | 16 ++++++++--- src/rail_map.h | 30 +++++++++++++++++---- src/settings.cpp | 2 + src/settings_gui.cpp | 2 + src/settings_type.h | 3 ++ src/signal_type.h | 12 ++++++--- 13 files changed, 190 insertions(+), 48 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 e5b5f85..ed7b78b 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1166,6 +1166,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} @@ -1659,12 +1667,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 a07c5ce..4c7dbf0 100644 --- a/src/misc/dbg_helpers.cpp +++ b/src/misc/dbg_helpers.cpp @@ -47,7 +47,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 7805347..70b1926 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -68,25 +68,25 @@ static void LandInfoWndProc(Window *w, WindowEvent *e) if (e->event == WE_PAINT) { DrawWindowWidgets(w); - DoDrawStringCentered(140, 16, _landinfo_data[0], TC_LIGHT_BLUE); - DoDrawStringCentered(140, 27, _landinfo_data[1], TC_FROMSTRING); - DoDrawStringCentered(140, 38, _landinfo_data[2], TC_FROMSTRING); - DoDrawStringCentered(140, 49, _landinfo_data[3], TC_FROMSTRING); - DoDrawStringCentered(140, 60, _landinfo_data[4], TC_FROMSTRING); - if (_landinfo_data[5][0] != '\0') DrawStringMultiCenter(140, 76, BindCString(_landinfo_data[5]), w->width - 4); - if (_landinfo_data[6][0] != '\0') DoDrawStringCentered(140, 71, _landinfo_data[6], TC_FROMSTRING); + DoDrawStringCentered(150, 16, _landinfo_data[0], TC_LIGHT_BLUE); + DoDrawStringCentered(150, 27, _landinfo_data[1], TC_FROMSTRING); + DoDrawStringCentered(150, 38, _landinfo_data[2], TC_FROMSTRING); + DoDrawStringCentered(150, 49, _landinfo_data[3], TC_FROMSTRING); + DoDrawStringCentered(150, 60, _landinfo_data[4], TC_FROMSTRING); + if (_landinfo_data[5][0] != '\0') DrawStringMultiCenter(150, 76, BindCString(_landinfo_data[5]), w->width - 4); + if (_landinfo_data[6][0] != '\0') DoDrawStringCentered(150, 71, _landinfo_data[6], TC_FROMSTRING); } } 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_NONE, 14, 0, 279, 14, 92, 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_NONE, 14, 0, 299, 14, 92, 0x0, STR_NULL}, { WIDGETS_END}, }; static const WindowDesc _land_info_desc = { - WDP_AUTO, WDP_AUTO, 280, 93, 280, 93, + WDP_AUTO, WDP_AUTO, 300, 93, 300, 93, WC_LAND_INFO, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _land_info_widgets, diff --git a/src/openttd.cpp b/src/openttd.cpp index bc12123..5f65315 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -2451,6 +2451,45 @@ 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(94)) { + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + if (HasSignals(t)) { + /* move the signal variant */ + SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); + SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); + ClrBit(_m[t].m2, 2); + ClrBit(_m[t].m2, 6); + } + + /* Clear PBS reservation on track */ + if (IsRailDepot(t) ||IsRailWaypoint(t)) + SetWaypointReservation(t, false); + else + SetTrackReservation(t, TRACK_BIT_NONE); + break; + + case MP_ROAD: /* Clear PBS reservation on crossing */ + if (IsLevelCrossing(t)) SetCrossingReservation(t, false); + break; + + case MP_STATION: /* Clear PBS reservation on station */ + if (IsRailwayStation(t)) SetRailwayStationReservation(t, false); + break; + + case MP_TUNNELBRIDGE: /* Clear PBS reservation on tunnels/birdges */ + if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) SetTunnelBridgeReservation(t, false); + break; + + default: break; + } + } + } + return InitializeWindowsAndCaches(); } @@ -2477,3 +2516,4 @@ void ReloadNewGRFData() /* redraw the whole screen */ MarkWholeScreenDirty(); } + diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 05f14a2..ec4e303 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); @@ -1448,7 +1460,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)); @@ -2195,30 +2207,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_gui.cpp b/src/rail_gui.cpp index bfcad9b..524f152 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -50,6 +50,9 @@ static bool _convert_signal_button; ///< convert signal button in the sign 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; byte numtracks; @@ -208,6 +211,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; @@ -215,13 +221,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[_patches.cycle_signal_types]); } else { SB(p1, 3, 1, _ctrl_pressed); SB(p1, 4, 1, (_cur_year < _patches.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[_patches.default_signal_type]); + SB(p1, 8, 1, 0); + SB(p1, 9, 6, cycle_bounds[_patches.cycle_signal_types]); } DoCommandP(tile, p1, 0, CcPlaySound1E, CMD_BUILD_SIGNALS | diff --git a/src/rail_map.h b/src/rail_map.h index 4205d3a..54f961e 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -308,19 +308,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) @@ -345,15 +350,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 @@ -480,6 +485,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/settings.cpp b/src/settings.cpp index f201ccb..fb45eff 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1419,6 +1419,8 @@ const SettingDesc _patch_settings[] = { SDT_VAR(Patches, drag_signals_density,SLE_UINT8,S, 0, 4, 1, 20, 0, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY,DragSignalsDensityChanged), SDT_VAR(Patches, semaphore_build_before,SLE_INT32, S, NC, 1975, MIN_YEAR, MAX_YEAR, 1, STR_CONFIG_PATCHES_SEMAPHORE_BUILD_BEFORE_DATE, ResetSignalVariant), SDT_CONDVAR(Patches, town_layout, SLE_UINT8, 59, SL_MAX_VERSION, 0, MS, TL_ORIGINAL, TL_NO_ROADS, NUM_TLS - 1, 1, STR_CONFIG_PATCHES_TOWN_LAYOUT, CheckTownLayout), + SDT_VAR(Patches, default_signal_type, SLE_UINT8,S,MS, 0, 0, 2, 1, STR_CONFIG_PATCHES_DEFAULT_SIGNAL_TYPE, NULL), + SDT_VAR(Patches, cycle_signal_types, SLE_UINT8,S,MS, 0, 0, 2, 1, STR_CONFIG_PATCHES_CYCLE_SIGNAL_TYPES, NULL), /***************************************************************************/ /* Vehicle section of the GUI-configure patches window */ diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 2d70115..0a7d35a 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -745,6 +745,8 @@ static const char *_patches_construction[] = { "drag_signals_density", "oil_refinery_limit", "semaphore_build_before", + "default_signal_type", + "cycle_signal_types", }; static const char *_patches_stations[] = { diff --git a/src/settings_type.h b/src/settings_type.h index 5b445e0..0dda823 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -234,6 +234,9 @@ struct Patches { bool enable_signal_gui; ///< Show the signal GUI when the signal button is pressed bool show_track_reservation; ///< Highlite 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. }; extern Patches _patches; 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.5 From 1c810e7326f7248af2994bf23d08b20d09978611 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 30 Mar 2008 23:52:14 +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 ec4e303..1f5f38c 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -2157,12 +2157,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 54f961e..5324c55 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -338,6 +338,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 03747ee..ef5b6e3 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) @@ -322,13 +323,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 @@ -336,6 +343,7 @@ static SigFlags ExploreSegment(Owner owner) flags |= SF_GREEN; } } + continue; } } @@ -433,13 +441,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.5 From 517aca15c35c8a40fd18494ce2633853ec31ea70 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 adbb422..f3ce5f0 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 09a38a3..3efbc17 100644 --- a/src/pathfind.cpp +++ b/src/pathfind.cpp @@ -655,7 +655,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 5756f04..685370e 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.5 From c1bffe6e117305cf1b932d10f8369b6f2cbde5c9 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 | 11 ++++++++++- src/rail_gui.cpp | 2 ++ 2 files changed, 12 insertions(+), 1 deletions(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 1f5f38c..7323444 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -986,6 +986,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 +1024,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 +1034,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 +1059,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 +1109,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 */ @@ -1164,6 +1172,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 524f152..f92dfa3 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -479,11 +479,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, _patches.drag_signals_density); } else { SB(p2, 3, 1, 0); SB(p2, 4, 1, (_cur_year < _patches.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC)); SB(p2, 6, 1, _ctrl_pressed); + SB(p2, 7, 3, _default_signal_type[_patches.default_signal_type]); SB(p2, 24, 8, _patches.drag_signals_density); } -- 1.5.5 From 8c86dcc508c361dbf3d0022daf718f1069100352 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 7323444..9240787 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 5324c55..4dcb6b0 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -350,7 +350,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.5 From ef1975df16ed785754dfbe3420f0ee3bec564967 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 ed7b78b..e91930e 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3552,10 +3552,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 f92dfa3..67de72b 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1256,10 +1256,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, @@ -1307,10 +1311,14 @@ static void SignalBuildWndProc(Window *w, WindowEvent *e) DrawSignalSprite(w, BSW_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, -1, 13); // xsize of sprite + 1 == 10 DrawSignalSprite(w, BSW_SEMAPHORE_EXIT, SPR_IMG_SIGNAL_SEMAPHORE_EXIT, 0, 12); // xsize of sprite + 1 == 9 DrawSignalSprite(w, BSW_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_COMBO, 0, 12); // xsize of sprite + 1 == 9 + DrawSignalSprite(w, BSW_SEMAPHORE_PBS, SPR_IMG_SIGNAL_SEMAPHORE_PBS, 0, 12); // xsize of sprite + 1 == 9 + DrawSignalSprite(w, BSW_SEMAPHORE_PBS_OWAY, SPR_IMG_SIGNAL_SEMAPHORE_PBSOWAY, -1, 13); // xsize of sprite + 1 == 10 DrawSignalSprite(w, BSW_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_NORM, -1, 4); DrawSignalSprite(w, BSW_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, -2, 6); DrawSignalSprite(w, BSW_ELECTRIC_EXIT, SPR_IMG_SIGNAL_ELECTRIC_EXIT, -2, 6); DrawSignalSprite(w, BSW_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_COMBO, -2, 6); + DrawSignalSprite(w, BSW_ELECTRIC_PBS, SPR_IMG_SIGNAL_ELECTRIC_PBS, -1, 4); + DrawSignalSprite(w, 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, _patches.drag_signals_density); @@ -1325,13 +1333,17 @@ static void SignalBuildWndProc(Window *w, WindowEvent *e) 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: w->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type); - _cur_signal_type = (SignalType)((uint)((e->we.click.widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_COMBO + 1))); + _cur_signal_type = (SignalType)((uint)((e->we.click.widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_LAST + 1))); _cur_signal_variant = e->we.click.widget >= BSW_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE; break; @@ -1374,29 +1386,33 @@ static void SignalBuildWndProc(Window *w, WindowEvent *e) /** 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, @@ -1695,6 +1711,6 @@ void InitializeRailGUI() SetDefaultRailGui(); _convert_signal_button = false; - _cur_signal_type = SIGTYPE_NORMAL; + _cur_signal_type = _default_signal_type[_patches.default_signal_type]; ResetSignalVariant(); } diff --git a/src/table/sprites.h b/src/table/sprites.h index cc2cb6a..289a3ec 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -1275,15 +1275,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.5 From 38f347cd948b992ac152663262347bc12c8a7414 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 25 Mar 2008 17:13:15 +0100 Subject: Extend YAPF with the possibility to override the railtype info of the vehicle. --- src/yapf/follow_track.hpp | 14 ++++++++------ src/yapf/yapf_costrail.hpp | 4 ++-- src/yapf/yapf_destrail.hpp | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/yapf/follow_track.hpp b/src/yapf/follow_track.hpp index e72d9de..4917f83 100644 --- a/src/yapf/follow_track.hpp +++ b/src/yapf/follow_track.hpp @@ -33,14 +33,15 @@ struct CFollowTrackT bool m_is_station; ///< last turn passed station int m_tiles_skipped; ///< number of skipped tunnel or station tiles ErrorCode m_err; - CPerformanceTimer* m_pPerf; + 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 685370e..872c5da 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().m_compatible_railtypes, &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().m_compatible_railtypes, &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 fab8400..72d20e6 100644 --- a/src/yapf/yapf_destrail.hpp +++ b/src/yapf/yapf_destrail.hpp @@ -7,13 +7,13 @@ class CYapfDestinationRailBase { -protected: +public: RailTypes m_compatible_railtypes; -public: - void SetDestination(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) @@ -84,7 +84,7 @@ protected: } public: - void SetDestination(Vehicle* v) + void SetDestination(const Vehicle* v) { switch (v->current_order.GetType()) { case OT_GOTO_STATION: -- 1.5.5 From e4a65c7e8899abd09ea6c1e967da41f68499795b Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 25 Mar 2008 17:41:43 +0100 Subject: YAPF is now able to reserve the found path. --- src/pbs.cpp | 234 ++++++++++++++++++++++++++++++++++++++++++- src/pbs.h | 32 ++++++ src/rail_map.h | 48 +++++++++ src/track_func.h | 11 ++ src/yapf/yapf.h | 5 +- src/yapf/yapf_node_rail.hpp | 31 ++++++ src/yapf/yapf_rail.cpp | 147 ++++++++++++++++++++++++--- 7 files changed, 490 insertions(+), 18 deletions(-) diff --git a/src/pbs.cpp b/src/pbs.cpp index c75eb8c..318fca0 100755 --- 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,14 @@ #include "road_map.h" #include "station_map.h" #include "tunnelbridge_map.h" +#include "functions.h" +#include "debug.h" +#include "direction_func.h" +#include "rail.h" +#include "road_func.h" +#include "vehicle_base.h" +#include "yapf/follow_track.hpp" +#include "misc/autoptr.hpp" /** * Get the reserved trackbits for any tile, regardless of type. @@ -41,3 +48,228 @@ 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 (_debug_pbs_level >= 2) MarkTileDirtyByTile(tile); + tile = TILE_ADD(tile, diff); + } while (IsCompatibleTrainStationTile(tile, start)); +} + +/** + * Try to reserve a specific track on a tile + * @param tile the tile + * @param t the track + * @return true if reservation was successfull, i.e. the track was + * free and didn't cross any other reserved tracks. + */ +bool TryReserveRailTrack(TileIndex tile, Track t) +{ + assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0); + + if (_patches.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 (_patches.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; + } +} + +/** + * 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, _patches.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 51a9ba2..5123673 100755 --- a/src/pbs.h +++ b/src/pbs.h @@ -6,8 +6,40 @@ #define PBS_H #include "tile_type.h" +#include "direction_type.h" #include "track_type.h" +#include "vehicle_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); + +struct PBSTileInfo { + TileIndex tile; + Trackdir trackdir; + bool okay; + + 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. + * + * @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 4dcb6b0..c121e47 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) @@ -504,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/track_func.h b/src/track_func.h index 676928e..aa9cdcd 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -347,6 +347,17 @@ static inline TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, Trackdir } /** + * Convert a trackdir to TrackBits, discarding the direction information. + * + * @param trackdir the Trackdir to convert + * @return the TrackBits + */ +static inline TrackBits TrackdirToTrackBits(Trackdir trackdir) +{ + return TrackToTrackBits(TrackdirToTrack(trackdir)); +} + +/** * Maps a trackdir to the trackdir that you will end up on if you go straight * ahead. * diff --git a/src/yapf/yapf.h b/src/yapf/yapf.h index f11ef2a..c3b405a 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(Vehicle *v, TileIndex tile, DiagDirection enterdir) * @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(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found); +Trackdir YapfChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, 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 3d11288..aa84958 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, Tbase &obj, bool (Tfunc::*func)(TileIndex, Trackdir)) const + { + typename Tbase::TrackFollower ft(v); + TileIndex cur = base::GetTile(); + Trackdir cur_td = base::GetTrackdir(); + + while (cur != GetLastTile() || 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 159c097..8919a18 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(), *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(), *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(), *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(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found) + static Trackdir stChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, 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(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found) + FORCEINLINE Trackdir ChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, 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(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found) +Trackdir YapfChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target) { // default is YAPF type 2 - typedef Trackdir (*PfnChooseRailTrack)(Vehicle*, TileIndex, DiagDirection, TrackBits, bool*); + typedef Trackdir (*PfnChooseRailTrack)(Vehicle*, TileIndex, DiagDirection, TrackBits, bool*, bool, PBSTileInfo*); PfnChooseRailTrack pfnChooseRailTrack = &CYapfRail1::stChooseRailTrack; // check if non-default YAPF type needed @@ -258,7 +376,7 @@ Trackdir YapfChooseRailTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, 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(Vehicle *v, int max_distance, int reverse_pe 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)(Vehicle*, TileIndex, Trackdir, TileIndex, Trackdir, int, int, TileIndex*, bool*); @@ -345,7 +460,7 @@ bool YapfFindNearestRailDepotTwoWay(Vehicle *v, int max_distance, int reverse_pe 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.5 From bd642860749224e7f1a919e8d9b7243ef92f7a91 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 25 Mar 2008 15:46:49 +0100 Subject: Added PBS related penalties to YAPF. --- src/settings.cpp | 3 + src/track_func.h | 13 +++++ src/yapf/yapf_costrail.hpp | 107 +++++++++++++++++++++++++++----------------- src/yapf/yapf_settings.h | 2 + 4 files changed, 84 insertions(+), 41 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index fb45eff..27880fa 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1601,6 +1601,8 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p0 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, 500 , -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p1 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, -100 , -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p2 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, 5 , -1000000, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR (Patches, yapf.rail_pbs_cross_penalty , SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR (Patches, yapf.rail_pbs_signal_back_penalty,SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 15 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), /* penalties for too long or too short station platforms */ SDT_CONDVAR (Patches, yapf.rail_longer_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 8 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_longer_platform_per_tile_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 0 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), @@ -2130,3 +2132,4 @@ static bool IsSignedVarMemType(VarType vt) } return false; } + diff --git a/src/track_func.h b/src/track_func.h index aa9cdcd..3411c78 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -581,6 +581,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 872c5da..46a14ba 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; diff --git a/src/yapf/yapf_settings.h b/src/yapf/yapf_settings.h index 0d84072..14def60 100644 --- a/src/yapf/yapf_settings.h +++ b/src/yapf/yapf_settings.h @@ -58,6 +58,8 @@ YS_DEF_BEGIN YS_DEF(int32 , rail_look_ahead_signal_p0) ///< constant in polynomial penalty function YS_DEF(int32 , rail_look_ahead_signal_p1) ///< constant in polynomial penalty function YS_DEF(int32 , rail_look_ahead_signal_p2) ///< constant in polynomial penalty function + YS_DEF(uint32, rail_pbs_cross_penalty) ///< penalty for crossing a reserved tile + YS_DEF(uint32, rail_pbs_signal_back_penalty) ///< penalty for passing a pbs signal from the backside YS_DEF(uint32, rail_longer_platform_penalty) ///< penalty for longer station platform than train YS_DEF(uint32, rail_longer_platform_per_tile_penalty) ///< penalty for longer station platform than train (per tile) -- 1.5.5 From 612ffdbbd221bf1dae17c127d2d0a6df61da15ff 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 | 2 ++ src/yapf/yapf_costrail.hpp | 11 +++++++++++ src/yapf/yapf_settings.h | 1 + 3 files changed, 14 insertions(+), 0 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 27880fa..1705177 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1603,6 +1603,7 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p2 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, 5 , -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_pbs_cross_penalty , SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_pbs_signal_back_penalty,SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 15 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR (Patches, yapf.rail_doubleslip_penalty , SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), /* penalties for too long or too short station platforms */ SDT_CONDVAR (Patches, yapf.rail_longer_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 8 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_longer_platform_per_tile_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 0 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), @@ -2133,3 +2134,4 @@ static bool IsSignedVarMemType(VarType vt) return false; } + diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index 46a14ba..df724a8 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) */ diff --git a/src/yapf/yapf_settings.h b/src/yapf/yapf_settings.h index 14def60..dad723d 100644 --- a/src/yapf/yapf_settings.h +++ b/src/yapf/yapf_settings.h @@ -60,6 +60,7 @@ YS_DEF_BEGIN YS_DEF(int32 , rail_look_ahead_signal_p2) ///< constant in polynomial penalty function YS_DEF(uint32, rail_pbs_cross_penalty) ///< penalty for crossing a reserved tile YS_DEF(uint32, rail_pbs_signal_back_penalty) ///< penalty for passing a pbs signal from the backside + YS_DEF(uint32, rail_doubleslip_penalty) ///< penalty for passing a double slip switch YS_DEF(uint32, rail_longer_platform_penalty) ///< penalty for longer station platform than train YS_DEF(uint32, rail_longer_platform_per_tile_penalty) ///< penalty for longer station platform than train (per tile) -- 1.5.5 From c5e28f2ddbb856684f935cdb24fc104e95591ba8 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 | 2 ++ src/yapf/yapf_costrail.hpp | 1 + src/yapf/yapf_settings.h | 1 + 3 files changed, 4 insertions(+), 0 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 1705177..a76e3ef 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1602,6 +1602,7 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p1 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, -100 , -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p2 , SLE_INT , 28, SL_MAX_VERSION, 0, 0, 5 , -1000000, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_pbs_cross_penalty , SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR (Patches, yapf.rail_pbs_cross_st_factor , SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 2 , 0, 2000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_pbs_signal_back_penalty,SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 15 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_doubleslip_penalty , SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), /* penalties for too long or too short station platforms */ @@ -2135,3 +2136,4 @@ static bool IsSignedVarMemType(VarType vt) } + diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp index df724a8..636d06d 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -147,6 +147,7 @@ public: 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; + if (IsRailwayStationTile(tile)) cost *= Yapf().PfGetSettings().rail_pbs_cross_st_factor; return cost * (skipped + 1); } return 0; diff --git a/src/yapf/yapf_settings.h b/src/yapf/yapf_settings.h index dad723d..72608f8 100644 --- a/src/yapf/yapf_settings.h +++ b/src/yapf/yapf_settings.h @@ -59,6 +59,7 @@ YS_DEF_BEGIN YS_DEF(int32 , rail_look_ahead_signal_p1) ///< constant in polynomial penalty function YS_DEF(int32 , rail_look_ahead_signal_p2) ///< constant in polynomial penalty function YS_DEF(uint32, rail_pbs_cross_penalty) ///< penalty for crossing a reserved tile + YS_DEF(uint32, rail_pbs_cross_st_factor) ///< factor for increasing the pbs penalty for station tiles YS_DEF(uint32, rail_pbs_signal_back_penalty) ///< penalty for passing a pbs signal from the backside YS_DEF(uint32, rail_doubleslip_penalty) ///< penalty for passing a double slip switch -- 1.5.5 From d1a37dc6b1a5abc4734c4d6f61dad32581710e7d 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 4917f83..0cd188d 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 c3b405a..beee436 100644 --- a/src/yapf/yapf.h +++ b/src/yapf/yapf.h @@ -66,6 +66,17 @@ bool YapfFindNearestRailDepotTwoWay(Vehicle *v, int max_distance, int reverse_pe /** Returns true if it is better to reverse the train before leaving station */ bool YapfCheckReverseTrain(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 636d06d..337b4c3 100644 --- a/src/yapf/yapf_costrail.hpp +++ b/src/yapf/yapf_costrail.hpp @@ -388,6 +388,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 @@ -420,6 +425,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; } @@ -433,6 +442,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 72d20e6..6b48b83 100644 --- a/src/yapf/yapf_destrail.hpp +++ b/src/yapf/yapf_destrail.hpp @@ -57,6 +57,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 aa84958..f9983ac 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 8919a18..6fb3992 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().m_compatible_railtypes); + 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(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target) { @@ -464,6 +549,19 @@ bool YapfFindNearestRailDepotTwoWay(Vehicle *v, int max_distance, int reverse_pe 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 (_patches.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.5 From d115627d2275f59561fcc80834e32cd84e93b65d Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 25 Mar 2008 17:43:13 +0100 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 f3ce5f0..08c2feb 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, _patches.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, _patches.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, _patches.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 = _patches.npf_max_search_nodes; } -void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v) +void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v, bool reserve_path) { /* Ships don't really reach their stations, but the tile in front. So don't * save the station id for ships. For roadvehs we don't store it either, @@ -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 ca4580d..4f0f298 100644 --- a/src/npf.h +++ b/src/npf.h @@ -44,6 +44,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[] */ @@ -66,6 +68,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 */ @@ -74,6 +77,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! */ @@ -104,7 +108,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.5 From c7c7cb922983fa60005fb0eb25dabd9132f200bb Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 13 Apr 2008 17:48:27 +0200 Subject: Added PBS related penalties to NPF. --- src/npf.cpp | 76 +++++++++++++++++++++++++++++++++++++-------------- src/npf.h | 2 + src/settings.cpp | 5 +++ src/settings_type.h | 2 + 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/npf.cpp b/src/npf.cpp index 08c2feb..6e267fa 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 _patches.npf_rail_pbs_cross_penalty * (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 1); + } + } + return _patches.npf_rail_pbs_cross_penalty; +} + /** * Mark tiles by mowing the grass when npf debug level >= 1. * Will not work for multiplayer games, since it can (will) cause desyncs. @@ -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 += _patches.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 += _patches.npf_rail_firstred_exit_penalty; + } else { + cost += _patches.npf_rail_firstred_penalty; + } + } + } + /* Record the state of this signal */ + NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true); + } else { + /* Record the state of this signal */ + NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false); + } + if (NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) { + if (NPFGetFlag(current, NPF_FLAG_2ND_SIGNAL)) { + NPFSetFlag(current, NPF_FLAG_3RD_SIGNAL, true); } else { - cost += _patches.npf_rail_firstred_penalty; + NPFSetFlag(current, NPF_FLAG_2ND_SIGNAL, true); } + } else { + NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true); } - /* Record the state of this signal */ - NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true); - } else { - /* Record the state of this signal */ - NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false); } - NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true); + + if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(trackdir)) && !NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL)) { + cost += _patches.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 4f0f298..043a6e6 100644 --- a/src/npf.h +++ b/src/npf.h @@ -65,6 +65,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 a76e3ef..b365fa9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1577,6 +1577,10 @@ const SettingDesc _patch_settings[] = { SDT_VAR(Patches, npf_crossing_penalty, SLE_UINT, 0, 0, (3 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL), /* This is the penalty for drive-through road, stops. */ SDT_CONDVAR (Patches, npf_road_drive_through_penalty, SLE_UINT, 47, SL_MAX_VERSION, 0, 0, 8 * NPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + /* Penalty for crossing a reserved tile */ + SDT_CONDVAR(Patches, npf_rail_pbs_cross_penalty, SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 3 * NPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + /* Penalty for passing a pbs signal from the backside */ + SDT_CONDVAR(Patches, npf_rail_pbs_signal_back_penalty,SLE_UINT, 94, SL_MAX_VERSION, 0, 0, 15 * NPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), /* The maximum number of nodes to search */ @@ -2137,3 +2141,4 @@ static bool IsSignedVarMemType(VarType vt) + diff --git a/src/settings_type.h b/src/settings_type.h index 0dda823..4ec603d 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -200,6 +200,8 @@ struct Patches { uint32 npf_road_curve_penalty; ///< The penalty for curves uint32 npf_crossing_penalty; ///< The penalty for level crossings uint32 npf_road_drive_through_penalty; ///< The penalty for going through a drive-through road stop + uint32 npf_rail_pbs_cross_penalty; ///< The penalty for crossing a reserved rail track + uint32 npf_rail_pbs_signal_back_penalty; ///< The penalty for passing a pbs signal from the backside bool population_in_label; ///< Show the population of a town in his label? -- 1.5.5 From 250393fbc24c274c99df98db15835a69116b8733 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 6e267fa..fd63ca6 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, _patches.forbid_90_deg) && + IsWaitingPositionFree(v, current->path.node.tile, (Trackdir)current->path.node.direction, _patches.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 043a6e6..c065f6f 100644 --- a/src/npf.h +++ b/src/npf.h @@ -71,6 +71,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 */ @@ -110,6 +111,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.5 From bdfb72b1eb3cfe4e51c5961a5af8a3d38e556a7d Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 25 Mar 2008 15:27:19 +0100 Subject: Trains reserve paths through PBS signals. Bump savegame version. --- src/lang/english.txt | 2 + src/order_cmd.cpp | 153 ++++++++------ src/order_func.h | 2 + src/saveload.cpp | 3 +- src/settings.cpp | 4 + src/settings_type.h | 3 + src/train.h | 2 + src/train_cmd.cpp | 542 +++++++++++++++++++++++++++++++++++++++++++++++--- src/vehicle.cpp | 5 +- src/vehicle_base.h | 5 +- src/vehicle_func.h | 2 +- src/vehicle_gui.cpp | 2 + 12 files changed, 623 insertions(+), 102 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index e91930e..4e62d44 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -983,6 +983,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! @@ -2878,6 +2879,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/order_cmd.cpp b/src/order_cmd.cpp index 60ec931..d452d7c 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1570,6 +1570,92 @@ static bool OrderConditionCompare(OrderConditionComparator occ, int variable, in } /** + * Process a conditional order and determine the next order. + * @return index of next order ti jump to, or INVALID_VEH_ORDER_ID to use the next order + */ +VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v) +{ + if (order->GetType() != OT_CONDITIONAL) return INVALID_VEH_ORDER_ID; + + bool skip_order = false; + OrderConditionComparator occ = order->GetConditionComparator(); + uint16 value = order->GetConditionValue(); + + switch (order->GetConditionVariable()) { + case OCV_LOAD_PERCENTAGE: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, NULL), value); break; + case OCV_RELIABILITY: skip_order = OrderConditionCompare(occ, v->reliability * 100 >> 16, value); break; + case OCV_MAX_SPEED: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed(), value); break; + case OCV_AGE: skip_order = OrderConditionCompare(occ, v->age / 366, value); break; + case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break; + case OCV_UNCONDITIONALLY: skip_order = true; break; + default: NOT_REACHED(); + } + + return skip_order ? order->GetConditionSkipToOrder() : INVALID_VEH_ORDER_ID; +} + +/** + * Update the vehicle's destination tile from an order. + * @param order the the order the vehicle currently has + * @param v the vehicle to update + */ +bool UpdateOrderDest(Vehicle *v, const Order *order) +{ + switch (order->GetType()) { + case OT_GOTO_STATION: + v->dest_tile = v->GetOrderStationLocation(order->GetDestination()); + break; + + case OT_GOTO_DEPOT: + if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) { + /* We need to search for the nearest depot (hangar). */ + TileIndex location; + DestinationID destination; + bool reverse; + + if (v->FindClosestDepot(&location, &destination, &reverse)) { + v->dest_tile = location; + v->current_order.MakeGoToDepot(destination, ODTFB_PART_OF_ORDERS); + + /* If there is no depot in front, reverse automatically (trains only) */ + if (v->type == VEH_TRAIN && reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION); + + if (v->type == VEH_AIRCRAFT && v->u.air.state == FLYING && v->u.air.targetairport != destination) { + /* The aircraft is now heading for a different hangar than the next in the orders */ + extern void AircraftNextAirportPos_and_Order(Vehicle *v); + AircraftNextAirportPos_and_Order(v); + } + } else { + UpdateVehicleTimetable(v, true); + v->cur_order_index++; + } + } else if (v->type != VEH_AIRCRAFT) { + v->dest_tile = GetDepot(order->GetDestination())->xy; + } + break; + + case OT_GOTO_WAYPOINT: + v->dest_tile = GetWaypoint(order->GetDestination())->xy; + break; + + case OT_CONDITIONAL: { + VehicleOrderID next_order = ProcessConditionalOrder(order, v); + UpdateVehicleTimetable(v, true); + if (next_order != INVALID_VEH_ORDER_ID) { + v->cur_order_index = next_order; + } else { + v->cur_order_index++; + } + } return false; + + default: + v->dest_tile = 0; + return false; + } + return true; +} + +/** * Handle the orders of a vehicle and determine the next place * to go to if needed. * @param v the vehicle to do this for. @@ -1667,71 +1753,7 @@ bool ProcessOrders(Vehicle *v) break; } - switch (order->GetType()) { - case OT_GOTO_STATION: - v->dest_tile = v->GetOrderStationLocation(order->GetDestination()); - break; - - case OT_GOTO_DEPOT: - if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) { - /* We need to search for the nearest depot (hangar). */ - TileIndex location; - DestinationID destination; - bool reverse; - - if (v->FindClosestDepot(&location, &destination, &reverse)) { - v->dest_tile = location; - v->current_order.MakeGoToDepot(destination, ODTFB_PART_OF_ORDERS); - - /* If there is no depot in front, reverse automatically (trains only) */ - if (v->type == VEH_TRAIN && reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION); - - if (v->type == VEH_AIRCRAFT && v->u.air.state == FLYING && v->u.air.targetairport != destination) { - /* The aircraft is now heading for a different hangar than the next in the orders */ - extern void AircraftNextAirportPos_and_Order(Vehicle *v); - AircraftNextAirportPos_and_Order(v); - } - } else { - UpdateVehicleTimetable(v, true); - v->cur_order_index++; - } - } else if (v->type != VEH_AIRCRAFT) { - v->dest_tile = GetDepot(order->GetDestination())->xy; - } - break; - - case OT_GOTO_WAYPOINT: - v->dest_tile = GetWaypoint(order->GetDestination())->xy; - break; - - case OT_CONDITIONAL: { - bool skip_order = false; - OrderConditionComparator occ = order->GetConditionComparator(); - uint16 value = order->GetConditionValue(); - - switch (order->GetConditionVariable()) { - case OCV_LOAD_PERCENTAGE: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, NULL), value); break; - case OCV_RELIABILITY: skip_order = OrderConditionCompare(occ, v->reliability * 100 >> 16, value); break; - case OCV_MAX_SPEED: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed(), value); break; - case OCV_AGE: skip_order = OrderConditionCompare(occ, v->age / 366, value); break; - case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break; - case OCV_UNCONDITIONALLY: skip_order = true; break; - default: NOT_REACHED(); - } - UpdateVehicleTimetable(v, true); - if (skip_order) { - v->cur_order_index = order->GetConditionSkipToOrder(); - } else { - v->cur_order_index++; - } - } return false; - - default: - v->dest_tile = 0; - return false; - } - - return may_reverse; + return UpdateOrderDest(v, order) && may_reverse; } /** @@ -1843,3 +1865,4 @@ static void Load_ORDR() extern const ChunkHandler _order_chunk_handlers[] = { { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST}, }; + diff --git a/src/order_func.h b/src/order_func.h index eec917b..910bdb5 100644 --- a/src/order_func.h +++ b/src/order_func.h @@ -36,6 +36,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); +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); diff --git a/src/saveload.cpp b/src/saveload.cpp index 85e9a7a..ac77a5b 100644 --- a/src/saveload.cpp +++ b/src/saveload.cpp @@ -34,7 +34,7 @@ #include "table/strings.h" -extern const uint16 SAVEGAME_VERSION = 93; +extern const uint16 SAVEGAME_VERSION = 94; uint16 _sl_version; ///< the major savegame version identifier byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! @@ -1806,3 +1806,4 @@ int GetSavegameType(char *file) return mode; } #endif + diff --git a/src/settings.cpp b/src/settings.cpp index b365fa9..fe0979e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1519,6 +1519,9 @@ const SettingDesc _patch_settings[] = { SDT_VAR(Patches, dist_local_authority,SLE_UINT8, 0, 0, 20, 5, 60, 0, STR_NULL, NULL), SDT_VAR(Patches, wait_oneway_signal, SLE_UINT8, 0, 0, 15, 2, 100, 0, STR_NULL, NULL), SDT_VAR(Patches, wait_twoway_signal, SLE_UINT8, 0, 0, 41, 2, 100, 0, STR_NULL, NULL), + SDT_CONDVAR(Patches, wait_for_pbs_path, SLE_UINT8, 94, SL_MAX_VERSION, 0, 0, 30, 2, 255, 0, STR_NULL, NULL), + SDT_CONDBOOL(Patches, reserve_paths, 94, SL_MAX_VERSION, 0, 0, false, STR_NULL, NULL), + SDT_CONDVAR(Patches, path_backoff_interval,SLE_UINT8,94, SL_MAX_VERSION, 0, 0, 20, 1, 100, 0, STR_NULL, NULL), /***************************************************************************/ /* New Pathfinding patch settings */ @@ -2142,3 +2145,4 @@ static bool IsSignedVarMemType(VarType vt) + diff --git a/src/settings_type.h b/src/settings_type.h index 4ec603d..1a1dc7e 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -236,6 +236,9 @@ struct Patches { bool enable_signal_gui; ///< Show the signal GUI when the signal button is pressed bool show_track_reservation; ///< Highlite reserved tracks. + 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. 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. diff --git a/src/train.h b/src/train.h index 6aa2ad7..4f0db8e 100644 --- a/src/train.h +++ b/src/train.h @@ -274,6 +274,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 15c4745..5b53a04 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -32,6 +32,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" @@ -49,10 +50,13 @@ #include "settings_type.h" #include "order_func.h" #include "newgrf_station.h" +#include "pbs.h" #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 TryPathReserve(Vehicle *v); static bool TrainCheckIfLineEnds(Vehicle *v); static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image); static TileIndex TrainApproachingCrossingTile(const Vehicle *v); @@ -1535,10 +1539,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); @@ -2036,14 +2057,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 (_patches.pathfinder_for_trains) { + uint8 pathfinder = _patches.pathfinder_for_trains; + if ((_patches.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 @@ -2072,12 +2096,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; } @@ -2227,6 +2251,42 @@ 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) +{ + /* 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; + + /* 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; + + 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 (_patches.pathfinder_for_trains != VPF_NTP && _patches.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 */ @@ -2275,6 +2335,85 @@ 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 (_patches.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)); + } +} + +/** 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; @@ -2340,25 +2479,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 = _patches.pathfinder_for_trains; + if (do_track_reservation && pathfinder == VPF_NTP) pathfinder = VPF_NPF; - switch (_patches.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 { @@ -2370,12 +2518,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); + + PBSTileInfo origin = FollowTrainReservation(v); + assert(IsValidTrackdir(origin.trackdir)); - NPFFoundTargetData ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_RAIL, 0, v->owner, v->u.rail.compatible_railtypes); + 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 @@ -2387,7 +2541,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); } @@ -2412,7 +2566,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 */ @@ -2426,6 +2580,115 @@ 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 (_patches.pathfinder_for_trains == VPF_YAPF) { + return YapfRailFindNearestSafeTile(v, tile, td, override_tailtype); + } else { + return NPFRouteToSafeTile(v, tile, td, override_tailtype).res_okay; + } +} + +/** + * 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) +{ + Track best_track = INVALID_TRACK; + /* pathfinders are able to tell that route was only 'guessed' */ + bool path_not_found = false; + bool do_track_reservation = _patches.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 */ @@ -2451,13 +2714,164 @@ 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; + + /* 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, _patches.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 (_patches.pathfinder_for_trains != VPF_NTP && _patches.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); return best_track; } +static bool IsEndOfLine(const Vehicle *v) +{ + CFollowTrackRailNo90 ft(v); + + ft.Follow(v->tile, GetVehicleTrackdir(v)); + if (ft.m_err == CFollowTrackRail::EC_NO_WAY || ft.m_err == CFollowTrackRail::EC_OWNER) return true; + + return false; +} + +/** + * Try to reserve a path to a safe position. + * + * @param v The vehicle + * @return True if a path could be reserved + */ +static bool TryPathReserve(Vehicle *v) +{ + assert(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)) { + 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. */ + 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 || IsEndOfLine(v))) { + /* 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 (_patches.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 (_patches.pathfinder_for_trains != VPF_NTP && _patches.forbid_90_deg) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir)); + + bool res_made = false; + ChooseTrainTrack(v, new_tile, exitdir, reachable, true, &res_made, false); + + 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) { @@ -2593,7 +3007,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 (_patches.realistic_acceleration) { accel = GetTrainAcceleration(v, AM_BRAKE) * 2; } else { @@ -2933,6 +3347,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 (_patches.show_track_reservation) MarkTileDirtyByTile(v->tile); + } if (v->current_order.IsType(OT_LEAVESTATION)) { v->current_order.Free(); @@ -2972,7 +3390,7 @@ 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)); + chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, false, NULL, true)); assert(chosen_track & bits); /* Check if it's a red signal and that force proceed is not clicked. */ @@ -2980,6 +3398,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; @@ -3032,6 +3453,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)) { @@ -3049,6 +3480,11 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir); v->direction = chosen_dir; + + if (IsFrontEngine(v)) { + /* Always try to extend the reservation when entering a tile. */ + CheckNextTrainTile(v); + } } } else { /* In a tunnel or on a bridge @@ -3059,7 +3495,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); @@ -3093,6 +3532,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 % _patches.path_backoff_interval == 0) CheckNextTrainTile(v); } return; @@ -3418,7 +3860,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) { @@ -3436,6 +3881,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; @@ -3452,6 +3898,38 @@ 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)) { + if (++(v->load_unload_time_rem) % _patches.path_backoff_interval != 0 && v->u.rail.force_proceed == 0) return; + if (!TryPathReserve(v)) { + /* Still stuck. */ + if (v->load_unload_time_rem % (_patches.wait_for_pbs_path * DAY_TICKS) == 0 && _patches.wait_for_pbs_path < 255) { + ReverseTrainDirection(v); + } + if (v->load_unload_time_rem > 2 * _patches.wait_for_pbs_path * DAY_TICKS) { + /* Show message to player. */ + if (_patches.lost_train_warn && v->owner == _local_player) { + SetDParam(0, v->unitnumber); + AddNewsItem( + STR_TRAIN_IS_STUCK, + NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, + 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' */ diff --git a/src/vehicle.cpp b/src/vehicle.cpp index a4515f9..fe1b351 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2153,7 +2153,7 @@ CommandCost SendAllVehiclesToDepot(VehicleType type, uint32 flags, bool service, * @param color The string to show depending on if we are unloading or loading * @return A percentage of how full the Vehicle is. */ -uint8 CalcPercentVehicleFilled(Vehicle *v, StringID *color) +uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *color) { int count = 0; int max = 0; @@ -2893,7 +2893,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, 92), + SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_UINT16, 93, 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 806dc2d..25e32a7 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -111,7 +111,7 @@ struct VehicleRail { RailTypeByte railtype; RailTypes compatible_railtypes; - byte flags; + uint16 flags; /* Link between the two ends of a multiheaded engine */ Vehicle *other_multiheaded_part; @@ -141,6 +141,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_func.h b/src/vehicle_func.h index 2d44eb6..43346b5 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -30,7 +30,7 @@ void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc); void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc); void CallVehicleTicks(); Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z); -uint8 CalcPercentVehicleFilled(Vehicle *v, StringID *color); +uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *color); void InitializeTrains(); byte VehicleRandomBits(); diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 55f824a..91e5f68 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1977,6 +1977,8 @@ static void DrawVehicleViewWindow(Window *w) } 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.5 From e3f061641c64ff16d3fb680c86b9a76bacfa8d70 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 | 168 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 142 insertions(+), 26 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 5b53a04..e0a8fde 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2588,6 +2588,108 @@ 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) +{ + 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 (_patches.pathfinder_for_trains != VPF_NTP && _patches.forbid_90_deg) { + 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(ft.m_new_td_bits))) 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, _patches.pathfinder_for_trains != VPF_NTP && _patches.forbid_90_deg)) { + bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _patches.pathfinder_for_trains != VPF_NTP && _patches.forbid_90_deg); + 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 (_patches.pathfinder_for_trains != VPF_NTP && _patches.forbid_90_deg) { + 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. * @@ -2654,8 +2756,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 = _patches.reserve_paths || force_res; bool changed_signal = false; @@ -2682,35 +2782,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 (_patches.lost_train_warn && v->owner == _local_player) { - SetDParam(0, v->unitnumber); - AddNewsItem( - STR_TRAIN_IS_LOST, - NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, - 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 (_patches.lost_train_warn && v->owner == _local_player) { + SetDParam(0, v->unitnumber); + AddNewsItem( + STR_TRAIN_IS_LOST, + NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, + 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.5 From d3a9287fba71847248ced722bcfae053459fe008 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 e0a8fde..e0c0988 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3534,6 +3534,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 (_patches.wait_for_pbs_path == 255 && UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) { + MarkTrainAsStuck(v); + return; + } goto reverse_train_direction; } } else { -- 1.5.5 From ea154628d55981f894cb5f623c555ac27736e355 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 | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 0 deletions(-) diff --git a/src/map_func.h b/src/map_func.h index 9e2a5d7..7a89a7d 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 e0c0988..49311e9 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3310,6 +3310,15 @@ 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); + Vehicle *u = v; + BEGIN_ENUM_WAGONS(u) + TryReserveRailTrack(u->tile, TrackdirToTrack(GetVehicleTrackdir(u))); + END_ENUM_WAGONS(u) + } + /* we may need to update crossing we were approaching */ TileIndex crossing = TrainApproachingCrossingTile(v); @@ -3708,6 +3717,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; -- 1.5.5 From bc186d3da8207632e492e642336f528a52ceae84 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 | 16 ++++++++++------ src/signal_func.h | 9 ++++++++- src/train_cmd.cpp | 20 +++++++++++++++++++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/signal.cpp b/src/signal.cpp index ef5b6e3..621a7ee 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -473,15 +473,15 @@ static inline void ResetSets() * Updates blocks in _globset buffer * * @param owner player whose signals we are updating - * @return false iff presignal entry would be green (needed for trains leaving depot) + * @return state of the first block from _globset * @pre IsValidPlayer(owner) */ -static bool UpdateSignalsInBuffer(Owner owner) +static SigSegState UpdateSignalsInBuffer(Owner owner) { assert(IsValidPlayer(owner)); bool first = true; // first block? - bool state = false; // value to return + SigSegState state = SIGSEG_FREE; // value to return TileIndex tile; DiagDirection dir; @@ -540,7 +540,11 @@ static bool UpdateSignalsInBuffer(Owner owner) if (first) { first = false; - state = (flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL); // true iff train CAN'T leave the depot + if (flags & SF_PBS) { + state = SIGSEG_PBS; + } else if (flags & SF_TRAIN || (flags & SF_EXIT && !(flags & SF_GREEN)) || flags & SF_FULL) { + state = SIGSEG_FULL; + } } /* do not do anything when some buffer was full */ @@ -635,9 +639,9 @@ void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner) * @param tile tile where we start * @param side side of tile * @param owner owner whose signals we will update - * @return false iff train can leave depot + * @return the state of the signal segment */ -bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner) +SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner) { assert(_globset.IsEmpty()); _globset.Add(tile, side); diff --git a/src/signal_func.h b/src/signal_func.h index a5479a3..371996b 100644 --- a/src/signal_func.h +++ b/src/signal_func.h @@ -41,7 +41,14 @@ static inline byte SignalOnTrack(Track track) return _signal_on_track[track]; } -bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner); +/** State of the signal segment */ +enum SigSegState { + SIGSEG_FREE, ///< Free and has no pre-signal exits or at least one green exit + SIGSEG_FULL, ///< Occupied by a train + SIGSEG_PBS ///< Segment is a PBS segment +}; + +SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner); void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner); void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner); void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 49311e9..378bbb9 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2301,7 +2301,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; @@ -2309,12 +2312,27 @@ static bool CheckTrainStayInDepot(Vehicle *v) v->load_unload_time_rem = 0; - if (UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner)) { + seg_state = _patches.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 = _patches.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 (_patches.show_track_reservation) MarkTileDirtyByTile(v->tile); + VehicleServiceInDepot(v); InvalidateWindowClasses(WC_TRAINS_LIST); v->PlayLeaveStationSound(); -- 1.5.5 From 9a5c5b1c177e2734dbbcf17682c19e9e2fd9a1a0 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 | 43 ++++++++++++++++++++++++++++++------------- 1 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 378bbb9..35e92e9 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -56,10 +56,11 @@ #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 TryPathReserve(Vehicle *v); +static bool TryPathReserve(Vehicle *v, bool first_tile_okay = false); 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}; @@ -1814,6 +1815,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); @@ -1850,6 +1854,29 @@ 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 || _patches.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, first_tile_okay)) { + MarkTrainAsStuck(v); + } else { + /* Do a look-ahead now in case our current tile was already a safe tile. */ + CheckNextTrainTile(v); + } + } } /** Reverse train. @@ -2927,23 +2954,13 @@ static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir return best_track; } -static bool IsEndOfLine(const Vehicle *v) -{ - CFollowTrackRailNo90 ft(v); - - ft.Follow(v->tile, GetVehicleTrackdir(v)); - if (ft.m_err == CFollowTrackRail::EC_NO_WAY || ft.m_err == CFollowTrackRail::EC_OWNER) return true; - - return false; -} - /** * Try to reserve a path to a safe position. * * @param v The vehicle * @return True if a path could be reserved */ -static bool TryPathReserve(Vehicle *v) +static bool TryPathReserve(Vehicle *v, bool first_tile_okay) { assert(IsFrontEngine(v)); @@ -2975,7 +2992,7 @@ static bool TryPathReserve(Vehicle *v) 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 || IsEndOfLine(v))) { + 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; -- 1.5.5 From 0f380a5dda45baa5c1bf5cec214c013f2b561d43 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 35e92e9..9612f80 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3330,14 +3330,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; } @@ -3690,7 +3694,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.5 From 13fa61cc94d4548c80a8b3c6ec77ff013e18191d 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 9612f80..e87a073 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -60,6 +60,7 @@ static bool TryPathReserve(Vehicle *v, bool first_tile_okay = false); 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}; @@ -2830,7 +2831,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.5 From 56300afd91b723956a6c75c565596cdbd610242a Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 31 Jan 2008 23:44:21 +0100 Subject: Protect a reserved track from being removed or overbuilt. --- src/lang/english.txt | 2 ++ src/rail_cmd.cpp | 10 +++++++--- src/rail_gui.cpp | 2 +- src/station_cmd.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 4e62d44..f1ef159 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2900,6 +2900,8 @@ STR_TIMETABLE_TOTAL_TIME_INCOMPLETE :This timetable STR_TIMETABLE_AUTOFILL :{BLACK}Autofill STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}Fill the timetable automatically with the values from the first journey +STR_TRAIN_IS_APPROACHING :{WHITE}Train is approaching + ##id 0x9000 STR_9000_ROAD_VEHICLE_IN_THE_WAY :{WHITE}Road vehicle in the way STR_9001_ROAD_VEHICLES :{WHITE}{COMPANY} - {COMMA} Road Vehicle{P "" s} diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 9240787..ae8e372 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -43,6 +43,7 @@ #include "tunnelbridge.h" #include "station_map.h" #include "water_map.h" +#include "pbs.h" #include "table/sprites.h" #include "table/strings.h" @@ -436,7 +437,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p /** Remove a single piece of track * @param tile tile to remove track from * @param flags operation to perform - * @param p1 unused + * @param p1 force remove even if track is reserved * @param p2 rail orientation */ CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) @@ -449,6 +450,8 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 if (!ValParamTrackOrientation((Track)p2)) return CMD_ERROR; trackbit = TrackToTrackBits(track); + if (p1 == 0 && HasReservedTracks(tile, trackbit)) return_cmd_error(STR_TRAIN_IS_APPROACHING); + /* Need to read tile owner now because it may change when the rail is removed * Also, in case of floods, _current_player != owner * There may be invalid tiletype even in exec run (when removing long track), @@ -671,7 +674,7 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, uint32 flags, uint32 p1, u if (flags & DC_EXEC) SndPlayTileFx(SND_20_SPLAT_2, tile); for (;;) { - ret = DoCommand(tile, railtype, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL); + ret = DoCommand(tile, (mode == 0) ? railtype : 0, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL); if (CmdFailed(ret)) { if ((_error_message != STR_1007_ALREADY_BUILT) && (mode == 0)) break; @@ -1404,7 +1407,8 @@ static CommandCost ClearTile_Track(TileIndex tile, byte flags) TrackBits tracks = GetTrackBits(tile); while (tracks != TRACK_BIT_NONE) { Track track = RemoveFirstTrack(&tracks); - ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL); + /* NOTE: Force removal of reserved tracks, maybe remove if no bugs found. */ + ret = DoCommand(tile, 1, track, flags, CMD_REMOVE_SINGLE_RAIL); if (CmdFailed(ret)) return CMD_ERROR; cost.AddCost(ret); } diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 67de72b..955f3c1 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -79,7 +79,7 @@ void CcPlaySound1E(bool success, TileIndex tile, uint32 p1, uint32 p2) static void GenericPlaceRail(TileIndex tile, int cmd) { - DoCommandP(tile, _cur_railtype, cmd, CcPlaySound1E, + DoCommandP(tile, _remove_button_clicked ? 0 : _cur_railtype, cmd, CcPlaySound1E, _remove_button_clicked ? CMD_REMOVE_SINGLE_RAIL | CMD_MSG(STR_1012_CAN_T_REMOVE_RAILROAD_TRACK) | CMD_NO_WATER : CMD_BUILD_SINGLE_RAIL | CMD_MSG(STR_1011_CAN_T_BUILD_RAILROAD_TRACK) | CMD_NO_WATER diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 79278eb..a0af0ca 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -42,6 +42,7 @@ #include "string_func.h" #include "signal_func.h" #include "oldpool_func.h" +#include #include "table/sprites.h" #include "table/strings.h" @@ -958,6 +959,20 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, if (st == CHECK_STATIONS_ERR) return CMD_ERROR; } + std::vector reserved_tiles; + if (_patches.nonuniform_stations) { + reserved_tiles.resize(w_org * h_org, false); + /* Overbuilding of existing station tiles is possible, check for reserved tiles. */ + BEGIN_TILE_LOOP(tile_cur, w_org, h_org, tile_org) { + if (IsRailwayStationTile(tile_cur)) { + bool reserved = GetRailwayStationReservation(tile_cur); + /* A reserved tile with the wrong axis can't be overbuilt. */ + if (reserved && axis != GetRailStationAxis(tile_cur)) return_cmd_error(STR_TRAIN_IS_APPROACHING); + reserved_tiles[(h_org-h_cur)*w_org + w_org-w_cur] = reserved; + } + } END_TILE_LOOP(tile_cur, w_org, h_org, tile_org) + } + /* See if there is a deleted station close to us. */ if (st == NULL) st = GetClosestStationFromTile(tile_org); @@ -1052,6 +1067,7 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, numtracks_orig = numtracks; + TileIndex start_tile = tile_org; do { TileIndex tile = tile_org; int w = plat_len; @@ -1061,6 +1077,11 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, SetCustomStationSpecIndex(tile, specindex); SetStationTileRandomBits(tile, GB(Random(), 0, 4)); SetStationAnimationFrame(tile, 0); + if (_patches.nonuniform_stations) { + /* Restore saved reservation state. */ + TileIndexDiffC diff = TileIndexToTileIndexDiffC(tile, start_tile); + SetRailwayStationReservation(tile, reserved_tiles[diff.y*w_org + diff.x]); + } if (statspec != NULL) { /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */ @@ -1185,6 +1206,9 @@ CommandCost CmdRemoveFromRailroadStation(TileIndex tile, uint32 flags, uint32 p1 continue; } + /* Don't remove tile if it is reserved. */ + if (GetRailwayStationReservation(tile2)) continue; + /* If there is a vehicle on ground, do not allow to remove (flood) the tile */ if (!EnsureNoVehicleOnGround(tile2)) { continue; -- 1.5.5 From a5e279489a28612c2b538f123b5c250f9d145938 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 318fca0..fae2818 100755 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -99,6 +99,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; @@ -142,7 +144,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 e87a073..7d6c79e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1695,8 +1695,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) { @@ -3658,6 +3658,10 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image) v->direction = chosen_dir; if (IsFrontEngine(v)) { + /* 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.5 From 3f2241754850c4c2dfdb1c6b02f39b713611f865 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 220da1e..1550d18 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -22,6 +22,7 @@ #include "gfx_func.h" #include "date_func.h" #include "player_func.h" +#include "rail_map.h" #include "table/sprites.h" #include "table/strings.h" @@ -394,7 +395,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 fae2818..e6b82e1 100755 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -66,7 +66,7 @@ void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bo do { SetRailwayStationReservation(tile, b); - if (_debug_pbs_level >= 2) MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile); tile = TILE_ADD(tile, diff); } while (IsCompatibleTrainStationTile(tile, start)); } @@ -91,6 +91,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t) if (IsRailWaypoint(tile) || IsRailDepot(tile)) { if (!GetWaypointReservation(tile)) { SetWaypointReservation(tile, true); + MarkTileDirtyByTile(tile); return true; } } @@ -108,6 +109,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; @@ -151,7 +156,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.5 From 6877a7b4607196bf5baa7a082e21a452c0ec74cd 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 | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index fe0979e..1062c02 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1594,9 +1594,9 @@ const SettingDesc _patch_settings[] = { SDT_CONDVAR (Patches, 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 (Patches, yapf.rail_lastred_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 10 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, 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 (Patches, yapf.rail_station_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 30 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR (Patches, yapf.rail_station_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 5 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_slope_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 2 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), - SDT_CONDVAR (Patches, yapf.rail_curve45_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), + SDT_CONDVAR (Patches, yapf.rail_curve45_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 2 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_curve90_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 6 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), /* This penalty is applied when a train reverses inside a depot */ SDT_CONDVAR (Patches, yapf.rail_depot_reverse_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 50 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), @@ -1615,7 +1615,7 @@ const SettingDesc _patch_settings[] = { /* penalties for too long or too short station platforms */ SDT_CONDVAR (Patches, yapf.rail_longer_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 8 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_longer_platform_per_tile_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 0 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), - SDT_CONDVAR (Patches, yapf.rail_shorter_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 40 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), + SDT_CONDVAR (Patches, yapf.rail_shorter_platform_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 80 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), SDT_CONDVAR (Patches, yapf.rail_shorter_platform_per_tile_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 0 * YAPF_TILE_LENGTH, 0, 20000, 0, STR_NULL, NULL), /* road vehicles - penalties */ SDT_CONDVAR (Patches, yapf.road_slope_penalty , SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 2 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL), -- 1.5.5