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
parent
13a726b18f
commit
5f1b148cf9
@ -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},
|
||||
};
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue