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.
pull/3/head
Jonathan G Rennison 9 years ago
parent 13a726b18f
commit 5f1b148cf9

@ -1193,3 +1193,8 @@ thread/thread.h
#else
thread/thread_none.cpp
#end
tracerestrict.h
tracerestrict.cpp
tracerestrict_gui.cpp
saveload/tracerestrict_sl.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
};
/*!

@ -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)
};

@ -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

@ -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();

@ -65,6 +65,7 @@
#include "viewport_sprite_sorter.h"
#include "linkgraph/linkgraphschedule.h"
#include "tracerestrict.h"
#include <stdarg.h>
@ -302,6 +303,7 @@ static void ShutdownGame()
#endif
LinkGraphSchedule::Clear();
ClearTraceRestrictMapping();
PoolBase::Clean(PT_ALL);
/* No NewGRFs were loaded when it was still bootstrapping. */

@ -13,6 +13,7 @@
#define YAPF_COSTRAIL_HPP
#include "../../pbs.h"
#include "../../tracerestrict.h"
template <class Types>
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;
}
}
}
}
}

@ -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) {

@ -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();
}

@ -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);

@ -2988,6 +2988,8 @@ bool AfterLoadGame()
ResetSignalHandlers();
AfterLoadLinkGraphs();
AfterLoadTraceRestrict();
return true;
}

@ -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,
};

@ -34,6 +34,7 @@ void AfterLoadLabelMaps();
void AfterLoadStoryBook();
void AfterLoadLinkGraphs();
void AfterLoadCompanyStats();
void AfterLoadTraceRestrict();
void UpdateHousesAndTowns();
void UpdateOldAircraft();

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @file tracerestrict_sl.cpp Code handling saving and loading of trace restrict programs */
#include "../stdafx.h"
#include "../tracerestrict.h"
#include "saveload.h"
#include <vector>
#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},
};

@ -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.

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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 <vector>
/** 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<TraceRestrictCondStackFlags> &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<TraceRestrictCondStackFlags>(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<TraceRestrictCondStackFlags> 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<TraceRestrictItem> &items) {
// static to avoid needing to re-alloc/resize on each execution
static std::vector<TraceRestrictCondStackFlags> 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<Track>(0)));
// First mapping for next tile, or later
TraceRestrictMapping::iterator upper_bound = _tracerestrictprogram_mapping.lower_bound(MakeTraceRestrictRefId(t + 1, static_cast<Track>(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<TraceRestrictMapping::iterator, bool> 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<Track>(GB(p1, 0, 3));
TraceRestrictDoCommandType type = static_cast<TraceRestrictDoCommandType>(GB(p1, 3, 5));
uint32 offset = GB(p1, 8, 16);
TraceRestrictItem item = static_cast<TraceRestrictItem>(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<TraceRestrictItem> 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<TraceRestrictItem>::iterator remove_start = items.begin() + offset;
std::vector<TraceRestrictItem>::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();
}

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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 <map>
#include <vector>
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<TraceRestrictProgram, TraceRestrictProgramID, 16, 256000> 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<TraceRestrictRefId, TraceRestrictMappingItem> 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<TraceRestrictProgramResultFlags>(0)) { }
};
typedef uint32 TraceRestrictItem;
struct TraceRestrictProgram : TraceRestrictProgramPool::PoolItem<&_tracerestrictprogram_pool> {
std::vector<TraceRestrictItem> 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<TraceRestrictItem> &items);
CommandCost Validate() const { return TraceRestrictProgram::Validate(items); }
};
static inline TraceRestrictItemType GetTraceRestrictType(TraceRestrictItem item)
{
return static_cast<TraceRestrictItemType>(GB(item, TRIFA_TYPE_OFFSET, TRIFA_TYPE_COUNT));
}
static inline TraceRestrictCondFlags GetTraceRestrictCondFlags(TraceRestrictItem item)
{
return static_cast<TraceRestrictCondFlags>(GB(item, TRIFA_COND_FLAGS_OFFSET, TRIFA_COND_FLAGS_COUNT));
}
static inline TraceRestrictCondOp GetTraceRestrictCondOp(TraceRestrictItem item)
{
return static_cast<TraceRestrictCondOp>(GB(item, TRIFA_COND_OP_OFFSET, TRIFA_COND_OP_COUNT));
}
static inline uint16 GetTraceRestrictValue(TraceRestrictItem item)
{
return static_cast<uint16>(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<TileIndex>(ref >> 3);
}
static inline Track GetTraceRestrictRefIdTrack(TraceRestrictRefId ref)
{
return static_cast<Track>(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 */

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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<int, const TraceRestrictDropDownListSet *> 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<TraceRestrictItem>(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<TraceRestrictItem>(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<TraceRestrictItemType>(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<TraceRestrictItemType>(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<TraceRestrictCondOp>(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<NWidgetBase>(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<TraceRestrictItem> &items = prog->items;
if (static_cast<size_t>(index) == items.size() + 1) {
return MakeSpecialItem(TRNTSV_END);
}
if (static_cast<size_t>(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<NWidgetBase>(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<TraceRestrictItem>(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<NWidgetStacked>(TR_WIDGET_SEL_TOP_LEFT);
NWidgetStacked *middle_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_MIDDLE);
NWidgetStacked *right_sel = this->GetWidget<NWidgetStacked>(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<NWidgetCore>(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<NWidgetCore>(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<NWidgetCore>(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);
}

@ -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.

@ -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.
};

Loading…
Cancel
Save