Merge remote-tracking branch 'upstream/master' into 13.0

This commit is contained in:
Pavel Stupnikov
2022-11-26 22:16:25 +04:00
508 changed files with 14617 additions and 9750 deletions

View File

@@ -8,7 +8,6 @@
/** @file rail_cmd.cpp Handling of rail tiles. */
#include "stdafx.h"
#include "cmd_helper.h"
#include "viewport_func.h"
#include "command_func.h"
#include "depot_base.h"
@@ -31,6 +30,8 @@
#include "strings_func.h"
#include "company_gui.h"
#include "object_map.h"
#include "rail_cmd.h"
#include "landscape_cmd.h"
#include "table/strings.h"
#include "table/railtypes.h"
@@ -427,20 +428,15 @@ static inline bool ValParamTrackOrientation(Track track)
/**
* Build a single piece of rail
* @param tile tile to build on
* @param flags operation to perform
* @param p1 railtype of being built piece (normal, mono, maglev)
* @param p2 various bitstuffed elements
* - (bit 0- 2) - track-orientation, valid values: 0-5 (@see Track)
* - (bit 3) - 0 = error on signal in the way, 1 = auto remove signals when in the way
* @param text unused
* @param tile tile to build on
* @param railtype railtype of being built piece (normal, mono, maglev)
* @param track track-orientation
* @param auto_remove_signals false = error on signal in the way, true = auto remove signals when in the way
* @return the cost of this operation or an error
*/
CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
{
RailType railtype = Extract<RailType, 0, 6>(p1);
Track track = Extract<Track, 0, 3>(p2);
bool auto_remove_signals = HasBit(p2, 3);
CommandCost cost(EXPENSES_CONSTRUCTION);
_rail_track_endtile = INVALID_TILE;
@@ -455,7 +451,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret;
if (!IsPlainRail(tile)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); // just get appropriate error message
if (!IsPlainRail(tile)) return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile); // just get appropriate error message
if (!IsCompatibleRail(GetRailType(tile), railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
@@ -476,7 +472,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
for (Track track_it = TRACK_BEGIN; track_it < TRACK_END; track_it++) {
if (HasTrack(tile, track_it) && HasSignalOnTrack(tile, track_it)) {
CommandCost ret_remove_signals = DoCommand(tile, track_it, 0, flags, CMD_REMOVE_SIGNALS);
CommandCost ret_remove_signals = Command<CMD_REMOVE_SIGNALS>::Do(flags, tile, track_it);
if (ret_remove_signals.Failed()) return ret_remove_signals;
cost.AddCost(ret_remove_signals);
}
@@ -488,7 +484,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
* the present rail type are powered on the new rail type. */
if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
if (HasPowerOnRail(GetRailType(tile), railtype)) {
ret = DoCommand(tile, tile, railtype, flags, CMD_CONVERT_RAIL);
ret = Command<CMD_CONVERT_RAIL>::Do(flags, tile, tile, railtype, false);
if (ret.Failed()) return ret;
cost.AddCost(ret);
} else {
@@ -589,7 +585,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (ret.Failed()) return ret;
cost.AddCost(ret);
ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
if (ret.Failed()) return ret;
cost.AddCost(ret);
@@ -624,16 +620,13 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
/**
* Remove a single piece of track
* @param tile tile to remove track from
* @param flags operation to perform
* @param p1 unused
* @param p2 rail orientation
* @param text unused
* @param tile tile to remove track from
* @param track rail orientation
* @return the cost of this operation or an error
*/
CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track)
{
Track track = Extract<Track, 0, 3>(p2);
CommandCost cost(EXPENSES_CONSTRUCTION);
bool crossing = false;
@@ -702,7 +695,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
/* Charge extra to remove signals on the track, if they are there */
if (HasSignalOnTrack(tile, track)) {
cost.AddCost(DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS));
cost.AddCost(Command<CMD_REMOVE_SIGNALS>::Do(flags, tile, track));
}
if (flags & DC_EXEC) {
@@ -796,7 +789,7 @@ bool FloodHalftile(TileIndex t)
TrackBits to_remove = lower_track & rail_bits;
if (to_remove != 0) {
Backup<CompanyID> cur_company(_current_company, OWNER_WATER, FILE_LINE);
flooded = DoCommand(t, 0, FIND_FIRST_BIT(to_remove), DC_EXEC, CMD_REMOVE_SINGLE_RAIL).Succeeded();
flooded = Command<CMD_REMOVE_SINGLE_RAIL>::Do(DC_EXEC, t, FindFirstTrack(to_remove)).Succeeded();
cur_company.Restore();
if (!flooded) return flooded; // not yet floodable
rail_bits = rail_bits & ~to_remove;
@@ -884,31 +877,25 @@ static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileInd
/**
* Build or remove a stretch of railroad tracks.
* @param tile start tile of drag
* @param flags operation to perform
* @param p1 end tile of drag
* @param p2 various bitstuffed elements
* - p2 = (bit 0-5) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev), only used for building
* - p2 = (bit 6-8) - track-orientation, valid values: 0-5 (Track enum)
* - p2 = (bit 9) - 0 = build, 1 = remove tracks
* - p2 = (bit 10) - 0 = build up to an obstacle, 1 = fail if an obstacle is found (used for AIs).
* - p2 = (bit 11) - 0 = error on signal in the way, 1 = auto remove signals when in the way
* @param text unused
* @param tile start tile of drag
* @param end_tile end tile of drag
* @param railtype railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev), only used for building
* @param track track-orientation
* @param remove remove tracks?
* @param auto_remove_signals false = build up to an obstacle, true = fail if an obstacle is found (used for AIs), only used for building
* @param fail_on_obstacle false = error on signal in the way, true = auto remove signals when in the way, only used for building
* @return the cost of this operation or an error
*/
static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
static CommandCost CmdRailTrackHelper(DoCommandFlag flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
{
CommandCost total_cost(EXPENSES_CONSTRUCTION);
Track track = Extract<Track, 6, 3>(p2);
bool remove = HasBit(p2, 9);
bool auto_remove_signals = HasBit(p2, 11);
RailType railtype = Extract<RailType, 0, 6>(p2);
_rail_track_endtile = INVALID_TILE;
if ((!remove && !ValParamRailtype(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
if (p1 >= MapSize()) return CMD_ERROR;
TileIndex end_tile = p1;
if (end_tile >= MapSize() || tile >= MapSize()) return CMD_ERROR;
Trackdir trackdir = TrackToTrackdir(track);
CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
@@ -918,13 +905,13 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint3
CommandCost last_error = CMD_ERROR;
for (;;) {
TileIndex last_endtile = _rail_track_endtile;
CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir) | (auto_remove_signals << 3), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
CommandCost ret = remove ? Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<CMD_BUILD_SINGLE_RAIL>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
if (ret.Failed()) {
last_error = ret;
if (_rail_track_endtile == INVALID_TILE) _rail_track_endtile = last_endtile;
if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
if (HasBit(p2, 10)) return last_error;
if (fail_on_obstacle) return last_error;
break;
}
@@ -950,63 +937,54 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint3
/**
* Build rail on a stretch of track.
* Stub for the unified rail builder/remover
* @param tile start tile of drag
* @param flags operation to perform
* @param p1 end tile of drag
* @param p2 various bitstuffed elements
* - p2 = (bit 0-5) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
* - p2 = (bit 6-8) - track-orientation, valid values: 0-5 (Track enum)
* - p2 = (bit 9) - 0 = build, 1 = remove tracks
* @param text unused
* @return the cost of this operation or an error
* @param end_tile end tile of drag
* @param start_tile start tile of drag
* @param railtype railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev), only used for building
* @param track track-orientation
* @param auto_remove_signals false = build up to an obstacle, true = fail if an obstacle is found (used for AIs).
* @param fail_on_obstacle false = error on signal in the way, true = auto remove signals when in the way
* @see CmdRailTrackHelper
*/
CommandCost CmdBuildRailroadTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdBuildRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
{
return CmdRailTrackHelper(tile, flags, p1, ClrBit(p2, 9), text);
return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
}
/**
* Build rail on a stretch of track.
* Stub for the unified rail builder/remover
* @param tile start tile of drag
* @param flags operation to perform
* @param p1 end tile of drag
* @param p2 various bitstuffed elements
* - p2 = (bit 0-5) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev), only used for building
* - p2 = (bit 6-8) - track-orientation, valid values: 0-5 (Track enum)
* - p2 = (bit 9) - 0 = build, 1 = remove tracks
* @param text unused
* @param end_tile end tile of drag
* @param start_tile start tile of drag
* @param track track-orientation
* @return the cost of this operation or an error
* @see CmdRailTrackHelper
*/
CommandCost CmdRemoveRailroadTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, Track track)
{
return CmdRailTrackHelper(tile, flags, p1, SetBit(p2, 9), text);
return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
}
/**
* Build a train depot
* @param tile position of the train depot
* @param flags operation to perform
* @param p1 rail type
* @param p2 bit 0..1 entrance direction (DiagDirection)
* @param text unused
* @param tile position of the train depot
* @param railtype rail type
* @param dir entrance direction
* @return the cost of this operation or an error
*
* @todo When checking for the tile slope,
* distinguish between "Flat land required" and "land sloped in wrong direction"
*/
CommandCost CmdBuildTrainDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir)
{
/* check railtype and valid direction for depot (0 through 3), 4 in total */
RailType railtype = Extract<RailType, 0, 6>(p1);
if (!ValParamRailtype(railtype)) return CMD_ERROR;
if (!ValParamRailtype(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
Slope tileh = GetTileSlope(tile);
DiagDirection dir = Extract<DiagDirection, 0, 2>(p2);
CommandCost cost(EXPENSES_CONSTRUCTION);
/* Prohibit construction if
@@ -1023,7 +1001,7 @@ CommandCost CmdBuildTrainDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, u
cost.AddCost(_price[PR_BUILD_FOUNDATION]);
}
cost.AddCost(DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR));
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile));
if (cost.Failed()) return cost;
if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
@@ -1053,45 +1031,36 @@ CommandCost CmdBuildTrainDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, u
/**
* Build signals, alternate between double/single, signal/semaphore,
* pre/exit/combo-signals, and what-else not. If the rail piece does not
* have any signals, bit 4 (cycle signal-type) is ignored
* @param tile tile where to build the signals
* have any signals, signal cycling is ignored
* @param flags operation to perform
* @param p1 various bitstuffed elements
* - 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-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
* - p1 = (bit 15-16)-cycle the signal direction this many times
* - p1 = (bit 17) - 1 = don't modify an existing signal but don't fail either, 0 = always set new signal type
* @param p2 used for CmdBuildManySignals() to copy direction of first signal
* @param text unused
* @param tile tile where to build the signals
* @param track track-orientation
* @param sigtype type of the signal
* @param sigvar variant of signal type (normal/semaphore)
* @param ctrl_pressed true = override signal/semaphore, or pre/exit/combo signal or toggle variant (CTRL-toggle)
* @param convert_signal convert the present signal type and variant
* @param cycle_start start cycle from this signal type
* @param cycle_stop wrap around after this signal type
* @param num_dir_cycle cycle the signal direction this many times
* @param skip_existing_signals true = don't modify an existing signal but don't fail either, false = always set new signal type
* @param signals_copy used for CmdBuildManySignals() to copy direction of first signal
* @return the cost of this operation or an error
* @todo p2 should be replaced by two bits for "along" and "against" the track.
*/
CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdBuildSingleSignal(DoCommandFlag flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8 num_dir_cycle, byte signals_copy)
{
Track track = Extract<Track, 0, 3>(p1);
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 = Extract<SignalType, 5, 3>(p1); // the signal type of the new signal
bool convert_signal = HasBit(p1, 8); // convert button pressed
SignalType cycle_start = Extract<SignalType, 9, 3>(p1);
SignalType cycle_stop = Extract<SignalType, 12, 3>(p1);
uint num_dir_cycle = GB(p1, 15, 2);
if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
if (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE) return CMD_ERROR;
if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
if (ctrl_pressed) sigvar = (SignalVariant)(sigvar ^ SIG_SEMAPHORE);
/* You can only build signals on plain rail tiles, and the selected track must exist */
if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
!HasTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
/* Protect against invalid signal copying */
if (p2 != 0 && (p2 & SignalOnTrack(track)) == 0) return CMD_ERROR;
if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret;
@@ -1100,7 +1069,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
if (TracksOverlap(GetTrackBits(tile))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
/* In case we don't want to change an existing signal, return without error. */
if (HasBit(p1, 17) && HasSignalOnTrack(tile, track)) return CommandCost();
if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
/* you can not convert a signal if no signal is on track */
if (convert_signal && !HasSignalOnTrack(tile, track)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
@@ -1110,7 +1079,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
/* build new signals */
cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]);
} else {
if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) {
if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
/* convert signals <-> semaphores */
cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
@@ -1152,7 +1121,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
/* Subtract old signal infrastructure count. */
Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
if (p2 == 0) {
if (signals_copy == 0) {
if (!HasSignalOnTrack(tile, track)) {
/* build new signals */
SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
@@ -1196,7 +1165,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
} else {
/* If CmdBuildManySignals is called with copying signals, just copy the
* direction of the first signal given as parameter by CmdBuildManySignals */
SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track)));
SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
SetSignalVariant(tile, track, sigvar);
SetSignalType(tile, track, sigtype);
}
@@ -1225,8 +1194,11 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
return cost;
}
static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal_ctr, bool remove)
static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
{
/* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
if (IsTileType(tile, MP_TUNNELBRIDGE)) tile = GetOtherTunnelBridgeEnd(tile);
tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
if (tile == INVALID_TILE) return false;
@@ -1249,76 +1221,51 @@ static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal
case MP_RAILWAY:
if (IsRailDepot(tile)) return false;
if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
signal_ctr++;
if (IsDiagonalTrackdir(trackdir)) {
signal_ctr++;
/* Ensure signal_ctr even so X and Y pieces get signals */
ClrBit(signal_ctr, 0);
}
return true;
break;
case MP_ROAD:
if (!IsLevelCrossing(tile)) return false;
signal_ctr += 2;
return true;
break;
case MP_TUNNELBRIDGE: {
TileIndex orig_tile = tile; // backup old value
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
/* Skip to end of tunnel or bridge
* note that tile is a parameter by reference, so it must be updated */
tile = GetOtherTunnelBridgeEnd(tile);
signal_ctr += (GetTunnelBridgeLength(orig_tile, tile) + 2) * 2;
return true;
break;
}
default: return false;
}
return true;
}
/**
* Build many signals by dragging; AutoSignals
* @param tile start tile of drag
* @param flags operation to perform
* @param p1 end tile of drag
* @param p2 various bitstuffed elements
* - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
* - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
* - 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 10) - 0 = keep fixed distance, 1 = minimise gaps between signals
* - p2 = (bit 24-31) - user defined signals_density
* @param text unused
* @param tile start tile of drag
* @param end_tile end tile of drag
* @param track track-orientation
* @param sigtype default signal type
* @param sigvar signal variant to build
* @param mode true = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
* @param remove remove signals?
* @param autofill fill beyond selected stretch?
* @param minimise_gaps false = keep fixed distance, true = minimise gaps between signals
* @param signal_density user defined signals_density
* @return the cost of this operation or an error
*/
static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
static CommandCost CmdSignalTrackHelper(DoCommandFlag flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool remove, bool autofill, bool minimise_gaps, int signal_density)
{
CommandCost total_cost(EXPENSES_CONSTRUCTION);
TileIndex start_tile = tile;
Track track = Extract<Track, 0, 3>(p2);
bool mode = HasBit(p2, 3);
bool semaphores = HasBit(p2, 4);
bool remove = HasBit(p2, 5);
bool autofill = HasBit(p2, 6);
bool minimise_gaps = HasBit(p2, 10);
byte signal_density = GB(p2, 24, 8);
if (p1 >= MapSize() || !ValParamTrackOrientation(track)) return CMD_ERROR;
TileIndex end_tile = p1;
if (end_tile >= MapSize() || !ValParamTrackOrientation(track)) return CMD_ERROR;
if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
if (!remove && (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE)) return CMD_ERROR;
if (!IsPlainRailTile(tile)) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
TileIndex start_tile = tile;
/* for vertical/horizontal tracks, double the given signals density
* since the original amount will be too dense (shorter tracks) */
signal_density *= 2;
/* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
signal_density *= TILE_AXIAL_DISTANCE;
Trackdir trackdir = TrackToTrackdir(track);
CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
@@ -1330,9 +1277,6 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin
/* Must start on a valid track to be able to avoid loops */
if (!HasTrack(tile, track)) return CMD_ERROR;
SignalType sigtype = (SignalType)GB(p2, 7, 3);
if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
byte signals;
/* copy the signal-style of the first rail-piece if existing */
if (HasSignalOnTrack(tile, track)) {
@@ -1340,7 +1284,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin
assert(signals != 0);
/* copy signal/semaphores style (independent of CTRL) */
semaphores = GetSignalVariant(tile, track) != SIG_ELECTRIC;
sigvar = GetSignalVariant(tile, track);
sigtype = GetSignalType(tile, track);
/* Don't but copy entry or exit-signal type */
@@ -1366,128 +1310,154 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin
* and convert all others to semaphore/signal
* remove - 1 remove signals, 0 build signals */
int signal_ctr = 0;
int last_used_ctr = INT_MIN; // initially INT_MIN to force building/removing at the first tile
int last_used_ctr = -signal_density; // to force signal at first tile
int last_suitable_ctr = 0;
TileIndex last_suitable_tile = INVALID_TILE;
Trackdir last_suitable_trackdir = INVALID_TRACKDIR;
CommandCost last_error = CMD_ERROR;
bool had_success = false;
for (;;) {
/* only build/remove signals with the specified density */
if (remove || minimise_gaps || signal_ctr % signal_density == 0) {
uint32 param1 = GB(TrackdirToTrack(trackdir), 0, 3);
SB(param1, 3, 1, mode);
SB(param1, 4, 1, semaphores);
SB(param1, 5, 3, sigtype);
if (!remove && signal_ctr == 0) SetBit(param1, 17);
auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
/* Pick the correct orientation for the track direction */
byte signals = 0;
if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
/* Pick the correct orientation for the track direction */
signals = 0;
if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
DoCommandFlag do_flags = test_only ? flags & ~DC_EXEC : flags;
CommandCost ret = remove ? Command<CMD_REMOVE_SIGNALS>::Do(do_flags, tile, TrackdirToTrack(trackdir)) : Command<CMD_BUILD_SIGNALS>::Do(do_flags, tile, TrackdirToTrack(trackdir), sigtype, sigvar, false, signal_ctr == 0, mode, SIGTYPE_NORMAL, SIGTYPE_NORMAL, 0, signals);
/* Test tiles in between for suitability as well if minimising gaps. */
bool test_only = !remove && minimise_gaps && signal_ctr < (last_used_ctr + signal_density);
CommandCost ret = DoCommand(tile, param1, signals, test_only ? flags & ~DC_EXEC : flags, remove ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
if (test_only) return ret.Succeeded();
if (ret.Succeeded()) {
/* Remember last track piece where we can place a signal. */
last_suitable_ctr = signal_ctr;
last_suitable_tile = tile;
last_suitable_trackdir = trackdir;
} else if (!test_only && last_suitable_tile != INVALID_TILE) {
/* If a signal can't be placed, place it at the last possible position. */
SB(param1, 0, 3, TrackdirToTrack(last_suitable_trackdir));
ClrBit(param1, 17);
/* Pick the correct orientation for the track direction. */
signals = 0;
if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(last_suitable_trackdir);
if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(last_suitable_trackdir);
ret = DoCommand(last_suitable_tile, param1, signals, flags, remove ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
if (ret.Succeeded()) {
had_success = true;
total_cost.AddCost(ret);
} else {
/* The "No railway" error is the least important one. */
if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
last_error.GetErrorMessage() == INVALID_STRING_ID) {
last_error = ret;
}
}
return ret.Succeeded();
};
/* Collect cost. */
if (!test_only) {
/* Be user-friendly and try placing signals as much as possible */
if (ret.Succeeded()) {
had_success = true;
total_cost.AddCost(ret);
last_used_ctr = last_suitable_ctr;
for (;;) {
if (remove) {
/* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
build_signal(tile, trackdir, false);
} else if (minimise_gaps) {
/* We're trying to minimize gaps wherever possible, so keep track of last suitable
* position and use it if current gap exceeds required signal density. */
if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
/* We overshot so build a signal in last good location. */
if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
last_suitable_tile = INVALID_TILE;
} else {
/* The "No railway" error is the least important one. */
if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
last_error.GetErrorMessage() == INVALID_STRING_ID) {
last_error = ret;
}
last_used_ctr = last_suitable_ctr;
}
}
if (signal_ctr == last_used_ctr + signal_density) {
/* Current gap matches the required density, build a signal. */
if (build_signal(tile, trackdir, false)) {
last_used_ctr = signal_ctr;
last_suitable_tile = INVALID_TILE;
}
} else {
/* Test tile for a potential signal spot. */
if (build_signal(tile, trackdir, true)) {
last_suitable_tile = tile;
last_suitable_ctr = signal_ctr;
last_suitable_trackdir = trackdir;
}
}
} else if(signal_ctr >= last_used_ctr + signal_density) {
/* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
build_signal(tile, trackdir, false);
last_used_ctr = signal_ctr;
}
if (autofill) {
if (!CheckSignalAutoFill(tile, trackdir, signal_ctr, remove)) break;
switch (GetTileType(tile)) {
case MP_RAILWAY:
signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
break;
case MP_ROAD:
signal_ctr += TILE_AXIAL_DISTANCE;
break;
case MP_TUNNELBRIDGE: {
uint len = (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 2) * TILE_AXIAL_DISTANCE;
if (remove || minimise_gaps) {
signal_ctr += len;
} else {
/* To keep regular interval we need to emulate placing signals on a bridge.
* We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
signal_ctr += TILE_AXIAL_DISTANCE;
for(uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
signal_ctr += TILE_AXIAL_DISTANCE;
}
}
break;
}
default: break;
}
if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
/* Prevent possible loops */
if (tile == start_tile && trackdir == start_trackdir) break;
} else {
if (tile == end_tile) break;
tile += ToTileIndexDiff(_trackdelta[trackdir]);
signal_ctr++;
signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
/* toggle railbit for the non-diagonal tracks (|, -- tracks) */
if (IsDiagonalTrackdir(trackdir)) {
signal_ctr++;
} else {
ToggleBit(trackdir, 0);
}
tile += ToTileIndexDiff(_trackdelta[trackdir]);
if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
}
}
/* We may end up with the current gap exceeding the signal density so fix that if needed. */
if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
build_signal(last_suitable_tile, last_suitable_trackdir, false);
}
return had_success ? total_cost : last_error;
}
/**
* Build signals on a stretch of track.
* Stub for the unified signal builder/remover
* @param tile start tile of drag
* @param flags operation to perform
* @param p1 end tile of drag
* @param p2 various bitstuffed elements
* - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
* - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
* - 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
* @param text unused
* @param tile start tile of drag
* @param end_tile end tile of drag
* @param track track-orientation
* @param sigtype default signal type
* @param sigvar signal variant to build
* @param mode true = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
* @param autofill fill beyond selected stretch?
* @param minimise_gaps false = keep fixed distance, true = minimise gaps between signals
* @param signal_density user defined signals_density
* @return the cost of this operation or an error
* @see CmdSignalTrackHelper
*/
CommandCost CmdBuildSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdBuildSignalTrack(DoCommandFlag flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool autofill, bool minimise_gaps, byte signal_density)
{
return CmdSignalTrackHelper(tile, flags, p1, p2, text);
return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
}
/**
* Remove signals
* @param tile coordinates where signal is being deleted from
* @param flags operation to perform
* @param p1 various bitstuffed elements, only track information is used
* - (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
* - (bit 3) - override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
* - (bit 4) - 0 = signals, 1 = semaphores
* @param p2 unused
* @param text unused
* @param tile coordinates where signal is being deleted from
* @param track track-orientation
* @return the cost of this operation or an error
*/
CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdRemoveSingleSignal(DoCommandFlag flags, TileIndex tile, Track track)
{
Track track = Extract<Track, 0, 3>(p1);
if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
}
@@ -1544,24 +1514,17 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1
/**
* Remove signals on a stretch of track.
* Stub for the unified signal builder/remover
* @param tile start tile of drag
* @param flags operation to perform
* @param p1 end tile of drag
* @param p2 various bitstuffed elements
* - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
* - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
* - 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
* @param text unused
* @param tile start tile of drag
* @param end_tile end tile of drag
* @param track track-orientation
* @param autofill fill beyond selected stretch?
* @return the cost of this operation or an error
* @see CmdSignalTrackHelper
*/
CommandCost CmdRemoveSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdRemoveSignalTrack(DoCommandFlag flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
{
return CmdSignalTrackHelper(tile, flags, p1, SetBit(p2, 5), text); // bit 5 is remove bit
return CmdSignalTrackHelper(flags, tile, end_tile, track, SIGTYPE_NORMAL, SIG_ELECTRIC, false, true, autofill, false, 1); // bit 5 is remove bit
}
/** Update power of train under which is the railtype being converted */
@@ -1578,21 +1541,16 @@ static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data)
/**
* Convert one rail type to the other. You can convert normal rail to
* monorail/maglev easily or vice-versa.
* @param tile end tile of rail conversion drag
* @param flags operation to perform
* @param p1 start tile of drag
* @param p2 various bitstuffed elements:
* - p2 = (bit 0- 5) new railtype to convert to.
* - p2 = (bit 6) build diagonally or not.
* @param text unused
* @param tile end tile of rail conversion drag
* @param area_start start tile of drag
* @param totype new railtype to convert to.
* @param diagonal build diagonally or not.
* @return the cost of this operation or an error
*/
CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
{
RailType totype = Extract<RailType, 0, 6>(p2);
TileIndex area_start = p1;
TileIndex area_end = tile;
bool diagonal = HasBit(p2, 6);
if (!ValParamRailtype(totype)) return CMD_ERROR;
if (area_start >= MapSize()) return CMD_ERROR;
@@ -1864,7 +1822,7 @@ static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
TrackBits tracks = GetTrackBits(tile);
while (tracks != TRACK_BIT_NONE) {
Track track = RemoveFirstTrack(&tracks);
CommandCost ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
CommandCost ret = Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile, track);
if (ret.Failed()) return ret;
cost.AddCost(ret);
}
@@ -1877,9 +1835,7 @@ static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
/* The track was removed, and left a coast tile. Now also clear the water. */
if (flags & DC_EXEC) {
bool remove = IsDockingTile(tile);
DoClearSquare(tile);
if (remove) RemoveDockingTile(tile);
}
cost.AddCost(_price[PR_CLEAR_WATER]);
}
@@ -2938,7 +2894,7 @@ static void ChangeTileOwner_Track(TileIndex tile, Owner old_owner, Owner new_own
SetTileOwner(tile, new_owner);
} else {
DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC | DC_BANKRUPT, tile);
}
}
@@ -3126,7 +3082,7 @@ static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlag flags, int
AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
}
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
}