diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj
index 0571ef4862..8d1c9058fe 100644
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -1273,6 +1273,10 @@
+
+
+
+
diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters
index 0b6dc573b4..37c6b1559c 100644
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -3048,6 +3048,18 @@
Threading
+
+ Threading
+
+
+ Threading
+
+
+ Threading
+
+
+ Threading
+
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index f6b938ed7d..5c874a0801 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -4490,6 +4490,22 @@
RelativePath=".\..\src\thread\thread_win32.cpp"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
index);
+ TraceRestrictRemoveDestinationID(TROCAF_DEPOT, this->index);
+
/* Delete the depot-window */
DeleteWindowById(WC_VEHICLE_DEPOT, this->xy);
diff --git a/src/lang/english.txt b/src/lang/english.txt
index ad29b3d596..e077670977 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -2370,6 +2370,93 @@ STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP :{BLACK}Dragging
STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_DECREASE_TOOLTIP :{BLACK}Decrease dragging signal density
STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Increase dragging signal density
+# Tracerestrict GUI
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS :is
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_NOT_EQUALS :is not
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_LESS_THAN :<
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_LESS_EQUALS :<=
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_MORE_THAN :>
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_MORE_EQUALS :>=
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_EQUALS :can carry
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_NOT_EQUALS :can't carry
+STR_TRACE_RESTRICT_CONDITIONAL_IF :If
+STR_TRACE_RESTRICT_CONDITIONAL_ELIF :Else if
+STR_TRACE_RESTRICT_CONDITIONAL_ORIF :Or if
+STR_TRACE_RESTRICT_CONDITIONAL_ELSE :Else
+STR_TRACE_RESTRICT_CONDITIONAL_ENDIF :End if
+STR_TRACE_RESTRICT_VARIABLE_TRAIN_LENGTH :train length
+STR_TRACE_RESTRICT_VARIABLE_MAX_SPEED :max speed
+STR_TRACE_RESTRICT_VARIABLE_CURRENT_ORDER :current order
+STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER :next order
+STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION :last visited station
+STR_TRACE_RESTRICT_VARIABLE_CARGO :cargo
+STR_TRACE_RESTRICT_VARIABLE_ENTRY_DIRECTION :entry direction
+STR_TRACE_RESTRICT_VARIABLE_UNDEFINED :undefined
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER :{STRING} {STRING} {STRING} {COMMA} then
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED :{STRING} {STRING} {STRING} {VELOCITY} then
+STR_TRACE_RESTRICT_CONDITIONAL_ORDER_STATION :{STRING} {STRING} {STRING} {STATION} then
+STR_TRACE_RESTRICT_CONDITIONAL_ORDER_WAYPOINT :{STRING} {STRING} {STRING} {WAYPOINT} then
+STR_TRACE_RESTRICT_CONDITIONAL_ORDER_DEPOT :{STRING} {STRING} {STRING} {DEPOT} then
+STR_TRACE_RESTRICT_CONDITIONAL_CARGO :{STRING} train {STRING} cargo: {STRING} then
+STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_DIRECTION :{STRING} train {STRING} entering from {STRING} tile edge then
+STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_SIGNAL_FACE :{STRING} train {STRING} entering from {STRING} of signal then
+STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED :{STRING} {STRING} {STRING} {RED}undefined {BLACK}{STRING}then
+STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED :{STRING} {RED}undefined {BLACK}{STRING}then
+STR_TRACE_RESTRICT_PF_PENALTY_ITEM :Add pathfinder penalty: {COMMA}
+STR_TRACE_RESTRICT_WHITE :{WHITE}
+STR_TRACE_RESTRICT_START :Start
+STR_TRACE_RESTRICT_END :End
+STR_TRACE_RESTRICT_PF_DENY :Deny
+STR_TRACE_RESTRICT_PF_ALLOW :Allow
+STR_TRACE_RESTRICT_PF_ALLOW_LONG :Allow (cancel previous Deny)
+STR_TRACE_RESTRICT_PF_PENALTY :Penalty
+STR_TRACE_RESTRICT_DIRECTION_FRONT :front
+STR_TRACE_RESTRICT_DIRECTION_BACK :back
+STR_TRACE_RESTRICT_DIRECTION_NE :north-east
+STR_TRACE_RESTRICT_DIRECTION_SE :south-east
+STR_TRACE_RESTRICT_DIRECTION_SW :south-west
+STR_TRACE_RESTRICT_DIRECTION_NW :north-west
+STR_TRACE_RESTRICT_VALUE_CAPTION :{WHITE}Value
+STR_TRACE_RESTRICT_CAPTION :{WHITE}Routefinding restriction
+STR_TRACE_RESTRICT_CAPTION_SHARED :{WHITE}Routefinding restriction - shared by {COMMA} signals
+STR_TRACE_RESTRICT_TYPE_TOOLTIP :{BLACK}Type
+STR_TRACE_RESTRICT_COND_COMPARATOR_TOOLTIP :{BLACK}Comparison operator
+STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP :{BLACK}Value
+STR_TRACE_RESTRICT_CONDFLAGS_TOOLTIP :{BLACK}Condition type
+STR_TRACE_RESTRICT_GOTO_SIGNAL_TOOLTIP :{BLACK}Go to signal
+STR_TRACE_RESTRICT_INSERT :{BLACK}Insert
+STR_TRACE_RESTRICT_REMOVE :{BLACK}Remove
+STR_TRACE_RESTRICT_RESET :{BLACK}Reset
+STR_TRACE_RESTRICT_COPY :{BLACK}Copy
+STR_TRACE_RESTRICT_SHARE :{BLACK}Share
+STR_TRACE_RESTRICT_UNSHARE :{BLACK}Unshare
+STR_TRACE_RESTRICT_SELECT_TARGET :{BLACK}Select Target
+STR_TRACE_RESTRICT_INSERT_TOOLTIP :{BLACK}Insert an instruction
+STR_TRACE_RESTRICT_REMOVE_TOOLTIP :{BLACK}Remove the selected instruction
+STR_TRACE_RESTRICT_RESET_TOOLTIP :{BLACK}Reset the current signal (without affecting shared programs)
+STR_TRACE_RESTRICT_COPY_TOOLTIP :{BLACK}Copy program from another signal
+STR_TRACE_RESTRICT_SHARE_TOOLTIP :{BLACK}Share program with another signal
+STR_TRACE_RESTRICT_UNSHARE_TOOLTIP :{BLACK}Stop sharing program with other signals, create a copy of the program
+STR_TRACE_RESTRICT_SIGNAL_GUI_TOOLTIP :{BLACK}Routefinding restriction
+STR_TRACE_RESTRICT_INSTRUCTION_LIST_TOOLTIP :{BLACK}Click an instruction to select it{}Ctrl+Click to scroll to the instruction's target (if any)
+STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM :{WHITE}Can't insert instruction
+STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM :{WHITE}Can't modify instruction
+STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM :{WHITE}Can't remove instruction
+STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE :{WHITE}Value too large, maximum is {COMMA}
+STR_TRACE_RESTRICT_ERROR_NO_PROGRAM :No trace restrict program exists
+STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE :Offset too large
+STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY :Can't change conditionality
+STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ENDIF :Can't remove an 'end if'
+STR_TRACE_RESTRICT_ERROR_VALIDATE_END_CONDSTACK :Validation failed: condstack non-empty at exit
+STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF :Validation failed: else/endif without opening if
+STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE :Validation failed: duplicate else
+STR_TRACE_RESTRICT_ERROR_VALIDATE_ELIF_NO_IF :Validation failed: else if without opening if
+STR_TRACE_RESTRICT_ERROR_SOURCE_SAME_AS_TARGET :Source and target signals are the same
+STR_TRACE_RESTRICT_ERROR_CAN_T_RESET_SIGNAL :{WHITE}Can't reset signal
+STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM :{WHITE}Can't copy program
+STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM :{WHITE}Can't share program
+STR_TRACE_RESTRICT_ERROR_CAN_T_UNSHARE_PROGRAM :{WHITE}Can't unshare program
+
# Bridge selection window
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Select Rail Bridge
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Select Road Bridge
@@ -2625,6 +2712,8 @@ STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS :{STRING} track
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS :{STRING} track with path and one-way path signals
STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :{STRING} train depot
+STR_LAI_RAIL_DESCRIPTION_RESTRICTED_SIGNAL :{STRING1} (restricted)
+
STR_LAI_ROAD_DESCRIPTION_ROAD :Road
STR_LAI_ROAD_DESCRIPTION_ROAD_WITH_STREETLIGHTS :Road with street lights
STR_LAI_ROAD_DESCRIPTION_TREE_LINED_ROAD :Tree-lined road
diff --git a/src/misc.cpp b/src/misc.cpp
index d9d506993f..939338af15 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -28,6 +28,7 @@
#include "core/pool_type.hpp"
#include "game/game.hpp"
#include "linkgraph/linkgraphschedule.h"
+#include "tracerestrict.h"
#include "safeguards.h"
@@ -72,6 +73,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
}
LinkGraphSchedule::Clear();
+ ClearTraceRestrictMapping();
PoolBase::Clean(PT_NORMAL);
ResetPersistentNewGRFData();
diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp
index 62da50b966..8794292b11 100644
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -175,6 +175,7 @@ public:
/* Tiletype */
SetDParam(0, td.dparam[0]);
+ SetDParam(1, td.dparam[1]);
GetString(this->landinfo_data[line_nr], td.str, lastof(this->landinfo_data[line_nr]));
line_nr++;
diff --git a/src/openttd.cpp b/src/openttd.cpp
index c149ebbd4d..1e4450da9e 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -65,6 +65,7 @@
#include "viewport_sprite_sorter.h"
#include "linkgraph/linkgraphschedule.h"
+#include "tracerestrict.h"
#include
@@ -302,6 +303,7 @@ static void ShutdownGame()
#endif
LinkGraphSchedule::Clear();
+ ClearTraceRestrictMapping();
PoolBase::Clean(PT_ALL);
/* No NewGRFs were loaded when it was still bootstrapping. */
diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp
index c6080f2a15..5a4b6531c3 100644
--- a/src/pathfinder/yapf/yapf_costrail.hpp
+++ b/src/pathfinder/yapf/yapf_costrail.hpp
@@ -13,6 +13,7 @@
#define YAPF_COSTRAIL_HPP
#include "../../pbs.h"
+#include "../../tracerestrict.h"
template
class CYapfCostRailT
@@ -180,6 +181,30 @@ public:
return 0;
}
+private:
+ // returns true if ExecuteTraceRestrict should be called
+ inline bool ShouldCheckTraceRestrict(Node& n, TileIndex tile)
+ {
+ return n.m_num_signals_passed < m_sig_look_ahead_costs.Size() &&
+ IsRestrictedSignal(tile);
+ }
+
+ // returns true if dead end bit has been set
+ inline bool ExecuteTraceRestrict(Node& n, TileIndex tile, Trackdir trackdir, int& cost, TraceRestrictProgramResult &out)
+ {
+ const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, TrackdirToTrack(trackdir));
+ if (prog) {
+ prog->Execute(Yapf().GetVehicle(), TraceRestrictProgramInput(tile, trackdir), out);
+ if (out.flags & TRPRF_DENY) {
+ n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
+ return true;
+ }
+ cost += out.penalty;
+ }
+ return false;
+ }
+
+public:
int SignalCost(Node& n, TileIndex tile, Trackdir trackdir)
{
int cost = 0;
@@ -239,6 +264,13 @@ public:
}
}
+ if (ShouldCheckTraceRestrict(n, tile)) {
+ TraceRestrictProgramResult out;
+ if (ExecuteTraceRestrict(n, tile, trackdir, cost, out)) {
+ return -1;
+ }
+ }
+
n.m_num_signals_passed++;
n.m_segment->m_last_signal_tile = tile;
n.m_segment->m_last_signal_td = trackdir;
@@ -246,6 +278,13 @@ public:
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;
+
+ if (ShouldCheckTraceRestrict(n, tile)) {
+ TraceRestrictProgramResult out;
+ if (ExecuteTraceRestrict(n, tile, trackdir, cost, out)) {
+ return -1;
+ }
+ }
}
}
}
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index d7a25d8bb1..35b7f1e3c3 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -33,6 +33,7 @@
#include "strings_func.h"
#include "company_gui.h"
#include "object_map.h"
+#include "tracerestrict.h"
#include "table/strings.h"
#include "table/railtypes.h"
@@ -1452,6 +1453,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1
SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
+ TraceRestrictNotifySignalRemoval(tile, track);
/* removed last signal from tile? */
if (GetPresentSignals(tile) == 0) {
@@ -2768,6 +2770,12 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
}
td->str = signal_type[secondary_signal][primary_signal];
+
+ if (IsRestrictedSignal(tile)) {
+ SetDParamX(td->dparam, 0, td->str);
+ SetDParamX(td->dparam, 1, rti->strings.name);
+ td->str = STR_LAI_RAIL_DESCRIPTION_RESTRICTED_SIGNAL;
+ }
break;
}
diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp
index a8c2fc6b33..1776a0e118 100644
--- a/src/rail_gui.cpp
+++ b/src/rail_gui.cpp
@@ -34,6 +34,7 @@
#include "vehicle_func.h"
#include "zoom_func.h"
#include "rail_gui.h"
+#include "tracerestrict.h"
#include "station_map.h"
#include "tunnelbridge_map.h"
@@ -49,6 +50,7 @@ static DiagDirection _build_depot_direction; ///< Currently selected depot direc
static byte _waypoint_count = 1; ///< Number of waypoint types
static byte _cur_waypoint_type; ///< Currently selected waypoint type
static bool _convert_signal_button; ///< convert signal button in the signal GUI pressed
+static bool _trace_restrict_button; ///< trace restrict button in the signal GUI pressed
static SignalVariant _cur_signal_variant; ///< set the signal variant (for signal GUI)
static SignalType _cur_signal_type; ///< set the signal type (for signal GUI)
@@ -224,6 +226,10 @@ static void GenericPlaceSignals(TileIndex tile)
if (_remove_button_clicked) {
DoCommandP(tile, track, 0, CMD_REMOVE_SIGNALS | CMD_MSG(STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM), CcPlaySound1E);
+ } else if (_trace_restrict_button) {
+ if (IsPlainRailTile(tile) && HasTrack(tile, track) && HasSignalOnTrack(tile, track)) {
+ ShowTraceRestrictProgramWindow(tile, track);
+ }
} else {
const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
@@ -1518,6 +1524,7 @@ public:
~BuildSignalWindow()
{
_convert_signal_button = false;
+ _trace_restrict_button = false;
}
virtual void OnInit()
@@ -1602,6 +1609,12 @@ public:
case WID_BS_CONVERT:
_convert_signal_button = !_convert_signal_button;
+ if (_convert_signal_button) _trace_restrict_button = false;
+ break;
+
+ case WID_BS_TRACE_RESTRICT:
+ _trace_restrict_button = !_trace_restrict_button;
+ if (_trace_restrict_button) _convert_signal_button = false;
break;
case WID_BS_DRAG_SIGNALS_DENSITY_DECREASE:
@@ -1635,6 +1648,7 @@ public:
this->LowerWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_type);
this->SetWidgetLoweredState(WID_BS_CONVERT, _convert_signal_button);
+ this->SetWidgetLoweredState(WID_BS_TRACE_RESTRICT, _trace_restrict_button);
this->SetWidgetDisabledState(WID_BS_DRAG_SIGNALS_DENSITY_DECREASE, _settings_client.gui.drag_signals_density == 1);
this->SetWidgetDisabledState(WID_BS_DRAG_SIGNALS_DENSITY_INCREASE, _settings_client.gui.drag_signals_density == 20);
@@ -1656,6 +1670,7 @@ static const NWidgetPart _nested_signal_builder_widgets[] = {
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_TOOLTIP), EndContainer(), SetFill(1, 1),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS_OWAY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TOOLTIP), EndContainer(), SetFill(1, 1),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_BS_CONVERT), SetDataTip(SPR_IMG_SIGNAL_CONVERT, STR_BUILD_SIGNAL_CONVERT_TOOLTIP), SetFill(1, 1),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_BS_TRACE_RESTRICT), SetDataTip(SPR_IMG_SETTINGS, STR_TRACE_RESTRICT_SIGNAL_GUI_TOOLTIP), SetFill(1, 1),
EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_NORM), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_NORM_TOOLTIP), EndContainer(), SetFill(1, 1),
@@ -1674,6 +1689,7 @@ static const NWidgetPart _nested_signal_builder_widgets[] = {
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
EndContainer(),
+ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), EndContainer(), SetFill(1, 1),
EndContainer(),
EndContainer(),
};
@@ -1974,6 +1990,7 @@ void InitializeRailGUI()
SetDefaultRailGui();
_convert_signal_button = false;
+ _trace_restrict_button = false;
_cur_signal_type = _default_signal_type[_settings_client.gui.default_signal_type];
ResetSignalVariant();
}
diff --git a/src/rail_map.h b/src/rail_map.h
index 2431a79202..e6f4d7e406 100644
--- a/src/rail_map.h
+++ b/src/rail_map.h
@@ -479,6 +479,26 @@ static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td)
!HasSignalOnTrackdir(tile, td) && IsOnewaySignal(tile, TrackdirToTrack(td));
}
+/**
+ * Does signal tile have "one or more trace restrict mappings present" bit set
+ * @param tile the tile to check
+ */
+static inline bool IsRestrictedSignal(TileIndex tile)
+{
+ assert(GetRailTileType(tile) == RAIL_TILE_SIGNALS);
+ return (bool) GB(_m[tile].m2, 12, 1);
+}
+
+/**
+ * Set signal tile "one or more trace restrict mappings present" bit
+ * @param tile the tile to set
+ */
+static inline void SetRestrictedSignal(TileIndex tile, bool is_restricted)
+{
+ assert(GetRailTileType(tile) == RAIL_TILE_SIGNALS);
+ SB(_m[tile].m2, 12, 1, is_restricted);
+}
+
RailType GetTileRailType(TileIndex tile);
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index 9690481154..132ab32bde 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -2988,6 +2988,8 @@ bool AfterLoadGame()
ResetSignalHandlers();
AfterLoadLinkGraphs();
+
+ AfterLoadTraceRestrict();
return true;
}
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index 7127faf7b6..7053a9bd96 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -45,6 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks
static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk
const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
+ { XSLFI_TRACE_RESTRICT, XSCF_NULL, 1, 1, "tracerestrict", NULL, NULL, "TRRM,TRRP" },
{ XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker
};
@@ -121,7 +122,12 @@ void SlXvCheckSpecialSavegameVersions()
{
extern uint16 _sl_version;
- // TODO: check for savegame versions
+ if (_sl_version == 2000) {
+ DEBUG(sl, 1, "Loading a trace restrict patch savegame version %d as version 194", _sl_version);
+ _sl_version = 194;
+ _sl_is_faked_ext = true;
+ _sl_xv_feature_versions[XSLFI_TRACE_RESTRICT] = 1;
+ }
}
/**
diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h
index a7e9496944..5532f1b6fd 100644
--- a/src/saveload/extended_ver_sl.h
+++ b/src/saveload/extended_ver_sl.h
@@ -21,6 +21,7 @@
*/
enum SlXvFeatureIndex {
XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use
+ XSLFI_TRACE_RESTRICT, ///< Trace restrict
XSLFI_SIZE, ///< Total count of features, including null feature
};
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index 55a37f8ee8..2e2d7e1465 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -454,6 +454,7 @@ extern const ChunkHandler _linkgraph_chunk_handlers[];
extern const ChunkHandler _airport_chunk_handlers[];
extern const ChunkHandler _object_chunk_handlers[];
extern const ChunkHandler _persistent_storage_chunk_handlers[];
+extern const ChunkHandler _trace_restrict_chunk_handlers[];
/** Array of all chunks in a savegame, \c NULL terminated. */
static const ChunkHandler * const _chunk_handlers[] = {
@@ -491,6 +492,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
_airport_chunk_handlers,
_object_chunk_handlers,
_persistent_storage_chunk_handlers,
+ _trace_restrict_chunk_handlers,
NULL,
};
diff --git a/src/saveload/saveload_internal.h b/src/saveload/saveload_internal.h
index 74e5b9936d..2d1ae11bf7 100644
--- a/src/saveload/saveload_internal.h
+++ b/src/saveload/saveload_internal.h
@@ -34,6 +34,7 @@ void AfterLoadLabelMaps();
void AfterLoadStoryBook();
void AfterLoadLinkGraphs();
void AfterLoadCompanyStats();
+void AfterLoadTraceRestrict();
void UpdateHousesAndTowns();
void UpdateOldAircraft();
diff --git a/src/saveload/tracerestrict_sl.cpp b/src/saveload/tracerestrict_sl.cpp
new file mode 100644
index 0000000000..93d43cf3ea
--- /dev/null
+++ b/src/saveload/tracerestrict_sl.cpp
@@ -0,0 +1,111 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file tracerestrict_sl.cpp Code handling saving and loading of trace restrict programs */
+
+#include "../stdafx.h"
+#include "../tracerestrict.h"
+#include "saveload.h"
+#include
+#include "saveload.h"
+
+static const SaveLoad _trace_restrict_mapping_desc[] = {
+ SLE_VAR(TraceRestrictMappingItem, program_id, SLE_UINT32),
+ SLE_END()
+};
+
+/**
+ * Load mappings
+ */
+static void Load_TRRM()
+{
+ int index;
+ while ((index = SlIterateArray()) != -1) {
+ TraceRestrictMappingItem &item = _tracerestrictprogram_mapping[index];
+ SlObject(&item, _trace_restrict_mapping_desc);
+ }
+}
+
+/**
+ * Save mappings
+ */
+static void Save_TRRM()
+{
+ for (TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.begin();
+ iter != _tracerestrictprogram_mapping.end(); ++iter) {
+ SlSetArrayIndex(iter->first);
+ SlObject(&(iter->second), _trace_restrict_mapping_desc);
+ }
+}
+
+/** program length save header struct */
+struct TraceRestrictProgramStub {
+ uint32 length;
+};
+
+static const SaveLoad _trace_restrict_program_stub_desc[] = {
+ SLE_VAR(TraceRestrictProgramStub, length, SLE_UINT32),
+ SLE_END()
+};
+
+/**
+ * Load program pool
+ */
+static void Load_TRRP()
+{
+ int index;
+ TraceRestrictProgramStub stub;
+ while ((index = SlIterateArray()) != -1) {
+ TraceRestrictProgram *prog = new (index) TraceRestrictProgram();
+ SlObject(&stub, _trace_restrict_program_stub_desc);
+ prog->items.resize(stub.length);
+ SlArray(&(prog->items[0]), stub.length, SLE_UINT32);
+ assert(prog->Validate().Succeeded());
+ }
+}
+
+/**
+ * Save a program, used by SlAutolength
+ */
+static void RealSave_TRRP(TraceRestrictProgram *prog)
+{
+ TraceRestrictProgramStub stub;
+ stub.length = prog->items.size();
+ SlObject(&stub, _trace_restrict_program_stub_desc);
+ SlArray(&(prog->items[0]), stub.length, SLE_UINT32);
+}
+
+/**
+ * Save program pool
+ */
+static void Save_TRRP()
+{
+ TraceRestrictProgram *prog;
+
+ FOR_ALL_TRACE_RESTRICT_PROGRAMS(prog) {
+ SlSetArrayIndex(prog->index);
+ SlAutolength((AutolengthProc*) RealSave_TRRP, prog);
+ }
+}
+
+/**
+ * Update program reference counts from just-loaded mapping
+ */
+void AfterLoadTraceRestrict()
+{
+ for (TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.begin();
+ iter != _tracerestrictprogram_mapping.end(); ++iter) {
+ _tracerestrictprogram_pool.Get(iter->second.program_id)->IncrementRefCount();
+ }
+}
+
+extern const ChunkHandler _trace_restrict_chunk_handlers[] = {
+ { 'TRRM', Save_TRRM, Load_TRRM, NULL, NULL, CH_SPARSE_ARRAY}, // Trace Restrict Mapping chunk
+ { 'TRRP', Save_TRRP, Load_TRRP, NULL, NULL, CH_ARRAY | CH_LAST}, // Trace Restrict Mapping Program Pool chunk
+};
diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp
index 58e114734e..4405f69451 100644
--- a/src/script/api/script_window.hpp
+++ b/src/script/api/script_window.hpp
@@ -2054,6 +2054,7 @@ public:
WID_BS_ELECTRIC_PBS = ::WID_BS_ELECTRIC_PBS, ///< Build an electric path signal.
WID_BS_ELECTRIC_PBS_OWAY = ::WID_BS_ELECTRIC_PBS_OWAY, ///< Build an electric one way path signal.
WID_BS_CONVERT = ::WID_BS_CONVERT, ///< Convert the signal.
+ WID_BS_TRACE_RESTRICT = ::WID_BS_TRACE_RESTRICT, ///< Open trace restrict window.
WID_BS_DRAG_SIGNALS_DENSITY_LABEL = ::WID_BS_DRAG_SIGNALS_DENSITY_LABEL, ///< The current signal density.
WID_BS_DRAG_SIGNALS_DENSITY_DECREASE = ::WID_BS_DRAG_SIGNALS_DENSITY_DECREASE, ///< Decrease the signal density.
WID_BS_DRAG_SIGNALS_DENSITY_INCREASE = ::WID_BS_DRAG_SIGNALS_DENSITY_INCREASE, ///< Increase the signal density.
diff --git a/src/station.cpp b/src/station.cpp
index 456262dea4..9b5dcfb4ca 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -26,6 +26,7 @@
#include "core/random_func.hpp"
#include "linkgraph/linkgraph.h"
#include "linkgraph/linkgraphschedule.h"
+#include "tracerestrict.h"
#include "table/strings.h"
@@ -137,6 +138,8 @@ Station::~Station()
/* Now delete all orders that go to the station */
RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index);
+ TraceRestrictRemoveDestinationID(TROCAF_STATION, this->index);
+
/* Remove all news items */
DeleteStationNews(this->index);
diff --git a/src/stdafx.h b/src/stdafx.h
index d68605c196..13bc9685d2 100644
--- a/src/stdafx.h
+++ b/src/stdafx.h
@@ -514,4 +514,15 @@ static inline void free(const void *ptr)
#define IGNORE_UNINITIALIZED_WARNING_STOP
#endif
+/*
+ * Conditional define for the override keyword.
+ * Use of the override keyword can prevent various types of problems when the base method signature is changed, but derived overriding methods are not
+ * This is conditional to maintain compatibility with legacy compilers
+ */
+#if __cplusplus >= 201103L || defined(__STDCXX_VERSION__) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__)
+ #define OVERRIDE override
+#else
+ #define OVERRIDE
+#endif
+
#endif /* STDAFX_H */
diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp
new file mode 100644
index 0000000000..968ab49eb0
--- /dev/null
+++ b/src/tracerestrict.cpp
@@ -0,0 +1,894 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file tracerestrict.cpp Main file for Trace Restrict */
+
+#include "stdafx.h"
+#include "tracerestrict.h"
+#include "train.h"
+#include "core/bitmath_func.hpp"
+#include "core/pool_func.hpp"
+#include "command_func.h"
+#include "company_func.h"
+#include "viewport_func.h"
+#include "window_func.h"
+#include "order_base.h"
+#include "cargotype.h"
+#include "pathfinder/yapf/yapf_cache.h"
+#include
+
+/** @file
+ *
+ * Trace Restrict Data Storage Model Notes:
+ *
+ * Signals may have 0, 1 or 2 trace restrict programs attached to them,
+ * up to one for each track. Two-way signals share the same program.
+ *
+ * The mapping between signals and programs is defined in terms of
+ * TraceRestrictRefId to TraceRestrictProgramID,
+ * where TraceRestrictRefId is formed of the tile index and track,
+ * and TraceRestrictProgramID is an index into the program pool.
+ *
+ * If one or more mappings exist for a given signal tile, bit 12 of M3 will be set to 1.
+ * This is updated whenever mappings are added/removed for that tile. This is to avoid
+ * needing to do a mapping lookup for the common case where there is no trace restrict
+ * program mapping for the given tile.
+ *
+ * Programs in the program pool are refcounted based on the number of mappings which exist.
+ * When this falls to 0, the program is deleted from the pool.
+ * If a program has a refcount greater than 1, it is a shared program.
+ *
+ * In all cases, an empty program is evaluated the same as the absence of a program.
+ * Therefore it is not necessary to store mappings to empty unshared programs.
+ * Any editing action which would otherwise result in a mapping to an empty program
+ * which has no other references, instead removes the mapping.
+ * This is not done for shared programs as this would delete the shared aspect whenever
+ * the program became empty.
+ *
+ * Empty programs with a refcount of 1 may still exist due to the edge case where:
+ * 1: There is an empty program with refcount 2
+ * 2: One of the two mappings is deleted
+ * Finding the other mapping would entail a linear search of the mappings, and there is little
+ * to be gained by doing so.
+ */
+
+TraceRestrictProgramPool _tracerestrictprogram_pool("TraceRestrictProgram");
+INSTANTIATE_POOL_METHODS(TraceRestrictProgram)
+
+/**
+ * TraceRestrictRefId --> TraceRestrictProgramID (Pool ID) mapping
+ * The indirection is mainly to enable shared programs
+ * TODO: use a more efficient container/indirection mechanism
+ */
+TraceRestrictMapping _tracerestrictprogram_mapping;
+
+/**
+ * This should be used when all pools have been or are immediately about to be also cleared
+ * Calling this at other times will leave dangling refcounts
+ */
+void ClearTraceRestrictMapping() {
+ _tracerestrictprogram_mapping.clear();
+}
+
+/**
+ * Flags used for the program execution condition stack
+ * Each 'if' pushes onto the stack
+ * Each 'end if' pops from the stack
+ * Elif/orif/else may modify the stack top
+ */
+enum TraceRestrictCondStackFlags {
+ TRCSF_DONE_IF = 1<<0, ///< The if/elif/else is "done", future elif/else branches will not be executed
+ TRCSF_SEEN_ELSE = 1<<1, ///< An else branch has been seen already, error if another is seen afterwards
+ TRCSF_ACTIVE = 1<<2, ///< The condition is currently active
+ TRCSF_PARENT_INACTIVE = 1<<3, ///< The parent condition is not active, thus this condition is also not active
+};
+DECLARE_ENUM_AS_BIT_SET(TraceRestrictCondStackFlags)
+
+/**
+ * Helper function to handle condition stack manipulatoin
+ */
+static void HandleCondition(std::vector &condstack, TraceRestrictCondFlags condflags, bool value)
+{
+ if (condflags & TRCF_OR) {
+ assert(!condstack.empty());
+ if (condstack.back() & TRCSF_ACTIVE) {
+ // leave TRCSF_ACTIVE set
+ return;
+ }
+ }
+
+ if (condflags & (TRCF_OR | TRCF_ELSE)) {
+ assert(!condstack.empty());
+ if (condstack.back() & (TRCSF_DONE_IF | TRCSF_PARENT_INACTIVE)) {
+ condstack.back() &= ~TRCSF_ACTIVE;
+ return;
+ }
+ } else {
+ if (!condstack.empty() && !(condstack.back() & TRCSF_ACTIVE)) {
+ //this is a 'nested if', the 'parent if' is not active
+ condstack.push_back(TRCSF_PARENT_INACTIVE);
+ return;
+ }
+ condstack.push_back(static_cast(0));
+ }
+
+ if (value) {
+ condstack.back() |= TRCSF_DONE_IF | TRCSF_ACTIVE;
+ } else {
+ condstack.back() &= ~TRCSF_ACTIVE;
+ }
+}
+
+/**
+ * Integer condition testing
+ * Test value op condvalue
+ */
+static bool TestCondition(uint16 value, TraceRestrictCondOp condop, uint16 condvalue)
+{
+ switch (condop) {
+ case TRCO_IS:
+ return value == condvalue;
+ case TRCO_ISNOT:
+ return value != condvalue;
+ case TRCO_LT:
+ return value < condvalue;
+ case TRCO_LTE:
+ return value <= condvalue;
+ case TRCO_GT:
+ return value > condvalue;
+ case TRCO_GTE:
+ return value >= condvalue;
+ default:
+ NOT_REACHED();
+ return false;
+ }
+}
+
+/**
+ * Binary condition testing helper function
+ */
+static bool TestBinaryConditionCommon(TraceRestrictItem item, bool input)
+{
+ switch (GetTraceRestrictCondOp(item)) {
+ case TRCO_IS:
+ return input;
+
+ case TRCO_ISNOT:
+ return !input;
+
+ default:
+ NOT_REACHED();
+ return false;
+ }
+}
+
+/**
+ * Test order condition
+ * @p order may be NULL
+ */
+static bool TestOrderCondition(const Order *order, TraceRestrictItem item)
+{
+ bool result = false;
+
+ if (order) {
+ DestinationID condvalue = GetTraceRestrictValue(item);
+ switch (static_cast(GetTraceRestrictAuxField(item))) {
+ case TROCAF_STATION:
+ result = order->IsType(OT_GOTO_STATION) && order->GetDestination() == condvalue;
+ break;
+
+ case TROCAF_WAYPOINT:
+ result = order->IsType(OT_GOTO_WAYPOINT) && order->GetDestination() == condvalue;
+ break;
+
+ case OT_GOTO_DEPOT:
+ result = order->IsType(OT_GOTO_DEPOT) && order->GetDestination() == condvalue;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+ }
+ return TestBinaryConditionCommon(item, result);
+}
+
+/**
+ * Test station condition
+ */
+static bool TestStationCondition(StationID station, TraceRestrictItem item)
+{
+ bool result = (GetTraceRestrictAuxField(item) == TROCAF_STATION) && (station == GetTraceRestrictValue(item));
+ return TestBinaryConditionCommon(item, result);
+
+}
+
+/**
+ * Execute program on train and store results in out
+ * @p v may not be NULL
+ * @p out should be zero-initialised
+ */
+void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInput &input, TraceRestrictProgramResult& out) const
+{
+ // static to avoid needing to re-alloc/resize on each execution
+ static std::vector condstack;
+ condstack.clear();
+
+ size_t size = this->items.size();
+ for (size_t i = 0; i < size; i++) {
+ TraceRestrictItem item = this->items[i];
+ TraceRestrictItemType type = GetTraceRestrictType(item);
+
+ if (IsTraceRestrictConditional(item)) {
+ TraceRestrictCondFlags condflags = GetTraceRestrictCondFlags(item);
+ TraceRestrictCondOp condop = GetTraceRestrictCondOp(item);
+
+ if (type == TRIT_COND_ENDIF) {
+ assert(!condstack.empty());
+ if (condflags & TRCF_ELSE) {
+ // else
+ assert(!(condstack.back() & TRCSF_SEEN_ELSE));
+ HandleCondition(condstack, condflags, true);
+ condstack.back() |= TRCSF_SEEN_ELSE;
+ } else {
+ // end if
+ condstack.pop_back();
+ }
+ } else {
+ uint16 condvalue = GetTraceRestrictValue(item);
+ bool result = false;
+ switch(type) {
+ case TRIT_COND_UNDEFINED:
+ result = false;
+ break;
+
+ case TRIT_COND_TRAIN_LENGTH:
+ result = TestCondition(CeilDiv(v->gcache.cached_total_length, TILE_SIZE), condop, condvalue);
+ break;
+
+ case TRIT_COND_MAX_SPEED:
+ result = TestCondition(v->GetDisplayMaxSpeed(), condop, condvalue);
+ break;
+
+ case TRIT_COND_CURRENT_ORDER:
+ result = TestOrderCondition(&(v->current_order), item);
+ break;
+
+ case TRIT_COND_NEXT_ORDER: {
+ if (v->orders.list == NULL) break;
+ if (v->orders.list->GetNumOrders() == 0) break;
+
+ const Order *current_order = v->GetOrder(v->cur_real_order_index);
+ for (const Order *order = v->orders.list->GetNext(current_order); order != current_order; order = v->orders.list->GetNext(order)) {
+ if (order->IsGotoOrder()) {
+ result = TestOrderCondition(order, item);
+ break;
+ }
+ }
+ break;
+ }
+
+ case TRIT_COND_LAST_STATION:
+ result = TestStationCondition(v->last_station_visited, item);
+ break;
+
+ case TRIT_COND_CARGO: {
+ bool have_cargo = false;
+ for (const Vehicle *v_iter = v; v_iter != NULL; v_iter = v_iter->Next()) {
+ if (v_iter->cargo_type == GetTraceRestrictValue(item) && v_iter->cargo_cap > 0) {
+ have_cargo = true;
+ break;
+ }
+ }
+ result = TestBinaryConditionCommon(item, have_cargo);
+ break;
+ }
+
+ case TRIT_COND_ENTRY_DIRECTION: {
+ bool direction_match;
+ switch (GetTraceRestrictValue(item)) {
+ case TRNTSV_NE:
+ case TRNTSV_SE:
+ case TRNTSV_SW:
+ case TRNTSV_NW:
+ direction_match = (static_cast(GetTraceRestrictValue(item)) == TrackdirToExitdir(ReverseTrackdir(input.trackdir)));
+ break;
+
+ case TRDTSV_FRONT:
+ direction_match = IsTileType(input.tile, MP_RAILWAY) && HasSignalOnTrackdir(input.tile, input.trackdir);
+ break;
+
+ case TRDTSV_BACK:
+ direction_match = IsTileType(input.tile, MP_RAILWAY) && !HasSignalOnTrackdir(input.tile, input.trackdir);
+ break;
+
+ default:
+ NOT_REACHED();
+ break;
+ }
+ result = TestBinaryConditionCommon(item, direction_match);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+ HandleCondition(condstack, condflags, result);
+ }
+ } else {
+ if (condstack.empty() || condstack.back() & TRCSF_ACTIVE) {
+ switch(type) {
+ case TRIT_PF_DENY:
+ if (GetTraceRestrictValue(item)) {
+ out.flags &= ~TRPRF_DENY;
+ } else {
+ out.flags |= TRPRF_DENY;
+ }
+ break;
+ case TRIT_PF_PENALTY:
+ out.penalty += GetTraceRestrictValue(item);
+ break;
+ default:
+ NOT_REACHED();
+ }
+ }
+ }
+ }
+ assert(condstack.empty());
+}
+
+/**
+ * Decrement ref count, only use when removing a mapping
+ */
+void TraceRestrictProgram::DecrementRefCount() {
+ assert(this->refcount > 0);
+ this->refcount--;
+ if (this->refcount == 0) {
+ delete this;
+ }
+}
+
+/**
+ * Validate a instruction list
+ * Returns successful result if program seems OK
+ * This only validates that conditional nesting is correct, at present
+ */
+CommandCost TraceRestrictProgram::Validate(const std::vector &items) {
+ // static to avoid needing to re-alloc/resize on each execution
+ static std::vector condstack;
+ condstack.clear();
+
+ size_t size = items.size();
+ for (size_t i = 0; i < size; i++) {
+ TraceRestrictItem item = items[i];
+ TraceRestrictItemType type = GetTraceRestrictType(item);
+
+ if (IsTraceRestrictConditional(item)) {
+ TraceRestrictCondFlags condflags = GetTraceRestrictCondFlags(item);
+
+ if (type == TRIT_COND_ENDIF) {
+ if (condstack.empty()) {
+ return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF); // else/endif with no starting if
+ }
+ if (condflags & TRCF_ELSE) {
+ // else
+ if (condstack.back() & TRCSF_SEEN_ELSE) {
+ return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // Two else clauses
+ }
+ HandleCondition(condstack, condflags, true);
+ condstack.back() |= TRCSF_SEEN_ELSE;
+ } else {
+ // end if
+ condstack.pop_back();
+ }
+ } else {
+ if (condflags & (TRCF_OR | TRCF_ELSE)) { // elif/orif
+ if (condstack.empty()) {
+ return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_ELIF_NO_IF); // Pre-empt assertions in HandleCondition
+ }
+ if (condstack.back() & TRCSF_SEEN_ELSE) {
+ return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // else clause followed by elif/orif
+ }
+ }
+ HandleCondition(condstack, condflags, true);
+ }
+ }
+ }
+ if(!condstack.empty()) {
+ return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_END_CONDSTACK);
+ }
+ return CommandCost();
+}
+
+/**
+ * Set the value and aux field of @p item, as per the value type in @p value_type
+ */
+void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueType value_type)
+{
+ switch (value_type) {
+ case TRVT_NONE:
+ case TRVT_INT:
+ case TRVT_DENY:
+ case TRVT_SPEED:
+ SetTraceRestrictValue(item, 0);
+ SetTraceRestrictAuxField(item, 0);
+ break;
+
+ case TRVT_ORDER:
+ SetTraceRestrictValue(item, INVALID_STATION);
+ SetTraceRestrictAuxField(item, TROCAF_STATION);
+ break;
+
+ case TRVT_CARGO_ID:
+ assert(_sorted_standard_cargo_specs_size > 0);
+ SetTraceRestrictValue(item, _sorted_cargo_specs[0]->Index());
+ SetTraceRestrictAuxField(item, 0);
+ break;
+
+ case TRVT_DIRECTION:
+ SetTraceRestrictValue(item, TRDTSV_FRONT);
+ SetTraceRestrictAuxField(item, 0);
+ break;
+
+ default:
+ NOT_REACHED();
+ break;
+ }
+}
+
+/**
+ * Set the type field of a TraceRestrictItem, and resets any other fields which are no longer valid/meaningful to sensible defaults
+ */
+void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItemType type)
+{
+ if (item != 0) {
+ assert(GetTraceRestrictType(item) != TRIT_NULL);
+ assert(IsTraceRestrictConditional(item) == IsTraceRestrictTypeConditional(type));
+ }
+ assert(type != TRIT_NULL);
+
+ TraceRestrictTypePropertySet old_properties = GetTraceRestrictTypeProperties(item);
+ SetTraceRestrictType(item, type);
+ TraceRestrictTypePropertySet new_properties = GetTraceRestrictTypeProperties(item);
+
+ if (old_properties.cond_type != new_properties.cond_type ||
+ old_properties.value_type != new_properties.value_type) {
+ SetTraceRestrictCondOp(item, TRCO_IS);
+ SetTraceRestrictValueDefault(item, new_properties.value_type);
+ }
+}
+
+/**
+ * Sets the "signal has a trace restrict mapping" bit
+ * This looks for mappings with that tile index
+ */
+void SetIsSignalRestrictedBit(TileIndex t)
+{
+ // First mapping for this tile, or later
+ TraceRestrictMapping::iterator lower_bound = _tracerestrictprogram_mapping.lower_bound(MakeTraceRestrictRefId(t, static_cast