From 5f1b148cf9ea64e41c34a6b848caf598ff641356 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 22 Jul 2015 00:28:53 +0100 Subject: [PATCH 01/15] Initial minimal working tracerestrict implementation. This is a port of the tracerestrict/routing restrictions feature from TTDPatch. At present this implements if tests (train length only), and pathfinder deny and penalty actions. This requires the use of YAPF. Note that restrictions are only evaluated within the YAPF lookahead distance. --- source.list | 5 + src/command.cpp | 4 + src/command_type.h | 2 + src/lang/english.txt | 48 ++ src/misc.cpp | 2 + src/openttd.cpp | 2 + src/pathfinder/yapf/yapf_costrail.hpp | 39 ++ src/rail_cmd.cpp | 2 + src/rail_gui.cpp | 17 + src/rail_map.h | 12 + src/saveload/afterload.cpp | 2 + src/saveload/saveload.cpp | 5 +- src/saveload/saveload_internal.h | 1 + src/saveload/tracerestrict_sl.cpp | 92 +++ src/script/api/script_window.hpp | 1 + src/tracerestrict.cpp | 510 ++++++++++++++++ src/tracerestrict.h | 284 +++++++++ src/tracerestrict_gui.cpp | 809 ++++++++++++++++++++++++++ src/widgets/rail_widget.h | 1 + src/window_type.h | 6 + 20 files changed, 1843 insertions(+), 1 deletion(-) create mode 100644 src/saveload/tracerestrict_sl.cpp create mode 100644 src/tracerestrict.cpp create mode 100644 src/tracerestrict.h create mode 100644 src/tracerestrict_gui.cpp diff --git a/source.list b/source.list index df35cdd26e..db128d271b 100644 --- a/source.list +++ b/source.list @@ -1193,3 +1193,8 @@ thread/thread.h #else thread/thread_none.cpp #end + +tracerestrict.h +tracerestrict.cpp +tracerestrict_gui.cpp +saveload/tracerestrict_sl.cpp diff --git a/src/command.cpp b/src/command.cpp index 959610cd28..96d88f3854 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -198,6 +198,8 @@ CommandProc CmdSetTimetableStart; CommandProc CmdOpenCloseAirport; +CommandProc CmdProgramSignalTraceRestrict; + #define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type} /** @@ -354,6 +356,8 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdSetTimetableStart, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_TIMETABLE_START DEF_CMD(CmdOpenCloseAirport, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_OPEN_CLOSE_AIRPORT + + DEF_CMD(CmdProgramSignalTraceRestrict, 0, CMDT_OTHER_MANAGEMENT ), // CMD_PROGRAM_TRACERESTRICT_SIGNAL }; /*! diff --git a/src/command_type.h b/src/command_type.h index f318216acc..1f99797ada 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -329,6 +329,8 @@ enum Commands { CMD_OPEN_CLOSE_AIRPORT, ///< open/close an airport to incoming aircraft + CMD_PROGRAM_TRACERESTRICT_SIGNAL, ///< modify a signal tracerestrict program + CMD_END, ///< Must ALWAYS be on the end of this list!! (period) }; diff --git a/src/lang/english.txt b/src/lang/english.txt index ad29b3d596..8b690d96d9 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2370,6 +2370,54 @@ 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_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_UNDEFINED :undefined +STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER :{STRING} {STRING} {STRING} {COMMA} 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_VALUE_CAPTION :{WHITE}Value +STR_TRACE_RESTRICT_CAPTION :{WHITE}Routefinding restriction +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_GOTO_SIGNAL_TOOLTIP :{BLACK}Go to signal +STR_TRACE_RESTRICT_INSERT :{BLACK}Insert +STR_TRACE_RESTRICT_REMOVE :{BLACK}Remove +STR_TRACE_RESTRICT_INSERT_TOOLTIP :{BLACK}Insert an instruction +STR_TRACE_RESTRICT_REMOVE_TOOLTIP :{BLACK}Remove the selected instruction +STR_TRACE_RESTRICT_SIGNAL_GUI_TOOLTIP :{BLACK}Routefinding restriction +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 + # Bridge selection window STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Select Rail Bridge STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Select Road Bridge 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/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..81c362ae31 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(), 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..29efc63de0 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) { 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..a42c84f784 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -479,6 +479,18 @@ static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td) !HasSignalOnTrackdir(tile, td) && IsOnewaySignal(tile, TrackdirToTrack(td)); } +static inline bool IsRestrictedSignal(TileIndex t) +{ + assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); + return (bool) GB(_m[t].m2, 12, 1); +} + +static inline void SetRestrictedSignal(TileIndex t, bool is_restricted) +{ + assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); + SB(_m[t].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/saveload.cpp b/src/saveload/saveload.cpp index bd3c83d139..3c239def5b 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -262,8 +262,9 @@ * 192 26700 * 193 26802 * 194 26881 1.5.x + * 2000 Trace restrict patch */ -extern const uint16 SAVEGAME_VERSION = 194; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 2000; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading @@ -447,6 +448,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[] = { @@ -483,6 +485,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..a646c3a72a --- /dev/null +++ b/src/saveload/tracerestrict_sl.cpp @@ -0,0 +1,92 @@ +/* $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() +}; + +static void Load_TRRM() +{ + int index; + while ((index = SlIterateArray()) != -1) { + TraceRestrictMappingItem &item = _tracerestrictprogram_mapping[index]; + SlObject(&item, _trace_restrict_mapping_desc); + } +} + +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); + } +} + +struct TraceRestrictProgramStub { + uint32 length; +}; + +static const SaveLoad _trace_restrict_program_stub_desc[] = { + SLE_VAR(TraceRestrictProgramStub, length, SLE_UINT32), + SLE_END() +}; + +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()); + } +} + +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); +} + +static void Save_TRRP() +{ + TraceRestrictProgram *prog; + + FOR_ALL_TRACE_RESTRICT_PROGRAMS(prog) { + SlSetArrayIndex(prog->index); + SlAutolength((AutolengthProc*) RealSave_TRRP, prog); + } +} + +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}, + { 'TRRP', Save_TRRP, Load_TRRP, NULL, NULL, CH_ARRAY | CH_LAST}, +}; 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/tracerestrict.cpp b/src/tracerestrict.cpp new file mode 100644 index 0000000000..74b182b4f0 --- /dev/null +++ b/src/tracerestrict.cpp @@ -0,0 +1,510 @@ +/* + * 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.h Header file for Trace Restriction. */ + +#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 "pathfinder/yapf/yapf_cache.h" +#include + +/** Initialize theprogram pool */ +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(); +} + +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) + +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; + } +} + +/// 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; + } +} + +/// Execute program on train and store results in out +void TraceRestrictProgram::Execute(const Train* v, 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; + + 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()); +} + +void TraceRestrictProgram::DecrementRefCount() { + assert(this->refcount > 0); + this->refcount--; + if (this->refcount == 0) { + delete this; + } +} + +/// 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(); +} + +void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueType value_type) +{ + switch (value_type) { + case TRVT_NONE: + case TRVT_INT: + case TRVT_DENY: + SetTraceRestrictValue(item, 0); + break; + + default: + NOT_REACHED(); + break; + } +} + +/// Set the type field of a TraceRestrictItem, and +/// reset 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); + } +} + +void SetIsSignalRestrictedBit(TileIndex t) +{ + // First mapping for this tile, or later + TraceRestrictMapping::iterator lower_bound = _tracerestrictprogram_mapping.lower_bound(MakeTraceRestrictRefId(t, static_cast(0))); + + // First mapping for next tile, or later + TraceRestrictMapping::iterator upper_bound = _tracerestrictprogram_mapping.lower_bound(MakeTraceRestrictRefId(t + 1, static_cast(0))); + + // If iterators are the same, there are no mappings for this tile + SetRestrictedSignal(t, lower_bound != upper_bound); +} + +void TraceRestrictCreateProgramMapping(TraceRestrictRefId ref, TraceRestrictProgram *prog) +{ + std::pair insert_result = + _tracerestrictprogram_mapping.insert(std::make_pair(ref, TraceRestrictMappingItem(prog->index))); + + if (!insert_result.second) { + // value was not inserted, there is an existing mapping + // unref the existing mapping before updating it + _tracerestrictprogram_pool.Get(insert_result.first->second.program_id)->DecrementRefCount(); + insert_result.first->second = prog->index; + } + prog->IncrementRefCount(); + + TileIndex tile = GetTraceRestrictRefIdTileIndex(ref); + Track track = GetTraceRestrictRefIdTrack(ref); + SetIsSignalRestrictedBit(tile); + MarkTileDirtyByTile(tile); + YapfNotifyTrackLayoutChange(tile, track); +} + +void TraceRestrictRemoveProgramMapping(TraceRestrictRefId ref) +{ + TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.find(ref); + if (iter != _tracerestrictprogram_mapping.end()) { + // Found + _tracerestrictprogram_pool.Get(iter->second.program_id)->DecrementRefCount(); + _tracerestrictprogram_mapping.erase(iter); + + TileIndex tile = GetTraceRestrictRefIdTileIndex(ref); + Track track = GetTraceRestrictRefIdTrack(ref); + SetIsSignalRestrictedBit(tile); + MarkTileDirtyByTile(tile); + YapfNotifyTrackLayoutChange(tile, track); + } +} + +/// Gets the trace restrict program for the tile/track ref ID identified by @p ref. +/// An empty program will be constructed if none exists, and @p create_new is true +/// unless the pool is full +TraceRestrictProgram *GetTraceRestrictProgram(TraceRestrictRefId ref, bool create_new) +{ + // Optimise for lookup, creating doesn't have to be that fast + + TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.find(ref); + if (iter != _tracerestrictprogram_mapping.end()) { + // Found + return _tracerestrictprogram_pool.Get(iter->second.program_id); + } else if (create_new) { + // Not found + + // Create new pool item + if (!TraceRestrictProgram::CanAllocateItem()) { + return NULL; + } + TraceRestrictProgram *prog = new TraceRestrictProgram(); + + // Create new mapping to pool item + TraceRestrictCreateProgramMapping(ref, prog); + return prog; + } else { + return NULL; + } +} + +/// Notify that a signal is being removed +/// Remove any trace restrict items associated with it +void TraceRestrictNotifySignalRemoval(TileIndex tile, Track track) +{ + TraceRestrictRefId ref = MakeTraceRestrictRefId(tile, track); + TraceRestrictRemoveProgramMapping(ref); + DeleteWindowById(WC_TRACE_RESTRICT, ref); +} + +void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg) +{ + uint32 p1 = 0; + SB(p1, 0, 3, track); + SB(p1, 3, 5, type); + assert(offset < (1 << 16)); + SB(p1, 8, 16, offset); + DoCommandP(tile, p1, value, CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG(error_msg)); +} + +/** + * The main command for editing a signal tracerestrict program. + * @param tile The tile which contains the signal. + * @param flags Internal command handler stuff. + * @param p1 Bitstuffed items + * @param p2 Item, for insert and modify operations + * @return the cost of this operation (which is free), or an error + */ +CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + Track track = static_cast(GB(p1, 0, 3)); + TraceRestrictDoCommandType type = static_cast(GB(p1, 3, 5)); + uint32 offset = GB(p1, 8, 16); + TraceRestrictItem item = static_cast(p2); + + // Check tile ownership + CommandCost ret = CheckTileOwnership(tile); + if (ret.Failed()) { + return ret; + } + + // Check that there actually is a signal here + if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) { + return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); + } + if (!HasSignalOnTrack(tile, track)) { + return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); + } + + bool can_make_new = (type == TRDCT_INSERT_ITEM) && (flags & DC_EXEC); + bool need_existing = (type != TRDCT_INSERT_ITEM); + TraceRestrictProgram *prog = GetTraceRestrictProgram(MakeTraceRestrictRefId(tile, track), can_make_new); + if (need_existing && !prog) { + return_cmd_error(STR_TRACE_RESTRICT_ERROR_NO_PROGRAM); + } + + uint32 offset_limit_exclusive = ((type == TRDCT_INSERT_ITEM) ? 1 : 0); + if (prog) offset_limit_exclusive += prog->items.size(); + + if (offset >= offset_limit_exclusive) { + return_cmd_error(STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE); + } + + // copy program + std::vector items; + if (prog) items = prog->items; + + switch (type) { + case TRDCT_INSERT_ITEM: + items.insert(items.begin() + offset, item); + if (IsTraceRestrictConditional(item) && + GetTraceRestrictCondFlags(item) == 0 && + GetTraceRestrictType(item) != TRIT_COND_ENDIF) { + // this is an opening if block, insert a corresponding end if + TraceRestrictItem endif_item = 0; + SetTraceRestrictType(endif_item, TRIT_COND_ENDIF); + items.insert(items.begin() + offset + 1, endif_item); + } + break; + + case TRDCT_MODIFY_ITEM: { + TraceRestrictItem old_item = items[offset]; + if (IsTraceRestrictConditional(old_item) != IsTraceRestrictConditional(item)) { + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY); + } + items[offset] = item; + break; + } + + case TRDCT_REMOVE_ITEM: { + TraceRestrictItem old_item = items[offset]; + if (IsTraceRestrictConditional(old_item)) { + bool remove_whole_block = false; + if (GetTraceRestrictCondFlags(old_item) == 0) { + if (GetTraceRestrictType(old_item) == TRIT_COND_ENDIF) { + // this is an end if, can't remove these + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ENDIF); + } else { + // this is an opening if + remove_whole_block = true; + } + } + + uint32 recursion_depth = 1; + std::vector::iterator remove_start = items.begin() + offset; + std::vector::iterator remove_end = remove_start + 1; + + // iterate until matching end block found + for (; remove_end != items.end(); ++remove_end) { + TraceRestrictItem current_item = *remove_end; + if (IsTraceRestrictConditional(current_item)) { + if (GetTraceRestrictCondFlags(current_item) == 0) { + if (GetTraceRestrictType(current_item) == TRIT_COND_ENDIF) { + // this is an end if + recursion_depth--; + if (recursion_depth == 0) { + if (remove_whole_block) { + // inclusively remove up to here + ++remove_end; + break; + } else { + // exclusively remove up to here + break; + } + } + } else { + // this is an opening if + recursion_depth++; + } + } else { + // this is an else/or type block + if (recursion_depth == 1 && !remove_whole_block) { + // exclusively remove up to here + recursion_depth = 0; + break; + } + } + } + } + if (recursion_depth != 0) return CMD_ERROR; // ran off the end + items.erase(remove_start, remove_end); + } else { + items.erase(items.begin() + offset); + } + break; + } + + default: + NOT_REACHED(); + break; + } + + CommandCost validation_result = TraceRestrictProgram::Validate(items); + if (validation_result.Failed()) { + return validation_result; + } + + if (flags & DC_EXEC) { + assert(prog); + + // move in modified program + prog->items.swap(items); + + if (prog->items.size() == 0 && prog->refcount == 1) { + // program is empty, and this tile is the only reference to it + // so delete it, as it's redundant + TraceRestrictRemoveProgramMapping(MakeTraceRestrictRefId(tile, track)); + } + + // update windows + InvalidateWindowClassesData(WC_TRACE_RESTRICT); + } + + return CommandCost(); +} diff --git a/src/tracerestrict.h b/src/tracerestrict.h new file mode 100644 index 0000000000..1309220669 --- /dev/null +++ b/src/tracerestrict.h @@ -0,0 +1,284 @@ +/* + * 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.h Header file for Trace Restriction. */ + +#ifndef TRACERESTRICT_H +#define TRACERESTRICT_H + +#include "stdafx.h" +#include "core/bitmath_func.hpp" +#include "core/enum_type.hpp" +#include "core/pool_type.hpp" +#include "command_func.h" +#include "rail_map.h" +#include "tile_type.h" +#include +#include + +struct Train; + +/** Unique identifiers for a trace restrict nodes. */ +typedef uint32 TraceRestrictProgramID; +struct TraceRestrictProgram; + +typedef uint32 TraceRestrictRefId; + +/** Type of the pool for trace restrict programs. */ +typedef Pool TraceRestrictProgramPool; +/** The actual pool for trace restrict nodes. */ +extern TraceRestrictProgramPool _tracerestrictprogram_pool; + +#define FOR_ALL_TRACE_RESTRICT_PROGRAMS_FROM(var, start) FOR_ALL_ITEMS_FROM(TraceRestrictProgram, tr_index, var, start) +#define FOR_ALL_TRACE_RESTRICT_PROGRAMS(var) FOR_ALL_TRACE_RESTRICT_PROGRAMS_FROM(var, 0) + +struct TraceRestrictMappingItem { + TraceRestrictProgramID program_id; + + TraceRestrictMappingItem() { } + + TraceRestrictMappingItem(TraceRestrictProgramID program_id_) + : program_id(program_id_) { } +}; + +typedef std::map TraceRestrictMapping; +extern TraceRestrictMapping _tracerestrictprogram_mapping; + +void ClearTraceRestrictMapping(); + +/// Of the fields below, the type and cond flags seem the most likely +/// to need future expansion, hence the reserved bits are placed +/// immediately after them +enum TraceRestrictItemFlagAllocation { + TRIFA_TYPE_COUNT = 5, + TRIFA_TYPE_OFFSET = 0, + + /* 3 bits reserved for future use */ + + TRIFA_COND_FLAGS_COUNT = 2, + TRIFA_COND_FLAGS_OFFSET = 8, + + /* 3 bits reserved for future use */ + + TRIFA_COND_OP_COUNT = 3, + TRIFA_COND_OP_OFFSET = 13, + + TRIFA_VALUE_COUNT = 16, + TRIFA_VALUE_OFFSET = 16, +}; + +enum TraceRestrictItemType { + TRIT_NULL = 0, + TRIT_PF_DENY = 1, + TRIT_PF_PENALTY = 2, + + TRIT_COND_BEGIN = 8, ///< Start of conditional item types + TRIT_COND_ENDIF = 8, ///< This is an endif block or an else block + TRIT_COND_UNDEFINED = 9, ///< This condition has no type defined (evaluate as false) + TRIT_COND_TRAIN_LENGTH = 10, ///< Test train length + /* space up to 31 */ +}; + +/* no flags set indicates end if for TRIT_COND_ENDIF, if otherwise */ +enum TraceRestrictCondFlags { + TRCF_ELSE = 1 << 0, + TRCF_OR = 1 << 1, +}; +DECLARE_ENUM_AS_BIT_SET(TraceRestrictCondFlags) + +enum TraceRestictNullTypeSpecialValue { + TRNTSV_NULL = 0, + TRNTSV_START = 1, + TRNTSV_END = 2, +}; + +enum TraceRestrictCondOp { + TRCO_IS = 0, + TRCO_ISNOT = 1, + TRCO_LT = 2, + TRCO_LTE = 3, + TRCO_GT = 4, + TRCO_GTE = 5, + /* space up to 7 */ +}; + +enum TraceRestrictProgramResultFlags { + TRPRF_DENY = 1 << 0, +}; +DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags) + +struct TraceRestrictProgramResult { + uint32 penalty; + TraceRestrictProgramResultFlags flags; + + TraceRestrictProgramResult() + : penalty(0), flags(static_cast(0)) { } +}; + +typedef uint32 TraceRestrictItem; + +struct TraceRestrictProgram : TraceRestrictProgramPool::PoolItem<&_tracerestrictprogram_pool> { + std::vector items; + uint32 refcount; + + TraceRestrictProgram() + : refcount(0) { } + + void Execute(const Train *v, TraceRestrictProgramResult &out) const; + + void IncrementRefCount() { refcount++; } + + void DecrementRefCount(); + + static CommandCost Validate(const std::vector &items); + + CommandCost Validate() const { return TraceRestrictProgram::Validate(items); } +}; + +static inline TraceRestrictItemType GetTraceRestrictType(TraceRestrictItem item) +{ + return static_cast(GB(item, TRIFA_TYPE_OFFSET, TRIFA_TYPE_COUNT)); +} + +static inline TraceRestrictCondFlags GetTraceRestrictCondFlags(TraceRestrictItem item) +{ + return static_cast(GB(item, TRIFA_COND_FLAGS_OFFSET, TRIFA_COND_FLAGS_COUNT)); +} + +static inline TraceRestrictCondOp GetTraceRestrictCondOp(TraceRestrictItem item) +{ + return static_cast(GB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT)); +} + +static inline uint16 GetTraceRestrictValue(TraceRestrictItem item) +{ + return static_cast(GB(item, TRIFA_VALUE_OFFSET, TRIFA_VALUE_COUNT)); +} + +static inline void SetTraceRestrictType(TraceRestrictItem &item, TraceRestrictItemType type) +{ + SB(item, TRIFA_TYPE_OFFSET, TRIFA_TYPE_COUNT, type); +} + +static inline void SetTraceRestrictCondOp(TraceRestrictItem &item, TraceRestrictCondOp condop) +{ + SB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT, condop); +} + +void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItemType type); + +static inline void SetTraceRestrictValue(TraceRestrictItem &item, uint16 value) +{ + SB(item, TRIFA_VALUE_OFFSET, TRIFA_VALUE_COUNT, value); +} + +static inline bool IsTraceRestrictTypeConditional(TraceRestrictItemType type) +{ + return type >= TRIT_COND_BEGIN; +} + +static inline bool IsTraceRestrictConditional(TraceRestrictItem item) +{ + return IsTraceRestrictTypeConditional(GetTraceRestrictType(item)); +} + +enum TraceRestrictConditionOpType { + TRCOT_NONE = 0, ///< takes no condition op + TRCOT_BINARY = 1, ///< takes "is" and "is not" condition ops + TRCOT_ALL = 2, ///< takes all condition ops (i.e. all relational ops) +}; + +enum TraceRestrictValueType { + TRVT_NONE = 0, ///< value field not used (set to 0) + TRVT_SPECIAL = 1, ///< special handling of value field + TRVT_INT = 2, ///< takes an integer value + TRVT_DENY = 3, ///< takes a value 0 = deny, 1 = allow (cancel previous deny) +}; + +struct TraceRestrictTypePropertySet { + TraceRestrictConditionOpType cond_type; + TraceRestrictValueType value_type; +}; + +static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceRestrictItem item) +{ + TraceRestrictTypePropertySet out; + + if (GetTraceRestrictType(item) == TRIT_NULL) { + out.cond_type = TRCOT_NONE; + out.value_type = TRVT_SPECIAL; + } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF || + GetTraceRestrictType(item) == TRIT_COND_UNDEFINED) { + out.cond_type = TRCOT_NONE; + out.value_type = TRVT_NONE; + } else if (IsTraceRestrictConditional(item)) { + out.cond_type = TRCOT_ALL; + out.value_type = TRVT_INT; + } else { + out.cond_type = TRCOT_NONE; + if (GetTraceRestrictType(item) == TRIT_PF_PENALTY) { + out.value_type = TRVT_INT; + } else if (GetTraceRestrictType(item) == TRIT_PF_DENY) { + out.value_type = TRVT_DENY; + } else { + out.value_type = TRVT_NONE; + } + } + + return out; +} + +static inline TraceRestrictRefId MakeTraceRestrictRefId(TileIndex t, Track track) +{ + return (t << 3) | track; +} + +static inline TileIndex GetTraceRestrictRefIdTileIndex(TraceRestrictRefId ref) +{ + return static_cast(ref >> 3); +} + +static inline Track GetTraceRestrictRefIdTrack(TraceRestrictRefId ref) +{ + return static_cast(ref & 7); +} + +void TraceRestrictCreateProgramMapping(TraceRestrictRefId ref, TraceRestrictProgram *prog); +void TraceRestrictRemoveProgramMapping(TraceRestrictRefId ref); + +/// Gets the signal program for the tile identified by @p t and @p track. +/// An empty program will be constructed if none exists, and create_new is true +/// unless the pool is full +TraceRestrictProgram *GetTraceRestrictProgram(TraceRestrictRefId ref, bool create_new); + +/// Notify that a signal is being removed +/// Remove any trace restrict items associated with it +void TraceRestrictNotifySignalRemoval(TileIndex tile, Track track); + +/// Gets the signal program for the tile identified by @p t and @p track, or NULL +static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIndex t, Track track) +{ + if (IsRestrictedSignal(t)) { + return GetTraceRestrictProgram(MakeTraceRestrictRefId(t, track), false); + } else { + return NULL; + } +} + +enum TraceRestrictDoCommandType { + TRDCT_INSERT_ITEM = 0, + TRDCT_MODIFY_ITEM = 1, + TRDCT_REMOVE_ITEM = 2, +}; + +void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg); + +CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); + +void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); + +#endif /* TRACERESTRICT_H */ diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp new file mode 100644 index 0000000000..1cfd46ccc5 --- /dev/null +++ b/src/tracerestrict_gui.cpp @@ -0,0 +1,809 @@ +/* $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_gui.cpp GUI related to signal tracerestrict */ + +#include "stdafx.h" +#include "tracerestrict.h" +#include "command_func.h" +#include "window_func.h" +#include "strings_func.h" +#include "string_func.h" +#include "viewport_func.h" +#include "textbuf_gui.h" +#include "company_func.h" +#include "widgets/dropdown_func.h" +#include "gui.h" +#include "gfx_func.h" +#include "rail_map.h" +#include "tile_cmd.h" +#include "error.h" +#include "table/sprites.h" + +enum TraceRestrictWindowWidgets { + TR_WIDGET_CAPTION, + TR_WIDGET_INSTRUCTION_LIST, + TR_WIDGET_SCROLLBAR, + + TR_WIDGET_SEL_TOP_LEFT, + TR_WIDGET_SEL_TOP_MIDDLE, + TR_WIDGET_SEL_TOP_RIGHT, + + TR_WIDGET_TYPE, + TR_WIDGET_COMPARATOR, + TR_WIDGET_VALUE_INT, + TR_WIDGET_VALUE_DROPDOWN, + + TR_WIDGET_BLANK_L, + TR_WIDGET_BLANK_M, + TR_WIDGET_BLANK_R, + + TR_WIDGET_GOTO_SIGNAL, + TR_WIDGET_INSERT, + TR_WIDGET_REMOVE, +}; + +enum PanelWidgets { + // Left + DPL_TYPE = 0, + DPL_BLANK, + + // Middle + DPM_COMPARATOR = 0, + DPM_BLANK, + + // Right + DPR_VALUE_INT = 0, + DPR_VALUE_DROPDOWN, + DPR_BLANK, +}; + +/// value_array *must* be at least as long as string_array, +/// where the length of string_array is defined as the offset +/// of the first INVALID_STRING_ID +struct TraceRestrictDropDownListSet { + const StringID *string_array; + const uint *value_array; +}; + +static const StringID _program_insert_str[] = { + STR_TRACE_RESTRICT_CONDITIONAL_IF, + STR_TRACE_RESTRICT_PF_DENY, + STR_TRACE_RESTRICT_PF_PENALTY, + INVALID_STRING_ID +}; +static const uint _program_insert_val[] = { + TRIT_COND_UNDEFINED, + TRIT_PF_DENY, + TRIT_PF_PENALTY, +}; + +static const TraceRestrictDropDownListSet _program_insert = { + _program_insert_str, _program_insert_val, +}; + +static const StringID _deny_value_str[] = { + STR_TRACE_RESTRICT_PF_DENY, + STR_TRACE_RESTRICT_PF_ALLOW, + INVALID_STRING_ID +}; +static const uint _deny_value_val[] = { + 0, + 1, +}; + +static const TraceRestrictDropDownListSet _deny_value = { + _deny_value_str, _deny_value_val, +}; + +static int GetDropDownListIndexByValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok) +{ + const StringID *string_array = list_set->string_array; + const uint *value_array = list_set->value_array; + + for (; *string_array != INVALID_STRING_ID; string_array++, value_array++) { + if (*value_array == value) { + return value_array - list_set->value_array; + } + } + assert(missing_ok == true); + return -1; +} + +static StringID GetDropDownStringByValue(const TraceRestrictDropDownListSet *list_set, uint value) +{ + return list_set->string_array[GetDropDownListIndexByValue(list_set, value, false)]; +} + +static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictItemType type) +{ + static const StringID str_action[] = { + STR_TRACE_RESTRICT_PF_DENY, + STR_TRACE_RESTRICT_PF_PENALTY, + INVALID_STRING_ID, + }; + static const uint val_action[] = { + TRIT_PF_DENY, + TRIT_PF_PENALTY, + }; + static const TraceRestrictDropDownListSet set_action = { + str_action, val_action, + }; + + static const StringID str_cond[] = { + STR_TRACE_RESTRICT_VARIABLE_TRAIN_LENGTH, + STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, + INVALID_STRING_ID, + }; + static const uint val_cond[] = { + TRIT_COND_TRAIN_LENGTH, + TRIT_COND_UNDEFINED, + }; + static const TraceRestrictDropDownListSet set_cond = { + str_cond, val_cond, + }; + + return IsTraceRestrictTypeConditional(type) ? &set_cond : &set_action; +} + +static StringID GetTypeString(TraceRestrictItemType type) +{ + return GetDropDownStringByValue(GetTypeDropDownListSet(type), type); +} + +static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestrictConditionOpType type) +{ + static const StringID str_long[] = { + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS, + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_NOT_EQUALS, + 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, + INVALID_STRING_ID, + }; + static const uint val_long[] = { + TRCO_IS, + TRCO_ISNOT, + TRCO_LT, + TRCO_LTE, + TRCO_GT, + TRCO_GTE, + }; + static const TraceRestrictDropDownListSet set_long = { + str_long, val_long, + }; + + static const StringID str_short[] = { + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS, + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_NOT_EQUALS, + INVALID_STRING_ID, + }; + static const uint val_short[] = { + TRCO_IS, + TRCO_ISNOT, + }; + static const TraceRestrictDropDownListSet set_short = { + str_short, val_short, + }; + + switch (type) { + case TRCOT_NONE: + return NULL; + + case TRCOT_BINARY: + return &set_short; + + case TRCOT_ALL: + return &set_long; + } + NOT_REACHED(); + return NULL; +} + +static const StringID _program_cond_type[] = { + /* 0 */ STR_TRACE_RESTRICT_CONDITIONAL_IF, + /* TRCF_ELSE */ STR_TRACE_RESTRICT_CONDITIONAL_ELIF, + /* TRCF_OR */ STR_TRACE_RESTRICT_CONDITIONAL_ORIF, +}; + +/** + * Draws an instruction in the programming GUI + * @param instruction The instruction to draw + * @param y Y position for drawing + * @param selected True, if the order is selected + * @param indent How many levels the instruction is indented + * @param left Left border for text drawing + * @param right Right border for text drawing + */ +static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, int indent, int left, int right) +{ + StringID instruction_string = INVALID_STRING_ID; + + TraceRestrictTypePropertySet properties = GetTraceRestrictTypeProperties(item); + + if (IsTraceRestrictConditional(item)) { + if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { + if (GetTraceRestrictCondFlags(item) & TRCF_ELSE) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ELSE; + } else { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENDIF; + } + } else if (GetTraceRestrictType(item) == TRIT_COND_UNDEFINED) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED; + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); + } else if (properties.value_type == TRVT_INT) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER; + + assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(1, GetTypeString(GetTraceRestrictType(item))); + SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties.cond_type), GetTraceRestrictCondOp(item))); + SetDParam(3, GetTraceRestrictValue(item)); + } else { + NOT_REACHED(); + } + } else { + switch (GetTraceRestrictType(item)) { + case TRIT_NULL: + switch (GetTraceRestrictValue(item)) { + case TRNTSV_START: + instruction_string = STR_TRACE_RESTRICT_START; + break; + + case TRNTSV_END: + instruction_string = STR_TRACE_RESTRICT_END; + break; + + default: + NOT_REACHED(); + break; + } + break; + + case TRIT_PF_DENY: + instruction_string = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW_LONG : STR_TRACE_RESTRICT_PF_DENY; + break; + + case TRIT_PF_PENALTY: + instruction_string = STR_TRACE_RESTRICT_PF_PENALTY_ITEM; + SetDParam(0, GetTraceRestrictValue(item)); + break; + + default: + NOT_REACHED(); + break; + } + } + + DrawString(left + indent * 16, right, y, instruction_string, selected ? TC_WHITE : TC_BLACK); +} + +class TraceRestrictWindow: public Window { + TileIndex tile; + Track track; + int selected_instruction; // NB: this is offset by one due to the display of the "start" item + Scrollbar *vscroll; + std::map drop_down_list_mapping; + TraceRestrictItem expecting_inserted_item; + +public: + TraceRestrictWindow(WindowDesc *desc, TileIndex tile, Track track) + : Window(desc) + { + this->tile = tile; + this->track = track; + this->selected_instruction = -1; + this->expecting_inserted_item = static_cast(0); + + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(TR_WIDGET_SCROLLBAR); + this->FinishInitNested(MakeTraceRestrictRefId(tile, track)); + + this->ReloadProgramme(); + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case TR_WIDGET_INSTRUCTION_LIST: { + int sel = this->GetInstructionFromPt(pt.y); + + this->DeleteChildWindows(); + HideDropDownMenu(this); + + if (sel == -1 || this->GetOwner() != _local_company) { + // Deselect + this->selected_instruction = -1; + } else { + this->selected_instruction = sel; + } + + this->expecting_inserted_item = static_cast(0); + + this->UpdateButtonState(); + break; + } + + case TR_WIDGET_INSERT: { + if (this->GetOwner() != _local_company || this->selected_instruction < 1) { + return; + } + this->ShowDropDownListWithValue(&_program_insert, 0, true, TR_WIDGET_INSERT, 0, 0, 0); + break; + } + + case TR_WIDGET_REMOVE: { + TraceRestrictItem item = this->GetSelected(); + if (this->GetOwner() != _local_company || item == 0) { + return; + } + + TraceRestrictDoCommandP(tile, track, TRDCT_REMOVE_ITEM, this->selected_instruction - 1, 0, STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM); + break; + } + + case TR_WIDGET_TYPE: { + TraceRestrictItem item = this->GetSelected(); + TraceRestrictItemType type = GetTraceRestrictType(item); + + if (type != TRIT_NULL) { + this->ShowDropDownListWithValue(GetTypeDropDownListSet(type), type, false, TR_WIDGET_TYPE, 0, 0, 0); + } + break; + } + + case TR_WIDGET_COMPARATOR: { + TraceRestrictItem item = this->GetSelected(); + const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(GetTraceRestrictTypeProperties(item).cond_type); + if (list_set) { + this->ShowDropDownListWithValue(list_set, GetTraceRestrictCondOp(item), false, TR_WIDGET_COMPARATOR, 0, 0, 0); + } + break; + } + + case TR_WIDGET_VALUE_INT: { + TraceRestrictItem item = this->GetSelected(); + if (GetTraceRestrictTypeProperties(item).value_type == TRVT_INT) { + SetDParam(0, GetTraceRestrictValue(item)); + ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 6, this, CS_NUMERAL, QSF_NONE); // 5 digit num, + terminating null + } + break; + } + + case TR_WIDGET_VALUE_DROPDOWN: { + TraceRestrictItem item = this->GetSelected(); + if (GetTraceRestrictTypeProperties(item).value_type == TRVT_DENY) { + this->ShowDropDownListWithValue(&_deny_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); + } + break; + } + + case TR_WIDGET_GOTO_SIGNAL: + ScrollMainWindowToTile(this->tile); + break; + } + } + + virtual void OnQueryTextFinished(char *str) + { + if (StrEmpty(str)) { + return; + } + + TraceRestrictItem item = GetSelected(); + if (GetTraceRestrictTypeProperties(item).value_type != TRVT_INT) { + return; + } + + uint value = atoi(str); + if (value >= (1 << TRIFA_VALUE_COUNT)) { + SetDParam(0, (1 << TRIFA_VALUE_COUNT) - 1); + ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO); + return; + } + + SetTraceRestrictValue(item, value); + TraceRestrictDoCommandP(tile, track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + } + + virtual void OnDropdownSelect(int widget, int index) + { + TraceRestrictItem item = GetSelected(); + if (item == 0 || index < 0 || this->selected_instruction < 1) { + return; + } + + const TraceRestrictDropDownListSet *list_set = this->drop_down_list_mapping[widget]; + if (!list_set) { + return; + } + + uint value = list_set->value_array[index]; + + switch (widget) { + case TR_WIDGET_INSERT: { + TraceRestrictItem insert_item = 0; + SetTraceRestrictTypeAndNormalise(insert_item, static_cast(value)); + this->expecting_inserted_item = insert_item; + TraceRestrictDoCommandP(this->tile, this->track, TRDCT_INSERT_ITEM, this->selected_instruction - 1, insert_item, STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM); + break; + } + + case TR_WIDGET_TYPE: { + SetTraceRestrictTypeAndNormalise(item, static_cast(value)); + TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + break; + } + + case TR_WIDGET_COMPARATOR: { + SetTraceRestrictCondOp(item, static_cast(value)); + TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + break; + } + + case TR_WIDGET_VALUE_DROPDOWN: { + SetTraceRestrictValue(item, value); + TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + break; + } + } + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + switch (widget) { + case TR_WIDGET_INSTRUCTION_LIST: + resize->height = FONT_HEIGHT_NORMAL; + size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + break; + } + } + + virtual void OnResize() + { + /* Update the scroll bar */ + this->vscroll->SetCapacityFromWidget(this, TR_WIDGET_INSTRUCTION_LIST); + } + + virtual void OnPaint() + { + this->DrawWidgets(); + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + if (widget != TR_WIDGET_INSTRUCTION_LIST) return; + + int y = r.top + WD_FRAMERECT_TOP; + int line_height = this->GetWidget(TR_WIDGET_INSTRUCTION_LIST)->resize_y; + int scroll_position = this->vscroll->GetPosition(); + + // prog may be NULL + const TraceRestrictProgram *prog = this->GetProgram(); + + int count = this->GetItemCount(prog); + uint indent = 1; + for(int i = 0; i < count; i++) { + TraceRestrictItem item = this->GetItem(prog, i); + uint this_indent = indent; + if (IsTraceRestrictConditional(item)) { + if (GetTraceRestrictCondFlags(item) & (TRCF_ELSE | TRCF_OR)) { + this_indent--; + } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { + indent--; + this_indent--; + } else { + indent++; + } + } else if (GetTraceRestrictType(item) == TRIT_NULL) { + this_indent = 0; + } + + if (i >= scroll_position && this->vscroll->IsVisible(i)) { + DrawInstructionString(item, y, i == this->selected_instruction, this_indent, r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT); + y += line_height; + } + } + } + + virtual void OnInvalidateData(int data, bool gui_scope) { + if (gui_scope) { + this->ReloadProgramme(); + } + } + + + virtual void SetStringParameters(int widget) const + { + switch (widget) { + case TR_WIDGET_VALUE_INT: { + SetDParam(0, 0); + + TraceRestrictItem item = this->GetSelected(); + if (GetTraceRestrictTypeProperties(item).value_type == TRVT_INT) { + SetDParam(0, GetTraceRestrictValue(item)); + } + } break; + } + } + +private: + TraceRestrictItem MakeSpecialItem(TraceRestictNullTypeSpecialValue value) const + { + TraceRestrictItem item = 0; + SetTraceRestrictType(item, TRIT_NULL); + SetTraceRestrictValue(item, value); + return item; + } + + int GetItemCount(const TraceRestrictProgram *prog) const + { + if (prog) { + return 2 + prog->items.size(); + } else { + return 2; + } + } + + /// This may return NULL if no program currently exists + const TraceRestrictProgram *GetProgram() const + { + return GetTraceRestrictProgram(MakeTraceRestrictRefId(tile, track), false); + } + + /// prog may be NULL + TraceRestrictItem GetItem(const TraceRestrictProgram *prog, int index) const + { + if (index < 0) { + return 0; + } + + if (index == 0) { + return MakeSpecialItem(TRNTSV_START); + } + + if (prog) { + const std::vector &items = prog->items; + + if (static_cast(index) == items.size() + 1) { + return MakeSpecialItem(TRNTSV_END); + } + + if (static_cast(index) > items.size() + 1) { + return 0; + } + + return items[index - 1]; + } else { + // No program defined, this is equivalent to an empty program + if (index == 1) { + return MakeSpecialItem(TRNTSV_END); + } else { + return 0; + } + } + } + + TraceRestrictItem GetSelected() const + { + return this->GetItem(this->GetProgram(), this->selected_instruction); + } + + Owner GetOwner() + { + return GetTileOwner(tile); + } + + int GetInstructionFromPt(int y) + { + NWidgetBase *nwid = this->GetWidget(TR_WIDGET_INSTRUCTION_LIST); + int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line + + if ((uint)sel >= this->vscroll->GetCapacity()) return -1; + + sel += this->vscroll->GetPosition(); + + return (sel < this->GetItemCount(this->GetProgram()) && sel >= 0) ? sel : -1; + } + + void ReloadProgramme() + { + const TraceRestrictProgram *prog = this->GetProgram(); + + if (this->vscroll->GetCount() != this->GetItemCount(prog)) { + // program length has changed + + if (this->GetItemCount(prog) < this->vscroll->GetCount() || + this->GetItem(prog, this->selected_instruction) != this->expecting_inserted_item) { + // length has shrunk or if we weren't expecting an insertion, deselect + this->selected_instruction = -1; + } + this->expecting_inserted_item = static_cast(0); + + // update scrollbar size + this->vscroll->SetCount(this->GetItemCount(prog)); + } + this->UpdateButtonState(); + } + + void UpdateButtonState() + { + this->RaiseWidget(TR_WIDGET_INSERT); + this->RaiseWidget(TR_WIDGET_REMOVE); + this->RaiseWidget(TR_WIDGET_TYPE); + this->RaiseWidget(TR_WIDGET_COMPARATOR); + this->RaiseWidget(TR_WIDGET_VALUE_INT); + this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN); + + NWidgetStacked *left_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT); + NWidgetStacked *middle_sel = this->GetWidget(TR_WIDGET_SEL_TOP_MIDDLE); + NWidgetStacked *right_sel = this->GetWidget(TR_WIDGET_SEL_TOP_RIGHT); + + this->DisableWidget(TR_WIDGET_TYPE); + this->DisableWidget(TR_WIDGET_COMPARATOR); + this->DisableWidget(TR_WIDGET_VALUE_INT); + this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN); + + this->DisableWidget(TR_WIDGET_INSERT); + this->DisableWidget(TR_WIDGET_REMOVE); + + this->DisableWidget(TR_WIDGET_BLANK_L); + this->DisableWidget(TR_WIDGET_BLANK_M); + this->DisableWidget(TR_WIDGET_BLANK_R); + + left_sel->SetDisplayedPlane(DPL_BLANK); + middle_sel->SetDisplayedPlane(DPM_BLANK); + right_sel->SetDisplayedPlane(DPR_BLANK); + + // Don't allow modifications if don't own, or have selected invalid instruction + if (this->GetOwner() != _local_company || this->selected_instruction < 1) { + this->SetDirty(); + return; + } + + TraceRestrictItem item = this->GetSelected(); + if (item != 0) { + if (GetTraceRestrictType(item) == TRIT_NULL) { + switch (GetTraceRestrictValue(item)) { + case TRNTSV_START: + break; + + case TRNTSV_END: + this->EnableWidget(TR_WIDGET_INSERT); + break; + + default: + NOT_REACHED(); + break; + } + } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { + this->EnableWidget(TR_WIDGET_INSERT); + if (GetTraceRestrictCondFlags(item) != 0) { + // this is not an end if, enable removing + this->EnableWidget(TR_WIDGET_REMOVE); + } + } else { + TraceRestrictTypePropertySet properties = GetTraceRestrictTypeProperties(item); + + left_sel->SetDisplayedPlane(DPL_TYPE); + this->EnableWidget(TR_WIDGET_TYPE); + + this->GetWidget(TR_WIDGET_TYPE)->widget_data = + GetTypeString(GetTraceRestrictType(item)); + + if (properties.cond_type == TRCOT_BINARY || properties.cond_type == TRCOT_ALL) { + middle_sel->SetDisplayedPlane(DPM_COMPARATOR); + this->EnableWidget(TR_WIDGET_COMPARATOR); + + const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(properties.cond_type); + + if (list_set) { + this->GetWidget(TR_WIDGET_COMPARATOR)->widget_data = + GetDropDownStringByValue(list_set, GetTraceRestrictCondOp(item)); + } + } + + if (properties.value_type == TRVT_INT) { + right_sel->SetDisplayedPlane(DPR_VALUE_INT); + this->EnableWidget(TR_WIDGET_VALUE_INT); + } else if (properties.value_type == TRVT_DENY) { + right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); + this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); + this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = + GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW : STR_TRACE_RESTRICT_PF_DENY; + } + + this->EnableWidget(TR_WIDGET_INSERT); + this->EnableWidget(TR_WIDGET_REMOVE); + } + } + + this->SetDirty(); + } + + void ShowDropDownListWithValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok, + int button, uint32 disabled_mask, uint32 hidden_mask, uint width) + { + drop_down_list_mapping[button] = list_set; + int selected = GetDropDownListIndexByValue(list_set, value, missing_ok); + ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width); + } +}; + +static const NWidgetPart _nested_program_widgets[] = { + // Title bar + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, TR_WIDGET_CAPTION), SetDataTip(STR_TRACE_RESTRICT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + + // Program display + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY, TR_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_TRACE_RESTRICT_SIGNAL_GUI_TOOLTIP), SetResize(1, 1), EndContainer(), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TR_WIDGET_SCROLLBAR), + EndContainer(), + + // Button Bar + NWidget(NWID_HORIZONTAL), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT), + NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_L), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_MIDDLE), + NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_COMPARATOR_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_M), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_RIGHT), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_INT), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_BLACK_COMMA, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_VALUE_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_R), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), + EndContainer(), + EndContainer(), + NWidget(WWT_IMGBTN, COLOUR_GREY, TR_WIDGET_GOTO_SIGNAL), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_RIGHT, STR_TRACE_RESTRICT_GOTO_SIGNAL_TOOLTIP), + EndContainer(), + + /* Second button row. */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_INSERT), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_INSERT, STR_TRACE_RESTRICT_INSERT_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_REMOVE), SetMinimalSize(186, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_REMOVE, STR_TRACE_RESTRICT_REMOVE_TOOLTIP), SetResize(1, 0), + EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), +}; + +static WindowDesc _program_desc( + WDP_AUTO, "trace_restrict_gui", 384, 100, + WC_TRACE_RESTRICT, WC_BUILD_SIGNAL, + WDF_CONSTRUCTION, + _nested_program_widgets, lengthof(_nested_program_widgets) +); + +void ShowTraceRestrictProgramWindow(TileIndex tile, Track track) +{ + if (BringWindowToFrontById(WC_TRACE_RESTRICT, MakeTraceRestrictRefId(tile, track)) != NULL) { + return; + } + + new TraceRestrictWindow(&_program_desc, tile, track); +} diff --git a/src/widgets/rail_widget.h b/src/widgets/rail_widget.h index f977f44803..fa271b0d95 100644 --- a/src/widgets/rail_widget.h +++ b/src/widgets/rail_widget.h @@ -91,6 +91,7 @@ enum BuildSignalWidgets { WID_BS_ELECTRIC_PBS, ///< Build an electric path signal. WID_BS_ELECTRIC_PBS_OWAY, ///< Build an electric one way path signal. WID_BS_CONVERT, ///< Convert the signal. + WID_BS_TRACE_RESTRICT, ///< Open trace restrict window. WID_BS_DRAG_SIGNALS_DENSITY_LABEL, ///< The current signal density. WID_BS_DRAG_SIGNALS_DENSITY_DECREASE, ///< Decrease the signal density. WID_BS_DRAG_SIGNALS_DENSITY_INCREASE, ///< Increase the signal density. diff --git a/src/window_type.h b/src/window_type.h index 809e81d485..48491e6601 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -681,6 +681,12 @@ enum WindowClass { */ WC_SAVE_PRESET, + /** + * Trace restrict programme window; %Window numbers: + * - #TileIndex << 3 | #Track = #TraceRestrictWindow + */ + WC_TRACE_RESTRICT, + WC_INVALID = 0xFFFF, ///< Invalid window. }; From 627288277e5f328422341240f5f8d55c690026b8 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 22 Jul 2015 20:04:05 +0100 Subject: [PATCH 02/15] Implement reset, copy, share and unshare of trace restrict programs. Add documentation on data storage model, wrt lookup, mapping and sharing. --- src/lang/english.txt | 14 +++ src/tracerestrict.cpp | 208 ++++++++++++++++++++++++++++++++++---- src/tracerestrict.h | 15 +++ src/tracerestrict_gui.cpp | 168 +++++++++++++++++++++++++++++- 4 files changed, 379 insertions(+), 26 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 8b690d96d9..4efc7db0ce 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2396,14 +2396,23 @@ STR_TRACE_RESTRICT_PF_ALLOW_LONG :Allow (cancel p STR_TRACE_RESTRICT_PF_PENALTY :Penalty 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_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_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_ERROR_CAN_T_INSERT_ITEM :{WHITE}Can't insert instruction STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM :{WHITE}Can't modify instruction @@ -2417,6 +2426,11 @@ STR_TRACE_RESTRICT_ERROR_VALIDATE_END_CONDSTACK :Validation fail 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 diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 74b182b4f0..1f8bea6be5 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -19,7 +19,40 @@ #include "pathfinder/yapf/yapf_cache.h" #include -/** Initialize theprogram pool */ +/** 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. + */ + +/** Initialize the program pool */ TraceRestrictProgramPool _tracerestrictprogram_pool("TraceRestrictProgram"); INSTANTIATE_POOL_METHODS(TraceRestrictProgram) @@ -353,27 +386,8 @@ void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommand DoCommandP(tile, p1, value, CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG(error_msg)); } -/** - * The main command for editing a signal tracerestrict program. - * @param tile The tile which contains the signal. - * @param flags Internal command handler stuff. - * @param p1 Bitstuffed items - * @param p2 Item, for insert and modify operations - * @return the cost of this operation (which is free), or an error - */ -CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +static CommandCost TraceRestrictCheckTileIsUsable(TileIndex tile, Track track) { - Track track = static_cast(GB(p1, 0, 3)); - TraceRestrictDoCommandType type = static_cast(GB(p1, 3, 5)); - uint32 offset = GB(p1, 8, 16); - TraceRestrictItem item = static_cast(p2); - - // Check tile ownership - CommandCost ret = CheckTileOwnership(tile); - if (ret.Failed()) { - return ret; - } - // Check that there actually is a signal here if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) { return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); @@ -382,6 +396,41 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); } + // Check tile ownership, do this afterwards to avoid tripping up on house/industry tiles + CommandCost ret = CheckTileOwnership(tile); + if (ret.Failed()) { + return ret; + } + + return CommandCost(); +} + +/** + * The main command for editing a signal tracerestrict program. + * @param tile The tile which contains the signal. + * @param flags Internal command handler stuff. + * Below apply for instruction modification actions only + * @param p1 Bitstuffed items + * @param p2 Item, for insert and modify operations + * @return the cost of this operation (which is free), or an error + */ +CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + TraceRestrictDoCommandType type = static_cast(GB(p1, 3, 5)); + + if (type >= TRDCT_PROG_COPY) { + return CmdProgramSignalTraceRestrictProgMgmt(tile, flags, p1, p2, text); + } + + Track track = static_cast(GB(p1, 0, 3)); + uint32 offset = GB(p1, 8, 16); + TraceRestrictItem item = static_cast(p2); + + CommandCost ret = TraceRestrictCheckTileIsUsable(tile, track); + if (ret.Failed()) { + return ret; + } + bool can_make_new = (type == TRDCT_INSERT_ITEM) && (flags & DC_EXEC); bool need_existing = (type != TRDCT_INSERT_ITEM); TraceRestrictProgram *prog = GetTraceRestrictProgram(MakeTraceRestrictRefId(tile, track), can_make_new); @@ -508,3 +557,120 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u return CommandCost(); } + +void TraceRestrictProgMgmtWithSourceDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, + TileIndex source_tile, Track source_track, StringID error_msg) +{ + uint32 p1 = 0; + SB(p1, 0, 3, track); + SB(p1, 3, 5, type); + SB(p1, 8, 3, source_track); + DoCommandP(tile, p1, source_tile, CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG(error_msg)); +} + +/** + * Sub command for copy/share/unshare operations on signal tracerestrict programs. + * @param tile The tile which contains the signal. + * @param flags Internal command handler stuff. + * @param p1 Bitstuffed items + * @param p2 Source tile, for share/copy operations + * @return the cost of this operation (which is free), or an error + */ +CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + TraceRestrictDoCommandType type = static_cast(GB(p1, 3, 5)); + Track track = static_cast(GB(p1, 0, 3)); + Track source_track = static_cast(GB(p1, 8, 3)); + TileIndex source_tile = static_cast(p2); + + TraceRestrictRefId self = MakeTraceRestrictRefId(tile, track); + TraceRestrictRefId source = MakeTraceRestrictRefId(source_tile, source_track); + + assert(type >= TRDCT_PROG_COPY); + + CommandCost ret = TraceRestrictCheckTileIsUsable(tile, track); + if (ret.Failed()) { + return ret; + } + + if (type == TRDCT_PROG_SHARE || type == TRDCT_PROG_COPY) { + if (self == source) { + return_cmd_error(STR_TRACE_RESTRICT_ERROR_SOURCE_SAME_AS_TARGET); + } + + ret = TraceRestrictCheckTileIsUsable(source_tile, source_track); + if (ret.Failed()) { + return ret; + } + } + + if (!(flags & DC_EXEC)) { + return CommandCost(); + } + + switch (type) { + case TRDCT_PROG_COPY: { + TraceRestrictRemoveProgramMapping(self); + TraceRestrictProgram *prog = GetTraceRestrictProgram(self, true); + if (!prog) { + // allocation failed + return CMD_ERROR; + } + + TraceRestrictProgram *source_prog = GetTraceRestrictProgram(source, false); + if (source_prog) { + prog->items = source_prog->items; // copy + } + break; + } + + case TRDCT_PROG_SHARE: { + TraceRestrictRemoveProgramMapping(self); + TraceRestrictProgram *source_prog = GetTraceRestrictProgram(source, true); + if (!source_prog) { + // allocation failed + return CMD_ERROR; + } + + TraceRestrictCreateProgramMapping(self, source_prog); + break; + } + + case TRDCT_PROG_UNSHARE: { + std::vector items; + TraceRestrictProgram *prog = GetTraceRestrictProgram(self, false); + if (prog) { + // copy program into temporary + items = prog->items; + } + // remove old program + TraceRestrictRemoveProgramMapping(self); + + if (items.size()) { + // if prog is non-empty, create new program and move temporary in + TraceRestrictProgram *new_prog = GetTraceRestrictProgram(self, true); + if (!new_prog) { + // allocation failed + return CMD_ERROR; + } + + new_prog->items.swap(items); + } + break; + } + + case TRDCT_PROG_RESET: { + TraceRestrictRemoveProgramMapping(self); + break; + } + + default: + NOT_REACHED(); + break; + } + + // update windows + InvalidateWindowClassesData(WC_TRACE_RESTRICT); + + return CommandCost(); +} diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 1309220669..43dd55c6d8 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -269,15 +269,30 @@ static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIn } } +// do not re-order enum TraceRestrictDoCommandType { TRDCT_INSERT_ITEM = 0, TRDCT_MODIFY_ITEM = 1, TRDCT_REMOVE_ITEM = 2, + + TRDCT_PROG_COPY = 3, + TRDCT_PROG_SHARE = 4, + TRDCT_PROG_UNSHARE = 5, + TRDCT_PROG_RESET = 6, }; void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg); +void TraceRestrictProgMgmtWithSourceDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, + TileIndex source_tile, Track source_track, StringID error_msg); + +inline void TraceRestrictProgMgmtDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, StringID error_msg) +{ + TraceRestrictProgMgmtWithSourceDoCommandP(tile, track, type, static_cast(0), static_cast(0), error_msg); +} + CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); +CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 1cfd46ccc5..55c6b39d39 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -18,6 +18,7 @@ #include "viewport_func.h" #include "textbuf_gui.h" #include "company_func.h" +#include "tilehighlight_func.h" #include "widgets/dropdown_func.h" #include "gui.h" #include "gfx_func.h" @@ -34,6 +35,7 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_SEL_TOP_LEFT, TR_WIDGET_SEL_TOP_MIDDLE, TR_WIDGET_SEL_TOP_RIGHT, + TR_WIDGET_SEL_SHARE, TR_WIDGET_TYPE, TR_WIDGET_COMPARATOR, @@ -47,6 +49,10 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_GOTO_SIGNAL, TR_WIDGET_INSERT, TR_WIDGET_REMOVE, + TR_WIDGET_RESET, + TR_WIDGET_COPY, + TR_WIDGET_SHARE, + TR_WIDGET_UNSHARE, }; enum PanelWidgets { @@ -62,6 +68,10 @@ enum PanelWidgets { DPR_VALUE_INT = 0, DPR_VALUE_DROPDOWN, DPR_BLANK, + + // Share + DPS_SHARE = 0, + DPS_UNSHARE, }; /// value_array *must* be at least as long as string_array, @@ -293,6 +303,7 @@ class TraceRestrictWindow: public Window { Scrollbar *vscroll; std::map drop_down_list_mapping; TraceRestrictItem expecting_inserted_item; + int current_placement_widget; public: TraceRestrictWindow(WindowDesc *desc, TileIndex tile, Track track) @@ -302,6 +313,7 @@ public: this->track = track; this->selected_instruction = -1; this->expecting_inserted_item = static_cast(0); + this->current_placement_widget = -1; this->CreateNestedTree(); this->vscroll = this->GetScrollbar(TR_WIDGET_SCROLLBAR); @@ -389,6 +401,21 @@ public: case TR_WIDGET_GOTO_SIGNAL: ScrollMainWindowToTile(this->tile); break; + + case TR_WIDGET_RESET: { + TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_RESET, STR_TRACE_RESTRICT_ERROR_CAN_T_RESET_SIGNAL); + break; + } + + case TR_WIDGET_COPY: + case TR_WIDGET_SHARE: + SelectSignalAction(widget); + break; + + case TR_WIDGET_UNSHARE: { + TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_UNSHARE, STR_TRACE_RESTRICT_ERROR_CAN_T_UNSHARE_PROGRAM); + break; + } } } @@ -457,6 +484,72 @@ public: } } + virtual void OnPlaceObject(Point pt, TileIndex source_tile) + { + int widget = this->current_placement_widget; + this->current_placement_widget = -1; + + this->RaiseButtons(); + ResetObjectToPlace(); + + if (widget < 0) { + return; + } + + int error_message = (widget == TR_WIDGET_COPY) ? STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM : STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM; + + if (!IsPlainRailTile(source_tile)) { + ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); + return; + } + + TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(source_tile, TRANSPORT_RAIL, 0)); + if (trackbits & TRACK_BIT_VERT) { // N-S direction + trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT; + } + + if (trackbits & TRACK_BIT_HORZ) { // E-W direction + trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER; + } + Track source_track = FindFirstTrack(trackbits); + if(source_track == INVALID_TRACK) { + ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); + return; + } + + if (!HasTrack(source_tile, source_track)) { + ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); + return; + } + + if (!HasSignalOnTrack(source_tile, source_track)) { + ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO); + return; + } + + switch (widget) { + case TR_WIDGET_COPY: + TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_COPY, + source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM); + break; + + case TR_WIDGET_SHARE: + TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_SHARE, + source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM); + break; + + default: + NOT_REACHED(); + break; + } + } + + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + this->current_placement_widget = -1; + } + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { @@ -531,7 +624,18 @@ public: if (GetTraceRestrictTypeProperties(item).value_type == TRVT_INT) { SetDParam(0, GetTraceRestrictValue(item)); } - } break; + break; + } + + case TR_WIDGET_CAPTION: { + const TraceRestrictProgram *prog = this->GetProgram(); + if (prog) { + SetDParam(0, prog->refcount); + } else { + SetDParam(0, 1); + } + break; + } } } @@ -646,6 +750,7 @@ private: NWidgetStacked *left_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT); NWidgetStacked *middle_sel = this->GetWidget(TR_WIDGET_SEL_TOP_MIDDLE); NWidgetStacked *right_sel = this->GetWidget(TR_WIDGET_SEL_TOP_RIGHT); + NWidgetStacked *share_sel = this->GetWidget(TR_WIDGET_SEL_SHARE); this->DisableWidget(TR_WIDGET_TYPE); this->DisableWidget(TR_WIDGET_COMPARATOR); @@ -654,6 +759,10 @@ private: this->DisableWidget(TR_WIDGET_INSERT); this->DisableWidget(TR_WIDGET_REMOVE); + this->DisableWidget(TR_WIDGET_RESET); + this->DisableWidget(TR_WIDGET_COPY); + this->DisableWidget(TR_WIDGET_SHARE); + this->DisableWidget(TR_WIDGET_UNSHARE); this->DisableWidget(TR_WIDGET_BLANK_L); this->DisableWidget(TR_WIDGET_BLANK_M); @@ -662,14 +771,40 @@ private: left_sel->SetDisplayedPlane(DPL_BLANK); middle_sel->SetDisplayedPlane(DPM_BLANK); right_sel->SetDisplayedPlane(DPR_BLANK); + share_sel->SetDisplayedPlane(DPS_SHARE); - // Don't allow modifications if don't own, or have selected invalid instruction - if (this->GetOwner() != _local_company || this->selected_instruction < 1) { + const TraceRestrictProgram *prog = this->GetProgram(); + + this->GetWidget(TR_WIDGET_CAPTION)->widget_data = + (prog && prog->refcount > 1) ? STR_TRACE_RESTRICT_CAPTION_SHARED : STR_TRACE_RESTRICT_CAPTION; + + // Don't allow modifications if don't own + if (this->GetOwner() != _local_company) { this->SetDirty(); return; } - TraceRestrictItem item = this->GetSelected(); + if (prog && prog->refcount > 1) { + // program is shared, show and enable unshare button, and reset button + share_sel->SetDisplayedPlane(DPS_UNSHARE); + this->EnableWidget(TR_WIDGET_UNSHARE); + this->EnableWidget(TR_WIDGET_RESET); + } else if (this->GetItemCount(prog) > 2) { + // program is non-empty and not shared, enable reset button + this->EnableWidget(TR_WIDGET_RESET); + } else { + // program is empty and not shared, show copy and share buttons + this->EnableWidget(TR_WIDGET_COPY); + this->EnableWidget(TR_WIDGET_SHARE); + } + + // haven't selected instruction + if (this->selected_instruction < 1) { + this->SetDirty(); + return; + } + + TraceRestrictItem item = this->GetItem(prog, this->selected_instruction); if (item != 0) { if (GetTraceRestrictType(item) == TRIT_NULL) { switch (GetTraceRestrictValue(item)) { @@ -736,6 +871,19 @@ private: int selected = GetDropDownListIndexByValue(list_set, value, missing_ok); ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width); } + + void SelectSignalAction(int widget) + { + this->ToggleWidgetLoweredState(widget); + this->SetWidgetDirty(widget); + if (this->IsWidgetLowered(widget)) { + SetObjectToPlaceWnd(ANIMCURSOR_BUILDSIGNALS, PAL_NONE, HT_RECT, this); + this->current_placement_widget = widget; + } else { + ResetObjectToPlace(); + this->current_placement_widget = -1; + } + } }; static const NWidgetPart _nested_program_widgets[] = { @@ -785,8 +933,18 @@ static const NWidgetPart _nested_program_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_INSERT), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_INSERT, STR_TRACE_RESTRICT_INSERT_TOOLTIP), SetResize(1, 0), - NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_REMOVE), SetMinimalSize(186, 12), SetFill(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_REMOVE), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_REMOVE, STR_TRACE_RESTRICT_REMOVE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_RESET), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_RESET, STR_TRACE_RESTRICT_RESET_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_COPY), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_COPY, STR_TRACE_RESTRICT_COPY_TOOLTIP), SetResize(1, 0), + NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_SHARE), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_SHARE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_SHARE, STR_TRACE_RESTRICT_SHARE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_UNSHARE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_UNSHARE, STR_TRACE_RESTRICT_UNSHARE_TOOLTIP), SetResize(1, 0), + EndContainer(), EndContainer(), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), From 0be3b053d53909e5b0ab16d94a0c96cbf7095577 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 22 Jul 2015 21:48:12 +0100 Subject: [PATCH 03/15] Add maximum train speed condition variable. --- src/lang/english.txt | 2 ++ src/tracerestrict.cpp | 5 +++ src/tracerestrict.h | 17 ++++++++- src/tracerestrict_gui.cpp | 75 ++++++++++++++++++++++++++++++--------- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 4efc7db0ce..ad6272d37a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2383,8 +2383,10 @@ 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_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_COMPARE_UNDEFINED :{STRING} {RED}undefined {BLACK}{STRING}then STR_TRACE_RESTRICT_PF_PENALTY_ITEM :Add pathfinder penalty: {COMMA} STR_TRACE_RESTRICT_WHITE :{WHITE} diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 1f8bea6be5..5b25448dd6 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -170,6 +170,10 @@ void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& o 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; + default: NOT_REACHED(); } @@ -260,6 +264,7 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp case TRVT_NONE: case TRVT_INT: case TRVT_DENY: + case TRVT_SPEED: SetTraceRestrictValue(item, 0); break; diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 43dd55c6d8..87e8cb9b8d 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -80,6 +80,7 @@ enum TraceRestrictItemType { TRIT_COND_ENDIF = 8, ///< This is an endif block or an else block TRIT_COND_UNDEFINED = 9, ///< This condition has no type defined (evaluate as false) TRIT_COND_TRAIN_LENGTH = 10, ///< Test train length + TRIT_COND_MAX_SPEED = 11, ///< Test train max speed /* space up to 31 */ }; @@ -197,6 +198,7 @@ enum TraceRestrictValueType { TRVT_SPECIAL = 1, ///< special handling of value field TRVT_INT = 2, ///< takes an integer value TRVT_DENY = 3, ///< takes a value 0 = deny, 1 = allow (cancel previous deny) + TRVT_SPEED = 4, ///< takes an integer speed value }; struct TraceRestrictTypePropertySet { @@ -217,7 +219,20 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.value_type = TRVT_NONE; } else if (IsTraceRestrictConditional(item)) { out.cond_type = TRCOT_ALL; - out.value_type = TRVT_INT; + + switch (GetTraceRestrictType(item)) { + case TRIT_COND_TRAIN_LENGTH: + out.value_type = TRVT_INT; + break; + + case TRIT_COND_MAX_SPEED: + out.value_type = TRVT_SPEED; + break; + + default: + NOT_REACHED(); + break; + } } else { out.cond_type = TRCOT_NONE; if (GetTraceRestrictType(item) == TRIT_PF_PENALTY) { diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 55c6b39d39..b1e8251898 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -27,6 +27,9 @@ #include "error.h" #include "table/sprites.h" +extern uint ConvertSpeedToDisplaySpeed(uint speed); +extern uint ConvertDisplaySpeedToSpeed(uint speed); + enum TraceRestrictWindowWidgets { TR_WIDGET_CAPTION, TR_WIDGET_INSTRUCTION_LIST, @@ -148,11 +151,13 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI static const StringID str_cond[] = { STR_TRACE_RESTRICT_VARIABLE_TRAIN_LENGTH, + STR_TRACE_RESTRICT_VARIABLE_MAX_SPEED, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; static const uint val_cond[] = { TRIT_COND_TRAIN_LENGTH, + TRIT_COND_MAX_SPEED, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { @@ -217,12 +222,50 @@ static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestric return NULL; } +static bool IsIntegerValueType(TraceRestrictValueType type) +{ + switch (type) { + case TRVT_INT: + case TRVT_SPEED: + return true; + + default: + return false; + } +} + +static uint ConvertIntegerValue(TraceRestrictValueType type, uint in, bool to_display) +{ + switch (type) { + case TRVT_INT: + return in; + + case TRVT_SPEED: + return to_display + ? ConvertSpeedToDisplaySpeed(in) * 10 / 16 + : ConvertDisplaySpeedToSpeed(in) * 16 / 10; + + default: + NOT_REACHED(); + return 0; + } +} + static const StringID _program_cond_type[] = { /* 0 */ STR_TRACE_RESTRICT_CONDITIONAL_IF, /* TRCF_ELSE */ STR_TRACE_RESTRICT_CONDITIONAL_ELIF, /* TRCF_OR */ STR_TRACE_RESTRICT_CONDITIONAL_ORIF, }; +static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) +{ + assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(1, GetTypeString(GetTraceRestrictType(item))); + SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties.cond_type), GetTraceRestrictCondOp(item))); + SetDParam(3, GetTraceRestrictValue(item)); +} + /** * Draws an instruction in the programming GUI * @param instruction The instruction to draw @@ -251,12 +294,10 @@ static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); } else if (properties.value_type == TRVT_INT) { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER; - - assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); - SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); - SetDParam(1, GetTypeString(GetTraceRestrictType(item))); - SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties.cond_type), GetTraceRestrictCondOp(item))); - SetDParam(3, GetTraceRestrictValue(item)); + DrawInstructionStringConditionalIntegerCommon(item, properties); + } else if (properties.value_type == TRVT_SPEED) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED; + DrawInstructionStringConditionalIntegerCommon(item, properties); } else { NOT_REACHED(); } @@ -383,9 +424,10 @@ public: case TR_WIDGET_VALUE_INT: { TraceRestrictItem item = this->GetSelected(); - if (GetTraceRestrictTypeProperties(item).value_type == TRVT_INT) { - SetDParam(0, GetTraceRestrictValue(item)); - ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 6, this, CS_NUMERAL, QSF_NONE); // 5 digit num, + terminating null + TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; + if (IsIntegerValueType(type)) { + SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true)); + ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE); } break; } @@ -426,13 +468,14 @@ public: } TraceRestrictItem item = GetSelected(); - if (GetTraceRestrictTypeProperties(item).value_type != TRVT_INT) { + TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; + if (!IsIntegerValueType(type)) { return; } - uint value = atoi(str); + uint value = ConvertIntegerValue(type, atoi(str), false); if (value >= (1 << TRIFA_VALUE_COUNT)) { - SetDParam(0, (1 << TRIFA_VALUE_COUNT) - 1); + SetDParam(0, ConvertIntegerValue(type, (1 << TRIFA_VALUE_COUNT) - 1, true)); ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO); return; } @@ -619,10 +662,10 @@ public: switch (widget) { case TR_WIDGET_VALUE_INT: { SetDParam(0, 0); - TraceRestrictItem item = this->GetSelected(); - if (GetTraceRestrictTypeProperties(item).value_type == TRVT_INT) { - SetDParam(0, GetTraceRestrictValue(item)); + TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; + if (IsIntegerValueType(type)) { + SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true)); } break; } @@ -846,7 +889,7 @@ private: } } - if (properties.value_type == TRVT_INT) { + if (IsIntegerValueType(properties.value_type)) { right_sel->SetDisplayedPlane(DPR_VALUE_INT); this->EnableWidget(TR_WIDGET_VALUE_INT); } else if (properties.value_type == TRVT_DENY) { From ffed0c194ad7de796a0c4574628df73b1c4eb10b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 24 Jul 2015 01:21:31 +0100 Subject: [PATCH 04/15] Add current order (station, waypoint or depot) condition variable. Adjust item bit allocations: * Increase cond flags from 2 bits to 3 bits, for future expansion. * Use 2 bits remaining in adjacent gap for an auxiliary type field. This is used for the type (station, waypoint, etc.) of order tests. Perform a linear scan of the program pool when deleting a station, waypoint or depot. --- src/depot.cpp | 3 + src/lang/english.txt | 7 ++ src/station.cpp | 3 + src/tracerestrict.cpp | 69 ++++++++++++++ src/tracerestrict.h | 32 ++++++- src/tracerestrict_gui.cpp | 193 ++++++++++++++++++++++++++++++++++---- src/waypoint.cpp | 2 + 7 files changed, 288 insertions(+), 21 deletions(-) diff --git a/src/depot.cpp b/src/depot.cpp index 821399fd2b..1890faa87d 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -17,6 +17,7 @@ #include "core/pool_func.hpp" #include "vehicle_gui.h" #include "vehiclelist.h" +#include "tracerestrict.h" #include "safeguards.h" @@ -42,6 +43,8 @@ Depot::~Depot() /* Clear the depot from all order-lists */ RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->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 ad6272d37a..65ec673cd3 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2384,9 +2384,14 @@ 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_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_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} @@ -2409,6 +2414,7 @@ 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) @@ -2416,6 +2422,7 @@ STR_TRACE_RESTRICT_COPY_TOOLTIP :{BLACK}Copy pro 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 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/tracerestrict.cpp b/src/tracerestrict.cpp index 5b25448dd6..737c57fddc 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -16,6 +16,7 @@ #include "company_func.h" #include "viewport_func.h" #include "window_func.h" +#include "order_base.h" #include "pathfinder/yapf/yapf_cache.h" #include @@ -131,6 +132,45 @@ static bool TestCondition(uint16 value, TraceRestrictCondOp condop, uint16 condv } } +/// Test order condition +/// 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(); + } + } + + switch (GetTraceRestrictCondOp(item)) { + case TRCO_IS: + return result; + + case TRCO_ISNOT: + return !result; + + default: + NOT_REACHED(); + return false; + } +} + /// Execute program on train and store results in out void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& out) const { @@ -174,6 +214,10 @@ void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& o result = TestCondition(v->GetDisplayMaxSpeed(), condop, condvalue); break; + case TRIT_COND_CURRENT_ORDER: + result = TestOrderCondition(&(v->current_order), item); + break; + default: NOT_REACHED(); } @@ -266,6 +310,12 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp 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; default: @@ -679,3 +729,22 @@ CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag return CommandCost(); } + +void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index) +{ + TraceRestrictProgram *prog; + + FOR_ALL_TRACE_RESTRICT_PROGRAMS(prog) { + for (size_t i = 0; i < prog->items.size(); i++) { + TraceRestrictItem &item = prog->items[i]; // note this is a reference, + if (GetTraceRestrictType(item) == TRIT_COND_CURRENT_ORDER) { + if (GetTraceRestrictAuxField(item) == type && GetTraceRestrictValue(item) == index) { + SetTraceRestrictValueDefault(item, TRVT_ORDER); // this updates the instruction in-place + } + } + } + } + + // update windows + InvalidateWindowClassesData(WC_TRACE_RESTRICT); +} diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 87e8cb9b8d..2106d1060c 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -59,10 +59,11 @@ enum TraceRestrictItemFlagAllocation { /* 3 bits reserved for future use */ - TRIFA_COND_FLAGS_COUNT = 2, + TRIFA_COND_FLAGS_COUNT = 3, TRIFA_COND_FLAGS_OFFSET = 8, - /* 3 bits reserved for future use */ + TRIFA_AUX_FIELD_COUNT = 2, + TRIFA_AUX_FIELD_OFFSET = 11, TRIFA_COND_OP_COUNT = 3, TRIFA_COND_OP_OFFSET = 13, @@ -81,6 +82,7 @@ enum TraceRestrictItemType { TRIT_COND_UNDEFINED = 9, ///< This condition has no type defined (evaluate as false) TRIT_COND_TRAIN_LENGTH = 10, ///< Test train length TRIT_COND_MAX_SPEED = 11, ///< Test train max speed + TRIT_COND_CURRENT_ORDER = 12, ///< Test train current order (station, waypoint or depot) /* space up to 31 */ }; @@ -88,6 +90,7 @@ enum TraceRestrictItemType { enum TraceRestrictCondFlags { TRCF_ELSE = 1 << 0, TRCF_OR = 1 << 1, + /* 1 bit spare */ }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictCondFlags) @@ -107,6 +110,13 @@ enum TraceRestrictCondOp { /* space up to 7 */ }; +enum TraceRestrictOrderCondAuxField { + TROCAF_STATION = 0, + TROCAF_WAYPOINT = 1, + TROCAF_DEPOT = 2, + /* space up to 7 */ +}; + enum TraceRestrictProgramResultFlags { TRPRF_DENY = 1 << 0, }; @@ -155,6 +165,11 @@ static inline TraceRestrictCondOp GetTraceRestrictCondOp(TraceRestrictItem item) return static_cast(GB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT)); } +static inline uint8 GetTraceRestrictAuxField(TraceRestrictItem item) +{ + return GB(item, TRIFA_AUX_FIELD_OFFSET, TRIFA_AUX_FIELD_COUNT); +} + static inline uint16 GetTraceRestrictValue(TraceRestrictItem item) { return static_cast(GB(item, TRIFA_VALUE_OFFSET, TRIFA_VALUE_COUNT)); @@ -170,6 +185,11 @@ static inline void SetTraceRestrictCondOp(TraceRestrictItem &item, TraceRestrict SB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT, condop); } +static inline void SetTraceRestrictAuxField(TraceRestrictItem &item, uint8 data) +{ + SB(item, TRIFA_AUX_FIELD_OFFSET, TRIFA_AUX_FIELD_COUNT, data); +} + void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItemType type); static inline void SetTraceRestrictValue(TraceRestrictItem &item, uint16 value) @@ -199,6 +219,7 @@ enum TraceRestrictValueType { TRVT_INT = 2, ///< takes an integer value TRVT_DENY = 3, ///< takes a value 0 = deny, 1 = allow (cancel previous deny) TRVT_SPEED = 4, ///< takes an integer speed value + TRVT_ORDER = 5, ///< takes an order target ID, as per the auxiliary field as type: TraceRestrictOrderCondAuxField }; struct TraceRestrictTypePropertySet { @@ -229,6 +250,11 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.value_type = TRVT_SPEED; break; + case TRIT_COND_CURRENT_ORDER: + out.value_type = TRVT_ORDER; + out.cond_type = TRCOT_BINARY; + break; + default: NOT_REACHED(); break; @@ -311,4 +337,6 @@ CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); +void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index); + #endif /* TRACERESTRICT_H */ diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index b1e8251898..8f04d0203b 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -23,7 +23,11 @@ #include "gui.h" #include "gfx_func.h" #include "rail_map.h" +#include "depot_map.h" #include "tile_cmd.h" +#include "station_base.h" +#include "waypoint_base.h" +#include "depot_base.h" #include "error.h" #include "table/sprites.h" @@ -44,6 +48,7 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_COMPARATOR, TR_WIDGET_VALUE_INT, TR_WIDGET_VALUE_DROPDOWN, + TR_WIDGET_VALUE_DEST, TR_WIDGET_BLANK_L, TR_WIDGET_BLANK_M, @@ -70,6 +75,7 @@ enum PanelWidgets { // Right DPR_VALUE_INT = 0, DPR_VALUE_DROPDOWN, + DPR_VALUE_DEST, DPR_BLANK, // Share @@ -152,12 +158,14 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI static const StringID str_cond[] = { STR_TRACE_RESTRICT_VARIABLE_TRAIN_LENGTH, STR_TRACE_RESTRICT_VARIABLE_MAX_SPEED, + STR_TRACE_RESTRICT_VARIABLE_CURRENT_ORDER, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; static const uint val_cond[] = { TRIT_COND_TRAIN_LENGTH, TRIT_COND_MAX_SPEED, + TRIT_COND_CURRENT_ORDER, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { @@ -257,12 +265,17 @@ static const StringID _program_cond_type[] = { /* TRCF_OR */ STR_TRACE_RESTRICT_CONDITIONAL_ORIF, }; -static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) +static void DrawInstructionStringConditionalCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) { assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, GetTypeString(GetTraceRestrictType(item))); SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties.cond_type), GetTraceRestrictCondOp(item))); +} + +static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) +{ + DrawInstructionStringConditionalCommon(item, properties); SetDParam(3, GetTraceRestrictValue(item)); } @@ -292,14 +305,55 @@ static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED; SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); - } else if (properties.value_type == TRVT_INT) { - instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER; - DrawInstructionStringConditionalIntegerCommon(item, properties); - } else if (properties.value_type == TRVT_SPEED) { - instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED; - DrawInstructionStringConditionalIntegerCommon(item, properties); } else { - NOT_REACHED(); + switch (properties.value_type) { + case TRVT_INT: + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER; + DrawInstructionStringConditionalIntegerCommon(item, properties); + break; + + case TRVT_SPEED: + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED; + DrawInstructionStringConditionalIntegerCommon(item, properties); + break; + + case TRVT_ORDER: { + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TROCAF_STATION: + if (GetTraceRestrictValue(item) != INVALID_STATION) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_STATION; + DrawInstructionStringConditionalIntegerCommon(item, properties); + } else { + // this is an invalid station, use a seperate string + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED; + DrawInstructionStringConditionalCommon(item, properties); + SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); + } + break; + + case TROCAF_WAYPOINT: + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_WAYPOINT; + DrawInstructionStringConditionalIntegerCommon(item, properties); + break; + + case TROCAF_DEPOT: + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_DEPOT; + DrawInstructionStringConditionalCommon(item, properties); + SetDParam(3, VEH_TRAIN); + SetDParam(4, GetTraceRestrictValue(item)); + break; + + default: + NOT_REACHED(); + break; + } + break; + } + + default: + NOT_REACHED(); + break; + } } } else { switch (GetTraceRestrictType(item)) { @@ -369,6 +423,35 @@ public: case TR_WIDGET_INSTRUCTION_LIST: { int sel = this->GetInstructionFromPt(pt.y); + if (_ctrl_pressed) { + // scroll to target (for stations, waypoints, depots) + + if (sel == -1) return; + + TraceRestrictItem item = this->GetItem(this->GetProgram(), sel); + if (GetTraceRestrictTypeProperties(item).value_type == TRVT_ORDER) { + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TROCAF_STATION: + case TROCAF_WAYPOINT: { + BaseStation *st = BaseStation::GetIfValid(GetTraceRestrictValue(item)); + if (st) { + ScrollMainWindowToTile(st->xy); + } + break; + } + + case TROCAF_DEPOT: { + Depot *depot = Depot::GetIfValid(GetTraceRestrictValue(item)); + if (depot) { + ScrollMainWindowToTile(depot->xy); + } + break; + } + } + } + return; + } + this->DeleteChildWindows(); HideDropDownMenu(this); @@ -440,6 +523,11 @@ public: break; } + case TR_WIDGET_VALUE_DEST: { + SetObjectToPlaceAction(widget, ANIMCURSOR_PICKSTATION); + break; + } + case TR_WIDGET_GOTO_SIGNAL: ScrollMainWindowToTile(this->tile); break; @@ -451,7 +539,7 @@ public: case TR_WIDGET_COPY: case TR_WIDGET_SHARE: - SelectSignalAction(widget); + SetObjectToPlaceAction(widget, ANIMCURSOR_BUILDSIGNALS); break; case TR_WIDGET_UNSHARE: { @@ -527,7 +615,7 @@ public: } } - virtual void OnPlaceObject(Point pt, TileIndex source_tile) + virtual void OnPlaceObject(Point pt, TileIndex tile) { int widget = this->current_placement_widget; this->current_placement_widget = -1; @@ -539,8 +627,27 @@ public: return; } - int error_message = (widget == TR_WIDGET_COPY) ? STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM : STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM; + switch (widget) { + case TR_WIDGET_COPY: + OnPlaceObjectSignal(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM); + break; + case TR_WIDGET_SHARE: + OnPlaceObjectSignal(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM); + break; + + case TR_WIDGET_VALUE_DEST: + OnPlaceObjectDestination(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + break; + + default: + NOT_REACHED(); + break; + } + } + + void OnPlaceObjectSignal(Point pt, TileIndex source_tile, int widget, int error_message) + { if (!IsPlainRailTile(source_tile)) { ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); return; @@ -587,6 +694,38 @@ public: } } + void OnPlaceObjectDestination(Point pt, TileIndex tile, int widget, int error_message) + { + TraceRestrictItem item = GetSelected(); + if (GetTraceRestrictTypeProperties(item).value_type != TRVT_ORDER) return; + + if (IsDepotTypeTile(tile, TRANSPORT_RAIL)) { + SetTraceRestrictValue(item, GetDepotIndex(tile)); + SetTraceRestrictAuxField(item, TROCAF_DEPOT); + } else if (IsRailWaypointTile(tile)) { + SetTraceRestrictValue(item, GetStationIndex(tile)); + SetTraceRestrictAuxField(item, TROCAF_WAYPOINT); + } else if (IsTileType(tile, MP_STATION)) { + StationID st_index = GetStationIndex(tile); + const Station *st = Station::Get(st_index); + if (st->facilities & FACIL_TRAIN) { + SetTraceRestrictValue(item, st_index); + SetTraceRestrictAuxField(item, TROCAF_STATION); + } else { + return; + } + } else { + return; + } + + if (!IsTileOwner(tile, _local_company)) { + ShowErrorMessage(error_message, STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, WL_INFO); + return; + } + + TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + } + virtual void OnPlaceObjectAbort() { this->RaiseButtons(); @@ -789,6 +928,7 @@ private: this->RaiseWidget(TR_WIDGET_COMPARATOR); this->RaiseWidget(TR_WIDGET_VALUE_INT); this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN); + this->RaiseWidget(TR_WIDGET_VALUE_DEST); NWidgetStacked *left_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT); NWidgetStacked *middle_sel = this->GetWidget(TR_WIDGET_SEL_TOP_MIDDLE); @@ -799,6 +939,7 @@ private: this->DisableWidget(TR_WIDGET_COMPARATOR); this->DisableWidget(TR_WIDGET_VALUE_INT); this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN); + this->DisableWidget(TR_WIDGET_VALUE_DEST); this->DisableWidget(TR_WIDGET_INSERT); this->DisableWidget(TR_WIDGET_REMOVE); @@ -892,11 +1033,23 @@ private: if (IsIntegerValueType(properties.value_type)) { right_sel->SetDisplayedPlane(DPR_VALUE_INT); this->EnableWidget(TR_WIDGET_VALUE_INT); - } else if (properties.value_type == TRVT_DENY) { - right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); - this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); - this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = - GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW : STR_TRACE_RESTRICT_PF_DENY; + } else { + switch (properties.value_type) { + case TRVT_DENY: + right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); + this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); + this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = + GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW : STR_TRACE_RESTRICT_PF_DENY; + break; + + case TRVT_ORDER: + right_sel->SetDisplayedPlane(DPR_VALUE_DEST); + this->EnableWidget(TR_WIDGET_VALUE_DEST); + break; + + default: + break; + } } this->EnableWidget(TR_WIDGET_INSERT); @@ -915,12 +1068,12 @@ private: ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width); } - void SelectSignalAction(int widget) + void SetObjectToPlaceAction(int widget, CursorID cursor) { this->ToggleWidgetLoweredState(widget); this->SetWidgetDirty(widget); if (this->IsWidgetLowered(widget)) { - SetObjectToPlaceWnd(ANIMCURSOR_BUILDSIGNALS, PAL_NONE, HT_RECT, this); + SetObjectToPlaceWnd(cursor, PAL_NONE, HT_RECT, this); this->current_placement_widget = widget; } else { ResetObjectToPlace(); @@ -940,7 +1093,7 @@ static const NWidgetPart _nested_program_widgets[] = { // Program display NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_GREY, TR_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_TRACE_RESTRICT_SIGNAL_GUI_TOOLTIP), SetResize(1, 1), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, TR_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_TRACE_RESTRICT_INSTRUCTION_LIST_TOOLTIP), SetResize(1, 1), EndContainer(), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TR_WIDGET_SCROLLBAR), EndContainer(), @@ -964,6 +1117,8 @@ static const NWidgetPart _nested_program_widgets[] = { SetDataTip(STR_BLACK_COMMA, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_VALUE_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_DEST), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_SELECT_TARGET, STR_TRACE_RESTRICT_SELECT_TARGET), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_R), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), EndContainer(), diff --git a/src/waypoint.cpp b/src/waypoint.cpp index 857f8ba874..3e5f02f3c7 100644 --- a/src/waypoint.cpp +++ b/src/waypoint.cpp @@ -15,6 +15,7 @@ #include "window_func.h" #include "newgrf_station.h" #include "waypoint_base.h" +#include "tracerestrict.h" #include "safeguards.h" @@ -54,4 +55,5 @@ Waypoint::~Waypoint() if (CleaningPool()) return; DeleteWindowById(WC_WAYPOINT_VIEW, this->index); RemoveOrderFromAllVehicles(OT_GOTO_WAYPOINT, this->index); + TraceRestrictRemoveDestinationID(TROCAF_WAYPOINT, this->index); } From 2296fc85e6e913bd265cc713adc4d783c1cefac3 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 24 Jul 2015 19:00:38 +0100 Subject: [PATCH 05/15] Add next order condition variable. --- src/lang/english.txt | 1 + src/tracerestrict.cpp | 17 ++++++++++++++++- src/tracerestrict.h | 2 ++ src/tracerestrict_gui.cpp | 2 ++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 65ec673cd3..cca349b9d8 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2385,6 +2385,7 @@ 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_UNDEFINED :undefined STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER :{STRING} {STRING} {STRING} {COMMA} then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED :{STRING} {STRING} {STRING} {VELOCITY} then diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 737c57fddc..046e35d99b 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -218,6 +218,20 @@ void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& o 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; + } + default: NOT_REACHED(); } @@ -737,7 +751,8 @@ void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint1 FOR_ALL_TRACE_RESTRICT_PROGRAMS(prog) { for (size_t i = 0; i < prog->items.size(); i++) { TraceRestrictItem &item = prog->items[i]; // note this is a reference, - if (GetTraceRestrictType(item) == TRIT_COND_CURRENT_ORDER) { + if (GetTraceRestrictType(item) == TRIT_COND_CURRENT_ORDER || + GetTraceRestrictType(item) == TRIT_COND_NEXT_ORDER) { if (GetTraceRestrictAuxField(item) == type && GetTraceRestrictValue(item) == index) { SetTraceRestrictValueDefault(item, TRVT_ORDER); // this updates the instruction in-place } diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 2106d1060c..9a2dc25e78 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -83,6 +83,7 @@ enum TraceRestrictItemType { TRIT_COND_TRAIN_LENGTH = 10, ///< Test train length TRIT_COND_MAX_SPEED = 11, ///< Test train max speed TRIT_COND_CURRENT_ORDER = 12, ///< Test train current order (station, waypoint or depot) + TRIT_COND_NEXT_ORDER = 13, ///< Test train next order (station, waypoint or depot) /* space up to 31 */ }; @@ -251,6 +252,7 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR break; case TRIT_COND_CURRENT_ORDER: + case TRIT_COND_NEXT_ORDER: out.value_type = TRVT_ORDER; out.cond_type = TRCOT_BINARY; break; diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 8f04d0203b..0f13f66099 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -159,6 +159,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI STR_TRACE_RESTRICT_VARIABLE_TRAIN_LENGTH, STR_TRACE_RESTRICT_VARIABLE_MAX_SPEED, STR_TRACE_RESTRICT_VARIABLE_CURRENT_ORDER, + STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; @@ -166,6 +167,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI TRIT_COND_TRAIN_LENGTH, TRIT_COND_MAX_SPEED, TRIT_COND_CURRENT_ORDER, + TRIT_COND_NEXT_ORDER, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { From fedf61897836354db523c0df09195ee06048a7a9 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 24 Jul 2015 19:04:45 +0100 Subject: [PATCH 06/15] Add last visited station condition. --- src/lang/english.txt | 1 + src/tracerestrict.cpp | 39 ++++++++++++++++++++++++++++----------- src/tracerestrict.h | 7 +++++-- src/tracerestrict_gui.cpp | 10 ++++++++++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index cca349b9d8..55cf9eb6be 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2386,6 +2386,7 @@ 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_UNDEFINED :undefined STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER :{STRING} {STRING} {STRING} {COMMA} then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED :{STRING} {STRING} {STRING} {VELOCITY} then diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 046e35d99b..3d877e6778 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -132,6 +132,21 @@ static bool TestCondition(uint16 value, TraceRestrictCondOp condop, uint16 condv } } +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 /// order may be NULL static bool TestOrderCondition(const Order *order, TraceRestrictItem item) @@ -157,18 +172,15 @@ static bool TestOrderCondition(const Order *order, TraceRestrictItem item) NOT_REACHED(); } } + return TestBinaryConditionCommon(item, result); +} - switch (GetTraceRestrictCondOp(item)) { - case TRCO_IS: - return result; +/// Test station condition +static bool TestStationCondition(StationID station, TraceRestrictItem item) +{ + bool result = (GetTraceRestrictAuxField(item) == TROCAF_STATION) && (station == GetTraceRestrictValue(item)); + return TestBinaryConditionCommon(item, result); - case TRCO_ISNOT: - return !result; - - default: - NOT_REACHED(); - return false; - } } /// Execute program on train and store results in out @@ -232,6 +244,10 @@ void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& o break; } + case TRIT_COND_LAST_STATION: + result = TestStationCondition(v->last_station_visited, item); + break; + default: NOT_REACHED(); } @@ -752,7 +768,8 @@ void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint1 for (size_t i = 0; i < prog->items.size(); i++) { TraceRestrictItem &item = prog->items[i]; // note this is a reference, if (GetTraceRestrictType(item) == TRIT_COND_CURRENT_ORDER || - GetTraceRestrictType(item) == TRIT_COND_NEXT_ORDER) { + GetTraceRestrictType(item) == TRIT_COND_NEXT_ORDER || + GetTraceRestrictType(item) == TRIT_COND_LAST_STATION) { if (GetTraceRestrictAuxField(item) == type && GetTraceRestrictValue(item) == index) { SetTraceRestrictValueDefault(item, TRVT_ORDER); // this updates the instruction in-place } diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 9a2dc25e78..7a62dc2053 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -84,6 +84,7 @@ enum TraceRestrictItemType { TRIT_COND_MAX_SPEED = 11, ///< Test train max speed TRIT_COND_CURRENT_ORDER = 12, ///< Test train current order (station, waypoint or depot) TRIT_COND_NEXT_ORDER = 13, ///< Test train next order (station, waypoint or depot) + TRIT_COND_LAST_STATION = 14, ///< Test train last visited station /* space up to 31 */ }; @@ -191,8 +192,6 @@ static inline void SetTraceRestrictAuxField(TraceRestrictItem &item, uint8 data) SB(item, TRIFA_AUX_FIELD_OFFSET, TRIFA_AUX_FIELD_COUNT, data); } -void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItemType type); - static inline void SetTraceRestrictValue(TraceRestrictItem &item, uint16 value) { SB(item, TRIFA_VALUE_OFFSET, TRIFA_VALUE_COUNT, value); @@ -223,6 +222,9 @@ enum TraceRestrictValueType { TRVT_ORDER = 5, ///< takes an order target ID, as per the auxiliary field as type: TraceRestrictOrderCondAuxField }; +void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueType value_type); +void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItemType type); + struct TraceRestrictTypePropertySet { TraceRestrictConditionOpType cond_type; TraceRestrictValueType value_type; @@ -253,6 +255,7 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR case TRIT_COND_CURRENT_ORDER: case TRIT_COND_NEXT_ORDER: + case TRIT_COND_LAST_STATION: out.value_type = TRVT_ORDER; out.cond_type = TRCOT_BINARY; break; diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 0f13f66099..c4d7d4a88d 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -160,6 +160,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI STR_TRACE_RESTRICT_VARIABLE_MAX_SPEED, STR_TRACE_RESTRICT_VARIABLE_CURRENT_ORDER, STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER, + STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; @@ -168,6 +169,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI TRIT_COND_MAX_SPEED, TRIT_COND_CURRENT_ORDER, TRIT_COND_NEXT_ORDER, + TRIT_COND_LAST_STATION, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { @@ -599,6 +601,10 @@ public: case TR_WIDGET_TYPE: { SetTraceRestrictTypeAndNormalise(item, static_cast(value)); + if (GetTraceRestrictType(item) == TRIT_COND_LAST_STATION && GetTraceRestrictAuxField(item) != TROCAF_STATION) { + // if changing type from another order type to last visited station, reset value if not currently a station + SetTraceRestrictValueDefault(item, TRVT_ORDER); + } TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; } @@ -701,10 +707,14 @@ public: TraceRestrictItem item = GetSelected(); if (GetTraceRestrictTypeProperties(item).value_type != TRVT_ORDER) return; + bool stations_only = (GetTraceRestrictType(item) == TRIT_COND_LAST_STATION); + if (IsDepotTypeTile(tile, TRANSPORT_RAIL)) { + if (stations_only) return; SetTraceRestrictValue(item, GetDepotIndex(tile)); SetTraceRestrictAuxField(item, TROCAF_DEPOT); } else if (IsRailWaypointTile(tile)) { + if (stations_only) return; SetTraceRestrictValue(item, GetStationIndex(tile)); SetTraceRestrictAuxField(item, TROCAF_WAYPOINT); } else if (IsTileType(tile, MP_STATION)) { From 3a7a81e02f3ced3ca57e8fcfb0a38398dda4d1b1 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 24 Jul 2015 20:21:33 +0100 Subject: [PATCH 07/15] Add GUI support for else if, or if and else blocks. Add another drop-down to change between these. Opening if blocks cannot be converted to/from these. --- src/lang/english.txt | 1 + src/tracerestrict.h | 6 + src/tracerestrict_gui.cpp | 227 +++++++++++++++++++++++++++++++++++--- 3 files changed, 219 insertions(+), 15 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 55cf9eb6be..132d7a1eb1 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2409,6 +2409,7 @@ STR_TRACE_RESTRICT_CAPTION_SHARED :{WHITE}Routefin 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 diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 7a62dc2053..0ffe9ba0f1 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -90,6 +90,7 @@ enum TraceRestrictItemType { /* no flags set indicates end if for TRIT_COND_ENDIF, if otherwise */ enum TraceRestrictCondFlags { + TRCF_DEFAULT = 0, TRCF_ELSE = 1 << 0, TRCF_OR = 1 << 1, /* 1 bit spare */ @@ -187,6 +188,11 @@ static inline void SetTraceRestrictCondOp(TraceRestrictItem &item, TraceRestrict SB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT, condop); } +static inline void SetTraceRestrictCondFlags(TraceRestrictItem &item, TraceRestrictCondFlags condflags) +{ + SB(item, TRIFA_COND_FLAGS_OFFSET, TRIFA_COND_FLAGS_COUNT, condflags); +} + static inline void SetTraceRestrictAuxField(TraceRestrictItem &item, uint8 data) { SB(item, TRIFA_AUX_FIELD_OFFSET, TRIFA_AUX_FIELD_COUNT, data); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index c4d7d4a88d..b81f252a23 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -39,17 +39,21 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_INSTRUCTION_LIST, TR_WIDGET_SCROLLBAR, + TR_WIDGET_SEL_TOP_LEFT_2, TR_WIDGET_SEL_TOP_LEFT, TR_WIDGET_SEL_TOP_MIDDLE, TR_WIDGET_SEL_TOP_RIGHT, TR_WIDGET_SEL_SHARE, - TR_WIDGET_TYPE, + TR_WIDGET_TYPE_COND, + TR_WIDGET_TYPE_NONCOND, + TR_WIDGET_CONDFLAGS, TR_WIDGET_COMPARATOR, TR_WIDGET_VALUE_INT, TR_WIDGET_VALUE_DROPDOWN, TR_WIDGET_VALUE_DEST, + TR_WIDGET_BLANK_L2, TR_WIDGET_BLANK_L, TR_WIDGET_BLANK_M, TR_WIDGET_BLANK_R, @@ -64,6 +68,11 @@ enum TraceRestrictWindowWidgets { }; enum PanelWidgets { + // Left 2 + DPL2_TYPE = 0, + DPL2_CONDFLAGS, + DPL2_BLANK, + // Left DPL_TYPE = 0, DPL_BLANK, @@ -93,14 +102,21 @@ struct TraceRestrictDropDownListSet { static const StringID _program_insert_str[] = { STR_TRACE_RESTRICT_CONDITIONAL_IF, + STR_TRACE_RESTRICT_CONDITIONAL_ELIF, + STR_TRACE_RESTRICT_CONDITIONAL_ELSE, STR_TRACE_RESTRICT_PF_DENY, STR_TRACE_RESTRICT_PF_PENALTY, INVALID_STRING_ID }; +static const uint _program_insert_else_flag = 0x100; +static const uint32 _program_insert_else_hide_mask = 4; +static const uint32 _program_insert_else_if_hide_mask = 2; static const uint _program_insert_val[] = { - TRIT_COND_UNDEFINED, - TRIT_PF_DENY, - TRIT_PF_PENALTY, + TRIT_COND_UNDEFINED, /// if block + TRIT_COND_UNDEFINED | _program_insert_else_flag, /// elif block + TRIT_COND_ENDIF | _program_insert_else_flag, /// else block + TRIT_PF_DENY, /// deny + TRIT_PF_PENALTY, /// penalty }; static const TraceRestrictDropDownListSet _program_insert = { @@ -269,6 +285,31 @@ static const StringID _program_cond_type[] = { /* TRCF_OR */ STR_TRACE_RESTRICT_CONDITIONAL_ORIF, }; +enum CondFlagsDropDownType { + CFDDT_ELSE = 0, ///< This is an else block + CFDDT_ELIF = TRCF_ELSE, ///< This is an else-if block + CFDDT_ORIF = TRCF_OR, ///< This is an or-if block +}; + +static const uint32 _condflags_dropdown_else_hide_mask = 1; +static const uint32 _condflags_dropdown_else_if_hide_mask = 6; +static const StringID _condflags_dropdown_str[] = { + /* CFDDT_ELSE */ STR_TRACE_RESTRICT_CONDITIONAL_ELSE, + /* CFDDT_ELIF */ STR_TRACE_RESTRICT_CONDITIONAL_ELIF, + /* CFDDT_ORIF */ STR_TRACE_RESTRICT_CONDITIONAL_ORIF, + INVALID_STRING_ID, +}; + +static const uint _condflags_dropdown_val[] = { + CFDDT_ELSE, + CFDDT_ELIF, + CFDDT_ORIF, +}; + +static const TraceRestrictDropDownListSet _condflags_dropdown = { + _condflags_dropdown_str, _condflags_dropdown_val, +}; + static void DrawInstructionStringConditionalCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) { assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); @@ -476,7 +517,21 @@ public: if (this->GetOwner() != _local_company || this->selected_instruction < 1) { return; } - this->ShowDropDownListWithValue(&_program_insert, 0, true, TR_WIDGET_INSERT, 0, 0, 0); + + uint32 disabled = 0; + TraceRestrictItem item = this->GetSelected(); + if (GetTraceRestrictType(item) == TRIT_COND_ENDIF || + (IsTraceRestrictConditional(item) && GetTraceRestrictCondFlags(item) != 0)) { + // this is either: an else/or if, an else, or an end if + // try to include else if, else in insertion list + if (!ElseInsertionDryRun(false)) disabled |= _program_insert_else_hide_mask; + if (!ElseIfInsertionDryRun(false)) disabled |= _program_insert_else_if_hide_mask; + } else { + // can't insert else/end if here + disabled |= _program_insert_else_hide_mask | _program_insert_else_if_hide_mask; + } + + this->ShowDropDownListWithValue(&_program_insert, 0, true, TR_WIDGET_INSERT, disabled, 0, 0); break; } @@ -490,12 +545,37 @@ public: break; } - case TR_WIDGET_TYPE: { + case TR_WIDGET_CONDFLAGS: { + TraceRestrictItem item = this->GetSelected(); + if (this->GetOwner() != _local_company || item == 0) { + return; + } + + CondFlagsDropDownType type; + if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { + if (GetTraceRestrictCondFlags(item) == 0) return; // end if + type = CFDDT_ELSE; + } else if (IsTraceRestrictConditional(item) && GetTraceRestrictCondFlags(item) != 0) { + type = static_cast(GetTraceRestrictCondFlags(item)); + } else { + return; + } + + uint32 disabled = 0; + if (!ElseInsertionDryRun(true)) disabled |= _condflags_dropdown_else_hide_mask; + if (!ElseIfInsertionDryRun(true)) disabled |= _condflags_dropdown_else_if_hide_mask; + + this->ShowDropDownListWithValue(&_condflags_dropdown, type, false, TR_WIDGET_CONDFLAGS, disabled, 0, 0); + break; + } + + case TR_WIDGET_TYPE_COND: + case TR_WIDGET_TYPE_NONCOND: { TraceRestrictItem item = this->GetSelected(); TraceRestrictItemType type = GetTraceRestrictType(item); if (type != TRIT_NULL) { - this->ShowDropDownListWithValue(GetTypeDropDownListSet(type), type, false, TR_WIDGET_TYPE, 0, 0, 0); + this->ShowDropDownListWithValue(GetTypeDropDownListSet(type), type, false, widget, 0, 0, 0); } break; } @@ -593,13 +673,40 @@ public: switch (widget) { case TR_WIDGET_INSERT: { TraceRestrictItem insert_item = 0; + + bool have_else = false; + if (value & _program_insert_else_flag) { + value &= ~_program_insert_else_flag; + have_else = true; + } SetTraceRestrictTypeAndNormalise(insert_item, static_cast(value)); + if (have_else) SetTraceRestrictCondFlags(insert_item, TRCF_ELSE); // this needs to happen after calling SetTraceRestrictTypeAndNormalise + this->expecting_inserted_item = insert_item; TraceRestrictDoCommandP(this->tile, this->track, TRDCT_INSERT_ITEM, this->selected_instruction - 1, insert_item, STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM); break; } - case TR_WIDGET_TYPE: { + case TR_WIDGET_CONDFLAGS: { + CondFlagsDropDownType cond_type = static_cast(value); + if (cond_type == CFDDT_ELSE) { + SetTraceRestrictTypeAndNormalise(item, TRIT_COND_ENDIF); + SetTraceRestrictCondFlags(item, TRCF_ELSE); + } else { + if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { + // item is currently an else, convert to else/or if + SetTraceRestrictTypeAndNormalise(item, TRIT_COND_UNDEFINED); + } + + SetTraceRestrictCondFlags(item, static_cast(cond_type)); + } + + TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + break; + } + + case TR_WIDGET_TYPE_COND: + case TR_WIDGET_TYPE_NONCOND: { SetTraceRestrictTypeAndNormalise(item, static_cast(value)); if (GetTraceRestrictType(item) == TRIT_COND_LAST_STATION && GetTraceRestrictAuxField(item) != TROCAF_STATION) { // if changing type from another order type to last visited station, reset value if not currently a station @@ -936,18 +1043,23 @@ private: { this->RaiseWidget(TR_WIDGET_INSERT); this->RaiseWidget(TR_WIDGET_REMOVE); - this->RaiseWidget(TR_WIDGET_TYPE); + this->RaiseWidget(TR_WIDGET_TYPE_COND); + this->RaiseWidget(TR_WIDGET_TYPE_NONCOND); + this->RaiseWidget(TR_WIDGET_CONDFLAGS); this->RaiseWidget(TR_WIDGET_COMPARATOR); this->RaiseWidget(TR_WIDGET_VALUE_INT); this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN); this->RaiseWidget(TR_WIDGET_VALUE_DEST); + NWidgetStacked *left_2_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT_2); NWidgetStacked *left_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT); NWidgetStacked *middle_sel = this->GetWidget(TR_WIDGET_SEL_TOP_MIDDLE); NWidgetStacked *right_sel = this->GetWidget(TR_WIDGET_SEL_TOP_RIGHT); NWidgetStacked *share_sel = this->GetWidget(TR_WIDGET_SEL_SHARE); - this->DisableWidget(TR_WIDGET_TYPE); + this->DisableWidget(TR_WIDGET_TYPE_COND); + this->DisableWidget(TR_WIDGET_TYPE_NONCOND); + this->DisableWidget(TR_WIDGET_CONDFLAGS); this->DisableWidget(TR_WIDGET_COMPARATOR); this->DisableWidget(TR_WIDGET_VALUE_INT); this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN); @@ -960,10 +1072,12 @@ private: this->DisableWidget(TR_WIDGET_SHARE); this->DisableWidget(TR_WIDGET_UNSHARE); + this->DisableWidget(TR_WIDGET_BLANK_L2); this->DisableWidget(TR_WIDGET_BLANK_L); this->DisableWidget(TR_WIDGET_BLANK_M); this->DisableWidget(TR_WIDGET_BLANK_R); + left_2_sel->SetDisplayedPlane(DPL2_BLANK); left_sel->SetDisplayedPlane(DPL_BLANK); middle_sel->SetDisplayedPlane(DPM_BLANK); right_sel->SetDisplayedPlane(DPR_BLANK); @@ -1018,16 +1132,53 @@ private: } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { this->EnableWidget(TR_WIDGET_INSERT); if (GetTraceRestrictCondFlags(item) != 0) { - // this is not an end if, enable removing + // this is not an end if, it must be an else, enable removing this->EnableWidget(TR_WIDGET_REMOVE); + + // setup condflags dropdown to show else + left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS); + this->EnableWidget(TR_WIDGET_CONDFLAGS); + this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ELSE; } } else { TraceRestrictTypePropertySet properties = GetTraceRestrictTypeProperties(item); - left_sel->SetDisplayedPlane(DPL_TYPE); - this->EnableWidget(TR_WIDGET_TYPE); + int type_widget; + if (IsTraceRestrictConditional(item)) { + // note that else and end if items are not handled here, they are handled above - this->GetWidget(TR_WIDGET_TYPE)->widget_data = + left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS); + left_sel->SetDisplayedPlane(DPL_TYPE); + type_widget = TR_WIDGET_TYPE_COND; + + // setup condflags dropdown box + left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS); + switch (GetTraceRestrictCondFlags(item)) { + case TRCF_DEFAULT: // opening if, leave disabled + this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_IF; + break; + + case TRCF_ELSE: // else-if + this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ELIF; + this->EnableWidget(TR_WIDGET_CONDFLAGS); + break; + + case TRCF_OR: // or-if + this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ORIF; + this->EnableWidget(TR_WIDGET_CONDFLAGS); + break; + + default: + NOT_REACHED(); + break; + } + } else { + left_2_sel->SetDisplayedPlane(DPL2_TYPE); + type_widget = TR_WIDGET_TYPE_NONCOND; + } + this->EnableWidget(type_widget); + + this->GetWidget(type_widget)->widget_data = GetTypeString(GetTraceRestrictType(item)); if (properties.cond_type == TRCOT_BINARY || properties.cond_type == TRCOT_ALL) { @@ -1092,6 +1243,44 @@ private: this->current_placement_widget = -1; } } + + /// This used for testing whether else or else-if blocks could be inserted, or replace the selection + bool GenericElseInsertionDryRun(TraceRestrictItem item, bool replace) + { + if (this->selected_instruction < 1) return false; + uint offset = this->selected_instruction - 1; + + const TraceRestrictProgram *prog = this->GetProgram(); + if (!prog) return false; + + std::vector items = prog->items; // copy + + if (offset >= (items.size() + (replace ? 0 : 1))) return false; // off the end of the program + + if (replace) { + items[offset] = item; + } else { + items.insert(items.begin() + offset, item); + } + + return TraceRestrictProgram::Validate(items).Succeeded(); + } + + bool ElseInsertionDryRun(bool replace) + { + TraceRestrictItem item = 0; + SetTraceRestrictType(item, TRIT_COND_ENDIF); + SetTraceRestrictCondFlags(item, TRCF_ELSE); + return GenericElseInsertionDryRun(item, replace); + } + + bool ElseIfInsertionDryRun(bool replace) + { + TraceRestrictItem item = 0; + SetTraceRestrictType(item, TRIT_COND_UNDEFINED); + SetTraceRestrictCondFlags(item, TRCF_ELSE); + return GenericElseInsertionDryRun(item, replace); + } }; static const NWidgetPart _nested_program_widgets[] = { @@ -1112,8 +1301,16 @@ static const NWidgetPart _nested_program_widgets[] = { // Button Bar NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT_2), + NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_NONCOND), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_CONDFLAGS), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_TRACE_RESTRICT_CONDFLAGS_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_L2), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), + EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT), - NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE), SetMinimalSize(124, 12), SetFill(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_COND), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_L), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), From 75869cae4159508a42ed40c0f77d8099457fa020 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 25 Jul 2015 00:10:14 +0100 Subject: [PATCH 08/15] Add train can carry cargo condition variable. --- src/lang/english.txt | 4 ++ src/tracerestrict.cpp | 19 ++++++++++ src/tracerestrict.h | 7 ++++ src/tracerestrict_gui.cpp | 80 +++++++++++++++++++++++++++++++++++---- 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 132d7a1eb1..785dc754db 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2377,6 +2377,8 @@ 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 @@ -2387,12 +2389,14 @@ 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_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_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} diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 3d877e6778..6bd9c8e7c5 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -17,6 +17,7 @@ #include "viewport_func.h" #include "window_func.h" #include "order_base.h" +#include "cargotype.h" #include "pathfinder/yapf/yapf_cache.h" #include @@ -248,6 +249,18 @@ void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& o 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; + } + default: NOT_REACHED(); } @@ -348,6 +361,12 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp 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; + default: NOT_REACHED(); break; diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 0ffe9ba0f1..424daa9fc8 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -85,6 +85,7 @@ enum TraceRestrictItemType { TRIT_COND_CURRENT_ORDER = 12, ///< Test train current order (station, waypoint or depot) TRIT_COND_NEXT_ORDER = 13, ///< Test train next order (station, waypoint or depot) TRIT_COND_LAST_STATION = 14, ///< Test train last visited station + TRIT_COND_CARGO = 15, ///< Test if train can carry cargo type /* space up to 31 */ }; @@ -226,6 +227,7 @@ enum TraceRestrictValueType { TRVT_DENY = 3, ///< takes a value 0 = deny, 1 = allow (cancel previous deny) TRVT_SPEED = 4, ///< takes an integer speed value TRVT_ORDER = 5, ///< takes an order target ID, as per the auxiliary field as type: TraceRestrictOrderCondAuxField + TRVT_CARGO_ID = 6, ///< takes a CargoID }; void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueType value_type); @@ -266,6 +268,11 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.cond_type = TRCOT_BINARY; break; + case TRIT_COND_CARGO: + out.value_type = TRVT_CARGO_ID; + out.cond_type = TRCOT_BINARY; + break; + default: NOT_REACHED(); break; diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index b81f252a23..940aefabd6 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -29,6 +29,7 @@ #include "waypoint_base.h" #include "depot_base.h" #include "error.h" +#include "cargotype.h" #include "table/sprites.h" extern uint ConvertSpeedToDisplaySpeed(uint speed); @@ -177,6 +178,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI STR_TRACE_RESTRICT_VARIABLE_CURRENT_ORDER, STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER, STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION, + STR_TRACE_RESTRICT_VARIABLE_CARGO, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; @@ -186,6 +188,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI TRIT_COND_CURRENT_ORDER, TRIT_COND_NEXT_ORDER, TRIT_COND_LAST_STATION, + TRIT_COND_CARGO, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { @@ -195,12 +198,49 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI return IsTraceRestrictTypeConditional(type) ? &set_cond : &set_action; } +static const TraceRestrictDropDownListSet *GetSortedCargoTypeDropDownListSet() +{ + static StringID cargo_list_str[NUM_CARGO + 1]; + static uint cargo_list_id[NUM_CARGO]; + static const TraceRestrictDropDownListSet cargo_list = { + cargo_list_str, cargo_list_id, + }; + + for (size_t i = 0; i < _sorted_standard_cargo_specs_size; ++i) { + const CargoSpec *cs = _sorted_cargo_specs[i]; + cargo_list_str[i] = cs->name; + cargo_list_id[i] = cs->Index(); + } + cargo_list_str[_sorted_standard_cargo_specs_size] = INVALID_STRING_ID; + + return &cargo_list; +} + +static const StringID _cargo_cond_ops_str[] = { + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_EQUALS, + STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_NOT_EQUALS, + INVALID_STRING_ID, +}; +static const uint _cargo_cond_ops_val[] = { + TRCO_IS, + TRCO_ISNOT, +}; +static const TraceRestrictDropDownListSet _cargo_cond_ops = { + _cargo_cond_ops_str, _cargo_cond_ops_val, +}; + +static StringID GetCargoStringByID(CargoID cargo) +{ + const CargoSpec *cs = CargoSpec::Get(cargo); + return cs->IsValid() ? cs->name : STR_NEWGRF_INVALID_CARGO; +} + static StringID GetTypeString(TraceRestrictItemType type) { return GetDropDownStringByValue(GetTypeDropDownListSet(type), type); } -static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestrictConditionOpType type) +static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestrictTypePropertySet properties) { static const StringID str_long[] = { STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS, @@ -236,7 +276,9 @@ static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestric str_short, val_short, }; - switch (type) { + if (properties.value_type == TRVT_CARGO_ID) return &_cargo_cond_ops; + + switch (properties.cond_type) { case TRCOT_NONE: return NULL; @@ -315,7 +357,7 @@ static void DrawInstructionStringConditionalCommon(TraceRestrictItem item, const assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, GetTypeString(GetTraceRestrictType(item))); - SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties.cond_type), GetTraceRestrictCondOp(item))); + SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); } static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) @@ -395,6 +437,14 @@ static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, break; } + case TRVT_CARGO_ID: + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_CARGO; + assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(1, GetDropDownStringByValue(&_cargo_cond_ops, GetTraceRestrictCondOp(item))); + SetDParam(2, GetCargoStringByID(GetTraceRestrictValue(item))); + break; + default: NOT_REACHED(); break; @@ -582,7 +632,7 @@ public: case TR_WIDGET_COMPARATOR: { TraceRestrictItem item = this->GetSelected(); - const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(GetTraceRestrictTypeProperties(item).cond_type); + const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(GetTraceRestrictTypeProperties(item)); if (list_set) { this->ShowDropDownListWithValue(list_set, GetTraceRestrictCondOp(item), false, TR_WIDGET_COMPARATOR, 0, 0, 0); } @@ -601,8 +651,17 @@ public: case TR_WIDGET_VALUE_DROPDOWN: { TraceRestrictItem item = this->GetSelected(); - if (GetTraceRestrictTypeProperties(item).value_type == TRVT_DENY) { - this->ShowDropDownListWithValue(&_deny_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); + switch (GetTraceRestrictTypeProperties(item).value_type) { + case TRVT_DENY: + this->ShowDropDownListWithValue(&_deny_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); + break; + + case TRVT_CARGO_ID: + this->ShowDropDownListWithValue(GetSortedCargoTypeDropDownListSet(), GetTraceRestrictValue(item), true, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); // current cargo is permitted to not be in list + break; + + default: + break; } break; } @@ -1185,7 +1244,7 @@ private: middle_sel->SetDisplayedPlane(DPM_COMPARATOR); this->EnableWidget(TR_WIDGET_COMPARATOR); - const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(properties.cond_type); + const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(properties); if (list_set) { this->GetWidget(TR_WIDGET_COMPARATOR)->widget_data = @@ -1210,6 +1269,13 @@ private: this->EnableWidget(TR_WIDGET_VALUE_DEST); break; + case TRVT_CARGO_ID: + right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); + this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); + this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = + GetCargoStringByID(GetTraceRestrictValue(item)); + break; + default: break; } From 3dd51e96b829409d37ab5b855d807e79f431c805 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 27 Jul 2015 18:55:57 +0100 Subject: [PATCH 09/15] Bulk documentation update, and a few style fixes. --- src/rail_map.h | 20 +++- src/saveload/tracerestrict_sl.cpp | 23 +++- src/tracerestrict.cpp | 104 ++++++++++++++---- src/tracerestrict.h | 175 ++++++++++++++++++++--------- src/tracerestrict_gui.cpp | 177 +++++++++++++++++++++++------- 5 files changed, 382 insertions(+), 117 deletions(-) diff --git a/src/rail_map.h b/src/rail_map.h index a42c84f784..e6f4d7e406 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -479,16 +479,24 @@ static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td) !HasSignalOnTrackdir(tile, td) && IsOnewaySignal(tile, TrackdirToTrack(td)); } -static inline bool IsRestrictedSignal(TileIndex t) +/** + * 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(t) == RAIL_TILE_SIGNALS); - return (bool) GB(_m[t].m2, 12, 1); + assert(GetRailTileType(tile) == RAIL_TILE_SIGNALS); + return (bool) GB(_m[tile].m2, 12, 1); } -static inline void SetRestrictedSignal(TileIndex t, bool is_restricted) +/** + * 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(t) == RAIL_TILE_SIGNALS); - SB(_m[t].m2, 12, 1, is_restricted); + assert(GetRailTileType(tile) == RAIL_TILE_SIGNALS); + SB(_m[tile].m2, 12, 1, is_restricted); } diff --git a/src/saveload/tracerestrict_sl.cpp b/src/saveload/tracerestrict_sl.cpp index a646c3a72a..93d43cf3ea 100644 --- a/src/saveload/tracerestrict_sl.cpp +++ b/src/saveload/tracerestrict_sl.cpp @@ -20,6 +20,9 @@ static const SaveLoad _trace_restrict_mapping_desc[] = { SLE_END() }; +/** + * Load mappings + */ static void Load_TRRM() { int index; @@ -29,6 +32,9 @@ static void Load_TRRM() } } +/** + * Save mappings + */ static void Save_TRRM() { for (TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.begin(); @@ -38,6 +44,7 @@ static void Save_TRRM() } } +/** program length save header struct */ struct TraceRestrictProgramStub { uint32 length; }; @@ -47,6 +54,9 @@ static const SaveLoad _trace_restrict_program_stub_desc[] = { SLE_END() }; +/** + * Load program pool + */ static void Load_TRRP() { int index; @@ -60,6 +70,9 @@ static void Load_TRRP() } } +/** + * Save a program, used by SlAutolength + */ static void RealSave_TRRP(TraceRestrictProgram *prog) { TraceRestrictProgramStub stub; @@ -68,6 +81,9 @@ static void RealSave_TRRP(TraceRestrictProgram *prog) SlArray(&(prog->items[0]), stub.length, SLE_UINT32); } +/** + * Save program pool + */ static void Save_TRRP() { TraceRestrictProgram *prog; @@ -78,6 +94,9 @@ static void Save_TRRP() } } +/** + * Update program reference counts from just-loaded mapping + */ void AfterLoadTraceRestrict() { for (TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.begin(); @@ -87,6 +106,6 @@ void AfterLoadTraceRestrict() } extern const ChunkHandler _trace_restrict_chunk_handlers[] = { - { 'TRRM', Save_TRRM, Load_TRRM, NULL, NULL, CH_SPARSE_ARRAY}, - { 'TRRP', Save_TRRP, Load_TRRP, NULL, NULL, CH_ARRAY | CH_LAST}, + { '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/tracerestrict.cpp b/src/tracerestrict.cpp index 6bd9c8e7c5..ccfee30c39 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -5,7 +5,7 @@ * 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.h Header file for Trace Restriction. */ +/** @file tracerestrict.cpp Main file for Trace Restrict */ #include "stdafx.h" #include "tracerestrict.h" @@ -21,7 +21,9 @@ #include "pathfinder/yapf/yapf_cache.h" #include -/** Trace Restrict Data Storage Model Notes: +/** @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. @@ -54,7 +56,6 @@ * to be gained by doing so. */ -/** Initialize the program pool */ TraceRestrictProgramPool _tracerestrictprogram_pool("TraceRestrictProgram"); INSTANTIATE_POOL_METHODS(TraceRestrictProgram) @@ -65,12 +66,20 @@ INSTANTIATE_POOL_METHODS(TraceRestrictProgram) */ 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 +/** + * 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 @@ -79,6 +88,9 @@ enum TraceRestrictCondStackFlags { }; 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) { @@ -111,7 +123,10 @@ static void HandleCondition(std::vector &condstack, } } -/// Test value op condvalue +/** + * Integer condition testing + * Test value op condvalue + */ static bool TestCondition(uint16 value, TraceRestrictCondOp condop, uint16 condvalue) { switch (condop) { @@ -133,6 +148,9 @@ static bool TestCondition(uint16 value, TraceRestrictCondOp condop, uint16 condv } } +/** + * Binary condition testing helper function + */ static bool TestBinaryConditionCommon(TraceRestrictItem item, bool input) { switch (GetTraceRestrictCondOp(item)) { @@ -148,8 +166,10 @@ static bool TestBinaryConditionCommon(TraceRestrictItem item, bool input) } } -/// Test order condition -/// order may be NULL +/** + * Test order condition + * @p order may be NULL + */ static bool TestOrderCondition(const Order *order, TraceRestrictItem item) { bool result = false; @@ -176,7 +196,9 @@ static bool TestOrderCondition(const Order *order, TraceRestrictItem item) return TestBinaryConditionCommon(item, result); } -/// Test station condition +/** + * Test station condition + */ static bool TestStationCondition(StationID station, TraceRestrictItem item) { bool result = (GetTraceRestrictAuxField(item) == TROCAF_STATION) && (station == GetTraceRestrictValue(item)); @@ -184,7 +206,11 @@ static bool TestStationCondition(StationID station, TraceRestrictItem item) } -/// Execute program on train and store results in out +/** + * 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, TraceRestrictProgramResult& out) const { // static to avoid needing to re-alloc/resize on each execution @@ -288,6 +314,9 @@ void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& o assert(condstack.empty()); } +/** + * Decrement ref count, only use when removing a mapping + */ void TraceRestrictProgram::DecrementRefCount() { assert(this->refcount > 0); this->refcount--; @@ -296,10 +325,13 @@ void TraceRestrictProgram::DecrementRefCount() { } } -/// returns successful result if program seems OK -/// This only validates that conditional nesting is correct, at present +/** + * 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 to avoid needing to re-alloc/resize on each execution static std::vector condstack; condstack.clear(); @@ -345,6 +377,9 @@ CommandCost TraceRestrictProgram::Validate(const std::vector 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) { @@ -373,9 +408,9 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp } } -/// Set the type field of a TraceRestrictItem, and -/// reset any other fields which are no longer valid/meaningful -/// to sensible defaults +/** + * 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) { @@ -395,6 +430,10 @@ void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItem } } +/** + * 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 @@ -407,6 +446,10 @@ void SetIsSignalRestrictedBit(TileIndex t) SetRestrictedSignal(t, lower_bound != upper_bound); } +/** + * Create a new program mapping to an existing program + * If a mapping already exists, it is removed + */ void TraceRestrictCreateProgramMapping(TraceRestrictRefId ref, TraceRestrictProgram *prog) { std::pair insert_result = @@ -427,6 +470,9 @@ void TraceRestrictCreateProgramMapping(TraceRestrictRefId ref, TraceRestrictProg YapfNotifyTrackLayoutChange(tile, track); } +/** + * Remove a program mapping + */ void TraceRestrictRemoveProgramMapping(TraceRestrictRefId ref) { TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.find(ref); @@ -443,9 +489,10 @@ void TraceRestrictRemoveProgramMapping(TraceRestrictRefId ref) } } -/// Gets the trace restrict program for the tile/track ref ID identified by @p ref. -/// An empty program will be constructed if none exists, and @p create_new is true -/// unless the pool is full +/** + * Gets the signal program for the tile ref @p ref + * An empty program will be constructed if none exists, and @p create_new is true, unless the pool is full + */ TraceRestrictProgram *GetTraceRestrictProgram(TraceRestrictRefId ref, bool create_new) { // Optimise for lookup, creating doesn't have to be that fast @@ -471,8 +518,10 @@ TraceRestrictProgram *GetTraceRestrictProgram(TraceRestrictRefId ref, bool creat } } -/// Notify that a signal is being removed -/// Remove any trace restrict items associated with it +/** + * Notify that a signal is being removed + * Remove any trace restrict mappings associated with it + */ void TraceRestrictNotifySignalRemoval(TileIndex tile, Track track) { TraceRestrictRefId ref = MakeTraceRestrictRefId(tile, track); @@ -480,6 +529,9 @@ void TraceRestrictNotifySignalRemoval(TileIndex tile, Track track) DeleteWindowById(WC_TRACE_RESTRICT, ref); } +/** + * Helper function to perform parameter bit-packing and call DoCommandP, for instruction modification actions + */ void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg) { uint32 p1 = 0; @@ -490,6 +542,9 @@ void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommand DoCommandP(tile, p1, value, CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG(error_msg)); } +/** + * Check whether a tile/tracl pair contains a usable signal + */ static CommandCost TraceRestrictCheckTileIsUsable(TileIndex tile, Track track) { // Check that there actually is a signal here @@ -662,6 +717,9 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u return CommandCost(); } +/** + * Helper function to perform parameter bit-packing and call DoCommandP, for program management actions + */ void TraceRestrictProgMgmtWithSourceDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, TileIndex source_tile, Track source_track, StringID error_msg) { @@ -779,6 +837,10 @@ CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag return CommandCost(); } +/** + * This is called when a station, waypoint or depot is about to be deleted + * Scan program pool and change any references to it to the invalid station ID, to avoid dangling references + */ void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index) { TraceRestrictProgram *prog; diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 424daa9fc8..91148facc7 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -5,7 +5,7 @@ * 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.h Header file for Trace Restriction. */ +/** @file tracerestrict.h Header file for Trace Restrict */ #ifndef TRACERESTRICT_H #define TRACERESTRICT_H @@ -22,10 +22,11 @@ struct Train; -/** Unique identifiers for a trace restrict nodes. */ +/** Program pool ID type. */ typedef uint32 TraceRestrictProgramID; struct TraceRestrictProgram; +/** Tile/track mapping type. */ typedef uint32 TraceRestrictRefId; /** Type of the pool for trace restrict programs. */ @@ -36,6 +37,7 @@ extern TraceRestrictProgramPool _tracerestrictprogram_pool; #define FOR_ALL_TRACE_RESTRICT_PROGRAMS_FROM(var, start) FOR_ALL_ITEMS_FROM(TraceRestrictProgram, tr_index, var, start) #define FOR_ALL_TRACE_RESTRICT_PROGRAMS(var) FOR_ALL_TRACE_RESTRICT_PROGRAMS_FROM(var, 0) +/** Type used for the TraceRestrictRefId -> TraceRestrictProgramID mapping */ struct TraceRestrictMappingItem { TraceRestrictProgramID program_id; @@ -46,13 +48,24 @@ struct TraceRestrictMappingItem { }; typedef std::map TraceRestrictMapping; + +/** The actual mapping from TraceRestrictRefId to TraceRestrictProgramID. */ extern TraceRestrictMapping _tracerestrictprogram_mapping; void ClearTraceRestrictMapping(); -/// Of the fields below, the type and cond flags seem the most likely -/// to need future expansion, hence the reserved bits are placed -/// immediately after them +/** Type of a single instruction, this is bit-packed as per TraceRestrictItemFlagAllocation */ +typedef uint32 TraceRestrictItem; + +/** + * Describes the allocation of bits to fields in TraceRestrictItem + * Of the fields below, the type seem the most likely + * to need future expansion, hence the reserved bits are placed + * immediately after them + * + * COUNT values describe the field bit width + * OFFSET values describe the field bit offset + */ enum TraceRestrictItemFlagAllocation { TRIFA_TYPE_COUNT = 5, TRIFA_TYPE_OFFSET = 0, @@ -72,12 +85,18 @@ enum TraceRestrictItemFlagAllocation { TRIFA_VALUE_OFFSET = 16, }; +/** + * Enumeration of TraceRestrictItem type field + * This is split into two halves: + * * non-conditionals < TRIT_COND_BEGIN + * * conditionals, >= TRIT_COND_BEGIN + */ enum TraceRestrictItemType { - TRIT_NULL = 0, - TRIT_PF_DENY = 1, - TRIT_PF_PENALTY = 2, + TRIT_NULL = 0, ///< Null-type, not in programs and not valid for execution, mainly used with TraceRestrictNullTypeSpecialValue for start/end + TRIT_PF_DENY = 1, ///< Pathfinder deny/allow + TRIT_PF_PENALTY = 2, ///< Add to pathfinder penalty - TRIT_COND_BEGIN = 8, ///< Start of conditional item types + TRIT_COND_BEGIN = 8, ///< Start of conditional item types, note that this has the save value as TRIT_COND_ENDIF TRIT_COND_ENDIF = 8, ///< This is an endif block or an else block TRIT_COND_UNDEFINED = 9, ///< This condition has no type defined (evaluate as false) TRIT_COND_TRAIN_LENGTH = 10, ///< Test train length @@ -89,53 +108,72 @@ enum TraceRestrictItemType { /* space up to 31 */ }; -/* no flags set indicates end if for TRIT_COND_ENDIF, if otherwise */ +/** + * TraceRestrictItem condition flags field, only valid with conditional types (IsTraceRestrictTypeConditional() is true) + */ enum TraceRestrictCondFlags { - TRCF_DEFAULT = 0, - TRCF_ELSE = 1 << 0, - TRCF_OR = 1 << 1, + TRCF_DEFAULT = 0, ///< indicates end if for type: TRIT_COND_ENDIF, if otherwise + TRCF_ELSE = 1 << 0, ///< indicates an else block for type: TRIT_COND_ENDIF, elif otherwise + TRCF_OR = 1 << 1, ///< indicates an orif block, not valid with type: TRIT_COND_ENDIF /* 1 bit spare */ }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictCondFlags) -enum TraceRestictNullTypeSpecialValue { - TRNTSV_NULL = 0, - TRNTSV_START = 1, - TRNTSV_END = 2, +/** + * Enumeration of TraceRestrictItemvalue type field when type is TRIT_NULL + */ +enum TraceRestrictNullTypeSpecialValue { + TRNTSV_NULL = 0, ///< null, what you get when you zero-init a TraceRestrictItemvalue + TRNTSV_START = 1, ///< start tag, generated within GUI + TRNTSV_END = 2, ///< end tag, generated within GUI }; +/** + * TraceRestrictItem condition operator field, only valid with conditional types (IsTraceRestrictTypeConditional() is true) + */ enum TraceRestrictCondOp { - TRCO_IS = 0, - TRCO_ISNOT = 1, - TRCO_LT = 2, - TRCO_LTE = 3, - TRCO_GT = 4, - TRCO_GTE = 5, + TRCO_IS = 0, ///< equality test, or can carry test for cargo + TRCO_ISNOT = 1, ///< inequality test, or can't carry test for cargo + TRCO_LT = 2, ///< less than test + TRCO_LTE = 3, ///< less than or equal test + TRCO_GT = 4, ///< greater than test + TRCO_GTE = 5, ///< greater than or equal test /* space up to 7 */ }; +/** + * TraceRestrictItem auxiliary type field, for order type conditionals + */ enum TraceRestrictOrderCondAuxField { - TROCAF_STATION = 0, - TROCAF_WAYPOINT = 1, - TROCAF_DEPOT = 2, - /* space up to 7 */ + TROCAF_STATION = 0, ///< value field is a station StationID + TROCAF_WAYPOINT = 1, ///< value field is a waypoint StationID + TROCAF_DEPOT = 2, ///< value field is a depot DepotID + /* space up to 3 */ }; +/** + * Enumeration for TraceRestrictProgramResult::flags + */ enum TraceRestrictProgramResultFlags { - TRPRF_DENY = 1 << 0, + TRPRF_DENY = 1 << 0, ///< Pathfinder deny is set }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags) +/** + * Execution result of a TraceRestrictProgram + */ struct TraceRestrictProgramResult { - uint32 penalty; - TraceRestrictProgramResultFlags flags; + uint32 penalty; ///< Total additional pathfinder penalty + TraceRestrictProgramResultFlags flags; ///< Flags of other actions to take TraceRestrictProgramResult() : penalty(0), flags(static_cast(0)) { } }; -typedef uint32 TraceRestrictItem; - +/** + * Program type, this stores the instruction list + * This is refcounted, see info at top of tracerestrict.cpp + */ struct TraceRestrictProgram : TraceRestrictProgramPool::PoolItem<&_tracerestrictprogram_pool> { std::vector items; uint32 refcount; @@ -145,99 +183,131 @@ struct TraceRestrictProgram : TraceRestrictProgramPool::PoolItem<&_tracerestrict void Execute(const Train *v, TraceRestrictProgramResult &out) const; + /** + * Increment ref count, only use when creating a mapping + */ void IncrementRefCount() { refcount++; } void DecrementRefCount(); static CommandCost Validate(const std::vector &items); + /** + * Call validation function on current program instruction list + */ CommandCost Validate() const { return TraceRestrictProgram::Validate(items); } }; +/** Get TraceRestrictItem type field */ static inline TraceRestrictItemType GetTraceRestrictType(TraceRestrictItem item) { return static_cast(GB(item, TRIFA_TYPE_OFFSET, TRIFA_TYPE_COUNT)); } +/** Get TraceRestrictItem condition flags field */ static inline TraceRestrictCondFlags GetTraceRestrictCondFlags(TraceRestrictItem item) { return static_cast(GB(item, TRIFA_COND_FLAGS_OFFSET, TRIFA_COND_FLAGS_COUNT)); } +/** Get TraceRestrictItem condition operator field */ static inline TraceRestrictCondOp GetTraceRestrictCondOp(TraceRestrictItem item) { return static_cast(GB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT)); } +/** Get TraceRestrictItem auxiliary field */ static inline uint8 GetTraceRestrictAuxField(TraceRestrictItem item) { return GB(item, TRIFA_AUX_FIELD_OFFSET, TRIFA_AUX_FIELD_COUNT); } +/** Get TraceRestrictItem value field */ static inline uint16 GetTraceRestrictValue(TraceRestrictItem item) { return static_cast(GB(item, TRIFA_VALUE_OFFSET, TRIFA_VALUE_COUNT)); } +/** Set TraceRestrictItem type field */ static inline void SetTraceRestrictType(TraceRestrictItem &item, TraceRestrictItemType type) { SB(item, TRIFA_TYPE_OFFSET, TRIFA_TYPE_COUNT, type); } +/** Set TraceRestrictItem condition operator field */ static inline void SetTraceRestrictCondOp(TraceRestrictItem &item, TraceRestrictCondOp condop) { SB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT, condop); } +/** Set TraceRestrictItem condition flags field */ static inline void SetTraceRestrictCondFlags(TraceRestrictItem &item, TraceRestrictCondFlags condflags) { SB(item, TRIFA_COND_FLAGS_OFFSET, TRIFA_COND_FLAGS_COUNT, condflags); } +/** Set TraceRestrictItem auxiliary field */ static inline void SetTraceRestrictAuxField(TraceRestrictItem &item, uint8 data) { SB(item, TRIFA_AUX_FIELD_OFFSET, TRIFA_AUX_FIELD_COUNT, data); } +/** Set TraceRestrictItem value field */ static inline void SetTraceRestrictValue(TraceRestrictItem &item, uint16 value) { SB(item, TRIFA_VALUE_OFFSET, TRIFA_VALUE_COUNT, value); } +/** Is TraceRestrictItemType a conditional type? */ static inline bool IsTraceRestrictTypeConditional(TraceRestrictItemType type) { return type >= TRIT_COND_BEGIN; } +/** Is TraceRestrictItem type field a conditional type? */ static inline bool IsTraceRestrictConditional(TraceRestrictItem item) { return IsTraceRestrictTypeConditional(GetTraceRestrictType(item)); } +/** + * Categorisation of what is allowed in the TraceRestrictItem condition op field + * see TraceRestrictTypePropertySet + */ enum TraceRestrictConditionOpType { TRCOT_NONE = 0, ///< takes no condition op TRCOT_BINARY = 1, ///< takes "is" and "is not" condition ops TRCOT_ALL = 2, ///< takes all condition ops (i.e. all relational ops) }; +/** + * Categorisation of what is in the TraceRestrictItem value field + * see TraceRestrictTypePropertySet + */ enum TraceRestrictValueType { TRVT_NONE = 0, ///< value field not used (set to 0) TRVT_SPECIAL = 1, ///< special handling of value field - TRVT_INT = 2, ///< takes an integer value + TRVT_INT = 2, ///< takes an unsigned integer value TRVT_DENY = 3, ///< takes a value 0 = deny, 1 = allow (cancel previous deny) TRVT_SPEED = 4, ///< takes an integer speed value TRVT_ORDER = 5, ///< takes an order target ID, as per the auxiliary field as type: TraceRestrictOrderCondAuxField TRVT_CARGO_ID = 6, ///< takes a CargoID }; -void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueType value_type); -void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItemType type); - +/** + * Describes formats of TraceRestrictItem condition op and value fields + */ struct TraceRestrictTypePropertySet { TraceRestrictConditionOpType cond_type; TraceRestrictValueType value_type; }; +void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueType value_type); +void SetTraceRestrictTypeAndNormalise(TraceRestrictItem &item, TraceRestrictItemType type); + +/** + * Get TraceRestrictTypePropertySet for a given instruction, only looks at value field + */ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceRestrictItem item) { TraceRestrictTypePropertySet out; @@ -291,16 +361,19 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR return out; } +/** Get mapping ref ID from tile and track */ static inline TraceRestrictRefId MakeTraceRestrictRefId(TileIndex t, Track track) { return (t << 3) | track; } +/** Get tile from mapping ref ID */ static inline TileIndex GetTraceRestrictRefIdTileIndex(TraceRestrictRefId ref) { return static_cast(ref >> 3); } +/** Get track from mapping ref ID */ static inline Track GetTraceRestrictRefIdTrack(TraceRestrictRefId ref) { return static_cast(ref & 7); @@ -309,16 +382,13 @@ static inline Track GetTraceRestrictRefIdTrack(TraceRestrictRefId ref) void TraceRestrictCreateProgramMapping(TraceRestrictRefId ref, TraceRestrictProgram *prog); void TraceRestrictRemoveProgramMapping(TraceRestrictRefId ref); -/// Gets the signal program for the tile identified by @p t and @p track. -/// An empty program will be constructed if none exists, and create_new is true -/// unless the pool is full TraceRestrictProgram *GetTraceRestrictProgram(TraceRestrictRefId ref, bool create_new); -/// Notify that a signal is being removed -/// Remove any trace restrict items associated with it void TraceRestrictNotifySignalRemoval(TileIndex tile, Track track); -/// Gets the signal program for the tile identified by @p t and @p track, or NULL +/** + * Gets the existing signal program for the tile identified by @p t and @p track, or NULL + */ static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIndex t, Track track) { if (IsRestrictedSignal(t)) { @@ -328,16 +398,18 @@ static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIn } } -// do not re-order +/** + * Enumeration for command action type field, indicates what command to do + */ enum TraceRestrictDoCommandType { - TRDCT_INSERT_ITEM = 0, - TRDCT_MODIFY_ITEM = 1, - TRDCT_REMOVE_ITEM = 2, + TRDCT_INSERT_ITEM = 0, ///< insert new instruction before offset field as given value + TRDCT_MODIFY_ITEM = 1, ///< modify instruction at offset field to given value + TRDCT_REMOVE_ITEM = 2, ///< remove instruction at offset field - TRDCT_PROG_COPY = 3, - TRDCT_PROG_SHARE = 4, - TRDCT_PROG_UNSHARE = 5, - TRDCT_PROG_RESET = 6, + TRDCT_PROG_COPY = 3, ///< copy program operation. Do not re-order this with respect to other values + TRDCT_PROG_SHARE = 4, ///< share program operation + TRDCT_PROG_UNSHARE = 5, ///< unshare program (copy as a new program) + TRDCT_PROG_RESET = 6, ///< reset program state of signal }; void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg); @@ -345,6 +417,9 @@ void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommand void TraceRestrictProgMgmtWithSourceDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, TileIndex source_tile, Track source_track, StringID error_msg); +/** + * Short-hand to call TraceRestrictProgMgmtWithSourceDoCommandP with 0 for source tile/track + */ inline void TraceRestrictProgMgmtDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, StringID error_msg) { TraceRestrictProgMgmtWithSourceDoCommandP(tile, track, type, static_cast(0), static_cast(0), error_msg); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 940aefabd6..1255b28a1d 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -7,7 +7,10 @@ * 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_gui.cpp GUI related to signal tracerestrict */ +/** @file tracerestrict_gui.cpp GUI code for Trace Restrict + * + * This is largely based on the programmable signals patch's GUI + * */ #include "stdafx.h" #include "tracerestrict.h" @@ -35,6 +38,7 @@ extern uint ConvertSpeedToDisplaySpeed(uint speed); extern uint ConvertDisplaySpeedToSpeed(uint speed); +/** Widget IDs */ enum TraceRestrictWindowWidgets { TR_WIDGET_CAPTION, TR_WIDGET_INSTRUCTION_LIST, @@ -68,6 +72,7 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_UNSHARE, }; +/** Selection mappings for NWID_SELECTION selectors */ enum PanelWidgets { // Left 2 DPL2_TYPE = 0, @@ -93,9 +98,13 @@ enum PanelWidgets { DPS_UNSHARE, }; -/// value_array *must* be at least as long as string_array, -/// where the length of string_array is defined as the offset -/// of the first INVALID_STRING_ID +/** + * drop down list string array, and corresponding integer values + * + * value_array *must* be at least as long as string_array, + * where the length of string_array is defined as the offset + * of the first INVALID_STRING_ID + */ struct TraceRestrictDropDownListSet { const StringID *string_array; const uint *value_array; @@ -109,17 +118,18 @@ static const StringID _program_insert_str[] = { STR_TRACE_RESTRICT_PF_PENALTY, INVALID_STRING_ID }; -static const uint _program_insert_else_flag = 0x100; -static const uint32 _program_insert_else_hide_mask = 4; -static const uint32 _program_insert_else_if_hide_mask = 2; +static const uint _program_insert_else_flag = 0x100; ///< flag to indicate that TRCF_ELSE should be set +static const uint32 _program_insert_else_hide_mask = 4; ///< disable bitmask for else +static const uint32 _program_insert_else_if_hide_mask = 2; ///< disable bitmask for elif static const uint _program_insert_val[] = { - TRIT_COND_UNDEFINED, /// if block - TRIT_COND_UNDEFINED | _program_insert_else_flag, /// elif block - TRIT_COND_ENDIF | _program_insert_else_flag, /// else block - TRIT_PF_DENY, /// deny - TRIT_PF_PENALTY, /// penalty + TRIT_COND_UNDEFINED, // if block + TRIT_COND_UNDEFINED | _program_insert_else_flag, // elif block + TRIT_COND_ENDIF | _program_insert_else_flag, // else block + TRIT_PF_DENY, // deny + TRIT_PF_PENALTY, // penalty }; +/** insert drop down list strings and values */ static const TraceRestrictDropDownListSet _program_insert = { _program_insert_str, _program_insert_val, }; @@ -134,10 +144,15 @@ static const uint _deny_value_val[] = { 1, }; +/** value drop down list for deny types strings and values */ static const TraceRestrictDropDownListSet _deny_value = { _deny_value_str, _deny_value_val, }; +/** + * Get index of @p value in @p list_set + * if @p value is not present, assert if @p missing_ok is false, otherwise return -1 + */ static int GetDropDownListIndexByValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok) { const StringID *string_array = list_set->string_array; @@ -152,11 +167,18 @@ static int GetDropDownListIndexByValue(const TraceRestrictDropDownListSet *list_ return -1; } +/** + * Get StringID correspoding to @p value, in @list_set + * @p value must be present + */ static StringID GetDropDownStringByValue(const TraceRestrictDropDownListSet *list_set, uint value) { return list_set->string_array[GetDropDownListIndexByValue(list_set, value, false)]; } +/** + * Return the appropriate type dropdown TraceRestrictDropDownListSet for the given item type @p type + */ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictItemType type) { static const StringID str_action[] = { @@ -198,6 +220,9 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI return IsTraceRestrictTypeConditional(type) ? &set_cond : &set_action; } +/** + * Get a TraceRestrictDropDownListSet of the sorted cargo list + */ static const TraceRestrictDropDownListSet *GetSortedCargoTypeDropDownListSet() { static StringID cargo_list_str[NUM_CARGO + 1]; @@ -225,21 +250,31 @@ static const uint _cargo_cond_ops_val[] = { TRCO_IS, TRCO_ISNOT, }; +/** cargo conditional operators dropdown list set */ static const TraceRestrictDropDownListSet _cargo_cond_ops = { _cargo_cond_ops_str, _cargo_cond_ops_val, }; +/** + * Get the StringID for a given CargoID @p cargo, or STR_NEWGRF_INVALID_CARGO + */ static StringID GetCargoStringByID(CargoID cargo) { const CargoSpec *cs = CargoSpec::Get(cargo); return cs->IsValid() ? cs->name : STR_NEWGRF_INVALID_CARGO; } +/** + * Get the StringID for a given item type @p type + */ static StringID GetTypeString(TraceRestrictItemType type) { return GetDropDownStringByValue(GetTypeDropDownListSet(type), type); } +/** + * Get the conditional operator field drop down list set for a given type property set @p properties + */ static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestrictTypePropertySet properties) { static const StringID str_long[] = { @@ -292,6 +327,9 @@ static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestric return NULL; } +/** + * Return true if item type field @p type is an integer value type + */ static bool IsIntegerValueType(TraceRestrictValueType type) { switch (type) { @@ -304,6 +342,9 @@ static bool IsIntegerValueType(TraceRestrictValueType type) } } +/** + * Convert integer values between internal units and display units + */ static uint ConvertIntegerValue(TraceRestrictValueType type, uint in, bool to_display) { switch (type) { @@ -321,37 +362,40 @@ static uint ConvertIntegerValue(TraceRestrictValueType type, uint in, bool to_di } } +/** String values for TraceRestrictCondFlags, value gives offset into array */ static const StringID _program_cond_type[] = { - /* 0 */ STR_TRACE_RESTRICT_CONDITIONAL_IF, - /* TRCF_ELSE */ STR_TRACE_RESTRICT_CONDITIONAL_ELIF, - /* TRCF_OR */ STR_TRACE_RESTRICT_CONDITIONAL_ORIF, + STR_TRACE_RESTRICT_CONDITIONAL_IF, // TRCF_DEFAULT + STR_TRACE_RESTRICT_CONDITIONAL_ELIF, // TRCF_ELSE + STR_TRACE_RESTRICT_CONDITIONAL_ORIF, // TRCF_OR }; +/** condition flags field drop down value types */ enum CondFlagsDropDownType { CFDDT_ELSE = 0, ///< This is an else block CFDDT_ELIF = TRCF_ELSE, ///< This is an else-if block CFDDT_ORIF = TRCF_OR, ///< This is an or-if block }; -static const uint32 _condflags_dropdown_else_hide_mask = 1; -static const uint32 _condflags_dropdown_else_if_hide_mask = 6; +static const uint32 _condflags_dropdown_else_hide_mask = 1; ///< disable bitmask for CFDDT_ELSE +static const uint32 _condflags_dropdown_else_if_hide_mask = 6; ///< disable bitmask for CFDDT_ELIF and CFDDT_ORIF + static const StringID _condflags_dropdown_str[] = { - /* CFDDT_ELSE */ STR_TRACE_RESTRICT_CONDITIONAL_ELSE, - /* CFDDT_ELIF */ STR_TRACE_RESTRICT_CONDITIONAL_ELIF, - /* CFDDT_ORIF */ STR_TRACE_RESTRICT_CONDITIONAL_ORIF, + STR_TRACE_RESTRICT_CONDITIONAL_ELSE, + STR_TRACE_RESTRICT_CONDITIONAL_ELIF, + STR_TRACE_RESTRICT_CONDITIONAL_ORIF, INVALID_STRING_ID, }; - static const uint _condflags_dropdown_val[] = { CFDDT_ELSE, CFDDT_ELIF, CFDDT_ORIF, }; - +/** condition flags dropdown list set */ static const TraceRestrictDropDownListSet _condflags_dropdown = { _condflags_dropdown_str, _condflags_dropdown_val, }; +/** Common function for drawing an ordinary conditional instruction */ static void DrawInstructionStringConditionalCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) { assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); @@ -360,6 +404,7 @@ static void DrawInstructionStringConditionalCommon(TraceRestrictItem item, const SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); } +/** Common function for drawing an integer conditional instruction */ static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) { DrawInstructionStringConditionalCommon(item, properties); @@ -486,14 +531,15 @@ static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, DrawString(left + indent * 16, right, y, instruction_string, selected ? TC_WHITE : TC_BLACK); } +/** Main GUI window class */ class TraceRestrictWindow: public Window { - TileIndex tile; - Track track; - int selected_instruction; // NB: this is offset by one due to the display of the "start" item - Scrollbar *vscroll; - std::map drop_down_list_mapping; - TraceRestrictItem expecting_inserted_item; - int current_placement_widget; + TileIndex tile; ///< tile this window is for + Track track; ///< track this window is for + int selected_instruction; ///< selected instruction index, this is offset by one due to the display of the "start" item + Scrollbar *vscroll; ///< scrollbar widget + std::map drop_down_list_mapping; ///< mapping of widget IDs to drop down list sets + TraceRestrictItem expecting_inserted_item; ///< set to instruction when performing an instruction insertion, used to handle selection update on insertion + int current_placement_widget; ///< which widget has a SetObjectToPlaceWnd, if any public: TraceRestrictWindow(WindowDesc *desc, TileIndex tile, Track track) @@ -516,7 +562,7 @@ public: { switch (widget) { case TR_WIDGET_INSTRUCTION_LIST: { - int sel = this->GetInstructionFromPt(pt.y); + int sel = this->GetItemIndexFromPt(pt.y); if (_ctrl_pressed) { // scroll to target (for stations, waypoints, depots) @@ -820,6 +866,9 @@ public: } } + /** + * Common OnPlaceObject handler for program management actions which involve clicking on a signal + */ void OnPlaceObjectSignal(Point pt, TileIndex source_tile, int widget, int error_message) { if (!IsPlainRailTile(source_tile)) { @@ -868,6 +917,9 @@ public: } } + /** + * Common OnPlaceObject handler for instruction value modification actions which involve selecting an order target + */ void OnPlaceObjectDestination(Point pt, TileIndex tile, int widget, int error_message) { TraceRestrictItem item = GetSelected(); @@ -967,13 +1019,13 @@ public: } } - virtual void OnInvalidateData(int data, bool gui_scope) { + virtual void OnInvalidateData(int data, bool gui_scope) + { if (gui_scope) { this->ReloadProgramme(); } } - virtual void SetStringParameters(int widget) const { switch (widget) { @@ -1000,7 +1052,10 @@ public: } private: - TraceRestrictItem MakeSpecialItem(TraceRestictNullTypeSpecialValue value) const + /** + * Helper function to make start and end instructions (these are not stored in the actual program) + */ + TraceRestrictItem MakeSpecialItem(TraceRestrictNullTypeSpecialValue value) const { TraceRestrictItem item = 0; SetTraceRestrictType(item, TRIT_NULL); @@ -1008,6 +1063,9 @@ private: return item; } + /** + * Get item count of program, including start and end markers + */ int GetItemCount(const TraceRestrictProgram *prog) const { if (prog) { @@ -1017,13 +1075,21 @@ private: } } - /// This may return NULL if no program currently exists + /** + * Get current program + * This may return NULL if no program currently exists + */ const TraceRestrictProgram *GetProgram() const { return GetTraceRestrictProgram(MakeTraceRestrictRefId(tile, track), false); } - /// prog may be NULL + /** + * Get instruction at @p index in program @p prog + * This correctly handles start/end markers, offsets, etc. + * This returns a 0 instruction if out of bounds + * @p prog may be NULL + */ TraceRestrictItem GetItem(const TraceRestrictProgram *prog, int index) const { if (index < 0) { @@ -1056,17 +1122,26 @@ private: } } + /** + * Get selected instruction, or a zero instruction + */ TraceRestrictItem GetSelected() const { return this->GetItem(this->GetProgram(), this->selected_instruction); } + /** + * Get owner of the signal tile this window is pointing at + */ Owner GetOwner() { - return GetTileOwner(tile); + return GetTileOwner(this->tile); } - int GetInstructionFromPt(int y) + /** + * Return item index from point in instruction list widget + */ + int GetItemIndexFromPt(int y) { NWidgetBase *nwid = this->GetWidget(TR_WIDGET_INSTRUCTION_LIST); int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line @@ -1078,6 +1153,9 @@ private: return (sel < this->GetItemCount(this->GetProgram()) && sel >= 0) ? sel : -1; } + /** + * Reload details of program, and adjust length/selection position as necessary + */ void ReloadProgramme() { const TraceRestrictProgram *prog = this->GetProgram(); @@ -1098,6 +1176,9 @@ private: this->UpdateButtonState(); } + /** + * Update button states, text values, etc. + */ void UpdateButtonState() { this->RaiseWidget(TR_WIDGET_INSERT); @@ -1289,6 +1370,10 @@ private: this->SetDirty(); } + /** + * Show a drop down list using @p list_set, setting the pre-selected item to the one corresponding to @p value + * This asserts if @p value is not in @p list_set, and @p missing_ok is false + */ void ShowDropDownListWithValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok, int button, uint32 disabled_mask, uint32 hidden_mask, uint width) { @@ -1297,6 +1382,9 @@ private: ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width); } + /** + * Helper function to set or unset a SetObjectToPlaceWnd, for the given widget and cursor type + */ void SetObjectToPlaceAction(int widget, CursorID cursor) { this->ToggleWidgetLoweredState(widget); @@ -1310,7 +1398,11 @@ private: } } - /// This used for testing whether else or else-if blocks could be inserted, or replace the selection + /** + * This used for testing whether else or else-if blocks could be inserted, or replace the selection + * If @p replace is true, replace selection with @p item, else insert @p item before selection + * Returns true if resulting instruction list passes validation + */ bool GenericElseInsertionDryRun(TraceRestrictItem item, bool replace) { if (this->selected_instruction < 1) return false; @@ -1332,6 +1424,9 @@ private: return TraceRestrictProgram::Validate(items).Succeeded(); } + /** + * Run GenericElseInsertionDryRun with an else instruction + */ bool ElseInsertionDryRun(bool replace) { TraceRestrictItem item = 0; @@ -1340,6 +1435,9 @@ private: return GenericElseInsertionDryRun(item, replace); } + /** + * Run GenericElseInsertionDryRun with an elif instruction + */ bool ElseIfInsertionDryRun(bool replace) { TraceRestrictItem item = 0; @@ -1430,6 +1528,9 @@ static WindowDesc _program_desc( _nested_program_widgets, lengthof(_nested_program_widgets) ); +/** + * Show or create program window for given @p tile and @p track + */ void ShowTraceRestrictProgramWindow(TileIndex tile, Track track) { if (BringWindowToFrontById(WC_TRACE_RESTRICT, MakeTraceRestrictRefId(tile, track)) != NULL) { From 5474ee3e4058882b99cd12f2f6358e7bc0a9b77b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 27 Jul 2015 21:15:43 +0100 Subject: [PATCH 10/15] Add entry direction condition variable. --- src/lang/english.txt | 9 +++++ src/pathfinder/yapf/yapf_costrail.hpp | 2 +- src/tracerestrict.cpp | 33 ++++++++++++++++++- src/tracerestrict.h | 32 +++++++++++++++++- src/tracerestrict_gui.cpp | 47 +++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 3 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 785dc754db..b390e09f5c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2390,6 +2390,7 @@ 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 @@ -2397,6 +2398,8 @@ STR_TRACE_RESTRICT_CONDITIONAL_ORDER_STATION :{STRING} {STRIN 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} @@ -2407,6 +2410,12 @@ 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 diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 81c362ae31..5a4b6531c3 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -194,7 +194,7 @@ private: { const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, TrackdirToTrack(trackdir)); if (prog) { - prog->Execute(Yapf().GetVehicle(), out); + 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; diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index ccfee30c39..968ab49eb0 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -211,7 +211,7 @@ static bool TestStationCondition(StationID station, TraceRestrictItem item) * @p v may not be NULL * @p out should be zero-initialised */ -void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& out) const +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; @@ -287,6 +287,32 @@ void TraceRestrictProgram::Execute(const Train* v, TraceRestrictProgramResult& o 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(); } @@ -402,6 +428,11 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp SetTraceRestrictAuxField(item, 0); break; + case TRVT_DIRECTION: + SetTraceRestrictValue(item, TRDTSV_FRONT); + SetTraceRestrictAuxField(item, 0); + break; + default: NOT_REACHED(); break; diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 91148facc7..4e0f0f52bb 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -105,6 +105,7 @@ enum TraceRestrictItemType { TRIT_COND_NEXT_ORDER = 13, ///< Test train next order (station, waypoint or depot) TRIT_COND_LAST_STATION = 14, ///< Test train last visited station TRIT_COND_CARGO = 15, ///< Test if train can carry cargo type + TRIT_COND_ENTRY_DIRECTION = 16, ///< Test which side of signal/signal tile is being entered from /* space up to 31 */ }; @@ -128,6 +129,18 @@ enum TraceRestrictNullTypeSpecialValue { TRNTSV_END = 2, ///< end tag, generated within GUI }; +/** + * Enumeration of TraceRestrictItemvalue type field when value type is TRVT_DIRECTION + */ +enum TraceRestrictDirectionTypeSpecialValue { + TRNTSV_NE = 0, ///< DIAGDIR_NE: entering at NE tile edge + TRNTSV_SE = 1, ///< DIAGDIR_SE: entering at SE tile edge + TRNTSV_SW = 2, ///< DIAGDIR_SW: entering at SW tile edge + TRNTSV_NW = 3, ///< DIAGDIR_NW: entering at NW tile edge + TRDTSV_FRONT = 4, ///< entering at front face of signal + TRDTSV_BACK = 5, ///< entering at rear face of signal +}; + /** * TraceRestrictItem condition operator field, only valid with conditional types (IsTraceRestrictTypeConditional() is true) */ @@ -159,6 +172,17 @@ enum TraceRestrictProgramResultFlags { }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags) +/** + * Execution input of a TraceRestrictProgram + */ +struct TraceRestrictProgramInput { + TileIndex tile; ///< Tile of restrict signal, for direction testing + Trackdir trackdir; ///< Track direction on tile of restrict signal, for direction testing + + TraceRestrictProgramInput(TileIndex tile_, Trackdir trackdir_) + : tile(tile_), trackdir(trackdir_) { } +}; + /** * Execution result of a TraceRestrictProgram */ @@ -181,7 +205,7 @@ struct TraceRestrictProgram : TraceRestrictProgramPool::PoolItem<&_tracerestrict TraceRestrictProgram() : refcount(0) { } - void Execute(const Train *v, TraceRestrictProgramResult &out) const; + void Execute(const Train *v, const TraceRestrictProgramInput &input, TraceRestrictProgramResult &out) const; /** * Increment ref count, only use when creating a mapping @@ -292,6 +316,7 @@ enum TraceRestrictValueType { TRVT_SPEED = 4, ///< takes an integer speed value TRVT_ORDER = 5, ///< takes an order target ID, as per the auxiliary field as type: TraceRestrictOrderCondAuxField TRVT_CARGO_ID = 6, ///< takes a CargoID + TRVT_DIRECTION = 7, ///< takes a TraceRestrictDirectionTypeSpecialValue }; /** @@ -343,6 +368,11 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.cond_type = TRCOT_BINARY; break; + case TRIT_COND_ENTRY_DIRECTION: + out.value_type = TRVT_DIRECTION; + out.cond_type = TRCOT_BINARY; + break; + default: NOT_REACHED(); break; diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 1255b28a1d..630c3abbd6 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -149,6 +149,29 @@ static const TraceRestrictDropDownListSet _deny_value = { _deny_value_str, _deny_value_val, }; +static const StringID _direction_value_str[] = { + STR_TRACE_RESTRICT_DIRECTION_FRONT, + STR_TRACE_RESTRICT_DIRECTION_BACK, + STR_TRACE_RESTRICT_DIRECTION_NE, + STR_TRACE_RESTRICT_DIRECTION_SE, + STR_TRACE_RESTRICT_DIRECTION_SW, + STR_TRACE_RESTRICT_DIRECTION_NW, + INVALID_STRING_ID +}; +static const uint _direction_value_val[] = { + TRDTSV_FRONT, + TRDTSV_BACK, + TRNTSV_NE, + TRNTSV_SE, + TRNTSV_SW, + TRNTSV_NW, +}; + +/** value drop down list for direction type strings and values */ +static const TraceRestrictDropDownListSet _direction_value = { + _direction_value_str, _direction_value_val, +}; + /** * Get index of @p value in @p list_set * if @p value is not present, assert if @p missing_ok is false, otherwise return -1 @@ -201,6 +224,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER, STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION, STR_TRACE_RESTRICT_VARIABLE_CARGO, + STR_TRACE_RESTRICT_VARIABLE_ENTRY_DIRECTION, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; @@ -211,6 +235,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI TRIT_COND_NEXT_ORDER, TRIT_COND_LAST_STATION, TRIT_COND_CARGO, + TRIT_COND_ENTRY_DIRECTION, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { @@ -490,6 +515,17 @@ static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, SetDParam(2, GetCargoStringByID(GetTraceRestrictValue(item))); break; + case TRVT_DIRECTION: + if (GetTraceRestrictValue(item) >= TRDTSV_FRONT) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_SIGNAL_FACE; + } else { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_DIRECTION; + } + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(1, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); + SetDParam(2, GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item))); + break; + default: NOT_REACHED(); break; @@ -706,6 +742,10 @@ public: this->ShowDropDownListWithValue(GetSortedCargoTypeDropDownListSet(), GetTraceRestrictValue(item), true, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); // current cargo is permitted to not be in list break; + case TRVT_DIRECTION: + this->ShowDropDownListWithValue(&_direction_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); + break; + default: break; } @@ -1357,6 +1397,13 @@ private: GetCargoStringByID(GetTraceRestrictValue(item)); break; + case TRVT_DIRECTION: + right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); + this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); + this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = + GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item)); + break; + default: break; } From 71012789d279dca3a276b6d9f423d801193df759 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 28 Jul 2015 01:51:31 +0100 Subject: [PATCH 11/15] Update project listings. --- projects/openttd_vs100.vcxproj | 4 ++++ projects/openttd_vs100.vcxproj.filters | 12 ++++++++++++ projects/openttd_vs80.vcproj | 16 ++++++++++++++++ projects/openttd_vs90.vcproj | 16 ++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index 37f9948032..27c0897a28 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -1271,6 +1271,10 @@ + + + + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index 06800ffdaf..c8c74caeda 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -3042,6 +3042,18 @@ Threading + + Threading + + + Threading + + + Threading + + + Threading + diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index f859fcf18a..f02bbcbd28 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -4482,6 +4482,22 @@ RelativePath=".\..\src\thread\thread_win32.cpp" > + + + + + + + + + + + + + + + + Date: Tue, 28 Jul 2015 21:43:23 +0100 Subject: [PATCH 12/15] Update land info window to indicate if signal tile is restricted. --- src/lang/english.txt | 2 ++ src/misc_gui.cpp | 1 + src/rail_cmd.cpp | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/src/lang/english.txt b/src/lang/english.txt index b390e09f5c..e077670977 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2712,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_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/rail_cmd.cpp b/src/rail_cmd.cpp index 29efc63de0..35b7f1e3c3 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -2770,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; } From d99ebb4976b9e8f583f7f3fdcb3919af19d6dcd7 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 1 Aug 2015 13:04:53 +0100 Subject: [PATCH 13/15] Add tracerestrict SLXI version info and special case loading from tracerestrict branch without SLXI. --- src/saveload/extended_ver_sl.cpp | 8 +++++++- src/saveload/extended_ver_sl.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 478e6d2f34..cb66259ade 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 }; @@ -118,7 +119,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 }; From 0b09a7ac6193a4db7b817e8cd4d830e7f1331bd4 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 1 Aug 2015 17:06:06 +0100 Subject: [PATCH 14/15] Add a 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. --- src/stdafx.h | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 */ From 804cfad053ad0b34a0a88ba681e22c845ab01464 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 1 Aug 2015 17:15:25 +0100 Subject: [PATCH 15/15] Add override keywords for tracerestrict GUI code --- src/tracerestrict_gui.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 630c3abbd6..cfd9a5eafe 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -594,7 +594,7 @@ public: this->ReloadProgramme(); } - virtual void OnClick(Point pt, int widget, int click_count) + virtual void OnClick(Point pt, int widget, int click_count) OVERRIDE { switch (widget) { case TR_WIDGET_INSTRUCTION_LIST: { @@ -778,7 +778,7 @@ public: } } - virtual void OnQueryTextFinished(char *str) + virtual void OnQueryTextFinished(char *str) OVERRIDE { if (StrEmpty(str)) { return; @@ -801,7 +801,7 @@ public: TraceRestrictDoCommandP(tile, track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); } - virtual void OnDropdownSelect(int widget, int index) + virtual void OnDropdownSelect(int widget, int index) OVERRIDE { TraceRestrictItem item = GetSelected(); if (item == 0 || index < 0 || this->selected_instruction < 1) { @@ -875,7 +875,7 @@ public: } } - virtual void OnPlaceObject(Point pt, TileIndex tile) + virtual void OnPlaceObject(Point pt, TileIndex tile) OVERRIDE { int widget = this->current_placement_widget; this->current_placement_widget = -1; @@ -996,13 +996,13 @@ public: TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); } - virtual void OnPlaceObjectAbort() + virtual void OnPlaceObjectAbort() OVERRIDE { this->RaiseButtons(); this->current_placement_widget = -1; } - virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) OVERRIDE { switch (widget) { case TR_WIDGET_INSTRUCTION_LIST: @@ -1012,18 +1012,18 @@ public: } } - virtual void OnResize() + virtual void OnResize() OVERRIDE { /* Update the scroll bar */ this->vscroll->SetCapacityFromWidget(this, TR_WIDGET_INSTRUCTION_LIST); } - virtual void OnPaint() + virtual void OnPaint() OVERRIDE { this->DrawWidgets(); } - virtual void DrawWidget(const Rect &r, int widget) const + virtual void DrawWidget(const Rect &r, int widget) const OVERRIDE { if (widget != TR_WIDGET_INSTRUCTION_LIST) return; @@ -1059,14 +1059,14 @@ public: } } - virtual void OnInvalidateData(int data, bool gui_scope) + virtual void OnInvalidateData(int data, bool gui_scope) OVERRIDE { if (gui_scope) { this->ReloadProgramme(); } } - virtual void SetStringParameters(int widget) const + virtual void SetStringParameters(int widget) const OVERRIDE { switch (widget) { case TR_WIDGET_VALUE_INT: {