/*
* 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.cpp Main file for Trace Restrict */
# include "stdafx.h"
# include "tracerestrict.h"
# include "train.h"
# include "core/bitmath_func.hpp"
# include "core/pool_func.hpp"
# include "command_func.h"
# include "company_func.h"
# include "viewport_func.h"
# include "window_func.h"
# include "order_base.h"
# include "cargotype.h"
# include "group.h"
# include "string_func.h"
# include "pathfinder/yapf/yapf_cache.h"
# include "scope_info.h"
# include "vehicle_func.h"
# include "date_func.h"
# include <vector>
# include <algorithm>
# include "safeguards.h"
/** @file
*
* Trace Restrict Data Storage Model Notes :
*
* Signals may have 0 , 1 or 2 trace restrict programs attached to them ,
* up to one for each track . Two - way signals share the same program .
*
* The mapping between signals and programs is defined in terms of
* TraceRestrictRefId to TraceRestrictProgramID ,
* where TraceRestrictRefId is formed of the tile index and track ,
* and TraceRestrictProgramID is an index into the program pool .
*
* If one or more mappings exist for a given signal tile , bit 12 of M3 will be set to 1.
* This is updated whenever mappings are added / removed for that tile . This is to avoid
* needing to do a mapping lookup for the common case where there is no trace restrict
* program mapping for the given tile .
*
* Programs in the program pool are refcounted based on the number of mappings which exist .
* When this falls to 0 , the program is deleted from the pool .
* If a program has a refcount greater than 1 , it is a shared program .
*
* In all cases , an empty program is evaluated the same as the absence of a program .
* Therefore it is not necessary to store mappings to empty unshared programs .
* Any editing action which would otherwise result in a mapping to an empty program
* which has no other references , instead removes the mapping .
* This is not done for shared programs as this would delete the shared aspect whenever
* the program became empty .
*
* Special case : In the case where an empty program with refcount 2 has one of its
* mappings removed , the other mapping is left pointing to an empty unshared program .
* This other mapping is then removed by performing a linear search of the mappings ,
* and removing the reference to that program ID .
*/
TraceRestrictProgramPool _tracerestrictprogram_pool ( " TraceRestrictProgram " ) ;
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
* TODO : use a more efficient container / indirection mechanism
*/
TraceRestrictMapping _tracerestrictprogram_mapping ;
/**
* List of pre - defined pathfinder penalty values
* This is indexed by TraceRestrictPathfinderPenaltyPresetIndex
*/
const uint16 _tracerestrict_pathfinder_penalty_preset_values [ ] = {
500 ,
2000 ,
8000 ,
} ;
static_assert ( lengthof ( _tracerestrict_pathfinder_penalty_preset_values ) = = TRPPPI_END ) ;
/**
* This should be used when all pools have been or are immediately about to be also cleared
* Calling this at other times will leave dangling refcounts
*/
void ClearTraceRestrictMapping ( ) {
_tracerestrictprogram_mapping . clear ( ) ;
}
/**
* Flags used for the program execution condition stack
* Each ' if ' pushes onto the stack
* Each ' end if ' pops from the stack
* Elif / orif / else may modify the stack top
*/
enum TraceRestrictCondStackFlags {
TRCSF_DONE_IF = 1 < < 0 , ///< The if/elif/else is "done", future elif/else branches will not be executed
TRCSF_SEEN_ELSE = 1 < < 1 , ///< An else branch has been seen already, error if another is seen afterwards
TRCSF_ACTIVE = 1 < < 2 , ///< The condition is currently active
TRCSF_PARENT_INACTIVE = 1 < < 3 , ///< The parent condition is not active, thus this condition is also not active
} ;
DECLARE_ENUM_AS_BIT_SET ( TraceRestrictCondStackFlags )
/**
* Helper function to handle condition stack manipulatoin
*/
static void HandleCondition ( std : : vector < 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 ;
}
}
/**
* Integer condition testing
* Test value op condvalue
*/
static bool TestCondition ( int value , TraceRestrictCondOp condop , int condvalue )
{
switch ( condop ) {
case TRCO_IS :
return value = = condvalue ;
case TRCO_ISNOT :
return value ! = condvalue ;
case TRCO_LT :
return value < condvalue ;
case TRCO_LTE :
return value < = condvalue ;
case TRCO_GT :
return value > condvalue ;
case TRCO_GTE :
return value > = condvalue ;
default :
NOT_REACHED ( ) ;
return false ;
}
}
/**
* Binary condition testing helper function
*/
static bool TestBinaryConditionCommon ( TraceRestrictItem item , bool input )
{
switch ( GetTraceRestrictCondOp ( item ) ) {
case TRCO_IS :
return input ;
case TRCO_ISNOT :
return ! input ;
default :
NOT_REACHED ( ) ;
return false ;
}
}
/**
* Test order condition
* @ p order may be nullptr
*/
static bool TestOrderCondition ( const Order * order , TraceRestrictItem item )
{
bool result = false ;
if ( order ) {
DestinationID condvalue = GetTraceRestrictValue ( item ) ;
switch ( static_cast < TraceRestrictOrderCondAuxField > ( GetTraceRestrictAuxField ( item ) ) ) {
case TROCAF_STATION :
result = ( order - > IsType ( OT_GOTO_STATION ) | | order - > IsType ( OT_LOADING_ADVANCE ) )
& & order - > GetDestination ( ) = = condvalue ;
break ;
case TROCAF_WAYPOINT :
result = order - > IsType ( OT_GOTO_WAYPOINT ) & & order - > GetDestination ( ) = = condvalue ;
break ;
case TROCAF_DEPOT :
result = order - > IsType ( OT_GOTO_DEPOT ) & & order - > GetDestination ( ) = = condvalue ;
break ;
default :
NOT_REACHED ( ) ;
}
}
return TestBinaryConditionCommon ( item , result ) ;
}
/**
* Test station condition
*/
static bool TestStationCondition ( StationID station , TraceRestrictItem item )
{
bool result = ( GetTraceRestrictAuxField ( item ) = = TROCAF_STATION ) & & ( station = = GetTraceRestrictValue ( item ) ) ;
return TestBinaryConditionCommon ( item , result ) ;
}
/**
* Execute program on train and store results in out
* @ p v may not be nullptr
* @ p out should be zero - initialised
*/
void TraceRestrictProgram : : Execute ( const Train * v , const TraceRestrictProgramInput & input , TraceRestrictProgramResult & out ) const
{
// static to avoid needing to re-alloc/resize on each execution
static std : : vector < TraceRestrictCondStackFlags > condstack ;
condstack . clear ( ) ;
byte have_previous_signal = 0 ;
TileIndex previous_signal_tile [ 2 ] ;
size_t size = this - > items . size ( ) ;
for ( size_t i = 0 ; i < size ; i + + ) {
TraceRestrictItem item = this - > items [ i ] ;
TraceRestrictItemType type = GetTraceRestrictType ( item ) ;
if ( IsTraceRestrictConditional ( item ) ) {
TraceRestrictCondFlags condflags = GetTraceRestrictCondFlags ( item ) ;
TraceRestrictCondOp condop = GetTraceRestrictCondOp ( item ) ;
if ( type = = TRIT_COND_ENDIF ) {
assert ( ! condstack . empty ( ) ) ;
if ( condflags & TRCF_ELSE ) {
// else
assert ( ! ( condstack . back ( ) & TRCSF_SEEN_ELSE ) ) ;
HandleCondition ( condstack , condflags , true ) ;
condstack . back ( ) | = TRCSF_SEEN_ELSE ;
} else {
// end if
condstack . pop_back ( ) ;
}
} else {
uint16 condvalue = GetTraceRestrictValue ( item ) ;
bool result = false ;
switch ( type ) {
case TRIT_COND_UNDEFINED :
result = false ;
break ;
case TRIT_COND_TRAIN_LENGTH :
result = TestCondition ( CeilDiv ( v - > gcache . cached_total_length , TILE_SIZE ) , condop , condvalue ) ;
break ;
case TRIT_COND_MAX_SPEED :
result = TestCondition ( v - > GetDisplayMaxSpeed ( ) , condop , condvalue ) ;
break ;
case TRIT_COND_CURRENT_ORDER :
result = TestOrderCondition ( & ( v - > current_order ) , item ) ;
break ;
case TRIT_COND_NEXT_ORDER : {
if ( v - > orders . list = = nullptr ) break ;
if ( v - > orders . list - > GetNumOrders ( ) = = 0 ) break ;
const Order * current_order = v - > GetOrder ( v - > cur_real_order_index ) ;
for ( const Order * order = v - > orders . list - > GetNext ( current_order ) ; order ! = current_order ; order = v - > orders . list - > GetNext ( order ) ) {
if ( order - > IsGotoOrder ( ) ) {
result = TestOrderCondition ( order , item ) ;
break ;
}
}
break ;
}
case TRIT_COND_LAST_STATION :
result = TestStationCondition ( v - > last_station_visited , item ) ;
break ;
case TRIT_COND_CARGO : {
bool have_cargo = false ;
for ( const Vehicle * v_iter = v ; v_iter ! = nullptr ; v_iter = v_iter - > Next ( ) ) {
if ( v_iter - > cargo_type = = GetTraceRestrictValue ( item ) & & v_iter - > cargo_cap > 0 ) {
have_cargo = true ;
break ;
}
}
result = TestBinaryConditionCommon ( item , have_cargo ) ;
break ;
}
case TRIT_COND_ENTRY_DIRECTION : {
bool direction_match ;
switch ( GetTraceRestrictValue ( item ) ) {
case TRNTSV_NE :
case TRNTSV_SE :
case TRNTSV_SW :
case TRNTSV_NW :
direction_match = ( static_cast < DiagDirection > ( GetTraceRestrictValue ( item ) ) = = TrackdirToExitdir ( ReverseTrackdir ( input . trackdir ) ) ) ;
break ;
case TRDTSV_FRONT :
direction_match = IsTileType ( input . tile , MP_RAILWAY ) & & HasSignalOnTrackdir ( input . tile , input . trackdir ) ;
break ;
case TRDTSV_BACK :
direction_match = IsTileType ( input . tile , MP_RAILWAY ) & & ! HasSignalOnTrackdir ( input . tile , input . trackdir ) ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
result = TestBinaryConditionCommon ( item , direction_match ) ;
break ;
}
case TRIT_COND_PBS_ENTRY_SIGNAL : {
// TRIT_COND_PBS_ENTRY_SIGNAL value type uses the next slot
i + + ;
TraceRestrictPBSEntrySignalAuxField mode = static_cast < TraceRestrictPBSEntrySignalAuxField > ( GetTraceRestrictAuxField ( item ) ) ;
assert ( mode = = TRPESAF_VEH_POS | | mode = = TRPESAF_RES_END ) ;
uint32_t signal_tile = this - > items [ i ] ;
if ( ! HasBit ( have_previous_signal , mode ) ) {
if ( input . previous_signal_callback ) {
previous_signal_tile [ mode ] = input . previous_signal_callback ( v , input . previous_signal_ptr , mode ) ;
} else {
previous_signal_tile [ mode ] = INVALID_TILE ;
}
SetBit ( have_previous_signal , mode ) ;
}
bool match = ( signal_tile ! = INVALID_TILE )
& & ( previous_signal_tile [ mode ] = = signal_tile ) ;
result = TestBinaryConditionCommon ( item , match ) ;
break ;
}
case TRIT_COND_TRAIN_GROUP : {
result = TestBinaryConditionCommon ( item , GroupIsInGroup ( v - > group_id , GetTraceRestrictValue ( item ) ) ) ;
break ;
}
case TRIT_COND_TRAIN_IN_SLOT : {
const TraceRestrictSlot * slot = TraceRestrictSlot : : GetIfValid ( GetTraceRestrictValue ( item ) ) ;
result = TestBinaryConditionCommon ( item , slot ! = nullptr & & slot - > IsOccupant ( v - > index ) ) ;
break ;
}
case TRIT_COND_SLOT_OCCUPANCY : {
// TRIT_COND_SLOT_OCCUPANCY value type uses the next slot
i + + ;
uint32_t value = this - > items [ i ] ;
const TraceRestrictSlot * slot = TraceRestrictSlot : : GetIfValid ( GetTraceRestrictValue ( item ) ) ;
switch ( static_cast < TraceRestrictSlotOccupancyCondAuxField > ( GetTraceRestrictAuxField ( item ) ) ) {
case TRSOCAF_OCCUPANTS :
result = TestCondition ( slot ! = nullptr ? slot - > occupants . size ( ) : 0 , condop , value ) ;
break ;
case TRSOCAF_REMAINING :
result = TestCondition ( slot ! = nullptr ? slot - > max_occupancy - slot - > occupants . size ( ) : 0 , condop , value ) ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
}
case TRIT_COND_PHYS_PROP : {
switch ( static_cast < TraceRestrictPhysPropCondAuxField > ( GetTraceRestrictAuxField ( item ) ) ) {
case TRPPCAF_WEIGHT :
result = TestCondition ( v - > gcache . cached_weight , condop , condvalue ) ;
break ;
case TRPPCAF_POWER :
result = TestCondition ( v - > gcache . cached_power , condop , condvalue ) ;
break ;
case TRPPCAF_MAX_TE :
result = TestCondition ( v - > gcache . cached_max_te / 1000 , condop , condvalue ) ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
}
case TRIT_COND_PHYS_RATIO : {
switch ( static_cast < TraceRestrictPhysPropRatioCondAuxField > ( GetTraceRestrictAuxField ( item ) ) ) {
case TRPPRCAF_POWER_WEIGHT :
result = TestCondition ( std : : min < uint > ( UINT16_MAX , ( 100 * v - > gcache . cached_power ) / std : : max < uint > ( 1 , v - > gcache . cached_weight ) ) , condop , condvalue ) ;
break ;
case TRPPRCAF_MAX_TE_WEIGHT :
result = TestCondition ( std : : min < uint > ( UINT16_MAX , ( v - > gcache . cached_max_te / 10 ) / std : : max < uint > ( 1 , v - > gcache . cached_weight ) ) , condop , condvalue ) ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
}
case TRIT_COND_TRAIN_OWNER : {
result = TestBinaryConditionCommon ( item , v - > owner = = condvalue ) ;
break ;
}
case TRIT_COND_TRAIN_STATUS : {
bool has_status = false ;
switch ( static_cast < TraceRestrictTrainStatusValueField > ( GetTraceRestrictValue ( item ) ) ) {
case TRTSVF_EMPTY :
has_status = true ;
for ( const Vehicle * v_iter = v ; v_iter ! = nullptr ; v_iter = v_iter - > Next ( ) ) {
if ( v_iter - > cargo . StoredCount ( ) > 0 ) {
has_status = false ;
break ;
}
}
break ;
case TRTSVF_FULL :
has_status = true ;
for ( const Vehicle * v_iter = v ; v_iter ! = nullptr ; v_iter = v_iter - > Next ( ) ) {
if ( v_iter - > cargo . StoredCount ( ) < v_iter - > cargo_cap ) {
has_status = false ;
break ;
}
}
break ;
case TRTSVF_BROKEN_DOWN :
has_status = v - > flags & VRF_IS_BROKEN ;
break ;
case TRTSVF_NEEDS_REPAIR :
has_status = v - > critical_breakdown_count > 0 ;
break ;
case TRTSVF_REVERSING :
has_status = v - > reverse_distance > 0 | | HasBit ( v - > flags , VRF_REVERSING ) ;
break ;
case TRTSVF_HEADING_TO_STATION_WAYPOINT :
has_status = v - > current_order . IsType ( OT_GOTO_STATION ) | | v - > current_order . IsType ( OT_GOTO_WAYPOINT ) ;
break ;
case TRTSVF_HEADING_TO_DEPOT :
has_status = v - > current_order . IsType ( OT_GOTO_DEPOT ) ;
break ;
case TRTSVF_LOADING :
has_status = v - > current_order . IsType ( OT_LOADING ) | | v - > current_order . IsType ( OT_LOADING_ADVANCE ) ;
break ;
case TRTSVF_WAITING :
has_status = v - > current_order . IsType ( OT_WAITING ) ;
break ;
case TRTSVF_LOST :
has_status = HasBit ( v - > vehicle_flags , VF_PATHFINDER_LOST ) ;
break ;
case TRTSVF_REQUIRES_SERVICE :
has_status = v - > NeedsServicing ( ) ;
break ;
}
result = TestBinaryConditionCommon ( item , has_status ) ;
break ;
}
case TRIT_COND_LOAD_PERCENT : {
result = TestCondition ( CalcPercentVehicleFilled ( v , nullptr ) , condop , condvalue ) ;
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 ;
}
case TRIT_COND_TIME_DATE_VALUE : {
// TRVT_TIME_DATE_INT value type uses the next slot
i + + ;
uint32_t value = this - > items [ i ] ;
result = TestCondition ( GetTraceRestrictTimeDateValue ( static_cast < TraceRestrictTimeDateValueField > ( GetTraceRestrictValue ( item ) ) ) , condop , value ) ;
break ;
}
case TRIT_COND_RESERVED_TILES : {
uint tiles_ahead = 0 ;
if ( v - > lookahead ! = nullptr ) {
tiles_ahead = std : : max < int > ( 0 , v - > lookahead - > reservation_end_position - v - > lookahead - > current_position ) / TILE_SIZE ;
}
result = TestCondition ( tiles_ahead , 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 :
switch ( static_cast < TraceRestrictPathfinderPenaltyAuxField > ( GetTraceRestrictAuxField ( item ) ) ) {
case TRPPAF_VALUE :
out . penalty + = GetTraceRestrictValue ( item ) ;
break ;
case TRPPAF_PRESET : {
uint16 index = GetTraceRestrictValue ( item ) ;
assert ( index < TRPPPI_END ) ;
out . penalty + = _tracerestrict_pathfinder_penalty_preset_values [ index ] ;
break ;
}
default :
NOT_REACHED ( ) ;
}
break ;
case TRIT_RESERVE_THROUGH :
if ( GetTraceRestrictValue ( item ) ) {
out . flags & = ~ TRPRF_RESERVE_THROUGH ;
} else {
out . flags | = TRPRF_RESERVE_THROUGH ;
}
break ;
case TRIT_LONG_RESERVE :
if ( GetTraceRestrictValue ( item ) ) {
out . flags & = ~ TRPRF_LONG_RESERVE ;
} else {
out . flags | = TRPRF_LONG_RESERVE ;
}
break ;
case TRIT_WAIT_AT_PBS :
switch ( static_cast < TraceRestrictWaitAtPbsValueField > ( GetTraceRestrictValue ( item ) ) ) {
case TRWAPVF_WAIT_AT_PBS :
out . flags | = TRPRF_WAIT_AT_PBS ;
break ;
case TRWAPVF_CANCEL_WAIT_AT_PBS :
out . flags & = ~ TRPRF_WAIT_AT_PBS ;
break ;
case TRWAPVF_PBS_RES_END_WAIT :
out . flags | = TRPRF_PBS_RES_END_WAIT ;
break ;
case TRWAPVF_CANCEL_PBS_RES_END_WAIT :
out . flags & = ~ TRPRF_PBS_RES_END_WAIT ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
case TRIT_SLOT : {
if ( ! input . permitted_slot_operations ) break ;
TraceRestrictSlot * slot = TraceRestrictSlot : : GetIfValid ( GetTraceRestrictValue ( item ) ) ;
if ( slot = = nullptr ) break ;
switch ( static_cast < TraceRestrictSlotCondOpField > ( GetTraceRestrictCondOp ( item ) ) ) {
case TRSCOF_ACQUIRE_WAIT :
if ( input . permitted_slot_operations & TRPISP_ACQUIRE ) {
if ( ! slot - > Occupy ( v - > index ) ) out . flags | = TRPRF_WAIT_AT_PBS ;
}
break ;
case TRSCOF_ACQUIRE_TRY :
if ( input . permitted_slot_operations & TRPISP_ACQUIRE ) slot - > Occupy ( v - > index ) ;
break ;
case TRSCOF_RELEASE_BACK :
if ( input . permitted_slot_operations & TRPISP_RELEASE_BACK ) slot - > Vacate ( v - > index ) ;
break ;
case TRSCOF_RELEASE_FRONT :
if ( input . permitted_slot_operations & TRPISP_RELEASE_FRONT ) slot - > Vacate ( v - > index ) ;
break ;
case TRSCOF_PBS_RES_END_ACQ_WAIT :
if ( input . permitted_slot_operations & TRPISP_PBS_RES_END_ACQUIRE ) {
if ( ! slot - > Occupy ( v - > index ) ) out . flags | = TRPRF_PBS_RES_END_WAIT ;
} else if ( input . permitted_slot_operations & TRPISP_PBS_RES_END_ACQ_DRY ) {
if ( ! slot - > OccupyDryRun ( v - > index ) ) out . flags | = TRPRF_PBS_RES_END_WAIT ;
}
break ;
case TRSCOF_PBS_RES_END_ACQ_TRY :
if ( input . permitted_slot_operations & TRPISP_PBS_RES_END_ACQUIRE ) slot - > Occupy ( v - > index ) ;
break ;
case TRSCOF_PBS_RES_END_RELEASE :
if ( input . permitted_slot_operations & TRPISP_PBS_RES_END_RELEASE ) slot - > Vacate ( v - > index ) ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
}
case TRIT_REVERSE :
switch ( static_cast < TraceRestrictReverseValueField > ( GetTraceRestrictValue ( item ) ) ) {
case TRRVF_REVERSE :
out . flags | = TRPRF_REVERSE ;
break ;
case TRRVF_CANCEL_REVERSE :
out . flags & = ~ TRPRF_REVERSE ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
case TRIT_SPEED_RESTRICTION : {
out . speed_restriction = GetTraceRestrictValue ( item ) ;
out . flags | = TRPRF_SPEED_RETRICTION_SET ;
break ;
}
case TRIT_NEWS_CONTROL :
switch ( static_cast < TraceRestrictNewsControlField > ( GetTraceRestrictValue ( item ) ) ) {
case TRRVF_TRAIN_NOT_STUCK :
out . flags | = TRPRF_TRAIN_NOT_STUCK ;
break ;
case TRRVF_CANCEL_TRAIN_NOT_STUCK :
out . flags & = ~ TRPRF_TRAIN_NOT_STUCK ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
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 ( ) ;
}
} else {
if ( IsTraceRestrictDoubleItem ( type ) ) i + + ;
}
}
}
assert ( condstack . empty ( ) ) ;
}
/**
* Decrement ref count , only use when removing a mapping
*/
void TraceRestrictProgram : : DecrementRefCount ( ) {
assert ( this - > refcount > 0 ) ;
this - > refcount - - ;
if ( this - > refcount = = 0 ) {
delete this ;
}
}
/**
* Validate a instruction list
* Returns successful result if program seems OK
* This only validates that conditional nesting is correct ,
* and that all instructions have a known type , at present
*/
CommandCost TraceRestrictProgram : : Validate ( const std : : vector < TraceRestrictItem > & items , TraceRestrictProgramActionsUsedFlags & actions_used_flags ) {
// static to avoid needing to re-alloc/resize on each execution
static std : : vector < TraceRestrictCondStackFlags > condstack ;
condstack . clear ( ) ;
actions_used_flags = static_cast < TraceRestrictProgramActionsUsedFlags > ( 0 ) ;
size_t size = items . size ( ) ;
for ( size_t i = 0 ; i < size ; i + + ) {
TraceRestrictItem item = items [ i ] ;
TraceRestrictItemType type = GetTraceRestrictType ( item ) ;
// check multi-word instructions
if ( IsTraceRestrictDoubleItem ( item ) ) {
i + + ;
if ( i > = size ) {
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE ) ; // instruction ran off end
}
}
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 ) ;
}
switch ( GetTraceRestrictType ( item ) ) {
case TRIT_COND_ENDIF :
case TRIT_COND_UNDEFINED :
case TRIT_COND_TRAIN_LENGTH :
case TRIT_COND_MAX_SPEED :
case TRIT_COND_CURRENT_ORDER :
case TRIT_COND_NEXT_ORDER :
case TRIT_COND_LAST_STATION :
case TRIT_COND_CARGO :
case TRIT_COND_ENTRY_DIRECTION :
case TRIT_COND_PBS_ENTRY_SIGNAL :
case TRIT_COND_TRAIN_GROUP :
case TRIT_COND_TRAIN_IN_SLOT :
case TRIT_COND_SLOT_OCCUPANCY :
case TRIT_COND_PHYS_PROP :
case TRIT_COND_PHYS_RATIO :
case TRIT_COND_TRAIN_OWNER :
case TRIT_COND_TRAIN_STATUS :
case TRIT_COND_LOAD_PERCENT :
case TRIT_COND_COUNTER_VALUE :
case TRIT_COND_TIME_DATE_VALUE :
case TRIT_COND_RESERVED_TILES :
break ;
default :
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION ) ;
}
} else {
switch ( GetTraceRestrictType ( item ) ) {
case TRIT_PF_DENY :
case TRIT_PF_PENALTY :
actions_used_flags | = TRPAUF_PF ;
break ;
case TRIT_RESERVE_THROUGH :
actions_used_flags | = TRPAUF_RESERVE_THROUGH ;
break ;
case TRIT_LONG_RESERVE :
actions_used_flags | = TRPAUF_LONG_RESERVE ;
break ;
case TRIT_WAIT_AT_PBS :
switch ( static_cast < TraceRestrictWaitAtPbsValueField > ( GetTraceRestrictValue ( item ) ) ) {
case TRWAPVF_WAIT_AT_PBS :
case TRWAPVF_CANCEL_WAIT_AT_PBS :
actions_used_flags | = TRPAUF_WAIT_AT_PBS ;
break ;
case TRWAPVF_PBS_RES_END_WAIT :
case TRWAPVF_CANCEL_PBS_RES_END_WAIT :
actions_used_flags | = TRPAUF_PBS_RES_END_WAIT ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
case TRIT_SLOT :
switch ( static_cast < TraceRestrictSlotCondOpField > ( GetTraceRestrictCondOp ( item ) ) ) {
case TRSCOF_ACQUIRE_WAIT :
actions_used_flags | = TRPAUF_SLOT_ACQUIRE | TRPAUF_WAIT_AT_PBS ;
break ;
case TRSCOF_ACQUIRE_TRY :
actions_used_flags | = TRPAUF_SLOT_ACQUIRE ;
break ;
case TRSCOF_RELEASE_BACK :
actions_used_flags | = TRPAUF_SLOT_RELEASE_BACK ;
break ;
case TRSCOF_RELEASE_FRONT :
actions_used_flags | = TRPAUF_SLOT_RELEASE_FRONT ;
break ;
case TRSCOF_PBS_RES_END_ACQ_WAIT :
actions_used_flags | = TRPAUF_PBS_RES_END_SLOT | TRPAUF_PBS_RES_END_WAIT ;
break ;
case TRSCOF_PBS_RES_END_ACQ_TRY :
case TRSCOF_PBS_RES_END_RELEASE :
actions_used_flags | = TRPAUF_PBS_RES_END_SLOT ;
break ;
default :
NOT_REACHED ( ) ;
break ;
}
break ;
case TRIT_REVERSE :
actions_used_flags | = TRPAUF_REVERSE ;
break ;
case TRIT_SPEED_RESTRICTION :
actions_used_flags | = TRPAUF_SPEED_RESTRICTION ;
break ;
case TRIT_NEWS_CONTROL :
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 ) ;
}
}
}
if ( ! condstack . empty ( ) ) {
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_VALIDATE_END_CONDSTACK ) ;
}
return CommandCost ( ) ;
}
/**
* Convert an instruction index into an item array index
*/
size_t TraceRestrictProgram : : InstructionOffsetToArrayOffset ( const std : : vector < TraceRestrictItem > & items , size_t offset )
{
size_t output_offset = 0 ;
size_t size = items . size ( ) ;
for ( size_t i = 0 ; i < offset & & output_offset < size ; i + + , output_offset + + ) {
if ( IsTraceRestrictDoubleItem ( items [ output_offset ] ) ) {
output_offset + + ;
}
}
return output_offset ;
}
/**
* Convert an item array index into an instruction index
*/
size_t TraceRestrictProgram : : ArrayOffsetToInstructionOffset ( const std : : vector < TraceRestrictItem > & items , size_t offset )
{
size_t output_offset = 0 ;
for ( size_t i = 0 ; i < offset ; i + + , output_offset + + ) {
if ( IsTraceRestrictDoubleItem ( items [ i ] ) ) {
i + + ;
}
}
return output_offset ;
}
/**
* Set the value and aux field of @ p item , as per the value type in @ p value_type
*/
void SetTraceRestrictValueDefault ( TraceRestrictItem & item , TraceRestrictValueType value_type )
{
switch ( value_type ) {
case TRVT_NONE :
case TRVT_INT :
case TRVT_DENY :
case TRVT_SPEED :
case TRVT_TILE_INDEX :
case TRVT_RESERVE_THROUGH :
case TRVT_LONG_RESERVE :
case TRVT_WEIGHT :
case TRVT_POWER :
case TRVT_FORCE :
case TRVT_POWER_WEIGHT_RATIO :
case TRVT_FORCE_WEIGHT_RATIO :
case TRVT_WAIT_AT_PBS :
case TRVT_TRAIN_STATUS :
case TRVT_REVERSE :
case TRVT_PERCENT :
case TRVT_NEWS_CONTROL :
case TRVT_TIME_DATE_INT :
SetTraceRestrictValue ( item , 0 ) ;
if ( ! IsTraceRestrictTypeAuxSubtype ( GetTraceRestrictType ( item ) ) ) {
SetTraceRestrictAuxField ( item , 0 ) ;
}
break ;
case TRVT_ORDER :
SetTraceRestrictValue ( item , INVALID_STATION ) ;
SetTraceRestrictAuxField ( item , TROCAF_STATION ) ;
break ;
case TRVT_CARGO_ID :
assert ( _standard_cargo_mask ! = 0 ) ;
SetTraceRestrictValue ( item , FindFirstBit64 ( _standard_cargo_mask ) ) ;
SetTraceRestrictAuxField ( item , 0 ) ;
break ;
case TRVT_DIRECTION :
SetTraceRestrictValue ( item , TRDTSV_FRONT ) ;
SetTraceRestrictAuxField ( item , 0 ) ;
break ;
case TRVT_PF_PENALTY :
SetTraceRestrictValue ( item , TRPPPI_SMALL ) ;
SetTraceRestrictAuxField ( item , TRPPAF_PRESET ) ;
break ;
case TRVT_GROUP_INDEX :
SetTraceRestrictValue ( item , INVALID_GROUP ) ;
SetTraceRestrictAuxField ( item , 0 ) ;
break ;
case TRVT_OWNER :
SetTraceRestrictValue ( item , INVALID_OWNER ) ;
SetTraceRestrictAuxField ( item , 0 ) ;
break ;
case TRVT_SLOT_INDEX :
SetTraceRestrictValue ( item , INVALID_TRACE_RESTRICT_SLOT_ID ) ;
SetTraceRestrictAuxField ( item , 0 ) ;
break ;
case TRVT_SLOT_INDEX_INT :
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 ;
}
}
/**
* Set the type field of a TraceRestrictItem , and resets any other fields which are no longer valid / meaningful to sensible defaults
*/
void SetTraceRestrictTypeAndNormalise ( TraceRestrictItem & item , TraceRestrictItemType type , uint8 aux_data )
{
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 ) ;
if ( IsTraceRestrictTypeAuxSubtype ( type ) ) {
SetTraceRestrictAuxField ( item , aux_data ) ;
} else {
assert ( aux_data = = 0 ) ;
}
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 ) ;
}
if ( GetTraceRestrictType ( item ) = = TRIT_COND_LAST_STATION & & GetTraceRestrictAuxField ( item ) ! = TROCAF_STATION ) {
// if changing type from another order type to last visited station, reset value if not currently a station
SetTraceRestrictValueDefault ( item , TRVT_ORDER ) ;
}
}
/**
* Sets the " signal has a trace restrict mapping " bit
* This looks for mappings with that tile index
*/
void TraceRestrictSetIsSignalRestrictedBit ( 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 ) ;
}
/**
* Create a new program mapping to an existing program
* If a mapping already exists , it is removed
*/
void TraceRestrictCreateProgramMapping ( TraceRestrictRefId ref , TraceRestrictProgram * prog )
{
std : : pair < 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 ) ;
TraceRestrictSetIsSignalRestrictedBit ( tile ) ;
MarkTileDirtyByTile ( tile , VMDF_NOT_MAP_MODE ) ;
YapfNotifyTrackLayoutChange ( tile , track ) ;
}
/**
* Remove a program mapping
* @ return true if a mapping was actually removed
*/
bool TraceRestrictRemoveProgramMapping ( TraceRestrictRefId ref )
{
TraceRestrictMapping : : iterator iter = _tracerestrictprogram_mapping . find ( ref ) ;
if ( iter ! = _tracerestrictprogram_mapping . end ( ) ) {
// Found
TraceRestrictProgram * prog = _tracerestrictprogram_pool . Get ( iter - > second . program_id ) ;
// check to see if another mapping needs to be removed as well
// do this before decrementing the refcount
bool remove_other_mapping = prog - > refcount = = 2 & & prog - > items . empty ( ) ;
prog - > DecrementRefCount ( ) ;
_tracerestrictprogram_mapping . erase ( iter ) ;
TileIndex tile = GetTraceRestrictRefIdTileIndex ( ref ) ;
Track track = GetTraceRestrictRefIdTrack ( ref ) ;
TraceRestrictSetIsSignalRestrictedBit ( tile ) ;
MarkTileDirtyByTile ( tile , VMDF_NOT_MAP_MODE ) ;
YapfNotifyTrackLayoutChange ( tile , track ) ;
if ( remove_other_mapping ) {
TraceRestrictProgramID id = prog - > index ;
for ( TraceRestrictMapping : : iterator rm_iter = _tracerestrictprogram_mapping . begin ( ) ;
rm_iter ! = _tracerestrictprogram_mapping . end ( ) ; + + rm_iter ) {
if ( rm_iter - > second . program_id = = id ) {
TraceRestrictRemoveProgramMapping ( rm_iter - > first ) ;
break ;
}
}
}
return true ;
} else {
return false ;
}
}
/**
* Gets the signal program for the tile ref @ p ref
* An empty program will be constructed if none exists , and @ p create_new is true , unless the pool is full
*/
TraceRestrictProgram * GetTraceRestrictProgram ( TraceRestrictRefId ref , bool create_new )
{
// Optimise for lookup, creating doesn't have to be that fast
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 nullptr ;
}
TraceRestrictProgram * prog = new TraceRestrictProgram ( ) ;
// Create new mapping to pool item
TraceRestrictCreateProgramMapping ( ref , prog ) ;
return prog ;
} else {
return nullptr ;
}
}
/**
* Notify that a signal is being removed
* Remove any trace restrict mappings associated with it
*/
void TraceRestrictNotifySignalRemoval ( TileIndex tile , Track track )
{
TraceRestrictRefId ref = MakeTraceRestrictRefId ( tile , track ) ;
bool removed = TraceRestrictRemoveProgramMapping ( ref ) ;
DeleteWindowById ( WC_TRACE_RESTRICT , ref ) ;
if ( removed ) InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
}
/**
* Helper function to perform parameter bit - packing and call DoCommandP , for instruction modification actions
*/
void TraceRestrictDoCommandP ( TileIndex tile , Track track , TraceRestrictDoCommandType type , uint32 offset , uint32 value , StringID error_msg )
{
uint32 p1 = 0 ;
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 ) ) ;
}
/**
* Check whether a tile / track pair contains a usable signal
*/
static CommandCost TraceRestrictCheckTileIsUsable ( TileIndex tile , Track track )
{
// 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 ) ;
}
// Check tile ownership, do this afterwards to avoid tripping up on house/industry tiles
CommandCost ret = CheckTileOwnership ( tile ) ;
if ( ret . Failed ( ) ) {
return ret ;
}
return CommandCost ( ) ;
}
/**
* Returns an appropriate default value for the second item of a dual - item instruction
* @ p item is the first item of the instruction
*/
static uint32 GetDualInstructionInitialValue ( TraceRestrictItem item )
{
switch ( GetTraceRestrictType ( item ) ) {
case TRIT_COND_PBS_ENTRY_SIGNAL :
return INVALID_TILE ;
case TRIT_COND_SLOT_OCCUPANCY :
case TRIT_COND_COUNTER_VALUE :
case TRIT_COND_TIME_DATE_VALUE :
return 0 ;
case TRIT_COUNTER :
return 1 ;
default :
NOT_REACHED ( ) ;
}
}
template < typename T > T InstructionIteratorNext ( T iter )
{
return IsTraceRestrictDoubleItem ( * iter ) ? iter + 2 : iter + 1 ;
}
template < typename T > void InstructionIteratorAdvance ( T & iter )
{
iter = InstructionIteratorNext ( iter ) ;
}
CommandCost TraceRestrictProgramRemoveItemAt ( std : : vector < TraceRestrictItem > & items , uint32 offset , bool shallow_mode )
{
TraceRestrictItem old_item = * TraceRestrictProgram : : InstructionAt ( items , offset ) ;
if ( IsTraceRestrictConditional ( old_item ) & & GetTraceRestrictCondFlags ( old_item ) ! = TRCF_OR ) {
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 = TraceRestrictProgram : : InstructionAt ( items , offset ) ;
std : : vector < TraceRestrictItem > : : iterator remove_end = InstructionIteratorNext ( remove_start ) ;
// iterate until matching end block found
for ( ; remove_end ! = items . end ( ) ; InstructionIteratorAdvance ( 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 ) {
if ( shallow_mode ) {
// must erase endif first, as it is later in the vector
items . erase ( remove_end , InstructionIteratorNext ( remove_end ) ) ;
} else {
// inclusively remove up to here
InstructionIteratorAdvance ( 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 = = 1 & & remove_whole_block & & shallow_mode ) {
// shallow-removing whole if block, and it contains an else/or if, bail out
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_CAN_T_SHALLOW_REMOVE_IF_ELIF ) ;
}
}
}
}
if ( recursion_depth ! = 0 ) return CMD_ERROR ; // ran off the end
if ( shallow_mode ) {
items . erase ( remove_start , InstructionIteratorNext ( remove_start ) ) ;
} else {
items . erase ( remove_start , remove_end ) ;
}
} else {
std : : vector < TraceRestrictItem > : : iterator remove_start = TraceRestrictProgram : : InstructionAt ( items , offset ) ;
std : : vector < TraceRestrictItem > : : iterator remove_end = InstructionIteratorNext ( remove_start ) ;
items . erase ( remove_start , remove_end ) ;
}
return CommandCost ( ) ;
}
CommandCost TraceRestrictProgramMoveItemAt ( std : : vector < TraceRestrictItem > & items , uint32 & offset , bool up , bool shallow_mode )
{
std : : vector < TraceRestrictItem > : : iterator move_start = TraceRestrictProgram : : InstructionAt ( items , offset ) ;
std : : vector < TraceRestrictItem > : : iterator move_end = InstructionIteratorNext ( move_start ) ;
TraceRestrictItem old_item = * move_start ;
if ( ! shallow_mode ) {
if ( IsTraceRestrictConditional ( old_item ) ) {
if ( GetTraceRestrictCondFlags ( old_item ) ! = 0 ) {
// can't move or/else blocks
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM ) ;
}
if ( GetTraceRestrictType ( old_item ) = = TRIT_COND_ENDIF ) {
// this is an end if, can't move these
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM ) ;
}
uint32 recursion_depth = 1 ;
// iterate until matching end block found
for ( ; move_end ! = items . end ( ) ; InstructionIteratorAdvance ( move_end ) ) {
TraceRestrictItem current_item = * move_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 ) {
// inclusively remove up to here
InstructionIteratorAdvance ( move_end ) ;
break ;
}
} else {
// this is an opening if
recursion_depth + + ;
}
}
}
}
if ( recursion_depth ! = 0 ) return CMD_ERROR ; // ran off the end
}
}
if ( up ) {
if ( move_start = = items . begin ( ) ) return_cmd_error ( STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM ) ;
std : : rotate ( TraceRestrictProgram : : InstructionAt ( items , offset - 1 ) , move_start , move_end ) ;
offset - - ;
} else {
if ( move_end = = items . end ( ) ) return_cmd_error ( STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM ) ;
std : : rotate ( move_start , move_end , InstructionIteratorNext ( move_end ) ) ;
offset + + ;
}
return CommandCost ( ) ;
}
/**
* The main command for editing a signal tracerestrict program .
* @ param tile The tile which contains the signal .
* @ param flags Internal command handler stuff .
* Below apply for instruction modification actions only
* @ param p1 Bitstuffed items
* @ param p2 Item , for insert and modify operations . Flags for instruction move operations
* @ return the cost of this operation ( which is free ) , or an error
*/
CommandCost CmdProgramSignalTraceRestrict ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
TraceRestrictDoCommandType type = static_cast < TraceRestrictDoCommandType > ( GB ( p1 , 3 , 5 ) ) ;
if ( type > = TRDCT_PROG_COPY ) {
return CmdProgramSignalTraceRestrictProgMgmt ( tile , flags , p1 , p2 , text ) ;
}
Track track = static_cast < Track > ( GB ( p1 , 0 , 3 ) ) ;
uint32 offset = GB ( p1 , 8 , 16 ) ;
TraceRestrictItem item = static_cast < TraceRestrictItem > ( p2 ) ;
CommandCost ret = TraceRestrictCheckTileIsUsable ( tile , track ) ;
if ( ret . Failed ( ) ) {
return ret ;
}
bool can_make_new = ( type = = TRDCT_INSERT_ITEM ) & & ( flags & DC_EXEC ) ;
bool need_existing = ( type ! = TRDCT_INSERT_ITEM ) ;
TraceRestrictProgram * prog = GetTraceRestrictProgram ( MakeTraceRestrictRefId ( tile , track ) , can_make_new ) ;
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 ( TraceRestrictProgram : : InstructionAt ( items , 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 ( TraceRestrictProgram : : InstructionAt ( items , offset ) + 1 , endif_item ) ;
} else if ( IsTraceRestrictDoubleItem ( item ) ) {
items . insert ( TraceRestrictProgram : : InstructionAt ( items , offset ) + 1 , GetDualInstructionInitialValue ( item ) ) ;
}
break ;
case TRDCT_MODIFY_ITEM : {
std : : vector < TraceRestrictItem > : : iterator old_item = TraceRestrictProgram : : InstructionAt ( items , offset ) ;
if ( IsTraceRestrictConditional ( * old_item ) ! = IsTraceRestrictConditional ( item ) ) {
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY ) ;
}
bool old_is_dual = IsTraceRestrictDoubleItem ( * old_item ) ;
bool new_is_dual = IsTraceRestrictDoubleItem ( item ) ;
* old_item = item ;
if ( old_is_dual & & ! new_is_dual ) {
items . erase ( old_item + 1 ) ;
} else if ( ! old_is_dual & & new_is_dual ) {
items . insert ( old_item + 1 , GetDualInstructionInitialValue ( item ) ) ;
}
break ;
}
case TRDCT_MODIFY_DUAL_ITEM : {
std : : vector < TraceRestrictItem > : : iterator old_item = TraceRestrictProgram : : InstructionAt ( items , offset ) ;
if ( ! IsTraceRestrictDoubleItem ( * old_item ) ) {
return CMD_ERROR ;
}
* ( old_item + 1 ) = p2 ;
break ;
}
case TRDCT_REMOVE_ITEM :
case TRDCT_SHALLOW_REMOVE_ITEM : {
CommandCost res = TraceRestrictProgramRemoveItemAt ( items , offset , type = = TRDCT_SHALLOW_REMOVE_ITEM ) ;
if ( res . Failed ( ) ) return res ;
break ;
}
case TRDCT_MOVE_ITEM : {
CommandCost res = TraceRestrictProgramMoveItemAt ( items , offset , p2 & 1 , p2 & 2 ) ;
if ( res . Failed ( ) ) return res ;
break ;
}
default :
NOT_REACHED ( ) ;
break ;
}
TraceRestrictProgramActionsUsedFlags actions_used_flags ;
CommandCost validation_result = TraceRestrictProgram : : Validate ( items , actions_used_flags ) ;
if ( validation_result . Failed ( ) ) {
return validation_result ;
}
if ( flags & DC_EXEC ) {
assert ( prog ) ;
// move in modified program
prog - > items . swap ( items ) ;
prog - > actions_used_flags = actions_used_flags ;
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 ( ) ;
}
/**
* Helper function to perform parameter bit - packing and call DoCommandP , for program management actions
*/
void TraceRestrictProgMgmtWithSourceDoCommandP ( TileIndex tile , Track track , TraceRestrictDoCommandType type ,
TileIndex source_tile , Track source_track , StringID error_msg )
{
uint32 p1 = 0 ;
SB ( p1 , 0 , 3 , track ) ;
SB ( p1 , 3 , 5 , type ) ;
SB ( p1 , 8 , 3 , source_track ) ;
DoCommandP ( tile , p1 , source_tile , CMD_PROGRAM_TRACERESTRICT_SIGNAL | CMD_MSG ( error_msg ) ) ;
}
/**
* Sub command for copy / share / unshare operations on signal tracerestrict programs .
* @ param tile The tile which contains the signal .
* @ param flags Internal command handler stuff .
* @ param p1 Bitstuffed items
* @ param p2 Source tile , for share / copy operations
* @ return the cost of this operation ( which is free ) , or an error
*/
CommandCost CmdProgramSignalTraceRestrictProgMgmt ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
TraceRestrictDoCommandType type = static_cast < TraceRestrictDoCommandType > ( GB ( p1 , 3 , 5 ) ) ;
Track track = static_cast < Track > ( GB ( p1 , 0 , 3 ) ) ;
Track source_track = static_cast < Track > ( GB ( p1 , 8 , 3 ) ) ;
TileIndex source_tile = static_cast < TileIndex > ( p2 ) ;
TraceRestrictRefId self = MakeTraceRestrictRefId ( tile , track ) ;
TraceRestrictRefId source = MakeTraceRestrictRefId ( source_tile , source_track ) ;
assert ( type > = TRDCT_PROG_COPY ) ;
CommandCost ret = TraceRestrictCheckTileIsUsable ( tile , track ) ;
if ( ret . Failed ( ) ) {
return ret ;
}
if ( type = = TRDCT_PROG_SHARE | | type = = TRDCT_PROG_COPY ) {
if ( self = = source ) {
return_cmd_error ( STR_TRACE_RESTRICT_ERROR_SOURCE_SAME_AS_TARGET ) ;
}
}
if ( type = = TRDCT_PROG_SHARE | | type = = TRDCT_PROG_COPY | | type = = TRDCT_PROG_COPY_APPEND ) {
ret = TraceRestrictCheckTileIsUsable ( source_tile , source_track ) ;
if ( ret . Failed ( ) ) {
return ret ;
}
}
if ( type ! = TRDCT_PROG_RESET & & ! TraceRestrictProgram : : CanAllocateItem ( ) ) {
return CMD_ERROR ;
}
if ( ! ( flags & DC_EXEC ) ) {
return CommandCost ( ) ;
}
switch ( type ) {
case TRDCT_PROG_COPY : {
TraceRestrictRemoveProgramMapping ( self ) ;
TraceRestrictProgram * source_prog = GetTraceRestrictProgram ( source , false ) ;
if ( source_prog & & ! source_prog - > items . empty ( ) ) {
TraceRestrictProgram * prog = GetTraceRestrictProgram ( self , true ) ;
if ( ! prog ) {
// allocation failed
return CMD_ERROR ;
}
prog - > items = source_prog - > items ; // copy
prog - > Validate ( ) ;
}
break ;
}
case TRDCT_PROG_COPY_APPEND : {
TraceRestrictProgram * source_prog = GetTraceRestrictProgram ( source , false ) ;
if ( source_prog & & ! source_prog - > items . empty ( ) ) {
TraceRestrictProgram * prog = GetTraceRestrictProgram ( self , true ) ;
if ( ! prog ) {
// allocation failed
return CMD_ERROR ;
}
prog - > items . reserve ( prog - > items . size ( ) + source_prog - > items . size ( ) ) ; // this is in case prog == source_prog
prog - > items . insert ( prog - > items . end ( ) , source_prog - > items . begin ( ) , source_prog - > items . end ( ) ) ; // append
prog - > Validate ( ) ;
}
break ;
}
case TRDCT_PROG_SHARE : {
TraceRestrictRemoveProgramMapping ( self ) ;
TraceRestrictProgram * source_prog = GetTraceRestrictProgram ( source , true ) ;
if ( ! source_prog ) {
// allocation failed
return CMD_ERROR ;
}
TraceRestrictCreateProgramMapping ( self , source_prog ) ;
break ;
}
case TRDCT_PROG_UNSHARE : {
std : : vector < TraceRestrictItem > items ;
TraceRestrictProgram * prog = GetTraceRestrictProgram ( self , false ) ;
if ( prog ) {
// copy program into temporary
items = prog - > items ;
}
// remove old program
TraceRestrictRemoveProgramMapping ( self ) ;
if ( items . size ( ) ) {
// if prog is non-empty, create new program and move temporary in
TraceRestrictProgram * new_prog = GetTraceRestrictProgram ( self , true ) ;
if ( ! new_prog ) {
// allocation failed
return CMD_ERROR ;
}
new_prog - > items . swap ( items ) ;
new_prog - > Validate ( ) ;
}
break ;
}
case TRDCT_PROG_RESET : {
TraceRestrictRemoveProgramMapping ( self ) ;
break ;
}
default :
NOT_REACHED ( ) ;
break ;
}
// update windows
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
return CommandCost ( ) ;
}
int GetTraceRestrictTimeDateValue ( TraceRestrictTimeDateValueField type )
{
Minutes minutes = ( _scaled_date_ticks / _settings_game . game_time . ticks_per_minute ) + _settings_game . game_time . clock_offset ;
switch ( type ) {
case TRTDVF_MINUTE :
return MINUTES_MINUTE ( minutes ) ;
case TRTDVF_HOUR :
return MINUTES_HOUR ( minutes ) ;
case TRTDVF_HOUR_MINUTE :
return ( MINUTES_HOUR ( minutes ) * 100 ) + MINUTES_MINUTE ( minutes ) ;
default :
return 0 ;
}
}
/**
* This is called when a station , waypoint or depot is about to be deleted
* Scan program pool and change any references to it to the invalid station ID , to avoid dangling references
*/
void TraceRestrictRemoveDestinationID ( TraceRestrictOrderCondAuxField type , uint16 index )
{
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_COND_CURRENT_ORDER | |
GetTraceRestrictType ( item ) = = TRIT_COND_NEXT_ORDER | |
GetTraceRestrictType ( item ) = = TRIT_COND_LAST_STATION ) {
if ( GetTraceRestrictAuxField ( item ) = = type & & GetTraceRestrictValue ( item ) = = index ) {
SetTraceRestrictValueDefault ( item , TRVT_ORDER ) ; // this updates the instruction in-place
}
}
if ( IsTraceRestrictDoubleItem ( item ) ) i + + ;
}
}
// update windows
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
}
/**
* This is called when a group is about to be deleted
* Scan program pool and change any references to it to the invalid group ID , to avoid dangling references
*/
void TraceRestrictRemoveGroupID ( GroupID 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_COND_TRAIN_GROUP & & GetTraceRestrictValue ( item ) = = index ) {
SetTraceRestrictValueDefault ( item , TRVT_GROUP_INDEX ) ; // this updates the instruction in-place
}
if ( IsTraceRestrictDoubleItem ( item ) ) i + + ;
}
}
// update windows
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
}
/**
* This is called when a company is about to be deleted or taken over
* Scan program pool and change any references to it to the new company ID , to avoid dangling references
* Change owner and / or delete slots
*/
void TraceRestrictUpdateCompanyID ( CompanyID old_company , CompanyID new_company )
{
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_COND_TRAIN_OWNER ) {
if ( GetTraceRestrictValue ( item ) = = old_company ) {
SetTraceRestrictValue ( item , new_company ) ; // this updates the instruction in-place
}
}
if ( IsTraceRestrictDoubleItem ( item ) ) i + + ;
}
}
for ( TraceRestrictSlot * slot : TraceRestrictSlot : : Iterate ( ) ) {
if ( slot - > owner ! = old_company ) continue ;
if ( new_company = = INVALID_OWNER ) {
TraceRestrictRemoveSlotID ( slot - > index ) ;
delete slot ;
} else {
slot - > owner = 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 ;
/**
* Add vehicle ID to occupants if possible and not already an occupant
* @ param id Vehicle ID
* @ param force Add the vehicle even if the slot is at / over capacity
* @ return whether vehicle ID is now an occupant
*/
bool TraceRestrictSlot : : Occupy ( VehicleID id , bool force )
{
if ( this - > IsOccupant ( id ) ) return true ;
if ( this - > occupants . size ( ) > = this - > max_occupancy & & ! force ) return false ;
this - > occupants . push_back ( id ) ;
slot_vehicle_index . emplace ( id , this - > index ) ;
SetBit ( Train : : Get ( id ) - > flags , VRF_HAVE_SLOT ) ;
SetWindowDirty ( WC_VEHICLE_DETAILS , id ) ;
InvalidateWindowClassesData ( WC_TRACE_RESTRICT_SLOTS ) ;
this - > UpdateSignals ( ) ;
return true ;
}
/**
* Dry - run adding vehicle ID to occupants if possible and not already an occupant
* @ param id Vehicle ID
* @ return whether vehicle IDwould be an occupant
*/
bool TraceRestrictSlot : : OccupyDryRun ( VehicleID id )
{
if ( this - > IsOccupant ( id ) ) return true ;
if ( this - > occupants . size ( ) > = this - > max_occupancy ) return false ;
return true ;
}
/**
* Remove vehicle ID from occupants
* @ param id Vehicle ID
*/
void TraceRestrictSlot : : Vacate ( VehicleID id )
{
if ( container_unordered_remove ( this - > occupants , id ) ) {
this - > DeIndex ( id ) ;
this - > UpdateSignals ( ) ;
}
}
/** Remove all occupants */
void TraceRestrictSlot : : Clear ( )
{
for ( VehicleID id : this - > occupants ) {
this - > DeIndex ( id ) ;
}
this - > occupants . clear ( ) ;
}
void TraceRestrictSlot : : UpdateSignals ( ) {
for ( SignalReference sr : this - > progsig_dependants ) {
AddTrackToSignalBuffer ( sr . tile , sr . track , GetTileOwner ( sr . tile ) ) ;
UpdateSignalsInBuffer ( ) ;
}
}
void TraceRestrictSlot : : DeIndex ( VehicleID id )
{
auto range = slot_vehicle_index . equal_range ( id ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
if ( it - > second = = this - > index ) {
bool is_first_in_range = ( it = = range . first ) ;
auto next = slot_vehicle_index . erase ( it ) ;
if ( is_first_in_range & & next = = range . second ) {
/* Only one item, which we've just erased, clear the vehicle flag */
ClrBit ( Train : : Get ( id ) - > flags , VRF_HAVE_SLOT ) ;
}
break ;
}
}
SetWindowDirty ( WC_VEHICLE_DETAILS , id ) ;
InvalidateWindowClassesData ( WC_TRACE_RESTRICT_SLOTS ) ;
}
/** Rebuild slot vehicle index after loading */
void TraceRestrictSlot : : RebuildVehicleIndex ( )
{
slot_vehicle_index . clear ( ) ;
for ( const TraceRestrictSlot * slot : TraceRestrictSlot : : Iterate ( ) ) {
for ( VehicleID id : slot - > occupants ) {
slot_vehicle_index . emplace ( id , slot - > index ) ;
}
}
}
bool TraceRestrictSlot : : ValidateVehicleIndex ( )
{
std : : unordered_multimap < VehicleID , TraceRestrictSlotID > saved_slot_vehicle_index = std : : move ( slot_vehicle_index ) ;
RebuildVehicleIndex ( ) ;
const bool ok = slot_vehicle_index = = saved_slot_vehicle_index ;
slot_vehicle_index = std : : move ( saved_slot_vehicle_index ) ;
return ok ;
}
void TraceRestrictSlot : : ValidateSlotOccupants ( std : : function < void ( const char * ) > log )
{
char cclog_buffer [ 1024 ] ;
# define CCLOG(...) { \
seprintf ( cclog_buffer , lastof ( cclog_buffer ) , __VA_ARGS__ ) ; \
DEBUG ( desync , 0 , " %s " , cclog_buffer ) ; \
if ( log ) log ( cclog_buffer ) ; \
}
for ( const TraceRestrictSlot * slot : TraceRestrictSlot : : Iterate ( ) ) {
for ( VehicleID id : slot - > occupants ) {
const Train * t = Train : : GetIfValid ( id ) ;
if ( t ) {
if ( ! t - > IsFrontEngine ( ) ) CCLOG ( " Slot %u (%s) has non-front engine train: %s " , slot - > index , slot - > name . c_str ( ) , scope_dumper ( ) . VehicleInfo ( t ) ) ;
if ( ! HasBit ( t - > flags , VRF_HAVE_SLOT ) ) CCLOG ( " Slot %u (%s) has train without VRF_HAVE_SLOT: %s " , slot - > index , slot - > name . c_str ( ) , scope_dumper ( ) . VehicleInfo ( t ) ) ;
} else {
CCLOG ( " Slot %u (%s) has non-existent vehicle ID: %u " , slot - > index , slot - > name . c_str ( ) , id ) ;
}
}
}
# undef CCLOG
}
/** Slot pool is about to be cleared */
void TraceRestrictSlot : : PreCleanPool ( )
{
slot_vehicle_index . clear ( ) ;
}
/** Remove vehicle ID from all slot occupants */
void TraceRestrictRemoveVehicleFromAllSlots ( VehicleID vehicle_id )
{
const auto range = slot_vehicle_index . equal_range ( vehicle_id ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
auto slot = TraceRestrictSlot : : Get ( it - > second ) ;
container_unordered_remove ( slot - > occupants , vehicle_id ) ;
slot - > UpdateSignals ( ) ;
}
const bool anything_to_erase = range . first ! = range . second ;
slot_vehicle_index . erase ( range . first , range . second ) ;
if ( anything_to_erase ) InvalidateWindowClassesData ( WC_TRACE_RESTRICT_SLOTS ) ;
}
/** Replace all instance of a vehicle ID with another, in all slot occupants */
void TraceRestrictTransferVehicleOccupantInAllSlots ( VehicleID from , VehicleID to )
{
auto range = slot_vehicle_index . equal_range ( from ) ;
std : : vector < TraceRestrictSlotID > slots ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
slots . push_back ( it - > second ) ;
}
slot_vehicle_index . erase ( range . first , range . second ) ;
for ( TraceRestrictSlotID slot_id : slots ) {
TraceRestrictSlot * slot = TraceRestrictSlot : : Get ( slot_id ) ;
for ( VehicleID & id : slot - > occupants ) {
if ( id = = from ) {
id = to ;
slot_vehicle_index . emplace ( to , slot_id ) ;
}
}
}
if ( ! slots . empty ( ) ) InvalidateWindowClassesData ( WC_TRACE_RESTRICT_SLOTS ) ;
}
/** Get list of slots occupied by a vehicle ID */
void TraceRestrictGetVehicleSlots ( VehicleID id , std : : vector < TraceRestrictSlotID > & out )
{
auto range = slot_vehicle_index . equal_range ( id ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
out . push_back ( it - > second ) ;
}
}
/**
* This is called when a slot is about to be deleted
* Scan program pool and change any references to it to the invalid group ID , to avoid dangling references
* Scan order list and change any references to it to the invalid group ID , to avoid dangling slot condition references
*/
void TraceRestrictRemoveSlotID ( TraceRestrictSlotID 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_SLOT | | GetTraceRestrictType ( item ) = = TRIT_COND_TRAIN_IN_SLOT ) & & GetTraceRestrictValue ( item ) = = index ) {
SetTraceRestrictValueDefault ( item , TRVT_SLOT_INDEX ) ; // this updates the instruction in-place
}
if ( ( GetTraceRestrictType ( item ) = = TRIT_COND_SLOT_OCCUPANCY ) & & GetTraceRestrictValue ( item ) = = index ) {
SetTraceRestrictValueDefault ( item , TRVT_SLOT_INDEX_INT ) ; // this updates the instruction in-place
}
if ( IsTraceRestrictDoubleItem ( item ) ) i + + ;
}
}
bool changed_order = false ;
for ( Order * o : Order : : Iterate ( ) ) {
if ( o - > IsType ( OT_CONDITIONAL ) & &
( o - > GetConditionVariable ( ) = = OCV_SLOT_OCCUPANCY | | o - > GetConditionVariable ( ) = = OCV_TRAIN_IN_SLOT ) & &
o - > GetXData ( ) = = index ) {
o - > GetXDataRef ( ) = INVALID_TRACE_RESTRICT_SLOT_ID ;
changed_order = true ;
}
if ( o - > IsType ( OT_RELEASE_SLOT ) & & o - > GetDestination ( ) = = index ) {
o - > SetDestination ( INVALID_TRACE_RESTRICT_SLOT_ID ) ;
changed_order = true ;
}
}
// update windows
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
if ( changed_order ) {
InvalidateWindowClassesData ( WC_VEHICLE_ORDERS ) ;
InvalidateWindowClassesData ( WC_VEHICLE_TIMETABLE ) ;
}
for ( SignalReference sr : TraceRestrictSlot : : Get ( index ) - > progsig_dependants ) {
if ( IsProgrammableSignal ( GetSignalType ( sr . tile , sr . track ) ) ) {
extern void RemoveProgramSlotDependencies ( TraceRestrictSlotID slot_being_removed , SignalReference signal_to_update ) ;
RemoveProgramSlotDependencies ( index , sr ) ;
}
}
}
static bool IsUniqueSlotName ( const char * name )
{
for ( const TraceRestrictSlot * slot : TraceRestrictSlot : : Iterate ( ) ) {
if ( slot - > name = = name ) return false ;
}
return true ;
}
/**
* Create a new slot .
* @ param tile unused
* @ param flags type of operation
* @ param p1 unused
* @ param p2 unused
* @ param text new slot name
* @ return the cost of this operation or an error
*/
CommandCost CmdCreateTraceRestrictSlot ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
if ( ! TraceRestrictSlot : : 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 ( ! IsUniqueSlotName ( text ) ) return_cmd_error ( STR_ERROR_NAME_MUST_BE_UNIQUE ) ;
if ( flags & DC_EXEC ) {
TraceRestrictSlot * slot = new TraceRestrictSlot ( _current_company ) ;
slot - > name = text ;
// update windows
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
InvalidateWindowClassesData ( WC_TRACE_RESTRICT_SLOTS ) ;
}
return CommandCost ( ) ;
}
/**
* Deletes a slot .
* @ param tile unused
* @ param flags type of operation
* @ param p1 index of array group
* - p1 bit 0 - 15 : Slot ID
* @ param p2 unused
* @ param text unused
* @ return the cost of this operation or an error
*/
CommandCost CmdDeleteTraceRestrictSlot ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
TraceRestrictSlot * slot = TraceRestrictSlot : : GetIfValid ( p1 ) ;
if ( slot = = nullptr | | slot - > owner ! = _current_company ) return CMD_ERROR ;
if ( flags & DC_EXEC ) {
/* notify tracerestrict that group is about to be deleted */
TraceRestrictRemoveSlotID ( slot - > index ) ;
delete slot ;
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
InvalidateWindowClassesData ( WC_TRACE_RESTRICT_SLOTS ) ;
InvalidateWindowClassesData ( WC_VEHICLE_ORDERS ) ;
}
return CommandCost ( ) ;
}
/**
* Alter a slot
* @ param tile unused
* @ param flags type of operation
* @ param p1 index of array group
* - p1 bit 0 - 15 : GroupID
* - p1 bit 16 : 0 - Rename group
* 1 - Change max occupancy
* @ param p2 new max occupancy
* @ param text the new name
* @ return the cost of this operation or an error
*/
CommandCost CmdAlterTraceRestrictSlot ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
TraceRestrictSlot * slot = TraceRestrictSlot : : GetIfValid ( GB ( p1 , 0 , 16 ) ) ;
if ( slot = = nullptr | | slot - > owner ! = _current_company ) return CMD_ERROR ;
if ( ! HasBit ( p1 , 16 ) ) {
/* Rename slot */
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 ( ! IsUniqueSlotName ( text ) ) return_cmd_error ( STR_ERROR_NAME_MUST_BE_UNIQUE ) ;
if ( flags & DC_EXEC ) {
slot - > name = text ;
}
} else {
/* Change max occupancy */
if ( flags & DC_EXEC ) {
slot - > max_occupancy = p2 ;
slot - > UpdateSignals ( ) ;
}
}
if ( flags & DC_EXEC ) {
// update windows
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
InvalidateWindowClassesData ( WC_TRACE_RESTRICT_SLOTS ) ;
InvalidateWindowClassesData ( WC_VEHICLE_ORDERS ) ;
InvalidateWindowClassesData ( WC_SIGNAL_PROGRAM ) ;
}
return CommandCost ( ) ;
}
/**
* Add a vehicle to a slot
* @ param tile unused
* @ param flags type of operation
* @ param p1 index of array group
* - p1 bit 0 - 15 : GroupID
* @ param p2 index of vehicle
* - p2 bit 0 - 19 : VehicleID
* @ param text unused
* @ return the cost of this operation or an error
*/
CommandCost CmdAddVehicleTraceRestrictSlot ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
TraceRestrictSlot * slot = TraceRestrictSlot : : GetIfValid ( p1 ) ;
Vehicle * v = Vehicle : : GetIfValid ( p2 ) ;
if ( slot = = nullptr | | slot - > owner ! = _current_company ) return CMD_ERROR ;
if ( v = = nullptr | | v - > owner ! = _current_company ) return CMD_ERROR ;
if ( flags & DC_EXEC ) {
slot - > Occupy ( v - > index , true ) ;
}
return CommandCost ( ) ;
}
/**
* Remove a vehicle from a slot
* @ param tile unused
* @ param flags type of operation
* @ param p1 index of array group
* - p1 bit 0 - 15 : GroupID
* @ param p2 index of vehicle
* - p2 bit 0 - 19 : VehicleID
* @ param text unused
* @ return the cost of this operation or an error
*/
CommandCost CmdRemoveVehicleTraceRestrictSlot ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text )
{
TraceRestrictSlot * slot = TraceRestrictSlot : : GetIfValid ( p1 ) ;
Vehicle * v = Vehicle : : GetIfValid ( p2 ) ;
if ( slot = = nullptr | | slot - > owner ! = _current_company ) return CMD_ERROR ;
if ( v = = nullptr ) return CMD_ERROR ; // permit removing vehicles of other owners from your own slot
if ( flags & DC_EXEC ) {
slot - > Vacate ( v - > index ) ;
}
return CommandCost ( ) ;
}
void TraceRestrictCounter : : UpdateValue ( int32 new_value )
{
new_value = std : : max < int32 > ( 0 , new_value ) ;
if ( new_value ! = this - > value ) {
this - > value = new_value ;
InvalidateWindowClassesData ( WC_TRACE_RESTRICT_COUNTERS ) ;
for ( SignalReference sr : this - > progsig_dependants ) {
AddTrackToSignalBuffer ( sr . tile , sr . track , GetTileOwner ( sr . tile ) ) ;
UpdateSignalsInBuffer ( ) ;
}
}
}
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 + + ;
}
}
bool changed_order = false ;
for ( Order * o : Order : : Iterate ( ) ) {
if ( o - > IsType ( OT_CONDITIONAL ) & &
( o - > GetConditionVariable ( ) = = OCV_COUNTER_VALUE ) & &
GB ( o - > GetXData ( ) , 16 , 16 ) = = index ) {
SB ( o - > GetXDataRef ( ) , 16 , 16 , INVALID_TRACE_RESTRICT_COUNTER_ID ) ;
changed_order = true ;
}
}
// update windows
InvalidateWindowClassesData ( WC_TRACE_RESTRICT ) ;
if ( changed_order ) {
InvalidateWindowClassesData ( WC_VEHICLE_ORDERS ) ;
InvalidateWindowClassesData ( WC_VEHICLE_TIMETABLE ) ;
}
for ( SignalReference sr : TraceRestrictCounter : : Get ( index ) - > progsig_dependants ) {
if ( IsProgrammableSignal ( GetSignalType ( sr . tile , sr . track ) ) ) {
extern void RemoveProgramCounterDependencies ( TraceRestrictCounterID ctr_being_removed , SignalReference signal_to_update ) ;
RemoveProgramCounterDependencies ( index , sr ) ;
}
}
}
/**
* 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 ) ;
InvalidateWindowClassesData ( WC_SIGNAL_PROGRAM ) ;
}
return CommandCost ( ) ;
}