/* $Id$ */
# include "stdafx.h"
# include "openttd.h"
# include "bridge_map.h"
# include "debug.h"
# include "functions.h"
# include "gui.h"
# include "station_map.h"
# include "table/strings.h"
# include "map.h"
# include "tile.h"
# include "tunnel_map.h"
# include "vehicle.h"
# include "command.h"
# include "pathfind.h"
# include "npf.h"
# include "station.h"
# include "table/train_cmd.h"
# include "news.h"
# include "engine.h"
# include "player.h"
# include "sound.h"
# include "depot.h"
# include "waypoint.h"
# include "vehicle_gui.h"
# include "train.h"
# include "newgrf_callbacks.h"
# include "newgrf_engine.h"
# include "newgrf_sound.h"
# include "newgrf_text.h"
# include "direction.h"
# include "yapf/yapf.h"
# include "date.h"
static bool TrainCheckIfLineEnds ( Vehicle * v ) ;
static void TrainController ( Vehicle * v ) ;
static const byte _vehicle_initial_x_fract [ 4 ] = { 10 , 8 , 4 , 8 } ;
static const byte _vehicle_initial_y_fract [ 4 ] = { 8 , 4 , 8 , 10 } ;
static const byte _state_dir_table [ 4 ] = { 0x20 , 8 , 0x10 , 4 } ;
/**
* Recalculates the cached weight of a train and its vehicles . Should be called each time the cargo on
* the consist changes .
* @ param v First vehicle of the consist .
*/
static void TrainCargoChanged ( Vehicle * v )
{
Vehicle * u ;
uint16 weight = 0 ;
for ( u = v ; u ! = NULL ; u = u - > next ) {
const RailVehicleInfo * rvi = RailVehInfo ( u - > engine_type ) ;
uint16 vweight = ( _cargoc . weights [ u - > cargo_type ] * u - > cargo_count ) / 16 ;
// Vehicle weight is not added for articulated parts.
if ( ! IsArticulatedPart ( u ) ) {
// vehicle weight is the sum of the weight of the vehicle and the weight of its cargo
vweight + = rvi - > weight ;
// powered wagons have extra weight added
if ( HASBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) )
vweight + = RailVehInfo ( u - > u . rail . first_engine ) - > pow_wag_weight ;
}
// consist weight is the sum of the weight of all vehicles in the consist
weight + = vweight ;
// store vehicle weight in cache
u - > u . rail . cached_veh_weight = vweight ;
} ;
// store consist weight in cache
v - > u . rail . cached_weight = weight ;
}
/**
* Recalculates the cached total power of a train . Should be called when the consist is changed
* @ param v First vehicle of the consist .
*/
void TrainPowerChanged ( Vehicle * v )
{
Vehicle * u ;
uint32 power = 0 ;
for ( u = v ; u ! = NULL ; u = u - > next ) {
const RailVehicleInfo * rvi_u ;
bool engine_has_power = true ;
bool wagon_has_power = true ;
/* Power is not added for articulated parts */
if ( IsArticulatedPart ( u ) ) continue ;
if ( IsBridgeTile ( u - > tile ) & & IsBridgeMiddle ( u - > tile ) & & DiagDirToAxis ( DirToDiagDir ( u - > direction ) ) = = GetBridgeAxis ( u - > tile ) ) {
if ( ! HasPowerOnRail ( u - > u . rail . railtype , GetRailTypeOnBridge ( u - > tile ) ) ) engine_has_power = false ;
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetRailTypeOnBridge ( u - > tile ) ) ) wagon_has_power = false ;
} else if ( IsLevelCrossingTile ( u - > tile ) ) {
if ( ! HasPowerOnRail ( u - > u . rail . railtype , GetRailTypeCrossing ( u - > tile ) ) ) engine_has_power = false ;
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetRailTypeCrossing ( u - > tile ) ) ) wagon_has_power = false ;
} else {
if ( ! HasPowerOnRail ( u - > u . rail . railtype , GetRailType ( u - > tile ) ) ) engine_has_power = false ;
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetRailType ( u - > tile ) ) ) wagon_has_power = false ;
}
rvi_u = RailVehInfo ( u - > engine_type ) ;
if ( engine_has_power ) power + = rvi_u - > power ;
if ( HASBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) & & ( wagon_has_power ) ) {
power + = RailVehInfo ( u - > u . rail . first_engine ) - > pow_wag_power ;
}
}
if ( v - > u . rail . cached_power ! = power ) {
v - > u . rail . cached_power = power ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
}
/**
* Recalculates the cached stuff of a train . Should be called each time a vehicle is added
* to / removed from the chain , and when the game is loaded .
* Note : this needs to be called too for ' wagon chains ' ( in the depot , without an engine )
* @ param v First vehicle of the chain .
*/
void TrainConsistChanged ( Vehicle * v )
{
const RailVehicleInfo * rvi_v ;
Vehicle * u ;
uint16 max_speed = 0xFFFF ;
EngineID first_engine ;
assert ( v - > type = = VEH_Train ) ;
assert ( IsFrontEngine ( v ) | | IsFreeWagon ( v ) ) ;
rvi_v = RailVehInfo ( v - > engine_type ) ;
first_engine = IsFrontEngine ( v ) ? v - > engine_type : INVALID_ENGINE ;
v - > u . rail . cached_total_length = 0 ;
v - > u . rail . compatible_railtypes = 0 ;
for ( u = v ; u ! = NULL ; u = u - > next ) {
const RailVehicleInfo * rvi_u = RailVehInfo ( u - > engine_type ) ;
uint16 veh_len ;
// Update the v->first cache. This is faster than having to brute force it later.
if ( u - > first = = NULL ) u - > first = v ;
// update the 'first engine'
u - > u . rail . first_engine = ( v = = u ) ? INVALID_ENGINE : first_engine ;
u - > u . rail . railtype = GetEngine ( u - > engine_type ) - > railtype ;
if ( IsTrainEngine ( u ) ) first_engine = u - > engine_type ;
if ( rvi_u - > visual_effect ! = 0 ) {
u - > u . rail . cached_vis_effect = rvi_u - > visual_effect ;
} else {
if ( IsTrainWagon ( u ) | | IsArticulatedPart ( u ) ) {
// Wagons and articulated parts have no effect by default
u - > u . rail . cached_vis_effect = 0x40 ;
} else if ( rvi_u - > engclass = = 0 ) {
// Steam is offset by -4 units
u - > u . rail . cached_vis_effect = 4 ;
} else {
// Diesel fumes and sparks come from the centre
u - > u . rail . cached_vis_effect = 8 ;
}
}
if ( ! IsArticulatedPart ( u ) ) {
// check if its a powered wagon
CLRBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ;
/* Check powered wagon / visual effect callback */
if ( HASBIT ( EngInfo ( u - > engine_type ) - > callbackmask , CBM_WAGON_POWER ) ) {
uint16 callback = GetVehicleCallback ( CBID_TRAIN_WAGON_POWER , 0 , 0 , u - > engine_type , u ) ;
if ( callback ! = CALLBACK_FAILED ) u - > u . rail . cached_vis_effect = callback ;
}
if ( ( rvi_v - > pow_wag_power ! = 0 ) & & ( rvi_u - > flags & RVI_WAGON ) & & UsesWagonOverride ( u ) ) {
if ( u - > u . rail . cached_vis_effect < 0x40 ) {
/* wagon is powered */
SETBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ; // cache 'powered' status
}
}
/* Do not count powered wagons for the compatible railtypes, as wagons always
have railtype normal */
if ( rvi_u - > power > 0 ) {
v - > u . rail . compatible_railtypes | = GetRailTypeInfo ( u - > u . rail . railtype ) - > powered_railtypes ;
}
/* Some electric engines can be allowed to run on normal rail. It happens to all
* existing electric engines when elrails are disabled and then re - enabled */
if ( HASBIT ( u - > u . rail . flags , VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL ) ) {
u - > u . rail . railtype = RAILTYPE_RAIL ;
u - > u . rail . compatible_railtypes | = ( 1 < < RAILTYPE_RAIL ) ;
}
// max speed is the minimum of the speed limits of all vehicles in the consist
if ( ! ( rvi_u - > flags & RVI_WAGON ) | | _patches . wagon_speed_limits )
if ( rvi_u - > max_speed ! = 0 & & ! UsesWagonOverride ( u ) )
max_speed = min ( rvi_u - > max_speed , max_speed ) ;
}
// check the vehicle length (callback)
veh_len = CALLBACK_FAILED ;
if ( HASBIT ( EngInfo ( u - > engine_type ) - > callbackmask , CBM_VEHICLE_LENGTH ) ) {
veh_len = GetVehicleCallback ( CBID_TRAIN_VEHICLE_LENGTH , 0 , 0 , u - > engine_type , u ) ;
}
if ( veh_len = = CALLBACK_FAILED ) veh_len = rvi_u - > shorten_factor ;
veh_len = clamp ( veh_len , 0 , u - > next = = NULL ? 7 : 5 ) ; // the clamp on vehicles not the last in chain is stricter, as too short wagons can break the 'follow next vehicle' code
u - > u . rail . cached_veh_length = 8 - veh_len ;
v - > u . rail . cached_total_length + = u - > u . rail . cached_veh_length ;
} ;
// store consist weight/max speed in cache
v - > u . rail . cached_max_speed = max_speed ;
TrainPowerChanged ( v ) ;
// recalculate cached weights too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added)
TrainCargoChanged ( v ) ;
}
/* These two arrays are used for realistic acceleration. XXX: How should they
* be interpreted ? */
static const byte _curve_neighbours45 [ 8 ] [ 2 ] = {
{ 7 , 1 } ,
{ 0 , 2 } ,
{ 1 , 3 } ,
{ 2 , 4 } ,
{ 3 , 5 } ,
{ 4 , 6 } ,
{ 5 , 7 } ,
{ 6 , 0 } ,
} ;
static const byte _curve_neighbours90 [ 8 ] [ 2 ] = {
{ 6 , 2 } ,
{ 7 , 3 } ,
{ 0 , 4 } ,
{ 1 , 5 } ,
{ 2 , 6 } ,
{ 3 , 7 } ,
{ 4 , 0 } ,
{ 5 , 1 } ,
} ;
enum AccelType {
AM_ACCEL ,
AM_BRAKE
} ;
static bool TrainShouldStop ( const Vehicle * v , TileIndex tile )
{
const Order * o = & v - > current_order ;
StationID sid = GetStationIndex ( tile ) ;
assert ( v - > type = = VEH_Train ) ;
//When does a train drive through a station
//first we deal with the "new nonstop handling"
if ( _patches . new_nonstop & & o - > flags & OF_NON_STOP & & sid = = o - > dest ) {
return false ;
}
if ( v - > last_station_visited = = sid ) return false ;
if ( sid ! = o - > dest & & ( o - > flags & OF_NON_STOP | | _patches . new_nonstop ) ) {
return false ;
}
return true ;
}
//new acceleration
static int GetTrainAcceleration ( Vehicle * v , bool mode )
{
const Vehicle * u ;
int num = 0 ; //number of vehicles, change this into the number of axles later
int power = 0 ;
int mass = 0 ;
int max_speed = 2000 ;
int area = 120 ;
int friction = 35 ; //[1e-3]
int drag_coeff = 20 ; //[1e-4]
int incl = 0 ;
int resistance ;
int speed = v - > cur_speed ; //[mph]
int force = 0x3FFFFFFF ;
int pos = 0 ;
int lastpos = - 1 ;
int curvecount [ 2 ] = { 0 , 0 } ;
int sum = 0 ;
int numcurve = 0 ;
speed * = 10 ;
speed / = 16 ;
//first find the curve speed limit
for ( u = v ; u - > next ! = NULL ; u = u - > next , pos + + ) {
Direction dir = u - > direction ;
Direction ndir = u - > next - > direction ;
int i ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( _curve_neighbours45 [ dir ] [ i ] = = ndir ) {
curvecount [ i ] + + ;
if ( lastpos ! = - 1 ) {
numcurve + + ;
sum + = pos - lastpos ;
if ( pos - lastpos = = 1 ) {
max_speed = 88 ;
}
}
lastpos = pos ;
}
}
//if we have a 90 degree turn, fix the speed limit to 60
if ( _curve_neighbours90 [ dir ] [ 0 ] = = ndir | |
_curve_neighbours90 [ dir ] [ 1 ] = = ndir ) {
max_speed = 61 ;
}
}
if ( numcurve > 0 ) sum / = numcurve ;
if ( ( curvecount [ 0 ] ! = 0 | | curvecount [ 1 ] ! = 0 ) & & max_speed > 88 ) {
int total = curvecount [ 0 ] + curvecount [ 1 ] ;
if ( curvecount [ 0 ] = = 1 & & curvecount [ 1 ] = = 1 ) {
max_speed = 0xFFFF ;
} else if ( total > 1 ) {
max_speed = 232 - ( 13 - clamp ( sum , 1 , 12 ) ) * ( 13 - clamp ( sum , 1 , 12 ) ) ;
}
}
max_speed + = ( max_speed / 2 ) * v - > u . rail . railtype ;
if ( IsTileType ( v - > tile , MP_STATION ) & & IsFrontEngine ( v ) ) {
if ( TrainShouldStop ( v , v - > tile ) ) {
uint station_length = GetPlatformLength ( v - > tile , DirToDiagDir ( v - > direction ) ) ;
int delta_v ;
max_speed = 120 ;
delta_v = v - > cur_speed / ( station_length + 1 ) ;
if ( v - > max_speed > ( v - > cur_speed - delta_v ) )
max_speed = v - > cur_speed - ( delta_v / 10 ) ;
max_speed = max ( max_speed , 25 * station_length ) ;
}
}
mass = v - > u . rail . cached_weight ;
power = v - > u . rail . cached_power * 746 ;
max_speed = min ( max_speed , v - > u . rail . cached_max_speed ) ;
for ( u = v ; u ! = NULL ; u = u - > next ) {
num + + ;
drag_coeff + = 3 ;
if ( u - > u . rail . track = = 0x80 ) max_speed = min ( max_speed , 61 ) ;
if ( HASBIT ( u - > u . rail . flags , VRF_GOINGUP ) ) {
incl + = u - > u . rail . cached_veh_weight * 60 ; //3% slope, quite a bit actually
} else if ( HASBIT ( u - > u . rail . flags , VRF_GOINGDOWN ) ) {
incl - = u - > u . rail . cached_veh_weight * 60 ;
}
}
v - > max_speed = max_speed ;
if ( v - > u . rail . railtype ! = RAILTYPE_MAGLEV ) {
resistance = 13 * mass / 10 ;
resistance + = 60 * num ;
resistance + = friction * mass * speed / 1000 ;
resistance + = ( area * drag_coeff * speed * speed ) / 10000 ;
} else {
resistance = ( area * ( drag_coeff / 2 ) * speed * speed ) / 10000 ;
}
resistance + = incl ;
resistance * = 4 ; //[N]
if ( speed > 0 ) {
switch ( v - > u . rail . railtype ) {
case RAILTYPE_RAIL :
case RAILTYPE_ELECTRIC :
case RAILTYPE_MONO :
force = power / speed ; //[N]
force * = 22 ;
force / = 10 ;
break ;
case RAILTYPE_MAGLEV :
force = power / 25 ;
break ;
}
} else {
//"kickoff" acceleration
force = ( mass * 8 ) + resistance ;
}
if ( force < = 0 ) force = 10000 ;
if ( v - > u . rail . railtype ! = RAILTYPE_MAGLEV ) force = min ( force , mass * 10 * 200 ) ;
if ( mode = = AM_ACCEL ) {
return ( force - resistance ) / ( mass * 4 ) ;
} else {
return min ( ( - force - resistance ) / ( mass * 4 ) , 10000 / ( mass * 4 ) ) ;
}
}
static void UpdateTrainAcceleration ( Vehicle * v )
{
uint power = 0 ;
uint weight = 0 ;
assert ( IsFrontEngine ( v ) ) ;
weight = v - > u . rail . cached_weight ;
power = v - > u . rail . cached_power ;
v - > max_speed = v - > u . rail . cached_max_speed ;
assert ( weight ! = 0 ) ;
v - > acceleration = clamp ( power / weight * 4 , 1 , 255 ) ;
}
int GetTrainImage ( const Vehicle * v , Direction direction )
{
int img = v - > spritenum ;
int base ;
if ( HASBIT ( v - > u . rail . flags , VRF_REVERSE_DIRECTION ) ) direction = ReverseDir ( direction ) ;
if ( is_custom_sprite ( img ) ) {
base = GetCustomVehicleSprite ( v , direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE ( img ) ) ;
if ( base ! = 0 ) return base ;
img = orig_rail_vehicle_info [ v - > engine_type ] . image_index ;
}
base = _engine_sprite_base [ img ] + ( ( direction + _engine_sprite_add [ img ] ) & _engine_sprite_and [ img ] ) ;
if ( v - > cargo_count > = v - > cargo_cap / 2 ) base + = _wagon_full_adder [ img ] ;
return base ;
}
void DrawTrainEngine ( int x , int y , EngineID engine , uint32 image_ormod )
{
const RailVehicleInfo * rvi = RailVehInfo ( engine ) ;
int img = rvi - > image_index ;
uint32 image = 0 ;
if ( is_custom_sprite ( img ) ) {
image = GetCustomVehicleIcon ( engine , DIR_W ) ;
if ( image = = 0 ) {
img = orig_rail_vehicle_info [ engine ] . image_index ;
} else {
y + = _traininfo_vehicle_pitch ;
}
}
if ( image = = 0 ) {
image = ( 6 & _engine_sprite_and [ img ] ) + _engine_sprite_base [ img ] ;
}
if ( rvi - > flags & RVI_MULTIHEAD ) {
DrawSprite ( image | image_ormod , x - 14 , y ) ;
x + = 15 ;
image = 0 ;
if ( is_custom_sprite ( img ) ) {
image = GetCustomVehicleIcon ( engine , 2 ) ;
if ( image = = 0 ) img = orig_rail_vehicle_info [ engine ] . image_index ;
}
if ( image = = 0 ) {
image =
( ( 6 + _engine_sprite_add [ img + 1 ] ) & _engine_sprite_and [ img + 1 ] ) +
_engine_sprite_base [ img + 1 ] ;
}
}
DrawSprite ( image | image_ormod , x , y ) ;
}
uint CountArticulatedParts ( EngineID engine_type )
{
uint16 callback ;
uint i ;
if ( ! HASBIT ( EngInfo ( engine_type ) - > callbackmask , CBM_ARTIC_ENGINE ) ) return 0 ;
for ( i = 1 ; i < 10 ; i + + ) {
callback = GetVehicleCallback ( CBID_TRAIN_ARTIC_ENGINE , i , 0 , engine_type , NULL ) ;
if ( callback = = CALLBACK_FAILED | | callback = = 0xFF ) break ;
}
return i - 1 ;
}
static void AddArticulatedParts ( Vehicle * * vl )
{
const RailVehicleInfo * rvi_artic ;
EngineID engine_type ;
Vehicle * v = vl [ 0 ] ;
Vehicle * u = v ;
uint16 callback ;
bool flip_image ;
uint i ;
if ( ! HASBIT ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_ARTIC_ENGINE ) ) return ;
for ( i = 1 ; i < 10 ; i + + ) {
callback = GetVehicleCallback ( CBID_TRAIN_ARTIC_ENGINE , i , 0 , v - > engine_type , v ) ;
if ( callback = = CALLBACK_FAILED | | callback = = 0xFF ) return ;
/* Attempt to use pre-allocated vehicles until they run out. This can happen
* if the callback returns different values depending on the cargo type . */
u - > next = vl [ i ] ;
if ( u - > next = = NULL ) u - > next = AllocateVehicle ( ) ;
if ( u - > next = = NULL ) return ;
u = u - > next ;
engine_type = GB ( callback , 0 , 7 ) ;
flip_image = HASBIT ( callback , 7 ) ;
rvi_artic = RailVehInfo ( engine_type ) ;
// get common values from first engine
u - > direction = v - > direction ;
u - > owner = v - > owner ;
u - > tile = v - > tile ;
u - > x_pos = v - > x_pos ;
u - > y_pos = v - > y_pos ;
u - > z_pos = v - > z_pos ;
u - > z_height = v - > z_height ;
u - > u . rail . track = v - > u . rail . track ;
u - > u . rail . railtype = v - > u . rail . railtype ;
u - > build_year = v - > build_year ;
u - > vehstatus = v - > vehstatus & ~ VS_STOPPED ;
u - > u . rail . first_engine = v - > engine_type ;
// get more settings from rail vehicle info
u - > spritenum = rvi_artic - > image_index ;
if ( flip_image ) u - > spritenum + + ;
u - > cargo_type = rvi_artic - > cargo_type ;
u - > cargo_subtype = 0 ;
u - > cargo_cap = rvi_artic - > capacity ;
u - > max_speed = 0 ;
u - > max_age = 0 ;
u - > engine_type = engine_type ;
u - > value = 0 ;
u - > type = VEH_Train ;
u - > subtype = 0 ;
SetArticulatedPart ( u ) ;
u - > cur_image = 0xAC2 ;
u - > random_bits = VehicleRandomBits ( ) ;
VehiclePositionChanged ( u ) ;
}
}
static int32 CmdBuildRailWagon ( EngineID engine , TileIndex tile , uint32 flags )
{
int32 value ;
const RailVehicleInfo * rvi ;
uint num_vehicles ;
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
rvi = RailVehInfo ( engine ) ;
value = ( rvi - > base_cost * _price . build_railwagon ) > > 8 ;
num_vehicles = 1 + CountArticulatedParts ( engine ) ;
if ( ! ( flags & DC_QUERY_COST ) ) {
Vehicle * vl [ 11 ] ; // Allow for wagon and upto 10 artic parts.
Vehicle * v ;
int x ;
int y ;
memset ( & vl , 0 , sizeof ( vl ) ) ;
if ( ! AllocateVehicles ( vl , num_vehicles ) )
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
if ( flags & DC_EXEC ) {
Vehicle * u , * w ;
DiagDirection dir ;
v = vl [ 0 ] ;
v - > spritenum = rvi - > image_index ;
u = NULL ;
FOR_ALL_VEHICLES ( w ) {
if ( w - > type = = VEH_Train & & w - > tile = = tile & &
IsFreeWagon ( w ) & & w - > engine_type = = engine ) {
u = GetLastVehicleInChain ( w ) ;
break ;
}
}
v - > engine_type = engine ;
dir = GetRailDepotDirection ( tile ) ;
v - > direction = DiagDirToDir ( dir ) ;
v - > tile = tile ;
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
x = TileX ( tile ) * TILE_SIZE | _vehicle_initial_x_fract [ dir ] ;
y = TileY ( tile ) * TILE_SIZE | _vehicle_initial_y_fract [ dir ] ;
v - > x_pos = x ;
v - > y_pos = y ;
v - > z_pos = GetSlopeZ ( x , y ) ;
v - > owner = _current_player ;
v - > z_height = 6 ;
v - > u . rail . track = 0x80 ;
v - > vehstatus = VS_HIDDEN | VS_DEFPAL ;
v - > subtype = 0 ;
SetTrainWagon ( v ) ;
if ( u ! = NULL ) {
u - > next = v ;
} else {
SetFreeWagon ( v ) ;
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
v - > cargo_type = rvi - > cargo_type ;
v - > cargo_subtype = 0 ;
v - > cargo_cap = rvi - > capacity ;
v - > value = value ;
// v->day_counter = 0;
v - > u . rail . railtype = GetEngine ( engine ) - > railtype ;
v - > build_year = _cur_year ;
v - > type = VEH_Train ;
v - > cur_image = 0xAC2 ;
v - > random_bits = VehicleRandomBits ( ) ;
AddArticulatedParts ( vl ) ;
_new_vehicle_id = v - > index ;
VehiclePositionChanged ( v ) ;
TrainConsistChanged ( GetFirstVehicleInChain ( v ) ) ;
GetPlayer ( _current_player ) - > num_engines [ engine ] + + ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
if ( IsLocalPlayer ( ) ) {
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ; // updates the replace Train window
}
}
}
return value ;
}
// Move all free vehicles in the depot to the train
static void NormalizeTrainVehInDepot ( const Vehicle * u )
{
const Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train & & IsFreeWagon ( v ) & &
v - > tile = = u - > tile & &
v - > u . rail . track = = 0x80 ) {
if ( CmdFailed ( DoCommand ( 0 , v - > index | ( u - > index < < 16 ) , 1 , DC_EXEC ,
CMD_MOVE_RAIL_VEHICLE ) ) )
break ;
}
}
}
static int32 EstimateTrainCost ( const RailVehicleInfo * rvi )
{
return rvi - > base_cost * ( _price . build_railvehicle > > 3 ) > > 5 ;
}
static void AddRearEngineToMultiheadedTrain ( Vehicle * v , Vehicle * u , bool building )
{
u - > direction = v - > direction ;
u - > owner = v - > owner ;
u - > tile = v - > tile ;
u - > x_pos = v - > x_pos ;
u - > y_pos = v - > y_pos ;
u - > z_pos = v - > z_pos ;
u - > z_height = 6 ;
u - > u . rail . track = 0x80 ;
u - > vehstatus = v - > vehstatus & ~ VS_STOPPED ;
u - > subtype = 0 ;
SetMultiheaded ( u ) ;
u - > spritenum = v - > spritenum + 1 ;
u - > cargo_type = v - > cargo_type ;
u - > cargo_subtype = v - > cargo_subtype ;
u - > cargo_cap = v - > cargo_cap ;
u - > u . rail . railtype = v - > u . rail . railtype ;
if ( building ) v - > next = u ;
u - > engine_type = v - > engine_type ;
u - > build_year = v - > build_year ;
if ( building ) v - > value > > = 1 ;
u - > value = v - > value ;
u - > type = VEH_Train ;
u - > cur_image = 0xAC2 ;
u - > random_bits = VehicleRandomBits ( ) ;
VehiclePositionChanged ( u ) ;
}
/** Build a railroad vehicle.
* @ param tile tile of the depot where rail - vehicle is built
* @ param p1 engine type id
* @ param p2 bit 0 when set , the train will get number 0 , otherwise it will get a free number
* bit 1 prevents any free cars from being added to the train
*/
int32 CmdBuildRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
const RailVehicleInfo * rvi ;
int value ;
Vehicle * v ;
UnitID unit_num ;
Engine * e ;
uint num_vehicles ;
/* Check if the engine-type is valid (for the player) */
if ( ! IsEngineBuildable ( p1 , VEH_Train ) ) return_cmd_error ( STR_ENGINE_NOT_BUILDABLE ) ;
/* Check if the train is actually being built in a depot belonging
* to the player . Doesn ' t matter if only the cost is queried */
if ( ! ( flags & DC_QUERY_COST ) ) {
if ( ! IsTileDepotType ( tile , TRANSPORT_RAIL ) ) return CMD_ERROR ;
if ( ! IsTileOwner ( tile , _current_player ) ) return CMD_ERROR ;
}
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
rvi = RailVehInfo ( p1 ) ;
e = GetEngine ( p1 ) ;
/* Check if depot and new engine uses the same kind of tracks */
/* We need to see if the engine got power on the tile to avoid eletric engines in non-electric depots */
if ( ! HasPowerOnRail ( e - > railtype , GetRailType ( tile ) ) ) return CMD_ERROR ;
if ( rvi - > flags & RVI_WAGON ) return CmdBuildRailWagon ( p1 , tile , flags ) ;
value = EstimateTrainCost ( rvi ) ;
num_vehicles = ( rvi - > flags & RVI_MULTIHEAD ) ? 2 : 1 ;
num_vehicles + = CountArticulatedParts ( p1 ) ;
if ( ! ( flags & DC_QUERY_COST ) ) {
Vehicle * vl [ 12 ] ; // Allow for upto 10 artic parts and dual-heads
memset ( & vl , 0 , sizeof ( vl ) ) ;
if ( ! AllocateVehicles ( vl , num_vehicles ) | | IsOrderPoolFull ( ) )
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
v = vl [ 0 ] ;
unit_num = HASBIT ( p2 , 0 ) ? 0 : GetFreeUnitNumber ( VEH_Train ) ;
if ( unit_num > _patches . max_trains )
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
if ( flags & DC_EXEC ) {
DiagDirection dir = GetRailDepotDirection ( tile ) ;
int x = TileX ( tile ) * TILE_SIZE + _vehicle_initial_x_fract [ dir ] ;
int y = TileY ( tile ) * TILE_SIZE + _vehicle_initial_y_fract [ dir ] ;
v - > unitnumber = unit_num ;
v - > direction = DiagDirToDir ( dir ) ;
v - > tile = tile ;
v - > owner = _current_player ;
v - > x_pos = x ;
v - > y_pos = y ;
v - > z_pos = GetSlopeZ ( x , y ) ;
v - > z_height = 6 ;
v - > u . rail . track = 0x80 ;
v - > vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL ;
v - > spritenum = rvi - > image_index ;
v - > cargo_type = rvi - > cargo_type ;
v - > cargo_subtype = 0 ;
v - > cargo_cap = rvi - > capacity ;
v - > max_speed = rvi - > max_speed ;
v - > value = value ;
v - > last_station_visited = INVALID_STATION ;
v - > dest_tile = 0 ;
v - > engine_type = p1 ;
v - > reliability = e - > reliability ;
v - > reliability_spd_dec = e - > reliability_spd_dec ;
v - > max_age = e - > lifelength * 366 ;
v - > string_id = STR_SV_TRAIN_NAME ;
v - > u . rail . railtype = e - > railtype ;
_new_vehicle_id = v - > index ;
v - > service_interval = _patches . servint_trains ;
v - > date_of_last_service = _date ;
v - > build_year = _cur_year ;
v - > type = VEH_Train ;
v - > cur_image = 0xAC2 ;
v - > random_bits = VehicleRandomBits ( ) ;
v - > subtype = 0 ;
SetFrontEngine ( v ) ;
SetTrainEngine ( v ) ;
VehiclePositionChanged ( v ) ;
if ( rvi - > flags & RVI_MULTIHEAD ) {
SetMultiheaded ( v ) ;
AddRearEngineToMultiheadedTrain ( vl [ 0 ] , vl [ 1 ] , true ) ;
/* Now we need to link the front and rear engines together
* other_multiheaded_part is the pointer that links to the other half of the engine
* vl [ 0 ] is the front and vl [ 1 ] is the rear
*/
vl [ 0 ] - > u . rail . other_multiheaded_part = vl [ 1 ] ;
vl [ 1 ] - > u . rail . other_multiheaded_part = vl [ 0 ] ;
} else {
AddArticulatedParts ( vl ) ;
}
TrainConsistChanged ( v ) ;
UpdateTrainAcceleration ( v ) ;
if ( ! HASBIT ( p2 , 1 ) ) { // check if the cars should be added to the new vehicle
NormalizeTrainVehInDepot ( v ) ;
}
GetPlayer ( _current_player ) - > num_engines [ p1 ] + + ;
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , tile ) ;
RebuildVehicleLists ( ) ;
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
if ( IsLocalPlayer ( ) ) {
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ; // updates the replace Train window
}
}
}
return value ;
}
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
/* Check if all the wagons of the given train are in a depot, returns the
* number of cars ( including loco ) then . If not it returns - 1 */
int CheckTrainInDepot ( const Vehicle * v , bool needs_to_be_stopped )
{
int count ;
TileIndex tile = v - > tile ;
/* check if stopped in a depot */
if ( ! IsTileDepotType ( tile , TRANSPORT_RAIL ) | | v - > cur_speed ! = 0 ) return - 1 ;
count = 0 ;
for ( ; v ! = NULL ; v = v - > next ) {
/* This count is used by the depot code to determine the number of engines
* in the consist . Exclude articulated parts so that autoreplacing to
* engines with more articulated parts than before works correctly .
*
* Also skip counting rear ends of multiheaded engines */
if ( ! IsArticulatedPart ( v ) & & ! ( ! IsTrainEngine ( v ) & & IsMultiheaded ( v ) ) ) count + + ;
if ( v - > u . rail . track ! = 0x80 | | v - > tile ! = tile | |
( IsFrontEngine ( v ) & & needs_to_be_stopped & & ! ( v - > vehstatus & VS_STOPPED ) ) ) {
return - 1 ;
}
}
return count ;
}
/* Used to check if the train is inside the depot and verifying that the VS_STOPPED flag is set */
inline int CheckTrainStoppedInDepot ( const Vehicle * v )
{
return CheckTrainInDepot ( v , true ) ;
}
/* Used to check if the train is inside the depot, but not checking the VS_STOPPED flag */
inline bool CheckTrainIsInsideDepot ( const Vehicle * v )
{
return ( CheckTrainInDepot ( v , false ) > 0 ) ;
}
/**
* Unlink a rail wagon from the consist .
* @ param v Vehicle to remove .
* @ param first The first vehicle of the consist .
* @ return The first vehicle of the consist .
*/
static Vehicle * UnlinkWagon ( Vehicle * v , Vehicle * first )
{
Vehicle * u ;
// unlinking the first vehicle of the chain?
if ( v = = first ) {
v = GetNextVehicle ( v ) ;
if ( v = = NULL ) return NULL ;
if ( IsTrainWagon ( v ) ) SetFreeWagon ( v ) ;
return v ;
}
for ( u = first ; GetNextVehicle ( u ) ! = v ; u = GetNextVehicle ( u ) ) { }
GetLastEnginePart ( u ) - > next = GetNextVehicle ( v ) ;
return first ;
}
static Vehicle * FindGoodVehiclePos ( const Vehicle * src )
{
Vehicle * dst ;
EngineID eng = src - > engine_type ;
TileIndex tile = src - > tile ;
FOR_ALL_VEHICLES ( dst ) {
if ( dst - > type = = VEH_Train & & IsFreeWagon ( dst ) & & dst - > tile = = tile ) {
// check so all vehicles in the line have the same engine.
Vehicle * v = dst ;
while ( v - > engine_type = = eng ) {
v = v - > next ;
if ( v = = NULL ) return dst ;
}
}
}
return NULL ;
}
/*
* add a vehicle v behind vehicle dest
* use this function since it sets flags as needed
*/
static void AddWagonToConsist ( Vehicle * v , Vehicle * dest )
{
UnlinkWagon ( v , GetFirstVehicleInChain ( v ) ) ;
if ( dest = = NULL ) return ;
v - > next = dest - > next ;
dest - > next = v ;
ClearFreeWagon ( v ) ;
ClearFrontEngine ( v ) ;
}
/*
* move around on the train so rear engines are placed correctly according to the other engines
* always call with the front engine
*/
static void NormaliseTrainConsist ( Vehicle * v )
{
Vehicle * u ;
if ( IsFreeWagon ( v ) ) return ;
assert ( IsFrontEngine ( v ) ) ;
for ( ; v ! = NULL ; v = GetNextVehicle ( v ) ) {
if ( ! IsMultiheaded ( v ) | | ! IsTrainEngine ( v ) ) continue ;
/* make sure that there are no free cars before next engine */
for ( u = v ; u - > next ! = NULL & & ! IsTrainEngine ( u - > next ) ; u = u - > next ) ;
if ( u = = v - > u . rail . other_multiheaded_part ) continue ;
AddWagonToConsist ( v - > u . rail . other_multiheaded_part , u ) ;
}
}
/** Move a rail vehicle around inside the depot.
* @ param tile unused
* @ param p1 various bitstuffed elements
* - p1 ( bit 0 - 15 ) source vehicle index
* - p1 ( bit 16 - 31 ) what wagon to put the source wagon AFTER , XXX - INVALID_VEHICLE to make a new line
* @ param p2 ( bit 0 ) move all vehicles following the source vehicle
*/
int32 CmdMoveRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
VehicleID s = GB ( p1 , 0 , 16 ) ;
VehicleID d = GB ( p1 , 16 , 16 ) ;
Vehicle * src , * dst , * src_head , * dst_head ;
if ( ! IsValidVehicleID ( s ) ) return CMD_ERROR ;
src = GetVehicle ( s ) ;
if ( src - > type ! = VEH_Train ) return CMD_ERROR ;
// if nothing is selected as destination, try and find a matching vehicle to drag to.
if ( d = = INVALID_VEHICLE ) {
dst = IsTrainEngine ( src ) ? NULL : FindGoodVehiclePos ( src ) ;
} else {
dst = GetVehicle ( d ) ;
}
// if an articulated part is being handled, deal with its parent vehicle
while ( IsArticulatedPart ( src ) ) src = GetPrevVehicleInChain ( src ) ;
if ( dst ! = NULL ) {
while ( IsArticulatedPart ( dst ) ) dst = GetPrevVehicleInChain ( dst ) ;
}
// don't move the same vehicle..
if ( src = = dst ) return 0 ;
/* the player must be the owner */
if ( ! CheckOwnership ( src - > owner ) | | ( dst ! = NULL & & ! CheckOwnership ( dst - > owner ) ) )
return CMD_ERROR ;
/* locate the head of the two chains */
src_head = GetFirstVehicleInChain ( src ) ;
dst_head = NULL ;
if ( dst ! = NULL ) {
dst_head = GetFirstVehicleInChain ( dst ) ;
// Now deal with articulated part of destination wagon
dst = GetLastEnginePart ( dst ) ;
}
if ( dst ! = NULL & & IsMultiheaded ( dst ) & & ! IsTrainEngine ( dst ) & & IsTrainWagon ( src ) ) {
/* We are moving a wagon to the rear part of a multiheaded engine */
if ( dst - > next = = NULL ) {
/* It's the last one, so we will add the wagon just before the rear engine */
dst = GetPrevVehicleInChain ( dst ) ;
/* Now if the vehicle we want to link to is the vehicle itself, drop out */
if ( dst = = src ) return CMD_ERROR ;
// if dst is NULL, it means that dst got a rear multiheaded engine as first engine. We can't use that
if ( dst = = NULL ) return CMD_ERROR ;
} else {
/* there are more units on this train, so we will add the wagon after the next one*/
dst = dst - > next ;
}
}
if ( IsTrainEngine ( src ) & & dst_head ! = NULL ) {
/* we need to make sure that we didn't place it between a pair of multiheaded engines */
Vehicle * u , * engine = NULL ;
for ( u = dst_head ; u ! = NULL ; u = u - > next ) {
if ( IsTrainEngine ( u ) & & IsMultiheaded ( u ) & & u - > u . rail . other_multiheaded_part ! = NULL ) {
engine = u ;
}
if ( engine ! = NULL & & engine - > u . rail . other_multiheaded_part = = u ) {
engine = NULL ;
}
if ( u = = dst ) {
if ( engine ! = NULL ) dst = engine - > u . rail . other_multiheaded_part ;
break ;
}
}
}
if ( IsMultiheaded ( src ) & & ! IsTrainEngine ( src ) ) return_cmd_error ( STR_REAR_ENGINE_FOLLOW_FRONT_ERROR ) ;
// when moving all wagons, we can't have the same src_head and dst_head
if ( HASBIT ( p2 , 0 ) & & src_head = = dst_head ) return 0 ;
{
int src_len = 0 ;
int max_len = _patches . mammoth_trains ? 100 : 9 ;
// check if all vehicles in the source train are stopped inside a depot.
src_len = CheckTrainStoppedInDepot ( src_head ) ;
if ( src_len < 0 ) return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
// check the destination row if the source and destination aren't the same.
if ( src_head ! = dst_head ) {
int dst_len = 0 ;
if ( dst_head ! = NULL ) {
// check if all vehicles in the dest train are stopped.
dst_len = CheckTrainStoppedInDepot ( dst_head ) ;
if ( dst_len < 0 ) return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
assert ( dst_head - > tile = = src_head - > tile ) ;
}
// We are moving between rows, so only count the wagons from the source
// row that are being moved.
if ( HASBIT ( p2 , 0 ) ) {
const Vehicle * u ;
for ( u = src_head ; u ! = src & & u ! = NULL ; u = GetNextVehicle ( u ) )
src_len - - ;
} else {
// If moving only one vehicle, just count that.
src_len = 1 ;
}
if ( src_len + dst_len > max_len ) {
// Abort if we're adding too many wagons to a train.
if ( dst_head ! = NULL & & IsFrontEngine ( dst_head ) ) return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
// Abort if we're making a train on a new row.
if ( dst_head = = NULL & & IsTrainEngine ( src ) ) return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
}
} else {
// Abort if we're creating a new train on an existing row.
if ( src_len > max_len & & src = = src_head & & IsTrainEngine ( GetNextVehicle ( src_head ) ) )
return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
}
}
// moving a loco to a new line?, then we need to assign a unitnumber.
if ( dst = = NULL & & ! IsFrontEngine ( src ) & & IsTrainEngine ( src ) ) {
UnitID unit_num = GetFreeUnitNumber ( VEH_Train ) ;
if ( unit_num > _patches . max_trains )
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
if ( flags & DC_EXEC ) src - > unitnumber = unit_num ;
}
if ( dst_head ! = NULL ) {
/* Check NewGRF Callback 0x1D */
uint16 callback = GetVehicleCallbackParent ( CBID_TRAIN_ALLOW_WAGON_ATTACH , 0 , 0 , dst_head - > engine_type , src , dst_head ) ;
if ( callback ! = CALLBACK_FAILED ) {
if ( callback = = 0xFD ) return_cmd_error ( STR_INCOMPATIBLE_RAIL_TYPES ) ;
if ( callback < 0xFD ) {
StringID error = GetGRFStringID ( GetEngineGRFID ( dst_head - > engine_type ) , 0xD000 + callback ) ;
return_cmd_error ( error ) ;
}
}
}
/* do it? */
if ( flags & DC_EXEC ) {
/* clear the ->first cache */
{
Vehicle * u ;
for ( u = src_head ; u ! = NULL ; u = u - > next ) u - > first = NULL ;
for ( u = dst_head ; u ! = NULL ; u = u - > next ) u - > first = NULL ;
}
if ( HASBIT ( p2 , 0 ) ) {
// unlink ALL wagons
if ( src ! = src_head ) {
Vehicle * v = src_head ;
while ( GetNextVehicle ( v ) ! = src ) v = GetNextVehicle ( v ) ;
GetLastEnginePart ( v ) - > next = NULL ;
} else {
InvalidateWindowData ( WC_VEHICLE_DEPOT , src_head - > tile ) ; // We removed a line
src_head = NULL ;
}
} else {
// if moving within the same chain, dont use dst_head as it may get invalidated
if ( src_head = = dst_head ) dst_head = NULL ;
// unlink single wagon from linked list
src_head = UnlinkWagon ( src , src_head ) ;
GetLastEnginePart ( src ) - > next = NULL ;
}
if ( dst = = NULL ) {
/* We make a new line in the depot, so we know already that we invalidate the window data */
InvalidateWindowData ( WC_VEHICLE_DEPOT , src - > tile ) ;
// move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4.
if ( IsTrainEngine ( src ) ) {
if ( ! IsFrontEngine ( src ) ) {
// setting the type to 0 also involves setting up the orders field.
SetFrontEngine ( src ) ;
assert ( src - > orders = = NULL ) ;
src - > num_orders = 0 ;
}
} else {
SetFreeWagon ( src ) ;
}
dst_head = src ;
} else {
if ( IsFrontEngine ( src ) ) {
// the vehicle was previously a loco. need to free the order list and delete vehicle windows etc.
DeleteWindowById ( WC_VEHICLE_VIEW , src - > index ) ;
DeleteVehicleOrders ( src ) ;
}
if ( IsFrontEngine ( src ) | | IsFreeWagon ( src ) ) {
InvalidateWindowData ( WC_VEHICLE_DEPOT , src - > tile ) ;
ClearFrontEngine ( src ) ;
ClearFreeWagon ( src ) ;
src - > unitnumber = 0 ; // doesn't occupy a unitnumber anymore.
}
// link in the wagon(s) in the chain.
{
Vehicle * v ;
for ( v = src ; GetNextVehicle ( v ) ! = NULL ; v = GetNextVehicle ( v ) ) ;
GetLastEnginePart ( v ) - > next = dst - > next ;
}
dst - > next = src ;
}
if ( src - > u . rail . other_multiheaded_part ! = NULL ) {
if ( src - > u . rail . other_multiheaded_part = = src_head ) {
src_head = src_head - > next ;
}
AddWagonToConsist ( src - > u . rail . other_multiheaded_part , src ) ;
// previous line set the front engine to the old front. We need to clear that
src - > u . rail . other_multiheaded_part - > first = NULL ;
}
if ( HASBIT ( p2 , 0 ) & & src_head ! = NULL & & src_head ! = src ) {
/* if we stole a rear multiheaded engine, we better give it back to the front end */
Vehicle * engine = NULL , * u ;
for ( u = src_head ; u ! = NULL ; u = u - > next ) {
if ( IsMultiheaded ( u ) ) {
if ( IsTrainEngine ( u ) ) {
engine = u ;
continue ;
}
/* we got the rear engine to match with the front one */
engine = NULL ;
}
}
if ( engine ! = NULL & & engine - > u . rail . other_multiheaded_part ! = NULL ) {
AddWagonToConsist ( engine - > u . rail . other_multiheaded_part , engine ) ;
// previous line set the front engine to the old front. We need to clear that
engine - > u . rail . other_multiheaded_part - > first = NULL ;
}
}
/* If there is an engine behind first_engine we moved away, it should become new first_engine
* To do this , CmdMoveRailVehicle must be called once more
* we can ' t loop forever here because next time we reach this line we will have a front engine */
if ( src_head ! = NULL & & ! IsFrontEngine ( src_head ) & & IsTrainEngine ( src_head ) ) {
CmdMoveRailVehicle ( 0 , flags , src_head - > index | ( INVALID_VEHICLE < < 16 ) , 1 ) ;
src_head = NULL ; // don't do anything more to this train since the new call will do it
}
if ( src_head ! = NULL ) {
NormaliseTrainConsist ( src_head ) ;
TrainConsistChanged ( src_head ) ;
if ( IsFrontEngine ( src_head ) ) {
UpdateTrainAcceleration ( src_head ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , src_head - > index ) ;
/* Update the refit button and window */
InvalidateWindow ( WC_VEHICLE_REFIT , src_head - > index ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , src_head - > index , 12 ) ;
}
/* Update the depot window */
InvalidateWindow ( WC_VEHICLE_DEPOT , src_head - > tile ) ;
} ;
if ( dst_head ! = NULL ) {
NormaliseTrainConsist ( dst_head ) ;
TrainConsistChanged ( dst_head ) ;
if ( IsFrontEngine ( dst_head ) ) {
UpdateTrainAcceleration ( dst_head ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , dst_head - > index ) ;
/* Update the refit button and window */
InvalidateWindowWidget ( WC_VEHICLE_VIEW , dst_head - > index , 12 ) ;
InvalidateWindow ( WC_VEHICLE_REFIT , dst_head - > index ) ;
}
/* Update the depot window */
InvalidateWindow ( WC_VEHICLE_DEPOT , dst_head - > tile ) ;
}
RebuildVehicleLists ( ) ;
}
return 0 ;
}
/** Start/Stop a train.
* @ param tile unused
* @ param p1 train to start / stop
* @ param p2 unused
*/
int32 CmdStartStopTrain ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
uint16 callback ;
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
/* Check if this train can be started/stopped. The callback will fail or
* return 0xFF if it can . */
callback = GetVehicleCallback ( CBID_VEHICLE_START_STOP_CHECK , 0 , 0 , v - > engine_type , v ) ;
if ( callback ! = CALLBACK_FAILED & & callback ! = 0xFF ) {
StringID error = GetGRFStringID ( GetEngineGRFID ( v - > engine_type ) , 0xD000 + callback ) ;
return_cmd_error ( error ) ;
}
if ( v - > vehstatus & VS_STOPPED & & v - > u . rail . cached_power = = 0 ) return_cmd_error ( STR_TRAIN_START_NO_CATENARY ) ;
if ( flags & DC_EXEC ) {
if ( v - > vehstatus & VS_STOPPED & & v - > u . rail . track = = 0x80 ) {
DeleteVehicleNews ( p1 , STR_8814_TRAIN_IS_WAITING_IN_DEPOT ) ;
}
v - > u . rail . days_since_order_progr = 0 ;
v - > vehstatus ^ = VS_STOPPED ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
return 0 ;
}
/** Sell a (single) train wagon/engine.
* @ param tile unused
* @ param p1 the wagon / engine index
* @ param p2 the selling mode
* - p2 = 0 : only sell the single dragged wagon / engine ( and any belonging rear - engines )
* - p2 = 1 : sell the vehicle and all vehicles following it in the chain
if the wagon is dragged , don ' t delete the possibly belonging rear - engine to some front
* - p2 = 2 : when selling attached locos , rearrange all vehicles after it to separate lines ;
* all wagons of the same type will go on the same line . Used by the AI currently
*/
int32 CmdSellRailWagon ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v , * tmp , * first ;
Vehicle * new_f = NULL ;
int32 cost = 0 ;
if ( ! IsValidVehicleID ( p1 ) | | p2 > 2 ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
while ( IsArticulatedPart ( v ) ) v = GetPrevVehicleInChain ( v ) ;
first = GetFirstVehicleInChain ( v ) ;
// make sure the vehicle is stopped in the depot
if ( CheckTrainStoppedInDepot ( first ) < 0 ) {
return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
}
if ( IsMultiheaded ( v ) & & ! IsTrainEngine ( v ) ) return_cmd_error ( STR_REAR_ENGINE_FOLLOW_FRONT_ERROR ) ;
if ( flags & DC_EXEC ) {
if ( v = = first & & IsFrontEngine ( first ) ) {
DeleteWindowById ( WC_VEHICLE_VIEW , first - > index ) ;
}
if ( IsLocalPlayer ( ) & & ( p1 = = 1 | | ! ( RailVehInfo ( v - > engine_type ) - > flags & RVI_WAGON ) ) ) {
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ;
}
InvalidateWindow ( WC_VEHICLE_DEPOT , first - > tile ) ;
RebuildVehicleLists ( ) ;
}
switch ( p2 ) {
case 0 : case 2 : { /* Delete given wagon */
bool switch_engine = false ; // update second wagon to engine?
byte ori_subtype = v - > subtype ; // backup subtype of deleted wagon in case DeleteVehicle() changes
/* 1. Delete the engine, if it is dualheaded also delete the matching
* rear engine of the loco ( from the point of deletion onwards ) */
Vehicle * rear = ( IsMultiheaded ( v ) & &
IsTrainEngine ( v ) ) ? v - > u . rail . other_multiheaded_part : NULL ;
if ( rear ! = NULL ) {
cost - = rear - > value ;
if ( flags & DC_EXEC ) {
UnlinkWagon ( rear , first ) ;
DeleteVehicle ( rear ) ;
}
}
/* 2. We are selling the first engine, some special action might be required
* here , so take attention */
if ( ( flags & DC_EXEC ) & & v = = first ) {
new_f = GetNextVehicle ( first ) ;
/* 2.1 If the first wagon is sold, update the first-> pointers to NULL */
for ( tmp = first ; tmp ! = NULL ; tmp = tmp - > next ) tmp - > first = NULL ;
/* 2.2 If there are wagons present after the deleted front engine, check
* if the second wagon ( which will be first ) is an engine . If it is one ,
* promote it as a new train , retaining the unitnumber , orders */
if ( new_f ! = NULL ) {
if ( IsTrainEngine ( new_f ) ) {
switch_engine = true ;
/* Copy important data from the front engine */
new_f - > unitnumber = first - > unitnumber ;
new_f - > current_order = first - > current_order ;
new_f - > cur_order_index = first - > cur_order_index ;
new_f - > orders = first - > orders ;
if ( first - > prev_shared ! = NULL ) {
first - > prev_shared - > next_shared = new_f ;
new_f - > prev_shared = first - > prev_shared ;
}
if ( first - > next_shared ! = NULL ) {
first - > next_shared - > prev_shared = new_f ;
new_f - > next_shared = first - > next_shared ;
}
new_f - > num_orders = first - > num_orders ;
first - > orders = NULL ; // XXX - to not to delete the orders */
if ( IsLocalPlayer ( ) ) ShowTrainViewWindow ( new_f ) ;
}
}
}
/* 3. Delete the requested wagon */
cost - = v - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
DeleteVehicle ( v ) ;
/* 4 If the second wagon was an engine, update it to front_engine
* which UnlinkWagon ( ) has changed to TS_Free_Car */
if ( switch_engine ) SetFrontEngine ( first ) ;
/* 5. If the train still exists, update its acceleration, window, etc. */
if ( first ! = NULL ) {
NormaliseTrainConsist ( first ) ;
TrainConsistChanged ( first ) ;
if ( IsFrontEngine ( first ) ) {
InvalidateWindow ( WC_VEHICLE_DETAILS , first - > index ) ;
InvalidateWindow ( WC_VEHICLE_REFIT , first - > index ) ;
UpdateTrainAcceleration ( first ) ;
}
}
/* (6.) Borked AI. If it sells an engine it expects all wagons lined
* up on a new line to be added to the newly built loco . Replace it is .
* Totally braindead cause building a new engine adds all loco - less
* engines to its train anyways */
if ( p2 = = 2 & & HASBIT ( ori_subtype , Train_Front ) ) {
for ( v = first ; v ! = NULL ; v = tmp ) {
tmp = GetNextVehicle ( v ) ;
DoCommand ( v - > tile , v - > index | INVALID_VEHICLE < < 16 , 0 , DC_EXEC , CMD_MOVE_RAIL_VEHICLE ) ;
}
}
}
} break ;
case 1 : { /* Delete wagon and all wagons after it given certain criteria */
/* Start deleting every vehicle after the selected one
* If we encounter a matching rear - engine to a front - engine
* earlier in the chain ( before deletion ) , leave it alone */
for ( ; v ! = NULL ; v = tmp ) {
tmp = GetNextVehicle ( v ) ;
if ( IsMultiheaded ( v ) ) {
if ( IsTrainEngine ( v ) ) {
/* We got a front engine of a multiheaded set. Now we will sell the rear end too */
Vehicle * rear = v - > u . rail . other_multiheaded_part ;
if ( rear ! = NULL ) {
cost - = rear - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( rear , first ) ;
DeleteVehicle ( rear ) ;
}
}
} else if ( v - > u . rail . other_multiheaded_part ! = NULL ) {
/* The front to this engine is earlier in this train. Do nothing */
continue ;
}
}
cost - = v - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
DeleteVehicle ( v ) ;
}
}
/* 3. If it is still a valid train after selling, update its acceleration and cached values */
if ( flags & DC_EXEC & & first ! = NULL ) {
NormaliseTrainConsist ( first ) ;
TrainConsistChanged ( first ) ;
if ( IsFrontEngine ( first ) ) UpdateTrainAcceleration ( first ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , first - > index ) ;
InvalidateWindow ( WC_VEHICLE_REFIT , first - > index ) ;
}
} break ;
}
return cost ;
}
static void UpdateTrainDeltaXY ( Vehicle * v , Direction direction )
{
# define MKIT(a,b,c,d) ((a&0xFF)<<24) | ((b&0xFF)<<16) | ((c&0xFF)<<8) | ((d&0xFF)<<0)
static const uint32 _delta_xy_table [ 8 ] = {
MKIT ( 3 , 3 , - 1 , - 1 ) ,
MKIT ( 3 , 7 , - 1 , - 3 ) ,
MKIT ( 3 , 3 , - 1 , - 1 ) ,
MKIT ( 7 , 3 , - 3 , - 1 ) ,
MKIT ( 3 , 3 , - 1 , - 1 ) ,
MKIT ( 3 , 7 , - 1 , - 3 ) ,
MKIT ( 3 , 3 , - 1 , - 1 ) ,
MKIT ( 7 , 3 , - 3 , - 1 ) ,
} ;
# undef MKIT
uint32 x = _delta_xy_table [ direction ] ;
v - > x_offs = GB ( x , 0 , 8 ) ;
v - > y_offs = GB ( x , 8 , 8 ) ;
v - > sprite_width = GB ( x , 16 , 8 ) ;
v - > sprite_height = GB ( x , 24 , 8 ) ;
}
static void UpdateVarsAfterSwap ( Vehicle * v )
{
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
BeginVehicleMove ( v ) ;
VehiclePositionChanged ( v ) ;
EndVehicleMove ( v ) ;
}
static void SetLastSpeed ( Vehicle * v , int spd )
{
int old = v - > u . rail . last_speed ;
if ( spd ! = old ) {
v - > u . rail . last_speed = spd ;
if ( _patches . vehicle_speed | | ( old = = 0 ) ! = ( spd = = 0 ) )
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
}
static void SwapTrainFlags ( byte * swap_flag1 , byte * swap_flag2 )
{
byte flag1 , flag2 ;
flag1 = * swap_flag1 ;
flag2 = * swap_flag2 ;
/* Clear the flags */
CLRBIT ( * swap_flag1 , VRF_GOINGUP ) ;
CLRBIT ( * swap_flag1 , VRF_GOINGDOWN ) ;
CLRBIT ( * swap_flag2 , VRF_GOINGUP ) ;
CLRBIT ( * swap_flag2 , VRF_GOINGDOWN ) ;
/* Reverse the rail-flags (if needed) */
if ( HASBIT ( flag1 , VRF_GOINGUP ) ) {
SETBIT ( * swap_flag2 , VRF_GOINGDOWN ) ;
} else if ( HASBIT ( flag1 , VRF_GOINGDOWN ) ) {
SETBIT ( * swap_flag2 , VRF_GOINGUP ) ;
}
if ( HASBIT ( flag2 , VRF_GOINGUP ) ) {
SETBIT ( * swap_flag1 , VRF_GOINGDOWN ) ;
} else if ( HASBIT ( flag2 , VRF_GOINGDOWN ) ) {
SETBIT ( * swap_flag1 , VRF_GOINGUP ) ;
}
}
static void ReverseTrainSwapVeh ( Vehicle * v , int l , int r )
{
Vehicle * a , * b ;
/* locate vehicles to swap */
for ( a = v ; l ! = 0 ; l - - ) a = a - > next ;
for ( b = v ; r ! = 0 ; r - - ) b = b - > next ;
if ( a ! = b ) {
/* swap the hidden bits */
{
uint16 tmp = ( a - > vehstatus & ~ VS_HIDDEN ) | ( b - > vehstatus & VS_HIDDEN ) ;
b - > vehstatus = ( b - > vehstatus & ~ VS_HIDDEN ) | ( a - > vehstatus & VS_HIDDEN ) ;
a - > vehstatus = tmp ;
}
/* swap variables */
swap_byte ( & a - > u . rail . track , & b - > u . rail . track ) ;
swap_byte ( & a - > direction , & b - > direction ) ;
/* toggle direction */
if ( ! ( a - > u . rail . track & 0x80 ) ) a - > direction = ReverseDir ( a - > direction ) ;
if ( ! ( b - > u . rail . track & 0x80 ) ) b - > direction = ReverseDir ( b - > direction ) ;
/* swap more variables */
swap_int32 ( & a - > x_pos , & b - > x_pos ) ;
swap_int32 ( & a - > y_pos , & b - > y_pos ) ;
swap_tile ( & a - > tile , & b - > tile ) ;
swap_byte ( & a - > z_pos , & b - > z_pos ) ;
SwapTrainFlags ( & a - > u . rail . flags , & b - > u . rail . flags ) ;
/* update other vars */
UpdateVarsAfterSwap ( a ) ;
UpdateVarsAfterSwap ( b ) ;
VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
VehicleEnterTile ( b , b - > tile , b - > x_pos , b - > y_pos ) ;
} else {
if ( ! ( a - > u . rail . track & 0x80 ) ) a - > direction = ReverseDir ( a - > direction ) ;
UpdateVarsAfterSwap ( a ) ;
VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
}
/* Update train's power incase tiles were different rail type */
TrainPowerChanged ( v ) ;
}
/* Check if the vehicle is a train and is on the tile we are testing */
static void * TestTrainOnCrossing ( Vehicle * v , void * data )
{
if ( v - > tile ! = * ( const TileIndex * ) data | | v - > type ! = VEH_Train ) return NULL ;
return v ;
}
static void DisableTrainCrossing ( TileIndex tile )
{
if ( IsLevelCrossingTile ( tile ) & &
VehicleFromPos ( tile , & tile , TestTrainOnCrossing ) = = NULL & & // empty?
IsCrossingBarred ( tile ) ) {
UnbarCrossing ( tile ) ;
MarkTileDirtyByTile ( tile ) ;
}
}
/**
* Advances wagons for train reversing , needed for variable length wagons .
* Needs to be called once before the train is reversed , and once after it .
* @ param v First vehicle in chain
* @ param before Set to true for the call before reversing , false otherwise
*/
static void AdvanceWagons ( Vehicle * v , bool before )
{
Vehicle * base ;
Vehicle * first ;
int length ;
base = v ;
first = base - > next ;
length = CountVehiclesInChain ( v ) ;
while ( length > 2 ) {
Vehicle * last ;
int differential ;
int i ;
// find pairwise matching wagon
// start<>end, start+1<>end-1, ...
last = first ;
for ( i = length - 3 ; i > 0 ; i - - ) last = last - > next ;
differential = last - > u . rail . cached_veh_length - base - > u . rail . cached_veh_length ;
if ( before ) differential * = - 1 ;
if ( differential > 0 ) {
Vehicle * tempnext ;
// disconnect last car to make sure only this subset moves
tempnext = last - > next ;
last - > next = NULL ;
for ( i = 0 ; i < differential ; i + + ) TrainController ( first ) ;
last - > next = tempnext ;
}
base = first ;
first = first - > next ;
length - = 2 ;
}
}
static void ReverseTrainDirection ( Vehicle * v )
{
int l = 0 , r = - 1 ;
Vehicle * u ;
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) ) {
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
/* Check if we were approaching a rail/road-crossing */
{
TileIndex tile = v - > tile ;
DiagDirection dir = DirToDiagDir ( v - > direction ) ;
/* Determine the diagonal direction in which we will exit this tile */
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ dir ] ) {
dir = ChangeDiagDir ( dir , DIAGDIRDIFF_90LEFT ) ;
}
/* Calculate next tile */
tile + = TileOffsByDiagDir ( dir ) ;
/* Check if the train left a rail/road-crossing */
DisableTrainCrossing ( tile ) ;
}
// count number of vehicles
u = v ;
do r + + ; while ( ( u = u - > next ) ! = NULL ) ;
AdvanceWagons ( v , true ) ;
/* swap start<>end, start+1<>end-1, ... */
do {
ReverseTrainSwapVeh ( v , l + + , r - - ) ;
} while ( l < = r ) ;
AdvanceWagons ( v , false ) ;
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) ) {
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
CLRBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
}
/** Reverse train.
* @ param tile unused
* @ param p1 train to reverse
* @ param p2 if true , reverse a unit in a train ( needs to be in a depot )
*/
int32 CmdReverseTrainDirection ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
if ( p2 ) {
// turn a single unit around
Vehicle * front ;
if ( IsMultiheaded ( v ) | | HASBIT ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_ARTIC_ENGINE ) ) {
return_cmd_error ( STR_ONLY_TURN_SINGLE_UNIT ) ;
}
front = GetFirstVehicleInChain ( v ) ;
// make sure the vehicle is stopped in the depot
if ( CheckTrainStoppedInDepot ( front ) < 0 ) {
return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
}
if ( flags & DC_EXEC ) {
TOGGLEBIT ( v - > u . rail . flags , VRF_REVERSE_DIRECTION ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
}
} else {
//turn the whole train around
if ( v - > u . rail . crash_anim_pos ! = 0 | | v - > breakdown_ctr ! = 0 ) return CMD_ERROR ;
if ( flags & DC_EXEC ) {
if ( _patches . realistic_acceleration & & v - > cur_speed ! = 0 ) {
TOGGLEBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
} else {
v - > cur_speed = 0 ;
SetLastSpeed ( v , 0 ) ;
ReverseTrainDirection ( v ) ;
}
}
}
return 0 ;
}
/** Force a train through a red signal
* @ param tile unused
* @ param p1 train to ignore the red signal
* @ param p2 unused
*/
int32 CmdForceTrainProceed ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
if ( flags & DC_EXEC ) v - > u . rail . force_proceed = 0x50 ;
return 0 ;
}
/** Refits a train to the specified cargo type.
* @ param tile unused
* @ param p1 vehicle ID of the train to refit
* param p2 various bitstuffed elements
* - p2 = ( bit 0 - 7 ) - the new cargo type to refit to
* - p2 = ( bit 8 - 15 ) - the new cargo subtype to refit to
*/
int32 CmdRefitRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
CargoID new_cid = GB ( p2 , 0 , 8 ) ;
byte new_subtype = GB ( p2 , 8 , 8 ) ;
Vehicle * v ;
int32 cost ;
uint num ;
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
if ( CheckTrainStoppedInDepot ( v ) < 0 ) return_cmd_error ( STR_TRAIN_MUST_BE_STOPPED ) ;
/* Check cargo */
if ( new_cid > NUM_CARGO ) return CMD_ERROR ;
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_RUN ) ;
cost = 0 ;
num = 0 ;
do {
/* XXX: We also refit all the attached wagons en-masse if they
* can be refitted . This is how TTDPatch does it . TODO : Have
* some nice [ Refit ] button near each wagon . - - pasky */
if ( ! CanRefitTo ( v - > engine_type , new_cid ) ) continue ;
if ( v - > cargo_cap ! = 0 ) {
const RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
uint16 amount = CALLBACK_FAILED ;
if ( HASBIT ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_REFIT_CAPACITY ) ) {
/* Back up the vehicle's cargo type */
CargoID temp_cid = v - > cargo_type ;
byte temp_subtype = v - > cargo_subtype ;
v - > cargo_type = new_cid ;
v - > cargo_subtype = new_subtype ;
/* Check the refit capacity callback */
amount = GetVehicleCallback ( CBID_VEHICLE_REFIT_CAPACITY , 0 , 0 , v - > engine_type , v ) ;
/* Restore the original cargo type */
v - > cargo_type = temp_cid ;
v - > cargo_subtype = temp_subtype ;
}
if ( amount = = CALLBACK_FAILED ) { // callback failed or not used, use default
CargoID old_cid = rvi - > cargo_type ;
/* normally, the capacity depends on the cargo type, a rail vehicle can
* carry twice as much mail / goods as normal cargo , and four times as
* many passengers
*/
amount = rvi - > capacity ;
switch ( old_cid ) {
case CT_PASSENGERS : break ;
case CT_MAIL :
case CT_GOODS : amount * = 2 ; break ;
default : amount * = 4 ; break ;
}
switch ( new_cid ) {
case CT_PASSENGERS : break ;
case CT_MAIL :
case CT_GOODS : amount / = 2 ; break ;
default : amount / = 4 ; break ;
}
} ;
if ( amount ! = 0 ) {
if ( new_cid ! = v - > cargo_type ) {
cost + = GetRefitCost ( v - > engine_type ) ;
}
num + = amount ;
if ( flags & DC_EXEC ) {
v - > cargo_count = ( v - > cargo_type = = new_cid ) ? min ( amount , v - > cargo_count ) : 0 ;
v - > cargo_type = new_cid ;
v - > cargo_cap = amount ;
v - > cargo_subtype = new_subtype ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
RebuildVehicleLists ( ) ;
}
}
}
} while ( ( v = v - > next ) ! = NULL ) ;
_returned_refit_capacity = num ;
/* Update the train's cached variables */
if ( flags & DC_EXEC ) TrainConsistChanged ( GetVehicle ( p1 ) ) ;
return cost ;
}
typedef struct TrainFindDepotData {
uint best_length ;
TileIndex tile ;
PlayerID owner ;
/**
* true if reversing is necessary for the train to get to this depot
* This value is unused when new depot finding and NPF are both disabled
*/
bool reverse ;
} TrainFindDepotData ;
static bool NtpCallbFindDepot ( TileIndex tile , TrainFindDepotData * tfdd , int track , uint length )
{
if ( IsTileType ( tile , MP_RAILWAY ) & &
IsTileOwner ( tile , tfdd - > owner ) & &
IsRailDepot ( tile ) ) {
/* approximate number of tiles by dividing by DIAG_FACTOR */
tfdd - > best_length = length / DIAG_FACTOR ;
tfdd - > tile = tile ;
return true ;
}
return false ;
}
// returns the tile of a depot to goto to. The given vehicle must not be
// crashed!
static TrainFindDepotData FindClosestTrainDepot ( Vehicle * v , int max_distance )
{
TrainFindDepotData tfdd ;
TileIndex tile = v - > tile ;
assert ( ! ( v - > vehstatus & VS_CRASHED ) ) ;
tfdd . owner = v - > owner ;
tfdd . best_length = ( uint ) - 1 ;
tfdd . reverse = false ;
if ( IsTileDepotType ( tile , TRANSPORT_RAIL ) ) {
tfdd . tile = tile ;
tfdd . best_length = 0 ;
return tfdd ;
}
if ( v - > u . rail . track = = 0x40 ) tile = GetVehicleOutOfTunnelTile ( v ) ;
if ( _patches . yapf . rail_use_yapf ) {
bool found = YapfFindNearestRailDepotTwoWay ( v , max_distance , NPF_INFINITE_PENALTY , & tfdd . tile , & tfdd . reverse ) ;
tfdd . best_length = found ? max_distance / 2 : - 1 ; // some fake distance or NOT_FOUND
} else if ( _patches . new_pathfinding_all ) {
NPFFoundTargetData ftd ;
Vehicle * last = GetLastVehicleInChain ( v ) ;
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
Trackdir trackdir = GetVehicleTrackdir ( v ) ;
Trackdir trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
assert ( trackdir ! = INVALID_TRACKDIR ) ;
ftd = NPFRouteToDepotBreadthFirstTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , TRANSPORT_RAIL , v - > owner , v - > u . rail . compatible_railtypes , NPF_INFINITE_PENALTY ) ;
if ( ftd . best_bird_dist = = 0 ) {
/* Found target */
tfdd . tile = ftd . node . tile ;
/* Our caller expects a number of tiles, so we just approximate that
* number by this . It might not be completely what we want , but it will
* work for now : - ) We can possibly change this when the old pathfinder
* is removed . */
tfdd . best_length = ftd . best_path_dist / NPF_TILE_LENGTH ;
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) ) tfdd . reverse = true ;
}
} else {
// search in the forward direction first.
DiagDirection i ;
i = DirToDiagDir ( v - > direction ) ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) {
i = ChangeDiagDir ( i , DIAGDIRDIFF_90LEFT ) ;
}
NewTrainPathfind ( tile , 0 , v - > u . rail . compatible_railtypes , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
if ( tfdd . best_length = = ( uint ) - 1 ) {
tfdd . reverse = true ;
// search in backwards direction
i = ReverseDiagDir ( DirToDiagDir ( v - > direction ) ) ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) {
i = ChangeDiagDir ( i , DIAGDIRDIFF_90LEFT ) ;
}
NewTrainPathfind ( tile , 0 , v - > u . rail . compatible_railtypes , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
}
}
return tfdd ;
}
/** Send a train to a depot
* @ param tile unused
* @ param p1 train to send to the depot
* @ param p2 various bitmasked elements
* - p2 bit 0 - 3 - DEPOT_ flags ( see vehicle . h )
* - p2 bit 8 - 10 - VLW flag ( for mass goto depot )
*/
int32 CmdSendTrainToDepot ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
TrainFindDepotData tfdd ;
if ( p2 & DEPOT_MASS_SEND ) {
/* Mass goto depot requested */
if ( ! ValidVLWFlags ( p2 & VLW_MASK ) ) return CMD_ERROR ;
return SendAllVehiclesToDepot ( VEH_Train , flags , p2 & DEPOT_SERVICE , _current_player , ( p2 & VLW_MASK ) , p1 ) ;
}
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
if ( v - > vehstatus & VS_CRASHED ) return CMD_ERROR ;
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
if ( ! ! ( p2 & DEPOT_SERVICE ) = = HASBIT ( v - > current_order . flags , OFB_HALT_IN_DEPOT ) ) {
/* We called with a different DEPOT_SERVICE setting.
* Now we change the setting to apply the new one and let the vehicle head for the same depot .
* Note : the if is ( true for requesting service = = true for ordered to stop in depot ) */
if ( flags & DC_EXEC ) {
TOGGLEBIT ( v - > current_order . flags , OFB_HALT_IN_DEPOT ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
return 0 ;
}
if ( p2 & DEPOT_DONT_CANCEL ) return CMD_ERROR ; // Requested no cancelation of depot orders
if ( flags & DC_EXEC ) {
if ( HASBIT ( v - > current_order . flags , OFB_PART_OF_ORDERS ) ) {
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
}
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
return 0 ;
}
/* check if at a standstill (not stopped only) in a depot
* the check is down here to make it possible to alter stop / service for trains entering the depot */
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) & & v - > cur_speed = = 0 ) return CMD_ERROR ;
tfdd = FindClosestTrainDepot ( v , 0 ) ;
if ( tfdd . best_length = = ( uint ) - 1 ) return_cmd_error ( STR_883A_UNABLE_TO_FIND_ROUTE_TO ) ;
if ( flags & DC_EXEC ) {
v - > dest_tile = tfdd . tile ;
v - > current_order . type = OT_GOTO_DEPOT ;
v - > current_order . flags = OF_NON_STOP ;
if ( ! ( p2 & DEPOT_SERVICE ) ) SETBIT ( v - > current_order . flags , OFB_HALT_IN_DEPOT ) ;
v - > current_order . dest = GetDepotByTile ( tfdd . tile ) - > index ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
/* If there is no depot in front, reverse automatically */
if ( tfdd . reverse )
DoCommand ( v - > tile , v - > index , 0 , DC_EXEC , CMD_REVERSE_TRAIN_DIRECTION ) ;
}
return 0 ;
}
void OnTick_Train ( void )
{
_age_cargo_skip_counter = ( _age_cargo_skip_counter = = 0 ) ? 184 : ( _age_cargo_skip_counter - 1 ) ;
}
static const int8 _vehicle_smoke_pos [ 8 ] = {
1 , 1 , 1 , 0 , - 1 , - 1 , - 1 , 0
} ;
static void HandleLocomotiveSmokeCloud ( const Vehicle * v )
{
const Vehicle * u ;
bool sound = false ;
if ( v - > vehstatus & VS_TRAIN_SLOWING | | v - > load_unload_time_rem ! = 0 | | v - > cur_speed < 2 )
return ;
u = v ;
do {
EngineID engtype = v - > engine_type ;
int effect_offset = GB ( v - > u . rail . cached_vis_effect , 0 , 4 ) - 8 ;
byte effect_type = GB ( v - > u . rail . cached_vis_effect , 4 , 2 ) ;
bool disable_effect = HASBIT ( v - > u . rail . cached_vis_effect , 6 ) ;
int x , y ;
// no smoke?
if ( ( RailVehInfo ( engtype ) - > flags & RVI_WAGON & & effect_type = = 0 ) | |
disable_effect | |
GetEngine ( engtype ) - > railtype > RAILTYPE_ELECTRIC | |
v - > vehstatus & VS_HIDDEN ) {
continue ;
}
// No smoke in depots or tunnels
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) | | IsTunnelTile ( v - > tile ) ) continue ;
// No sparks for electric vehicles on nonelectrified tracks
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetTileRailType ( v - > tile , GetVehicleTrackdir ( v ) ) ) ) continue ;
if ( effect_type = = 0 ) {
// Use default effect type for engine class.
effect_type = RailVehInfo ( engtype ) - > engclass ;
} else {
effect_type - - ;
}
x = _vehicle_smoke_pos [ v - > direction ] * effect_offset ;
y = _vehicle_smoke_pos [ ( v - > direction + 2 ) % 8 ] * effect_offset ;
if ( HASBIT ( v - > u . rail . flags , VRF_REVERSE_DIRECTION ) ) {
x = - x ;
y = - y ;
}
switch ( effect_type ) {
case 0 :
// steam smoke.
if ( GB ( v - > tick_counter , 0 , 4 ) = = 0 ) {
CreateEffectVehicleRel ( v , x , y , 10 , EV_STEAM_SMOKE ) ;
sound = true ;
}
break ;
case 1 :
// diesel smoke
if ( u - > cur_speed < = 40 & & CHANCE16 ( 15 , 128 ) ) {
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_DIESEL_SMOKE ) ;
sound = true ;
}
break ;
case 2 :
// blue spark
if ( GB ( v - > tick_counter , 0 , 2 ) = = 0 & & CHANCE16 ( 1 , 45 ) ) {
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_ELECTRIC_SPARK ) ;
sound = true ;
}
break ;
}
} while ( ( v = v - > next ) ! = NULL ) ;
if ( sound ) PlayVehicleSound ( u , VSE_TRAIN_EFFECT ) ;
}
static void TrainPlayLeaveStationSound ( const Vehicle * v )
{
static const SoundFx sfx [ ] = {
SND_04_TRAIN ,
SND_0A_TRAIN_HORN ,
SND_0A_TRAIN_HORN
} ;
EngineID engtype = v - > engine_type ;
if ( PlayVehicleSound ( v , VSE_START ) ) return ;
switch ( GetEngine ( engtype ) - > railtype ) {
case RAILTYPE_RAIL :
case RAILTYPE_ELECTRIC :
SndPlayVehicleFx ( sfx [ RailVehInfo ( engtype ) - > engclass ] , v ) ;
break ;
case RAILTYPE_MONO : SndPlayVehicleFx ( SND_47_MAGLEV_2 , v ) ; break ;
case RAILTYPE_MAGLEV : SndPlayVehicleFx ( SND_41_MAGLEV , v ) ; break ;
}
}
static bool CheckTrainStayInDepot ( Vehicle * v )
{
Vehicle * u ;
// bail out if not all wagons are in the same depot or not in a depot at all
for ( u = v ; u ! = NULL ; u = u - > next ) {
if ( u - > u . rail . track ! = 0x80 | | u - > tile ! = v - > tile ) return false ;
}
// if the train got no power, then keep it in the depot
if ( v - > u . rail . cached_power = = 0 ) {
v - > vehstatus | = VS_STOPPED ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
return true ;
}
if ( v - > u . rail . force_proceed = = 0 ) {
if ( + + v - > load_unload_time_rem < 37 ) {
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
return true ;
}
v - > load_unload_time_rem = 0 ;
if ( UpdateSignalsOnSegment ( v - > tile , DirToDiagDir ( v - > direction ) ) ) {
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
return true ;
}
}
(svn r3472) - [PBS] Remove from trunk. Anyone interested can still find it in branch/pbs. This reverts revisions r3158, r3140, r3075, r2977, r2674, r2625, r2621, r2529, r2528, r2525, r2524, r2519, r2517, r2516, r2507, r2499. (in conjunction with Tron)
- The only change is that the nsignalsw.grf file is kept and that existing nightlies with PBS signals get those signals converted to combo-signals.
19 years ago
VehicleServiceInDepot ( v ) ;
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
TrainPlayLeaveStationSound ( v ) ;
v - > u . rail . track = 1 ;
if ( v - > direction & 2 ) v - > u . rail . track = 2 ;
v - > vehstatus & = ~ VS_HIDDEN ;
v - > cur_speed = 0 ;
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
VehiclePositionChanged ( v ) ;
UpdateSignalsOnSegment ( v - > tile , DirToDiagDir ( v - > direction ) ) ;
UpdateTrainAcceleration ( v ) ;
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
return false ;
}
/* Check for station tiles */
typedef struct TrainTrackFollowerData {
TileIndex dest_coords ;
StationID station_index ; // station index we're heading for
uint best_bird_dist ;
uint best_track_dist ;
byte best_track ;
} TrainTrackFollowerData ;
static bool NtpCallbFindStation ( TileIndex tile , TrainTrackFollowerData * ttfd , int track , uint length )
{
// heading for nowhere?
if ( ttfd - > dest_coords = = 0 ) return false ;
// did we reach the final station?
if ( ( ttfd - > station_index = = INVALID_STATION & & tile = = ttfd - > dest_coords ) | | (
IsTileType ( tile , MP_STATION ) & &
IsRailwayStation ( tile ) & &
GetStationIndex ( tile ) = = ttfd - > station_index
) ) {
/* We do not check for dest_coords if we have a station_index,
* because in that case the dest_coords are just an
* approximation of where the station is */
// found station
ttfd - > best_track = track ;
return true ;
} else {
uint dist ;
// didn't find station, keep track of the best path so far.
dist = DistanceManhattan ( tile , ttfd - > dest_coords ) ;
if ( dist < ttfd - > best_bird_dist ) {
ttfd - > best_bird_dist = dist ;
ttfd - > best_track = track ;
}
return false ;
}
}
static void FillWithStationData ( TrainTrackFollowerData * fd , const Vehicle * v )
{
fd - > dest_coords = v - > dest_tile ;
if ( v - > current_order . type = = OT_GOTO_STATION ) {
fd - > station_index = v - > current_order . dest ;
} else {
fd - > station_index = INVALID_STATION ;
}
}
static const byte _initial_tile_subcoord [ 6 ] [ 4 ] [ 3 ] = {
{ { 15 , 8 , 1 } , { 0 , 0 , 0 } , { 0 , 8 , 5 } , { 0 , 0 , 0 } } ,
{ { 0 , 0 , 0 } , { 8 , 0 , 3 } , { 0 , 0 , 0 } , { 8 , 15 , 7 } } ,
{ { 0 , 0 , 0 } , { 7 , 0 , 2 } , { 0 , 7 , 6 } , { 0 , 0 , 0 } } ,
{ { 15 , 8 , 2 } , { 0 , 0 , 0 } , { 0 , 0 , 0 } , { 8 , 15 , 6 } } ,
{ { 15 , 7 , 0 } , { 8 , 0 , 4 } , { 0 , 0 , 0 } , { 0 , 0 , 0 } } ,
{ { 0 , 0 , 0 } , { 0 , 0 , 0 } , { 0 , 8 , 4 } , { 7 , 15 , 0 } } ,
} ;
static const uint32 _reachable_tracks [ 4 ] = {
0x10091009 ,
0x00160016 ,
0x05200520 ,
0x2A002A00 ,
} ;
static const byte _search_directions [ 6 ] [ 4 ] = {
{ 0 , 9 , 2 , 9 } , // track 1
{ 9 , 1 , 9 , 3 } , // track 2
{ 9 , 0 , 3 , 9 } , // track upper
{ 1 , 9 , 9 , 2 } , // track lower
{ 3 , 2 , 9 , 9 } , // track left
{ 9 , 9 , 1 , 0 } , // track right
} ;
static const byte _pick_track_table [ 6 ] = { 1 , 3 , 2 , 2 , 0 , 0 } ;
/* choose a track */
static byte ChooseTrainTrack ( Vehicle * v , TileIndex tile , DiagDirection enterdir , TrackdirBits trackdirbits )
{
TrainTrackFollowerData fd ;
uint best_track ;
// pathfinders are able to tell that route was only 'guessed'
bool path_not_found = false ;
# ifdef PF_BENCHMARK
TIC ( )
# endif
assert ( ( trackdirbits & ~ 0x3F ) = = 0 ) ;
/* quick return in case only one possible track is available */
if ( KILL_FIRST_BIT ( trackdirbits ) = = 0 ) return FIND_FIRST_BIT ( trackdirbits ) ;
if ( _patches . yapf . rail_use_yapf ) {
Trackdir trackdir = YapfChooseRailTrack ( v , tile , enterdir , trackdirbits , & path_not_found ) ;
if ( trackdir ! = INVALID_TRACKDIR ) {
best_track = TrackdirToTrack ( trackdir ) ;
} else {
best_track = FIND_FIRST_BIT ( TrackdirBitsToTrackBits ( trackdirbits ) ) ;
}
} else if ( _patches . new_pathfinding_all ) { /* Use a new pathfinding for everything */
void * perf = NpfBeginInterval ( ) ;
int time = 0 ;
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
Trackdir trackdir ;
NPFFillWithOrderData ( & fstd , v ) ;
/* The enterdir for the new tile, is the exitdir for the old tile */
trackdir = GetVehicleTrackdir ( v ) ;
assert ( trackdir ! = 0xff ) ;
ftd = NPFRouteToStationOrTile ( tile - TileOffsByDiagDir ( enterdir ) , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . compatible_railtypes ) ;
if ( ftd . best_trackdir = = 0xff ) {
/* We are already at our target. Just do something */
//TODO: maybe display error?
//TODO: go straight ahead if possible?
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
best_track = FIND_FIRST_BIT ( trackdirbits ) ;
} else {
/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
the direction we need to take to get there , if ftd . best_bird_dist is not 0 ,
we did not find our target , but ftd . best_trackdir contains the direction leading
to the tile closest to our target . */
if ( ftd . best_bird_dist ! = 0 ) path_not_found = true ;
/* Discard enterdir information, making it a normal track */
best_track = TrackdirToTrack ( ftd . best_trackdir ) ;
}
time = NpfEndInterval ( perf ) ;
DEBUG ( yapf , 4 ) ( " [YAPF][NPFT] %d us - %d rounds - %d open - %d closed -- " , time , 0 , _aystar_stats_open_size , _aystar_stats_closed_size ) ;
} else {
void * perf = NpfBeginInterval ( ) ;
int time = 0 ;
FillWithStationData ( & fd , v ) ;
/* New train pathfinding */
fd . best_bird_dist = ( uint ) - 1 ;
fd . best_track_dist = ( uint ) - 1 ;
fd . best_track = 0xFF ;
NewTrainPathfind ( tile - TileOffsByDiagDir ( enterdir ) , v - > dest_tile ,
v - > u . rail . compatible_railtypes , enterdir , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
// check whether the path was found or only 'guessed'
if ( fd . best_bird_dist ! = 0 ) path_not_found = true ;
if ( fd . best_track = = 0xff ) {
// blaha
best_track = FIND_FIRST_BIT ( trackdirbits ) ;
} else {
best_track = fd . best_track & 7 ;
}
time = NpfEndInterval ( perf ) ;
DEBUG ( yapf , 4 ) ( " [YAPF][NTPT] %d us - %d rounds - %d open - %d closed -- " , time , 0 , 0 , 0 ) ;
}
// handle "path not found" state
if ( path_not_found ) {
// PF didn't find the route
if ( ! HASBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ) {
// it is first time the problem occurred, set the "path not found" flag
SETBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ;
// and notify user about the event
if ( _patches . lost_train_warn & & v - > owner = = _local_player ) {
SetDParam ( 0 , v - > unitnumber ) ;
AddNewsItem (
STR_TRAIN_IS_LOST ,
NEWS_FLAGS ( NM_SMALL , NF_VIEWPORT | NF_VEHICLE , NT_ADVICE , 0 ) ,
v - > index ,
0 ) ;
}
}
} else {
// route found, is the train marked with "path not found" flag?
if ( HASBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ) {
// clear the flag as the PF's problem was solved
CLRBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ;
// can we also delete the "News" item somehow?
}
}
# ifdef PF_BENCHMARK
TOC ( " PF time = " , 1 )
# endif
return best_track ;
}
static bool CheckReverseTrain ( Vehicle * v )
{
TrainTrackFollowerData fd ;
int i , r ;
int best_track ;
uint best_bird_dist = 0 ;
uint best_track_dist = 0 ;
uint reverse , reverse_best ;
if ( _opt . diff . line_reverse_mode ! = 0 | |
v - > u . rail . track & 0xC0 | |
! ( v - > direction & 1 ) )
return false ;
FillWithStationData ( & fd , v ) ;
best_track = - 1 ;
reverse_best = reverse = 0 ;
assert ( v - > u . rail . track ) ;
i = _search_directions [ FIND_FIRST_BIT ( v - > u . rail . track ) ] [ DirToDiagDir ( v - > direction ) ] ;
if ( _patches . yapf . rail_use_yapf ) {
reverse_best = YapfCheckReverseTrain ( v ) ;
} else if ( _patches . new_pathfinding_all ) { /* Use a new pathfinding for everything */
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
byte trackdir , trackdir_rev ;
Vehicle * last = GetLastVehicleInChain ( v ) ;
NPFFillWithOrderData ( & fstd , v ) ;
trackdir = GetVehicleTrackdir ( v ) ;
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
assert ( trackdir ! = 0xff ) ;
assert ( trackdir_rev ! = 0xff ) ;
ftd = NPFRouteToStationOrTileTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . compatible_railtypes ) ;
if ( ftd . best_bird_dist ! = 0 ) {
/* We didn't find anything, just keep on going straight ahead */
reverse_best = false ;
} else {
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) ) {
reverse_best = true ;
} else {
reverse_best = false ;
}
}
} else {
for ( ; ; ) {
fd . best_bird_dist = ( uint ) - 1 ;
fd . best_track_dist = ( uint ) - 1 ;
NewTrainPathfind ( v - > tile , v - > dest_tile , v - > u . rail . compatible_railtypes , reverse ^ i , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
if ( best_track ! = - 1 ) {
if ( best_bird_dist ! = 0 ) {
if ( fd . best_bird_dist ! = 0 ) {
/* neither reached the destination, pick the one with the smallest bird dist */
if ( fd . best_bird_dist > best_bird_dist ) goto bad ;
if ( fd . best_bird_dist < best_bird_dist ) goto good ;
} else {
/* we found the destination for the first time */
goto good ;
}
} else {
if ( fd . best_bird_dist ! = 0 ) {
/* didn't find destination, but we've found the destination previously */
goto bad ;
} else {
/* both old & new reached the destination, compare track length */
if ( fd . best_track_dist > best_track_dist ) goto bad ;
if ( fd . best_track_dist < best_track_dist ) goto good ;
}
}
/* if we reach this position, there's two paths of equal value so far.
* pick one randomly . */
r = GB ( Random ( ) , 0 , 8 ) ;
if ( _pick_track_table [ i ] = = ( v - > direction & 3 ) ) r + = 80 ;
if ( _pick_track_table [ best_track ] = = ( v - > direction & 3 ) ) r - = 80 ;
if ( r < = 127 ) goto bad ;
}
good : ;
best_track = i ;
best_bird_dist = fd . best_bird_dist ;
best_track_dist = fd . best_track_dist ;
reverse_best = reverse ;
bad : ;
if ( reverse ! = 0 ) break ;
reverse = 2 ;
}
}
return reverse_best ! = 0 ;
}
static bool ProcessTrainOrder ( Vehicle * v )
{
const Order * order ;
bool at_waypoint = false ;
switch ( v - > current_order . type ) {
case OT_GOTO_DEPOT :
if ( ! ( v - > current_order . flags & OF_PART_OF_ORDERS ) ) return false ;
if ( ( v - > current_order . flags & OF_SERVICE_IF_NEEDED ) & &
! VehicleNeedsService ( v ) ) {
v - > cur_order_index + + ;
}
break ;
case OT_LOADING :
case OT_LEAVESTATION :
return false ;
default : break ;
}
// check if we've reached the waypoint?
if ( v - > current_order . type = = OT_GOTO_WAYPOINT & & v - > tile = = v - > dest_tile ) {
v - > cur_order_index + + ;
at_waypoint = true ;
}
// check if we've reached a non-stop station while TTDPatch nonstop is enabled..
if ( _patches . new_nonstop & &
v - > current_order . flags & OF_NON_STOP & &
IsTileType ( v - > tile , MP_STATION ) & &
v - > current_order . dest = = GetStationIndex ( v - > tile ) ) {
v - > cur_order_index + + ;
}
// Get the current order
if ( v - > cur_order_index > = v - > num_orders ) v - > cur_order_index = 0 ;
order = GetVehicleOrder ( v , v - > cur_order_index ) ;
// If no order, do nothing.
if ( order = = NULL ) {
v - > current_order . type = OT_NOTHING ;
v - > current_order . flags = 0 ;
v - > dest_tile = 0 ;
return false ;
}
// If it is unchanged, keep it.
if ( order - > type = = v - > current_order . type & &
order - > flags = = v - > current_order . flags & &
order - > dest = = v - > current_order . dest )
return false ;
// Otherwise set it, and determine the destination tile.
v - > current_order = * order ;
v - > dest_tile = 0 ;
InvalidateVehicleOrder ( v ) ;
switch ( order - > type ) {
case OT_GOTO_STATION :
if ( order - > dest = = v - > last_station_visited )
v - > last_station_visited = INVALID_STATION ;
v - > dest_tile = GetStation ( order - > dest ) - > xy ;
break ;
case OT_GOTO_DEPOT :
v - > dest_tile = GetDepot ( order - > dest ) - > xy ;
break ;
case OT_GOTO_WAYPOINT :
v - > dest_tile = GetWaypoint ( order - > dest ) - > xy ;
break ;
default :
return false ;
}
return ! at_waypoint & & CheckReverseTrain ( v ) ;
}
static void MarkTrainDirty ( Vehicle * v )
{
do {
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
MarkAllViewportsDirty ( v - > left_coord , v - > top_coord , v - > right_coord + 1 , v - > bottom_coord + 1 ) ;
} while ( ( v = v - > next ) ! = NULL ) ;
}
static void HandleTrainLoading ( Vehicle * v , bool mode )
{
if ( v - > current_order . type = = OT_NOTHING ) return ;
if ( v - > current_order . type ! = OT_DUMMY ) {
if ( v - > current_order . type ! = OT_LOADING ) return ;
if ( mode ) return ;
// don't mark the train as lost if we're loading on the final station.
if ( v - > current_order . flags & OF_NON_STOP )
v - > u . rail . days_since_order_progr = 0 ;
if ( - - v - > load_unload_time_rem ) return ;
if ( v - > current_order . flags & OF_FULL_LOAD & & CanFillVehicle ( v ) ) {
v - > u . rail . days_since_order_progr = 0 ; /* Prevent a train lost message for full loading trains */
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_INC ) ;
if ( LoadUnloadVehicle ( v ) ) {
InvalidateWindow ( WC_TRAINS_LIST , v - > owner ) ;
MarkTrainDirty ( v ) ;
// need to update acceleration and cached values since the goods on the train changed.
TrainCargoChanged ( v ) ;
UpdateTrainAcceleration ( v ) ;
}
return ;
}
TrainPlayLeaveStationSound ( v ) ;
{
Order b = v - > current_order ;
v - > current_order . type = OT_LEAVESTATION ;
v - > current_order . flags = 0 ;
// If this was not the final order, don't remove it from the list.
if ( ! ( b . flags & OF_NON_STOP ) ) return ;
}
}
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
InvalidateVehicleOrder ( v ) ;
}
static int UpdateTrainSpeed ( Vehicle * v )
{
uint spd ;
uint accel ;
if ( v - > vehstatus & VS_STOPPED | | HASBIT ( v - > u . rail . flags , VRF_REVERSING ) ) {
if ( _patches . realistic_acceleration ) {
accel = GetTrainAcceleration ( v , AM_BRAKE ) * 2 ;
} else {
accel = v - > acceleration * - 2 ;
}
} else {
if ( _patches . realistic_acceleration ) {
accel = GetTrainAcceleration ( v , AM_ACCEL ) ;
} else {
accel = v - > acceleration ;
}
}
spd = v - > subspeed + accel * 2 ;
v - > subspeed = ( byte ) spd ;
{
int tempmax = v - > max_speed ;
if ( v - > cur_speed > v - > max_speed )
tempmax = v - > cur_speed - ( v - > cur_speed / 10 ) - 1 ;
v - > cur_speed = spd = clamp ( v - > cur_speed + ( ( int ) spd > > 8 ) , 0 , tempmax ) ;
}
if ( ! ( v - > direction & 1 ) ) spd = spd * 3 > > 2 ;
spd + = v - > progress ;
v - > progress = ( byte ) spd ;
return ( spd > > 8 ) ;
}
static void TrainEnterStation ( Vehicle * v , StationID station )
{
Station * st ;
uint32 flags ;
v - > last_station_visited = station ;
/* check if a train ever visited this station before */
st = GetStation ( station ) ;
if ( ! ( st - > had_vehicle_of_type & HVOT_TRAIN ) ) {
st - > had_vehicle_of_type | = HVOT_TRAIN ;
SetDParam ( 0 , st - > index ) ;
flags = ( v - > owner = = _local_player ) ? NEWS_FLAGS ( NM_THIN , NF_VIEWPORT | NF_VEHICLE , NT_ARRIVAL_PLAYER , 0 ) : NEWS_FLAGS ( NM_THIN , NF_VIEWPORT | NF_VEHICLE , NT_ARRIVAL_OTHER , 0 ) ;
AddNewsItem (
STR_8801_CITIZENS_CELEBRATE_FIRST ,
flags ,
v - > index ,
0
) ;
}
// Did we reach the final destination?
if ( v - > current_order . type = = OT_GOTO_STATION & &
v - > current_order . dest = = station ) {
// Yeah, keep the load/unload flags
// Non Stop now means if the order should be increased.
v - > current_order . type = OT_LOADING ;
v - > current_order . flags & = OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER ;
v - > current_order . flags | = OF_NON_STOP ;
} else {
// No, just do a simple load
v - > current_order . type = OT_LOADING ;
v - > current_order . flags = 0 ;
}
v - > current_order . dest = 0 ;
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_INC ) ;
if ( LoadUnloadVehicle ( v ) ! = 0 ) {
InvalidateWindow ( WC_TRAINS_LIST , v - > owner ) ;
TrainCargoChanged ( v ) ;
UpdateTrainAcceleration ( v ) ;
}
MarkTrainDirty ( v ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
static byte AfterSetTrainPos ( Vehicle * v , bool new_tile )
{
byte new_z , old_z ;
// need this hint so it returns the right z coordinate on bridges.
_get_z_hint = v - > z_pos ;
new_z = GetSlopeZ ( v - > x_pos , v - > y_pos ) ;
_get_z_hint = 0 ;
old_z = v - > z_pos ;
v - > z_pos = new_z ;
if ( new_tile ) {
CLRBIT ( v - > u . rail . flags , VRF_GOINGUP ) ;
CLRBIT ( v - > u . rail . flags , VRF_GOINGDOWN ) ;
if ( new_z ! = old_z ) {
TileIndex tile = TileVirtXY ( v - > x_pos , v - > y_pos ) ;
// XXX workaround, whole UP/DOWN detection needs overhaul
if ( ! IsTunnelTile ( tile ) ) {
SETBIT ( v - > u . rail . flags , ( new_z > old_z ) ? VRF_GOINGUP : VRF_GOINGDOWN ) ;
}
}
}
VehiclePositionChanged ( v ) ;
EndVehicleMove ( v ) ;
return old_z ;
}
static const Direction _new_vehicle_direction_table [ 11 ] = {
DIR_N , DIR_NW , DIR_W , 0 ,
DIR_NE , DIR_N , DIR_SW , 0 ,
DIR_E , DIR_SE , DIR_S
} ;
static Direction GetNewVehicleDirectionByTile ( TileIndex new_tile , TileIndex old_tile )
{
uint offs = ( TileY ( new_tile ) - TileY ( old_tile ) + 1 ) * 4 +
TileX ( new_tile ) - TileX ( old_tile ) + 1 ;
assert ( offs < 11 ) ;
return _new_vehicle_direction_table [ offs ] ;
}
static Direction GetNewVehicleDirection ( const Vehicle * v , int x , int y )
{
uint offs = ( y - v - > y_pos + 1 ) * 4 + ( x - v - > x_pos + 1 ) ;
assert ( offs < 11 ) ;
return _new_vehicle_direction_table [ offs ] ;
}
static int GetDirectionToVehicle ( const Vehicle * v , int x , int y )
{
byte offs ;
x - = v - > x_pos ;
if ( x > = 0 ) {
offs = ( x > 2 ) ? 0 : 1 ;
} else {
offs = ( x < - 2 ) ? 2 : 1 ;
}
y - = v - > y_pos ;
if ( y > = 0 ) {
offs + = ( ( y > 2 ) ? 0 : 1 ) * 4 ;
} else {
offs + = ( ( y < - 2 ) ? 2 : 1 ) * 4 ;
}
assert ( offs < 11 ) ;
return _new_vehicle_direction_table [ offs ] ;
}
/* Check if the vehicle is compatible with the specified tile */
static bool CheckCompatibleRail ( const Vehicle * v , TileIndex tile )
{
switch ( GetTileType ( tile ) ) {
case MP_RAILWAY :
case MP_STATION :
// normal tracks, jump to owner check
break ;
case MP_TUNNELBRIDGE :
if ( IsBridge ( tile ) & & IsBridgeMiddle ( tile ) ) {
// is train going over the bridge?
if ( v - > z_pos > GetTileMaxZ ( tile ) ) return true ;
}
break ;
case MP_STREET :
// tracks over roads, do owner check of tracks
return
IsTileOwner ( tile , v - > owner ) & & (
! IsFrontEngine ( v ) | |
IsCompatibleRail ( v - > u . rail . railtype , GetRailTypeCrossing ( tile ) )
) ;
default :
return true ;
}
return
IsTileOwner ( tile , v - > owner ) & & (
! IsFrontEngine ( v ) | |
HASBIT ( v - > u . rail . compatible_railtypes , GetRailType ( tile ) )
) ;
}
typedef struct {
byte small_turn , large_turn ;
byte z_up ; // fraction to remove when moving up
byte z_down ; // fraction to remove when moving down
} RailtypeSlowdownParams ;
static const RailtypeSlowdownParams _railtype_slowdown [ ] = {
// normal accel
{ 256 / 4 , 256 / 2 , 256 / 4 , 2 } , // normal
{ 256 / 4 , 256 / 2 , 256 / 4 , 2 } , // electrified
{ 256 / 4 , 256 / 2 , 256 / 4 , 2 } , // monorail
{ 0 , 256 / 2 , 256 / 4 , 2 } , // maglev
} ;
/* Modify the speed of the vehicle due to a turn */
static void AffectSpeedByDirChange ( Vehicle * v , Direction new_dir )
{
DirDiff diff ;
const RailtypeSlowdownParams * rsp ;
if ( _patches . realistic_acceleration ) return ;
diff = DirDifference ( v - > direction , new_dir ) ;
if ( diff = = DIRDIFF_SAME ) return ;
rsp = & _railtype_slowdown [ v - > u . rail . railtype ] ;
v - > cur_speed - = ( diff = = DIRDIFF_45RIGHT | | diff = = DIRDIFF_45LEFT ? rsp - > small_turn : rsp - > large_turn ) * v - > cur_speed > > 8 ;
}
/* Modify the speed of the vehicle due to a change in altitude */
static void AffectSpeedByZChange ( Vehicle * v , byte old_z )
{
const RailtypeSlowdownParams * rsp ;
if ( old_z = = v - > z_pos | | _patches . realistic_acceleration ) return ;
rsp = & _railtype_slowdown [ v - > u . rail . railtype ] ;
if ( old_z < v - > z_pos ) {
v - > cur_speed - = ( v - > cur_speed * rsp - > z_up > > 8 ) ;
} else {
uint16 spd = v - > cur_speed + rsp - > z_down ;
if ( spd < = v - > max_speed ) v - > cur_speed = spd ;
}
}
static const DiagDirection _otherside_signal_directions [ ] = {
DIAGDIR_NE , DIAGDIR_SE , DIAGDIR_NE , DIAGDIR_SE , DIAGDIR_SW , DIAGDIR_SE , 0 , 0 ,
DIAGDIR_SW , DIAGDIR_NW , DIAGDIR_NW , DIAGDIR_SW , DIAGDIR_NW , DIAGDIR_NE
} ;
static void TrainMovedChangeSignals ( TileIndex tile , DiagDirection dir )
{
if ( IsTileType ( tile , MP_RAILWAY ) & &
GetRailTileType ( tile ) = = RAIL_TILE_SIGNALS ) {
uint i = FindFirstBit2x64 ( GetTrackBits ( tile ) * 0x101 & _reachable_tracks [ dir ] ) ;
UpdateSignalsOnSegment ( tile , _otherside_signal_directions [ i ] ) ;
}
}
typedef struct TrainCollideChecker {
const Vehicle * v ;
const Vehicle * v_skip ;
} TrainCollideChecker ;
static void * FindTrainCollideEnum ( Vehicle * v , void * data )
{
const TrainCollideChecker * tcc = data ;
if ( v ! = tcc - > v & &
v ! = tcc - > v_skip & &
v - > type = = VEH_Train & &
v - > u . rail . track ! = 0x80 & &
myabs ( v - > z_pos - tcc - > v - > z_pos ) < = 6 & &
myabs ( v - > x_pos - tcc - > v - > x_pos ) < 6 & &
myabs ( v - > y_pos - tcc - > v - > y_pos ) < 6 ) {
return v ;
} else {
return NULL ;
}
}
static void SetVehicleCrashed ( Vehicle * v )
{
Vehicle * u ;
if ( v - > u . rail . crash_anim_pos ! = 0 ) return ;
v - > u . rail . crash_anim_pos + + ;
u = v ;
BEGIN_ENUM_WAGONS ( v )
v - > vehstatus | = VS_CRASHED ;
END_ENUM_WAGONS ( v )
InvalidateWindowWidget ( WC_VEHICLE_VIEW , u - > index , STATUS_BAR ) ;
}
static uint CountPassengersInTrain ( const Vehicle * v )
{
uint num = 0 ;
BEGIN_ENUM_WAGONS ( v )
if ( v - > cargo_type = = CT_PASSENGERS ) num + = v - > cargo_count ;
END_ENUM_WAGONS ( v )
return num ;
}
/*
* Checks whether the specified train has a collision with another vehicle . If
* so , destroys this vehicle , and the other vehicle if its subtype has TS_Front .
* Reports the incident in a flashy news item , modifies station ratings and
* plays a sound .
*/
static void CheckTrainCollision ( Vehicle * v )
{
TrainCollideChecker tcc ;
Vehicle * coll ;
Vehicle * realcoll ;
uint num ;
/* can't collide in depot */
if ( v - > u . rail . track = = 0x80 ) return ;
assert ( v - > u . rail . track = = 0x40 | | TileVirtXY ( v - > x_pos , v - > y_pos ) = = v - > tile ) ;
tcc . v = v ;
tcc . v_skip = v - > next ;
/* find colliding vehicle */
realcoll = VehicleFromPos ( TileVirtXY ( v - > x_pos , v - > y_pos ) , & tcc , FindTrainCollideEnum ) ;
if ( realcoll = = NULL ) return ;
coll = GetFirstVehicleInChain ( realcoll ) ;
/* it can't collide with its own wagons */
if ( v = = coll | |
( v - > u . rail . track & 0x40 & & ( v - > direction & 2 ) ! = ( realcoll - > direction & 2 ) ) )
return ;
//two drivers + passangers killed in train v
num = 2 + CountPassengersInTrain ( v ) ;
if ( ! ( coll - > vehstatus & VS_CRASHED ) )
//two drivers + passangers killed in train coll (if it was not crashed already)
num + = 2 + CountPassengersInTrain ( coll ) ;
SetVehicleCrashed ( v ) ;
if ( IsFrontEngine ( coll ) ) SetVehicleCrashed ( coll ) ;
SetDParam ( 0 , num ) ;
AddNewsItem ( STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL ,
NEWS_FLAGS ( NM_THIN , NF_VIEWPORT | NF_VEHICLE , NT_ACCIDENT , 0 ) ,
v - > index ,
0
) ;
ModifyStationRatingAround ( v - > tile , v - > owner , - 160 , 30 ) ;
SndPlayVehicleFx ( SND_13_BIG_CRASH , v ) ;
}
typedef struct VehicleAtSignalData {
TileIndex tile ;
Direction direction ;
} VehicleAtSignalData ;
static void * CheckVehicleAtSignal ( Vehicle * v , void * data )
{
const VehicleAtSignalData * vasd = data ;
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) & & v - > tile = = vasd - > tile ) {
DirDiff diff = ChangeDirDiff ( DirDifference ( v - > direction , vasd - > direction ) , DIRDIFF_90RIGHT ) ;
if ( diff = = DIRDIFF_90RIGHT | | ( v - > cur_speed < = 5 & & diff < = DIRDIFF_REVERSE ) ) return v ;
}
return NULL ;
}
static void TrainController ( Vehicle * v )
{
Vehicle * prev ;
GetNewVehiclePosResult gp ;
uint32 r , tracks , ts ;
int i ;
DiagDirection enterdir ;
Direction dir ;
Direction newdir ;
Direction chosen_dir ;
byte chosen_track ;
byte old_z ;
/* For every vehicle after and including the given vehicle */
for ( prev = GetPrevVehicleInChain ( v ) ; v ! = NULL ; prev = v , v = v - > next ) {
BeginVehicleMove ( v ) ;
if ( v - > u . rail . track ! = 0x40 ) {
/* Not inside tunnel */
if ( GetNewVehiclePos ( v , & gp ) ) {
/* Staying in the old tile */
if ( v - > u . rail . track = = 0x80 ) {
/* inside depot */
gp . x = v - > x_pos ;
gp . y = v - > y_pos ;
} else {
/* is not inside depot */
if ( ! TrainCheckIfLineEnds ( v ) ) return ;
r = VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) ;
if ( r & 0x8 ) {
//debug("%x & 0x8", r);
goto invalid_rail ;
}
if ( r & 0x2 ) {
TrainEnterStation ( v , r > > 8 ) ;
return ;
}
if ( v - > current_order . type = = OT_LEAVESTATION ) {
v - > current_order . type = OT_NOTHING ;
v - > current_order . flags = 0 ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
}
} else {
/* A new tile is about to be entered. */
byte bits ;
/* Determine what direction we're entering the new tile from */
dir = GetNewVehicleDirectionByTile ( gp . new_tile , gp . old_tile ) ;
enterdir = DirToDiagDir ( dir ) ;
assert ( enterdir = = 0 | | enterdir = = 1 | | enterdir = = 2 | | enterdir = = 3 ) ;
/* Get the status of the tracks in the new tile and mask
* away the bits that aren ' t reachable . */
ts = GetTileTrackStatus ( gp . new_tile , TRANSPORT_RAIL ) & _reachable_tracks [ enterdir ] ;
/* Combine the from & to directions.
* Now , the lower byte contains the track status , and the byte at bit 16 contains
* the signal status . */
tracks = ts | ( ts > > 8 ) ;
bits = tracks & 0xFF ;
if ( ( _patches . new_pathfinding_all | | _patches . yapf . rail_use_yapf ) & & _patches . forbid_90_deg & & prev = = NULL ) {
/* We allow wagons to make 90 deg turns, because forbid_90_deg
* can be switched on halfway a turn */
(svn r2448) General cleanup of rail related code, more to follow.
* Add: rail.[ch] for rail-related enums and wrapper functions.
* Codechange: Removed dozens of magic numbers with below enums.
* Codechange: Rewrote CheckTrackCombination().
* Add: TILE_SIZE, TILE_PIXELS and TILE_HEIGHT constants.
* Add: enums RailTileType, RailTileSubtype, SignalType to mask against the map arrays.
* Add: enums Track, TrackBits, Trackdir, TrackdirBits for railway track data. (Note that the old RAIL_BIT constants are replaced by TRACK_BIT ones).
* Add: enums Direction and DiagDirection
* Codechange: Moved a bunch of track(dir) related lookup arrays from npf.[ch] to rail.[ch].
* Codechange: move RailType enum from tile.h to rail.h.
* Add: Wrapper functions for masking signal status in the map arrays: SignalAlongTrackdir, SignalAgainstTrackdir and SignalOnTrack.
* Add: Wrapper functions to access rail tiles, using above enums
* Add: Wrapper functions to modify tracks, trackdirs, directions, etc.
* Add: Wrapper functions for all lookup arrays in rail.[ch] (Arrays are still used in parts of the code)
* Codechange: Renamed some variables and arguments to better represent what they contain (railbit -> track, bits -> trackdirbits, etc.).
* Codechange: Don't use FindLandscapeHeight() in CmdRemoveSingleRail(), since it returns way too much info. Use GetTileSlope() instead.
* Codechange: [NPF] Removed some unused globals and code from npf.c.
19 years ago
bits & = ~ TrackCrossesTracks ( FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
}
if ( bits = = 0 ) {
//debug("%x == 0", bits);
goto invalid_rail ;
}
/* Check if the new tile contrains tracks that are compatible
* with the current train , if not , bail out . */
if ( ! CheckCompatibleRail ( v , gp . new_tile ) ) {
//debug("!CheckCompatibleRail(%p, %x)", v, gp.new_tile);
goto invalid_rail ;
}
if ( prev = = NULL ) {
/* Currently the locomotive is active. Determine which one of the
* available tracks to choose */
chosen_track = 1 < < ChooseTrainTrack ( v , gp . new_tile , enterdir , bits ) ;
assert ( chosen_track & tracks ) ;
/* Check if it's a red signal and that force proceed is not clicked. */
if ( ( tracks > > 16 ) & chosen_track & & v - > u . rail . force_proceed = = 0 ) goto red_light ;
} else {
static byte _matching_tracks [ 8 ] = { 0x30 , 1 , 0xC , 2 , 0x30 , 1 , 0xC , 2 } ;
/* The wagon is active, simply follow the prev vehicle. */
chosen_track = ( byte ) ( _matching_tracks [ GetDirectionToVehicle ( prev , gp . x , gp . y ) ] & bits ) ;
}
/* make sure chosen track is a valid track */
assert ( chosen_track = = 1 | | chosen_track = = 2 | | chosen_track = = 4 | | chosen_track = = 8 | | chosen_track = = 16 | | chosen_track = = 32 ) ;
/* Update XY to reflect the entrance to the new tile, and select the direction to use */
{
const byte * b = _initial_tile_subcoord [ FIND_FIRST_BIT ( chosen_track ) ] [ enterdir ] ;
gp . x = ( gp . x & ~ 0xF ) | b [ 0 ] ;
gp . y = ( gp . y & ~ 0xF ) | b [ 1 ] ;
chosen_dir = b [ 2 ] ;
}
/* Call the landscape function and tell it that the vehicle entered the tile */
r = VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) ;
if ( r & 0x8 ) {
//debug("%x & 0x8", r);
goto invalid_rail ;
}
if ( IsLevelCrossingTile ( v - > tile ) & & v - > next = = NULL ) {
UnbarCrossing ( v - > tile ) ;
MarkTileDirtyByTile ( v - > tile ) ;
}
if ( IsFrontEngine ( v ) ) v - > load_unload_time_rem = 0 ;
if ( ! ( r & 0x4 ) ) {
v - > tile = gp . new_tile ;
if ( GetTileRailType ( gp . new_tile , chosen_track ) ! = GetTileRailType ( gp . old_tile , v - > u . rail . track ) ) {
TrainPowerChanged ( GetFirstVehicleInChain ( v ) ) ;
}
v - > u . rail . track = chosen_track ;
assert ( v - > u . rail . track ) ;
}
if ( IsFrontEngine ( v ) ) TrainMovedChangeSignals ( gp . new_tile , enterdir ) ;
/* Signals can only change when the first
* ( above ) or the last vehicle moves . */
if ( v - > next = = NULL )
TrainMovedChangeSignals ( gp . old_tile , ReverseDiagDir ( enterdir ) ) ;
if ( prev = = NULL ) AffectSpeedByDirChange ( v , chosen_dir ) ;
v - > direction = chosen_dir ;
}
} else {
/* in tunnel */
GetNewVehiclePos ( v , & gp ) ;
// Check if to exit the tunnel...
if ( ! IsTunnelTile ( gp . new_tile ) | |
! ( VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) & 0x4 ) ) {
v - > x_pos = gp . x ;
v - > y_pos = gp . y ;
VehiclePositionChanged ( v ) ;
continue ;
}
}
/* update image of train, as well as delta XY */
newdir = GetNewVehicleDirection ( v , gp . x , gp . y ) ;
UpdateTrainDeltaXY ( v , newdir ) ;
v - > cur_image = GetTrainImage ( v , newdir ) ;
v - > x_pos = gp . x ;
v - > y_pos = gp . y ;
/* update the Z position of the vehicle */
old_z = AfterSetTrainPos ( v , ( gp . new_tile ! = gp . old_tile ) ) ;
if ( prev = = NULL ) {
/* This is the first vehicle in the train */
AffectSpeedByZChange ( v , old_z ) ;
}
}
return ;
invalid_rail :
/* We've reached end of line?? */
if ( prev ! = NULL ) error ( " !Disconnecting train " ) ;
goto reverse_train_direction ;
red_light : {
/* We're in front of a red signal ?? */
/* find the first set bit in ts. need to do it in 2 steps, since
* FIND_FIRST_BIT only handles 6 bits at a time . */
i = FindFirstBit2x64 ( ts ) ;
if ( ! HasSignalOnTrackdir ( gp . new_tile , ReverseTrackdir ( i ) ) ) {
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
v - > progress = 255 - 100 ;
if ( + + v - > load_unload_time_rem < _patches . wait_oneway_signal * 20 ) return ;
} else if ( HasSignalOnTrackdir ( gp . new_tile , i ) ) {
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
v - > progress = 255 - 10 ;
if ( + + v - > load_unload_time_rem < _patches . wait_twoway_signal * 73 ) {
TileIndex o_tile = gp . new_tile + TileOffsByDiagDir ( enterdir ) ;
VehicleAtSignalData vasd ;
vasd . tile = o_tile ;
vasd . direction = ReverseDir ( dir ) ;
/* check if a train is waiting on the other side */
if ( VehicleFromPos ( o_tile , & vasd , CheckVehicleAtSignal ) = = NULL ) return ;
}
}
}
reverse_train_direction :
v - > load_unload_time_rem = 0 ;
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
ReverseTrainDirection ( v ) ;
}
extern TileIndex CheckTunnelBusy ( TileIndex tile , uint * length ) ;
/**
* Deletes / Clears the last wagon of a crashed train . It takes the engine of the
* train , then goes to the last wagon and deletes that . Each call to this function
* will remove the last wagon of a crashed train . If this wagon was on a crossing ,
* or inside a tunnel , recalculate the signals as they might need updating
* @ param v the @ Vehicle of which last wagon is to be removed
*/
static void DeleteLastWagon ( Vehicle * v )
{
Vehicle * u = v ;
/* Go to the last wagon and delete the link pointing there
* * u is then the one - before - last wagon , and * v the last
* one which will physicially be removed */
for ( ; v - > next ! = NULL ; v = v - > next ) u = v ;
u - > next = NULL ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
DeleteWindowById ( WC_VEHICLE_VIEW , v - > index ) ;
RebuildVehicleLists ( ) ;
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
BeginVehicleMove ( v ) ;
EndVehicleMove ( v ) ;
DeleteVehicle ( v ) ;
if ( ! ( v - > u . rail . track & 0xC0 ) )
SetSignalsOnBothDir ( v - > tile , FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
/* Check if the wagon was on a road/rail-crossing and disable it if no
* others are on it */
DisableTrainCrossing ( v - > tile ) ;
if ( v - > u . rail . track = = 0x40 ) { // inside a tunnel
TileIndex endtile = CheckTunnelBusy ( v - > tile , NULL ) ;
if ( endtile = = INVALID_TILE ) return ; // tunnel is busy (error returned)
switch ( v - > direction ) {
case 1 :
case 5 :
SetSignalsOnBothDir ( v - > tile , 0 ) ;
SetSignalsOnBothDir ( endtile , 0 ) ;
break ;
case 3 :
case 7 :
SetSignalsOnBothDir ( v - > tile , 1 ) ;
SetSignalsOnBothDir ( endtile , 1 ) ;
break ;
default :
break ;
}
}
}
static void ChangeTrainDirRandomly ( Vehicle * v )
{
static const DirDiff delta [ ] = {
DIRDIFF_45LEFT , DIRDIFF_SAME , DIRDIFF_SAME , DIRDIFF_45RIGHT
} ;
do {
//I need to buffer the train direction
if ( ! ( v - > u . rail . track & 0x40 ) ) {
v - > direction = ChangeDir ( v - > direction , delta [ GB ( Random ( ) , 0 , 2 ) ] ) ;
}
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
BeginVehicleMove ( v ) ;
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
AfterSetTrainPos ( v , false ) ;
}
} while ( ( v = v - > next ) ! = NULL ) ;
}
static void HandleCrashedTrain ( Vehicle * v )
{
int state = + + v - > u . rail . crash_anim_pos ;
uint32 r ;
Vehicle * u ;
if ( state = = 4 & & v - > u . rail . track ! = 0x40 ) {
CreateEffectVehicleRel ( v , 4 , 4 , 8 , EV_EXPLOSION_LARGE ) ;
}
if ( state < = 200 & & CHANCE16R ( 1 , 7 , r ) ) {
int index = ( r * 10 > > 16 ) ;
u = v ;
do {
if ( - - index < 0 ) {
r = Random ( ) ;
CreateEffectVehicleRel ( u ,
GB ( r , 8 , 3 ) + 2 ,
GB ( r , 16 , 3 ) + 2 ,
GB ( r , 0 , 3 ) + 5 ,
EV_EXPLOSION_SMALL ) ;
break ;
}
} while ( ( u = u - > next ) ! = NULL ) ;
}
if ( state < = 240 & & ! ( v - > tick_counter & 3 ) ) ChangeTrainDirRandomly ( v ) ;
if ( state > = 4440 & & ! ( v - > tick_counter & 0x1F ) ) {
DeleteLastWagon ( v ) ;
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ;
}
}
static void HandleBrokenTrain ( Vehicle * v )
{
if ( v - > breakdown_ctr ! = 1 ) {
v - > breakdown_ctr = 1 ;
v - > cur_speed = 0 ;
if ( v - > breakdowns_since_last_service ! = 255 )
v - > breakdowns_since_last_service + + ;
InvalidateWindow ( WC_VEHICLE_VIEW , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
if ( ! PlayVehicleSound ( v , VSE_BREAKDOWN ) ) {
SndPlayVehicleFx ( ( _opt . landscape ! = LT_CANDY ) ?
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2 , v ) ;
}
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
Vehicle * u = CreateEffectVehicleRel ( v , 4 , 4 , 5 , EV_BREAKDOWN_SMOKE ) ;
if ( u ! = NULL ) u - > u . special . unk0 = v - > breakdown_delay * 2 ;
}
}
if ( ! ( v - > tick_counter & 3 ) ) {
if ( ! - - v - > breakdown_delay ) {
v - > breakdown_ctr = 0 ;
InvalidateWindow ( WC_VEHICLE_VIEW , v - > index ) ;
}
}
}
static const byte _breakdown_speeds [ 16 ] = {
225 , 210 , 195 , 180 , 165 , 150 , 135 , 120 , 105 , 90 , 75 , 60 , 45 , 30 , 15 , 15
} ;
static bool TrainCheckIfLineEnds ( Vehicle * v )
{
TileIndex tile ;
uint x , y ;
uint16 break_speed ;
DiagDirection dir ;
int t ;
uint32 ts ;
t = v - > breakdown_ctr ;
if ( t > 1 ) {
v - > vehstatus | = VS_TRAIN_SLOWING ;
break_speed = _breakdown_speeds [ GB ( ~ t , 4 , 4 ) ] ;
if ( break_speed < v - > cur_speed ) v - > cur_speed = break_speed ;
} else {
v - > vehstatus & = ~ VS_TRAIN_SLOWING ;
}
if ( v - > u . rail . track & 0x40 ) return true ; // exit if inside a tunnel
if ( v - > u . rail . track & 0x80 ) return true ; // exit if inside a depot
tile = v - > tile ;
// tunnel entrance?
if ( IsTunnelTile ( tile ) & &
DiagDirToDir ( GetTunnelDirection ( tile ) ) = = v - > direction ) {
return true ;
}
// depot?
/* XXX -- When enabled, this makes it possible to crash trains of others
( by building a depot right against a station ) */
/* if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_DEPOT_WAYPOINT)
return true ; */
/* Determine the non-diagonal direction in which we will exit this tile */
dir = DirToDiagDir ( v - > direction ) ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ dir ] ) {
dir = ChangeDiagDir ( dir , DIAGDIRDIFF_90LEFT ) ;
}
/* Calculate next tile */
tile + = TileOffsByDiagDir ( dir ) ;
// determine the track status on the next tile.
ts = GetTileTrackStatus ( tile , TRANSPORT_RAIL ) & _reachable_tracks [ dir ] ;
/* Calc position within the current tile ?? */
x = v - > x_pos & 0xF ;
y = v - > y_pos & 0xF ;
switch ( v - > direction ) {
case DIR_N : x = ~ x + ~ y + 24 ; break ;
case DIR_NW : x = y ; /* FALLTHROUGH */
case DIR_NE : x = ~ x + 16 ; break ;
case DIR_E : x = ~ x + y + 8 ; break ;
case DIR_SE : x = y ; break ;
case DIR_S : x = x + y - 8 ; break ;
case DIR_W : x = ~ y + x + 8 ; break ;
}
if ( GB ( ts , 0 , 16 ) ! = 0 ) {
/* If we approach a rail-piece which we can't enter, or the back of a depot, don't enter it! */
if ( x + 4 > = TILE_SIZE & &
( ! CheckCompatibleRail ( v , tile ) | |
( IsTileDepotType ( tile , TRANSPORT_RAIL ) & &
GetRailDepotDirection ( tile ) = = dir ) ) ) {
v - > cur_speed = 0 ;
ReverseTrainDirection ( v ) ;
return false ;
}
if ( ( ts & = ( ts > > 16 ) ) = = 0 ) {
// make a rail/road crossing red
if ( IsLevelCrossingTile ( tile ) ) {
if ( ! IsCrossingBarred ( tile ) ) {
BarCrossing ( tile ) ;
SndPlayVehicleFx ( SND_0E_LEVEL_CROSSING , v ) ;
MarkTileDirtyByTile ( tile ) ;
}
}
return true ;
}
} else if ( x + 4 > = TILE_SIZE ) {
v - > cur_speed = 0 ;
ReverseTrainDirection ( v ) ;
return false ;
}
// slow down
v - > vehstatus | = VS_TRAIN_SLOWING ;
break_speed = _breakdown_speeds [ x & 0xF ] ;
if ( ! ( v - > direction & 1 ) ) break_speed > > = 1 ;
if ( break_speed < v - > cur_speed ) v - > cur_speed = break_speed ;
return true ;
}
static void TrainLocoHandler ( Vehicle * v , bool mode )
{
int j ;
/* train has crashed? */
if ( v - > u . rail . crash_anim_pos ! = 0 ) {
if ( ! mode ) HandleCrashedTrain ( v ) ;
return ;
}
if ( v - > u . rail . force_proceed ! = 0 ) v - > u . rail . force_proceed - - ;
/* train is broken down? */
if ( v - > breakdown_ctr ! = 0 ) {
if ( v - > breakdown_ctr < = 2 ) {
HandleBrokenTrain ( v ) ;
return ;
}
v - > breakdown_ctr - - ;
}
if ( HASBIT ( v - > u . rail . flags , VRF_REVERSING ) & & v - > cur_speed = = 0 ) {
ReverseTrainDirection ( v ) ;
}
/* exit if train is stopped */
if ( v - > vehstatus & VS_STOPPED & & v - > cur_speed = = 0 ) return ;
if ( ProcessTrainOrder ( v ) ) {
v - > load_unload_time_rem = 0 ;
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
ReverseTrainDirection ( v ) ;
return ;
}
HandleTrainLoading ( v , mode ) ;
if ( v - > current_order . type = = OT_LOADING ) return ;
if ( CheckTrainStayInDepot ( v ) ) return ;
if ( ! mode ) HandleLocomotiveSmokeCloud ( v ) ;
j = UpdateTrainSpeed ( v ) ;
if ( j = = 0 ) {
// if the vehicle has speed 0, update the last_speed field.
if ( v - > cur_speed ! = 0 ) return ;
} else {
TrainCheckIfLineEnds ( v ) ;
do {
TrainController ( v ) ;
CheckTrainCollision ( v ) ;
if ( v - > cur_speed < = 0x100 )
break ;
} while ( - - j ! = 0 ) ;
}
SetLastSpeed ( v , v - > cur_speed ) ;
}
void Train_Tick ( Vehicle * v )
{
if ( _age_cargo_skip_counter = = 0 & & v - > cargo_days ! = 0xff )
v - > cargo_days + + ;
v - > tick_counter + + ;
if ( IsFrontEngine ( v ) ) {
TrainLocoHandler ( v , false ) ;
// make sure vehicle wasn't deleted.
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) )
TrainLocoHandler ( v , true ) ;
} else if ( IsFreeWagon ( v ) & & HASBITS ( v - > vehstatus , VS_CRASHED ) ) {
// Delete flooded standalone wagon
if ( + + v - > u . rail . crash_anim_pos > = 4400 )
DeleteVehicle ( v ) ;
}
}
# define MAX_ACCEPTABLE_DEPOT_DIST 16
static void CheckIfTrainNeedsService ( Vehicle * v )
{
const Depot * depot ;
TrainFindDepotData tfdd ;
if ( _patches . servint_trains = = 0 ) return ;
if ( ! VehicleNeedsService ( v ) ) return ;
if ( v - > vehstatus & VS_STOPPED ) return ;
if ( _patches . gotodepot & & VehicleHasDepotOrders ( v ) ) return ;
// Don't interfere with a depot visit scheduled by the user, or a
// depot visit by the order list.
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
( v - > current_order . flags & ( OF_HALT_IN_DEPOT | OF_PART_OF_ORDERS ) ) ! = 0 )
return ;
if ( CheckTrainIsInsideDepot ( v ) ) {
VehicleServiceInDepot ( v ) ;
return ;
}
tfdd = FindClosestTrainDepot ( v , MAX_ACCEPTABLE_DEPOT_DIST ) ;
/* Only go to the depot if it is not too far out of our way. */
if ( tfdd . best_length = = ( uint ) - 1 | | tfdd . best_length > MAX_ACCEPTABLE_DEPOT_DIST ) {
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
/* If we were already heading for a depot but it has
* suddenly moved farther away , we continue our normal
* schedule ? */
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
return ;
}
depot = GetDepotByTile ( tfdd . tile ) ;
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
v - > current_order . dest ! = depot - > index & &
! CHANCE16 ( 3 , 16 ) ) {
return ;
}
v - > current_order . type = OT_GOTO_DEPOT ;
v - > current_order . flags = OF_NON_STOP ;
v - > current_order . dest = depot - > index ;
v - > dest_tile = tfdd . tile ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
int32 GetTrainRunningCost ( const Vehicle * v )
{
int32 cost = 0 ;
do {
const RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
if ( rvi - > running_cost_base > 0 )
cost + = rvi - > running_cost_base * _price . running_rail [ rvi - > running_cost_class ] ;
} while ( ( v = GetNextVehicle ( v ) ) ! = NULL ) ;
return cost ;
}
void OnNewDay_Train ( Vehicle * v )
{
TileIndex tile ;
if ( ( + + v - > day_counter & 7 ) = = 0 ) DecreaseVehicleValue ( v ) ;
if ( IsFrontEngine ( v ) ) {
CheckVehicleBreakdown ( v ) ;
AgeVehicle ( v ) ;
CheckIfTrainNeedsService ( v ) ;
CheckOrders ( v ) ;
/* update destination */
if ( v - > current_order . type = = OT_GOTO_STATION & &
( tile = GetStation ( v - > current_order . dest ) - > train_tile ) ! = 0 ) {
v - > dest_tile = tile ;
}
if ( ( v - > vehstatus & VS_STOPPED ) = = 0 ) {
/* running costs */
int32 cost = GetTrainRunningCost ( v ) / 364 ;
v - > profit_this_year - = cost > > 8 ;
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_RUN ) ;
SubtractMoneyFromPlayerFract ( v - > owner , cost ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
}
}
}
void TrainsYearlyLoop ( void )
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) ) {
// show warning if train is not generating enough income last 2 years (corresponds to a red icon in the vehicle list)
if ( _patches . train_income_warn & & v - > owner = = _local_player & & v - > age > = 730 & & v - > profit_this_year < 0 ) {
SetDParam ( 1 , v - > profit_this_year ) ;
SetDParam ( 0 , v - > unitnumber ) ;
AddNewsItem (
STR_TRAIN_IS_UNPROFITABLE ,
NEWS_FLAGS ( NM_SMALL , NF_VIEWPORT | NF_VEHICLE , NT_ADVICE , 0 ) ,
v - > index ,
0 ) ;
}
v - > profit_last_year = v - > profit_this_year ;
v - > profit_this_year = 0 ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
}
}
}
void InitializeTrains ( void )
{
_age_cargo_skip_counter = 1 ;
}
/*
* Link front and rear multiheaded engines to each other
* This is done when loading a savegame
*/
void ConnectMultiheadedTrains ( void )
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train ) {
v - > u . rail . other_multiheaded_part = NULL ;
}
}
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) ) {
Vehicle * u = v ;
BEGIN_ENUM_WAGONS ( u ) {
if ( u - > u . rail . other_multiheaded_part ! = NULL ) continue ; // we already linked this one
if ( IsMultiheaded ( u ) ) {
if ( ! IsTrainEngine ( u ) ) {
/* we got a rear car without a front car. We will convert it to a front one */
SetTrainEngine ( u ) ;
u - > spritenum - - ;
}
{
Vehicle * w ;
for ( w = u - > next ; w ! = NULL & & ( w - > engine_type ! = u - > engine_type | | w - > u . rail . other_multiheaded_part ! = NULL ) ; w = GetNextVehicle ( w ) ) ;
if ( w ! = NULL ) {
/* we found a car to partner with this engine. Now we will make sure it face the right way */
if ( IsTrainEngine ( w ) ) {
ClearTrainEngine ( w ) ;
w - > spritenum + + ;
}
}
if ( w ! = NULL ) {
w - > u . rail . other_multiheaded_part = u ;
u - > u . rail . other_multiheaded_part = w ;
} else {
/* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */
ClearMultiheaded ( u ) ;
}
}
}
} END_ENUM_WAGONS ( u )
}
}
}
/*
* Converts all trains to the new subtype format introduced in savegame 16.2
* It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found
*/
void ConvertOldMultiheadToNew ( void )
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train ) {
SETBIT ( v - > subtype , 7 ) ; // indicates that it's the old format and needs to be converted in the next loop
}
}
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train ) {
if ( HASBIT ( v - > subtype , 7 ) & & ( ( v - > subtype & ~ 0x80 ) = = 0 | | ( v - > subtype & ~ 0x80 ) = = 4 ) ) {
Vehicle * u = v ;
BEGIN_ENUM_WAGONS ( u ) {
const RailVehicleInfo * rvi = RailVehInfo ( u - > engine_type ) ;
CLRBIT ( u - > subtype , 7 ) ;
switch ( u - > subtype ) {
case 0 : /* TS_Front_Engine */
if ( rvi - > flags & RVI_MULTIHEAD ) SetMultiheaded ( u ) ;
SetFrontEngine ( u ) ;
SetTrainEngine ( u ) ;
break ;
case 1 : /* TS_Artic_Part */
u - > subtype = 0 ;
SetArticulatedPart ( u ) ;
break ;
case 2 : /* TS_Not_First */
u - > subtype = 0 ;
if ( rvi - > flags & RVI_WAGON ) {
// normal wagon
SetTrainWagon ( u ) ;
break ;
}
if ( rvi - > flags & RVI_MULTIHEAD & & rvi - > image_index = = u - > spritenum - 1 ) {
// rear end of a multiheaded engine
SetMultiheaded ( u ) ;
break ;
}
if ( rvi - > flags & RVI_MULTIHEAD ) SetMultiheaded ( u ) ;
SetTrainEngine ( u ) ;
break ;
case 4 : /* TS_Free_Car */
u - > subtype = 0 ;
SetTrainWagon ( u ) ;
SetFreeWagon ( u ) ;
break ;
default : NOT_REACHED ( ) ; break ;
}
} END_ENUM_WAGONS ( u )
}
}
}
}