Changeset - r26494:22737b47738b
[Not reviewed]
master
0 10 0
Tyler Trahan - 18 months ago 2022-11-01 20:51:23
tyler@tylertrahan.com
Feature: Multi-track level crossings (#9931)
10 files changed with 219 insertions and 24 deletions:
0 comments (0 inline, 0 general)
src/pbs.cpp
Show inline comments
 
@@ -105,8 +105,7 @@ bool TryReserveRailTrack(TileIndex tile,
 
		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;
src/rail_cmd.cpp
Show inline comments
 
@@ -548,6 +548,7 @@ CommandCost CmdBuildSingleRail(DoCommand
 
					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(DoComman
 
			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);
src/road_cmd.cpp
Show inline comments
 
@@ -380,6 +380,8 @@ static CommandCost RemoveRoad(TileIndex 
 
			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 f
 
				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_Ro
 
					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;
 
				}
 

	
src/road_func.h
Show inline comments
 
@@ -153,7 +153,8 @@ RoadTypes GetCompanyRoadTypes(CompanyID 
 
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;
src/roadveh_cmd.cpp
Show inline comments
 
@@ -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()) {
src/roadveh_cmd.h
Show inline comments
 
@@ -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);
src/saveload/afterload.cpp
Show inline comments
 
@@ -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();
 

	
src/saveload/saveload.h
Show inline comments
 
@@ -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
 
};
src/table/road_land.h
Show inline comments
 
@@ -53,6 +53,46 @@ static const DrawTileSprites _crossing_l
 
	{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
 

	
src/train_cmd.cpp
Show inline comments
 
@@ -1674,29 +1674,88 @@ static bool TrainApproachingCrossing(Til
 
	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));
 

	
 
	/* reserved || train on crossing || train approaching crossing */
 
	bool new_state = HasCrossingReservation(tile) || HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum) || TrainApproachingCrossing(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);
 
	bool set_barred;
 

	
 
	/* 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);
 

	
 
	/* 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,
 
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);
 
	}
 
}
 

	
0 comments (0 inline, 0 general)