2004-08-09 19:04:08 +02:00
# include "stdafx.h"
2005-06-02 21:30:21 +02:00
# include "openttd.h"
2005-06-07 00:44:11 +02:00
# include "debug.h"
2005-07-22 09:02:20 +02:00
# include "functions.h"
2004-11-25 11:47:30 +01:00
# include "table/strings.h"
2004-12-15 23:18:54 +01:00
# include "map.h"
2005-01-29 13:19:05 +01:00
# include "tile.h"
2004-08-09 19:04:08 +02:00
# include "vehicle.h"
# include "command.h"
# include "pathfind.h"
2005-01-31 12:23:10 +01:00
# include "npf.h"
2004-08-09 19:04:08 +02:00
# include "station.h"
# include "table/train_cmd.h"
# include "news.h"
# include "engine.h"
# include "player.h"
2004-11-06 00:12:33 +01:00
# include "sound.h"
2005-02-06 11:18:47 +01:00
# include "depot.h"
2005-07-04 16:58:55 +02:00
# include "debug.h"
2005-03-24 18:03:37 +01:00
# include "waypoint.h"
2005-05-03 01:59:11 +02:00
# include "vehicle_gui.h"
2004-08-09 19:04:08 +02:00
2005-05-05 22:46:14 +02:00
# define IS_FIRSTHEAD_SPRITE(spritenum) \
( is_custom_sprite ( spritenum ) ? IS_CUSTOM_FIRSTHEAD_SPRITE ( spritenum ) : _engine_sprite_add [ spritenum ] = = 0 )
2004-08-09 19:04:08 +02:00
2004-12-21 17:00:14 +01:00
static bool TrainCheckIfLineEnds ( Vehicle * v ) ;
2005-06-07 00:44:11 +02:00
static void TrainController ( Vehicle * v ) ;
2005-05-05 22:46:14 +02:00
extern void ShowTrainViewWindow ( Vehicle * v ) ;
2004-08-09 19:04:08 +02:00
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 } ;
2005-06-05 17:37:00 +02:00
/**
* 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 .
*/
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 = 0 ;
2005-06-06 02:19:24 +02:00
// vehicle weight is the sum of the weight of the vehicle and the weight of its cargo
2005-06-05 17:37:00 +02:00
vweight + = rvi - > weight ;
vweight + = ( _cargoc . weights [ u - > cargo_type ] * u - > cargo_count ) / 16 ;
2005-06-06 02:19:24 +02:00
// powered wagons have extra weight added
2005-07-20 00:29:35 +02:00
if ( HASBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) )
2005-06-06 02:19:24 +02:00
vweight + = RailVehInfo ( v - > engine_type ) - > pow_wag_weight ;
2005-06-05 17:37:00 +02:00
// 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 ;
}
/**
2005-06-06 16:26:15 +02:00
* 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 .
2005-06-05 17:37:00 +02:00
*/
void TrainConsistChanged ( Vehicle * v ) {
2005-06-06 16:26:15 +02:00
const RailVehicleInfo * rvi_v ;
2005-06-05 17:37:00 +02:00
Vehicle * u ;
uint16 max_speed = 0xFFFF ;
uint32 power = 0 ;
2005-06-06 16:26:15 +02:00
EngineID first_engine ;
assert ( v - > type = = VEH_Train ) ;
assert ( v - > subtype = = TS_Front_Engine | | v - > subtype = = TS_Free_Car ) ;
rvi_v = RailVehInfo ( v - > engine_type ) ;
first_engine = ( v - > subtype = = TS_Front_Engine ) ? v - > engine_type : INVALID_VEHICLE ;
2005-06-05 17:37:00 +02:00
for ( u = v ; u ! = NULL ; u = u - > next ) {
2005-06-06 02:19:24 +02:00
const RailVehicleInfo * rvi_u = RailVehInfo ( u - > engine_type ) ;
2005-06-07 00:44:11 +02:00
uint16 veh_len ;
2005-06-06 02:19:24 +02:00
2005-06-06 16:26:15 +02:00
// update the 'first engine'
u - > u . rail . first_engine = ( v = = u ) ? INVALID_VEHICLE : first_engine ;
2005-06-06 02:19:24 +02:00
// power is the sum of the powers of all engines and powered wagons in the consist
power + = rvi_u - > power ;
// check if its a powered wagon
CLRBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ;
if ( ( rvi_v - > pow_wag_power ! = 0 ) & & ( rvi_u - > flags & RVI_WAGON ) & & UsesWagonOverride ( u ) ) {
2005-06-06 03:31:36 +02:00
uint16 callback = CALLBACK_FAILED ;
2005-06-05 17:37:00 +02:00
2005-06-06 03:31:36 +02:00
if ( HASBIT ( rvi_u - > callbackmask , CBM_WAGON_POWER ) )
callback = GetCallBackResult ( CBID_WAGON_POWER , u - > engine_type , u ) ;
2005-06-05 17:37:00 +02:00
2005-06-06 02:19:24 +02:00
if ( callback = = CALLBACK_FAILED )
callback = rvi_u - > visual_effect ;
if ( callback < 0x40 ) {
/* wagon is powered */
SETBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ; // cache 'powered' status
power + = rvi_v - > pow_wag_power ;
}
}
// max speed is the minimum of the speed limits of all vehicles in the consist
if ( rvi_u - > max_speed ! = 0 )
max_speed = min ( rvi_u - > max_speed , max_speed ) ;
2005-06-07 00:44:11 +02:00
// check the vehicle length (callback)
veh_len = CALLBACK_FAILED ;
if ( HASBIT ( rvi_u - > callbackmask , CBM_VEH_LENGTH ) )
veh_len = GetCallBackResult ( CBID_VEH_LENGTH , u - > engine_type , u ) ;
if ( veh_len = = CALLBACK_FAILED )
veh_len = rvi_u - > shorten_factor ;
2005-06-15 16:04:48 +02:00
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
2005-06-07 00:44:11 +02:00
u - > u . rail . cached_veh_length = 8 - veh_len ;
2005-06-05 17:37:00 +02:00
} ;
// store consist weight/max speed in cache
v - > u . rail . cached_max_speed = max_speed ;
v - > u . rail . cached_power = power ;
2005-06-06 02:19:24 +02:00
// 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 ) ;
2005-06-05 17:37:00 +02:00
}
2004-08-09 19:04:08 +02:00
2005-01-31 12:23:10 +01:00
/* These two arrays are used for realistic acceleration. XXX: How should they
* be interpreted ? */
2005-01-26 13:51:04 +01:00
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
} ;
2005-01-30 20:51:39 +01:00
static bool TrainShouldStop ( Vehicle * v , TileIndex tile )
{
Order * o = & v - > current_order ;
assert ( v - > type = = VEH_Train ) ;
assert ( IsTileType ( v - > tile , MP_STATION ) ) ;
//When does a train drive through a station
//first we deal with the "new nonstop handling"
2005-03-09 20:09:04 +01:00
if ( _patches . new_nonstop & & o - > flags & OF_NON_STOP & &
2005-07-13 20:04:01 +02:00
_m [ tile ] . m2 = = o - > station )
2005-01-30 20:51:39 +01:00
return false ;
2005-07-13 20:04:01 +02:00
if ( v - > last_station_visited = = _m [ tile ] . m2 )
2005-01-30 20:51:39 +01:00
return false ;
2005-07-13 20:04:01 +02:00
if ( _m [ tile ] . m2 ! = o - > station & &
2005-03-09 20:09:04 +01:00
( o - > flags & OF_NON_STOP | | _patches . new_nonstop ) )
2005-01-30 20:51:39 +01:00
return false ;
return true ;
}
2005-01-26 13:51:04 +01:00
//new acceleration
static int GetTrainAcceleration ( Vehicle * v , bool mode )
{
2005-03-23 09:24:13 +01:00
const Vehicle * u ;
2005-01-26 13:51:04 +01:00
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
2005-03-23 09:24:13 +01:00
for ( u = v ; u - > next ! = NULL ; u = u - > next , pos + + ) {
2005-01-26 13:51:04 +01:00
int dir = u - > direction ;
int ndir = u - > next - > direction ;
2005-03-23 09:24:13 +01:00
int i ;
2005-01-26 13:51:04 +01:00
for ( i = 0 ; i < 2 ; i + + ) {
if ( _curve_neighbours45 [ dir ] [ i ] = = ndir ) {
curvecount [ i ] + + ;
if ( lastpos ! = - 1 ) {
2005-03-23 09:24:13 +01:00
numcurve + + ;
sum + = pos - lastpos ;
2005-01-26 13:51:04 +01:00
if ( pos - lastpos = = 1 ) {
max_speed = 88 ;
}
}
lastpos = pos ;
}
}
//if we have a 90 degree turn, fix the speed limit to 60
2005-03-09 20:09:04 +01:00
if ( _curve_neighbours90 [ dir ] [ 0 ] = = ndir | |
_curve_neighbours90 [ dir ] [ 1 ] = = ndir ) {
2005-01-26 13:51:04 +01:00
max_speed = 61 ;
}
}
2005-03-09 20:09:04 +01:00
if ( numcurve > 0 ) sum / = numcurve ;
2005-01-26 13:51:04 +01:00
2005-03-09 20:09:04 +01:00
if ( ( curvecount [ 0 ] ! = 0 | | curvecount [ 1 ] ! = 0 ) & & max_speed > 88 ) {
2005-01-26 13:51:04 +01:00
int total = curvecount [ 0 ] + curvecount [ 1 ] ;
2005-03-09 20:09:04 +01:00
2005-01-26 13:51:04 +01:00
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 ) & & v - > subtype = = TS_Front_Engine ) {
2005-01-30 20:51:39 +01:00
if ( TrainShouldStop ( v , v - > tile ) ) {
2005-01-26 13:51:04 +01:00
int station_length = 0 ;
TileIndex tile = v - > tile ;
int delta_v ;
max_speed = 120 ;
do {
station_length + + ;
2005-03-23 09:24:13 +01:00
tile = TILE_ADD ( tile , TileOffsByDir ( v - > direction / 2 ) ) ;
2005-04-12 12:31:26 +02:00
} while ( IsCompatibleTrainStationTile ( tile , v - > tile ) ) ;
2005-01-26 13:51:04 +01:00
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 ) ;
}
}
2005-06-05 17:37:00 +02:00
mass = v - > u . rail . cached_weight ;
power = v - > u . rail . cached_power * 746 ;
max_speed = min ( max_speed , v - > u . rail . cached_max_speed ) ;
2005-01-26 13:51:04 +01:00
2005-06-05 17:37:00 +02:00
for ( u = v ; u ! = NULL ; u = u - > next ) {
2005-01-26 13:51:04 +01:00
num + + ;
drag_coeff + = 3 ;
if ( u - > u . rail . track = = 0x80 )
2005-06-01 12:30:45 +02:00
max_speed = min ( 61 , max_speed ) ;
2005-01-26 13:51:04 +01:00
2005-04-12 11:17:51 +02:00
if ( HASBIT ( u - > u . rail . flags , VRF_GOINGUP ) ) {
2005-06-05 17:37:00 +02:00
incl + = u - > u . rail . cached_veh_weight * 60 ; //3% slope, quite a bit actually
2005-04-12 11:17:51 +02:00
} else if ( HASBIT ( u - > u . rail . flags , VRF_GOINGDOWN ) ) {
2005-06-05 17:37:00 +02:00
incl - = u - > u . rail . cached_veh_weight * 60 ;
2005-01-26 13:51:04 +01:00
}
}
v - > max_speed = max_speed ;
if ( v - > u . rail . railtype ! = 2 ) {
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 0 :
case 1 :
force = power / speed ; //[N]
force * = 22 ;
force / = 10 ;
2005-03-09 20:09:04 +01:00
break ;
2005-01-26 13:51:04 +01:00
case 2 :
force = power / 25 ;
2005-03-09 20:09:04 +01:00
break ;
2005-01-26 13:51:04 +01:00
}
2005-03-09 20:09:04 +01:00
} else {
2005-01-26 13:51:04 +01:00
//"kickoff" acceleration
2005-06-23 08:19:06 +02:00
force = ( mass * 8 ) + resistance ;
2005-03-09 20:09:04 +01:00
}
2005-01-26 13:51:04 +01:00
if ( force < = 0 ) force = 10000 ;
2005-03-09 20:09:04 +01:00
if ( v - > u . rail . railtype ! = 2 ) force = min ( force , mass * 10 * 200 ) ;
2005-01-26 13:51:04 +01:00
if ( mode = = AM_ACCEL ) {
2005-04-12 11:21:04 +02:00
return ( force - resistance ) / ( mass * 4 ) ;
2005-01-26 13:51:04 +01:00
} else {
2005-03-09 20:09:04 +01:00
return min ( ( - force - resistance ) / ( mass * 4 ) , 10000 / ( mass * 4 ) ) ;
2005-01-26 13:51:04 +01:00
}
}
2004-08-09 19:04:08 +02:00
void UpdateTrainAcceleration ( Vehicle * v )
{
2005-03-09 20:09:04 +01:00
uint power = 0 ;
uint weight = 0 ;
2004-08-09 19:04:08 +02:00
2005-01-19 20:15:03 +01:00
assert ( v - > subtype = = TS_Front_Engine ) ;
2004-08-09 19:04:08 +02:00
2005-06-05 17:37:00 +02:00
weight = v - > u . rail . cached_weight ;
power = v - > u . rail . cached_power ;
v - > max_speed = v - > u . rail . cached_max_speed ;
2004-08-09 19:04:08 +02:00
assert ( weight ! = 0 ) ;
2005-03-09 20:09:04 +01:00
v - > acceleration = clamp ( power / weight * 4 , 1 , 255 ) ;
2004-08-09 19:04:08 +02:00
}
2005-03-09 22:54:52 +01:00
int GetTrainImage ( const Vehicle * v , byte direction )
2004-08-09 19:04:08 +02:00
{
int img = v - > spritenum ;
int base ;
if ( is_custom_sprite ( img ) ) {
2005-05-05 22:46:14 +02:00
base = GetCustomVehicleSprite ( v , direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE ( img ) ) ;
2005-03-09 20:09:04 +01:00
if ( base ! = 0 ) return base ;
2004-08-09 19:04:08 +02:00
img = _engine_original_sprites [ v - > engine_type ] ;
}
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
base = _engine_sprite_base [ img ] + ( ( direction + _engine_sprite_add [ img ] ) & _engine_sprite_and [ img ] ) ;
if ( v - > cargo_count > = ( v - > cargo_cap > > 1 ) )
base + = _wagon_full_adder [ img ] ;
return base ;
}
void DrawTrainEngine ( int x , int y , int engine , uint32 image_ormod )
{
2004-12-04 08:41:37 +01:00
const RailVehicleInfo * rvi = RailVehInfo ( engine ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
int img = rvi - > image_index ;
uint32 image = 0 ;
if ( is_custom_sprite ( img ) ) {
2004-11-12 18:16:13 +01:00
image = GetCustomVehicleIcon ( engine , 6 ) ;
2005-03-09 20:09:04 +01:00
if ( image = = 0 ) img = _engine_original_sprites [ engine ] ;
2004-08-09 19:04:08 +02:00
}
2005-03-09 20:09:04 +01:00
if ( image = = 0 ) {
2004-08-09 19:04:08 +02:00
image = ( 6 & _engine_sprite_and [ img ] ) + _engine_sprite_base [ img ] ;
}
if ( rvi - > flags & RVI_MULTIHEAD ) {
2005-03-09 20:09:04 +01:00
DrawSprite ( image | image_ormod , x - 14 , y ) ;
2004-08-09 19:04:08 +02:00
x + = 15 ;
image = 0 ;
if ( is_custom_sprite ( img ) ) {
2004-11-12 18:16:13 +01:00
image = GetCustomVehicleIcon ( engine , 2 ) ;
2005-03-09 20:09:04 +01:00
if ( image = = 0 ) img = _engine_original_sprites [ engine ] ;
2004-08-09 19:04:08 +02:00
}
2005-03-09 20:09:04 +01:00
if ( image = = 0 ) {
image =
( ( 6 + _engine_sprite_add [ img + 1 ] ) & _engine_sprite_and [ img + 1 ] ) +
_engine_sprite_base [ img + 1 ] ;
2004-08-09 19:04:08 +02:00
}
}
DrawSprite ( image | image_ormod , x , y ) ;
}
2005-06-24 14:38:35 +02:00
static int32 CmdBuildRailWagon ( uint engine , TileIndex tile , uint32 flags )
2004-08-09 19:04:08 +02:00
{
int32 value ;
Vehicle * v ;
const RailVehicleInfo * rvi ;
const Engine * e ;
int x , y ;
2005-01-04 18:11:03 +01:00
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
2004-12-04 08:41:37 +01:00
rvi = RailVehInfo ( engine ) ;
2004-08-09 19:04:08 +02:00
value = ( rvi - > base_cost * _price . build_railwagon ) > > 8 ;
if ( ! ( flags & DC_QUERY_COST ) ) {
_error_message = STR_00E1_TOO_MANY_VEHICLES_IN_GAME ;
v = AllocateVehicle ( ) ;
if ( v = = NULL )
return CMD_ERROR ;
if ( flags & DC_EXEC ) {
byte img = rvi - > image_index ;
2005-01-06 23:31:58 +01:00
Vehicle * u , * w ;
2005-07-21 08:31:02 +02:00
uint dir ;
2004-08-09 19:04:08 +02:00
v - > spritenum = img ;
2005-01-06 23:31:58 +01:00
u = NULL ;
2004-08-09 19:04:08 +02:00
2005-01-06 23:31:58 +01:00
FOR_ALL_VEHICLES ( w ) {
2005-06-27 08:57:24 +02:00
if ( w - > type = = VEH_Train & & w - > tile = = tile & &
2005-01-19 20:15:03 +01:00
w - > subtype = = TS_Free_Car & & w - > engine_type = = engine ) {
2005-01-06 23:31:58 +01:00
u = GetLastVehicleInChain ( w ) ;
2004-08-09 19:04:08 +02:00
break ;
}
}
v - > engine_type = engine ;
2005-07-21 08:31:02 +02:00
dir = GB ( _m [ tile ] . m5 , 0 , 2 ) ;
2004-08-09 19:04:08 +02:00
2005-07-21 08:31:02 +02:00
v - > direction = dir * 2 + 1 ;
2005-06-27 08:57:24 +02:00
v - > tile = tile ;
2004-09-10 21:02:27 +02:00
(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.
2005-06-16 20:04:02 +02:00
x = TileX ( tile ) * TILE_SIZE | _vehicle_initial_x_fract [ dir ] ;
y = TileY ( tile ) * TILE_SIZE | _vehicle_initial_y_fract [ dir ] ;
2004-08-09 19:04:08 +02:00
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 ;
2005-01-19 20:15:03 +01:00
v - > subtype = TS_Free_Car ;
2004-08-09 19:04:08 +02:00
if ( u ! = NULL ) {
u - > next = v ;
2005-01-19 20:15:03 +01:00
v - > subtype = TS_Not_First ;
2004-08-09 19:04:08 +02:00
}
v - > cargo_type = rvi - > cargo_type ;
v - > cargo_cap = rvi - > capacity ;
v - > value = value ;
// v->day_counter = 0;
2005-06-07 20:13:49 +02:00
e = GetEngine ( engine ) ;
2004-08-09 19:04:08 +02:00
v - > u . rail . railtype = e - > railtype ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
v - > build_year = _cur_year ;
v - > type = VEH_Train ;
v - > cur_image = 0xAC2 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
_new_wagon_id = v - > index ;
VehiclePositionChanged ( v ) ;
2005-06-06 16:26:15 +02:00
TrainConsistChanged ( GetFirstVehicleInChain ( v ) ) ;
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
}
return value ;
}
// Move all free vehicles in the depot to the train
static void NormalizeTrainVehInDepot ( Vehicle * u )
{
Vehicle * v ;
2005-03-09 20:09:04 +01:00
2004-08-09 19:04:08 +02:00
FOR_ALL_VEHICLES ( v ) {
2005-01-19 20:15:03 +01:00
if ( v - > type = = VEH_Train & & v - > subtype = = TS_Free_Car & &
2004-08-09 19:04:08 +02:00
v - > tile = = u - > tile & &
v - > u . rail . track = = 0x80 ) {
2005-03-09 20:09:04 +01:00
if ( DoCommandByTile ( 0 , v - > index | ( u - > index < < 16 ) , 1 , DC_EXEC ,
CMD_MOVE_RAIL_VEHICLE ) = = CMD_ERROR )
2004-08-09 19:04:08 +02:00
break ;
}
}
}
static const byte _railveh_unk1 [ ] = {
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 1 , 0 , 1 , 0 ,
0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
} ;
static const byte _railveh_score [ ] = {
1 , 4 , 7 , 19 , 20 , 30 , 31 , 19 ,
20 , 21 , 22 , 10 , 11 , 30 , 31 , 32 ,
33 , 34 , 35 , 29 , 45 , 32 , 50 , 40 ,
41 , 51 , 52 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 60 , 62 ,
63 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 70 , 71 , 72 , 73 ,
74 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
} ;
2004-12-27 19:18:44 +01:00
int32 EstimateTrainCost ( const RailVehicleInfo * rvi )
2004-08-09 19:04:08 +02:00
{
return ( rvi - > base_cost * ( _price . build_railvehicle > > 3 ) ) > > 5 ;
}
2005-01-23 14:09:35 +01:00
void AddRearEngineToMultiheadedTrain ( Vehicle * v , Vehicle * u , bool building )
2005-01-19 00:27:06 +01:00
{
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 ;
2005-01-19 20:15:03 +01:00
u - > subtype = TS_Not_First ;
2005-01-19 00:27:06 +01:00
u - > spritenum = v - > spritenum + 1 ;
u - > cargo_type = v - > cargo_type ;
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 ;
2005-03-09 20:09:04 +01:00
if ( building ) v - > value > > = 1 ;
u - > value = v - > value ;
2005-01-19 00:27:06 +01:00
u - > type = VEH_Train ;
u - > cur_image = 0xAC2 ;
VehiclePositionChanged ( u ) ;
}
2005-05-10 00:33:00 +02:00
/** Build a railroad vehicle.
* @ param x , y tile coordinates ( depot ) where rail - vehicle is built
* @ param p1 engine type id
* @ param p2 unused
2004-08-09 19:04:08 +02:00
*/
int32 CmdBuildRailVehicle ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
const RailVehicleInfo * rvi ;
2005-07-21 08:31:02 +02:00
int value ;
2004-08-09 19:04:08 +02:00
Vehicle * v , * u ;
2005-02-04 15:24:23 +01:00
UnitID unit_num ;
2004-08-09 19:04:08 +02:00
Engine * e ;
2005-06-25 08:15:43 +02:00
TileIndex tile = TileVirtXY ( x , y ) ;
2004-08-09 19:04:08 +02:00
2005-05-10 00:33:00 +02:00
/* Check if the engine-type is valid (for the player) */
2005-01-27 22:18:03 +01:00
if ( ! IsEngineBuildable ( p1 , VEH_Train ) ) return CMD_ERROR ;
2005-01-27 22:00:05 +01:00
2005-05-10 00:33:00 +02:00
/* Check if the train is actually being built in a depot belonging
* to the player . Doesn ' t matter if only the cost is queried */
2005-03-06 17:58:42 +01:00
if ( ! ( flags & DC_QUERY_COST ) ) {
if ( ! IsTileDepotType ( tile , TRANSPORT_RAIL ) ) return CMD_ERROR ;
2005-06-04 13:56:32 +02:00
if ( ! IsTileOwner ( tile , _current_player ) ) return CMD_ERROR ;
2005-03-06 17:58:42 +01:00
}
2005-01-30 00:58:07 +01:00
2004-08-09 19:04:08 +02:00
_cmd_build_rail_veh_var1 = 0 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
2004-12-04 08:41:37 +01:00
rvi = RailVehInfo ( p1 ) ;
2004-09-10 21:02:27 +02:00
2005-05-10 00:33:00 +02:00
if ( rvi - > flags & RVI_WAGON ) return CmdBuildRailWagon ( p1 , tile , flags ) ;
2004-08-09 19:04:08 +02:00
value = EstimateTrainCost ( rvi ) ;
if ( ! ( flags & DC_QUERY_COST ) ) {
v = AllocateVehicle ( ) ;
2005-01-15 20:06:22 +01:00
if ( v = = NULL | | IsOrderPoolFull ( ) )
2004-08-09 19:04:08 +02:00
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
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 ) {
2005-07-21 08:31:02 +02:00
uint dir ;
2004-08-09 19:04:08 +02:00
v - > unitnumber = unit_num ;
2005-07-21 08:31:02 +02:00
dir = GB ( _m [ tile ] . m5 , 0 , 2 ) ;
2004-08-09 19:04:08 +02:00
2005-07-21 08:31:02 +02:00
v - > direction = dir * 2 + 1 ;
2005-06-27 08:57:24 +02:00
v - > tile = tile ;
2004-08-09 19:04:08 +02:00
v - > owner = _current_player ;
v - > x_pos = ( x | = _vehicle_initial_x_fract [ dir ] ) ;
v - > y_pos = ( y | = _vehicle_initial_y_fract [ dir ] ) ;
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_cap = rvi - > capacity ;
v - > max_speed = rvi - > max_speed ;
v - > value = value ;
2005-02-02 17:16:43 +01:00
v - > last_station_visited = INVALID_STATION ;
2004-08-09 19:04:08 +02:00
v - > dest_tile = 0 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
v - > engine_type = ( byte ) p1 ;
2005-06-07 20:13:49 +02:00
e = GetEngine ( p1 ) ;
2004-08-09 19:04:08 +02:00
v - > reliability = e - > reliability ;
v - > reliability_spd_dec = e - > reliability_spd_dec ;
v - > max_age = e - > lifelength * 366 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
v - > string_id = STR_SV_TRAIN_NAME ;
v - > u . rail . railtype = e - > railtype ;
_new_train_id = v - > index ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
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 ;
VehiclePositionChanged ( v ) ;
2005-01-19 00:27:06 +01:00
if ( rvi - > flags & RVI_MULTIHEAD & & ( u = AllocateVehicle ( ) ) ! = NULL )
AddRearEngineToMultiheadedTrain ( v , u , true ) ;
2004-08-09 19:04:08 +02:00
2005-06-05 17:37:00 +02:00
TrainConsistChanged ( v ) ;
2004-08-09 19:04:08 +02:00
UpdateTrainAcceleration ( v ) ;
NormalizeTrainVehInDepot ( v ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , tile ) ;
2004-12-10 19:16:08 +01:00
RebuildVehicleLists ( ) ;
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
}
}
_cmd_build_rail_veh_var1 = _railveh_unk1 [ p1 ] ;
_cmd_build_rail_veh_score = _railveh_score [ p1 ] ;
2005-01-23 23:01:51 +01:00
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ; // updates the replace Train window
2004-09-10 21:02:27 +02:00
return value ;
2004-08-09 19:04:08 +02:00
}
(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.
2005-06-16 20:04:02 +02:00
/* Check if all the wagons of the given train are in a depot, returns the
* number of cars ( including loco ) then . If not , sets the error message to
* STR_881A_TRAINS_CAN_ONLY_BE_ALTERED and returns - 1 */
2005-03-09 20:09:04 +01:00
int CheckTrainStoppedInDepot ( const Vehicle * v )
2004-08-09 19:04:08 +02:00
{
int count ;
TileIndex tile = v - > tile ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* check if stopped in a depot */
2005-02-06 23:36:08 +01:00
if ( ! IsTileDepotType ( tile , TRANSPORT_RAIL ) | | v - > cur_speed ! = 0 ) {
2004-08-09 19:04:08 +02:00
_error_message = STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ;
return - 1 ;
}
count = 0 ;
2005-03-09 20:09:04 +01:00
for ( ; v ! = NULL ; v = v - > next ) {
2004-08-09 19:04:08 +02:00
count + + ;
2005-03-09 20:09:04 +01:00
if ( v - > u . rail . track ! = 0x80 | | v - > tile ! = tile | |
( v - > subtype = = TS_Front_Engine & & ! ( v - > vehstatus & VS_STOPPED ) ) ) {
_error_message = STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ;
return - 1 ;
}
}
2004-08-09 19:04:08 +02:00
return count ;
}
// unlink a rail wagon from the linked list.
// returns the new value of first
static Vehicle * UnlinkWagon ( Vehicle * v , Vehicle * first )
{
2005-05-05 22:46:14 +02:00
Vehicle * u ;
2004-08-09 19:04:08 +02:00
// unlinking the first vehicle of the chain?
if ( v = = first ) {
2005-03-09 20:09:04 +01:00
v = v - > next ;
if ( v = = NULL ) return NULL ;
2005-05-05 22:46:14 +02:00
2005-01-19 20:15:03 +01:00
v - > subtype = TS_Free_Car ;
2004-08-09 19:04:08 +02:00
return v ;
}
2005-05-05 22:46:14 +02:00
for ( u = first ; u - > next ! = v ; u = u - > next ) { }
u - > next = v - > next ;
return first ;
2004-08-09 19:04:08 +02:00
}
2005-03-09 20:09:04 +01:00
static Vehicle * FindGoodVehiclePos ( const Vehicle * src )
2004-08-09 19:04:08 +02:00
{
Vehicle * dst ;
uint16 eng = src - > engine_type ;
TileIndex tile = src - > tile ;
FOR_ALL_VEHICLES ( dst ) {
2005-03-09 20:09:04 +01:00
if ( dst - > type = = VEH_Train & & dst - > subtype = = TS_Free_Car & &
dst - > tile = = tile ) {
2004-08-09 19:04:08 +02:00
// check so all vehicles in the line have the same engine.
Vehicle * v = dst ;
2005-03-09 20:09:04 +01:00
2004-08-09 19:04:08 +02:00
while ( v - > engine_type = = eng ) {
2005-03-09 20:09:04 +01:00
v = v - > next ;
if ( v = = NULL ) return dst ;
2004-08-09 19:04:08 +02:00
}
}
}
return NULL ;
}
2005-05-10 00:33:00 +02:00
/** Move a rail vehicle around inside the depot.
* @ param x , y unused
* @ param p1 various bitstuffed elements
* - p1 ( bit 0 - 15 ) source vehicle index ( p1 & 0xFFFF )
* - p1 ( bit 16 - 31 ) what wagon to put the source wagon AFTER ( p1 & 0xFFFF0000 ) XXX - 0xFFFF0000 to make a new line
* @ param p2 ( bit 0 ) move all vehicles following the source vehicle
2004-08-09 19:04:08 +02:00
*/
int32 CmdMoveRailVehicle ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * src , * dst , * src_head , * dst_head ;
bool is_loco ;
2005-01-30 21:50:06 +01:00
if ( ! IsVehicleIndex ( p1 & 0xFFFF ) ) return CMD_ERROR ;
2005-01-06 23:31:58 +01:00
src = GetVehicle ( p1 & 0xFFFF ) ;
2005-01-30 21:50:06 +01:00
2004-08-09 19:04:08 +02:00
if ( src - > type ! = VEH_Train ) return CMD_ERROR ;
2005-05-05 22:46:14 +02:00
is_loco = ! ( RailVehInfo ( src - > engine_type ) - > flags & RVI_WAGON ) & & IS_FIRSTHEAD_SPRITE ( src - > spritenum ) ;
2004-08-09 19:04:08 +02:00
// if nothing is selected as destination, try and find a matching vehicle to drag to.
2004-09-10 21:02:27 +02:00
if ( ( ( int32 ) p1 > > 16 ) = = - 1 ) {
2004-08-09 19:04:08 +02:00
dst = NULL ;
if ( ! is_loco ) dst = FindGoodVehiclePos ( src ) ;
} else {
2005-03-09 20:09:04 +01:00
dst = GetVehicle ( ( int32 ) p1 > > 16 ) ;
2004-08-09 19:04:08 +02:00
}
// don't move the same vehicle..
2005-05-10 00:33:00 +02:00
if ( src = = dst ) return 0 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* 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 ) ;
2004-09-10 21:02:27 +02:00
2005-03-29 13:19:10 +02:00
/* 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 ;
}
2005-05-10 00:33:00 +02:00
/* check if all vehicles in the source train are stopped inside a depot */
if ( CheckTrainStoppedInDepot ( src_head ) < 0 ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
/* check if all the vehicles in the dest train are stopped,
* and that the length of the dest train is no longer than XXX vehicles */
if ( dst_head ! = NULL ) {
2005-02-06 11:18:47 +01:00
int num = CheckTrainStoppedInDepot ( dst_head ) ;
2005-05-10 00:33:00 +02:00
if ( num < 0 ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
2005-05-05 22:46:14 +02:00
if ( num > ( _patches . mammoth_trains ? 100 : 9 ) & & dst_head - > subtype = = TS_Front_Engine )
2004-08-09 19:04:08 +02:00
return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
// if it's a multiheaded vehicle we're dragging to, drag to the vehicle before..
2005-05-05 22:46:14 +02:00
while ( IS_CUSTOM_SECONDHEAD_SPRITE ( dst - > spritenum ) | | (
! is_custom_sprite ( dst - > spritenum ) & & _engine_sprite_add [ dst - > spritenum ] ! = 0 )
2005-03-09 20:09:04 +01:00
) {
2004-08-09 19:04:08 +02:00
Vehicle * v = GetPrevVehicleInChain ( dst ) ;
2005-03-09 20:09:04 +01:00
if ( v = = NULL | | src = = v ) break ;
2004-08-09 19:04:08 +02:00
dst = v ;
}
assert ( dst_head - > tile = = src_head - > tile ) ;
}
// when moving all wagons, we can't have the same src_head and dst_head
2005-05-10 00:33:00 +02:00
if ( HASBIT ( p2 , 0 ) & & src_head = = dst_head ) return 0 ;
2004-08-09 19:04:08 +02:00
// moving a loco to a new line?, then we need to assign a unitnumber.
2005-01-19 20:15:03 +01:00
if ( dst = = NULL & & src - > subtype ! = TS_Front_Engine & & is_loco ) {
2005-02-04 15:24:23 +01:00
UnitID unit_num = GetFreeUnitNumber ( VEH_Train ) ;
2004-08-09 19:04:08 +02:00
if ( unit_num > _patches . max_trains )
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
if ( flags & DC_EXEC )
src - > unitnumber = unit_num ;
}
/* do it? */
if ( flags & DC_EXEC ) {
2005-05-10 00:33:00 +02:00
if ( HASBIT ( p2 , 0 ) ) {
2004-08-09 19:04:08 +02:00
// unlink ALL wagons
if ( src ! = src_head ) {
Vehicle * v = src_head ;
while ( v - > next ! = src ) v = v - > next ;
v - > next = NULL ;
2005-06-06 16:26:15 +02:00
} else {
src_head = NULL ;
2004-08-09 19:04:08 +02:00
}
} else {
2005-06-06 23:32:04 +02:00
// if moving within the same chain, dont use dst_head as it may get invalidated
if ( src_head = = dst_head )
dst_head = NULL ;
2004-08-09 19:04:08 +02:00
// unlink single wagon from linked list
2005-06-06 16:26:15 +02:00
src_head = UnlinkWagon ( src , src_head ) ;
2004-08-09 19:04:08 +02:00
src - > next = NULL ;
}
if ( dst = = NULL ) {
// move the train to an empty line. for locomotives, we set the type to 0. for wagons, 4.
if ( is_loco ) {
2005-01-19 20:15:03 +01:00
if ( src - > subtype ! = TS_Front_Engine ) {
2005-01-15 20:06:22 +01:00
// setting the type to 0 also involves setting up the orders field.
2005-01-19 20:15:03 +01:00
src - > subtype = TS_Front_Engine ;
2005-01-15 20:06:22 +01:00
assert ( src - > orders = = NULL ) ;
2004-08-09 19:04:08 +02:00
src - > num_orders = 0 ;
}
} else {
2005-01-19 20:15:03 +01:00
src - > subtype = TS_Free_Car ;
2004-08-09 19:04:08 +02:00
}
2005-06-06 16:26:15 +02:00
dst_head = src ;
2004-08-09 19:04:08 +02:00
} else {
2005-01-19 20:15:03 +01:00
if ( src - > subtype = = TS_Front_Engine ) {
2005-01-15 20:06:22 +01:00
// the vehicle was previously a loco. need to free the order list and delete vehicle windows etc.
2004-08-09 19:04:08 +02:00
DeleteWindowById ( WC_VEHICLE_VIEW , src - > index ) ;
2005-01-15 20:06:22 +01:00
DeleteVehicleOrders ( src ) ;
2004-08-09 19:04:08 +02:00
}
2004-09-10 21:02:27 +02:00
2005-01-19 20:15:03 +01:00
src - > subtype = TS_Not_First ;
2004-08-09 19:04:08 +02:00
src - > unitnumber = 0 ; // doesn't occupy a unitnumber anymore.
// link in the wagon(s) in the chain.
{
2005-03-09 20:09:04 +01:00
Vehicle * v ;
2005-06-06 16:26:15 +02:00
for ( v = src ; v - > next ! = NULL ; v = v - > next ) { } ;
2004-08-09 19:04:08 +02:00
v - > next = dst - > next ;
}
dst - > next = src ;
}
2005-06-06 16:26:15 +02:00
if ( src_head ) {
2005-06-05 17:37:00 +02:00
TrainConsistChanged ( src_head ) ;
2005-06-06 16:26:15 +02:00
if ( src_head - > subtype = = TS_Front_Engine ) {
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 ) ;
} ;
2004-08-09 19:04:08 +02:00
if ( dst_head ) {
2005-06-06 16:26:15 +02:00
TrainConsistChanged ( dst_head ) ;
2005-06-05 17:37:00 +02:00
if ( dst_head - > subtype = = TS_Front_Engine ) {
2004-08-09 19:04:08 +02:00
UpdateTrainAcceleration ( dst_head ) ;
2005-06-06 16:26:15 +02:00
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 ) ;
2005-06-05 17:37:00 +02:00
}
2005-06-06 16:26:15 +02:00
/* Update the depot window */
InvalidateWindow ( WC_VEHICLE_DEPOT , dst_head - > tile ) ;
2004-08-09 19:04:08 +02:00
}
2004-12-20 19:11:22 +01:00
RebuildVehicleLists ( ) ;
2004-08-09 19:04:08 +02:00
}
return 0 ;
}
2005-05-10 00:33:00 +02:00
/** Start/Stop a train.
* @ param x , y unused
* @ param p1 train to start / stop
* @ param p2 unused
*/
2004-08-09 19:04:08 +02:00
int32 CmdStartStopTrain ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
2005-01-30 21:50:06 +01:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 23:31:58 +01:00
v = GetVehicle ( p1 ) ;
2004-08-09 19:04:08 +02:00
2005-05-10 00:33:00 +02:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
if ( flags & DC_EXEC ) {
v - > u . rail . days_since_order_progr = 0 ;
v - > vehstatus ^ = VS_STOPPED ;
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
return 0 ;
}
2005-05-05 22:46:14 +02:00
/**
* Search for a matching rear - engine of a dual - headed train .
* Do this as if you would find matching parentheses . If a new
* engine is ' started ' , first ' close ' that before ' closing ' our
* searched engine
*/
static Vehicle * GetRearEngine ( const Vehicle * v , uint16 engine )
{
Vehicle * u ;
int en_count = 1 ;
for ( u = v - > next ; u ! = NULL ; u = u - > next ) {
if ( u - > engine_type = = engine ) { // find matching engine
en_count + = ( IS_FIRSTHEAD_SPRITE ( u - > spritenum ) ) ? + 1 : - 1 ;
if ( en_count = = 0 ) return ( Vehicle * ) u ;
}
}
return NULL ;
}
2005-05-10 00:33:00 +02:00
/** Sell a (single) train wagon/engine.
* @ param x , y unused
2005-05-05 22:46:14 +02:00
* @ param p1 the wagon / engine index
* @ param p2 the selling mode
2005-05-10 00:33:00 +02:00
* - 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
2005-05-05 22:46:14 +02:00
*/
2004-08-09 19:04:08 +02:00
int32 CmdSellRailWagon ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
2005-05-05 22:46:14 +02:00
Vehicle * v , * tmp , * first ;
int32 cost = 0 ;
2004-08-09 19:04:08 +02:00
2005-05-05 22:46:14 +02:00
if ( ! IsVehicleIndex ( p1 ) | | p2 > 2 ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
2005-01-06 23:31:58 +01:00
v = GetVehicle ( p1 ) ;
2004-08-09 19:04:08 +02:00
2005-05-10 00:33:00 +02:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
2005-01-30 21:50:06 +01:00
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
2005-05-05 22:46:14 +02:00
first = GetFirstVehicleInChain ( v ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// make sure the vehicle is stopped in the depot
2005-05-10 00:33:00 +02:00
if ( CheckTrainStoppedInDepot ( first ) < 0 ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
2005-05-18 01:08:21 +02:00
if ( flags & DC_EXEC ) {
if ( v = = first & & first - > subtype = = TS_Front_Engine ) {
DeleteWindowById ( WC_VEHICLE_VIEW , first - > index ) ;
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ;
}
2005-05-05 22:46:14 +02:00
InvalidateWindow ( WC_VEHICLE_DEPOT , first - > tile ) ;
2005-05-08 22:53:02 +02:00
RebuildVehicleLists ( ) ;
2005-05-05 22:46:14 +02:00
}
2004-08-09 19:04:08 +02:00
2005-05-05 22:46:14 +02:00
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
2005-03-09 20:09:04 +01:00
2005-05-05 22:46:14 +02:00
/* 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 = ( RailVehInfo ( v - > engine_type ) - > flags & RVI_MULTIHEAD ) ? GetRearEngine ( v , v - > engine_type ) : NULL ;
if ( rear ! = NULL ) {
cost - = v - > value ;
if ( flags & DC_EXEC ) {
v = UnlinkWagon ( rear , v ) ;
DeleteVehicle ( rear ) ;
}
}
2005-03-09 20:09:04 +01:00
2005-05-05 22:46:14 +02:00
/* 2. We are selling the first engine, some special action might be required
* here , so take attention */
2005-05-06 18:13:44 +02:00
if ( ( flags & DC_EXEC ) & & v = = first ) {
2005-05-05 22:46:14 +02:00
Vehicle * new_f = first - > next ;
/* 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 ( ! ( RailVehInfo ( new_f - > engine_type ) - > flags & RVI_WAGON ) & & IS_FIRSTHEAD_SPRITE ( new_f - > spritenum ) ) {
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 ;
new_f - > num_orders = first - > num_orders ;
first - > orders = NULL ; // XXX - to not to delete the orders */
ShowTrainViewWindow ( new_f ) ;
}
}
}
/* 3. Delete the requested wagon */
cost - = v - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
DeleteVehicle ( v ) ;
2005-03-29 13:19:10 +02:00
2005-05-05 22:46:14 +02:00
/* 4 If the second wagon was an engine, update it to front_engine
* which UnlinkWagon ( ) has changed to TS_Free_Car */
if ( switch_engine ) first - > subtype = TS_Front_Engine ;
/* 5. If the train still exists, update its acceleration, window, etc. */
2005-06-06 16:26:15 +02:00
if ( first ! = NULL ) {
2005-06-05 17:37:00 +02:00
TrainConsistChanged ( first ) ;
2005-06-06 16:26:15 +02:00
if ( first - > subtype = = TS_Front_Engine ) {
InvalidateWindow ( WC_VEHICLE_DETAILS , first - > index ) ;
InvalidateWindow ( WC_VEHICLE_REFIT , first - > index ) ;
UpdateTrainAcceleration ( first ) ;
}
2005-05-05 22:46:14 +02:00
}
2004-09-10 21:02:27 +02:00
2005-03-09 20:09:04 +01:00
2005-05-05 22:46:14 +02:00
/* (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 & & ori_subtype = = TS_Front_Engine ) {
for ( v = first ; v ! = NULL ; v = tmp ) {
tmp = v - > next ;
DoCommandByTile ( v - > tile , v - > index | INVALID_VEHICLE < < 16 , 0 , DC_EXEC , CMD_MOVE_RAIL_VEHICLE ) ;
}
2004-08-09 19:04:08 +02:00
}
}
2005-05-05 22:46:14 +02:00
} break ;
case 1 : { /* Delete wagon and all wagons after it given certain criteria */
/* 1. Count the number for first and rear engines for dualheads
* to be able to deduce which ones go with which ones */
int enf_count = 0 ;
int enr_count = 0 ;
for ( tmp = first ; tmp ! = NULL ; tmp = tmp - > next ) {
if ( RailVehInfo ( tmp - > engine_type ) - > flags & RVI_MULTIHEAD )
( IS_FIRSTHEAD_SPRITE ( tmp - > spritenum ) ) ? enf_count + + : enr_count + + ;
}
2004-08-09 19:04:08 +02:00
2005-05-05 22:46:14 +02:00
/* 2. 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 = v - > next ;
if ( RailVehInfo ( v - > engine_type ) - > flags & RVI_MULTIHEAD ) {
/* Always delete newly encountered front-engines */
if ( IS_FIRSTHEAD_SPRITE ( v - > spritenum ) ) {
enf_count - - ;
/* If we have more rear engines than front engines, then that means
* that this rear - engine does not belong to any front - engine ; delete */
} else if ( enr_count > enf_count ) { enr_count - - ; }
/* Otherwise leave it alone */
else continue ;
}
2004-09-10 21:02:27 +02:00
2005-05-05 22:46:14 +02:00
cost - = v - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
DeleteVehicle ( v ) ;
}
}
2004-08-09 19:04:08 +02:00
2005-06-05 17:37:00 +02:00
/* 3. If it is still a valid train after selling, update its acceleration and cached values */
2005-06-06 16:26:15 +02:00
if ( ( flags & DC_EXEC ) & & first ! = NULL ) {
2005-06-05 17:37:00 +02:00
TrainConsistChanged ( first ) ;
2005-06-06 16:26:15 +02:00
if ( first - > subtype = = TS_Front_Engine )
UpdateTrainAcceleration ( first ) ;
2005-06-05 17:37:00 +02:00
}
2005-05-05 22:46:14 +02:00
} break ;
}
2004-08-09 19:04:08 +02:00
return cost ;
}
static void UpdateTrainDeltaXY ( Vehicle * v , int 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 ] ;
2005-07-21 08:31:02 +02:00
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 ) ;
2004-08-09 19:04:08 +02:00
}
static void UpdateVarsAfterSwap ( Vehicle * v )
{
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
BeginVehicleMove ( v ) ;
VehiclePositionChanged ( v ) ;
EndVehicleMove ( v ) ;
}
2004-09-10 21:02:27 +02:00
static void SetLastSpeed ( Vehicle * v , int spd ) {
2004-08-09 19:04:08 +02:00
int old = v - > u . rail . last_speed ;
if ( spd ! = old ) {
v - > u . rail . last_speed = spd ;
if ( _patches . vehicle_speed | | ! old ! = ! spd )
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
}
}
2005-01-09 17:02:06 +01:00
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 ) ;
}
}
2004-08-09 19:04:08 +02:00
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 ;
}
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* 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 ^ = 4 ;
if ( ! ( b - > u . rail . track & 0x80 ) ) b - > direction ^ = 4 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* swap more variables */
2005-01-25 22:43:57 +01:00
swap_int32 ( & a - > x_pos , & b - > x_pos ) ;
swap_int32 ( & a - > y_pos , & b - > y_pos ) ;
2004-08-09 19:04:08 +02:00
swap_tile ( & a - > tile , & b - > tile ) ;
swap_byte ( & a - > z_pos , & b - > z_pos ) ;
2005-01-09 17:02:06 +01:00
SwapTrainFlags ( & a - > u . rail . flags , & b - > u . rail . flags ) ;
2004-08-09 19:04:08 +02:00
/* update other vars */
UpdateVarsAfterSwap ( a ) ;
UpdateVarsAfterSwap ( b ) ;
2005-03-25 13:07:26 +01:00
VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
VehicleEnterTile ( b , b - > tile , b - > x_pos , b - > y_pos ) ;
2004-08-09 19:04:08 +02:00
} else {
if ( ! ( a - > u . rail . track & 0x80 ) ) a - > direction ^ = 4 ;
2004-09-10 21:02:27 +02:00
UpdateVarsAfterSwap ( a ) ;
2005-03-25 13:07:26 +01:00
VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
2004-08-09 19:04:08 +02:00
}
}
2004-12-21 17:17:27 +01:00
/* 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 ;
}
2005-01-23 11:40:54 +01:00
static void DisableTrainCrossing ( TileIndex tile )
{
/* Test if we have a rail/road-crossing */
2005-06-07 23:37:00 +02:00
if ( IsTileType ( tile , MP_STREET ) & & IsLevelCrossing ( tile ) ) {
2005-01-23 11:40:54 +01:00
/* Check if there is a train on the tile itself */
if ( VehicleFromPos ( tile , & tile , TestTrainOnCrossing ) = = NULL ) {
/* If light is on, switch light off */
2005-07-13 20:04:01 +02:00
if ( _m [ tile ] . m5 & 4 ) {
_m [ tile ] . m5 & = ~ 4 ;
2005-01-23 11:40:54 +01:00
MarkTileDirtyByTile ( tile ) ;
}
}
}
}
2005-06-07 00:44:11 +02:00
/**
* 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 , * first , * last , * tempnext ;
int i , length ;
int differential ;
base = v ;
first = base - > next ;
length = CountVehiclesInChain ( v ) ;
while ( length > 2 ) {
// find pairwise matching wagon
// start<>end, start+1<>end-1, ... */
last = first ;
for ( i = length - 3 ; i ; i - - ) {
last = last - > next ;
}
differential = last - > u . rail . cached_veh_length - base - > u . rail . cached_veh_length ;
if ( before )
differential * = - 1 ;
if ( differential > 0 ) {
// 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 ;
}
}
2005-07-04 16:58:55 +02:00
TileIndex GetVehicleTileOutOfTunnel ( const Vehicle * v , bool reverse )
{
TileIndex tile ;
byte direction = ( ! reverse ) ? DirToDiagdir ( v - > direction ) : ReverseDiagdir ( v - > direction > > 1 ) ;
TileIndexDiff delta = TileOffsByDir ( direction ) ;
if ( v - > u . rail . track ! = 0x40 )
return v - > tile ;
for ( tile = v - > tile ; ; tile + = delta ) {
2005-07-19 13:42:40 +02:00
if ( IsTunnelTile ( tile ) & & ( _m [ tile ] . m5 & 0x3 ) ! = ( direction ) & & GetTileZ ( tile ) = = v - > z_pos )
2005-07-04 16:58:55 +02:00
break ;
}
return tile ;
2005-07-23 21:48:24 +02:00
}
2005-07-04 16:58:55 +02:00
2004-08-09 19:04:08 +02:00
static void ReverseTrainDirection ( Vehicle * v )
{
int l = 0 , r = - 1 ;
Vehicle * u ;
2005-07-04 16:58:55 +02:00
TileIndex tile ;
2005-07-17 22:09:02 +02:00
Trackdir trackdir ;
TileIndex pbs_end_tile = v - > u . rail . pbs_end_tile ; // these may be changed, and we may need
Trackdir pbs_end_trackdir = v - > u . rail . pbs_end_trackdir ; // the old values, so cache them
2005-07-04 16:58:55 +02:00
u = GetLastVehicleInChain ( v ) ;
tile = GetVehicleTileOutOfTunnel ( u , false ) ;
trackdir = ReverseTrackdir ( GetVehicleTrackdir ( u ) ) ;
2005-07-17 22:09:02 +02:00
if ( PBSTileReserved ( tile ) & ( 1 < < TrackdirToTrack ( trackdir ) ) ) {
2005-07-04 16:58:55 +02:00
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
NPFFillWithOrderData ( & fstd , v ) ;
tile = GetVehicleTileOutOfTunnel ( u , true ) ;
DEBUG ( pbs , 2 ) ( " pbs: (%i) choose reverse (RV), tile:%x, trackdir:%i " , v - > unitnumber , u - > tile , trackdir ) ;
ftd = NPFRouteToStationOrTile ( tile , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_ANY ) ;
if ( ftd . best_trackdir = = 0xFF ) {
DEBUG ( pbs , 0 ) ( " pbs: (%i) no nodes encountered (RV) " , v - > unitnumber ) ;
CLRBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
return ;
}
// we found a way out of the pbs block
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_EXIT ) ) {
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_BLOCKED ) ) {
CLRBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
return ;
}
}
2005-07-17 22:09:02 +02:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 16:58:55 +02:00
}
tile = GetVehicleTileOutOfTunnel ( v , false ) ;
trackdir = GetVehicleTrackdir ( v ) ;
if ( v - > u . rail . pbs_status = = PBS_STAT_HAS_PATH ) {
TileIndex tile = AddTileIndexDiffCWrap ( v - > tile , TileIndexDiffCByDir ( TrackdirToExitdir ( trackdir ) ) ) ;
uint32 ts ;
assert ( tile ! = INVALID_TILE ) ;
ts = GetTileTrackStatus ( tile , TRANSPORT_RAIL ) ;
ts & = TrackdirReachesTrackdirs ( trackdir ) ;
assert ( ts ! = 0 & & KillFirstBit2x64 ( ts ) = = 0 ) ;
trackdir = FindFirstBit2x64 ( ts ) ;
2005-07-17 22:09:02 +02:00
PBSClearPath ( tile , trackdir , pbs_end_tile , pbs_end_trackdir ) ;
2005-07-04 16:58:55 +02:00
v - > u . rail . pbs_status = PBS_STAT_NONE ;
2005-07-17 22:09:02 +02:00
} else if ( PBSTileReserved ( tile ) & ( 1 < < TrackdirToTrack ( trackdir ) ) ) {
PBSClearPath ( tile , trackdir , pbs_end_tile , pbs_end_trackdir ) ;
2005-07-04 16:58:55 +02:00
if ( v - > u . rail . track ! = 0x40 )
PBSReserveTrack ( tile , trackdir & 7 ) ;
} ;
2004-08-09 19:04:08 +02:00
2005-02-06 23:36:08 +01:00
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) )
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2005-07-04 16:58:55 +02:00
2004-12-21 17:02:14 +01:00
/* Check if we were approaching a rail/road-crossing */
{
TileIndex tile = v - > tile ;
int t ;
2005-03-08 20:54:10 +01:00
/* Determine the diagonal direction in which we will exit this tile */
2004-12-21 17:02:14 +01:00
t = v - > direction > > 1 ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ t ] ) {
t = ( t - 1 ) & 3 ;
}
/* Calculate next tile */
2005-01-05 14:32:03 +01:00
tile + = TileOffsByDir ( t ) ;
2005-01-23 11:40:54 +01:00
/* Check if the train left a rail/road-crossing */
DisableTrainCrossing ( tile ) ;
2004-12-21 17:02:14 +01:00
}
2004-08-09 19:04:08 +02:00
// count number of vehicles
u = v ;
do r + + ; while ( ( u = u - > next ) ! = NULL ) ;
2005-06-07 00:44:11 +02:00
AdvanceWagons ( v , true ) ;
2004-08-09 19:04:08 +02:00
/* swap start<>end, start+1<>end-1, ... */
do {
ReverseTrainSwapVeh ( v , l + + , r - - ) ;
} while ( l < = r ) ;
2005-06-07 00:44:11 +02:00
AdvanceWagons ( v , false ) ;
2005-02-06 23:36:08 +01:00
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) )
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2005-01-09 17:02:06 +01:00
CLRBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
2004-08-09 19:04:08 +02:00
}
2005-05-10 00:33:00 +02:00
/** Reverse train.
* @ param x , y unused
* @ param p1 train to reverse
* @ param p2 unused
*/
int32 CmdReverseTrainDirection ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 19:04:08 +02:00
{
Vehicle * v ;
2005-01-30 21:50:06 +01:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 23:31:58 +01:00
v = GetVehicle ( p1 ) ;
2004-08-09 19:04:08 +02:00
2005-05-10 00:33:00 +02:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
_error_message = STR_EMPTY ;
2005-02-06 23:36:08 +01:00
// if (v->u.rail.track & 0x80 || IsTileDepotType(v->tile, TRANSPORT_RAIL))
2004-08-09 19:04:08 +02:00
// return CMD_ERROR;
2005-05-10 00:33:00 +02:00
if ( v - > u . rail . crash_anim_pos ! = 0 | | v - > breakdown_ctr ! = 0 ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
if ( flags & DC_EXEC ) {
if ( _patches . realistic_acceleration & & v - > cur_speed ! = 0 ) {
2005-01-09 17:02:06 +01:00
TOGGLEBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
2004-08-09 19:04:08 +02:00
} else {
v - > cur_speed = 0 ;
SetLastSpeed ( v , 0 ) ;
ReverseTrainDirection ( v ) ;
}
}
return 0 ;
}
2005-05-10 00:33:00 +02:00
/** Force a train through a red signal
* @ param x , y unused
* @ param p1 train to ignore the red signal
* @ param p2 unused
*/
2004-08-09 19:04:08 +02:00
int32 CmdForceTrainProceed ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
2005-01-30 21:50:06 +01:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 23:31:58 +01:00
v = GetVehicle ( p1 ) ;
2004-08-09 19:04:08 +02:00
2005-05-10 00:33:00 +02:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
if ( flags & DC_EXEC )
v - > u . rail . force_proceed = 0x50 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
return 0 ;
}
2005-05-14 14:36:16 +02:00
/** Refits a train to the specified cargo type.
* @ param x , y 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 & 0xFF )
* - p2 = ( bit 8 ) - skip check for stopped in depot , used by autoreplace ( p2 & 0x100 )
* @ todo p2 bit8 check < b > NEEDS TO GO < / b >
*/
2004-08-09 19:04:08 +02:00
int32 CmdRefitRailVehicle ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
int32 cost ;
uint num ;
2005-05-14 14:36:16 +02:00
CargoID new_cid = p2 & 0xFF ; //gets the cargo number
bool SkipStoppedInDepotCheck = ! ! HASBIT ( p2 , 8 ) ; // XXX - needs to go, yes?
2004-08-09 19:04:08 +02:00
2005-01-30 21:50:06 +01:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 19:45:28 +01:00
2005-01-06 23:31:58 +01:00
v = GetVehicle ( p1 ) ;
2005-01-30 21:50:06 +01:00
2005-05-14 14:36:16 +02:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
if ( ! SkipStoppedInDepotCheck & & CheckTrainStoppedInDepot ( v ) < 0 ) return_cmd_error ( STR_TRAIN_MUST_BE_STOPPED ) ;
/* Check cargo */
if ( new_cid > NUM_CARGO ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
2005-01-30 21:50:06 +01:00
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_RUN ) ;
2004-08-09 19:04:08 +02:00
cost = 0 ;
num = 0 ;
2005-01-06 19:45:28 +01:00
2004-08-09 19:04:08 +02:00
do {
2004-11-23 00:05:34 +01:00
/* 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 */
2005-05-14 14:36:16 +02:00
if ( ! CanRefitTo ( v , new_cid ) ) continue ;
2005-05-26 18:31:32 +02:00
if ( v - > cargo_cap ! = 0 ) {
RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
2005-06-03 12:39:30 +02:00
uint16 amount = CALLBACK_FAILED ;
if ( HASBIT ( rvi - > callbackmask , CBM_REFIT_CAP ) ) {
/* Check the 'refit capacity' callback */
CargoID temp_cid = v - > cargo_type ;
v - > cargo_type = new_cid ;
amount = GetCallBackResult ( CBID_REFIT_CAP , v - > engine_type , v ) ;
v - > cargo_type = temp_cid ;
}
2005-06-01 13:34:37 +02:00
2005-06-03 12:39:30 +02:00
if ( amount = = CALLBACK_FAILED ) { // callback failed or not used, use default
2005-06-01 13:34:37 +02:00
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 much passengers */
amount = rvi - > capacity ;
( old_cid = = CT_PASSENGERS ) | |
( amount < < = 1 , old_cid = = CT_MAIL | | old_cid = = CT_GOODS ) | |
( amount < < = 1 , true ) ;
( new_cid = = CT_PASSENGERS ) | |
( amount > > = 1 , new_cid = = CT_MAIL | | new_cid = = CT_GOODS ) | |
( amount > > = 1 , true ) ;
} ;
if ( amount ! = 0 ) {
if ( new_cid ! = v - > cargo_type )
cost + = ( _price . build_railvehicle > > 8 ) ;
num + = amount ;
if ( flags & DC_EXEC ) {
//autorefitted train cars wants to keep the cargo
//it will be checked if the cargo is valid in CmdReplaceVehicle
if ( ! ( SkipStoppedInDepotCheck ) )
v - > cargo_count = 0 ;
v - > cargo_type = new_cid ;
v - > cargo_cap = amount ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
2004-08-09 19:04:08 +02:00
}
2004-09-10 21:02:27 +02:00
}
2005-01-02 18:23:04 +01:00
// SkipStoppedInDepotCheck is called by CmdReplace and it should only apply to the single car it is called for
} while ( ( v = v - > next ) ! = NULL | | SkipStoppedInDepotCheck ) ;
2004-08-09 19:04:08 +02:00
_returned_refit_amount = num ;
return cost ;
}
typedef struct TrainFindDepotData {
uint best_length ;
2005-06-24 14:38:35 +02:00
TileIndex tile ;
2004-08-09 19:04:08 +02:00
byte owner ;
2005-05-08 00:00:36 +02:00
/**
* true if reversing is necesarry for the train to get to this depot This
* value is unused when new depot finding and NPF are both disabled
*/
bool reverse ;
2004-08-09 19:04:08 +02:00
} TrainFindDepotData ;
2005-07-19 13:42:40 +02:00
static bool NtpCallbFindDepot ( TileIndex tile , TrainFindDepotData * tfdd , int track , uint length )
2004-08-09 19:04:08 +02:00
{
2005-06-04 13:56:32 +02:00
if ( IsTileType ( tile , MP_RAILWAY ) & & IsTileOwner ( tile , tfdd - > owner ) ) {
2005-07-13 20:04:01 +02:00
if ( ( _m [ tile ] . m5 & ~ 0x3 ) = = 0xC0 ) {
2005-07-19 13:42:40 +02:00
tfdd - > best_length = length ;
tfdd - > tile = tile ;
2004-08-09 19:04:08 +02:00
return true ;
}
}
2005-07-19 13:42:40 +02:00
return false ;
2004-08-09 19:04:08 +02:00
}
2005-05-03 22:45:23 +02:00
// returns the tile of a depot to goto to. The given vehicle must not be
// crashed!
2004-09-23 23:20:38 +02:00
static TrainFindDepotData FindClosestTrainDepot ( Vehicle * v )
2004-08-09 19:04:08 +02:00
{
int i ;
TrainFindDepotData tfdd ;
2005-06-24 14:38:35 +02:00
TileIndex tile = v - > tile ;
2004-08-09 19:04:08 +02:00
2005-05-03 22:45:23 +02:00
assert ( ! ( v - > vehstatus & VS_CRASHED ) ) ;
2004-08-09 19:04:08 +02:00
tfdd . owner = v - > owner ;
tfdd . best_length = ( uint ) - 1 ;
2005-05-08 00:00:36 +02:00
tfdd . reverse = false ;
2004-08-09 19:04:08 +02:00
2005-02-06 23:36:08 +01:00
if ( IsTileDepotType ( tile , TRANSPORT_RAIL ) ) {
2004-09-23 23:20:38 +02:00
tfdd . tile = tile ;
tfdd . best_length = 0 ;
return tfdd ;
}
if ( v - > u . rail . track = = 0x40 ) { tile = GetVehicleOutOfTunnelTile ( v ) ; }
2005-01-31 12:23:10 +01:00
if ( _patches . new_pathfinding_all ) {
NPFFoundTargetData ftd ;
2005-05-08 00:00:36 +02:00
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.
2005-06-16 20:04:02 +02:00
Trackdir trackdir = GetVehicleTrackdir ( v ) ;
Trackdir trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
2005-05-08 00:00:36 +02:00
(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.
2005-06-16 20:04:02 +02:00
assert ( trackdir ! = INVALID_TRACKDIR ) ;
2005-07-03 15:02:54 +02:00
ftd = NPFRouteToDepotBreadthFirstTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , NPF_INFINITE_PENALTY ) ;
2005-01-31 12:23:10 +01:00
if ( ftd . best_bird_dist = = 0 ) {
/* Found target */
tfdd . tile = ftd . node . tile ;
2005-04-11 14:08:09 +02:00
/* 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 . */
2005-05-08 00:00:36 +02:00
tfdd . best_length = ftd . best_path_dist / NPF_TILE_LENGTH ;
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) )
tfdd . reverse = true ;
2005-01-31 12:23:10 +01:00
}
2004-08-09 19:04:08 +02:00
} else {
// search in the forward direction first.
i = v - > direction > > 1 ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) { i = ( i - 1 ) & 3 ; }
2005-07-19 13:42:40 +02:00
NewTrainPathfind ( tile , 0 , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
2004-09-23 23:20:38 +02:00
if ( tfdd . best_length = = ( uint ) - 1 ) {
2005-05-08 00:00:36 +02:00
tfdd . reverse = true ;
2004-09-23 23:20:38 +02:00
// search in backwards direction
i = ( v - > direction ^ 4 ) > > 1 ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) { i = ( i - 1 ) & 3 ; }
2005-07-19 13:42:40 +02:00
NewTrainPathfind ( tile , 0 , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
2004-09-23 23:20:38 +02:00
}
2004-08-09 19:04:08 +02:00
}
2004-09-10 21:02:27 +02:00
2004-09-23 23:20:38 +02:00
return tfdd ;
2004-08-09 19:04:08 +02:00
}
2005-05-10 00:33:00 +02:00
/** Send a train to a depot
* @ param x , y unused
* @ param p1 train to send to the depot
* @ param p2 unused
*/
2005-05-12 02:18:30 +02:00
int32 CmdSendTrainToDepot ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 19:04:08 +02:00
{
2005-01-30 21:50:06 +01:00
Vehicle * v ;
2004-09-23 23:20:38 +02:00
TrainFindDepotData tfdd ;
2004-08-09 19:04:08 +02:00
2005-01-30 21:50:06 +01:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
2005-05-10 00:33:00 +02:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2005-01-29 13:01:36 +01:00
2005-05-10 00:33:00 +02:00
if ( v - > vehstatus & VS_CRASHED ) return CMD_ERROR ;
2005-05-03 21:31:33 +02:00
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
2004-08-09 19:04:08 +02:00
if ( flags & DC_EXEC ) {
2005-04-02 03:08:01 +02:00
if ( HASBIT ( v - > current_order . flags , OFB_PART_OF_ORDERS ) ) {
2004-08-09 19:04:08 +02:00
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
}
2004-09-10 21:02:27 +02:00
2004-12-05 13:43:04 +01:00
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
}
return 0 ;
}
2004-09-23 23:20:38 +02:00
tfdd = FindClosestTrainDepot ( v ) ;
if ( tfdd . best_length = = ( uint ) - 1 )
2004-08-09 19:04:08 +02:00
return_cmd_error ( STR_883A_UNABLE_TO_FIND_ROUTE_TO ) ;
if ( flags & DC_EXEC ) {
2004-09-23 23:20:38 +02:00
v - > dest_tile = tfdd . tile ;
2004-12-05 13:43:04 +01:00
v - > current_order . type = OT_GOTO_DEPOT ;
2005-03-19 22:16:22 +01:00
v - > current_order . flags = OF_NON_STOP | OF_FULL_LOAD ;
2005-02-06 11:18:47 +01:00
v - > current_order . station = GetDepotByTile ( tfdd . tile ) - > index ;
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2005-05-08 00:00:36 +02:00
/* If there is no depot in front, reverse automatically */
if ( tfdd . reverse )
DoCommandByTile ( v - > tile , v - > index , 0 , DC_EXEC , CMD_REVERSE_TRAIN_DIRECTION ) ;
2004-08-09 19:04:08 +02:00
}
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
return 0 ;
}
2005-05-11 02:00:27 +02:00
/** Change the service interval for trains.
* @ param x , y unused
* @ param p1 vehicle ID that is being service - interval - changed
2005-05-11 18:17:03 +02:00
* @ param p2 new service interval
2004-08-09 19:04:08 +02:00
*/
int32 CmdChangeTrainServiceInt ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
2005-01-30 21:50:06 +01:00
Vehicle * v ;
2005-05-11 18:17:03 +02:00
uint16 serv_int = GetServiceIntervalClamped ( p2 ) ; /* Double check the service interval from the user-input */
2005-01-30 21:50:06 +01:00
2005-05-11 18:17:03 +02:00
if ( serv_int ! = p2 | | ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
2005-01-30 21:50:06 +01:00
v = GetVehicle ( p1 ) ;
2005-05-11 02:00:27 +02:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 19:04:08 +02:00
if ( flags & DC_EXEC ) {
2005-05-11 18:17:03 +02:00
v - > service_interval = serv_int ;
2004-08-09 19:04:08 +02:00
InvalidateWindowWidget ( WC_VEHICLE_DETAILS , v - > index , 8 ) ;
}
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
return 0 ;
}
2005-01-22 21:23:18 +01:00
void OnTick_Train ( void )
2004-08-09 19:04:08 +02:00
{
_age_cargo_skip_counter = ( _age_cargo_skip_counter = = 0 ) ? 184 : ( _age_cargo_skip_counter - 1 ) ;
}
static const int8 _vehicle_smoke_pos [ 16 ] = {
- 4 , - 4 , - 4 , 0 , 4 , 4 , 4 , 0 ,
- 4 , 0 , 4 , 4 , 4 , 0 , - 4 , - 4 ,
} ;
static void HandleLocomotiveSmokeCloud ( Vehicle * v )
{
Vehicle * u ;
if ( v - > vehstatus & VS_TRAIN_SLOWING | | v - > load_unload_time_rem ! = 0 | | v - > cur_speed < 2 )
return ;
u = v ;
do {
int engtype = v - > engine_type ;
// no smoke?
2005-06-07 20:13:49 +02:00
if ( RailVehInfo ( engtype ) - > flags & 2 | |
GetEngine ( engtype ) - > railtype > 0 | |
( v - > vehstatus & VS_HIDDEN ) | | ( v - > u . rail . track & 0xC0 ) )
2004-08-09 19:04:08 +02:00
continue ;
2004-12-04 08:41:37 +01:00
switch ( RailVehInfo ( engtype ) - > engclass ) {
2004-08-09 19:04:08 +02:00
case 0 :
// steam smoke.
2005-02-06 23:36:08 +01:00
if ( ( v - > tick_counter & 0xF ) = = 0 & & ! IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) & & ! IsTunnelTile ( v - > tile ) ) {
2004-09-10 21:02:27 +02:00
CreateEffectVehicleRel ( v ,
2004-08-09 19:04:08 +02:00
( _vehicle_smoke_pos [ v - > direction ] ) ,
( _vehicle_smoke_pos [ v - > direction + 8 ] ) ,
10 ,
EV_STEAM_SMOKE ) ;
}
break ;
case 1 :
// diesel smoke
2005-02-06 23:36:08 +01:00
if ( u - > cur_speed < = 40 & & ! IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) & & ! IsTunnelTile ( v - > tile ) & & ( uint16 ) Random ( ) < = 0x1E00 ) {
2005-02-12 16:53:32 +01:00
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_DIESEL_SMOKE ) ;
2004-08-09 19:04:08 +02:00
}
break ;
case 2 :
// blue spark
2005-02-06 23:36:08 +01:00
if ( ( v - > tick_counter & 0x3 ) = = 0 & & ! IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) & & ! IsTunnelTile ( v - > tile ) & & ( uint16 ) Random ( ) < = 0x5B0 ) {
2005-02-12 16:53:32 +01:00
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_ELECTRIC_SPARK ) ;
2004-08-09 19:04:08 +02:00
}
break ;
}
} while ( ( v = v - > next ) ! = NULL ) ;
}
static void TrainPlayLeaveStationSound ( Vehicle * v )
{
2004-12-04 10:26:39 +01:00
static const SoundFx sfx [ ] = {
SND_04_TRAIN ,
SND_0A_TRAIN_HORN ,
SND_0A_TRAIN_HORN
} ;
2004-08-09 19:04:08 +02:00
int engtype = v - > engine_type ;
2005-06-07 20:13:49 +02:00
switch ( GetEngine ( engtype ) - > railtype ) {
2004-11-06 00:12:33 +01:00
case 0 :
2004-12-04 08:41:37 +01:00
SndPlayVehicleFx ( sfx [ RailVehInfo ( engtype ) - > engclass ] , v ) ;
2004-11-06 00:12:33 +01:00
break ;
case 1 :
SndPlayVehicleFx ( SND_47_MAGLEV_2 , v ) ;
break ;
case 2 :
SndPlayVehicleFx ( SND_41_MAGLEV , v ) ;
break ;
2004-08-09 19:04:08 +02:00
}
}
static bool CheckTrainStayInDepot ( Vehicle * v )
{
Vehicle * u ;
2004-11-23 21:25:18 +01:00
// bail out if not all wagons are in the same depot or not in a depot at all
2004-12-21 17:00:14 +01:00
for ( u = v ; u ! = NULL ; u = u - > next )
2004-11-23 21:25:18 +01:00
if ( u - > u . rail . track ! = 0x80 | | u - > tile ! = v - > tile )
return false ;
2004-08-09 19:04:08 +02:00
if ( v - > u . rail . force_proceed = = 0 ) {
2005-07-22 10:40:19 +02:00
byte trackdir = GetVehicleTrackdir ( v ) ;
2005-01-24 23:24:47 +01:00
if ( + + v - > load_unload_time_rem < 37 ) {
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 19:04:08 +02:00
return true ;
2005-01-24 23:24:47 +01:00
}
2004-08-09 19:04:08 +02:00
v - > load_unload_time_rem = 0 ;
2005-07-22 10:40:19 +02:00
if ( PBSIsPbsSegment ( v - > tile , trackdir ) ) {
2005-07-04 16:58:55 +02:00
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
if ( PBSTileUnavail ( v - > tile ) & ( 1 < < trackdir ) )
return true ;
NPFFillWithOrderData ( & fstd , v ) ;
DEBUG ( pbs , 2 ) ( " pbs: (%i) choose depot (DP), tile:%x, trackdir:%i " , v - > unitnumber , v - > tile , trackdir ) ;
ftd = NPFRouteToStationOrTile ( v - > tile , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_GREEN ) ;
// we found a way out of the pbs block
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_EXIT ) ) {
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_BLOCKED ) | | NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_RED ) )
return true ;
2005-07-17 22:09:02 +02:00
else {
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 16:58:55 +02:00
goto green ;
2005-07-17 22:09:02 +02:00
}
2005-07-04 16:58:55 +02:00
}
}
2005-01-24 23:24:47 +01:00
if ( UpdateSignalsOnSegment ( v - > tile , v - > direction ) ) {
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 19:04:08 +02:00
return true ;
2005-01-24 23:24:47 +01:00
}
2004-08-09 19:04:08 +02:00
}
2005-07-04 16:58:55 +02:00
green :
2004-12-09 22:46:56 +01:00
VehicleServiceInDepot ( v ) ;
2005-01-24 23:24:47 +01:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 19:04:08 +02:00
TrainPlayLeaveStationSound ( v ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
v - > u . rail . track = 1 ;
if ( v - > direction & 2 )
v - > u . rail . track = 2 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
v - > vehstatus & = ~ VS_HIDDEN ;
v - > cur_speed = 0 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
VehiclePositionChanged ( v ) ;
UpdateSignalsOnSegment ( v - > tile , v - > direction ) ;
UpdateTrainAcceleration ( v ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
return false ;
}
2005-01-31 12:23:10 +01:00
/* Check for station tiles */
2004-08-09 19:04:08 +02:00
typedef struct TrainTrackFollowerData {
TileIndex dest_coords ;
2005-03-25 11:40:58 +01:00
StationID station_index ; // station index we're heading for
2004-08-09 19:04:08 +02:00
uint best_bird_dist ;
uint best_track_dist ;
byte best_track ;
} TrainTrackFollowerData ;
2005-07-19 13:42:40 +02:00
static bool NtpCallbFindStation ( TileIndex tile , TrainTrackFollowerData * ttfd , int track , uint length )
2005-06-24 14:38:35 +02:00
{
2004-08-09 19:04:08 +02:00
// heading for nowhere?
if ( ttfd - > dest_coords = = 0 )
return false ;
// did we reach the final station?
2005-07-12 22:28:19 +02:00
if ( ( ttfd - > station_index = = INVALID_STATION & & tile = = ttfd - > dest_coords ) | |
2005-07-13 20:04:01 +02:00
( IsTileType ( tile , MP_STATION ) & & IS_BYTE_INSIDE ( _m [ tile ] . m5 , 0 , 8 ) & & _m [ tile ] . m2 = = ttfd - > station_index ) ) {
2005-07-12 22:28:19 +02:00
/* 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 */
2004-08-09 19:04:08 +02:00
// found station
2005-07-19 13:42:40 +02:00
ttfd - > best_track = track ;
2004-08-09 19:04:08 +02:00
return true ;
} else {
uint dist ;
2005-07-19 13:42:40 +02:00
// didn't find station, keep track of the best path so far.
2005-01-31 08:23:15 +01:00
dist = DistanceManhattan ( tile , ttfd - > dest_coords ) ;
2004-08-09 19:04:08 +02:00
if ( dist < ttfd - > best_bird_dist ) {
ttfd - > best_bird_dist = dist ;
2005-07-19 13:42:40 +02:00
ttfd - > best_track = track ;
2004-08-09 19:04:08 +02:00
}
return false ;
}
}
static void FillWithStationData ( TrainTrackFollowerData * fd , Vehicle * v )
{
2004-08-22 01:56:01 +02:00
fd - > dest_coords = v - > dest_tile ;
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_STATION )
fd - > station_index = v - > current_order . station ;
2004-08-22 01:56:01 +02:00
else
2005-03-25 11:40:58 +01:00
fd - > station_index = INVALID_STATION ;
2004-08-09 19:04:08 +02:00
}
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 } ;
2005-01-31 12:23:10 +01:00
# if PF_BENCHMARK
2005-07-12 22:28:19 +02:00
# if !defined(_MSC_VER)
2005-01-31 12:23:10 +01:00
unsigned int rdtsc ( )
{
unsigned int high , low ;
__asm__ __volatile__ ( " rdtsc " : " =a " ( low ) , " =d " ( high ) ) ;
return low ;
}
2005-07-12 22:28:19 +02:00
# else
static unsigned int _declspec ( naked ) rdtsc ( void )
{
_asm {
rdtsc
ret
}
}
# endif
2005-01-31 12:23:10 +01:00
# endif
2004-08-09 19:04:08 +02:00
2005-07-12 22:28:19 +02:00
2004-08-09 19:04:08 +02:00
/* choose a track */
2005-06-24 14:38:35 +02:00
static byte ChooseTrainTrack ( Vehicle * v , TileIndex tile , int enterdir , TrackdirBits trackdirbits )
2004-09-10 21:02:27 +02:00
{
2004-08-09 19:04:08 +02:00
TrainTrackFollowerData fd ;
uint best_track ;
2005-01-31 12:23:10 +01:00
# if PF_BENCHMARK
2004-08-09 19:04:08 +02:00
int time = rdtsc ( ) ;
static float f ;
# endif
(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.
2005-06-16 20:04:02 +02:00
assert ( ( trackdirbits & ~ 0x3F ) = = 0 ) ;
2004-08-09 19:04:08 +02:00
2005-01-31 12:23:10 +01:00
/* quick return in case only one possible track is available */
(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.
2005-06-16 20:04:02 +02:00
if ( KILL_FIRST_BIT ( trackdirbits ) = = 0 )
return FIND_FIRST_BIT ( trackdirbits ) ;
2004-08-09 19:04:08 +02:00
2005-01-31 12:23:10 +01:00
if ( _patches . new_pathfinding_all ) { /* Use a new pathfinding for everything */
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.
2005-06-16 20:04:02 +02:00
Trackdir trackdir ;
2005-07-04 16:58:55 +02:00
uint16 pbs_tracks ;
2005-01-31 12:23:10 +01:00
NPFFillWithOrderData ( & fstd , v ) ;
/* The enterdir for the new tile, is the exitdir for the old tile */
2005-05-03 01:59:11 +02:00
trackdir = GetVehicleTrackdir ( v ) ;
2005-01-31 12:23:10 +01:00
assert ( trackdir ! = 0xff ) ;
2005-07-04 16:58:55 +02:00
pbs_tracks = PBSTileReserved ( tile ) ;
pbs_tracks | = pbs_tracks < < 8 ;
pbs_tracks & = TrackdirReachesTrackdirs ( trackdir ) ;
if ( pbs_tracks | | ( v - > u . rail . pbs_status = = PBS_STAT_NEED_PATH ) ) {
DEBUG ( pbs , 2 ) ( " pbs: (%i) choosefromblock, tile_org:%x tile_dst:%x trackdir:%i pbs_tracks:%i " , v - > unitnumber , tile , tile - TileOffsByDir ( enterdir ) , trackdir , pbs_tracks ) ;
// clear the currently planned path
2005-07-17 22:09:02 +02:00
if ( v - > u . rail . pbs_status ! = PBS_STAT_NEED_PATH ) PBSClearPath ( tile , FindFirstBit2x64 ( pbs_tracks ) , v - > u . rail . pbs_end_tile , v - > u . rail . pbs_end_trackdir ) ;
2005-07-04 16:58:55 +02:00
// try to find a route to a green exit signal
ftd = NPFRouteToStationOrTile ( tile - TileOffsByDir ( enterdir ) , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_ANY ) ;
2005-07-17 22:09:02 +02:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 16:58:55 +02:00
} else
ftd = NPFRouteToStationOrTile ( tile - TileOffsByDir ( enterdir ) , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_NONE ) ;
2005-04-15 15:48:08 +02:00
if ( ftd . best_trackdir = = 0xff ) {
/* We are already at our target. Just do something */
2005-01-31 12:23:10 +01:00
//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.
2005-06-16 20:04:02 +02:00
best_track = FIND_FIRST_BIT ( trackdirbits ) ;
2004-08-09 19:04:08 +02:00
} else {
2005-04-15 15:48:08 +02:00
/* 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 . */
2005-01-31 12:23:10 +01:00
/* Discard enterdir information, making it a normal track */
2005-07-04 16:58:55 +02:00
best_track = TrackdirToTrack ( ftd . best_trackdir ) ;
2004-08-09 19:04:08 +02:00
}
} else {
2005-01-31 12:23:10 +01:00
FillWithStationData ( & fd , v ) ;
2004-08-09 19:04:08 +02:00
2005-07-12 22:28:19 +02:00
/* New train pathfinding */
fd . best_bird_dist = ( uint ) - 1 ;
fd . best_track_dist = ( uint ) - 1 ;
fd . best_track = 0xFF ;
2004-09-10 21:02:27 +02:00
2005-07-19 13:42:40 +02:00
NewTrainPathfind ( tile - TileOffsByDir ( enterdir ) , v - > dest_tile ,
enterdir , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
2005-01-31 12:23:10 +01:00
2005-07-12 22:28:19 +02:00
if ( fd . best_track = = 0xff ) {
// blaha
best_track = FIND_FIRST_BIT ( trackdirbits ) ;
2005-01-31 12:23:10 +01:00
} else {
2005-07-12 22:28:19 +02:00
best_track = fd . best_track & 7 ;
2005-01-31 12:23:10 +01:00
}
2004-08-09 19:04:08 +02:00
}
2005-01-31 12:23:10 +01:00
# if PF_BENCHMARK
2004-08-09 19:04:08 +02:00
time = rdtsc ( ) - time ;
f = f * 0.99 + 0.01 * time ;
printf ( " PF time = %d %f \n " , time , f ) ;
# 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 ) ] [ v - > direction > > 1 ] ;
2005-01-31 12:23:10 +01:00
if ( _patches . new_pathfinding_all ) { /* Use a new pathfinding for everything */
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
byte trackdir , trackdir_rev ;
Vehicle * last = GetLastVehicleInChain ( v ) ;
2004-08-09 19:04:08 +02:00
2005-01-31 12:23:10 +01:00
NPFFillWithOrderData ( & fstd , v ) ;
2004-08-09 19:04:08 +02:00
2005-05-03 01:59:11 +02:00
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.
2005-06-16 20:04:02 +02:00
trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
2005-01-31 12:23:10 +01:00
assert ( trackdir ! = 0xff ) ;
assert ( trackdir_rev ! = 0xff ) ;
2005-07-04 16:58:55 +02:00
ftd = NPFRouteToStationOrTileTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_NONE ) ;
2005-01-31 12:23:10 +01:00
if ( ftd . best_bird_dist ! = 0 ) {
/* We didn't find anything, just keep on going straight ahead */
reverse_best = false ;
} else {
2005-03-08 21:13:21 +01:00
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) )
2005-01-31 12:23:10 +01:00
reverse_best = true ;
else
reverse_best = false ;
}
} else {
while ( true ) {
fd . best_bird_dist = ( uint ) - 1 ;
fd . best_track_dist = ( uint ) - 1 ;
2005-07-19 13:42:40 +02:00
NewTrainPathfind ( v - > tile , v - > dest_tile , reverse ^ i , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
2005-01-31 12:23:10 +01:00
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 ;
}
2004-08-09 19:04:08 +02:00
} else {
2005-01-31 12:23:10 +01:00
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 ;
}
2004-08-09 19:04:08 +02:00
}
2004-09-10 21:02:27 +02:00
2005-01-31 12:23:10 +01:00
/* if we reach this position, there's two paths of equal value so far.
* pick one randomly . */
2005-07-21 08:31:02 +02:00
r = GB ( Random ( ) , 0 , 8 ) ;
2005-01-31 12:23:10 +01:00
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 ;
}
2004-08-09 19:04:08 +02:00
good : ;
2005-01-31 12:23:10 +01:00
best_track = i ;
best_bird_dist = fd . best_bird_dist ;
best_track_dist = fd . best_track_dist ;
reverse_best = reverse ;
2004-08-09 19:04:08 +02:00
bad : ;
2005-01-31 12:23:10 +01:00
if ( reverse ! = 0 )
break ;
reverse = 2 ;
}
2004-08-09 19:04:08 +02:00
}
return reverse_best ! = 0 ;
}
static bool ProcessTrainOrder ( Vehicle * v )
{
2005-01-15 20:06:22 +01:00
const Order * order ;
2004-08-09 19:04:08 +02:00
bool result ;
// These are un-interruptible
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type > = OT_GOTO_DEPOT & &
v - > current_order . type < = OT_LEAVESTATION ) {
2005-01-15 20:06:22 +01:00
// Let a depot order in the orderlist interrupt.
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type ! = OT_GOTO_DEPOT | |
! ( v - > current_order . flags & OF_UNLOAD ) )
2004-08-09 19:04:08 +02:00
return false ;
}
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
( v - > current_order . flags & ( OF_UNLOAD | OF_FULL_LOAD ) ) = = ( OF_UNLOAD | OF_FULL_LOAD ) & &
2005-03-19 22:16:22 +01:00
! VehicleNeedsService ( v ) ) {
2004-08-09 19:04:08 +02:00
v - > cur_order_index + + ;
}
2004-11-14 14:07:07 +01:00
// check if we've reached the waypoint?
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_WAYPOINT & & v - > tile = = v - > dest_tile ) {
2004-08-09 19:04:08 +02:00
v - > cur_order_index + + ;
}
2004-09-10 21:02:27 +02:00
// check if we've reached a non-stop station while TTDPatch nonstop is enabled..
2004-12-05 13:43:04 +01:00
if ( _patches . new_nonstop & & v - > current_order . flags & OF_NON_STOP & &
2005-07-13 20:04:01 +02:00
v - > current_order . station = = _m [ v - > tile ] . m2 & & IsTileType ( v - > tile , MP_STATION ) ) {
2004-09-10 21:02:27 +02:00
v - > cur_order_index + + ;
2004-08-09 19:04:08 +02:00
}
// Get the current order
if ( v - > cur_order_index > = v - > num_orders )
v - > cur_order_index = 0 ;
2005-01-15 20:06:22 +01:00
order = GetVehicleOrder ( v , v - > cur_order_index ) ;
2004-08-09 19:04:08 +02:00
// If no order, do nothing.
2005-01-15 20:06:22 +01:00
if ( order = = NULL ) {
2004-12-05 13:43:04 +01:00
v - > current_order . type = OT_NOTHING ;
v - > current_order . flags = 0 ;
2004-08-09 19:04:08 +02:00
v - > dest_tile = 0 ;
return false ;
}
// If it is unchanged, keep it.
2005-01-15 20:06:22 +01:00
if ( order - > type = = v - > current_order . type & &
order - > flags = = v - > current_order . flags & &
order - > station = = v - > current_order . station )
2004-08-09 19:04:08 +02:00
return false ;
// Otherwise set it, and determine the destination tile.
2005-01-15 20:06:22 +01:00
v - > current_order = * order ;
2004-08-09 19:04:08 +02:00
v - > dest_tile = 0 ;
result = false ;
2005-01-15 20:06:22 +01:00
switch ( order - > type ) {
case OT_GOTO_STATION :
if ( order - > station = = v - > last_station_visited )
2005-02-02 17:16:43 +01:00
v - > last_station_visited = INVALID_STATION ;
2005-01-15 20:06:22 +01:00
v - > dest_tile = GetStation ( order - > station ) - > xy ;
result = CheckReverseTrain ( v ) ;
break ;
case OT_GOTO_DEPOT :
2005-02-06 11:18:47 +01:00
v - > dest_tile = GetDepot ( order - > station ) - > xy ;
2005-01-15 20:06:22 +01:00
result = CheckReverseTrain ( v ) ;
break ;
case OT_GOTO_WAYPOINT :
2005-03-24 18:03:37 +01:00
v - > dest_tile = GetWaypoint ( order - > station ) - > xy ;
2005-01-15 20:06:22 +01:00
result = CheckReverseTrain ( v ) ;
break ;
2004-08-09 19:04:08 +02:00
}
2005-01-15 20:06:22 +01:00
InvalidateVehicleOrder ( v ) ;
2004-08-09 19:04:08 +02:00
return result ;
}
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 )
{
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_NOTHING )
2004-08-09 19:04:08 +02:00
return ;
2004-09-10 21:02:27 +02:00
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type ! = OT_DUMMY ) {
if ( v - > current_order . type ! = OT_LOADING )
2004-08-09 19:04:08 +02:00
return ;
if ( mode )
return ;
// don't mark the train as lost if we're loading on the final station.
2004-12-05 13:43:04 +01:00
if ( v - > current_order . flags & OF_NON_STOP )
2004-08-09 19:04:08 +02:00
v - > u . rail . days_since_order_progr = 0 ;
if ( - - v - > load_unload_time_rem )
return ;
2004-12-05 13:43:04 +01:00
if ( v - > current_order . flags & OF_FULL_LOAD & & CanFillVehicle ( v ) ) {
2005-01-04 01:54:12 +01:00
v - > u . rail . days_since_order_progr = 0 ; /* Prevent a train lost message for full loading trains */
2004-08-09 19:04:08 +02:00
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_INC ) ;
if ( LoadUnloadVehicle ( v ) ) {
InvalidateWindow ( WC_TRAINS_LIST , v - > owner ) ;
MarkTrainDirty ( v ) ;
2004-09-10 21:02:27 +02:00
2005-06-05 17:37:00 +02:00
// need to update acceleration and cached values since the goods on the train changed.
TrainCargoChanged ( v ) ;
2004-08-09 19:04:08 +02:00
UpdateTrainAcceleration ( v ) ;
}
return ;
}
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
TrainPlayLeaveStationSound ( v ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
{
2004-12-05 13:43:04 +01:00
Order b = v - > current_order ;
v - > current_order . type = OT_LEAVESTATION ;
v - > current_order . flags = 0 ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// If this was not the final order, don't remove it from the list.
2004-12-05 13:43:04 +01:00
if ( ! ( b . flags & OF_NON_STOP ) )
2004-08-09 19:04:08 +02:00
return ;
}
}
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
2005-01-15 20:06:22 +01:00
InvalidateVehicleOrder ( v ) ;
2004-08-09 19:04:08 +02:00
}
static int UpdateTrainSpeed ( Vehicle * v )
{
uint spd ;
uint accel ;
2005-01-09 17:02:06 +01:00
if ( v - > vehstatus & VS_STOPPED | | HASBIT ( v - > u . rail . flags , VRF_REVERSING ) ) {
2005-01-26 13:51:04 +01:00
if ( _patches . realistic_acceleration )
accel = GetTrainAcceleration ( v , AM_BRAKE ) * 2 ;
else
accel = v - > acceleration * - 2 ;
2004-08-09 19:04:08 +02:00
} else {
2005-01-26 13:51:04 +01:00
if ( _patches . realistic_acceleration )
accel = GetTrainAcceleration ( v , AM_ACCEL ) ;
else
accel = v - > acceleration ;
2004-08-09 19:04:08 +02:00
}
spd = v - > subspeed + accel * 2 ;
v - > subspeed = ( byte ) spd ;
2005-01-26 13:51:04 +01:00
{
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 ) ;
}
2004-08-09 19:04:08 +02:00
if ( ! ( v - > direction & 1 ) ) spd = spd * 3 > > 2 ;
spd + = v - > progress ;
v - > progress = ( byte ) spd ;
return ( spd > > 8 ) ;
}
2005-03-25 11:40:58 +01:00
static void TrainEnterStation ( Vehicle * v , StationID station )
2004-08-09 19:04:08 +02:00
{
Station * st ;
uint32 flags ;
v - > last_station_visited = station ;
/* check if a train ever visited this station before */
2005-01-06 23:31:58 +01:00
st = GetStation ( station ) ;
2004-08-09 19:04:08 +02:00
if ( ! ( st - > had_vehicle_of_type & HVOT_TRAIN ) ) {
st - > had_vehicle_of_type | = HVOT_TRAIN ;
2004-12-02 23:53:07 +01:00
SetDParam ( 0 , st - > index ) ;
2004-08-09 19:04:08 +02:00
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?
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_STATION & &
2005-03-25 12:03:05 +01:00
v - > current_order . station = = station ) {
2004-08-09 19:04:08 +02:00
// Yeah, keep the load/unload flags
// Non Stop now means if the order should be increased.
2004-12-05 13:43:04 +01:00
v - > current_order . type = OT_LOADING ;
2005-06-15 18:58:15 +02:00
v - > current_order . flags & = OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER ;
2004-12-05 13:43:04 +01:00
v - > current_order . flags | = OF_NON_STOP ;
2004-08-09 19:04:08 +02:00
} else {
// No, just do a simple load
2004-12-05 13:43:04 +01:00
v - > current_order . type = OT_LOADING ;
v - > current_order . flags = 0 ;
2004-08-09 19:04:08 +02:00
}
2004-12-05 13:43:04 +01:00
v - > current_order . station = 0 ;
2004-08-09 19:04:08 +02:00
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_INC ) ;
if ( LoadUnloadVehicle ( v ) ! = 0 ) {
InvalidateWindow ( WC_TRAINS_LIST , v - > owner ) ;
MarkTrainDirty ( v ) ;
2005-06-05 17:37:00 +02:00
TrainCargoChanged ( v ) ;
2004-08-09 19:04:08 +02:00
UpdateTrainAcceleration ( v ) ;
}
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
}
2005-01-09 17:02:06 +01:00
static byte AfterSetTrainPos ( Vehicle * v , bool new_tile )
2004-08-09 19:04:08 +02:00
{
byte new_z , old_z ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// 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 ;
2005-01-09 17:02:06 +01:00
if ( new_tile ) {
CLRBIT ( v - > u . rail . flags , VRF_GOINGUP ) ;
CLRBIT ( v - > u . rail . flags , VRF_GOINGDOWN ) ;
2004-08-09 19:04:08 +02:00
2005-01-09 17:02:06 +01:00
if ( new_z ! = old_z ) {
2005-06-25 08:15:43 +02:00
TileIndex tile = TileVirtXY ( v - > x_pos , v - > y_pos ) ;
2005-04-12 11:17:51 +02:00
// XXX workaround, whole UP/DOWN detection needs overhaul
2005-07-13 20:04:01 +02:00
if ( ! IsTileType ( tile , MP_TUNNELBRIDGE ) | | ( _m [ tile ] . m5 & 0x80 ) ! = 0 )
2005-04-12 11:17:51 +02:00
SETBIT ( v - > u . rail . flags , ( new_z > old_z ) ? VRF_GOINGUP : VRF_GOINGDOWN ) ;
2005-01-09 17:02:06 +01:00
}
2004-08-09 19:04:08 +02:00
}
VehiclePositionChanged ( v ) ;
EndVehicleMove ( v ) ;
return old_z ;
}
static const byte _new_vehicle_direction_table [ 11 ] = {
0 , 7 , 6 , 0 ,
1 , 0 , 5 , 0 ,
2 , 3 , 4 ,
} ;
2005-06-24 14:38:35 +02:00
static int GetNewVehicleDirectionByTile ( TileIndex new_tile , TileIndex old_tile )
2004-08-09 19:04:08 +02:00
{
2005-01-07 18:02:43 +01:00
uint offs = ( TileY ( new_tile ) - TileY ( old_tile ) + 1 ) * 4 +
TileX ( new_tile ) - TileX ( old_tile ) + 1 ;
2004-08-09 19:04:08 +02:00
assert ( offs < 11 ) ;
return _new_vehicle_direction_table [ offs ] ;
}
static int GetNewVehicleDirection ( 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 ( 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 */
2005-01-17 10:41:46 +01:00
static bool CheckCompatibleRail ( const Vehicle * v , TileIndex tile )
2004-08-09 19:04:08 +02:00
{
2005-01-29 16:12:40 +01:00
switch ( GetTileType ( tile ) ) {
2005-01-17 10:41:46 +01:00
case MP_RAILWAY :
case MP_STATION :
// normal tracks, jump to owner check
break ;
2004-09-21 23:28:42 +02:00
2005-01-17 10:41:46 +01:00
case MP_TUNNELBRIDGE :
2005-07-13 20:04:01 +02:00
if ( ( _m [ tile ] . m5 & 0xC0 ) = = 0xC0 ) { // is bridge middle part?
2005-01-27 13:52:20 +01:00
uint height ;
uint tileh = GetTileSlope ( tile , & height ) ;
2004-08-09 19:04:08 +02:00
2005-01-17 10:41:46 +01:00
// correct Z position of a train going under a bridge on slopes
2005-02-22 13:48:03 +01:00
if ( CorrectZ ( tileh ) ) height + = 8 ;
2005-01-17 10:41:46 +01:00
2005-01-27 13:52:20 +01:00
if ( v - > z_pos ! = height ) return true ; // train is going over bridge
2005-01-17 10:41:46 +01:00
}
break ;
case MP_STREET :
2005-07-13 20:04:01 +02:00
// tracks over roads, do owner check of tracks (_m[tile].owner)
2005-01-17 10:41:46 +01:00
return
2005-06-04 13:56:32 +02:00
IsTileOwner ( tile , v - > owner ) & &
2005-07-13 20:04:01 +02:00
( v - > subtype ! = TS_Front_Engine | | ( _m [ tile ] . m4 & 0xF ) = = v - > u . rail . railtype ) ;
2005-01-17 10:41:46 +01:00
default :
return true ;
}
2004-09-10 21:02:27 +02:00
2005-01-17 10:41:46 +01:00
return
2005-06-04 13:56:32 +02:00
IsTileOwner ( tile , v - > owner ) & &
2005-07-10 03:25:17 +02:00
( v - > subtype ! = TS_Front_Engine | |
IsCompatibleRail ( v - > u . rail . railtype , GetRailType ( tile ) ) ) ;
2004-08-09 19:04:08 +02:00
}
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 [ 3 ] = {
// normal accel
{ 256 / 4 , 256 / 2 , 256 / 4 , 2 } , // normal
{ 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 , byte new_dir )
{
byte diff ;
const RailtypeSlowdownParams * rsp ;
if ( _patches . realistic_acceleration | | ( diff = ( v - > direction - new_dir ) & 7 ) = = 0 )
return ;
rsp = & _railtype_slowdown [ v - > u . rail . railtype ] ;
v - > cur_speed - = ( ( diff = = 1 | | diff = = 7 ) ? 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 byte _otherside_signal_directions [ 14 ] = {
1 , 3 , 1 , 3 , 5 , 3 , 0 , 0 ,
5 , 7 , 7 , 5 , 7 , 1 ,
} ;
2005-06-24 14:38:35 +02:00
static void TrainMovedChangeSignals ( TileIndex tile , int dir )
2004-08-09 19:04:08 +02:00
{
int i ;
2005-07-13 20:04:01 +02:00
if ( IsTileType ( tile , MP_RAILWAY ) & & ( _m [ tile ] . m5 & 0xC0 ) = = 0x40 ) {
i = FindFirstBit2x64 ( ( _m [ tile ] . m5 + ( _m [ tile ] . m5 < < 8 ) ) & _reachable_tracks [ dir ] ) ;
2004-08-09 19:04:08 +02:00
UpdateSignalsOnSegment ( tile , _otherside_signal_directions [ i ] ) ;
}
}
typedef struct TrainCollideChecker {
2005-03-06 13:54:19 +01:00
const Vehicle * v ;
const Vehicle * v_skip ;
2004-08-09 19:04:08 +02:00
} TrainCollideChecker ;
2005-03-06 13:54:19 +01:00
static void * FindTrainCollideEnum ( Vehicle * v , void * data )
2004-08-09 19:04:08 +02:00
{
2005-03-06 13:54:19 +01:00
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 ;
}
2004-08-09 19:04:08 +02:00
}
static void SetVehicleCrashed ( Vehicle * v )
{
Vehicle * u ;
if ( v - > u . rail . crash_anim_pos ! = 0 )
return ;
v - > u . rail . crash_anim_pos + + ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
u = v ;
BEGIN_ENUM_WAGONS ( v )
v - > vehstatus | = VS_CRASHED ;
END_ENUM_WAGONS ( v )
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , u - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
}
2005-03-06 13:54:19 +01:00
static int CountPassengersInTrain ( const Vehicle * v )
2004-08-09 19:04:08 +02:00
{
2004-09-10 21:02:27 +02:00
int num = 0 ;
2004-08-09 19:04:08 +02:00
BEGIN_ENUM_WAGONS ( v )
2005-01-19 20:15:03 +01:00
if ( v - > cargo_type = = CT_PASSENGERS ) num + = v - > cargo_count ;
2004-08-09 19:04:08 +02:00
END_ENUM_WAGONS ( v )
return num ;
}
2004-08-12 00:07:08 +02:00
/*
2005-03-06 13:54:19 +01:00
* Checks whether the specified train has a collision with another vehicle . If
2005-01-19 20:15:03 +01:00
* so , destroys this vehicle , and the other vehicle if its subtype is 0 ( TS_Front_Engine ) .
2004-08-12 00:07:08 +02:00
* Reports the incident in a flashy news item , modifies station ratings and
* plays a sound .
*/
2004-08-09 19:04:08 +02:00
static void CheckTrainCollision ( Vehicle * v )
{
TrainCollideChecker tcc ;
2005-03-06 13:54:19 +01:00
Vehicle * coll ;
Vehicle * realcoll ;
2004-08-09 19:04:08 +02:00
int num ;
/* can't collide in depot */
if ( v - > u . rail . track = = 0x80 )
return ;
2004-09-10 21:02:27 +02:00
2005-06-25 08:15:43 +02:00
assert ( v - > u . rail . track = = 0x40 | | TileVirtXY ( v - > x_pos , v - > y_pos ) = = v - > tile ) ;
2004-08-09 19:04:08 +02:00
tcc . v = v ;
tcc . v_skip = v - > next ;
/* find colliding vehicle */
2005-06-25 08:15:43 +02:00
realcoll = VehicleFromPos ( TileVirtXY ( v - > x_pos , v - > y_pos ) , & tcc , FindTrainCollideEnum ) ;
2005-03-06 13:54:19 +01:00
if ( realcoll = = NULL )
2004-08-09 19:04:08 +02:00
return ;
2004-09-10 21:02:27 +02:00
2005-03-06 13:54:19 +01:00
coll = GetFirstVehicleInChain ( realcoll ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* it can't collide with its own wagons */
2005-03-06 13:54:19 +01:00
if ( v = = coll | |
( v - > u . rail . track & 0x40 & & ( v - > direction & 2 ) ! = ( realcoll - > direction & 2 ) ) )
2004-08-09 19:04:08 +02:00
return ;
2004-09-10 21:02:27 +02:00
//two drivers + passangers killed in train v
2004-08-09 19:04:08 +02:00
num = 2 + CountPassengersInTrain ( v ) ;
2005-03-06 13:54:19 +01:00
if ( ! ( coll - > vehstatus & VS_CRASHED ) )
2004-08-09 19:04:08 +02:00
//two drivers + passangers killed in train coll (if it was not crashed already)
num + = 2 + CountPassengersInTrain ( coll ) ;
SetVehicleCrashed ( v ) ;
2005-01-19 20:15:03 +01:00
if ( coll - > subtype = = TS_Front_Engine )
2004-08-09 19:04:08 +02:00
SetVehicleCrashed ( coll ) ;
2004-09-10 21:02:27 +02:00
2004-12-02 23:53:07 +01:00
SetDParam ( 0 , num ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
AddNewsItem ( STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL ,
2005-03-06 13:54:19 +01:00
NEWS_FLAGS ( NM_THIN , NF_VIEWPORT | NF_VEHICLE , NT_ACCIDENT , 0 ) ,
2004-08-09 19:04:08 +02:00
v - > index ,
2005-03-06 13:54:19 +01:00
0
) ;
2004-08-09 19:04:08 +02:00
ModifyStationRatingAround ( v - > tile , v - > owner , - 160 , 30 ) ;
2004-12-04 10:26:39 +01:00
SndPlayVehicleFx ( SND_13_BIG_CRASH , v ) ;
2004-08-09 19:04:08 +02:00
}
2005-03-06 13:41:18 +01:00
typedef struct VehicleAtSignalData {
TileIndex tile ;
byte direction ;
} VehicleAtSignalData ;
2004-08-09 19:04:08 +02:00
static void * CheckVehicleAtSignal ( Vehicle * v , void * data )
{
2005-03-06 13:41:18 +01:00
const VehicleAtSignalData * vasd = data ;
if ( v - > type = = VEH_Train & & v - > subtype = = TS_Front_Engine & &
v - > tile = = vasd - > tile ) {
byte diff = ( v - > direction - vasd - > direction + 2 ) & 7 ;
2004-08-09 19:04:08 +02:00
if ( diff = = 2 | | ( v - > cur_speed < = 5 & & diff < = 4 ) )
2005-03-06 13:41:18 +01:00
return v ;
2004-08-09 19:04:08 +02:00
}
2005-03-06 13:41:18 +01:00
return NULL ;
2004-08-09 19:04:08 +02:00
}
static void TrainController ( Vehicle * v )
{
2005-06-21 16:50:08 +02:00
Vehicle * prev ;
2004-08-09 19:04:08 +02:00
GetNewVehiclePosResult gp ;
uint32 r , tracks , ts ;
2005-01-31 12:23:10 +01:00
int i , enterdir , newdir , dir ;
2004-08-09 19:04:08 +02:00
byte chosen_dir ;
byte chosen_track ;
byte old_z ;
2004-08-12 00:07:08 +02:00
/* For every vehicle after and including the given vehicle */
2005-06-21 16:50:08 +02:00
for ( prev = GetPrevVehicleInChain ( v ) ; v ! = NULL ; prev = v , v = v - > next ) {
2004-08-09 19:04:08 +02:00
BeginVehicleMove ( v ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
if ( v - > u . rail . track ! = 0x40 ) {
2004-08-12 00:07:08 +02:00
/* Not inside tunnel */
2004-08-09 19:04:08 +02:00
if ( GetNewVehiclePos ( v , & gp ) ) {
2004-08-12 00:07:08 +02:00
/* Staying in the old tile */
2004-08-09 19:04:08 +02:00
if ( v - > u . rail . track = = 0x80 ) {
/* inside depot */
gp . x = v - > x_pos ;
gp . y = v - > y_pos ;
} else {
2004-08-12 00:07:08 +02:00
/* is not inside depot */
2004-12-21 17:00:14 +01:00
2005-07-04 16:58:55 +02:00
if ( ( prev = = NULL ) & & ( ! TrainCheckIfLineEnds ( v ) ) )
2004-12-21 17:00:14 +01:00
return ;
2004-08-09 19:04:08 +02:00
r = VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) ;
2005-01-31 12:23:10 +01:00
if ( r & 0x8 ) {
//debug("%x & 0x8", r);
2004-08-09 19:04:08 +02:00
goto invalid_rail ;
2005-01-31 12:23:10 +01:00
}
2004-08-09 19:04:08 +02:00
if ( r & 0x2 ) {
TrainEnterStation ( v , r > > 8 ) ;
return ;
}
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_LEAVESTATION ) {
v - > current_order . type = OT_NOTHING ;
v - > current_order . flags = 0 ;
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
}
}
} else {
/* A new tile is about to be entered. */
2005-01-31 12:23:10 +01:00
byte bits ;
2004-08-09 19:04:08 +02:00
/* Determine what direction we're entering the new tile from */
dir = GetNewVehicleDirectionByTile ( gp . new_tile , gp . old_tile ) ;
2005-01-31 12:23:10 +01:00
enterdir = dir > > 1 ;
assert ( enterdir = = 0 | | enterdir = = 1 | | enterdir = = 2 | | enterdir = = 3 ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* Get the status of the tracks in the new tile and mask
* away the bits that aren ' t reachable . */
2005-01-31 12:23:10 +01:00
ts = GetTileTrackStatus ( gp . new_tile , TRANSPORT_RAIL ) & _reachable_tracks [ enterdir ] ;
2004-08-09 19:04:08 +02:00
/* 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 ) ;
2005-01-31 12:23:10 +01:00
bits = tracks & 0xFF ;
if ( _patches . new_pathfinding_all & & _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.
2005-06-16 20:04:02 +02:00
bits & = ~ TrackCrossesTracks ( FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
2005-01-31 12:23:10 +01:00
if ( bits = = 0 ) {
//debug("%x == 0", bits);
2004-08-09 19:04:08 +02:00
goto invalid_rail ;
2005-01-31 12:23:10 +01:00
}
2004-08-09 19:04:08 +02:00
/* Check if the new tile contrains tracks that are compatible
* with the current train , if not , bail out . */
2005-01-31 12:23:10 +01:00
if ( ! CheckCompatibleRail ( v , gp . new_tile ) ) {
//debug("!CheckCompatibleRail(%p, %x)", v, gp.new_tile);
2004-08-09 19:04:08 +02:00
goto invalid_rail ;
2005-01-31 12:23:10 +01:00
}
2004-08-09 19:04:08 +02:00
if ( prev = = NULL ) {
2005-07-04 16:58:55 +02:00
byte trackdir ;
2004-08-09 19:04:08 +02:00
/* Currently the locomotive is active. Determine which one of the
* available tracks to choose */
2005-01-31 12:23:10 +01:00
chosen_track = 1 < < ChooseTrainTrack ( v , gp . new_tile , enterdir , bits ) ;
assert ( chosen_track & tracks ) ;
2004-08-09 19:04:08 +02:00
2005-07-04 16:58:55 +02:00
trackdir = TrackEnterdirToTrackdir ( FIND_FIRST_BIT ( chosen_track ) , enterdir ) ;
assert ( trackdir ! = 0xff ) ;
if ( PBSIsPbsSignal ( gp . new_tile , trackdir ) ) {
// encountered a pbs signal, and possible a pbs block
DEBUG ( pbs , 3 ) ( " pbs: (%i) arrive AT signal, tile:%x pbs_stat:%i " , v - > unitnumber , gp . new_tile , v - > u . rail . pbs_status ) ;
if ( v - > u . rail . pbs_status = = PBS_STAT_NONE ) {
// we havent planned a path already, so try to find one now
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
NPFFillWithOrderData ( & fstd , v ) ;
DEBUG ( pbs , 2 ) ( " pbs: (%i) choose signal (TC), tile:%x, trackdir:%i " , v - > unitnumber , gp . new_tile , trackdir ) ;
ftd = NPFRouteToStationOrTile ( gp . new_tile , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_GREEN ) ;
if ( v - > u . rail . force_proceed ! = 0 )
goto green_light ;
if ( ftd . best_trackdir = = 0xFF )
goto red_light ;
// we found a way out of the pbs block
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_EXIT ) ) {
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_BLOCKED ) | | NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_RED ) )
goto red_light ;
else {
2005-07-17 22:09:02 +02:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 16:58:55 +02:00
goto green_light ;
}
} ;
} else {
// we have already planned a path through this pbs block
// on entering the block, we reset our status
v - > u . rail . pbs_status = PBS_STAT_NONE ;
goto green_light ;
} ;
DEBUG ( pbs , 3 ) ( " pbs: (%i) no green light found, or was no pbs-block " , v - > unitnumber ) ;
} ;
2004-08-09 19:04:08 +02:00
/* 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 } ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* The wagon is active, simply follow the prev vehicle. */
2005-01-31 12:23:10 +01:00
chosen_track = ( byte ) ( _matching_tracks [ GetDirectionToVehicle ( prev , gp . x , gp . y ) ] & bits ) ;
2004-08-09 19:04:08 +02:00
}
2005-07-04 16:58:55 +02:00
green_light :
if ( v - > next = = NULL )
PBSClearTrack ( gp . old_tile , FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
2004-08-09 19:04:08 +02:00
/* 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 */
{
2005-01-31 12:23:10 +01:00
const byte * b = _initial_tile_subcoord [ FIND_FIRST_BIT ( chosen_track ) ] [ enterdir ] ;
2004-08-09 19:04:08 +02:00
gp . x = ( gp . x & ~ 0xF ) | b [ 0 ] ;
gp . y = ( gp . y & ~ 0xF ) | b [ 1 ] ;
chosen_dir = b [ 2 ] ;
}
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* Call the landscape function and tell it that the vehicle entered the tile */
r = VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) ;
2005-01-31 12:23:10 +01:00
if ( r & 0x8 ) {
//debug("%x & 0x8", r);
2004-08-09 19:04:08 +02:00
goto invalid_rail ;
2005-01-31 12:23:10 +01:00
}
2004-08-09 19:04:08 +02:00
2005-01-19 20:15:03 +01:00
if ( v - > subtype = = TS_Front_Engine ) v - > load_unload_time_rem = 0 ;
2004-08-09 19:04:08 +02:00
if ( ! ( r & 0x4 ) ) {
v - > tile = gp . new_tile ;
v - > u . rail . track = chosen_track ;
2005-02-06 23:36:08 +01:00
assert ( v - > u . rail . track ) ;
2004-08-09 19:04:08 +02:00
}
2005-01-19 20:15:03 +01:00
if ( v - > subtype = = TS_Front_Engine )
2005-07-04 16:58:55 +02:00
TrainMovedChangeSignals ( gp . new_tile , enterdir ) ;
2004-08-09 19:04:08 +02:00
2004-08-12 00:07:08 +02:00
/* Signals can only change when the first
* ( above ) or the last vehicle moves . */
2004-08-09 19:04:08 +02:00
if ( v - > next = = NULL )
2005-07-04 16:58:55 +02:00
TrainMovedChangeSignals ( gp . old_tile , ( enterdir ) ^ 2 ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
if ( prev = = NULL ) {
AffectSpeedByDirChange ( v , chosen_dir ) ;
}
v - > direction = chosen_dir ;
}
} else {
/* in tunnel */
GetNewVehiclePos ( v , & gp ) ;
2004-09-10 21:02:27 +02:00
2005-07-19 13:42:40 +02:00
// 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 ;
2004-08-09 19:04:08 +02:00
}
}
/* update image of train, as well as delta XY */
2005-01-31 12:23:10 +01:00
newdir = GetNewVehicleDirection ( v , gp . x , gp . y ) ;
UpdateTrainDeltaXY ( v , newdir ) ;
v - > cur_image = GetTrainImage ( v , newdir ) ;
2004-08-09 19:04:08 +02:00
v - > x_pos = gp . x ;
v - > y_pos = gp . y ;
/* update the Z position of the vehicle */
2005-01-09 17:02:06 +01:00
old_z = AfterSetTrainPos ( v , ( gp . new_tile ! = gp . old_tile ) ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
if ( prev = = NULL ) {
2004-08-12 00:07:08 +02:00
/* This is the first vehicle in the train */
2004-08-09 19:04:08 +02:00
AffectSpeedByZChange ( v , old_z ) ;
}
}
2005-03-06 16:15:27 +01:00
return ;
2004-08-09 19:04:08 +02:00
invalid_rail :
2004-08-12 00:07:08 +02:00
/* We've reached end of line?? */
2004-08-09 19:04:08 +02:00
if ( prev ! = NULL ) {
error ( " !Disconnecting train " ) ;
}
goto reverse_train_direction ;
red_light : {
2004-08-12 00:07:08 +02:00
/* We're in front of a red signal ?? */
2004-08-09 19:04:08 +02:00
/* 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 ) ;
2004-09-10 21:02:27 +02:00
2005-07-13 20:04:01 +02:00
if ( ! ( _m [ gp . new_tile ] . m3 & SignalAgainstTrackdir ( i ) ) ) {
2004-08-09 19:04:08 +02:00
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
v - > progress = 255 - 100 ;
if ( + + v - > load_unload_time_rem < _patches . wait_oneway_signal * 20 )
return ;
2005-07-13 20:04:01 +02:00
} else if ( _m [ gp . new_tile ] . m3 & SignalAlongTrackdir ( i ) ) {
2004-08-09 19:04:08 +02:00
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
v - > progress = 255 - 10 ;
if ( + + v - > load_unload_time_rem < _patches . wait_twoway_signal * 73 ) {
2005-03-06 13:41:18 +01:00
TileIndex o_tile = gp . new_tile + TileOffsByDir ( enterdir ) ;
2005-03-15 13:21:59 +01:00
VehicleAtSignalData vasd ;
vasd . tile = o_tile ;
vasd . direction = dir ^ 4 ;
2005-03-06 13:41:18 +01:00
2004-08-09 19:04:08 +02:00
/* check if a train is waiting on the other side */
2005-03-06 13:41:18 +01:00
if ( VehicleFromPos ( o_tile , & vasd , CheckVehicleAtSignal ) = = NULL )
2004-08-09 19:04:08 +02:00
return ;
}
}
}
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
reverse_train_direction :
v - > load_unload_time_rem = 0 ;
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
ReverseTrainDirection ( v ) ;
}
2005-03-06 13:31:07 +01:00
extern TileIndex CheckTunnelBusy ( TileIndex tile , uint * length ) ;
2004-08-21 11:57:02 +02:00
2005-03-04 00:26:35 +01:00
/**
* 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
*/
2004-08-09 19:04:08 +02:00
static void DeleteLastWagon ( Vehicle * v )
{
Vehicle * u = v ;
2005-03-04 00:26:35 +01:00
/* 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 */
2005-03-06 13:54:19 +01:00
for ( ; v - > next ! = NULL ; v = v - > next ) u = v ;
2004-08-09 19:04:08 +02:00
u - > next = NULL ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
DeleteWindowById ( WC_VEHICLE_VIEW , v - > index ) ;
2004-12-10 19:16:08 +01:00
RebuildVehicleLists ( ) ;
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
BeginVehicleMove ( v ) ;
EndVehicleMove ( v ) ;
DeleteVehicle ( v ) ;
2005-07-04 16:58:55 +02:00
// clear up reserved pbs tracks
if ( PBSTileReserved ( v - > tile ) & v - > u . rail . track ) {
if ( v = = u ) {
2005-07-17 22:09:02 +02:00
PBSClearPath ( v - > tile , FIND_FIRST_BIT ( v - > u . rail . track ) , v - > u . rail . pbs_end_tile , v - > u . rail . pbs_end_trackdir ) ;
PBSClearPath ( v - > tile , FIND_FIRST_BIT ( v - > u . rail . track ) + 8 , v - > u . rail . pbs_end_tile , v - > u . rail . pbs_end_trackdir ) ;
2005-07-04 16:58:55 +02:00
} ;
if ( v - > tile ! = u - > tile ) {
PBSClearTrack ( v - > tile , FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
} ;
}
2005-03-04 00:26:35 +01:00
if ( ! ( v - > u . rail . track & 0xC0 ) )
SetSignalsOnBothDir ( v - > tile , FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
2004-09-10 21:02:27 +02:00
2005-03-06 13:54:19 +01:00
/* Check if the wagon was on a road/rail-crossing and disable it if no
* others are on it */
2005-01-23 11:40:54 +01:00
DisableTrainCrossing ( v - > tile ) ;
2005-03-04 00:26:35 +01:00
if ( v - > u . rail . track = = 0x40 ) { // inside a tunnel
2005-03-06 13:31:07 +01:00
TileIndex endtile = CheckTunnelBusy ( v - > tile , NULL ) ;
2005-03-04 00:26:35 +01:00
2005-03-06 13:31:07 +01:00
if ( endtile = = INVALID_TILE ) // tunnel is busy (error returned)
2005-03-04 00:26:35 +01:00
return ;
2005-03-06 13:33:33 +01:00
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 ;
}
2004-08-21 11:57:02 +02:00
}
2004-08-09 19:04:08 +02:00
}
static void ChangeTrainDirRandomly ( Vehicle * v )
{
static int8 _random_dir_change [ 4 ] = { - 1 , 0 , 0 , 1 } ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
do {
2004-08-21 11:57:02 +02:00
//I need to buffer the train direction
2004-09-18 18:40:06 +02:00
if ( ! ( v - > u . rail . track & 0x40 ) )
2005-01-24 15:39:22 +01:00
v - > direction = ( v - > direction + _random_dir_change [ Random ( ) & 3 ] ) & 7 ;
2004-08-09 19:04:08 +02:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
BeginVehicleMove ( v ) ;
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
2005-01-09 17:02:06 +01:00
AfterSetTrainPos ( v , false ) ;
2004-08-09 19:04:08 +02:00
}
} while ( ( v = v - > next ) ! = NULL ) ;
}
static void HandleCrashedTrain ( Vehicle * v )
{
int state = + + v - > u . rail . crash_anim_pos , index ;
uint32 r ;
Vehicle * u ;
2004-09-10 21:02:27 +02:00
2004-08-21 11:57:02 +02:00
if ( ( state = = 4 ) & & ( v - > u . rail . track ! = 0x40 ) ) {
2005-02-12 16:53:32 +01:00
CreateEffectVehicleRel ( v , 4 , 4 , 8 , EV_EXPLOSION_LARGE ) ;
2004-08-09 19:04:08 +02:00
}
2005-01-24 15:39:22 +01:00
if ( state < = 200 & & ( uint16 ) ( r = Random ( ) ) < = 0x2492 ) {
2004-08-09 19:04:08 +02:00
index = ( r * 10 > > 16 ) ;
u = v ;
do {
if ( - - index < 0 ) {
2005-01-24 15:39:22 +01:00
r = Random ( ) ;
2004-08-09 19:04:08 +02:00
CreateEffectVehicleRel ( u ,
2005-07-20 17:29:28 +02:00
GB ( r , 8 , 3 ) + 2 ,
GB ( r , 16 , 3 ) + 2 ,
GB ( r , 0 , 3 ) + 5 ,
2005-02-12 16:53:32 +01:00
EV_EXPLOSION_SMALL ) ;
2004-08-09 19:04:08 +02:00
break ;
}
} while ( ( u = u - > next ) ! = NULL ) ;
}
if ( state < = 240 & & ! ( v - > tick_counter & 3 ) ) {
ChangeTrainDirRandomly ( v ) ;
}
2005-01-23 23:01:51 +01:00
if ( state > = 4440 & & ! ( v - > tick_counter & 0x1F ) ) {
2004-08-09 19:04:08 +02:00
DeleteLastWagon ( v ) ;
2005-01-23 23:01:51 +01:00
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ;
}
2004-08-09 19:04:08 +02:00
}
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 + + ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_VEHICLE_VIEW , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
2004-09-10 21:02:27 +02:00
2004-12-04 10:26:39 +01:00
SndPlayVehicleFx ( ( _opt . landscape ! = LT_CANDY ) ?
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2 , v ) ;
2004-08-09 19:04:08 +02:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
Vehicle * u = CreateEffectVehicleRel ( v , 4 , 4 , 5 , EV_BREAKDOWN_SMOKE ) ;
if ( u )
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
} ;
2004-12-21 17:00:14 +01:00
static bool TrainCheckIfLineEnds ( Vehicle * v )
2004-08-09 19:04:08 +02:00
{
2005-06-24 14:38:35 +02:00
TileIndex tile ;
2004-08-09 19:04:08 +02:00
uint x , y ;
int t ;
uint32 ts ;
2005-07-04 16:58:55 +02:00
byte trackdir ;
2004-08-09 19:04:08 +02:00
if ( ( uint ) ( t = v - > breakdown_ctr ) > 1 ) {
v - > vehstatus | = VS_TRAIN_SLOWING ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
t = _breakdown_speeds [ ( ( ~ t ) > > 4 ) & 0xF ] ;
if ( ( uint16 ) t < = v - > cur_speed )
v - > cur_speed = t ;
} else {
v - > vehstatus & = ~ VS_TRAIN_SLOWING ;
}
// exit if inside a tunnel
if ( v - > u . rail . track & 0x40 )
2004-12-21 17:00:14 +01:00
return true ;
2004-08-09 19:04:08 +02:00
2005-07-04 16:58:55 +02:00
// exit if inside a depot
if ( v - > u . rail . track & 0x80 )
return true ;
2004-08-09 19:04:08 +02:00
tile = v - > tile ;
// tunnel entrance?
2005-07-21 08:31:02 +02:00
if ( IsTunnelTile ( tile ) & & GB ( _m [ tile ] . m5 , 0 , 2 ) * 2 + 1 = = v - > direction )
return true ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// depot?
2004-12-21 17:00:14 +01:00
/* XXX -- When enabled, this makes it possible to crash trains of others
( by building a depot right against a station ) */
2005-07-13 20:04:01 +02:00
/* if (IsTileType(tile, MP_RAILWAY) && (_m[tile].m5 & 0xFC) == 0xC0)
2004-12-21 17:00:14 +01:00
return true ; */
2004-08-09 19:04:08 +02:00
2004-08-12 00:07:08 +02:00
/* Determine the non-diagonal direction in which we will exit this tile */
2004-08-09 19:04:08 +02:00
t = v - > direction > > 1 ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ t ] ) {
t = ( t - 1 ) & 3 ;
}
2004-08-12 00:07:08 +02:00
/* Calculate next tile */
2005-01-05 14:32:03 +01:00
tile + = TileOffsByDir ( t ) ;
2004-08-12 00:07:08 +02:00
// determine the track status on the next tile.
2005-01-24 23:24:47 +01:00
ts = GetTileTrackStatus ( tile , TRANSPORT_RAIL ) & _reachable_tracks [ t ] ;
2004-09-10 21:02:27 +02:00
2005-07-04 16:58:55 +02:00
// if there are tracks on the new tile, pick one (trackdir will only be used when its a signal tile, in which case only 1 trackdir is accessible for us)
if ( ts & TRACKDIR_BIT_MASK )
trackdir = FindFirstBit2x64 ( ts & TRACKDIR_BIT_MASK ) ;
else
trackdir = INVALID_TRACKDIR ;
2004-08-12 00:07:08 +02:00
/* Calc position within the current tile ?? */
2004-08-09 19:04:08 +02:00
x = v - > x_pos & 0xF ;
y = v - > y_pos & 0xF ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
switch ( v - > direction ) {
case 0 :
x = ( ~ x ) + ( ~ y ) + 24 ;
break ;
case 7 :
x = y ;
/* fall through */
case 1 :
x = ( ~ x ) + 16 ;
break ;
case 2 :
x = ( ~ x ) + y + 8 ;
break ;
case 3 :
x = y ;
break ;
case 4 :
x = x + y - 8 ;
break ;
case 6 :
x = ( ~ y ) + x + 8 ;
break ;
}
if ( ( uint16 ) ts ! = 0 ) {
2004-12-21 17:00:14 +01:00
/* If we approach a rail-piece which we can't enter, don't enter it! */
if ( x + 4 > 15 & & ! CheckCompatibleRail ( v , tile ) ) {
v - > cur_speed = 0 ;
ReverseTrainDirection ( v ) ;
return false ;
}
2004-08-09 19:04:08 +02:00
if ( ( ts & = ( ts > > 16 ) ) = = 0 ) {
// make a rail/road crossing red
2005-06-07 23:37:00 +02:00
if ( IsTileType ( tile , MP_STREET ) & & IsLevelCrossing ( tile ) ) {
2005-07-13 20:04:01 +02:00
if ( ! ( _m [ tile ] . m5 & 4 ) ) {
_m [ tile ] . m5 | = 4 ;
2004-12-04 10:26:39 +01:00
SndPlayVehicleFx ( SND_0E_LEVEL_CROSSING , v ) ;
2004-08-09 19:04:08 +02:00
MarkTileDirtyByTile ( tile ) ;
}
}
2004-12-21 17:00:14 +01:00
return true ;
2004-08-09 19:04:08 +02:00
}
} else if ( x + 4 > 15 ) {
v - > cur_speed = 0 ;
ReverseTrainDirection ( v ) ;
2004-12-21 17:00:14 +01:00
return false ;
2004-08-09 19:04:08 +02:00
}
2005-07-04 16:58:55 +02:00
if ( v - > u . rail . pbs_status = = PBS_STAT_HAS_PATH )
return true ;
2005-07-13 20:04:01 +02:00
if ( ( trackdir ! = INVALID_TRACKDIR ) & & ( PBSIsPbsSignal ( tile , trackdir ) ) & & ! ( IsTileType ( v - > tile , MP_STATION ) & & ( v - > current_order . station = = _m [ v - > tile ] . m2 ) ) ) {
2005-07-04 16:58:55 +02:00
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
NPFFillWithOrderData ( & fstd , v ) ;
DEBUG ( pbs , 2 ) ( " pbs: (%i) choose signal (CEOL), tile:%x trackdir:%i " , v - > unitnumber , tile , trackdir ) ;
ftd = NPFRouteToStationOrTile ( tile , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_GREEN ) ;
if ( ftd . best_trackdir ! = 0xFF & & NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_EXIT ) ) {
if ( ! ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_BLOCKED ) | | NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_RED ) ) ) {
v - > u . rail . pbs_status = PBS_STAT_HAS_PATH ;
2005-07-17 22:09:02 +02:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 16:58:55 +02:00
return true ;
}
} ;
} ;
2004-08-09 19:04:08 +02:00
// slow down
v - > vehstatus | = VS_TRAIN_SLOWING ;
t = _breakdown_speeds [ x & 0xF ] ;
if ( ! ( v - > direction & 1 ) ) t > > = 1 ;
if ( ( uint16 ) t < v - > cur_speed )
v - > cur_speed = t ;
2004-12-21 17:00:14 +01:00
return true ;
2004-08-09 19:04:08 +02:00
}
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 - - ;
}
2005-01-09 17:02:06 +01:00
if ( HASBIT ( v - > u . rail . flags , VRF_REVERSING ) & & v - > cur_speed = = 0 ) {
2004-08-09 19:04:08 +02:00
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 ) ;
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_LOADING )
2004-08-09 19:04:08 +02:00
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 ) ;
2005-06-07 00:44:11 +02:00
CheckTrainCollision ( v ) ;
2004-08-09 19:04:08 +02:00
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 + + ;
2005-01-19 20:15:03 +01:00
if ( v - > subtype = = TS_Front_Engine ) {
2004-08-09 19:04:08 +02:00
TrainLocoHandler ( v , false ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// make sure vehicle wasn't deleted.
2005-01-19 20:15:03 +01:00
if ( v - > type = = VEH_Train & & v - > subtype = = TS_Front_Engine )
2004-08-09 19:04:08 +02:00
TrainLocoHandler ( v , true ) ;
2005-01-24 01:36:22 +01:00
} else if ( v - > subtype = = TS_Free_Car & & HASBITS ( v - > vehstatus , VS_CRASHED ) ) {
// Delete flooded standalone wagon
if ( + + v - > u . rail . crash_anim_pos > = 4400 )
DeleteVehicle ( v ) ;
2004-08-09 19:04:08 +02:00
}
}
static const byte _depot_track_ind [ 4 ] = { 0 , 1 , 0 , 1 } ;
2004-12-19 10:39:19 +01:00
// Validation for the news item "Train is waiting in depot"
2005-01-22 23:47:58 +01:00
static bool ValidateTrainInDepot ( uint data_a , uint data_b )
2004-12-19 10:39:19 +01:00
{
2005-01-06 23:31:58 +01:00
Vehicle * v = GetVehicle ( data_a ) ;
2005-01-19 11:00:37 +01:00
return ( v - > u . rail . track = = 0x80 & & ( v - > vehstatus | VS_STOPPED ) ) ;
2004-12-19 10:39:19 +01:00
}
2005-06-24 14:38:35 +02:00
void TrainEnterDepot ( Vehicle * v , TileIndex tile )
2004-08-09 19:04:08 +02:00
{
2005-07-13 20:04:01 +02:00
SetSignalsOnBothDir ( tile , _depot_track_ind [ _m [ tile ] . m5 & 3 ] ) ;
2004-08-09 19:04:08 +02:00
2005-01-19 20:15:03 +01:00
if ( v - > subtype ! = TS_Front_Engine )
2004-08-09 19:04:08 +02:00
v = GetFirstVehicleInChain ( v ) ;
2004-12-09 22:46:56 +01:00
VehicleServiceInDepot ( v ) ;
2004-08-09 19:04:08 +02:00
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
v - > load_unload_time_rem = 0 ;
v - > cur_speed = 0 ;
2005-01-02 18:23:04 +01:00
MaybeReplaceVehicle ( v ) ;
2004-08-09 19:04:08 +02:00
2004-11-17 09:52:47 +01:00
TriggerVehicle ( v , VEHICLE_TRIGGER_DEPOT ) ;
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
Order t ;
2004-09-10 21:02:27 +02:00
2004-12-05 13:43:04 +01:00
InvalidateWindow ( WC_VEHICLE_VIEW , v - > index ) ;
2004-08-09 19:04:08 +02:00
2004-12-05 13:43:04 +01:00
t = v - > current_order ;
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
2004-08-09 19:04:08 +02:00
2005-03-20 09:43:29 +01:00
if ( HASBIT ( t . flags , OFB_PART_OF_ORDERS ) ) { // Part of the orderlist?
2004-12-05 13:43:04 +01:00
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
2005-03-20 09:43:29 +01:00
} else if ( HASBIT ( t . flags , OFB_HALT_IN_DEPOT ) ) { // User initiated?
2004-08-09 19:04:08 +02:00
v - > vehstatus | = VS_STOPPED ;
if ( v - > owner = = _local_player ) {
2004-12-02 23:53:07 +01:00
SetDParam ( 0 , v - > unitnumber ) ;
2004-12-19 10:39:19 +01:00
AddValidatedNewsItem (
2004-08-09 19:04:08 +02:00
STR_8814_TRAIN_IS_WAITING_IN_DEPOT ,
NEWS_FLAGS ( NM_SMALL , NF_VIEWPORT | NF_VEHICLE , NT_ADVICE , 0 ) ,
v - > index ,
2004-12-19 10:39:19 +01:00
0 ,
ValidateTrainInDepot ) ;
2004-08-09 19:04:08 +02:00
}
}
}
2005-01-24 23:24:47 +01:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 19:04:08 +02:00
}
static void CheckIfTrainNeedsService ( Vehicle * v )
{
2005-02-06 11:18:47 +01:00
Depot * depot ;
2004-09-23 23:20:38 +02:00
TrainFindDepotData tfdd ;
2004-08-09 19:04:08 +02:00
2005-07-04 16:58:55 +02:00
if ( PBSTileReserved ( v - > tile ) & v - > u . rail . track )
return ;
if ( v - > u . rail . pbs_status = = PBS_STAT_HAS_PATH )
return ;
2005-03-19 22:16:22 +01:00
if ( _patches . servint_trains = = 0 )
2004-08-09 19:04:08 +02:00
return ;
2005-03-19 22:16:22 +01:00
if ( ! VehicleNeedsService ( v ) )
2004-08-09 19:04:08 +02:00
return ;
if ( v - > vehstatus & VS_STOPPED )
return ;
2005-01-15 20:06:22 +01:00
if ( _patches . gotodepot & & VehicleHasDepotOrders ( v ) )
2004-08-09 19:04:08 +02:00
return ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// Don't interfere with a depot visit scheduled by the user, or a
// depot visit by the order list.
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
2005-03-20 09:43:29 +01:00
( v - > current_order . flags & ( OF_HALT_IN_DEPOT | OF_PART_OF_ORDERS ) ) ! = 0 )
2004-08-09 19:04:08 +02:00
return ;
2004-09-23 23:20:38 +02:00
tfdd = FindClosestTrainDepot ( v ) ;
/* Only go to the depot if it is not too far out of our way. */
if ( tfdd . best_length = = ( uint ) - 1 | | tfdd . best_length > 16 ) {
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
2004-09-23 23:20:38 +02:00
/* If we were already heading for a depot but it has
* suddenly moved farther away , we continue our normal
* schedule ? */
2004-12-05 13:43:04 +01:00
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
}
return ;
}
2004-09-23 23:20:38 +02:00
depot = GetDepotByTile ( tfdd . tile ) ;
2004-08-09 19:04:08 +02:00
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
2005-02-06 11:18:47 +01:00
v - > current_order . station ! = depot - > index & &
2004-12-05 13:43:04 +01:00
! CHANCE16 ( 3 , 16 ) )
2004-08-09 19:04:08 +02:00
return ;
2004-12-05 13:43:04 +01:00
v - > current_order . type = OT_GOTO_DEPOT ;
v - > current_order . flags = OF_NON_STOP ;
2005-02-06 11:18:47 +01:00
v - > current_order . station = depot - > index ;
2004-09-23 23:20:38 +02:00
v - > dest_tile = tfdd . tile ;
2004-12-22 00:27:58 +01:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 19:04:08 +02:00
}
2005-05-11 18:17:03 +02:00
int32 GetTrainRunningCost ( const Vehicle * v )
2004-08-09 19:04:08 +02:00
{
int32 cost = 0 ;
do {
2004-12-04 08:41:37 +01:00
const RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
2004-08-09 19:04:08 +02:00
if ( rvi - > running_cost_base )
cost + = rvi - > running_cost_base * _price . running_rail [ rvi - > engclass ] ;
} while ( ( v = v - > next ) ! = NULL ) ;
return cost ;
}
void OnNewDay_Train ( Vehicle * v )
{
TileIndex tile ;
if ( ( + + v - > day_counter & 7 ) = = 0 )
DecreaseVehicleValue ( v ) ;
2005-01-19 20:15:03 +01:00
if ( v - > subtype = = TS_Front_Engine ) {
2004-08-09 19:04:08 +02:00
CheckVehicleBreakdown ( v ) ;
AgeVehicle ( v ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
CheckIfTrainNeedsService ( v ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// check if train hasn't advanced in its order list for a set number of days
2005-01-18 17:01:35 +01:00
if ( _patches . lost_train_days & & v - > num_orders & & ! ( v - > vehstatus & ( VS_STOPPED | VS_CRASHED ) ) & & + + v - > u . rail . days_since_order_progr > = _patches . lost_train_days & & v - > owner = = _local_player ) {
2004-08-09 19:04:08 +02:00
v - > u . rail . days_since_order_progr = 0 ;
2004-12-02 23:53:07 +01:00
SetDParam ( 0 , v - > unitnumber ) ;
2004-08-09 19:04:08 +02:00
AddNewsItem (
STR_TRAIN_IS_LOST ,
NEWS_FLAGS ( NM_SMALL , NF_VIEWPORT | NF_VEHICLE , NT_ADVICE , 0 ) ,
v - > index ,
0 ) ;
}
2005-01-17 22:54:45 +01:00
CheckOrders ( v - > index , OC_INIT ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
/* update destination */
2004-12-05 13:43:04 +01:00
if ( v - > current_order . type = = OT_GOTO_STATION & &
2005-01-06 23:31:58 +01:00
( tile = GetStation ( v - > current_order . station ) - > train_tile ) ! = 0 )
2004-08-09 19:04:08 +02:00
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 ) ;
2005-01-24 23:24:47 +01:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 19:04:08 +02:00
}
}
}
2005-01-22 21:23:18 +01:00
void TrainsYearlyLoop ( void )
2004-08-09 19:04:08 +02:00
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
2005-01-19 20:15:03 +01:00
if ( v - > type = = VEH_Train & & v - > subtype = = TS_Front_Engine ) {
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
// 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 ) {
2004-12-02 23:53:07 +01:00
SetDParam ( 1 , v - > profit_this_year ) ;
SetDParam ( 0 , v - > unitnumber ) ;
2004-08-09 19:04:08 +02:00
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 HandleClickOnTrain ( Vehicle * v )
{
2005-01-19 20:15:03 +01:00
if ( v - > subtype ! = TS_Front_Engine ) v = GetFirstVehicleInChain ( v ) ;
2004-08-09 19:04:08 +02:00
ShowTrainViewWindow ( v ) ;
}
2005-01-22 21:23:18 +01:00
void InitializeTrains ( void )
2004-08-09 19:04:08 +02:00
{
_age_cargo_skip_counter = 1 ;
}