mirror of https://github.com/OpenTTD/OpenTTD.git
Feature: Multi-track level crossings (#9931)
This commit is contained in:
parent
c65a2799c9
commit
c19abebf8d
|
@ -105,8 +105,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
|
|||
case MP_ROAD:
|
||||
if (IsLevelCrossing(tile) && !HasCrossingReservation(tile)) {
|
||||
SetCrossingReservation(tile, true);
|
||||
BarCrossing(tile);
|
||||
MarkTileDirtyByTile(tile); // crossing barred, make tile dirty
|
||||
UpdateLevelCrossing(tile, false);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -548,6 +548,7 @@ CommandCost CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType rai
|
|||
if (flags & DC_EXEC) {
|
||||
MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
|
||||
UpdateLevelCrossing(tile, false);
|
||||
MarkDirtyAdjacentLevelCrossingTiles(tile, GetCrossingRoadAxis(tile));
|
||||
Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
|
||||
DirtyCompanyInfrastructureWindows(_current_company);
|
||||
if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
|
||||
|
@ -649,6 +650,8 @@ CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track
|
|||
cost.AddCost(RailClearCost(GetRailType(tile)));
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
MarkDirtyAdjacentLevelCrossingTiles(tile, GetCrossingRoadAxis(tile));
|
||||
|
||||
if (HasReservedTracks(tile, trackbit)) {
|
||||
v = GetTrainForReservation(tile, track);
|
||||
if (v != nullptr) FreeTrainTrackReservation(v);
|
||||
|
|
|
@ -380,6 +380,8 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
|
|||
uint len = GetTunnelBridgeLength(other_end, tile) + 2;
|
||||
cost.AddCost(len * 2 * RoadClearCost(existing_rt));
|
||||
if (flags & DC_EXEC) {
|
||||
MarkDirtyAdjacentLevelCrossingTiles(tile, GetCrossingRoadAxis(tile));
|
||||
|
||||
/* A full diagonal road tile has two road bits. */
|
||||
UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
|
||||
|
||||
|
@ -780,6 +782,7 @@ CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, R
|
|||
MakeRoadCrossing(tile, company, company, GetTileOwner(tile), roaddir, GetRailType(tile), rtt == RTT_ROAD ? rt : INVALID_ROADTYPE, (rtt == RTT_TRAM) ? rt : INVALID_ROADTYPE, town_id);
|
||||
SetCrossingReservation(tile, reserved);
|
||||
UpdateLevelCrossing(tile, false);
|
||||
MarkDirtyAdjacentLevelCrossingTiles(tile, GetCrossingRoadAxis(tile));
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
return CommandCost(EXPENSES_CONSTRUCTION, 2 * RoadBuildCost(rt));
|
||||
|
@ -1703,7 +1706,42 @@ static void DrawTile_Road(TileInfo *ti)
|
|||
SpriteID rail = GetCustomRailSprite(rti, ti->tile, RTSG_CROSSING) + axis;
|
||||
DrawGroundSprite(rail, pal);
|
||||
|
||||
DrawRailTileSeq(ti, &_crossing_layout, TO_CATENARY, rail, 0, PAL_NONE);
|
||||
const Axis road_axis = GetCrossingRoadAxis(ti->tile);
|
||||
const DiagDirection dir1 = AxisToDiagDir(road_axis);
|
||||
const DiagDirection dir2 = ReverseDiagDir(dir1);
|
||||
uint adjacent_diagdirs = 0;
|
||||
for (DiagDirection dir : { dir1, dir2 }) {
|
||||
const TileIndex t = TileAddByDiagDir(ti->tile, dir);
|
||||
if (t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
|
||||
SetBit(adjacent_diagdirs, dir);
|
||||
}
|
||||
}
|
||||
|
||||
switch (adjacent_diagdirs) {
|
||||
case 0:
|
||||
DrawRailTileSeq(ti, &_crossing_layout, TO_CATENARY, rail, 0, PAL_NONE);
|
||||
break;
|
||||
|
||||
case (1 << DIAGDIR_NE):
|
||||
DrawRailTileSeq(ti, &_crossing_layout_SW, TO_CATENARY, rail, 0, PAL_NONE);
|
||||
break;
|
||||
|
||||
case (1 << DIAGDIR_SE):
|
||||
DrawRailTileSeq(ti, &_crossing_layout_NW, TO_CATENARY, rail, 0, PAL_NONE);
|
||||
break;
|
||||
|
||||
case (1 << DIAGDIR_SW):
|
||||
DrawRailTileSeq(ti, &_crossing_layout_NE, TO_CATENARY, rail, 0, PAL_NONE);
|
||||
break;
|
||||
|
||||
case (1 << DIAGDIR_NW):
|
||||
DrawRailTileSeq(ti, &_crossing_layout_SE, TO_CATENARY, rail, 0, PAL_NONE);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Show no sprites */
|
||||
break;
|
||||
}
|
||||
} else if (draw_pbs || tram_rti != nullptr || road_rti->UsesOverlay()) {
|
||||
/* Add another rail overlay, unless there is only the base road sprite. */
|
||||
PaletteID pal = draw_pbs ? PALETTE_CRASH : PAL_NONE;
|
||||
|
@ -2033,7 +2071,16 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u
|
|||
if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
|
||||
|
||||
trackdirbits = TrackBitsToTrackdirBits(AxisToTrackBits(axis));
|
||||
if (IsCrossingBarred(tile)) red_signals = trackdirbits;
|
||||
if (IsCrossingBarred(tile)) {
|
||||
red_signals = trackdirbits;
|
||||
auto mask_red_signal_bits_if_crossing_barred = [&](TileIndex t, TrackdirBits mask) {
|
||||
if (IsLevelCrossingTile(t) && IsCrossingBarred(t)) red_signals &= mask;
|
||||
};
|
||||
/* Check for blocked adjacent crossing to south, keep only southbound red signal trackdirs, allow northbound traffic */
|
||||
mask_red_signal_bits_if_crossing_barred(TileAddByDiagDir(tile, AxisToDiagDir(axis)), TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_SE);
|
||||
/* Check for blocked adjacent crossing to north, keep only northbound red signal trackdirs, allow southbound traffic */
|
||||
mask_red_signal_bits_if_crossing_barred(TileAddByDiagDir(tile, ReverseDiagDir(AxisToDiagDir(axis))), TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_NW);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,8 @@ RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces = true);
|
|||
RoadTypes GetRoadTypes(bool introduces);
|
||||
RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date);
|
||||
|
||||
void UpdateLevelCrossing(TileIndex tile, bool sound = true);
|
||||
void UpdateLevelCrossing(TileIndex tile, bool sound = true, bool force_bar = false);
|
||||
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis);
|
||||
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count);
|
||||
|
||||
struct TileInfo;
|
||||
|
|
|
@ -1004,7 +1004,7 @@ struct RoadDriveEntry {
|
|||
|
||||
#include "table/roadveh_movement.h"
|
||||
|
||||
static bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
|
||||
bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
|
||||
{
|
||||
/* Don't leave unless v and following wagons are in the depot. */
|
||||
for (const RoadVehicle *u = v; u != nullptr; u = u->Next()) {
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "engine_type.h"
|
||||
#include "vehicle_type.h"
|
||||
|
||||
bool RoadVehLeaveDepot(RoadVehicle *v, bool first);
|
||||
|
||||
CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **v);
|
||||
|
||||
CommandCost CmdTurnRoadVeh(DoCommandFlag flags, VehicleID veh_id);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "../string_func.h"
|
||||
#include "../date_func.h"
|
||||
#include "../roadveh.h"
|
||||
#include "../roadveh_cmd.h"
|
||||
#include "../train.h"
|
||||
#include "../station_base.h"
|
||||
#include "../waypoint_base.h"
|
||||
|
@ -57,7 +58,6 @@
|
|||
#include "../ship.h"
|
||||
#include "../water.h"
|
||||
|
||||
|
||||
#include "saveload_internal.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
@ -3155,6 +3155,50 @@ bool AfterLoadGame()
|
|||
}
|
||||
}
|
||||
|
||||
/* Road vehicles stopped on multitrack level crossings need teleporting to a depot
|
||||
* to avoid crashing into the side of the train they're waiting for. */
|
||||
if (IsSavegameVersionBefore(SLV_MULTITRACK_LEVEL_CROSSINGS)) {
|
||||
/* Teleport road vehicles to the nearest depot. */
|
||||
for (RoadVehicle *rv : RoadVehicle::Iterate()) {
|
||||
/* Ignore trailers of articulated vehicles. */
|
||||
if (rv->IsArticulatedPart()) continue;
|
||||
|
||||
/* Ignore moving vehicles. */
|
||||
if (rv->cur_speed > 0) continue;
|
||||
|
||||
/* Ignore vehicles not on level crossings. */
|
||||
TileIndex cur_tile = rv->tile;
|
||||
if (!IsLevelCrossingTile(cur_tile)) continue;
|
||||
|
||||
TileIndex location;
|
||||
DestinationID destination;
|
||||
bool reverse = true;
|
||||
|
||||
/* Try to find a depot with a distance limit of 512 tiles (Manhattan distance). */
|
||||
if (rv->FindClosestDepot(&location, &destination, &reverse) && DistanceManhattan(rv->tile, location) < 512u) {
|
||||
/* Teleport all parts of articulated vehicles. */
|
||||
for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) {
|
||||
u->tile = location;
|
||||
int x = TileX(location) * TILE_SIZE + TILE_SIZE / 2;
|
||||
int y = TileY(location) * TILE_SIZE + TILE_SIZE / 2;
|
||||
u->x_pos = x;
|
||||
u->y_pos = y;
|
||||
u->z_pos = GetSlopePixelZ(x, y);
|
||||
|
||||
u->vehstatus |= VS_HIDDEN;
|
||||
u->state = RVSB_IN_DEPOT;
|
||||
u->UpdatePosition();
|
||||
}
|
||||
RoadVehLeaveDepot(rv, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Refresh all level crossings to bar adjacent crossing tiles. */
|
||||
for (TileIndex tile = 0; tile < MapSize(); tile++) {
|
||||
if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */
|
||||
Station::RecomputeCatchmentForAll();
|
||||
|
||||
|
|
|
@ -342,6 +342,7 @@ enum SaveLoadVersion : uint16 {
|
|||
SLV_REPAIR_OBJECT_DOCKING_TILES, ///< 299 PR#9594 v12.0 Fixing issue with docking tiles overlapping objects.
|
||||
SLV_U64_TICK_COUNTER, ///< 300 PR#10035 Make _tick_counter 64bit to avoid wrapping.
|
||||
SLV_LAST_LOADING_TICK, ///< 301 PR#9693 Store tick of last loading for vehicles.
|
||||
SLV_MULTITRACK_LEVEL_CROSSINGS, ///< 302 PR#9931 Multi-track level crossings.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
|
|
@ -53,6 +53,46 @@ static const DrawTileSprites _crossing_layout = {
|
|||
{0, PAL_NONE}, _crossing_layout_ALL
|
||||
};
|
||||
|
||||
static const DrawTileSeqStruct _crossing_layout_SW_ALL[] = {
|
||||
TILE_SEQ_LINE(6, PAL_NONE, 13, 0, 3, 3)
|
||||
TILE_SEQ_LINE(8, PAL_NONE, 13, 13, 3, 3)
|
||||
TILE_SEQ_END()
|
||||
};
|
||||
|
||||
static const DrawTileSprites _crossing_layout_SW = {
|
||||
{0, PAL_NONE}, _crossing_layout_SW_ALL
|
||||
};
|
||||
|
||||
static const DrawTileSeqStruct _crossing_layout_NW_ALL[] = {
|
||||
TILE_SEQ_LINE(2, PAL_NONE, 0, 0, 3, 3)
|
||||
TILE_SEQ_LINE(6, PAL_NONE, 13, 0, 3, 3)
|
||||
TILE_SEQ_END()
|
||||
};
|
||||
|
||||
static const DrawTileSprites _crossing_layout_NW = {
|
||||
{0, PAL_NONE}, _crossing_layout_NW_ALL
|
||||
};
|
||||
|
||||
static const DrawTileSeqStruct _crossing_layout_NE_ALL[] = {
|
||||
TILE_SEQ_LINE(2, PAL_NONE, 0, 0, 3, 3)
|
||||
TILE_SEQ_LINE(4, PAL_NONE, 0, 13, 3, 3)
|
||||
TILE_SEQ_END()
|
||||
};
|
||||
|
||||
static const DrawTileSprites _crossing_layout_NE = {
|
||||
{0, PAL_NONE}, _crossing_layout_NE_ALL
|
||||
};
|
||||
|
||||
static const DrawTileSeqStruct _crossing_layout_SE_ALL[] = {
|
||||
TILE_SEQ_LINE(4, PAL_NONE, 0, 13, 3, 3)
|
||||
TILE_SEQ_LINE(8, PAL_NONE, 13, 13, 3, 3)
|
||||
TILE_SEQ_END()
|
||||
};
|
||||
|
||||
static const DrawTileSprites _crossing_layout_SE = {
|
||||
{0, PAL_NONE}, _crossing_layout_SE_ALL
|
||||
};
|
||||
|
||||
#undef TILE_SEQ_LINE
|
||||
#undef TILE_SEQ_END
|
||||
|
||||
|
|
|
@ -1674,29 +1674,88 @@ static bool TrainApproachingCrossing(TileIndex tile)
|
|||
return HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a level crossing should be barred.
|
||||
* @param tile The tile to check.
|
||||
* @return True if the crossing should be barred, else false.
|
||||
*/
|
||||
static inline bool CheckLevelCrossing(TileIndex tile)
|
||||
{
|
||||
/* reserved || train on crossing || train approaching crossing */
|
||||
return HasCrossingReservation(tile) || HasVehicleOnPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets correct crossing state
|
||||
* @param tile tile to update
|
||||
* @param sound should we play sound?
|
||||
* @pre tile is a rail-road crossing
|
||||
* Sets a level crossing tile to the correct state.
|
||||
* @param tile Tile to update.
|
||||
* @param sound Should we play sound?
|
||||
* @param force_barred Should we set the crossing to barred?
|
||||
* @pre tile is a rail-road crossing.
|
||||
*/
|
||||
void UpdateLevelCrossing(TileIndex tile, bool sound)
|
||||
static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
|
||||
{
|
||||
assert(IsLevelCrossingTile(tile));
|
||||
bool set_barred;
|
||||
|
||||
/* reserved || train on crossing || train approaching crossing */
|
||||
bool new_state = HasCrossingReservation(tile) || HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum) || TrainApproachingCrossing(tile);
|
||||
/* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
|
||||
set_barred = force_barred || CheckLevelCrossing(tile);
|
||||
|
||||
if (new_state != IsCrossingBarred(tile)) {
|
||||
if (new_state && sound) {
|
||||
if (_settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
|
||||
}
|
||||
SetCrossingBarred(tile, new_state);
|
||||
/* The state has changed */
|
||||
if (set_barred != IsCrossingBarred(tile)) {
|
||||
if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
|
||||
SetCrossingBarred(tile, set_barred);
|
||||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
|
||||
* @param tile Tile which causes the update.
|
||||
* @param sound Should we play sound?
|
||||
* @param force_bar Should we force the crossing to be barred?
|
||||
*/
|
||||
void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
|
||||
{
|
||||
if (!IsLevelCrossingTile(tile)) return;
|
||||
|
||||
bool forced_state = force_bar;
|
||||
|
||||
const Axis axis = GetCrossingRoadAxis(tile);
|
||||
const DiagDirection dir1 = AxisToDiagDir(axis);
|
||||
const DiagDirection dir2 = ReverseDiagDir(dir1);
|
||||
|
||||
/* Check if an adjacent crossing is barred. */
|
||||
for (DiagDirection dir : { dir1, dir2 }) {
|
||||
for (TileIndex t = tile; !forced_state && t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
|
||||
forced_state |= CheckLevelCrossing(t);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that we know whether all tiles in this crossing should be barred or open,
|
||||
* we need to update those tiles. We start with the tile itself, then look along the road axis. */
|
||||
UpdateLevelCrossingTile(tile, sound, forced_state);
|
||||
for (DiagDirection dir : { dir1, dir2 }) {
|
||||
for (TileIndex t = TileAddByDiagDir(tile, dir); t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
|
||||
UpdateLevelCrossingTile(t, sound, forced_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find adjacent level crossing tiles in this multi-track crossing and mark them dirty.
|
||||
* @param The tile which causes the update.
|
||||
*/
|
||||
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis)
|
||||
{
|
||||
const DiagDirection dir1 = AxisToDiagDir(road_axis);
|
||||
const DiagDirection dir2 = ReverseDiagDir(dir1);
|
||||
for (DiagDirection dir : { dir1, dir2 }) {
|
||||
const TileIndex t = TileAddByDiagDir(tile, dir);
|
||||
if (t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
|
||||
MarkTileDirtyByTile(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bars crossing and plays ding-ding sound if not barred already
|
||||
|
@ -1706,9 +1765,8 @@ void UpdateLevelCrossing(TileIndex tile, bool sound)
|
|||
static inline void MaybeBarCrossingWithSound(TileIndex tile)
|
||||
{
|
||||
if (!IsCrossingBarred(tile)) {
|
||||
BarCrossing(tile);
|
||||
if (_settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
|
||||
MarkTileDirtyByTile(tile);
|
||||
SetCrossingReservation(tile, true);
|
||||
UpdateLevelCrossing(tile, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue