Initial implementation of tracerestrict counter mechanism

pull/192/head
Jonathan G Rennison 4 years ago
parent dea2be5d3f
commit 817bc44a3e

@ -245,6 +245,9 @@ CommandProc CmdAlterTraceRestrictSlot;
CommandProc CmdDeleteTraceRestrictSlot;
CommandProc CmdAddVehicleTraceRestrictSlot;
CommandProc CmdRemoveVehicleTraceRestrictSlot;
CommandProc CmdCreateTraceRestrictCounter;
CommandProc CmdAlterTraceRestrictCounter;
CommandProc CmdDeleteTraceRestrictCounter;
CommandProc CmdInsertSignalInstruction;
CommandProc CmdModifySignalInstruction;
@ -469,6 +472,9 @@ static const Command _command_proc_table[] = {
DEF_CMD(CmdDeleteTraceRestrictSlot, 0, CMDT_OTHER_MANAGEMENT ), // CMD_DELETE_TRACERESTRICT_SLOT
DEF_CMD(CmdAddVehicleTraceRestrictSlot, 0, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_VEHICLE_TRACERESTRICT_SLOT
DEF_CMD(CmdRemoveVehicleTraceRestrictSlot, 0, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_VEHICLE_TRACERESTRICT_SLOT
DEF_CMD(CmdCreateTraceRestrictCounter, 0, CMDT_OTHER_MANAGEMENT ), // CMD_CREATE_TRACERESTRICT_COUNTER
DEF_CMD(CmdAlterTraceRestrictCounter, 0, CMDT_OTHER_MANAGEMENT ), // CMD_ALTER_TRACERESTRICT_COUNTER
DEF_CMD(CmdDeleteTraceRestrictCounter, 0, CMDT_OTHER_MANAGEMENT ), // CMD_DELETE_TRACERESTRICT_COUNTER
DEF_CMD(CmdInsertSignalInstruction, 0, CMDT_OTHER_MANAGEMENT ), // CMD_INSERT_SIGNAL_INSTRUCTION
DEF_CMD(CmdModifySignalInstruction, 0, CMDT_OTHER_MANAGEMENT ), // CMD_MODIFY_SIGNAL_INSTRUCTION

@ -408,6 +408,9 @@ enum Commands {
CMD_DELETE_TRACERESTRICT_SLOT, ///< delete a tracerestrict slot
CMD_ADD_VEHICLE_TRACERESTRICT_SLOT, ///< add a vehicle to a tracerestrict slot
CMD_REMOVE_VEHICLE_TRACERESTRICT_SLOT, ///< remove a vehicle from a tracerestrict slot
CMD_CREATE_TRACERESTRICT_COUNTER, ///< create a tracerestrict counter
CMD_ALTER_TRACERESTRICT_COUNTER, ///< alter a tracerestrict counter
CMD_DELETE_TRACERESTRICT_COUNTER, ///< delete a tracerestrict counter
CMD_INSERT_SIGNAL_INSTRUCTION, ///< insert a signal instruction
CMD_MODIFY_SIGNAL_INSTRUCTION, ///< modifies a signal instruction

@ -1006,6 +1006,12 @@ public:
break;
}
case ADI_TRACERESTRICT_COUNTER_MGMT: {
extern void ShowTraceRestrictCounterWindow(CompanyID company);
ShowTraceRestrictCounterWindow(this->owner);
break;
}
default: NOT_REACHED();
}
break;

@ -2731,6 +2731,7 @@ STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY :slot occupancy
STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY_REMAINING :slot occupancy remaining
STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY_SHORT :occupancy
STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY_REMAINING_SHORT :occupancy remaining
STR_TRACE_RESTRICT_VARIABLE_COUNTER_VALUE :counter value
STR_TRACE_RESTRICT_VARIABLE_TRAIN_WEIGHT :weight
STR_TRACE_RESTRICT_VARIABLE_TRAIN_POWER :power
STR_TRACE_RESTRICT_VARIABLE_TRAIN_MAX_TE :max T.E.
@ -2762,6 +2763,8 @@ STR_TRACE_RESTRICT_CONDITIONAL_SLOT_STR :{STRING} train
STR_TRACE_RESTRICT_CONDITIONAL_SLOT_OCCUPANCY :{STRING} {STRING} of slot: {TRSLOT} {STRING} {COMMA} then
STR_TRACE_RESTRICT_CONDITIONAL_SLOT_OCCUPANCY_STR :{STRING} {STRING} of slot: {STRING} {BLACK}{STRING} {STRING} {COMMA} then
STR_TRACE_RESTRICT_CONDITIONAL_TRAIN_STATUS :{STRING} train {STRING} status: {STRING} then
STR_TRACE_RESTRICT_CONDITIONAL_COUNTER :{STRING} value of counter: {TRCOUNTER} {STRING} {COMMA} then
STR_TRACE_RESTRICT_CONDITIONAL_COUNTER_STR :{STRING} value of counter: {STRING} {BLACK}{STRING} {STRING} {COMMA} then
STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED :{STRING} {STRING} {STRING} {RED}undefined {BLACK}{STRING}then
STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED :{STRING} {RED}undefined {BLACK}{STRING}then
STR_TRACE_RESTRICT_PF_PENALTY_ITEM :Add pathfinder penalty: {COMMA}
@ -2799,6 +2802,7 @@ STR_TRACE_RESTRICT_SLOT_OP :Slot operation
STR_TRACE_RESTRICT_REVERSE :Reverse
STR_TRACE_RESTRICT_SPEED_RESTRICTION :Speed restriction
STR_TRACE_RESTRICT_NEWS_CONTROL :News control
STR_TRACE_RESTRICT_COUNTER_OP :Counter operation
STR_TRACE_RESTRICT_SLOT_ACQUIRE_WAIT :Acquire or wait
STR_TRACE_RESTRICT_SLOT_TRY_ACQUIRE :Try to acquire
STR_TRACE_RESTRICT_SLOT_RELEASE_FRONT :Release (front)
@ -2816,6 +2820,13 @@ STR_TRACE_RESTRICT_SLOT_PBS_RES_END_RELEASE_ITEM :PBS reservation
STR_TRACE_RESTRICT_SLOT_NAME :{TRSLOT}
STR_TRACE_RESTRICT_SLOT_LIST_HEADER :{BLACK}Slot{CONSUME_ARG}{P "" s}: {LTBLUE}
STR_TRACE_RESTRICT_SLOT_LIST_SEPARATOR :{BLACK}, {LTBLUE}
STR_TRACE_RESTRICT_COUNTER_NAME :{TRCOUNTER}
STR_TRACE_RESTRICT_COUNTER_INCREASE :Increase
STR_TRACE_RESTRICT_COUNTER_DECREASE :Decrease
STR_TRACE_RESTRICT_COUNTER_SET :Set
STR_TRACE_RESTRICT_COUNTER_INCREASE_ITEM :Increase counter: {STRING1}{BLACK}{STRING} by {COMMA}
STR_TRACE_RESTRICT_COUNTER_DECREASE_ITEM :Decrease counter: {STRING1}{BLACK}{STRING} by {COMMA}
STR_TRACE_RESTRICT_COUNTER_SET_ITEM :Set counter: {STRING1}{BLACK}{STRING} to {COMMA}
STR_TRACE_RESTRICT_TRAIN_STATUS_EMPTY :empty
STR_TRACE_RESTRICT_TRAIN_STATUS_FULL :full
STR_TRACE_RESTRICT_TRAIN_STATUS_BROKEN_DOWN :broken down
@ -2859,6 +2870,23 @@ STR_TRACE_RESTRICT_SLOT_CREATE_CAPTION :{BLACK}Create a
STR_TRACE_RESTRICT_SLOT_SET_MAX_OCCUPANCY_CAPTION :{BLACK}Set maximum occupancy of a slot
STR_TRACE_RESTRICT_SLOT_QUERY_DELETE_CAPTION :{WHITE}Delete Slot
STR_TRACE_RESTRICT_SLOT_DELETE_QUERY_TEXT :{WHITE}Are you sure you want to delete this slot?
STR_TRACE_RESTRICT_COUNTER_OP_TOOLTIP :{BLACK}Counter operation type
STR_TRACE_RESTRICT_COUNTER_GUI_LIST_TOOLTIP :{BLACK}Counters - click on a counter to modify it.
STR_TRACE_RESTRICT_COUNTER_CREATE :{BLACK}Create
STR_TRACE_RESTRICT_COUNTER_DELETE :{BLACK}Delete
STR_TRACE_RESTRICT_COUNTER_RENAME :{BLACK}Rename
STR_TRACE_RESTRICT_COUNTER_SET_VALUE :{BLACK}Set Value
STR_TRACE_RESTRICT_COUNTER_CREATE_TOOLTIP :{BLACK}Click to create a counter
STR_TRACE_RESTRICT_COUNTER_DELETE_TOOLTIP :{BLACK}Delete the selected counter
STR_TRACE_RESTRICT_COUNTER_RENAME_TOOLTIP :{BLACK}Rename the selected counter
STR_TRACE_RESTRICT_COUNTER_SET_VALUE_TOOLTIP :{BLACK}Set the value of the selected counter
STR_TRACE_RESTRICT_COUNTER_CAPTION :{WHITE}Routing Restrictions - Counter Management
STR_TRACE_RESTRICT_COUNTER_MANAGE :Manage counters
STR_TRACE_RESTRICT_COUNTER_RENAME_CAPTION :{BLACK}Rename a counter
STR_TRACE_RESTRICT_COUNTER_CREATE_CAPTION :{BLACK}Create a counter
STR_TRACE_RESTRICT_COUNTER_SET_VALUE_CAPTION :{BLACK}Set counter value
STR_TRACE_RESTRICT_COUNTER_QUERY_DELETE_CAPTION :{WHITE}Delete counter
STR_TRACE_RESTRICT_COUNTER_DELETE_QUERY_TEXT :{WHITE}Are you sure you want to delete this counter?
STR_TRACE_RESTRICT_INSERT :{BLACK}Insert
STR_TRACE_RESTRICT_REMOVE :{BLACK}Remove
STR_TRACE_RESTRICT_RESET :{BLACK}Reset
@ -2903,6 +2931,10 @@ STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_RENAME :{WHITE}Can't re
STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_ADD_VEHICLE :{WHITE}Can't add the vehicle to this slot...
STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_REMOVE_VEHICLE :{WHITE}Can't remove the vehicle from this slot...
STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_SET_MAX_OCCUPANCY :{WHITE}Can't set maximum occupancy of this slot...
STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_CREATE :{WHITE}Can't create counter...
STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_DELETE :{WHITE}Can't delete this counter...
STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_RENAME :{WHITE}Can't rename counter...
STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_MODIFY :{WHITE}Can't modify this counter...
# Programmable Pre-Signals
STR_PROGRAM_SIGNAL_TOOLTIP :{BLACK}Program pre-signal

@ -71,6 +71,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_TRACE_RESTRICT_STATUSCND,XSCF_NULL, 1, 1, "tracerestrict_status_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_REVERSE, XSCF_NULL, 1, 1, "tracerestrict_reverse", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_NEWSCTRL,XSCF_NULL, 1, 1, "tracerestrict_newsctrl", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_COUNTER, XSCF_NULL, 1, 1, "tracerestrict_counter", nullptr, nullptr, "TRRC" },
{ XSLFI_PROG_SIGS, XSCF_NULL, 1, 1, "programmable_signals", nullptr, nullptr, "SPRG" },
{ XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", nullptr, nullptr, nullptr },
{ XSLFI_SAFER_CROSSINGS, XSCF_NULL, 1, 1, "safer_crossings", nullptr, nullptr, nullptr },

@ -28,6 +28,7 @@ enum SlXvFeatureIndex {
XSLFI_TRACE_RESTRICT_STATUSCND, ///< Trace restrict: train status condition
XSLFI_TRACE_RESTRICT_REVERSE, ///< Trace restrict: reverse
XSLFI_TRACE_RESTRICT_NEWSCTRL, ///< Trace restrict: news control
XSLFI_TRACE_RESTRICT_COUNTER, ///< Trace restrict: counters
XSLFI_PROG_SIGS, ///< programmable pre-signals patch
XSLFI_ADJACENT_CROSSINGS, ///< Adjacent level crossings closure patch
XSLFI_SAFER_CROSSINGS, ///< Safer level crossings

@ -170,6 +170,44 @@ static void Save_TRRS()
}
}
static const SaveLoad _trace_restrict_counter_desc[] = {
SLE_VAR(TraceRestrictCounter, value, SLE_INT32),
SLE_SSTR(TraceRestrictCounter, name, SLF_ALLOW_CONTROL),
SLE_VAR(TraceRestrictCounter, owner, SLE_UINT8),
SLE_END()
};
/**
* Load counter pool
*/
static void Load_TRRC()
{
int index;
while ((index = SlIterateArray()) != -1) {
TraceRestrictCounter *ctr = new (index) TraceRestrictCounter();
SlObject(ctr, _trace_restrict_counter_desc);
}
}
/**
* Save a counter, used by SlAutolength
*/
static void RealSave_TRRC(TraceRestrictCounter *ctr)
{
SlObject(ctr, _trace_restrict_counter_desc);
}
/**
* Save counter pool
*/
static void Save_TRRC()
{
for (TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
SlSetArrayIndex(ctr->index);
SlAutolength((AutolengthProc*) RealSave_TRRC, ctr);
}
}
/**
* Update program reference counts from just-loaded mapping
*/
@ -184,5 +222,6 @@ void AfterLoadTraceRestrict()
extern const ChunkHandler _trace_restrict_chunk_handlers[] = {
{ 'TRRM', Save_TRRM, Load_TRRM, nullptr, nullptr, CH_SPARSE_ARRAY}, // Trace Restrict Mapping chunk
{ 'TRRP', Save_TRRP, Load_TRRP, nullptr, nullptr, CH_ARRAY}, // Trace Restrict Mapping Program Pool chunk
{ 'TRRS', Save_TRRS, Load_TRRS, nullptr, nullptr, CH_ARRAY | CH_LAST}, // Trace Restrict Slot Pool chunk
{ 'TRRS', Save_TRRS, Load_TRRS, nullptr, nullptr, CH_ARRAY}, // Trace Restrict Slot Pool chunk
{ 'TRRC', Save_TRRC, Load_TRRC, nullptr, nullptr, CH_ARRAY | CH_LAST}, // Trace Restrict Counter Pool chunk
};

@ -1783,6 +1783,15 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
break;
}
case SCC_TR_COUNTER_NAME: { // {TRCOUNTER}
const TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(args->GetInt32(SCC_TR_SLOT_NAME));
if (ctr == nullptr) break;
int64 args_array[] = {(int64)(size_t)ctr->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
break;
}
case SCC_STATION_FEATURES: { // {STATIONFEATURES}
buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
break;

@ -48,6 +48,7 @@ enum StringControlCode {
SCC_PRESIDENT_NAME,
SCC_ENGINE_NAME,
SCC_TR_SLOT_NAME,
SCC_TR_COUNTER_NAME,
SCC_CURRENCY_SHORT,
SCC_CURRENCY_LONG,

@ -127,6 +127,7 @@ static const CmdStruct _cmd_structs[] = {
{"COMPANY_NUM", EmitSingleChar, SCC_COMPANY_NUM, 1, -1, C_NONE},
{"PRESIDENT_NAME", EmitSingleChar, SCC_PRESIDENT_NAME, 1, -1, C_NONE | C_GENDER},
{"TRSLOT", EmitSingleChar, SCC_TR_SLOT_NAME, 1, -1, C_NONE | C_GENDER},
{"TRCOUNTER", EmitSingleChar, SCC_TR_COUNTER_NAME, 1, -1, C_NONE | C_GENDER},
{"", EmitSingleChar, '\n', 0, -1, C_DONTCOUNT},
{"{", EmitSingleChar, '{', 0, -1, C_DONTCOUNT},

@ -69,6 +69,9 @@ INSTANTIATE_POOL_METHODS(TraceRestrictProgram)
TraceRestrictSlotPool _tracerestrictslot_pool("TraceRestrictSlot");
INSTANTIATE_POOL_METHODS(TraceRestrictSlot)
TraceRestrictCounterPool _tracerestrictcounter_pool("TraceRestrictCounter");
INSTANTIATE_POOL_METHODS(TraceRestrictCounter)
/**
* TraceRestrictRefId --> TraceRestrictProgramID (Pool ID) mapping
* The indirection is mainly to enable shared programs
@ -499,6 +502,15 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp
break;
}
case TRIT_COND_COUNTER_VALUE: {
// TRVT_COUNTER_INDEX_INT value type uses the next slot
i++;
uint32_t value = this->items[i];
const TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(GetTraceRestrictValue(item));
result = TestCondition(ctr != nullptr ? ctr->value : 0, condop, value);
break;
}
default:
NOT_REACHED();
}
@ -657,6 +669,33 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp
}
break;
case TRIT_COUNTER: {
// TRVT_COUNTER_INDEX_INT value type uses the next slot
i++;
uint32_t value = this->items[i];
if (!(input.permitted_slot_operations & TRPISP_CHANGE_COUNTER)) break;
TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(GetTraceRestrictValue(item));
if (ctr == nullptr) break;
switch (static_cast<TraceRestrictCounterCondOpField>(GetTraceRestrictCondOp(item))) {
case TRCCOF_INCREASE:
ctr->UpdateValue(ctr->value + value);
break;
case TRCCOF_DECREASE:
ctr->UpdateValue(ctr->value - value);
break;
case TRCCOF_SET:
ctr->UpdateValue(value);
break;
default:
NOT_REACHED();
break;
}
break;
}
default:
NOT_REACHED();
}
@ -751,6 +790,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
case TRIT_COND_TRAIN_OWNER:
case TRIT_COND_TRAIN_STATUS:
case TRIT_COND_LOAD_PERCENT:
case TRIT_COND_COUNTER_VALUE:
break;
default:
@ -834,6 +874,10 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
actions_used_flags |= TRPAUF_TRAIN_NOT_STUCK;
break;
case TRIT_COUNTER:
actions_used_flags |= TRPAUF_CHANGE_COUNTER;
break;
default:
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION);
}
@ -943,6 +987,10 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp
SetTraceRestrictValue(item, INVALID_TRACE_RESTRICT_SLOT_ID);
break;
case TRVT_COUNTER_INDEX_INT:
SetTraceRestrictValue(item, INVALID_TRACE_RESTRICT_COUNTER_ID);
break;
default:
NOT_REACHED();
break;
@ -1147,8 +1195,12 @@ static uint32 GetDualInstructionInitialValue(TraceRestrictItem item)
return INVALID_TILE;
case TRIT_COND_SLOT_OCCUPANCY:
case TRIT_COND_COUNTER_VALUE:
return 0;
case TRIT_COUNTER:
return 1;
default:
NOT_REACHED();
}
@ -1636,9 +1688,20 @@ void TraceRestrictUpdateCompanyID(CompanyID old_company, CompanyID new_company)
}
}
for (TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
if (ctr->owner != old_company) continue;
if (new_company == INVALID_OWNER) {
TraceRestrictRemoveCounterID(ctr->index);
delete ctr;
} else {
ctr->owner = new_company;
}
}
// update windows
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
InvalidateWindowClassesData(WC_TRACE_RESTRICT_COUNTERS);
}
static std::unordered_multimap<VehicleID, TraceRestrictSlotID> slot_vehicle_index;
@ -1922,7 +1985,7 @@ CommandCost CmdDeleteTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint
* @param flags type of operation
* @param p1 index of array group
* - p1 bit 0-15 : GroupID
* - p1 bit 16: 0 - Rename grouop
* - p1 bit 16: 0 - Rename group
* 1 - Change max occupancy
* @param p2 new max occupancy
* @param text the new name
@ -2012,3 +2075,148 @@ CommandCost CmdRemoveVehicleTraceRestrictSlot(TileIndex tile, DoCommandFlag flag
return CommandCost();
}
void TraceRestrictCounter::UpdateValue(int32 new_value)
{
new_value = max<int32>(0, new_value);
if (new_value != this->value) {
this->value = new_value;
InvalidateWindowClassesData(WC_TRACE_RESTRICT_COUNTERS);
}
}
static bool IsUniqueCounterName(const char *name)
{
for (const TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
if (ctr->name == name) return false;
}
return true;
}
/**
* This is called when a counter is about to be deleted
* Scan program pool and change any references to it to the invalid counter ID, to avoid dangling references
*/
void TraceRestrictRemoveCounterID(TraceRestrictCounterID index)
{
for (TraceRestrictProgram *prog : TraceRestrictProgram::Iterate()) {
for (size_t i = 0; i < prog->items.size(); i++) {
TraceRestrictItem &item = prog->items[i]; // note this is a reference,
if ((GetTraceRestrictType(item) == TRIT_COUNTER || GetTraceRestrictType(item) == TRIT_COND_COUNTER_VALUE) && GetTraceRestrictValue(item) == index) {
SetTraceRestrictValueDefault(item, TRVT_COUNTER_INDEX_INT); // this updates the instruction in-place
}
if (IsTraceRestrictDoubleItem(item)) i++;
}
}
// update windows
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
}
/**
* Create a new counter.
* @param tile unused
* @param flags type of operation
* @param p1 unused
* @param p2 unused
* @param text new counter name
* @return the cost of this operation or an error
*/
CommandCost CmdCreateTraceRestrictCounter(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
if (!TraceRestrictCounter::CanAllocateItem()) return CMD_ERROR;
if (StrEmpty(text)) return CMD_ERROR;
size_t length = Utf8StringLength(text);
if (length <= 0) return CMD_ERROR;
if (length >= MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS) return CMD_ERROR;
if (!IsUniqueCounterName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
if (flags & DC_EXEC) {
TraceRestrictCounter *ctr = new TraceRestrictCounter(_current_company);
ctr->name = text;
// update windows
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
InvalidateWindowClassesData(WC_TRACE_RESTRICT_COUNTERS);
}
return CommandCost();
}
/**
* Deletes a counter.
* @param tile unused
* @param flags type of operation
* @param p1 index of array group
* - p1 bit 0-15 : Counter ID
* @param p2 unused
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdDeleteTraceRestrictCounter(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(p1);
if (ctr == nullptr || ctr->owner != _current_company) return CMD_ERROR;
if (flags & DC_EXEC) {
/* notify tracerestrict that counter is about to be deleted */
TraceRestrictRemoveCounterID(ctr->index);
delete ctr;
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
InvalidateWindowClassesData(WC_TRACE_RESTRICT_COUNTERS);
InvalidateWindowClassesData(WC_VEHICLE_ORDERS);
}
return CommandCost();
}
/**
* Alter a counter
* @param tile unused
* @param flags type of operation
* @param p1 index of array counter
* - p1 bit 0-15 : Counter ID
* - p1 bit 16: 0 - Rename counter
* 1 - Change value
* @param p2 new value
* @param text the new name
* @return the cost of this operation or an error
*/
CommandCost CmdAlterTraceRestrictCounter(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(GB(p1, 0, 16));
if (ctr == nullptr || ctr->owner != _current_company) return CMD_ERROR;
if (!HasBit(p1, 16)) {
/* Rename counter */
if (StrEmpty(text)) return CMD_ERROR;
size_t length = Utf8StringLength(text);
if (length <= 0) return CMD_ERROR;
if (length >= MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS) return CMD_ERROR;
if (!IsUniqueCounterName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
if (flags & DC_EXEC) {
ctr->name = text;
}
} else {
/* Change value */
if (flags & DC_EXEC) {
ctr->UpdateValue(p2);
}
}
if (flags & DC_EXEC) {
// update windows
InvalidateWindowClassesData(WC_TRACE_RESTRICT);
InvalidateWindowClassesData(WC_TRACE_RESTRICT_COUNTERS);
InvalidateWindowClassesData(WC_VEHICLE_ORDERS);
}
return CommandCost();
}

@ -52,6 +52,18 @@ static const TraceRestrictSlotID NEW_TRACE_RESTRICT_SLOT_ID = 0xFFFD; //
static const TraceRestrictSlotID ALL_TRAINS_TRACE_RESTRICT_SLOT_ID = 0xFFFE; // for GUI use only
static const TraceRestrictSlotID INVALID_TRACE_RESTRICT_SLOT_ID = 0xFFFF;
/** Counter pool ID type. */
typedef uint16 TraceRestrictCounterID;
struct TraceRestrictCounter;
/** Type of the pool for trace restrict slots. */
typedef Pool<TraceRestrictCounter, TraceRestrictCounterID, 16, 0xFFF0> TraceRestrictCounterPool;
/** The actual pool for trace restrict nodes. */
extern TraceRestrictCounterPool _tracerestrictcounter_pool;
static const TraceRestrictCounterID NEW_TRACE_RESTRICT_COUNTER_ID = 0xFFFE; // for GUI use only
static const TraceRestrictCounterID INVALID_TRACE_RESTRICT_COUNTER_ID = 0xFFFF;
extern const uint16 _tracerestrict_pathfinder_penalty_preset_values[];
/** Type used for the TraceRestrictRefId -> TraceRestrictProgramID mapping */
@ -136,11 +148,13 @@ enum TraceRestrictItemType {
TRIT_COND_TRAIN_OWNER = 24, ///< Test train owner
TRIT_COND_TRAIN_STATUS = 25, ///< Test train status
TRIT_COND_LOAD_PERCENT = 26, ///< Test train load percentage
TRIT_COND_COUNTER_VALUE = 27, ///< Test counter value
TRIT_COND_END = 48, ///< End (exclusive) of conditional item types, note that this has the same value as TRIT_REVERSE
TRIT_REVERSE = 48, ///< Reverse behind signal
TRIT_SPEED_RESTRICTION = 49, ///< Speed restriction
TRIT_NEWS_CONTROL = 50, ///< News control
TRIT_COUNTER = 51, ///< Change counter value
/* space up to 63 */
};
@ -294,6 +308,16 @@ enum TraceRestrictSlotOccupancyCondAuxField {
/* space up to 3 */
};
/**
* TraceRestrictItem repurposed condition operator field, for counter operation type actions
*/
enum TraceRestrictCounterCondOpField {
TRCCOF_INCREASE = 0, ///< increase counter by value
TRCCOF_DECREASE = 1, ///< decrease counter by value
TRCCOF_SET = 2, ///< set counter to value
/* space up to 8 */
};
/**
* TraceRestrictItem pathfinder penalty preset index
* This may not be shortened, only lengthened, as preset indexes are stored in save games
@ -336,6 +360,7 @@ enum TraceRestrictProgramActionsUsedFlags {
TRPAUF_REVERSE = 1 << 9, ///< Reverse behind signal
TRPAUF_SPEED_RESTRICTION = 1 << 10, ///< Speed restriction
TRPAUF_TRAIN_NOT_STUCK = 1 << 11, ///< Train is not stuck
TRPAUF_CHANGE_COUNTER = 1 << 12, ///< Change counter value is present
};
DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramActionsUsedFlags)
@ -349,6 +374,7 @@ enum TraceRestrictProgramInputSlotPermissions {
TRPISP_PBS_RES_END_ACQUIRE = 1 << 3, ///< Slot acquire (PBS reservations ending at this signal) is permitted
TRPISP_PBS_RES_END_ACQ_DRY = 1 << 4, ///< Dry-run slot acquire (PBS reservations ending at this signal) is permitted
TRPISP_PBS_RES_END_RELEASE = 1 << 5, ///< Slot release (PBS reservations ending at this signal) is permitted
TRPISP_CHANGE_COUNTER = 1 << 6, ///< Change counter value is permitted
};
DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramInputSlotPermissions)
@ -527,7 +553,7 @@ static inline bool IsTraceRestrictConditional(TraceRestrictItem item)
static inline bool IsTraceRestrictDoubleItem(TraceRestrictItem item)
{
const TraceRestrictItemType type = GetTraceRestrictType(item);
return type == TRIT_COND_PBS_ENTRY_SIGNAL || type == TRIT_COND_SLOT_OCCUPANCY;
return type == TRIT_COND_PBS_ENTRY_SIGNAL || type == TRIT_COND_SLOT_OCCUPANCY || type == TRIT_COUNTER || type == TRIT_COND_COUNTER_VALUE;
}
/**
@ -571,6 +597,7 @@ enum TraceRestrictValueType {
TRVT_TRAIN_STATUS = 41,///< takes a TraceRestrictTrainStatusValueField
TRVT_REVERSE = 42,///< takes a TraceRestrictReverseValueField
TRVT_NEWS_CONTROL = 43,///< takes a TraceRestrictNewsControlField
TRVT_COUNTER_INDEX_INT = 44,///< takes a TraceRestrictCounterID, and an integer in the next item slot
};
/**
@ -696,6 +723,10 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR
out.value_type = TRVT_PERCENT;
break;
case TRIT_COND_COUNTER_VALUE:
out.value_type = TRVT_COUNTER_INDEX_INT;
break;
default:
NOT_REACHED();
break;
@ -720,6 +751,8 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR
out.value_type = TRVT_SPEED;
} else if (GetTraceRestrictType(item) == TRIT_NEWS_CONTROL) {
out.value_type = TRVT_NEWS_CONTROL;
} else if (GetTraceRestrictType(item) == TRIT_COUNTER) {
out.value_type = TRVT_COUNTER_INDEX_INT;
} else {
out.value_type = TRVT_NONE;
}
@ -823,6 +856,7 @@ void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint1
void TraceRestrictRemoveGroupID(GroupID index);
void TraceRestrictUpdateCompanyID(CompanyID old_company, CompanyID new_company);
void TraceRestrictRemoveSlotID(TraceRestrictSlotID index);
void TraceRestrictRemoveCounterID(TraceRestrictCounterID index);
void TraceRestrictRemoveVehicleFromAllSlots(VehicleID id);
void TraceRestrictTransferVehicleOccupantInAllSlots(VehicleID from, VehicleID to);
@ -871,4 +905,20 @@ struct TraceRestrictSlot : TraceRestrictSlotPool::PoolItem<&_tracerestrictslot_p
void DeIndex(VehicleID id);
};
/**
* Slot type, used for slot operations
*/
struct TraceRestrictCounter : TraceRestrictCounterPool::PoolItem<&_tracerestrictcounter_pool> {
int32 value = 0;
std::string name;
Owner owner;
TraceRestrictCounter(CompanyID owner = INVALID_COMPANY)
{
this->owner = owner;
}
void UpdateValue(int32 new_value);
};
#endif /* TRACERESTRICT_H */

@ -69,6 +69,7 @@ enum TraceRestrictWindowWidgets {
TR_WIDGET_CONDFLAGS,
TR_WIDGET_COMPARATOR,
TR_WIDGET_SLOT_OP,
TR_WIDGET_COUNTER_OP,
TR_WIDGET_VALUE_INT,
TR_WIDGET_VALUE_DECIMAL,
TR_WIDGET_VALUE_DROPDOWN,
@ -100,6 +101,7 @@ enum PanelWidgets {
// Left
DPL_TYPE = 0,
DPL_COUNTER_OP,
DPL_BLANK,
// Left aux
@ -153,6 +155,7 @@ static const StringID _program_insert_str[] = {
STR_TRACE_RESTRICT_REVERSE,
STR_TRACE_RESTRICT_SPEED_RESTRICTION,
STR_TRACE_RESTRICT_NEWS_CONTROL,
STR_TRACE_RESTRICT_COUNTER_OP,
INVALID_STRING_ID
};
static const uint32 _program_insert_else_hide_mask = 8; ///< disable bitmask for else
@ -162,6 +165,7 @@ static const uint32 _program_wait_pbs_hide_mask = 0x100; ///< disable bitm
static const uint32 _program_slot_hide_mask = 0x200; ///< disable bitmask for slot
static const uint32 _program_reverse_hide_mask = 0x400; ///< disable bitmask for reverse
static const uint32 _program_speed_res_hide_mask = 0x800; ///< disable bitmask for speed restriction
static const uint32 _program_counter_hide_mask = 0x2000; ///< disable bitmask for counter
static const uint _program_insert_val[] = {
TRIT_COND_UNDEFINED, // if block
TRIT_COND_UNDEFINED | (TRCF_ELSE << 16), // elif block
@ -176,6 +180,7 @@ static const uint _program_insert_val[] = {
TRIT_REVERSE, // reverse
TRIT_SPEED_RESTRICTION, // speed restriction
TRIT_NEWS_CONTROL, // news control
TRIT_COUNTER, // counter operation
};
/** insert drop down list strings and values */
@ -392,6 +397,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG
STR_TRACE_RESTRICT_REVERSE,
STR_TRACE_RESTRICT_SPEED_RESTRICTION,
STR_TRACE_RESTRICT_NEWS_CONTROL,
STR_TRACE_RESTRICT_COUNTER_OP,
INVALID_STRING_ID,
};
static const uint val_action[] = {
@ -404,6 +410,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG
TRIT_REVERSE,
TRIT_SPEED_RESTRICTION,
TRIT_NEWS_CONTROL,
TRIT_COUNTER,
};
static const TraceRestrictDropDownListSet set_action = {
str_action, val_action,
@ -430,6 +437,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG
STR_TRACE_RESTRICT_VARIABLE_TRAIN_SLOT,
STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY,
STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY_REMAINING,
STR_TRACE_RESTRICT_VARIABLE_COUNTER_VALUE,
STR_TRACE_RESTRICT_VARIABLE_UNDEFINED,
INVALID_STRING_ID,
};
@ -454,6 +462,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG
TRIT_COND_TRAIN_IN_SLOT,
TRIT_COND_SLOT_OCCUPANCY | (TRSOCAF_OCCUPANTS << 16),
TRIT_COND_SLOT_OCCUPANCY | (TRSOCAF_REMAINING << 16),
TRIT_COND_COUNTER_VALUE,
TRIT_COND_UNDEFINED,
};
static const TraceRestrictDropDownListSet set_cond = {
@ -465,7 +474,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG
if (_settings_client.gui.show_adv_tracerestrict_features) {
*hide_mask = 0;
} else {
*hide_mask = is_conditional ? 0xE0000 : 0xF0;
*hide_mask = is_conditional ? 0x1E0000 : 0xF0;
}
}
return is_conditional ? &set_cond : &set_action;
@ -568,6 +577,46 @@ DropDownList GetSlotDropDownList(Owner owner, TraceRestrictSlotID slot_id, int &
return dlist;
}
/** Sort counters by their name */
static bool CounterNameSorter(const TraceRestrictCounter * const &a, const TraceRestrictCounter * const &b)
{
int r = strnatcmp(a->name.c_str(), b->name.c_str()); // Sort by name (natural sorting).
if (r == 0) return a->index < b->index;
return r < 0;
}
/**
* Get a DropDownList of the counter list
*/
DropDownList GetCounterDropDownList(Owner owner, TraceRestrictCounterID ctr_id, int &selected)
{
GUIList<const TraceRestrictCounter*> list;
DropDownList dlist;
for (const TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
if (ctr->owner == owner) {
list.push_back(ctr);
}
}
if (list.size() == 0) return dlist;
list.ForceResort();
list.Sort(&CounterNameSorter);
selected = -1;
for (size_t i = 0; i < list.size(); ++i) {
const TraceRestrictCounter *s = list[i];
if (ctr_id == s->index) selected = ctr_id;
DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TRACE_RESTRICT_COUNTER_NAME, s->index, false);
item->SetParam(0, s->index);
dlist.emplace_back(item);
}
return dlist;
}
static const StringID _cargo_cond_ops_str[] = {
STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_EQUALS,
STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_NOT_EQUALS,
@ -620,6 +669,22 @@ static const TraceRestrictDropDownListSet _slot_op_cond_ops = {
_slot_op_cond_ops_str, _slot_op_cond_ops_val,
};
static const StringID _counter_op_cond_ops_str[] = {
STR_TRACE_RESTRICT_COUNTER_INCREASE,
STR_TRACE_RESTRICT_COUNTER_DECREASE,
STR_TRACE_RESTRICT_COUNTER_SET,
INVALID_STRING_ID,
};
static const uint _counter_op_cond_ops_val[] = {
TRCCOF_INCREASE,
TRCCOF_DECREASE,
TRCCOF_SET,
};
/** counter operators dropdown list set */
static const TraceRestrictDropDownListSet _counter_op_cond_ops = {
_counter_op_cond_ops_str, _counter_op_cond_ops_val,
};
/**
* Get the StringID for a given CargoID @p cargo, or STR_NEWGRF_INVALID_CARGO
*/
@ -1114,6 +1179,26 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric
SetDParam(2, GetDropDownStringByValue(&_train_status_value, GetTraceRestrictValue(item)));
break;
case TRVT_COUNTER_INDEX_INT: {
assert(prog != nullptr);
assert(GetTraceRestrictType(item) == TRIT_COND_COUNTER_VALUE);
uint32 value = *(TraceRestrictProgram::InstructionAt(prog->items, index - 1) + 1);
SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
if (GetTraceRestrictValue(item) == INVALID_TRACE_RESTRICT_COUNTER_ID) {
instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COUNTER_STR;
SetDParam(1, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED);
SetDParam(2, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
SetDParam(3, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
SetDParam(4, value);
} else {
instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COUNTER;
SetDParam(1, GetTraceRestrictValue(item));
SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
SetDParam(3, value);
}
break;
}
default:
NOT_REACHED();
break;
@ -1277,6 +1362,36 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric
}
break;
case TRIT_COUNTER: {
uint32 value = *(TraceRestrictProgram::InstructionAt(prog->items, index - 1) + 1);
switch (static_cast<TraceRestrictCounterCondOpField>(GetTraceRestrictCondOp(item))) {
case TRCCOF_INCREASE:
instruction_string = STR_TRACE_RESTRICT_COUNTER_INCREASE_ITEM;
break;
case TRCCOF_DECREASE:
instruction_string = STR_TRACE_RESTRICT_COUNTER_DECREASE_ITEM;
break;
case TRCCOF_SET:
instruction_string = STR_TRACE_RESTRICT_COUNTER_SET_ITEM;
break;
default:
NOT_REACHED();
break;
}
if (GetTraceRestrictValue(item) == INVALID_TRACE_RESTRICT_COUNTER_ID) {
SetDParam(0, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED);
} else {
SetDParam(0, STR_TRACE_RESTRICT_COUNTER_NAME);
SetDParam(1, GetTraceRestrictValue(item));
}
SetDParam(2, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
SetDParam(3, value);
break;
}
default:
NOT_REACHED();
break;
@ -1400,7 +1515,7 @@ public:
if (ElseIfInsertionDryRun(false)) disabled &= ~_program_insert_or_if_hide_mask;
}
}
if (!_settings_client.gui.show_adv_tracerestrict_features) hidden |= _program_slot_hide_mask | _program_wait_pbs_hide_mask | _program_reverse_hide_mask | _program_speed_res_hide_mask;
if (!_settings_client.gui.show_adv_tracerestrict_features) hidden |= _program_slot_hide_mask | _program_wait_pbs_hide_mask | _program_reverse_hide_mask | _program_speed_res_hide_mask | _program_counter_hide_mask;
this->ShowDropDownListWithValue(&_program_insert, 0, true, TR_WIDGET_INSERT, disabled, hidden, 0);
break;
@ -1489,13 +1604,19 @@ public:
break;
}
case TR_WIDGET_COUNTER_OP: {
TraceRestrictItem item = this->GetSelected();
this->ShowDropDownListWithValue(&_counter_op_cond_ops, GetTraceRestrictCondOp(item), false, TR_WIDGET_COUNTER_OP, 0, 0, 0);
break;
}
case TR_WIDGET_VALUE_INT: {
TraceRestrictItem item = this->GetSelected();
TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type;
if (IsIntegerValueType(type)) {
SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true));
ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
} else if (type == TRVT_SLOT_INDEX_INT) {
} else if (type == TRVT_SLOT_INDEX_INT || type == TRVT_COUNTER_INDEX_INT) {
SetDParam(0, *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, this->selected_instruction - 1) + 1));
ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
}
@ -1595,6 +1716,13 @@ public:
break;
}
case TRVT_COUNTER_INDEX_INT: {
int selected;
DropDownList dlist = GetCounterDropDownList(this->GetOwner(), GetTraceRestrictValue(item), selected);
if (!dlist.empty()) ShowDropDownList(this, std::move(dlist), selected, TR_WIDGET_LEFT_AUX_DROPDOWN);
break;
}
default:
break;
}
@ -1670,7 +1798,7 @@ public:
ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO);
return;
}
} else if (type == TRVT_SLOT_INDEX_INT) {
} else if (type == TRVT_SLOT_INDEX_INT || type == TRVT_COUNTER_INDEX_INT) {
value = atoi(str);
TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_DUAL_ITEM, this->selected_instruction - 1, value, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
return;
@ -1691,7 +1819,7 @@ public:
if (widget == TR_WIDGET_VALUE_DROPDOWN || widget == TR_WIDGET_LEFT_AUX_DROPDOWN) {
TraceRestrictTypePropertySet type = GetTraceRestrictTypeProperties(item);
if (this->value_drop_down_is_company || type.value_type == TRVT_GROUP_INDEX || type.value_type == TRVT_SLOT_INDEX || type.value_type == TRVT_SLOT_INDEX_INT) {
if (this->value_drop_down_is_company || type.value_type == TRVT_GROUP_INDEX || type.value_type == TRVT_SLOT_INDEX || type.value_type == TRVT_SLOT_INDEX_INT || type.value_type == TRVT_COUNTER_INDEX_INT) {
// this is a special company drop-down or group/slot-index drop-down
SetTraceRestrictValue(item, index);
TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
@ -1747,7 +1875,8 @@ public:
}
case TR_WIDGET_COMPARATOR:
case TR_WIDGET_SLOT_OP: {
case TR_WIDGET_SLOT_OP:
case TR_WIDGET_COUNTER_OP: {
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;
@ -2023,7 +2152,7 @@ public:
TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type;
if (IsIntegerValueType(type)) {
SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true));
} else if (type == TRVT_SLOT_INDEX_INT) {
} else if (type == TRVT_SLOT_INDEX_INT || type == TRVT_COUNTER_INDEX_INT) {
SetDParam(0, *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, this->selected_instruction - 1) + 1));
}
break;
@ -2068,7 +2197,7 @@ public:
case TR_WIDGET_LEFT_AUX_DROPDOWN: {
TraceRestrictItem item = this->GetSelected();
TraceRestrictTypePropertySet type = GetTraceRestrictTypeProperties(item);
if (type.value_type == TRVT_SLOT_INDEX_INT) {
if (type.value_type == TRVT_SLOT_INDEX_INT || type.value_type == TRVT_COUNTER_INDEX_INT) {
SetDParam(0, GetTraceRestrictValue(item));
}
break;
@ -2239,6 +2368,7 @@ private:
this->RaiseWidget(TR_WIDGET_CONDFLAGS);
this->RaiseWidget(TR_WIDGET_COMPARATOR);
this->RaiseWidget(TR_WIDGET_SLOT_OP);
this->RaiseWidget(TR_WIDGET_COUNTER_OP);
this->RaiseWidget(TR_WIDGET_VALUE_INT);
this->RaiseWidget(TR_WIDGET_VALUE_DECIMAL);
this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN);
@ -2259,6 +2389,7 @@ private:
this->DisableWidget(TR_WIDGET_CONDFLAGS);
this->DisableWidget(TR_WIDGET_COMPARATOR);
this->DisableWidget(TR_WIDGET_SLOT_OP);
this->DisableWidget(TR_WIDGET_COUNTER_OP);
this->DisableWidget(TR_WIDGET_VALUE_INT);
this->DisableWidget(TR_WIDGET_VALUE_DECIMAL);
this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN);
@ -2576,6 +2707,36 @@ private:
GetDropDownStringByValue(&_news_control_value, GetTraceRestrictValue(item));
break;
case TRVT_COUNTER_INDEX_INT: {
right_sel->SetDisplayedPlane(DPR_VALUE_INT);
left_aux_sel->SetDisplayedPlane(DPLA_DROPDOWN);
this->EnableWidget(TR_WIDGET_VALUE_INT);
if (!IsTraceRestrictConditional(item)) {
left_sel->SetDisplayedPlane(DPL_COUNTER_OP);
this->EnableWidget(TR_WIDGET_COUNTER_OP);
this->GetWidget<NWidgetCore>(TR_WIDGET_COUNTER_OP)->widget_data =
GetDropDownStringByValue(&_counter_op_cond_ops, GetTraceRestrictCondOp(item));
}
for (const TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
if (ctr->owner == this->GetOwner()) {
this->EnableWidget(TR_WIDGET_LEFT_AUX_DROPDOWN);
break;
}
}
switch (GetTraceRestrictValue(item)) {
case INVALID_TRACE_RESTRICT_COUNTER_ID:
this->GetWidget<NWidgetCore>(TR_WIDGET_LEFT_AUX_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_VARIABLE_UNDEFINED;
break;
default:
this->GetWidget<NWidgetCore>(TR_WIDGET_LEFT_AUX_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_COUNTER_NAME;
break;
}
break;
}
default:
break;
}
@ -2728,6 +2889,8 @@ static const NWidgetPart _nested_program_widgets[] = {
NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT),
NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_COND), SetMinimalSize(124, 12), SetFill(1, 0),
SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_COUNTER_OP), SetMinimalSize(124, 12), SetFill(1, 0),
SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COUNTER_OP_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(),
@ -3488,3 +3651,343 @@ void DeleteTraceRestrictSlotHighlightOfVehicle(const Vehicle *v)
TraceRestrictSlotWindow *w = FindTraceRestrictSlotWindow(v->owner);
if (w != nullptr) w->UnselectVehicle(v->index);
}
/** Counter GUI widget IDs */
enum TraceRestrictCounterWindowWidgets {
WID_TRCL_CAPTION,
WID_TRCL_LIST_COUNTERS,
WID_TRCL_LIST_COUNTERS_SCROLLBAR,
WID_TRCL_CREATE_COUNTER,
WID_TRCL_DELETE_COUNTER,
WID_TRCL_RENAME_COUNTER,
WID_TRCL_SET_COUNTER_VALUE,
};
static const NWidgetPart _nested_counter_widgets[] = {
NWidget(NWID_HORIZONTAL), // Window header
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_TRCL_CAPTION), SetDataTip(STR_TRACE_RESTRICT_COUNTER_CAPTION, STR_NULL),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(NWID_VERTICAL),
//NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetFill(1, 0), EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_MATRIX, COLOUR_GREY, WID_TRCL_LIST_COUNTERS), SetMatrixDataTip(1, 0, STR_TRACE_RESTRICT_COUNTER_GUI_LIST_TOOLTIP),
SetFill(1, 1), SetResize(1, 1), SetScrollbar(WID_TRCL_LIST_COUNTERS_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_TRCL_LIST_COUNTERS_SCROLLBAR),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TRCL_CREATE_COUNTER), SetMinimalSize(75, 12), SetFill(1, 0),
SetDataTip(STR_TRACE_RESTRICT_COUNTER_CREATE, STR_TRACE_RESTRICT_COUNTER_CREATE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TRCL_DELETE_COUNTER), SetMinimalSize(75, 12), SetFill(1, 0),
SetDataTip(STR_TRACE_RESTRICT_COUNTER_DELETE, STR_TRACE_RESTRICT_COUNTER_DELETE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TRCL_RENAME_COUNTER), SetMinimalSize(75, 12), SetFill(1, 0),
SetDataTip(STR_TRACE_RESTRICT_COUNTER_RENAME, STR_TRACE_RESTRICT_COUNTER_RENAME_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TRCL_SET_COUNTER_VALUE), SetMinimalSize(75, 12), SetFill(1, 0),
SetDataTip(STR_TRACE_RESTRICT_COUNTER_SET_VALUE, STR_TRACE_RESTRICT_COUNTER_SET_VALUE_TOOLTIP),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
EndContainer(),
};
class TraceRestrictCounterWindow : public Window {
private:
enum QueryTextOperation {
QTO_RENAME,
QTO_SET_VALUE,
};
CompanyID owner;
QueryTextOperation qto; ///< Active query text operation
TraceRestrictCounterID ctr_qt_op; ///< Counter being adjusted in query text operation, INVALID_TRACE_RESTRICT_COUNTER_ID if none
TraceRestrictCounterID ctr_confirm; ///< Counter awaiting delete confirmation
TraceRestrictCounterID selected; ///< Selected counter
GUIList<const TraceRestrictCounter*> ctrs; ///< List of slots
uint tiny_step_height; ///< Step height for the counter list
uint value_col_width; ///< Value column width
Scrollbar *sb;
void BuildCounterList()
{
if (!this->ctrs.NeedRebuild()) return;
this->ctrs.clear();
for (const TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
if (ctr->owner == this->owner) {
this->ctrs.push_back(ctr);
}
}
this->ctrs.ForceResort();
this->ctrs.Sort(&CounterNameSorter);
this->ctrs.shrink_to_fit();
this->ctrs.RebuildDone();
}
/**
* Compute tiny_step_height and column_size
* @return Total width required for the group list.
*/
uint ComputeInfoSize()
{
SetDParamMaxValue(0, 9999, 3);
Dimension dim = GetStringBoundingBox(STR_JUST_COMMA);
this->tiny_step_height = dim.height + WD_MATRIX_TOP;
this->value_col_width = dim.width;
return WD_FRAMERECT_LEFT + 8 +
170 + 8 +
dim.width + 8 +
WD_FRAMERECT_RIGHT;
}
/**
* Draw a row in the slot list.
* @param y Top of the row.
* @param left Left of the row.
* @param right Right of the row.
* @param g_id Group to list.
*/
void DrawCounterInfo(int y, int left, int right, TraceRestrictCounterID ctr_id) const
{
/* draw the selected counter in white, else we draw it in black */
TextColour colour = ctr_id == this->selected ? TC_WHITE : TC_BLACK;
bool rtl = _current_text_dir == TD_RTL;
SetDParam(0, ctr_id);
DrawString(left + WD_FRAMERECT_LEFT + 8 + (rtl ? this->value_col_width + 8 : 0),
right - WD_FRAMERECT_RIGHT - 8 - (rtl ? 0 : this->value_col_width + 8),
y, STR_TRACE_RESTRICT_COUNTER_NAME, colour);
SetDParam(0, TraceRestrictCounter::Get(ctr_id)->value);
DrawString(rtl ? left + WD_FRAMERECT_LEFT + 8 : right - WD_FRAMERECT_RIGHT - 8 - this->value_col_width,
rtl ? left + WD_FRAMERECT_LEFT + 8 + this->value_col_width : right - WD_FRAMERECT_RIGHT - 8,
y, STR_JUST_COMMA, colour, SA_RIGHT | SA_FORCE);
}
public:
TraceRestrictCounterWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
{
this->owner = (CompanyID)window_number;
this->CreateNestedTree();
this->sb = this->GetScrollbar(WID_TRCL_LIST_COUNTERS_SCROLLBAR);
this->ctr_qt_op = INVALID_TRACE_RESTRICT_COUNTER_ID;
this->ctr_confirm = INVALID_TRACE_RESTRICT_COUNTER_ID;
this->selected = INVALID_TRACE_RESTRICT_COUNTER_ID;
this->ctrs.ForceRebuild();
this->ctrs.NeedResort();
this->BuildCounterList();
this->FinishInitNested(window_number);
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
switch (widget) {
case WID_TRCL_LIST_COUNTERS: {
size->width = max<uint>(size->width, this->ComputeInfoSize());
resize->height = this->tiny_step_height;
size->height = max<uint>(size->height, 8 * resize->height);
break;
}
}
}
/**
* Some data on this window has become invalid.
* @param data Information about the changed data.
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
*/
virtual void OnInvalidateData(int data = 0, bool gui_scope = true) override
{
if (data == 0) {
/* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
this->ctrs.ForceRebuild();
} else {
this->ctrs.ForceResort();
}
if (this->ctr_qt_op != INVALID_TRACE_RESTRICT_COUNTER_ID && this->ctr_qt_op != NEW_TRACE_RESTRICT_COUNTER_ID &&
!TraceRestrictCounter::IsValidID(this->ctr_qt_op)) {
DeleteWindowByClass(WC_QUERY_STRING);
this->ctr_qt_op = INVALID_TRACE_RESTRICT_COUNTER_ID;
}
if (this->selected != INVALID_TRACE_RESTRICT_COUNTER_ID && !TraceRestrictCounter::IsValidID(this->selected)) {
this->selected = INVALID_TRACE_RESTRICT_COUNTER_ID;
}
this->SetDirty();
}
virtual void OnPaint() override
{
this->BuildCounterList();
this->sb->SetCount(this->ctrs.size());
/* Disable the counter specific functions when no counter is selected */
this->SetWidgetsDisabledState(this->selected == INVALID_TRACE_RESTRICT_COUNTER_ID || _local_company != this->owner,
WID_TRCL_DELETE_COUNTER,
WID_TRCL_RENAME_COUNTER,
WID_TRCL_SET_COUNTER_VALUE,
WIDGET_LIST_END);
/* Disable remaining buttons for non-local companies
* Needed while changing _local_company, eg. by cheats
* All procedures (eg. move vehicle to a slot)
* verify, whether you are the owner of the vehicle,
* so it doesn't have to be disabled
*/
this->SetWidgetsDisabledState(_local_company != this->owner,
WID_TRCL_CREATE_COUNTER,
WIDGET_LIST_END);
this->DrawWidgets();
}
virtual void DrawWidget(const Rect &r, int widget) const override
{
switch (widget) {
case WID_TRCL_LIST_COUNTERS: {
int y1 = r.top + WD_FRAMERECT_TOP;
int max = min(this->sb->GetPosition() + this->sb->GetCapacity(), this->ctrs.size());
for (int i = this->sb->GetPosition(); i < max; ++i) {
const TraceRestrictCounter *ctr = this->ctrs[i];
assert(ctr->owner == this->owner);
DrawCounterInfo(y1, r.left, r.right, ctr->index);
y1 += this->tiny_step_height;
}
break;
}
}
}
static void DeleteCounterCallback(Window *win, bool confirmed)
{
if (confirmed) {
TraceRestrictCounterWindow *w = (TraceRestrictCounterWindow*)win;
w->selected = INVALID_TRACE_RESTRICT_COUNTER_ID;
DoCommandP(0, w->ctr_confirm, 0, CMD_DELETE_TRACERESTRICT_COUNTER | CMD_MSG(STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_DELETE));
}
}
virtual void OnClick(Point pt, int widget, int click_count) override
{
switch (widget) {
case WID_TRCL_LIST_COUNTERS: { // Matrix
uint id_s = this->sb->GetScrolledRowFromWidget(pt.y, this, WID_TRCL_LIST_COUNTERS, 0, this->tiny_step_height);
if (id_s >= this->ctrs.size()) return;
this->selected = this->ctrs[id_s]->index;
this->SetDirty();
break;
}
case WID_TRCL_CREATE_COUNTER: { // Create a new counter
this->ShowCreateCounterWindow();
break;
}
case WID_TRCL_DELETE_COUNTER: { // Delete the selected counter
this->ctr_confirm = this->selected;
ShowQuery(STR_TRACE_RESTRICT_COUNTER_QUERY_DELETE_CAPTION, STR_TRACE_RESTRICT_COUNTER_DELETE_QUERY_TEXT, this, DeleteCounterCallback);
break;
}
case WID_TRCL_RENAME_COUNTER: // Rename the selected counter
this->ShowRenameCounterWindow(this->selected);
break;
case WID_TRCL_SET_COUNTER_VALUE:
this->ShowSetCounterValueWindow(this->selected);
break;
}
}
virtual void OnQueryTextFinished(char *str) override
{
if (str != nullptr) {
switch (this->qto) {
case QTO_RENAME:
if (this->ctr_qt_op == NEW_TRACE_RESTRICT_COUNTER_ID) {
DoCommandP(0, 0, 0, CMD_CREATE_TRACERESTRICT_COUNTER | CMD_MSG(STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_CREATE), nullptr, str);
} else {
DoCommandP(0, this->ctr_qt_op, 0, CMD_ALTER_TRACERESTRICT_COUNTER | CMD_MSG(STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_MODIFY), nullptr, str);
}
break;
case QTO_SET_VALUE:
if (!StrEmpty(str)) DoCommandP(0, this->ctr_qt_op | (1 << 16), atoi(str), CMD_ALTER_TRACERESTRICT_COUNTER | CMD_MSG(STR_TRACE_RESTRICT_ERROR_COUNTER_CAN_T_MODIFY));
break;
}
}
this->ctr_qt_op = INVALID_TRACE_RESTRICT_COUNTER_ID;
}
virtual void OnResize() override
{
this->sb->SetCapacityFromWidget(this, WID_TRCL_LIST_COUNTERS);
}
virtual void OnGameTick() override
{
if (this->ctrs.NeedResort()) {
this->SetDirty();
}
}
void ShowRenameCounterWindow(TraceRestrictCounterID ctr_id)
{
assert(TraceRestrictCounter::IsValidID(ctr_id));
this->qto = QTO_RENAME;
this->ctr_qt_op = ctr_id;
SetDParam(0, ctr_id);
ShowQueryString(STR_TRACE_RESTRICT_COUNTER_NAME, STR_TRACE_RESTRICT_COUNTER_RENAME_CAPTION, MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
}
void ShowSetCounterValueWindow(TraceRestrictCounterID ctr_id)
{
assert(TraceRestrictCounter::IsValidID(ctr_id));
this->qto = QTO_SET_VALUE;
this->ctr_qt_op = ctr_id;
SetDParam(0, TraceRestrictCounter::Get(ctr_id)->value);
ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_COUNTER_SET_VALUE_CAPTION, 5, this, CS_NUMERAL, QSF_ENABLE_DEFAULT);
}
void ShowCreateCounterWindow()
{
this->qto = QTO_RENAME;
this->ctr_qt_op = NEW_TRACE_RESTRICT_COUNTER_ID;
ShowQueryString(STR_EMPTY, STR_TRACE_RESTRICT_COUNTER_CREATE_CAPTION, MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
}
};
static WindowDesc _counter_window_desc(
WDP_AUTO, "list_tr_counters", 525, 246,
WC_TRACE_RESTRICT_COUNTERS, WC_NONE,
0,
_nested_counter_widgets, lengthof(_nested_counter_widgets)
);
/**
* Show the trace restrict counter window for the given company.
* @param company The company to show the window for.
*/
void ShowTraceRestrictCounterWindow(CompanyID company)
{
if (!Company::IsValidID(company)) return;
AllocateWindowDescFront<TraceRestrictCounterWindow>(&_counter_window_desc, (WindowNumber)company);
}

@ -4123,10 +4123,10 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
const Trackdir dir = FindFirstTrackdir(trackdirbits);
if (HasSignalOnTrack(gp.new_tile, TrackdirToTrack(dir))) {
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.new_tile, TrackdirToTrack(dir));
if (prog && prog->actions_used_flags & (TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_RELEASE_FRONT | TRPAUF_REVERSE | TRPAUF_SPEED_RESTRICTION)) {
if (prog && prog->actions_used_flags & (TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_RELEASE_FRONT | TRPAUF_REVERSE | TRPAUF_SPEED_RESTRICTION | TRPAUF_CHANGE_COUNTER)) {
TraceRestrictProgramResult out;
TraceRestrictProgramInput input(gp.new_tile, dir, nullptr, nullptr);
input.permitted_slot_operations = TRPISP_ACQUIRE | TRPISP_RELEASE_FRONT;
input.permitted_slot_operations = TRPISP_ACQUIRE | TRPISP_RELEASE_FRONT | TRPISP_CHANGE_COUNTER;
prog->Execute(v, input, out);
if (out.flags & TRPRF_REVERSE && GetSignalType(gp.new_tile, TrackdirToTrack(dir)) == SIGTYPE_PBS &&
!HasSignalOnTrackdir(gp.new_tile, dir)) {

@ -367,6 +367,7 @@ DropDownList BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplac
}
if (this->vli.vtype == VEH_TRAIN && _settings_client.gui.show_adv_tracerestrict_features) {
list.emplace_back(new DropDownListStringItem(STR_TRACE_RESTRICT_SLOT_MANAGE, ADI_TRACERESTRICT_SLOT_MGMT, false));
list.emplace_back(new DropDownListStringItem(STR_TRACE_RESTRICT_COUNTER_MANAGE, ADI_TRACERESTRICT_COUNTER_MGMT, false));
}
if (change_order_str != 0) {
list.emplace_back(new DropDownListStringItem(change_order_str, ADI_CHANGE_ORDER, disable));
@ -2115,6 +2116,12 @@ public:
break;
}
case ADI_TRACERESTRICT_COUNTER_MGMT: {
extern void ShowTraceRestrictCounterWindow(CompanyID company);
ShowTraceRestrictCounterWindow(this->owner);
break;
}
default: NOT_REACHED();
}
break;

@ -51,6 +51,7 @@ struct BaseVehicleListWindow : public Window {
ADI_CHANGE_ORDER,
ADI_CREATE_GROUP,
ADI_TRACERESTRICT_SLOT_MGMT,
ADI_TRACERESTRICT_COUNTER_MGMT,
};
static const StringID vehicle_depot_name[];

@ -733,6 +733,12 @@ enum WindowClass {
*/
WC_TRACE_RESTRICT_SLOTS,
/**
* Trace restrict counter window; %Window numbers:
* - Packed value = #SlotListWidgets / #VehicleListWidgets
*/
WC_TRACE_RESTRICT_COUNTERS,
/**
* Programmable pre-signals window
*/

Loading…
Cancel
Save